@recursiv/sdk 0.7.22 → 0.7.25

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
@@ -110,7 +110,7 @@ for await (const chunk of r.agents.chatStream(agent.id, { message: 'Hello!' }))
110
110
  | `r.admin` | User management, stats, content reports (`listReports`, `resolveReport`, `dismissReport`) |
111
111
  | `r.dispatcher` | Task queue, claims, signals, outcomes, webhooks |
112
112
  | `r.goals` | Org goals with task rollups (group dispatcher tasks) |
113
- | `r.protocols` | `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` |
113
+ | `r.protocols` | 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
114
 
115
115
  Full reference: see [llm.md](./llm.md) (ships with this package) or the type definitions in `dist/`.
116
116
 
@@ -423,3 +423,34 @@ import type {
423
423
  ## License
424
424
 
425
425
  FSL-1.1-ALv2 — source-available, converts to Apache 2.0 after 2 years.
426
+
427
+ ### Public source trends
428
+
429
+ Check `publicNetworks()` for `public_trends` before calling
430
+ `getPublicTrends({ protocol, limit: 10, cursor })`. The response is a discriminated
431
+ union: `kind: 'topics'` contains Bluesky topics; `kind: 'posts'` contains public
432
+ ActivityPub posts ranked by mastodon.social. Preserve provider order and display
433
+ `provider`, `observedAt`, and `coverage: 'provider_ranked_window'`. A null
434
+ `rankingWindow` means the provider has not supplied an exact ranking interval.
435
+
436
+ For a network with `trend_feed`, open a returned topic with
437
+ `getPublicTrendFeed({ protocol, topic: topic.nativeId, limit: 10, cursor })`.
438
+ The service rechecks current topic availability before each page; a disappeared
439
+ topic returns `trend_unavailable` rather than an empty feed. Keep canonical post
440
+ IDs and provider `readRef` when opening `getPublicConversation`. Cursors are
441
+ opaque; topic catalogs have no pagination and the Mastodon post window stops at
442
+ 200 source positions. Both reads require `users:read` and `posts:read` on the
443
+ current human project-bound app key. Nostr does not advertise trends. Provider
444
+ failures propagate as SDK errors, and neither method ingests or publishes posts.
445
+
446
+ ### Native Nostr signing
447
+
448
+ Use `protocols.nativeNostrAccount()` to check the app's explicit native-write
449
+ capability. For an enabled connected key, prepare a text post with
450
+ `prepareNativeNostr(identityId, { action: 'publish', content })`, show its
451
+ identity, relay and text, and ask the client signer to sign the returned event.
452
+ Submit only the public event with `submitNativeNostr(identityId, { intent, event,
453
+ approve: true })`. Preserve that exact event on uncertain retries. Check
454
+ `getNativeNostrPost(identityId, eventId)` for relay visibility. An owned note can
455
+ prepare `{ action: 'delete', postId }`; deletion is a signed request, not a
456
+ promise of erasure across all relays. Keys are never sent to this SDK service.
package/dist/index.d.ts CHANGED
@@ -189,7 +189,7 @@ export type { UploadMediaInput, UploadUrlInput, UploadUrlResult } from './resour
189
189
  export { WalletResource } from './resources/wallet.js';
190
190
  export { ProtocolsResource } from './resources/protocols.js';
191
191
  export { PROTOCOL_ADAPTER_MATURITIES, PROTOCOL_CAPABILITY_OPERATIONS, PROTOCOL_CAPABILITY_STATUSES, } from './resources/protocols.js';
192
- export type { SocialProfile, SocialSubscription, SocialSubscriptionChange, SocialProfilePage, SocialNetwork, SocialPost, SocialAuthorFeed, SocialFollowingFeed, SocialFollowingSource, SearchPublicProfilesParams, HostedProtocolConnections, HostedProtocol, HostedProtocolIdentity, HostedNostrChallenge, HostedNostrIdentityProof, HostedPostPreview, ConnectHostedIdentityInput, PreviewHostedPostInput, PublishHostedPostInput, ListProtocolCandidatesParams, ProtocolAdapterMaturity, ProtocolAdapterMetadata, ProtocolCandidateListResponse, ProtocolCandidatePageMeta, ProtocolCapabilityOperation, ProtocolCapabilityStatus, ProtocolCollectionStatus, ProtocolQuarantinedCandidate, ProtocolRefreshResult, ProtocolSettings, ProtocolSettingsUpdateResult, UpdateSettingsInput as UpdateProtocolSettingsInput, } 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, HostedPostPreview, ConnectHostedIdentityInput, PreviewHostedPostInput, PublishHostedPostInput, ListProtocolCandidatesParams, ProtocolAdapterMaturity, ProtocolAdapterMetadata, ProtocolCandidateListResponse, ProtocolCandidatePageMeta, ProtocolCapabilityOperation, ProtocolCapabilityStatus, ProtocolCollectionStatus, ProtocolQuarantinedCandidate, ProtocolRefreshResult, ProtocolSettings, ProtocolSettingsUpdateResult, UpdateSettingsInput as UpdateProtocolSettingsInput, } from './resources/protocols.js';
193
193
  export { InboxResource } from './resources/inbox.js';
194
194
  export { NetworkResource } from './resources/network.js';
195
195
  export { SimulatorResource } from './resources/simulator.js';
@@ -159,6 +159,55 @@ export interface HostedNostrIdentityProof {
159
159
  created_at: number;
160
160
  tags: string[][];
161
161
  }
162
+ export type NativeNostrPrepareInput = {
163
+ action: 'publish';
164
+ content: string;
165
+ } | {
166
+ action: 'delete';
167
+ postId: string;
168
+ };
169
+ export interface NativeNostrPrepared {
170
+ intent: string;
171
+ event: {
172
+ kind: number;
173
+ created_at: number;
174
+ content: string;
175
+ tags: string[][];
176
+ };
177
+ eventId: string;
178
+ action: 'publish' | 'delete';
179
+ identityId: string;
180
+ pubkey: string;
181
+ relay: string;
182
+ expiresAt: string;
183
+ postId?: string;
184
+ }
185
+ export interface NativeNostrAccount {
186
+ enabled: boolean;
187
+ identity: HostedProtocolIdentity | null;
188
+ capabilities: Array<'publish_original_text' | 'request_own_post_deletion'>;
189
+ signing?: 'client';
190
+ }
191
+ export interface NativeNostrPostState {
192
+ postId: string;
193
+ relay: string;
194
+ pubkey: string;
195
+ state: 'published' | 'deletion_requested' | 'not_found';
196
+ post: {
197
+ content: string;
198
+ createdAt: string;
199
+ } | null;
200
+ }
201
+ export interface NativeNostrReceipt {
202
+ eventId: string;
203
+ postId: string;
204
+ relay: string;
205
+ action: 'publish' | 'delete';
206
+ state: 'published' | 'deletion_requested' | 'unknown' | 'rejected';
207
+ acknowledged: boolean;
208
+ readBack: 'found' | 'absent' | 'unavailable';
209
+ reason?: 'blocked' | 'rate_limited' | 'restricted' | 'invalid' | 'relay_error';
210
+ }
162
211
  export type ConnectHostedIdentityInput = {
163
212
  protocol: 'atproto';
164
213
  identifier: string; /** Prefer a native app password. Used once, never stored. */
@@ -201,8 +250,9 @@ export interface SocialNetwork {
201
250
  url: string;
202
251
  };
203
252
  coverage: string;
204
- capabilities: Array<'profile_search' | 'profile_read' | 'original_author_posts' | 'minds_subscription' | 'following_feed'>;
253
+ capabilities: Array<'profile_search' | 'profile_read' | 'original_author_posts' | 'minds_subscription' | 'following_feed' | 'conversation_read' | 'public_trends' | 'trend_feed'>;
205
254
  profileSearchPagination: boolean;
255
+ conversationCoverage?: string;
206
256
  }
207
257
  /** Public source identity, independent of any local Minds user or handle. */
208
258
  export interface SocialProfile {
@@ -248,6 +298,8 @@ export interface SocialPost {
248
298
  protocol: string;
249
299
  nativeId: string;
250
300
  sourceUrl: string;
301
+ /** Opaque provider-local reference returned by public reads. */
302
+ readRef?: string;
251
303
  author: Omit<SocialProfile, 'subscription'>;
252
304
  text: string;
253
305
  createdAt: string;
@@ -266,6 +318,48 @@ export interface SocialAuthorFeed {
266
318
  cursor: string | null;
267
319
  coverage: 'original_posts';
268
320
  }
321
+ export interface SocialTrendProvenance {
322
+ provider: {
323
+ name: string;
324
+ url: string;
325
+ };
326
+ observedAt: string;
327
+ coverage: 'provider_ranked_window';
328
+ /** Exact ranking interval is not supplied by these providers. */
329
+ rankingWindow: null;
330
+ }
331
+ export interface SocialTrend {
332
+ protocol: string;
333
+ nativeId: string;
334
+ title: string;
335
+ description: string;
336
+ sourceUrl: string;
337
+ startedAt: string | null;
338
+ postCount: number | null;
339
+ }
340
+ export type SocialTrends = SocialTrendProvenance & ({
341
+ kind: 'topics';
342
+ items: SocialTrend[];
343
+ cursor: null;
344
+ } | {
345
+ kind: 'posts';
346
+ items: SocialPost[];
347
+ cursor: string | null;
348
+ });
349
+ export interface SocialTrendFeed extends SocialTrendProvenance {
350
+ trend: SocialTrend;
351
+ items: SocialPost[];
352
+ cursor: string | null;
353
+ }
354
+ export interface SocialConversation {
355
+ focus: SocialPost;
356
+ /** Oldest available ancestor first, ending with the immediate parent. */
357
+ ancestors: SocialPost[];
358
+ /** Available direct replies to focus, not all descendants. */
359
+ replies: SocialPost[];
360
+ coverage: 'public_provider_window';
361
+ limited: boolean;
362
+ }
269
363
  export interface SocialFollowingSource {
270
364
  subscriptionId: string;
271
365
  protocol: string;
@@ -306,6 +400,35 @@ export declare class ProtocolsResource {
306
400
  protocol?: string;
307
401
  cursor?: string;
308
402
  }): Promise<SingleResponse<SocialFollowingFeed>>;
403
+ /** Read the selected source post and its bounded public conversation; never publishes or imports it. */
404
+ getPublicConversation(params: {
405
+ protocol: string;
406
+ post: string;
407
+ readRef?: string;
408
+ }): Promise<SingleResponse<SocialConversation>>;
409
+ /** Current provider-ranked topics or posts, with explicit source and coverage. Does not ingest content. */
410
+ getPublicTrends(params: {
411
+ protocol: string;
412
+ limit?: number;
413
+ cursor?: string;
414
+ }): Promise<SingleResponse<SocialTrends>>;
415
+ /** Open a topic currently listed by the provider. Use its nativeId as topic and preserve the opaque cursor. */
416
+ getPublicTrendFeed(params: {
417
+ protocol: string;
418
+ topic: string;
419
+ limit?: number;
420
+ cursor?: string;
421
+ }): Promise<SingleResponse<SocialTrendFeed>>;
422
+ /** Native writes are off unless enabled for this exact app; public readers are independent. */
423
+ nativeNostrAccount(): Promise<SingleResponse<NativeNostrAccount>>;
424
+ prepareNativeNostr(id: string, input: NativeNostrPrepareInput): Promise<SingleResponse<NativeNostrPrepared>>;
425
+ /** Pass only the public signed event. Retry with this same intent/event, never sign a replacement after a timeout. */
426
+ submitNativeNostr(id: string, input: {
427
+ intent: string;
428
+ event: HostedNostrIdentityProof;
429
+ approve: true;
430
+ }): Promise<SingleResponse<NativeNostrReceipt>>;
431
+ getNativeNostrPost(id: string, postId: string): Promise<SingleResponse<NativeNostrPostState>>;
309
432
  /** Save a private reading subscription after resolving the immutable native ID. */
310
433
  subscribeToProfile(input: {
311
434
  protocol: string;
@@ -78,6 +78,41 @@ export class ProtocolsResource {
78
78
  getFollowingFeed(params = {}) {
79
79
  return this.client.get('/protocols/social/following-feed', { protocol: params.protocol, cursor: params.cursor });
80
80
  }
81
+ /** Read the selected source post and its bounded public conversation; never publishes or imports it. */
82
+ getPublicConversation(params) {
83
+ return this.client.get('/protocols/social/conversation', { protocol: params.protocol, post: params.post, readRef: params.readRef });
84
+ }
85
+ /** Current provider-ranked topics or posts, with explicit source and coverage. Does not ingest content. */
86
+ getPublicTrends(params) {
87
+ return this.client.get('/protocols/social/trends', { protocol: params.protocol, limit: params.limit, cursor: params.cursor });
88
+ }
89
+ /** Open a topic currently listed by the provider. Use its nativeId as topic and preserve the opaque cursor. */
90
+ getPublicTrendFeed(params) {
91
+ return this.client.get('/protocols/social/trend-feed', { protocol: params.protocol, topic: params.topic, limit: params.limit, cursor: params.cursor });
92
+ }
93
+ /** Native writes are off unless enabled for this exact app; public readers are independent. */
94
+ nativeNostrAccount() {
95
+ return this.client.get('/protocols/social/nostr/account');
96
+ }
97
+ prepareNativeNostr(id, input) {
98
+ hostedIdentityPath(id);
99
+ const body = input.action === 'publish' ? { action: input.action, content: input.content } : { action: input.action, postId: input.postId };
100
+ return this.client.post(`/protocols/social/nostr/${encodeURIComponent(id)}/prepare`, body);
101
+ }
102
+ /** Pass only the public signed event. Retry with this same intent/event, never sign a replacement after a timeout. */
103
+ submitNativeNostr(id, input) {
104
+ hostedIdentityPath(id);
105
+ const { id: eventId, pubkey, sig, kind, content, created_at, tags } = input.event;
106
+ return this.client.post(`/protocols/social/nostr/${encodeURIComponent(id)}/submit`, {
107
+ intent: input.intent, event: { id: eventId, pubkey, sig, kind, content, created_at, tags }, approve: input.approve,
108
+ });
109
+ }
110
+ getNativeNostrPost(id, postId) {
111
+ hostedIdentityPath(id);
112
+ if (!/^[0-9a-f]{64}$/.test(postId))
113
+ throw new ValidationError({ type: 'validation_error', code: 'invalid_post_id', message: 'A Nostr event ID is required' });
114
+ return this.client.get(`/protocols/social/nostr/${encodeURIComponent(id)}/posts/${postId}`);
115
+ }
81
116
  /** Save a private reading subscription after resolving the immutable native ID. */
82
117
  subscribeToProfile(input) {
83
118
  return this.client.post('/protocols/social/subscriptions', input).then(result => {
package/llm.md CHANGED
@@ -619,7 +619,8 @@ if (subscription.active) {
619
619
  - `r.admin` — Admin operations (user management, stats, network settings); content reports: `listReports({ status? })`, `resolveReport(id)`, `dismissReport(id)` (project-bound keys see only their app's reports)
620
620
  - `r.inviteCodes` / `r.inviteCodesAdmin` — Invite code management
621
621
  - `r.organizationSettings` / `r.organizationSecurity` — Org configuration
622
- - `r.protocols` — 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`.
622
+ - `r.protocols` — 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`.
623
+ - 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.
623
624
  - `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.
624
625
  - Lists return `{ data: ProtocolQuarantinedCandidate[], meta: { limit, has_more, next_cursor } }`; use the opaque `next_cursor`, not offset pagination. The API defaults `limit` to 20 and permits at most 50. Details return `{ data: ProtocolQuarantinedCandidate }`. Every candidate has `state: 'quarantined'` and `content_format: 'plain'`; render its bounded `content` as text, respect `content_truncated`, and distinguish upstream `published_at` from local `observed_at` / `created_at`.
625
626
  - Safe source IDs are preserved byte-exact. `external_id: null` means unsafe or malformed provenance was withheld, not an invented ID; `author.native_id` may likewise be absent or withheld. Preserve nulls in both list and detail views, and use the candidate's own `id` for `getCandidate`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@recursiv/sdk",
3
- "version": "0.7.22",
3
+ "version": "0.7.25",
4
4
  "description": "TypeScript SDK for the Recursiv API",
5
5
  "license": "FSL-1.1-ALv2",
6
6
  "type": "module",