@recursiv/sdk 0.7.29 → 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 +20 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/resources/e2ee.d.ts +93 -0
- package/dist/resources/e2ee.js +36 -0
- package/dist/resources/protocols.d.ts +8 -0
- package/dist/resources/protocols.js +10 -0
- package/llm.md +14 -1
- package/package.json +1 -1
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).
|
|
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';
|
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
|
+
}
|
|
@@ -703,6 +703,14 @@ export declare class ProtocolsResource {
|
|
|
703
703
|
private subscriptionChanged;
|
|
704
704
|
/** Confirmed safety choices from this authenticated SDK instance. Unsubscribe when its account or view changes. */
|
|
705
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>>;
|
|
706
714
|
publicNetworks(): Promise<SingleResponse<SocialNetwork[]>>;
|
|
707
715
|
/** Query the live public network. It does not import users or posts. */
|
|
708
716
|
searchPublicProfiles(params: SearchPublicProfilesParams): Promise<SingleResponse<SocialProfilePage>>;
|
|
@@ -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
|
}
|
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.
|
|
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.
|