@recursiv/sdk 0.7.70 → 0.7.72

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.
@@ -1,5 +1,5 @@
1
1
  import type { HttpClient } from '../client.js';
2
- import type { CompassFilterMeta, DiscoveryLayout, PaginatedResponse, PaginationParams, SingleResponse } from '../types.js';
2
+ import type { CompassFilterMeta, DiscoveryLayout, PaginatedResponse, PaginationParams, SingleResponse, StoryOutlets } from '../types.js';
3
3
  /** Canonical protocol operation names returned by the adapter registry. */
4
4
  export declare const PROTOCOL_CAPABILITY_OPERATIONS: readonly ["discover", "stream", "backfill", "search", "resolveIdentity", "fetchProfile", "fetchThread", "publish", "reply", "react", "repost", "follow", "delete", "health", "checkpoint"];
5
5
  export declare const PROTOCOL_CAPABILITY_STATUSES: readonly ["supported", "planned", "unsupported"];
@@ -181,6 +181,134 @@ export interface ProtocolIngestionReplayResult {
181
181
  job_id: string;
182
182
  status: 'replayed';
183
183
  }
184
+ /** A section of the ingestion health report that can fail to answer within its budget. */
185
+ export type ProtocolIngestionHealthSection = 'activity' | 'circuits' | 'dead_letters' | 'jobs' | 'kill_switch' | 'lanes';
186
+ /**
187
+ * Where a job is in its lane, read from its checkpoint: RSS reads one feed per page (feed_index); Hacker News and
188
+ * Event Registry walk term_index and page. Null when the checkpoint holds no such index.
189
+ */
190
+ export interface ProtocolIngestionHealthPosition {
191
+ feed_index: number | null;
192
+ term_index: number | null;
193
+ page: number | null;
194
+ }
195
+ export interface ProtocolIngestionHealthRuntime {
196
+ mode: 'collector' | 'runtime';
197
+ enabled: boolean;
198
+ simulation: boolean;
199
+ global_kill_switch: boolean;
200
+ controls_valid: boolean;
201
+ project_allowed: boolean;
202
+ /** This app's operator kill switch; null when that read did not answer. */
203
+ kill_switch: boolean | null;
204
+ }
205
+ export interface ProtocolIngestionLaneJobs {
206
+ queued: number;
207
+ leased: number;
208
+ done: number;
209
+ dead: number;
210
+ }
211
+ /** The lane's open job (the worker keeps one open per lane). */
212
+ export interface ProtocolIngestionLaneCurrentJob {
213
+ status: 'queued' | 'leased';
214
+ position: ProtocolIngestionHealthPosition;
215
+ pages_committed: number;
216
+ consecutive_failures: number;
217
+ last_error: string | null;
218
+ retry_at: string | null;
219
+ /** A leased job's lease end; null while queued. */
220
+ lease_until: string | null;
221
+ /** Leased, but the lease ran out and no worker reclaimed it (the worker died mid-page). */
222
+ lease_expired: boolean;
223
+ /** Eligible to run since then; null while a live lease holds it or it waits out a retry. */
224
+ waiting_since: string | null;
225
+ /** waiting_since once the wait passes the worker's starvation threshold (30 minutes); otherwise null. */
226
+ starved_since: string | null;
227
+ }
228
+ export interface ProtocolIngestionActivityReason {
229
+ status: 'retry' | 'dead' | 'invalid_job' | 'deferred' | 'abandoned';
230
+ reason: string | null;
231
+ count: number;
232
+ }
233
+ /** Attempts over the last window_hours; last success, last failure and failing_since look back lookback_days. */
234
+ export interface ProtocolIngestionLaneActivity {
235
+ window_hours: number;
236
+ pages_committed: number;
237
+ /** Attempts that ended in a retry, a dead letter or an invalid job. */
238
+ failed: number;
239
+ deferred: number;
240
+ abandoned: number;
241
+ reasons: ProtocolIngestionActivityReason[];
242
+ lookback_days: number;
243
+ last_success_at: string | null;
244
+ last_failure_at: string | null;
245
+ /** The first failure after the last success; null when the newest finished attempt succeeded or none failed. */
246
+ failing_since: string | null;
247
+ }
248
+ export interface ProtocolIngestionDeadLetterGroup {
249
+ reason: string | null;
250
+ position: ProtocolIngestionHealthPosition;
251
+ count: number;
252
+ last_at: string | null;
253
+ }
254
+ /** Every dead job of the lane counted in total; the newest 20 (reason, position) groups listed. */
255
+ export interface ProtocolIngestionLaneDeadLetters {
256
+ total: number;
257
+ last_dead_at: string | null;
258
+ groups: ProtocolIngestionDeadLetterGroup[];
259
+ }
260
+ /** One ingestion lane. A section that did not answer reads as null here and is named in the report's `unavailable`. */
261
+ export interface ProtocolIngestionLaneHealth {
262
+ provider_id: string;
263
+ source_subscription_key: string;
264
+ active: boolean;
265
+ /** RSS only: the feeds this process reads, one page each, so a pass ends after feed_count pages. Null for other providers. */
266
+ feed_count: number | null;
267
+ jobs: ProtocolIngestionLaneJobs | null;
268
+ /** When a job of this lane last reached done (read every page of its pass). */
269
+ last_done_at: string | null;
270
+ /** Null when the lane has no open job or the jobs section is unavailable. */
271
+ current: ProtocolIngestionLaneCurrentJob | null;
272
+ activity: ProtocolIngestionLaneActivity | null;
273
+ dead_letters: ProtocolIngestionLaneDeadLetters | null;
274
+ }
275
+ /** A circuit row that is open, counting failures or killed: the provider's own, or (per-RSS-host circuit on) a fetch host's. */
276
+ export interface ProtocolIngestionCircuitHealth {
277
+ provider_id: string;
278
+ kind: 'provider' | 'host';
279
+ /**
280
+ * A host circuit's fetch host: read from a readable key, or for a hashed key the configured feed host (with any
281
+ * explicit port) it belongs to. Null for the provider's own circuit and for a hashed key no configured feed names.
282
+ */
283
+ host: string | null;
284
+ /** The quota row key: `rss`, `rss--h-feeds_bbci_co_uk`, `rss--x-<hash>`. */
285
+ domain: string;
286
+ open: boolean;
287
+ open_until: string | null;
288
+ /**
289
+ * When the circuit last opened, or re-opened on a failed probe (open_until minus the open window). Null when it has
290
+ * not opened since it last closed.
291
+ */
292
+ opened_at: string | null;
293
+ /** Failures in a row since the circuit last closed (a host circuit's: feeds whose host never accepted the connection). */
294
+ failures: number;
295
+ killed: boolean;
296
+ }
297
+ /**
298
+ * Counts-only ingestion health for the key's app (audit row 8a): per lane, job counts, the open job's position,
299
+ * the last done, success and failure, 24-hour attempts by reason and dead letters by reason and position; plus the
300
+ * open or counting circuits. Never a checkpoint, payload, candidate or feed body.
301
+ */
302
+ export interface ProtocolIngestionHealthReport {
303
+ generated_at: string;
304
+ runtime: ProtocolIngestionHealthRuntime;
305
+ /** Null when the lanes section is unavailable. */
306
+ lanes: ProtocolIngestionLaneHealth[] | null;
307
+ /** Open first. Null when the circuits section is unavailable. */
308
+ circuits: ProtocolIngestionCircuitHealth[] | null;
309
+ /** Sections that did not answer within their statement budget; their fields read as null. */
310
+ unavailable: ProtocolIngestionHealthSection[];
311
+ }
184
312
  /** App/network scope comes only from the authenticated project-bound key. */
185
313
  export interface ListProtocolCandidatesParams {
186
314
  protocol?: string;
@@ -250,6 +378,14 @@ export type NativeNostrPrepareInput = {
250
378
  content: string;
251
379
  /** Your own post in this app (sdk.posts) that this note copies, so deleting it can offer this copy. */
252
380
  crossPostOf?: string;
381
+ /**
382
+ * A NIP-18 quote of one public note (0.7.71; accounts advertising `quote`). The server reads the note and adds its
383
+ * `q` and `p` tags and a `nostr:nevent` line after your text; review `quote` and the exact event before signing.
384
+ */
385
+ quote?: {
386
+ protocol: 'nostr';
387
+ nativeId: string;
388
+ };
253
389
  } | {
254
390
  action: 'reply';
255
391
  content: string;
@@ -662,13 +798,22 @@ export interface NativeReplyBatch {
662
798
  /** Public text and Minds-hosted images; accounts are selected explicitly in the shared editor. */
663
799
  export interface NativePostPrepareInput {
664
800
  clientMutationId: string;
801
+ /**
802
+ * The exact public text. Pass '' for a photo-only post (no words), which needs at least one `media`
803
+ * item: Bluesky gets an empty text with the images and Mastodon a status with only the photos, each
804
+ * photo's `alt` as its description. '' without media, or only spaces, is refused. Replies always need text.
805
+ */
665
806
  text: string;
666
807
  accountIds: string[];
667
808
  /** Minds-hosted images attached to every selected destination. Omit for a text-only post. */
668
809
  media?: NativeMediaInput[];
669
- /** Native quote on Bluesky only. The server reads the source and permissions using each selected account. */
810
+ /**
811
+ * A native quote: a Bluesky record (`at://` URI), or a Mastodon status by its public URI (0.7.71) on a server that
812
+ * creates quote posts (Mastodon API version 7). The server reads the source and permissions using each selected account;
813
+ * a server without quote posts answers `quote_not_supported`, never a plain link. Nostr quotes use `prepareNativeNostr`.
814
+ */
670
815
  quote?: {
671
- protocol: 'bluesky';
816
+ protocol: 'bluesky' | 'activitypub';
672
817
  nativeId: string;
673
818
  };
674
819
  /**
@@ -685,17 +830,24 @@ export interface NativePostBatch {
685
830
  deliveries: Array<Omit<NativeReplyBatch['deliveries'][number], 'kind' | 'parent' | 'recoverable' | 'replacedBy'> & {
686
831
  audience: 'public';
687
832
  maxCharacters: number;
688
- quote?: {
689
- protocol: 'bluesky';
690
- nativeId: string;
691
- cid: string;
692
- authorId: string;
693
- authorHandle: string;
694
- sourceUrl: string;
695
- text: string;
696
- };
833
+ quote?: NativePostQuote;
697
834
  }>;
698
835
  }
836
+ /** The reviewed quote of a delivery: Bluesky binds the record's CID, Mastodon the status's id on the member's server and its revision. */
837
+ export type NativePostQuote = {
838
+ nativeId: string;
839
+ authorId: string;
840
+ authorHandle: string;
841
+ sourceUrl: string;
842
+ text: string;
843
+ } & ({
844
+ protocol: 'bluesky';
845
+ cid: string;
846
+ } | {
847
+ protocol: 'activitypub';
848
+ localId: string;
849
+ revision: string;
850
+ });
699
851
  export interface NativeDelegatedPostHistoryItem {
700
852
  id: string;
701
853
  createdAt: string;
@@ -822,11 +974,22 @@ export interface NativeNostrPrepared {
822
974
  text: string;
823
975
  textTruncated?: boolean;
824
976
  };
977
+ /** The quoted note (a publish with `quote`), and the relay hint its `q` tag and `nostr:nevent` carry. */
978
+ quote?: {
979
+ protocol: 'nostr';
980
+ nativeId: string;
981
+ authorId: string;
982
+ authorHandle: string;
983
+ sourceUrl: string;
984
+ text: string;
985
+ textTruncated: boolean;
986
+ relay: string;
987
+ };
825
988
  }
826
989
  export interface NativeNostrAccount {
827
990
  enabled: boolean;
828
991
  identity: HostedProtocolIdentity | null;
829
- capabilities: Array<'publish_original_text' | 'request_own_post_deletion' | 'reply_text_note' | 'publish_linked_text' | 'like' | 'unlike' | 'repost' | 'unrepost' | 'follow' | 'unfollow'>;
992
+ capabilities: Array<'publish_original_text' | 'request_own_post_deletion' | 'reply_text_note' | 'publish_linked_text' | 'quote' | 'like' | 'unlike' | 'repost' | 'unrepost' | 'follow' | 'unfollow'>;
830
993
  signing?: 'client';
831
994
  }
832
995
  export interface NativeNostrPostState {
@@ -1212,12 +1375,19 @@ export interface SocialRadarCluster {
1212
1375
  * or a party or politician. Hide politics (`compass: 'hide'`) leaves these out. Absent from a server before it was served.
1213
1376
  */
1214
1377
  political?: boolean;
1378
+ /** Members per source kind, counted before the member cap: `outlet` counts articles, not outlets (use `outlets`). */
1215
1379
  sources: Partial<Record<SocialRadarSourceKind, number>>;
1216
1380
  corroboration: number;
1381
+ /** Distinct outlets per lane; from 0.7.71's servers, `outlets`' two lanes. */
1217
1382
  laneMix: {
1218
1383
  mainstream: number;
1219
1384
  independent: number;
1220
1385
  };
1386
+ /**
1387
+ * How many outlets the story's coverage holds (StoryOutlets), from the members this read serves: the number its card,
1388
+ * page, the News section and World Today all name. Absent from a server older than 0.7.71.
1389
+ */
1390
+ outlets?: StoryOutlets;
1221
1391
  velocity: {
1222
1392
  perHour: number;
1223
1393
  normalized: number;
@@ -1288,7 +1458,7 @@ export interface SocialRadarPosts {
1288
1458
  /** A story that has left the radar, as the radar's story archive keeps it (up to 30 days after it was last on the radar). */
1289
1459
  export interface SocialRadarStoryArchive {
1290
1460
  /** The story as the radar last published it. Articles keep their headline and link; a post is a member by reference only (no `title`). */
1291
- cluster: Pick<SocialRadarCluster, 'id' | 'label' | 'kind' | 'seed' | 'theme' | 'political' | 'sources' | 'laneMix'> & {
1461
+ cluster: Pick<SocialRadarCluster, 'id' | 'label' | 'kind' | 'seed' | 'theme' | 'political' | 'sources' | 'laneMix' | 'outlets'> & {
1292
1462
  members: Array<Pick<SocialRadarMember, 'kind' | 'key' | 'url' | 'publishedAt' | 'outlet'> & {
1293
1463
  title?: string;
1294
1464
  }>;
@@ -1598,7 +1768,11 @@ export declare class ProtocolsResource {
1598
1768
  }): Promise<SingleResponse<SocialAuthorFeed>>;
1599
1769
  /**
1600
1770
  * Public, app-moderated discovery without account authority. Enrichment uses cached judgments only.
1601
- * Every page, previews included, gets the safety floor; `discovery` only asks for a layout.
1771
+ * Every page, previews included, gets the safety floor; `discovery` only asks for a layout. `compass` (0.7.72): a
1772
+ * visitor's Politics choice, `all` or `hide` only (a side or Mix is refused, 400 `invalid_input`). Hide politics leaves
1773
+ * out the topics and posts whose cached browse category is politics, for every visitor, and the page says what it did
1774
+ * in `meta.compass_filter` (`basis: 'flags'` until the operator serves visitors the full filter). An API older than
1775
+ * 0.7.72 refuses `compass` here with a 400.
1602
1776
  */
1603
1777
  getSharedTrends(projectId: string, params: {
1604
1778
  protocol: string;
@@ -1607,6 +1781,7 @@ export declare class ProtocolsResource {
1607
1781
  includePreviews?: boolean;
1608
1782
  category?: string;
1609
1783
  discovery?: boolean;
1784
+ compass?: string;
1610
1785
  }): Promise<SingleResponse<SocialTrends>>;
1611
1786
  /** Open a provider-listed topic through the same public-only app boundary, with the same safety floor. */
1612
1787
  getSharedTrendFeed(projectId: string, params: {
@@ -1673,7 +1848,8 @@ export declare class ProtocolsResource {
1673
1848
  * Current provider-ranked topics or posts, with explicit source and coverage. Does not ingest content.
1674
1849
  * Every page, previews included, gets the safety floor; `discovery` only asks for a layout. `compass` (a Politics
1675
1850
  * choice, spelled as `posts.list` takes it): Hide politics (`hide`) leaves out the topics and posts whose cached
1676
- * browse category is politics, where the filter is served to the member; a side or Mix is answered unfiltered. The
1851
+ * browse category is politics, for every member (from 0.7.72; before, only where the filter was served to them), with
1852
+ * `meta.compass_filter.basis` 'flags' where the full filter is not served; a side or Mix is answered unfiltered. The
1677
1853
  * page then says what it did in `meta.compass_filter`.
1678
1854
  */
1679
1855
  getPublicTrends(params: {
@@ -1761,7 +1937,7 @@ export declare class ProtocolsResource {
1761
1937
  }): Promise<SingleResponse<NativeRepostReview>>;
1762
1938
  /** Reconcile the saved action without sending another native write. */
1763
1939
  nativeRepostStatus(id: string): Promise<SingleResponse<NativeRepostReview>>;
1764
- /** Prepare exact public text and reviewed images for selected connected Bluesky/ActivityPub accounts. */
1940
+ /** Prepare exact public text, reviewed images and an optional native quote for selected connected Bluesky/ActivityPub accounts. */
1765
1941
  prepareNativePost(input: NativePostPrepareInput): Promise<SingleResponse<NativePostBatch>>;
1766
1942
  /** Explicitly approve selected deliveries; never auto-retry an uncertain write. */
1767
1943
  submitNativePost(id: string, input: {
@@ -2005,6 +2181,17 @@ export declare class ProtocolsResource {
2005
2181
  * project-admin authorization as listCandidates.
2006
2182
  */
2007
2183
  ingestionStatus(): Promise<SingleResponse<ProtocolIngestionStatus>>;
2184
+ /**
2185
+ * Read counts-only ingestion health for the key's app: per lane, whether it
2186
+ * reaches done or dies part way (the open job's position, failures in a row,
2187
+ * starvation, last success and failure, attempts by reason, dead letters by
2188
+ * reason and position), plus open or counting circuits. Read-only. Needs a
2189
+ * project-bound admin key and current project-admin authority, but not the
2190
+ * content_intelligence_admin binding. Scope comes only from the key, so no
2191
+ * query is sent (the API rejects one). A section that does not answer in its
2192
+ * budget reads as null and is listed in `unavailable`.
2193
+ */
2194
+ ingestionHealth(): Promise<SingleResponse<ProtocolIngestionHealthReport>>;
2008
2195
  /** Turn the app's ingestion kill switch on: no reservation, screening or approval proceeds. */
2009
2196
  killIngestion(): Promise<SingleResponse<{
2010
2197
  kill_switch: true;
@@ -123,11 +123,15 @@ export class ProtocolsResource {
123
123
  }
124
124
  /**
125
125
  * Public, app-moderated discovery without account authority. Enrichment uses cached judgments only.
126
- * Every page, previews included, gets the safety floor; `discovery` only asks for a layout.
126
+ * Every page, previews included, gets the safety floor; `discovery` only asks for a layout. `compass` (0.7.72): a
127
+ * visitor's Politics choice, `all` or `hide` only (a side or Mix is refused, 400 `invalid_input`). Hide politics leaves
128
+ * out the topics and posts whose cached browse category is politics, for every visitor, and the page says what it did
129
+ * in `meta.compass_filter` (`basis: 'flags'` until the operator serves visitors the full filter). An API older than
130
+ * 0.7.72 refuses `compass` here with a 400.
127
131
  */
128
132
  getSharedTrends(projectId, params) {
129
133
  return this.client.get(`/protocols/public/${encodeURIComponent(projectId)}/trends`, { protocol: params.protocol, limit: params.limit, cursor: params.cursor,
130
- includePreviews: params.includePreviews, category: params.category, discovery: params.discovery });
134
+ includePreviews: params.includePreviews, category: params.category, discovery: params.discovery, compass: params.compass });
131
135
  }
132
136
  /** Open a provider-listed topic through the same public-only app boundary, with the same safety floor. */
133
137
  getSharedTrendFeed(projectId, params) {
@@ -207,7 +211,8 @@ export class ProtocolsResource {
207
211
  * Current provider-ranked topics or posts, with explicit source and coverage. Does not ingest content.
208
212
  * Every page, previews included, gets the safety floor; `discovery` only asks for a layout. `compass` (a Politics
209
213
  * choice, spelled as `posts.list` takes it): Hide politics (`hide`) leaves out the topics and posts whose cached
210
- * browse category is politics, where the filter is served to the member; a side or Mix is answered unfiltered. The
214
+ * browse category is politics, for every member (from 0.7.72; before, only where the filter was served to them), with
215
+ * `meta.compass_filter.basis` 'flags' where the full filter is not served; a side or Mix is answered unfiltered. The
211
216
  * page then says what it did in `meta.compass_filter`.
212
217
  */
213
218
  getPublicTrends(params, options) {
@@ -327,7 +332,7 @@ export class ProtocolsResource {
327
332
  hostedIdentityPath(id);
328
333
  return this.client.get(`/protocols/social/reposts/${encodeURIComponent(id)}`);
329
334
  }
330
- /** Prepare exact public text and reviewed images for selected connected Bluesky/ActivityPub accounts. */
335
+ /** Prepare exact public text, reviewed images and an optional native quote for selected connected Bluesky/ActivityPub accounts. */
331
336
  prepareNativePost(input) {
332
337
  return this.client.post('/protocols/social/posts/prepare', { clientMutationId: input.clientMutationId,
333
338
  text: input.text, accountIds: input.accountIds,
@@ -479,7 +484,8 @@ export class ProtocolsResource {
479
484
  prepareNativeNostr(id, input) {
480
485
  hostedIdentityPath(id);
481
486
  const body = input.action === 'publish' ? { action: input.action, content: input.content,
482
- ...(input.crossPostOf !== undefined ? { crossPostOf: input.crossPostOf } : {}) }
487
+ ...(input.crossPostOf !== undefined ? { crossPostOf: input.crossPostOf } : {}),
488
+ ...(input.quote ? { quote: { protocol: input.quote.protocol, nativeId: input.quote.nativeId } } : {}) }
483
489
  : input.action === 'reply' ? { action: input.action, content: input.content, parentId: input.parentId }
484
490
  : input.action === 'linked_post' ? { action: input.action, content: input.content,
485
491
  source: { protocol: input.source.protocol, nativeId: input.source.nativeId,
@@ -619,6 +625,19 @@ export class ProtocolsResource {
619
625
  ingestionStatus() {
620
626
  return this.client.get('/protocols/ingestion/status');
621
627
  }
628
+ /**
629
+ * Read counts-only ingestion health for the key's app: per lane, whether it
630
+ * reaches done or dies part way (the open job's position, failures in a row,
631
+ * starvation, last success and failure, attempts by reason, dead letters by
632
+ * reason and position), plus open or counting circuits. Read-only. Needs a
633
+ * project-bound admin key and current project-admin authority, but not the
634
+ * content_intelligence_admin binding. Scope comes only from the key, so no
635
+ * query is sent (the API rejects one). A section that does not answer in its
636
+ * budget reads as null and is listed in `unavailable`.
637
+ */
638
+ ingestionHealth() {
639
+ return this.client.get('/protocols/ingestion/health');
640
+ }
622
641
  /** Turn the app's ingestion kill switch on: no reservation, screening or approval proceeds. */
623
642
  killIngestion() {
624
643
  return this.client.post('/protocols/ingestion/kill', {});
@@ -1,5 +1,5 @@
1
1
  import type { HttpClient } from '../client.js';
2
- import type { Post, ViewerModeration } from '../types.js';
2
+ import type { Post, StoryOutlets, ViewerModeration } from '../types.js';
3
3
  import type { SocialPost } from './protocols.js';
4
4
  /** The sections a blended search can answer, in no particular order (see `SearchAnswer.order`). */
5
5
  export declare const SEARCH_SECTIONS: readonly ["top_posts", "latest_posts", "open_network", "people", "groups", "topics", "stories", "related", "trending"];
@@ -52,6 +52,12 @@ export interface SearchGroup {
52
52
  member_count: number;
53
53
  post_count: number;
54
54
  is_member: boolean;
55
+ /** The group's description as one line (legacy entities decoded, cut at a word with an ellipsis), null when it has none. Absent from an older API. */
56
+ description?: string | null;
57
+ /** When the group was last posted in (ISO 8601), null when nobody has or the API had no time to read it. Absent from an older API. */
58
+ last_active_at?: string | null;
59
+ /** The group's own words label it adult: cover its avatar. Absent from an older API. */
60
+ is_nsfw?: boolean;
55
61
  }
56
62
  export type SearchTopic = {
57
63
  kind: 'tag';
@@ -71,6 +77,8 @@ export interface SearchStory {
71
77
  theme: string | null;
72
78
  newest_at: string;
73
79
  sources: Partial<Record<'outlet' | 'hackernews' | 'bluesky' | 'activitypub' | 'nostr' | 'minds', number>>;
80
+ /** How many outlets reported it (StoryOutlets): the number every surface names for the story. Absent from a server older than 0.7.71. */
81
+ outlets?: StoryOutlets;
74
82
  lead: {
75
83
  type: 'image' | 'video';
76
84
  url: string;
@@ -1,5 +1,5 @@
1
1
  import type { HttpClient } from '../client.js';
2
- import type { ChangePasswordInput, ConfirmationCodePurpose, ConfirmationCodeSent, DeletionStatus, LoginHistoryEntry, PaginatedResponse, RequestEmailChangeInput, RevokeOtherSessionsInput, RevokeOtherSessionsResult, Session, SingleResponse, SuccessResponse, UpdateAppearanceInput, UpdateContentPreferencesInput, UpdateNotificationChannelsInput, UpdatePrivacyInput, UserPreferences } from '../types.js';
2
+ import type { ChangePasswordInput, ConfirmationCodePurpose, ConfirmationCodeSent, DeletionStatus, DisableAccountInput, LoginHistoryEntry, PaginatedResponse, RequestEmailChangeInput, RevokeOtherSessionsInput, RevokeOtherSessionsResult, Session, SingleResponse, SuccessResponse, UpdateAppearanceInput, UpdateContentPreferencesInput, UpdateNotificationChannelsInput, UpdatePrivacyInput, UserPreferences } from '../types.js';
3
3
  export declare class SettingsResource {
4
4
  private client;
5
5
  constructor(client: HttpClient);
@@ -66,6 +66,13 @@ export declare class SettingsResource {
66
66
  success: true;
67
67
  new_email: string;
68
68
  }>>;
69
+ /**
70
+ * Deactivate the account (POST /settings/account/disable): it is hidden and nothing is deleted; signing back in
71
+ * restores it. Confirmed with the password and asked once (`retry: false`), so a retried 5xx never lands after the
72
+ * first attempt already went through. An API that does not serve deactivation answers 404 (`NotFoundError`): keep
73
+ * the control hidden until the app's API serves it.
74
+ */
75
+ disableAccount(input: DisableAccountInput): Promise<SuccessResponse>;
69
76
  /** Request account deletion */
70
77
  requestDeletion(input?: {
71
78
  password?: string;
@@ -102,6 +102,15 @@ export class SettingsResource {
102
102
  verifyEmailChange(token) {
103
103
  return this.client.post('/settings/email-change/verify', { token });
104
104
  }
105
+ /**
106
+ * Deactivate the account (POST /settings/account/disable): it is hidden and nothing is deleted; signing back in
107
+ * restores it. Confirmed with the password and asked once (`retry: false`), so a retried 5xx never lands after the
108
+ * first attempt already went through. An API that does not serve deactivation answers 404 (`NotFoundError`): keep
109
+ * the control hidden until the app's API serves it.
110
+ */
111
+ disableAccount(input) {
112
+ return this.client.post('/settings/account/disable', input, { retry: false });
113
+ }
105
114
  /** Request account deletion */
106
115
  requestDeletion(input) {
107
116
  return this.client.post('/settings/delete-account', input);
@@ -0,0 +1,58 @@
1
+ import type { HttpClient } from '../client.js';
2
+ import type { SingleResponse } from '../types.js';
3
+ /**
4
+ * What a member did with a post. `more_like` ("More like this") and `unhide` ("Show the post again" after "Less like
5
+ * this") are stored as marked clicks; an API before they were accepted answers 400 for the whole batch that carries
6
+ * one, so send them in a request of their own if the API may be older.
7
+ */
8
+ export type SignalEventType = 'view' | 'click' | 'dwell' | 'react_up' | 'react_down' | 'save' | 'hide' | 'share' | 'mute_source' | 'open_chat' | 'ask_agent' | 'more_like' | 'unhide';
9
+ /** One engagement signal. An open-network post has no Minds id: name it in `metadata` (e.g. `{ ref }`) instead. */
10
+ export interface SignalEventInput {
11
+ event_type: SignalEventType;
12
+ /** The Minds post's id (a UUID). */
13
+ post_id?: string;
14
+ /** Milliseconds on screen, for `dwell` (0 to one hour). */
15
+ dwell_ms?: number;
16
+ metadata?: Record<string, unknown>;
17
+ }
18
+ /**
19
+ * Why nothing was stored, on a 200: `no_project_scope` (the key is not bound to an app, so there is no app database
20
+ * for the signal) or `project_db_unavailable` (the app database did not take it; signals are best-effort).
21
+ */
22
+ export type SignalNotRecordedReason = 'no_project_scope' | 'project_db_unavailable';
23
+ export interface SignalBatchResult {
24
+ /** Events stored. */
25
+ recorded: number;
26
+ reason?: SignalNotRecordedReason;
27
+ }
28
+ export interface SignalRecordResult {
29
+ recorded: boolean;
30
+ reason?: SignalNotRecordedReason;
31
+ }
32
+ export interface SignalSendOptions {
33
+ /**
34
+ * The request outlives the page (`fetch` keepalive), for a flush as the page is hidden or unloads: it is on the wire
35
+ * before the call returns and is asked once. Browsers cap keepalive bodies (about 64 KB in flight).
36
+ */
37
+ keepalive?: boolean;
38
+ /** `false` asks once: a 429 or 5xx is the call's failure at once, not retried after Retry-After. */
39
+ retry?: boolean;
40
+ }
41
+ /** The most events one `signals.batch` call may carry; the server answers 400 for a larger array. */
42
+ export declare const SIGNAL_BATCH_MAX = 100;
43
+ /**
44
+ * Engagement signals (views, dwell, hides, "More like this") for the member's own ranking, under the member's
45
+ * app-bound key (`agents:write`). They land in the app's database and feed For You. Best-effort: a signal the server
46
+ * cannot store still answers 200 with `recorded` 0 or false and a `reason`.
47
+ */
48
+ export declare class SignalsResource {
49
+ private client;
50
+ constructor(client: HttpClient);
51
+ /**
52
+ * Store up to SIGNAL_BATCH_MAX events in one request (POST /signals/batch). The server reads a post's newest hide or
53
+ * unhide, so send a batch only after the one before it has landed when order matters. An empty array stores nothing.
54
+ */
55
+ batch(events: SignalEventInput[], options?: SignalSendOptions): Promise<SingleResponse<SignalBatchResult>>;
56
+ /** Store one event (POST /signals/post). Prefer `batch`; this is for an API that predates it (404 there). */
57
+ record(event: SignalEventInput, options?: SignalSendOptions): Promise<SingleResponse<SignalRecordResult>>;
58
+ }
@@ -0,0 +1,32 @@
1
+ /** The most events one `signals.batch` call may carry; the server answers 400 for a larger array. */
2
+ export const SIGNAL_BATCH_MAX = 100;
3
+ /**
4
+ * Engagement signals (views, dwell, hides, "More like this") for the member's own ranking, under the member's
5
+ * app-bound key (`agents:write`). They land in the app's database and feed For You. Best-effort: a signal the server
6
+ * cannot store still answers 200 with `recorded` 0 or false and a `reason`.
7
+ */
8
+ export class SignalsResource {
9
+ client;
10
+ constructor(client) {
11
+ this.client = client;
12
+ }
13
+ /**
14
+ * Store up to SIGNAL_BATCH_MAX events in one request (POST /signals/batch). The server reads a post's newest hide or
15
+ * unhide, so send a batch only after the one before it has landed when order matters. An empty array stores nothing.
16
+ */
17
+ batch(events, options) {
18
+ return this.client.post('/signals/batch', { events }, sendOptions(options));
19
+ }
20
+ /** Store one event (POST /signals/post). Prefer `batch`; this is for an API that predates it (404 there). */
21
+ record(event, options) {
22
+ return this.client.post('/signals/post', event, sendOptions(options));
23
+ }
24
+ }
25
+ function sendOptions(options) {
26
+ if (!options)
27
+ return undefined;
28
+ return {
29
+ ...(options.retry === false ? { retry: false } : {}),
30
+ ...(options.keepalive ? { keepalive: true } : {}),
31
+ };
32
+ }