@recursiv/sdk 0.7.58 → 0.7.59

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -75,7 +75,7 @@ for await (const chunk of r.agents.chatStream(agent.id, { message: 'Hello!' }))
75
75
  | Resource | Key methods |
76
76
  |----------|------------|
77
77
  | `r.agents` | `create`, `update`, `chat`, `chatStream`, `retryChatStream`, `grantProjectAccess`, `conversations` |
78
- | `r.e2ee` | `status`, `registerDevice`, `devices`, `revokeDevice` (`{ cascade }` to also revoke devices approved through it; 409 `has_dependents` otherwise), `publishKeyPackages`, `keyPackageStatus`, `claimKeyPackage`, `appendEvent`, `events`, `acknowledge`, `conversations`; device linking by scan: `createLink`, `link`, `scannedLink`, `approveLink`, `completeLink`, `linkProgress`, `cancelLink` (flag-gated E2EE v2 transport; its doorbell is `r.realtime.onE2eeEvent`, which carries a small message's stored ciphertext to the owner's own sockets; link and device changes arrive on `r.realtime.onE2eeLink` and `onE2eeDevices`) |
78
+ | `r.e2ee` | `status`, `registerDevice`, `devices`, `revokeDevice` (`{ cascade }` to also revoke devices approved through it; 409 `has_dependents` otherwise), `requestResetCode` and `resetAccount` (reset encrypted chat with an emailed code where `status().accountReset`), `publishKeyPackages`, `keyPackageStatus`, `claimKeyPackage`, `appendEvent`, `events`, `acknowledge`, `conversations`; device linking by scan: `createLink`, `link`, `scannedLink`, `approveLink`, `completeLink`, `linkProgress`, `cancelLink` (flag-gated E2EE v2 transport; its doorbell is `r.realtime.onE2eeEvent`, which carries a small message's stored ciphertext to the owner's own sockets; link and device changes arrive on `r.realtime.onE2eeLink` and `onE2eeDevices`) |
79
79
  | `r.brain` | `sendMessage` |
80
80
  | `r.commands` | `execute`, `gatePrompt` |
81
81
  | `r.projectBrain` | `tasks`, `decisions`, `milestones`, `completeTask`, `usage` |
@@ -115,7 +115,7 @@ for await (const chunk of r.agents.chatStream(agent.id, { message: 'Hello!' }))
115
115
  | `r.admin` | User management, stats, content reports (`listReports`, `resolveReport`, `dismissReport`) |
116
116
  | `r.dispatcher` | Task queue, claims, signals, outcomes, webhooks |
117
117
  | `r.goals` | Org goals with task rollups (group dispatcher tasks) |
118
- | `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? })`. 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`; 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), 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); 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` |
118
+ | `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? })`. 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`; 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), 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` |
119
119
 
120
120
  Full reference: see [llm.md](./llm.md) (ships with this package) or the type definitions in `dist/`.
121
121
 
@@ -364,7 +364,7 @@ try {
364
364
  }
365
365
  ```
366
366
 
367
- Auto-retry is built in. 429 and 5xx responses retry with exponential backoff (configurable via `maxRetries`, default 2). A 429 with `X-RateLimit-Scope: action` refuses one member action past its budget and is never retried: its `RateLimitError` has `scope: 'action'` and `resetAt` (a `Date`, when that action's budget reopens).
367
+ Auto-retry is built in. 429 and 5xx responses retry with exponential backoff (configurable via `maxRetries`, default 2). A 429 with `X-RateLimit-Scope: action` refuses one member action past its budget and is never retried: its `RateLimitError` has `scope: 'action'` and `resetAt` (a `Date`, when that action's budget reopens). A read that holds its own deadline can ask once with `retry: false` in its params: `r.posts.list({ ...params, retry: false })` and `r.curator.forYou({ ...params, retry: false })` throw the first 5xx or 429 with no retry and no `Retry-After` sleep, and never send `retry` to the server.
368
368
 
369
369
  ## Pagination
370
370
 
@@ -664,6 +664,14 @@ and says whether the member is personalized yet (`meta.personalized`). With a
664
664
  `category` (the member's first), the session also reads open-network posts from it;
665
665
  without, it is Minds only. It never carries the radar.
666
666
 
667
+ Both take a Politics choice, `compass` (`all`, `mix`, `hide`, or cells in the order
668
+ `lo,co,ro,lm,cm,rm,ll,cl,rl`, optionally `np`), and send it with every Minds read
669
+ (`posts.list` and `curator.forYou` take it too). Each such page says what it did in
670
+ `meta.compass_filter` (`status` `applied` or `unavailable`, and counts only), and each session
671
+ page carries its Minds read's answer as `page.compass` (`unavailable`: the page is unfiltered). Only Minds
672
+ posts are filtered, so a view that leaves posts out reads best with `source: 'minds'`.
673
+ The catalog lists `compass_filter` on every network when the filter is served to the viewer.
674
+
667
675
  `sdk.discovery.recommendations({ kind, category?, limit?, session?, exclude? })` is
668
676
  the app's one recommender for suggestion rails: `kind` is `member`, `group`, `tag`,
669
677
  `network_account` or `story`, and `category` is `all` or a browse category id. It
@@ -689,7 +697,11 @@ category, refreshed after 30 s, kept up to 300 s and warmed as the warmer reache
689
697
  viewer's `people` and `groups`; a section that timed out, failed or is off is empty with a
690
698
  `reason` for that request only (a failure is never cached, so asking again retries it), and one this
691
699
  key may not read is empty with `refused` (`stories` and `network` need the member's own app
692
- key with `users:read`; `people` and `groups` need `users:read`). A plain category page
700
+ key with `users:read`; `people` and `groups` need `users:read`). `sdk.discovery.videos({ category?, limit?, cursor?, offset? })`
701
+ is Watch's Minds read: the app's public video posts in hot order, from one list the server shares among every viewer of
702
+ the app and rebuilds about once a minute, each post checked again on every call under the public floors and the viewer's
703
+ blocks and mutes, in the `Post` shape with the viewer's own `user_reaction` and `reposted_by_viewer_id` (page on
704
+ `meta.next_cursor`; a feed session's Minds slot can read it as its `list`). A plain category page
693
705
  (`posts.list({ category })` with no other filter) reads on past a slice the safety floors
694
706
  thinned, up to six reads, until it holds `limit` posts or its candidates end: `meta.scanned`
695
707
  is how many it examined (the next offset is `offset + scanned`), and the stage ends on the
package/dist/errors.d.ts CHANGED
@@ -84,3 +84,16 @@ export declare class RateLimitError extends RecursivError {
84
84
  export declare class ConflictError extends RecursivError {
85
85
  constructor(body: ApiErrorBody['error']);
86
86
  }
87
+ /**
88
+ * `email.send` answered 200 but sent to nobody: every recipient was held
89
+ * (no marketing consent, suppressed, preference off). Thrown so a caller that
90
+ * only checks for success cannot report mail as sent. `details` carries the
91
+ * per-recipient `messages` with each `reason`.
92
+ */
93
+ export declare class EmailNotSentError extends RecursivError {
94
+ constructor(body: {
95
+ message: string;
96
+ hint?: string;
97
+ details?: unknown;
98
+ });
99
+ }
package/dist/errors.js CHANGED
@@ -189,3 +189,15 @@ export class ConflictError extends RecursivError {
189
189
  this.name = 'ConflictError';
190
190
  }
191
191
  }
192
+ /**
193
+ * `email.send` answered 200 but sent to nobody: every recipient was held
194
+ * (no marketing consent, suppressed, preference off). Thrown so a caller that
195
+ * only checks for success cannot report mail as sent. `details` carries the
196
+ * per-recipient `messages` with each `reason`.
197
+ */
198
+ export class EmailNotSentError extends RecursivError {
199
+ constructor(body) {
200
+ super(200, { type: 'email_not_sent', code: 'email_held', message: body.message, hint: body.hint, details: body.details });
201
+ this.name = 'EmailNotSentError';
202
+ }
203
+ }
package/dist/index.d.ts CHANGED
@@ -154,12 +154,12 @@ export { UsersResource } from './resources/users.js';
154
154
  export { CommunitiesResource } from './resources/communities.js';
155
155
  export { ChatResource } from './resources/chat.js';
156
156
  export { E2eeResource } from './resources/e2ee.js';
157
- export type { E2eeDevice, E2eeDevicesRealtimeEvent, E2eeEnvelope, E2eeEvent, E2eeHasDependentsDetails, E2eeKeyPackageStatus, E2eeLink, E2eeLinkCancelledBy, E2eeLinkProgress, E2eeLinkRealtimeEvent, E2eeLinkState, E2eeObjectDownload, E2eeObjectUpload, E2eeRealtimeEvent, E2eeRevokeResponse, E2eeStatus, E2eeStatusReason, } from './resources/e2ee.js';
157
+ export type { E2eeAccountReset, E2eeDevice, E2eeDevicesRealtimeEvent, E2eeEnvelope, E2eeEvent, E2eeHasDependentsDetails, E2eeKeyPackageStatus, E2eeLink, E2eeLinkCancelledBy, E2eeLinkProgress, E2eeLinkRealtimeEvent, E2eeLinkState, E2eeObjectDownload, E2eeObjectUpload, E2eeRealtimeEvent, E2eeResetCodeSent, E2eeResetErrorCode, E2eeRevokeResponse, E2eeStatus, E2eeStatusReason, } from './resources/e2ee.js';
158
158
  export { ProjectsResource } from './resources/projects.js';
159
159
  export { AgentsResource, SUPPORTED_MODELS } from './resources/agents.js';
160
160
  export { EvidenceResource } from './resources/evidence.js';
161
161
  export type { EvidenceTarget, EvalType, CreateEvalInput, SimulateInput, SimulateResult, EvalRunRow, EvalResultRow, EvalRunDetail, EvidenceSummary, EvidenceEvent, EvidenceRecordDetail } from './resources/evidence.js';
162
- export type { AgentMessage, AgentMessageInput, AgentInboxParams, SupportedModel, SalesLead, SalesLeadListParams, SalesLeadStatus } from './resources/agents.js';
162
+ export type { AgentMessage, AgentMessageInput, AgentInboxParams, AgentAskInput, AgentAskResult, SupportedModel, SalesLead, SalesLeadListParams, SalesLeadStatus } from './resources/agents.js';
163
163
  export { TagsResource } from './resources/tags.js';
164
164
  export { SandboxResource } from './resources/sandbox.js';
165
165
  export { ProfilesResource } from './resources/profiles.js';
@@ -173,6 +173,7 @@ export { InviteCodesResource } from './resources/invite-codes.js';
173
173
  export { InviteCodesAdminResource } from './resources/invite-codes-admin.js';
174
174
  export { AdminResource } from './resources/admin.js';
175
175
  export { EmailResource } from './resources/email.js';
176
+ export type { SendEmailInput, SendEmailResult, SendEmailMessage } from './resources/email.js';
176
177
  export { StorageResource } from './resources/storage.js';
177
178
  export { DatabasesResource } from './resources/databases.js';
178
179
  export { EnvVarsResource } from './resources/env-vars.js';
@@ -194,7 +195,7 @@ export type { CuratorSource, CuratorPrompt, CuratorRunInput, CuratorRunResult, C
194
195
  export { ReportsResource } from './resources/reports.js';
195
196
  export type { ReportTargetType, ReportStatus, CreateReportInput, ReportRecord, ResolveReportInput, } from './resources/reports.js';
196
197
  export { ModerationResource } from './resources/moderation.js';
197
- export type { ModerationTargetType, ModerationActionType, ModerationAppealStatus, ModerationPrinciple, PublicModerationAction, PublicModerationLog, OwnModerationAppeal, OwnModerationAction, OwnModerationLog, ApplyModerationActionInput, } from './resources/moderation.js';
198
+ export type { ModerationTargetType, ModerationActionType, ModerationAppealStatus, ModerationPrinciple, PublicModerationAction, PublicModerationLog, OwnModerationAppeal, OwnModerationAction, OwnModerationLog, ApplyModerationActionInput, AgentModerationMode, AgentModerationGrant, } from './resources/moderation.js';
198
199
  export { LinkPreviewResource } from './resources/link-preview.js';
199
200
  export { SEARCH_SECTIONS, SearchResource } from './resources/search.js';
200
201
  export type { SearchAnswer, SearchGroup, SearchOmitReason, SearchParams, SearchPerson, SearchRelated, SearchResponse, SearchSection, SearchStory, SearchTopic } from './resources/search.js';
@@ -214,8 +215,8 @@ export type { LinkedTokenWallet, PayoutState, TokenBalance, TokenLedgerEntry } f
214
215
  export { BoostsResource } from './resources/boosts.js';
215
216
  export type { BoostBalance, BoostBalanceResponse, BoostCampaign, BoostCampaignResponse, BoostEventResult, BoostLedgerEntry, BoostLedgerKind, BoostPlanCredit, BoostRejectionReason, BoostReviewStatus, BoostStatus, BoostTopupCheckout, BoostTopupConfirmation, BoostTopupLimits, } from './resources/boosts.js';
216
217
  export { ProtocolsResource } from './resources/protocols.js';
217
- export { PROTOCOL_ADAPTER_MATURITIES, PROTOCOL_CAPABILITY_OPERATIONS, PROTOCOL_CAPABILITY_STATUSES, } from './resources/protocols.js';
218
- export type { SocialProfile, SocialSubscription, SocialSubscriptionChange, SocialProfilePage, SocialRadar, SocialRadarCluster, SocialRadarMember, SocialRadarParams, SocialRadarPosts, SocialRadarStory, SocialRadarStoryArchive, SocialNetwork, SocialPost, SocialConversation, SocialTrend, SocialTrendProvenance, SocialTrends, SocialTrendSourceIndex, SocialTrendFeed, SocialPopularProfiles, SocialAuthorFeed, SocialFollowingFeed, SocialFollowingSource, SearchPublicProfilesParams, SearchPublicPostsParams, SocialPostSearch, HostedProtocolConnections, HostedProtocol, HostedProtocolIdentity, HostedNostrChallenge, HostedNostrIdentityProof, NativeNostrPrepareInput, NativeNostrPrepared, NativeNostrAccount, NativeNostrPostState, NativeNostrReceipt, NativeSocialProtocol, FederationIdentity, FederationIdentityView, NativeSocialAccount, NativeSocialCallbackInput, NativeReplyPrepareInput, NativePostPrepareInput, NativeMediaInput, NativeMediaItem, NativePostBatch, NativePostHistory, NativeDelegatedPostHistoryItem, NativeNostrPostHistoryItem, NativeFollowSourceInput, NativeFollowPrepareInput, NativeFollowTarget, NativeFollowReceipt, NativeFollowState, NativeFollowAccount, NativeFollowStates, NativeFollowReview, NativeLikeSourceInput, NativeRepostSourceInput, NativeLikePrepareInput, NativeRepostPrepareInput, NativeLikeTarget, NativeRepostTarget, NativeLikeReceipt, NativeRepostReceipt, NativeLikeState, NativeRepostState, NativeLikeReview, NativeRepostReview, NativeReplyReceipt, NativeReplyBatch, NativeReplyHistory, NativeDelegatedReplyHistoryItem, NativeNostrReplyResult, NativeNostrReplyHistoryItem, NativeReplyRemovalReceipt, NativeReplyRemovalPreview, SocialSourceRef, SocialSourceControl, SocialSourceControlDisplay, SocialSourceControlChange, HostedPostPreview, ConnectHostedIdentityInput, CreateHostedIdentityInput, CreatedHostedProtocolIdentity, PreviewHostedPostInput, PublishHostedPostInput, ListProtocolCandidatesParams, ProtocolAdapterMaturity, ProtocolAdapterMetadata, ProtocolCandidateListResponse, ProtocolCandidatePageMeta, ProtocolCapabilityOperation, ProtocolCapabilityStatus, ProtocolCollectionStatus, ProtocolIngestionReplayResult, ProtocolIngestionStatus, ProtocolQuarantinedCandidate, ProtocolRefreshResult, ProtocolSettings, ProtocolSettingsUpdateResult, ReplayProtocolIngestionJobInput, UpdateSettingsInput as UpdateProtocolSettingsInput, } from './resources/protocols.js';
218
+ export { PROTOCOL_ADAPTER_MATURITIES, PROTOCOL_CAPABILITY_OPERATIONS, PROTOCOL_CAPABILITY_STATUSES, viralItemHasVideo, } from './resources/protocols.js';
219
+ export type { SocialProfile, SocialBridge, SocialSubscription, SocialSubscriptionChange, SocialProfilePage, SocialRadar, SocialRadarCluster, SocialRadarMember, SocialRadarParams, SocialRadarPosts, SocialRadarStory, SocialRadarStoryArchive, ViralBaseline, ViralItem, ViralItemFields, ViralItemSpread, ViralItemVelocity, ViralLabel, ViralMindsCard, ViralMindsItem, ViralNetwork, ViralOpenProtocol, ViralProtocol, ViralProvider, ViralReadMeta, ViralReadParams, ViralReadResponse, ViralReadStatus, ViralRef, ViralSocialItem, SocialNetwork, SocialPost, SocialConversation, SocialTrend, SocialTrendProvenance, SocialTrends, SocialTrendSourceIndex, SocialTrendFeed, SocialPopularProfiles, SocialAuthorFeed, SocialFollowingFeed, SocialFollowingSource, SearchPublicProfilesParams, SearchPublicPostsParams, SocialPostSearch, HostedProtocolConnections, HostedProtocol, HostedProtocolIdentity, HostedNostrChallenge, HostedNostrIdentityProof, NativeNostrPrepareInput, NativeNostrPrepared, NativeNostrAccount, NativeNostrPostState, NativeNostrReceipt, NativeSocialProtocol, FederationIdentity, FederationIdentityView, NativeSocialAccount, NativeSocialCallbackInput, NativeReplyPrepareInput, NativePostPrepareInput, NativeMediaInput, NativeMediaItem, NativePostBatch, NativePostHistory, NativeDelegatedPostHistoryItem, NativeNostrPostHistoryItem, NativeFollowSourceInput, NativeFollowPrepareInput, NativeFollowTarget, NativeFollowReceipt, NativeFollowState, NativeFollowAccount, NativeFollowStates, NativeFollowReview, NativeLikeSourceInput, NativeRepostSourceInput, NativeLikePrepareInput, NativeRepostPrepareInput, NativeLikeTarget, NativeRepostTarget, NativeLikeReceipt, NativeRepostReceipt, NativeLikeState, NativeRepostState, NativeLikeReview, NativeRepostReview, NativeReplyReceipt, NativeReplyBatch, NativeReplyHistory, NativeDelegatedReplyHistoryItem, NativeNostrReplyResult, NativeNostrReplyHistoryItem, NativeReplyRemovalReceipt, NativeReplyRemovalPreview, SocialSourceRef, SocialSourceControl, SocialSourceControlDisplay, SocialSourceControlChange, HostedPostPreview, ConnectHostedIdentityInput, CreateHostedIdentityInput, CreatedHostedProtocolIdentity, PreviewHostedPostInput, PublishHostedPostInput, ListProtocolCandidatesParams, ProtocolAdapterMaturity, ProtocolAdapterMetadata, ProtocolCandidateListResponse, ProtocolCandidatePageMeta, ProtocolCapabilityOperation, ProtocolCapabilityStatus, ProtocolCollectionStatus, ProtocolIngestionReplayResult, ProtocolIngestionStatus, ProtocolQuarantinedCandidate, ProtocolRefreshResult, ProtocolSettings, ProtocolSettingsUpdateResult, ReplayProtocolIngestionJobInput, UpdateSettingsInput as UpdateProtocolSettingsInput, } from './resources/protocols.js';
219
220
  export { InboxResource } from './resources/inbox.js';
220
221
  export { NetworkResource } from './resources/network.js';
221
222
  export { SimulatorResource } from './resources/simulator.js';
@@ -240,7 +241,7 @@ export { TemplatesResource } from './resources/templates.js';
240
241
  export type { Template, CreateTemplateInput, UpdateTemplateInput, ListTemplatesParams } from './resources/templates.js';
241
242
  export { MediaResource } from './resources/media.js';
242
243
  export type { GenerateImageInput, GeneratedImage, ImageProvider } from './resources/media.js';
243
- export { RecursivError, AuthenticationError, AuthorizationError, LegacyHandoffError, NotFoundError, ValidationError, InsufficientCreditsError, RateLimitError, ConflictError, isActionRateLimitScope, } from './errors.js';
244
+ export { RecursivError, AuthenticationError, AuthorizationError, LegacyHandoffError, NotFoundError, ValidationError, InsufficientCreditsError, RateLimitError, ConflictError, isActionRateLimitScope, EmailNotSentError, } from './errors.js';
244
245
  export type { RateLimitDetails } from './errors.js';
245
246
  export { parseSSE } from './lib/sse.js';
246
247
  export * from './lib/webhooks.js';
package/dist/index.js CHANGED
@@ -261,7 +261,7 @@ export { UploadsResource } from './resources/uploads.js';
261
261
  export { WalletResource } from './resources/wallet.js';
262
262
  export { BoostsResource } from './resources/boosts.js';
263
263
  export { ProtocolsResource } from './resources/protocols.js';
264
- export { PROTOCOL_ADAPTER_MATURITIES, PROTOCOL_CAPABILITY_OPERATIONS, PROTOCOL_CAPABILITY_STATUSES, } from './resources/protocols.js';
264
+ export { PROTOCOL_ADAPTER_MATURITIES, PROTOCOL_CAPABILITY_OPERATIONS, PROTOCOL_CAPABILITY_STATUSES, viralItemHasVideo, } from './resources/protocols.js';
265
265
  export { InboxResource } from './resources/inbox.js';
266
266
  export { NetworkResource } from './resources/network.js';
267
267
  export { SimulatorResource } from './resources/simulator.js';
@@ -277,6 +277,6 @@ export { HumanInputResource } from './resources/human-input.js';
277
277
  export { SwarmsResource } from './resources/swarms.js';
278
278
  export { TemplatesResource } from './resources/templates.js';
279
279
  export { MediaResource } from './resources/media.js';
280
- export { RecursivError, AuthenticationError, AuthorizationError, LegacyHandoffError, NotFoundError, ValidationError, InsufficientCreditsError, RateLimitError, ConflictError, isActionRateLimitScope, } from './errors.js';
280
+ export { RecursivError, AuthenticationError, AuthorizationError, LegacyHandoffError, NotFoundError, ValidationError, InsufficientCreditsError, RateLimitError, ConflictError, isActionRateLimitScope, EmailNotSentError, } from './errors.js';
281
281
  export { parseSSE } from './lib/sse.js';
282
282
  export * from './lib/webhooks.js';
@@ -26,6 +26,68 @@ export interface AgentInboxParams {
26
26
  limit?: number;
27
27
  offset?: number;
28
28
  }
29
+ export interface AgentAskInput {
30
+ organization_id: string;
31
+ /** The person whose agent you are asking: name, first name, @username, email or user id. */
32
+ owner: string;
33
+ question: string;
34
+ /** How far back to read their record (1-90, default 7). */
35
+ since_days?: number;
36
+ /** false = read the record only, leave nothing in their agent's inbox. */
37
+ deliver?: boolean;
38
+ }
39
+ export interface AgentAskResult {
40
+ owner: {
41
+ id: string;
42
+ name: string | null;
43
+ username: string | null;
44
+ };
45
+ agents: Array<{
46
+ id: string;
47
+ name: string;
48
+ client: string | null;
49
+ }>;
50
+ window: {
51
+ since: string;
52
+ days: number;
53
+ };
54
+ record: {
55
+ claims: Array<{
56
+ task_id: string;
57
+ task_title: string;
58
+ task_status: string;
59
+ agent: string;
60
+ claim_status: string;
61
+ claimed_at: string;
62
+ completed_at: string | null;
63
+ released_at: string | null;
64
+ release_reason: string | null;
65
+ notes: string | null;
66
+ context_handoff: string | null;
67
+ pr_urls: string[] | null;
68
+ commits: string[] | null;
69
+ }>;
70
+ decisions: Array<{
71
+ id: string;
72
+ decision: string;
73
+ context: string | null;
74
+ tags: string[] | null;
75
+ project_id: string | null;
76
+ created_at: string;
77
+ }>;
78
+ };
79
+ question: {
80
+ delivered: true;
81
+ message_id: string;
82
+ to_agent_id: string;
83
+ to_agent_name: string;
84
+ from_agent_id: string;
85
+ } | {
86
+ delivered: false;
87
+ reason: string;
88
+ };
89
+ how_to_answer: string;
90
+ }
29
91
  export interface AgentLeaderboardParams extends PaginationParams {
30
92
  organization_id?: string;
31
93
  }
@@ -264,6 +326,14 @@ export declare class AgentsResource {
264
326
  }>>;
265
327
  /** Send a direct message to an agent */
266
328
  sendMessage(toAgentId: string, input: AgentMessageInput): Promise<SingleResponse<AgentMessage>>;
329
+ /**
330
+ * Ask a teammate's agent by naming its owner ("bill", "@ottman", an email).
331
+ * Returns that person's agents' work in the organization over the window
332
+ * (task claims with notes, handoffs, PRs and commits; decisions) and leaves
333
+ * the question in their agent's inbox; its reply arrives in yours as a
334
+ * `result` message with `metadata.in_reply_to`.
335
+ */
336
+ ask(input: AgentAskInput): Promise<SingleResponse<AgentAskResult>>;
267
337
  /** Get an agent's inbox (messages sent to them) */
268
338
  inbox(agentId: string, params?: AgentInboxParams): Promise<SingleResponse<AgentMessage[]>>;
269
339
  /** Get unread message count for an agent */
@@ -172,6 +172,16 @@ export class AgentsResource {
172
172
  sendMessage(toAgentId, input) {
173
173
  return this.client.post(`/agents/${toAgentId}/messages`, input);
174
174
  }
175
+ /**
176
+ * Ask a teammate's agent by naming its owner ("bill", "@ottman", an email).
177
+ * Returns that person's agents' work in the organization over the window
178
+ * (task claims with notes, handoffs, PRs and commits; decisions) and leaves
179
+ * the question in their agent's inbox; its reply arrives in yours as a
180
+ * `result` message with `metadata.in_reply_to`.
181
+ */
182
+ ask(input) {
183
+ return this.client.post('/agents/ask', input);
184
+ }
175
185
  /** Get an agent's inbox (messages sent to them) */
176
186
  inbox(agentId, params) {
177
187
  return this.client.get(`/agents/${agentId}/inbox`, params);
@@ -11,6 +11,10 @@ export type ForYouParams = {
11
11
  discovery?: boolean;
12
12
  /** `discover` ranks for Discover: fresher, beyond the member's follows, with their category order. */
13
13
  surface?: ForYouSurface;
14
+ /** A Politics choice, as `posts.list` takes it (`ListPostsParams.compass`); the page says what it did in `meta.compass_filter`. */
15
+ compass?: string;
16
+ /** `false` asks once and is never sent, as `posts.list` takes it (`ListPostsParams.retry`). */
17
+ retry?: boolean;
14
18
  };
15
19
  export interface ForYouMeta extends PaginationMeta {
16
20
  discovery?: DiscoveryLayout;
@@ -269,7 +273,7 @@ export declare class CuratorResource {
269
273
  * affinity, no model calls). Returns posts in the same shape as
270
274
  * `sdk.posts.list()`, so any feed UI renders it unchanged. Requires a
271
275
  * project-scoped key (the feed is per-app). Servers that predate `cursor`,
272
- * `discovery` and `surface` ignore them.
276
+ * `discovery` and `surface` ignore them. `retry: false` asks once and is never sent (`ListPostsParams.retry`).
273
277
  */
274
278
  forYou(params?: ForYouParams): Promise<PaginatedResponse<Post> & {
275
279
  meta: ForYouMeta;
@@ -48,9 +48,13 @@ export class CuratorResource {
48
48
  * affinity, no model calls). Returns posts in the same shape as
49
49
  * `sdk.posts.list()`, so any feed UI renders it unchanged. Requires a
50
50
  * project-scoped key (the feed is per-app). Servers that predate `cursor`,
51
- * `discovery` and `surface` ignore them.
51
+ * `discovery` and `surface` ignore them. `retry: false` asks once and is never sent (`ListPostsParams.retry`).
52
52
  */
53
53
  forYou(params = {}) {
54
- return this.client.get('/curator/foryou', params);
54
+ // Without `retry`, the same request older SDKs make.
55
+ if (!('retry' in params))
56
+ return this.client.get('/curator/foryou', params);
57
+ const { retry, ...query } = params;
58
+ return retry === false ? this.client.get('/curator/foryou', query, { retry: false }) : this.client.get('/curator/foryou', query);
55
59
  }
56
60
  }
@@ -1,10 +1,69 @@
1
1
  import type { HttpClient } from '../client.js';
2
- import type { DiscoveryLayout, FalloverStep, Post } from '../types.js';
2
+ import type { CompassFilterMeta, DiscoveryLayout, FalloverStep, MediaItem, Post } from '../types.js';
3
3
  import type { CuratorResource } from './curator.js';
4
4
  import type { DiscoveryLearning } from './discovery-learning.js';
5
5
  import type { PostsResource } from './posts.js';
6
6
  import type { ProtocolsResource, SocialNetwork, SocialPost, SocialRadar, SocialRadarPosts } from './protocols.js';
7
7
  export type DiscoveryRecommendationKind = 'groups' | 'people' | 'tags';
8
+ /** A page of Watch's Minds videos (`discovery.videos`). */
9
+ export interface WatchVideosParams {
10
+ /** A browse category id: the videos among the category's judged posts. Omitted: the whole app. */
11
+ category?: string;
12
+ /** Posts per page, 1 to 25 (default 12). */
13
+ limit?: number;
14
+ /** The `meta.next_cursor` of the page before. */
15
+ cursor?: string;
16
+ /** A position in the list to start a first read at (0 to 400); a later page sends `cursor`. */
17
+ offset?: number;
18
+ }
19
+ /** A media item as `discovery.videos` serves it: the poster, and the player's size, length and HLS address when known. */
20
+ export interface WatchVideoMedia extends MediaItem {
21
+ thumbnail_url: string | null;
22
+ /** Set while the file is not ready on the CDN; absent once it is. */
23
+ status?: string;
24
+ width?: number;
25
+ height?: number;
26
+ duration_sec?: number;
27
+ hls_url?: string;
28
+ }
29
+ /**
30
+ * A post as `discovery.videos` serves it: the `Post` shape (so a page is a valid `posts.list` page), with the engagement
31
+ * GET /posts sends (live votes plus imported likes, the replies the reader can open) and the viewer's own vote and repost.
32
+ */
33
+ export interface WatchVideoPost extends Post {
34
+ title: string | null;
35
+ is_nsfw: boolean;
36
+ score: number;
37
+ upvote_count: number;
38
+ downvote_count: number;
39
+ /** The viewer's own reaction ('upvote', 'downvote', ...); null for a visitor or when they have none. */
40
+ user_reaction: string | null;
41
+ reply_count: number;
42
+ reply_count_external: number;
43
+ unique_commenters: number;
44
+ reposts_count: number;
45
+ /** Id of the viewer's own repost of the post; delete it to undo the repost. */
46
+ reposted_by_viewer_id: string | null;
47
+ media: WatchVideoMedia[];
48
+ }
49
+ export interface WatchVideosResponse {
50
+ /** Public Minds video posts in hot order (with the viewer's own `user_reaction` and `reposted_by_viewer_id`). */
51
+ data: WatchVideoPost[];
52
+ meta: {
53
+ limit: number;
54
+ /** The position in the list this page started at. */
55
+ offset: number;
56
+ has_more: boolean;
57
+ /** Pass as `cursor` for the next page; null at the end of the list. */
58
+ next_cursor: string | null;
59
+ /** Positions in the list this page examined (a client paging by offset adds it to the offset). */
60
+ scanned: number;
61
+ /** When the shared list the page was cut from was built; null when the app has none. */
62
+ built_at: string | null;
63
+ /** `no_app`: the API key is bound to no app, so there is no list. */
64
+ reason?: 'no_app';
65
+ };
66
+ }
8
67
  /** What GET /discover/recommendations suggests: members, groups, tags, open-network accounts or World Today stories. */
9
68
  export type EntityKind = 'member' | 'group' | 'tag' | 'network_account' | 'story';
10
69
  /**
@@ -280,6 +339,12 @@ export interface DiscoveryFeedPage {
280
339
  * on servers that predate it.
281
340
  */
282
341
  fallover?: FalloverStep[];
342
+ /**
343
+ * What the session's Politics choice (`compass`, createFeed and createPersonalFeed) did to this page's Minds read:
344
+ * that read's `meta.compass_filter`, counts only. `unavailable` means the page was served unfiltered. Absent without
345
+ * a choice, on a page whose Minds read failed or was not made, and on servers that predate the filter.
346
+ */
347
+ compass?: CompassFilterMeta;
283
348
  }
284
349
  /** A gallery strip holds up to twelve image posts gathered from the whole page. */
285
350
  export declare const GALLERY_MAX_ITEMS = 12;
@@ -369,6 +434,16 @@ export declare class DiscoveryResource {
369
434
  }, options?: {
370
435
  retry?: boolean;
371
436
  }): Promise<CategorySurfaceResponse>;
437
+ /**
438
+ * Watch's Minds videos for the viewer: the app's public video posts in hot order (engagement decayed by age), from
439
+ * one list the server shares among every viewer of the app and rebuilds about once a minute, each post checked again
440
+ * on every read under the public floors (not NSFW, removed or hidden; no spam-labelled or adult author or group; the
441
+ * cached safety verdict) and the viewer's blocks and mutes. A video whose file is gone or not on the CDN yet is left
442
+ * out or marked `unplayable`. Page on `meta.next_cursor`. Needs posts:read; the app is the API key's project.
443
+ */
444
+ videos(params?: WatchVideosParams, options?: {
445
+ retry?: boolean;
446
+ }): Promise<WatchVideosResponse>;
372
447
  /**
373
448
  * A For you Discover feed: the member's For You ranking (asked for Discover's surface, with layouts and a
374
449
  * snapshot cursor) in a session's Minds slot. With a category, the session also reads open-network posts
@@ -380,12 +455,19 @@ export declare class DiscoveryResource {
380
455
  learning?: DiscoveryLearning;
381
456
  recommend?: DiscoveryRecommendationSources;
382
457
  category?: string;
458
+ compass?: string;
383
459
  }): DiscoveryFeedSession;
460
+ /**
461
+ * Discover's feed session. `compass` (a Politics choice, as `posts.list` takes it) goes on every Minds read, whose
462
+ * pages then carry `meta.compass_filter`; the open networks' reads and the radar are not filtered by it, so a view
463
+ * that leaves posts out (Hide politics, Choose sides) is best read with `source: 'minds'`.
464
+ */
384
465
  createFeed(options: {
385
466
  networks: SocialNetwork[];
386
467
  category?: string;
387
468
  source?: string;
388
469
  learning?: DiscoveryLearning;
389
470
  recommend?: DiscoveryRecommendationSources;
471
+ compass?: string;
390
472
  }): DiscoveryFeedSession;
391
473
  }
@@ -191,7 +191,12 @@ export class DiscoveryFeedSession {
191
191
  if (stream.protocol === 'minds') {
192
192
  // Discover leads with what is moving now: the REST hot order (engagement
193
193
  // decayed by age). Hot pages by offset; the server sends no cursor for it.
194
- const response = await this.posts.list({ ...params, sort: 'hot', ...(stream.cursor ? { cursor: stream.cursor } : { offset: stream.offset }) });
194
+ // Asked once, as the open networks are: a 503 retried twice with its Retry-After ran past this read's 15 s
195
+ // deadline, and the Minds lane failed (Bill 2026-10-01: "couldnt load discover minds posts didnt load"). The
196
+ // opt-out rides in the params (ListPostsParams.retry), which a reader handed to a session forwards to read the
197
+ // right page at all: a second argument was dropped by the Minds app's own wrappers (lib/discoverFeedResource,
198
+ // lib/compassStatus withCompass, lib/watchSource), which pass the params alone (review of #4047).
199
+ const response = await this.posts.list({ ...params, sort: 'hot', ...(stream.cursor ? { cursor: stream.cursor } : { offset: stream.offset }), retry: false });
195
200
  stream.offset += response.meta.scanned ?? response.meta.limit;
196
201
  const next = response.meta.next_cursor ?? undefined;
197
202
  stream.done = !response.meta.has_more || !!next && (next === stream.cursor || stream.used.includes(next));
@@ -202,7 +207,8 @@ export class DiscoveryFeedSession {
202
207
  if (Array.isArray(response.meta.fallover))
203
208
  stream.fallover = response.meta.fallover;
204
209
  return { items: response.data.map(post => ({ key: key('minds', post.id), kind: 'minds', post })),
205
- layout: response.meta.discovery ?? ordinary, partial: response.meta.category_filter?.status === 'partial' };
210
+ layout: response.meta.discovery ?? ordinary, partial: response.meta.category_filter?.status === 'partial',
211
+ ...(response.meta.compass_filter ? { compass: response.meta.compass_filter } : {}) };
206
212
  }
207
213
  const validatePosts = (data) => {
208
214
  if (data.coverage !== 'provider_ranked_window' || data.items.length > 25 || data.items.some(post => post.protocol !== stream.protocol || post.author.protocol !== stream.protocol))
@@ -402,9 +408,11 @@ export class DiscoveryFeedSession {
402
408
  if (cluster)
403
409
  modules.splice(page === 0 ? 0 : Math.min(2, modules.length), 0, this.radarModule(page, cluster, clusterItems));
404
410
  const fallover = this.streams.find(stream => stream.protocol === 'minds')?.fallover;
411
+ // Only the Minds read carries what a Politics choice did (the open networks' reads are not filtered by it).
412
+ const compass = reads.find(read => read.compass)?.compass;
405
413
  return { modules, hasMore: this.streams.some(stream => !stream.done && !stream.failed),
406
414
  failedSources: this.streams.filter(stream => stream.failed).map(stream => stream.protocol), partialCategories: reads.some(read => read.partial),
407
- ...(fallover ? { fallover } : {}) };
415
+ ...(fallover ? { fallover } : {}), ...(compass ? { compass } : {}) };
408
416
  }
409
417
  /** Page 0: the top breaking cluster, else the top cluster. Every second later page: the next unused story or viral cluster. */
410
418
  async radarCluster(page) {
@@ -583,6 +591,18 @@ export class DiscoveryResource {
583
591
  throw new Error('category needs the HTTP client');
584
592
  return this.http.get('/discover/category', params, options);
585
593
  }
594
+ /**
595
+ * Watch's Minds videos for the viewer: the app's public video posts in hot order (engagement decayed by age), from
596
+ * one list the server shares among every viewer of the app and rebuilds about once a minute, each post checked again
597
+ * on every read under the public floors (not NSFW, removed or hidden; no spam-labelled or adult author or group; the
598
+ * cached safety verdict) and the viewer's blocks and mutes. A video whose file is gone or not on the CDN yet is left
599
+ * out or marked `unplayable`. Page on `meta.next_cursor`. Needs posts:read; the app is the API key's project.
600
+ */
601
+ videos(params = {}, options) {
602
+ if (!this.http)
603
+ throw new Error('videos needs the HTTP client');
604
+ return this.http.get('/discover/videos', { ...params }, options);
605
+ }
586
606
  /**
587
607
  * A For you Discover feed: the member's For You ranking (asked for Discover's surface, with layouts and a
588
608
  * snapshot cursor) in a session's Minds slot. With a category, the session also reads open-network posts
@@ -593,10 +613,13 @@ export class DiscoveryResource {
593
613
  const curator = this.curator;
594
614
  if (!curator)
595
615
  throw new Error('createPersonalFeed needs the curator resource');
616
+ const compass = options.compass;
596
617
  const list = {
597
- // The session's `sort` and `category` are Discover's hot-order knobs; the ranker ignores them.
618
+ // The session's `sort` and `category` are Discover's hot-order knobs; the ranker ignores them. A Politics choice
619
+ // goes on every ranker read.
598
620
  list: async (params) => {
599
- const response = await curator.forYou({ limit: params?.limit, offset: params?.offset, cursor: params?.cursor, discovery: true, surface: 'discover' });
621
+ const response = await curator.forYou({ limit: params?.limit, offset: params?.offset, cursor: params?.cursor, discovery: true, surface: 'discover',
622
+ ...(compass ? { compass } : {}), ...(params?.retry === undefined ? {} : { retry: params.retry }) });
600
623
  // A server that predates the cursor says has_more: false on a partial ranked page although its
601
624
  // continuation serves the next offset. A cursor-aware server always says `personalized`, and its
602
625
  // has_more is the end of the feed.
@@ -606,9 +629,16 @@ export class DiscoveryResource {
606
629
  };
607
630
  return new DiscoveryFeedSession(list, this.protocols, options.category ? options.networks : [], options.category, options.category ? 'all' : 'minds', options.learning, options.recommend);
608
631
  }
632
+ /**
633
+ * Discover's feed session. `compass` (a Politics choice, as `posts.list` takes it) goes on every Minds read, whose
634
+ * pages then carry `meta.compass_filter`; the open networks' reads and the radar are not filtered by it, so a view
635
+ * that leaves posts out (Hide politics, Choose sides) is best read with `source: 'minds'`.
636
+ */
609
637
  createFeed(options) {
638
+ const compass = options.compass;
639
+ const posts = compass ? { list: params => this.posts.list({ ...params, compass }) } : this.posts;
610
640
  // The radar rides along by default; an older server answers 404 and the session simply shows no story module.
611
- return new DiscoveryFeedSession(this.posts, this.protocols, options.networks, options.category, options.source, options.learning, options.recommend, params => this.protocols.getPublicRadar(params, NO_RETRY), { social: params => this.protocols.getPublicRadarPosts(params, NO_RETRY), minds: id => this.posts.get(id) });
641
+ return new DiscoveryFeedSession(posts, this.protocols, options.networks, options.category, options.source, options.learning, options.recommend, params => this.protocols.getPublicRadar(params, NO_RETRY), { social: params => this.protocols.getPublicRadarPosts(params, NO_RETRY), minds: id => this.posts.get(id) });
612
642
  }
613
643
  }
614
644
  /** A read bounded to three seconds; a late or failed one resolves to `fallback`, never an error. */