@recursiv/sdk 0.7.28 → 0.7.30

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,6 +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`, `grantProjectAccess`, `conversations` |
78
+ | `r.e2ee` | `registerDevice`, `devices`, `revokeDevice`, `publishKeyPackages`, `claimKeyPackage`, `appendEvent`, `events`, `acknowledge` (flag-gated E2EE v2 transport) |
78
79
  | `r.brain` | `sendMessage` |
79
80
  | `r.commands` | `execute`, `gatePrompt` |
80
81
  | `r.projectBrain` | `tasks`, `decisions`, `milestones`, `completeTask`, `usage` |
@@ -110,7 +111,7 @@ for await (const chunk of r.agents.chatStream(agent.id, { message: 'Hello!' }))
110
111
  | `r.admin` | User management, stats, content reports (`listReports`, `resolveReport`, `dismissReport`) |
111
112
  | `r.dispatcher` | Task queue, claims, signals, outcomes, webhooks |
112
113
  | `r.goals` | Org goals with task rollups (group dispatcher tasks) |
113
- | `r.protocols` | Private Minds safety: `sourceControls` (current enablement and saved choices), `setSourceControl`, `onSourceControlChange`, `reportSource`. Delegated accounts: `nativeSocialAccounts`, `startNativeSocialConnection`, `finishNativeSocialConnection`, `disconnectNativeSocialAccount`. Reviewed replies and optional linked posts: `prepareNativeReply`, `submitNativeReply`, `nativeReplyStatus`, `nativeReplyHistory`, `prepareNativeReplyRemoval`, `removeNativeReply` (require deployed API support and app enablement). Public reading: `publicNetworks`, `searchPublicProfiles`, `getPublicProfile`, `getPublicAuthorFeed`, `getFollowingFeed`, `getPublicConversation`, `getPublicTrends`, `getPublicTrendFeed`; 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` |
114
+ | `r.protocols` | Private Minds safety: `sourceControls` (current enablement and saved choices), `setSourceControl`, `onSourceControlChange`, `reportSource`. Delegated accounts: `nativeSocialAccounts`, `startNativeSocialConnection`, `finishNativeSocialConnection`, `disconnectNativeSocialAccount`. 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: `publicNetworks`, `searchPublicProfiles`, `getPublicProfile`, `getPublicAuthorFeed`, `getFollowingFeed`, `getPublicConversation`, `getPublicTrends`, `getPublicTrendFeed`; 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` |
114
115
 
115
116
  Full reference: see [llm.md](./llm.md) (ships with this package) or the type definitions in `dist/`.
116
117
 
@@ -570,3 +571,21 @@ Keep the cursor opaque and discard it when the app or signed-in user changes.
570
571
  History order is fixed by creation time, so later delivery updates do not move
571
572
  older entries across page boundaries. Reopen each result for its current saved
572
573
  state and obtain a fresh review before a new write.
574
+
575
+ ### Public protocol post links
576
+
577
+ Visitors can read a public conversation in an explicitly configured app without signing in:
578
+
579
+ ```ts
580
+ const visitor = new Recursiv({ anonymous: true });
581
+ const { data: networks } = await visitor.protocols.sharedNetworks(projectId);
582
+ const { data: conversation } = await visitor.protocols.getSharedConversation(projectId, {
583
+ protocol: 'nostr', post: publicEventId,
584
+ });
585
+ ```
586
+
587
+ These reads apply app moderation, provider public-visibility/deletion checks and bounded
588
+ per-IP/app budgets. They return no subscriptions, personal safety preferences, account
589
+ connections or signing authority. Missing configuration or unavailable safety checks fail
590
+ closed. Signed-in members continue using `getPublicConversation` to apply personal
591
+ blocks/mutes as well. All reply and account endpoints still require authentication.
package/dist/index.d.ts CHANGED
@@ -3,6 +3,7 @@ import { PostsResource } from './resources/posts.js';
3
3
  import { UsersResource } from './resources/users.js';
4
4
  import { CommunitiesResource } from './resources/communities.js';
5
5
  import { ChatResource } from './resources/chat.js';
6
+ import { E2eeResource } from './resources/e2ee.js';
6
7
  import { ProjectsResource } from './resources/projects.js';
7
8
  import { AgentsResource } from './resources/agents.js';
8
9
  import { EvidenceResource } from './resources/evidence.js';
@@ -64,6 +65,8 @@ export declare class Recursiv {
64
65
  readonly users: UsersResource;
65
66
  readonly communities: CommunitiesResource;
66
67
  readonly chat: ChatResource;
68
+ /** E2EE v2 ciphertext transport (flag-gated). */
69
+ readonly e2ee: E2eeResource;
67
70
  readonly projects: ProjectsResource;
68
71
  readonly agents: AgentsResource;
69
72
  readonly evidence: EvidenceResource;
@@ -136,6 +139,8 @@ export { PostsResource } from './resources/posts.js';
136
139
  export { UsersResource } from './resources/users.js';
137
140
  export { CommunitiesResource } from './resources/communities.js';
138
141
  export { ChatResource } from './resources/chat.js';
142
+ export { E2eeResource } from './resources/e2ee.js';
143
+ export type { E2eeDevice, E2eeEnvelope, E2eeEvent } from './resources/e2ee.js';
139
144
  export { ProjectsResource } from './resources/projects.js';
140
145
  export { AgentsResource, SUPPORTED_MODELS } from './resources/agents.js';
141
146
  export { EvidenceResource } from './resources/evidence.js';
@@ -189,7 +194,7 @@ export type { UploadMediaInput, UploadUrlInput, UploadUrlResult } from './resour
189
194
  export { WalletResource } from './resources/wallet.js';
190
195
  export { ProtocolsResource } from './resources/protocols.js';
191
196
  export { PROTOCOL_ADAPTER_MATURITIES, PROTOCOL_CAPABILITY_OPERATIONS, PROTOCOL_CAPABILITY_STATUSES, } from './resources/protocols.js';
192
- export type { SocialProfile, SocialSubscription, SocialSubscriptionChange, SocialProfilePage, SocialNetwork, SocialPost, SocialConversation, SocialTrend, SocialTrendProvenance, SocialTrends, SocialTrendFeed, SocialAuthorFeed, SocialFollowingFeed, SocialFollowingSource, SearchPublicProfilesParams, HostedProtocolConnections, HostedProtocol, HostedProtocolIdentity, HostedNostrChallenge, HostedNostrIdentityProof, NativeNostrPrepareInput, NativeNostrPrepared, NativeNostrAccount, NativeNostrPostState, NativeNostrReceipt, NativeSocialProtocol, FederationIdentity, FederationIdentityView, NativeSocialAccount, NativeSocialCallbackInput, NativeReplyPrepareInput, NativeReplyReceipt, NativeReplyBatch, NativeReplyHistory, NativeDelegatedReplyHistoryItem, NativeNostrReplyResult, NativeNostrReplyHistoryItem, NativeReplyRemovalReceipt, NativeReplyRemovalPreview, SocialSourceRef, SocialSourceControl, SocialSourceControlChange, HostedPostPreview, ConnectHostedIdentityInput, PreviewHostedPostInput, PublishHostedPostInput, ListProtocolCandidatesParams, ProtocolAdapterMaturity, ProtocolAdapterMetadata, ProtocolCandidateListResponse, ProtocolCandidatePageMeta, ProtocolCapabilityOperation, ProtocolCapabilityStatus, ProtocolCollectionStatus, ProtocolQuarantinedCandidate, ProtocolRefreshResult, ProtocolSettings, ProtocolSettingsUpdateResult, UpdateSettingsInput as UpdateProtocolSettingsInput, } from './resources/protocols.js';
197
+ export type { SocialProfile, SocialSubscription, SocialSubscriptionChange, SocialProfilePage, SocialNetwork, SocialPost, SocialConversation, SocialTrend, SocialTrendProvenance, SocialTrends, SocialTrendFeed, SocialPopularProfiles, SocialAuthorFeed, SocialFollowingFeed, SocialFollowingSource, SearchPublicProfilesParams, HostedProtocolConnections, HostedProtocol, HostedProtocolIdentity, HostedNostrChallenge, HostedNostrIdentityProof, NativeNostrPrepareInput, NativeNostrPrepared, NativeNostrAccount, NativeNostrPostState, NativeNostrReceipt, NativeSocialProtocol, FederationIdentity, FederationIdentityView, NativeSocialAccount, NativeSocialCallbackInput, NativeReplyPrepareInput, NativeReplyReceipt, NativeReplyBatch, NativeReplyHistory, NativeDelegatedReplyHistoryItem, NativeNostrReplyResult, NativeNostrReplyHistoryItem, NativeReplyRemovalReceipt, NativeReplyRemovalPreview, SocialSourceRef, SocialSourceControl, SocialSourceControlChange, HostedPostPreview, ConnectHostedIdentityInput, PreviewHostedPostInput, PublishHostedPostInput, ListProtocolCandidatesParams, ProtocolAdapterMaturity, ProtocolAdapterMetadata, ProtocolCandidateListResponse, ProtocolCandidatePageMeta, ProtocolCapabilityOperation, ProtocolCapabilityStatus, ProtocolCollectionStatus, ProtocolIngestionReplayResult, ProtocolIngestionStatus, ProtocolQuarantinedCandidate, ProtocolRefreshResult, ProtocolSettings, ProtocolSettingsUpdateResult, ReplayProtocolIngestionJobInput, UpdateSettingsInput as UpdateProtocolSettingsInput, } from './resources/protocols.js';
193
198
  export { InboxResource } from './resources/inbox.js';
194
199
  export { NetworkResource } from './resources/network.js';
195
200
  export { SimulatorResource } from './resources/simulator.js';
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@ import { PostsResource } from './resources/posts.js';
4
4
  import { UsersResource } from './resources/users.js';
5
5
  import { CommunitiesResource } from './resources/communities.js';
6
6
  import { ChatResource } from './resources/chat.js';
7
+ import { E2eeResource } from './resources/e2ee.js';
7
8
  import { ProjectsResource } from './resources/projects.js';
8
9
  import { AgentsResource } from './resources/agents.js';
9
10
  import { EvidenceResource } from './resources/evidence.js';
@@ -65,6 +66,8 @@ export class Recursiv {
65
66
  users;
66
67
  communities;
67
68
  chat;
69
+ /** E2EE v2 ciphertext transport (flag-gated). */
70
+ e2ee;
68
71
  projects;
69
72
  agents;
70
73
  evidence;
@@ -125,6 +128,7 @@ export class Recursiv {
125
128
  this.users = new UsersResource(client);
126
129
  this.communities = new CommunitiesResource(client);
127
130
  this.chat = new ChatResource(client);
131
+ this.e2ee = new E2eeResource(client);
128
132
  this.projects = new ProjectsResource(client);
129
133
  this.agents = new AgentsResource(client);
130
134
  this.evidence = new EvidenceResource(client);
@@ -200,6 +204,7 @@ export { PostsResource } from './resources/posts.js';
200
204
  export { UsersResource } from './resources/users.js';
201
205
  export { CommunitiesResource } from './resources/communities.js';
202
206
  export { ChatResource } from './resources/chat.js';
207
+ export { E2eeResource } from './resources/e2ee.js';
203
208
  export { ProjectsResource } from './resources/projects.js';
204
209
  export { AgentsResource, SUPPORTED_MODELS } from './resources/agents.js';
205
210
  export { EvidenceResource } from './resources/evidence.js';
@@ -0,0 +1,93 @@
1
+ import type { HttpClient } from '../client.js';
2
+ import type { SingleResponse } from '../types.js';
3
+ /** Public device record from the E2EE v2 directory. Never contains private keys. */
4
+ export interface E2eeDevice {
5
+ id: string;
6
+ userId: string;
7
+ deviceName: string;
8
+ realm: string;
9
+ signingKey: string;
10
+ credential: unknown;
11
+ isRoot: boolean;
12
+ status: 'active' | 'revoked' | string;
13
+ revokedAt: string | null;
14
+ }
15
+ /** Opaque `recursiv-mls-v2-draft1` envelope with base64 MLS bytes. */
16
+ export interface E2eeEnvelope {
17
+ profile: 'recursiv-mls-v2-draft1';
18
+ realm: string;
19
+ conversation: string;
20
+ event_id: string;
21
+ sender_device: string;
22
+ kind: 'application' | 'commit';
23
+ mls: string;
24
+ }
25
+ export interface E2eeEvent {
26
+ seq: number;
27
+ kind: string;
28
+ envelope: E2eeEnvelope;
29
+ /** Present only for the device the Welcome was issued to. */
30
+ welcome: string | null;
31
+ createdAt: string;
32
+ }
33
+ /**
34
+ * E2EE v2 ciphertext transport. The server relays envelopes and public
35
+ * credentials only; encryption happens in the shared core on each device.
36
+ * Requires the `e2eeV2` network feature flag.
37
+ */
38
+ export declare class E2eeResource {
39
+ private client;
40
+ constructor(client: HttpClient);
41
+ registerDevice(input: {
42
+ device_name: string;
43
+ realm: string;
44
+ signing_key: string;
45
+ credential: Record<string, unknown>;
46
+ }): Promise<SingleResponse<E2eeDevice>>;
47
+ devices(params?: {
48
+ user_id?: string;
49
+ }): Promise<{
50
+ data: E2eeDevice[];
51
+ }>;
52
+ revokeDevice(deviceId: string): Promise<SingleResponse<E2eeDevice>>;
53
+ publishKeyPackages(input: {
54
+ device_id: string;
55
+ packages: string[];
56
+ }): Promise<SingleResponse<{
57
+ published: number;
58
+ }>>;
59
+ claimKeyPackage(input: {
60
+ device_id: string;
61
+ target_device_id: string;
62
+ }): Promise<SingleResponse<{
63
+ keyPackageId: string;
64
+ bytes: string;
65
+ device: E2eeDevice;
66
+ }>>;
67
+ appendEvent(conversationId: string, input: {
68
+ device_id: string;
69
+ envelope: E2eeEnvelope;
70
+ welcome?: string;
71
+ welcome_for_device_id?: string;
72
+ expected_commit_seq?: number;
73
+ }): Promise<SingleResponse<{
74
+ seq: number;
75
+ duplicate: boolean;
76
+ commitSeq: number;
77
+ }>>;
78
+ events(conversationId: string, params: {
79
+ device_id: string;
80
+ after_seq?: number;
81
+ limit?: number;
82
+ }): Promise<SingleResponse<{
83
+ headSeq: number;
84
+ commitSeq: number;
85
+ events: E2eeEvent[];
86
+ }>>;
87
+ acknowledge(conversationId: string, input: {
88
+ device_id: string;
89
+ seq: number;
90
+ }): Promise<SingleResponse<{
91
+ ackedSeq: number;
92
+ }>>;
93
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * E2EE v2 ciphertext transport. The server relays envelopes and public
3
+ * credentials only; encryption happens in the shared core on each device.
4
+ * Requires the `e2eeV2` network feature flag.
5
+ */
6
+ export class E2eeResource {
7
+ client;
8
+ constructor(client) {
9
+ this.client = client;
10
+ }
11
+ registerDevice(input) {
12
+ return this.client.post('/e2ee/devices', input);
13
+ }
14
+ devices(params) {
15
+ return this.client.get('/e2ee/devices', params);
16
+ }
17
+ revokeDevice(deviceId) {
18
+ return this.client.post(`/e2ee/devices/${encodeURIComponent(deviceId)}/revoke`);
19
+ }
20
+ publishKeyPackages(input) {
21
+ return this.client.post('/e2ee/key-packages', input);
22
+ }
23
+ claimKeyPackage(input) {
24
+ return this.client.post('/e2ee/key-packages/claim', input);
25
+ }
26
+ appendEvent(conversationId, input) {
27
+ // Retries are safe: the server is idempotent on (conversation, device, event id).
28
+ return this.client.post(`/e2ee/conversations/${encodeURIComponent(conversationId)}/events`, input, { retry: true });
29
+ }
30
+ events(conversationId, params) {
31
+ return this.client.get(`/e2ee/conversations/${encodeURIComponent(conversationId)}/events`, params);
32
+ }
33
+ acknowledge(conversationId, input) {
34
+ return this.client.post(`/e2ee/conversations/${encodeURIComponent(conversationId)}/ack`, input);
35
+ }
36
+ }
@@ -130,6 +130,57 @@ export interface ProtocolCandidateDecisionResult {
130
130
  candidate: ProtocolQuarantinedCandidate;
131
131
  decision_id: string;
132
132
  }
133
+ /** Operator view of the durable ingestion runtime for the authenticated app (goal ed3759f0 row 7). */
134
+ export interface ProtocolIngestionStatus {
135
+ /** Exactly one ingestion writer per process: the legacy collector or the runtime. */
136
+ mode: 'collector' | 'runtime';
137
+ enabled: boolean;
138
+ simulation: boolean;
139
+ /** Process-wide PROTOCOL_INGESTION_KILL_SWITCH. */
140
+ global_kill_switch: boolean;
141
+ controls_valid: boolean;
142
+ /** Whether this app's tuple is on the PROTOCOL_INGESTION_PROJECTS allow-list. */
143
+ project_allowed: boolean;
144
+ /** This app's operator kill switch (protocol_collection_setting.kill_switch). */
145
+ kill_switch: boolean;
146
+ jobs: {
147
+ queued: number;
148
+ leased: number;
149
+ done: number;
150
+ dead: number;
151
+ };
152
+ quotas: Array<{
153
+ provider_id: string;
154
+ window: 'run' | 'hour' | 'day';
155
+ killed: boolean;
156
+ circuit_open_until: string | null;
157
+ circuit_failures: number;
158
+ consumed: {
159
+ calls: number;
160
+ items: number;
161
+ bytes: number;
162
+ };
163
+ ceilings: {
164
+ calls: number | null;
165
+ items: number | null;
166
+ bytes: number | null;
167
+ };
168
+ }>;
169
+ lanes: Array<{
170
+ provider_id: string;
171
+ source_key: string;
172
+ source_subscription_key: string;
173
+ active: boolean;
174
+ }>;
175
+ }
176
+ export interface ReplayProtocolIngestionJobInput {
177
+ /** Operator note recorded on the audit row; 8 to 1024 characters. */
178
+ reason: string;
179
+ }
180
+ export interface ProtocolIngestionReplayResult {
181
+ job_id: string;
182
+ status: 'replayed';
183
+ }
133
184
  /** App/network scope comes only from the authenticated project-bound key. */
134
185
  export interface ListProtocolCandidatesParams {
135
186
  protocol?: string;
@@ -497,7 +548,7 @@ export interface SocialNetwork {
497
548
  url: string;
498
549
  };
499
550
  coverage: string;
500
- capabilities: Array<'profile_search' | 'profile_read' | 'original_author_posts' | 'minds_subscription' | 'following_feed' | 'conversation_read' | 'public_trends' | 'trend_feed'>;
551
+ capabilities: Array<'profile_search' | 'profile_read' | 'original_author_posts' | 'minds_subscription' | 'following_feed' | 'conversation_read' | 'public_trends' | 'trend_feed' | 'popular_accounts'>;
501
552
  profileSearchPagination: boolean;
502
553
  conversationCoverage?: string;
503
554
  }
@@ -598,6 +649,24 @@ export interface SocialTrendFeed extends SocialTrendProvenance {
598
649
  items: SocialPost[];
599
650
  cursor: string | null;
600
651
  }
652
+ /** Provider-ranked accounts. `source` says whether this page is the app's hourly index snapshot or a live read. */
653
+ export interface SocialPopularProfiles {
654
+ items: SocialProfile[];
655
+ cursor: string | null;
656
+ provenance: {
657
+ provider: {
658
+ name: string;
659
+ url: string;
660
+ };
661
+ /** The provider's own ranking label, never a Minds popularity score. */
662
+ ranking: string;
663
+ fetchedAt: string;
664
+ coverage: 'provider_ranked_window';
665
+ source: 'index' | 'live';
666
+ /** Provider read time of the served snapshot; null for live reads. */
667
+ snapshotAt: string | null;
668
+ };
669
+ }
601
670
  export interface SocialConversation {
602
671
  focus: SocialPost;
603
672
  /** Oldest available ancestor first, ending with the immediate parent. */
@@ -634,6 +703,14 @@ export declare class ProtocolsResource {
634
703
  private subscriptionChanged;
635
704
  /** Confirmed safety choices from this authenticated SDK instance. Unsubscribe when its account or view changes. */
636
705
  onSourceControlChange(listener: (change: SocialSourceControlChange) => void): () => void;
706
+ /** Public-only conversation catalog for a configured app. Works with an anonymous client. */
707
+ sharedNetworks(projectId: string): Promise<SingleResponse<SocialNetwork[]>>;
708
+ /** Keyless public post read with app moderation; no account state or connected write authority. */
709
+ getSharedConversation(projectId: string, params: {
710
+ protocol: string;
711
+ post: string;
712
+ readRef?: string;
713
+ }): Promise<SingleResponse<SocialConversation>>;
637
714
  publicNetworks(): Promise<SingleResponse<SocialNetwork[]>>;
638
715
  /** Query the live public network. It does not import users or posts. */
639
716
  searchPublicProfiles(params: SearchPublicProfilesParams): Promise<SingleResponse<SocialProfilePage>>;
@@ -669,6 +746,12 @@ export declare class ProtocolsResource {
669
746
  limit?: number;
670
747
  cursor?: string;
671
748
  }): Promise<SingleResponse<SocialTrendFeed>>;
749
+ /** Provider-ranked public accounts with explicit provenance; served from the app's index when a fresh snapshot exists. */
750
+ getPublicPopularProfiles(params: {
751
+ protocol: string;
752
+ limit?: number;
753
+ cursor?: string;
754
+ }): Promise<SingleResponse<SocialPopularProfiles>>;
672
755
  /** Prepare one exact shared reply preview. Keep the mutation ID across uncertain responses. */
673
756
  prepareNativeReply(input: NativeReplyPrepareInput): Promise<SingleResponse<NativeReplyBatch>>;
674
757
  /** Send only explicitly approved delivery IDs from this preview. Repeated submissions reconcile; they do not replay a write. */
@@ -832,6 +915,26 @@ export declare class ProtocolsResource {
832
915
  * an agent key may only reject or escalate; escalation needs a reason.
833
916
  */
834
917
  decideCandidate(id: string, input: DecideProtocolCandidateInput): Promise<SingleResponse<ProtocolCandidateDecisionResult>>;
918
+ /**
919
+ * Read the ingestion runtime status for the key's app: mode, switches, job
920
+ * counts, quota ledger and lanes. Read-only. Requires the app's
921
+ * content_intelligence_admin binding (404 otherwise) and the same
922
+ * project-admin authorization as listCandidates.
923
+ */
924
+ ingestionStatus(): Promise<SingleResponse<ProtocolIngestionStatus>>;
925
+ /** Turn the app's ingestion kill switch on: no reservation, screening or approval proceeds. */
926
+ killIngestion(): Promise<SingleResponse<{
927
+ kill_switch: true;
928
+ }>>;
929
+ /** Turn the app's ingestion kill switch off. Does not enable the runtime by itself. */
930
+ resumeIngestion(): Promise<SingleResponse<{
931
+ kill_switch: false;
932
+ }>>;
933
+ /**
934
+ * Requeue one dead ingestion job. Refused (409) while the runtime is
935
+ * disabled, simulating or killed; 404 when no dead job matches in this app.
936
+ */
937
+ replayIngestionJob(jobId: string, input: ReplayProtocolIngestionJobInput): Promise<SingleResponse<ProtocolIngestionReplayResult>>;
835
938
  /**
836
939
  * Read one quarantined candidate under the same authorization as listCandidates.
837
940
  * Empty and URL dot-segment IDs reject with ValidationError before any request;
@@ -60,6 +60,16 @@ export class ProtocolsResource {
60
60
  this.sourceControlListeners.add(listener);
61
61
  return () => { this.sourceControlListeners.delete(listener); };
62
62
  }
63
+ /** Public-only conversation catalog for a configured app. Works with an anonymous client. */
64
+ sharedNetworks(projectId) {
65
+ return this.client.get(`/protocols/public/${encodeURIComponent(projectId)}/networks`);
66
+ }
67
+ /** Keyless public post read with app moderation; no account state or connected write authority. */
68
+ getSharedConversation(projectId, params) {
69
+ return this.client.get(`/protocols/public/${encodeURIComponent(projectId)}/conversation`, {
70
+ protocol: params.protocol, post: params.post, readRef: params.readRef,
71
+ });
72
+ }
63
73
  publicNetworks() {
64
74
  return this.client.get('/protocols/social/networks');
65
75
  }
@@ -96,6 +106,10 @@ export class ProtocolsResource {
96
106
  getPublicTrendFeed(params) {
97
107
  return this.client.get('/protocols/social/trend-feed', { protocol: params.protocol, topic: params.topic, limit: params.limit, cursor: params.cursor });
98
108
  }
109
+ /** Provider-ranked public accounts with explicit provenance; served from the app's index when a fresh snapshot exists. */
110
+ getPublicPopularProfiles(params) {
111
+ return this.client.get('/protocols/social/popular-profiles', { protocol: params.protocol, limit: params.limit, cursor: params.cursor });
112
+ }
99
113
  /** Prepare one exact shared reply preview. Keep the mutation ID across uncertain responses. */
100
114
  prepareNativeReply(input) {
101
115
  return this.client.post('/protocols/social/replies/prepare', { clientMutationId: input.clientMutationId, text: input.text,
@@ -308,6 +322,30 @@ export class ProtocolsResource {
308
322
  async decideCandidate(id, input) {
309
323
  return this.client.post(`/protocols/candidates/${encodeURIComponent(assertCandidateId(id))}/decision`, input);
310
324
  }
325
+ /**
326
+ * Read the ingestion runtime status for the key's app: mode, switches, job
327
+ * counts, quota ledger and lanes. Read-only. Requires the app's
328
+ * content_intelligence_admin binding (404 otherwise) and the same
329
+ * project-admin authorization as listCandidates.
330
+ */
331
+ ingestionStatus() {
332
+ return this.client.get('/protocols/ingestion/status');
333
+ }
334
+ /** Turn the app's ingestion kill switch on: no reservation, screening or approval proceeds. */
335
+ killIngestion() {
336
+ return this.client.post('/protocols/ingestion/kill', {});
337
+ }
338
+ /** Turn the app's ingestion kill switch off. Does not enable the runtime by itself. */
339
+ resumeIngestion() {
340
+ return this.client.post('/protocols/ingestion/resume', {});
341
+ }
342
+ /**
343
+ * Requeue one dead ingestion job. Refused (409) while the runtime is
344
+ * disabled, simulating or killed; 404 when no dead job matches in this app.
345
+ */
346
+ async replayIngestionJob(jobId, input) {
347
+ return this.client.post(`/protocols/ingestion/replay/${encodeURIComponent(assertCandidateId(jobId))}`, input);
348
+ }
311
349
  /**
312
350
  * Read one quarantined candidate under the same authorization as listCandidates.
313
351
  * Empty and URL dot-segment IDs reject with ValidationError before any request;
package/llm.md CHANGED
@@ -476,6 +476,19 @@ const { data: msgs } = await r.chat.messages(dm.id, { limit: 50 })
476
476
 
477
477
  Methods: `conversations`, `communityConversation`, `conversation`, `messages`, `send`, `dm`, `createGroup`, `deleteConversation`, `reactToMessage`, `editMessage`, `deleteMessage`, `markAsRead`, `unreadCount`
478
478
 
479
+ ### r.e2ee — End-to-end encryption transport (flag-gated)
480
+
481
+ Relays opaque MLS envelopes, public device credentials and one-time key packages for the shared E2EE core; the server never sees keys or plaintext. Requires the `e2eeV2` network feature flag; otherwise every call returns `feature_disabled`.
482
+
483
+ ```ts
484
+ const { data: device } = await r.e2ee.registerDevice({ device_name: 'phone-1', realm: 'minds', signing_key, credential });
485
+ await r.e2ee.publishKeyPackages({ device_id: device.id, packages: [keyPackageBase64] });
486
+ const { data: claimed } = await r.e2ee.claimKeyPackage({ device_id: device.id, target_device_id: peerDeviceId });
487
+ await r.e2ee.appendEvent(conversationId, { device_id: device.id, envelope }); // idempotent per event_id
488
+ const { data: page } = await r.e2ee.events(conversationId, { device_id: device.id, after_seq: 0 });
489
+ await r.e2ee.acknowledge(conversationId, { device_id: device.id, seq: page.headSeq });
490
+ ```
491
+
479
492
  ### r.realtime — WebSocket Chat Client
480
493
 
481
494
  Provides persistent real-time streaming and chat presence, useful for building frontends (e.g. React Native, Web).
@@ -620,7 +633,7 @@ if (subscription.active) {
620
633
  - `r.inviteCodes` / `r.inviteCodesAdmin` — Invite code management
621
634
  - `r.organizationSettings` / `r.organizationSecurity` — Org configuration
622
635
  - 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, 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 and exact `wireText`; connection is not publication approval. `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.
623
- - `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. Public reading: `publicNetworks`, `searchPublicProfiles`, `getPublicProfile`, `getPublicAuthorFeed`, `getFollowingFeed`, `getPublicConversation`, `getPublicTrends`, `getPublicTrendFeed`; private app subscriptions: `listSubscriptions`, `subscribeToProfile`, `unsubscribeFromProfile`, `onSubscriptionChange`. 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. 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`.
636
+ - `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. Keyless post links: `const visitor = new Recursiv({ anonymous: true }); 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`. 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. 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`.
624
637
  - 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.
625
638
  - Public trends: require `public_trends` from `publicNetworks()`, then `getPublicTrends({ protocol, limit?, cursor? })`; render `kind: topics` as source topics and `kind: posts` as source posts. With `trend_feed`, open only a returned current topic using `getPublicTrendFeed({ protocol, topic: trend.nativeId, limit?, cursor? })`. Show provider, observedAt and provider_ranked_window coverage; null rankingWindow is unknown, not a daily/global ranking. Preserve source order, canonical IDs and readRef, and opaque cursors. Topic catalogs do not paginate; Mastodon trends stop at 200 source positions. Both methods require users:read plus posts:read on the current human app key; Nostr trends are unsupported. Propagate errors; trend_unavailable means refresh the topic list. No ingestion or native writes.
626
639
  - `listCandidates({ protocol?, limit?, cursor? })` and `getCandidate(id)` are read-only quarantine review, not published-post search. Both require `admin` scope, a project-bound key, a live admin role, and existing project-admin access. The key supplies app/network scope; neither method accepts another project or network. They never invoke refresh, change settings, or publish.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@recursiv/sdk",
3
- "version": "0.7.28",
3
+ "version": "0.7.30",
4
4
  "description": "TypeScript SDK for the Recursiv API",
5
5
  "license": "FSL-1.1-ALv2",
6
6
  "type": "module",