@recursiv/sdk 0.7.27 → 0.7.28

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/dist/index.d.ts CHANGED
@@ -171,7 +171,7 @@ export { VideoResource } from './resources/video.js';
171
171
  export type { VideoUploadCreds, CreateVideoUploadResult, VideoStatusInfo, VideoPlaybackToken, } from './resources/video.js';
172
172
  export { AppSubscriptionsResource } from './resources/app-subscriptions.js';
173
173
  export type { AppSubscriptionCheckoutResult, AppSubscriptionPortalResult, AppSubscriptionStatusResult, } from './resources/app-subscriptions.js';
174
- export type { CuratorSource, CuratorPrompt, CuratorRunInput, CuratorRunResult, } from './resources/curator.js';
174
+ export type { CuratorSource, CuratorPrompt, CuratorRunInput, CuratorRunResult, CuratorTodayEdition, CuratorTodayResponse, CuratorTodaySource, CuratorTodayStory, } from './resources/curator.js';
175
175
  export { ReportsResource } from './resources/reports.js';
176
176
  export type { ReportTargetType, ReportStatus, CreateReportInput, ReportRecord, ResolveReportInput, } from './resources/reports.js';
177
177
  export { ModerationResource } from './resources/moderation.js';
@@ -6,11 +6,15 @@ export interface SiblingAccount {
6
6
  username: string;
7
7
  name: string;
8
8
  image: string | null;
9
- created_at: string;
9
+ created_at: string | null;
10
+ /** Followers on this account. The main-account rule reads this field. */
11
+ follower_count: number;
10
12
  /** The stored primary email is a migration placeholder, not a real inbox. */
11
13
  is_placeholder_email: boolean;
12
14
  /** Server heuristic used to collapse test/demo identities in account pickers. */
13
15
  looks_like_test: boolean;
16
+ /** The account OTP sign-in lands on. Exactly one row carries it. */
17
+ is_primary: boolean;
14
18
  is_current: boolean;
15
19
  }
16
20
  export interface SiblingAccountsResponse {
@@ -19,6 +23,8 @@ export interface SiblingAccountsResponse {
19
23
  /** The verified real address shared by the returned accounts. */
20
24
  email: string;
21
25
  count: number;
26
+ /** Id of the main account, or null when the caller has no usable address. */
27
+ primary_id?: string | null;
22
28
  };
23
29
  }
24
30
  export interface SwitchAccountResult {
@@ -36,7 +42,7 @@ export interface SwitchAccountResult {
36
42
  export declare class AccountsResource {
37
43
  private client;
38
44
  constructor(client: HttpClient);
39
- /** List every account controlled by the caller's verified real email. */
45
+ /** List every account controlled by the caller's verified real email, main account first. */
40
46
  listSiblings(): Promise<SiblingAccountsResponse>;
41
47
  /** Mint a replacement key for a sibling account in the caller's project. */
42
48
  switchAccount(targetUserId: string): Promise<SingleResponse<SwitchAccountResult>>;
@@ -11,7 +11,7 @@ export class AccountsResource {
11
11
  constructor(client) {
12
12
  this.client = client;
13
13
  }
14
- /** List every account controlled by the caller's verified real email. */
14
+ /** List every account controlled by the caller's verified real email, main account first. */
15
15
  listSiblings() {
16
16
  return this.client.get('/accounts/siblings');
17
17
  }
@@ -105,9 +105,77 @@ export interface CuratorSeedDeckResponse {
105
105
  data: CuratorSeedDeckItem[];
106
106
  meta: CuratorSeedDeckMeta;
107
107
  }
108
+ /** Which half of the press an outlet belongs to. */
109
+ export type CuratorOutletLane = 'mainstream' | 'independent';
110
+ /** One outlet item behind a world story. Headline and link only, never an article body. */
111
+ export interface CuratorTodaySource {
112
+ outlet: string;
113
+ /** The outlet's lane. Absent on stories written before lanes existed. */
114
+ lane?: CuratorOutletLane;
115
+ title: string;
116
+ url: string;
117
+ published_at: string | null;
118
+ }
119
+ /** Distinct outlets per lane behind a world story. */
120
+ export interface CuratorTodayLaneMix {
121
+ mainstream: number;
122
+ independent: number;
123
+ }
124
+ export interface CuratorTodayStory {
125
+ id: string;
126
+ /** 1 = lead. World stories hold 1..N, Minds stories follow. */
127
+ rank: number;
128
+ /**
129
+ * 'world': the open web set it, ranked by how many outlets moved on it in
130
+ * the last 24 h with an independent outlet weighted above a mainstream one.
131
+ * 'minds': a group of real Minds posts.
132
+ */
133
+ kind: 'world' | 'minds';
134
+ kicker: string;
135
+ headline: string;
136
+ dek: string | null;
137
+ topic: string | null;
138
+ /** Outlet links for a world story; null for a Minds story. */
139
+ sources: CuratorTodaySource[] | null;
140
+ /** Distinct outlets behind a world story; null for a Minds story. */
141
+ outlet_count: number | null;
142
+ /** Distinct outlets per lane behind a world story; null for a Minds story. */
143
+ lane_mix: CuratorTodayLaneMix | null;
144
+ /**
145
+ * Which lanes carry a world story: 'mainstream', 'independent' (nobody in
146
+ * the mainstream lane ran it) or 'both'. Null for a Minds story.
147
+ */
148
+ lane: CuratorOutletLane | 'both' | null;
149
+ /** The discussion-room post when one exists. */
150
+ discussion_post_id: string | null;
151
+ /** Replies on the discussion post; 0 when there is none. */
152
+ reply_count: number;
153
+ /** Minds posts in the same shape as `sdk.posts.list()`. May be empty for a world story. */
154
+ posts: Post[];
155
+ }
156
+ export interface CuratorTodayEdition {
157
+ id: string;
158
+ edition_date: string;
159
+ status: 'draft' | 'published';
160
+ generated_at: string;
161
+ stories: CuratorTodayStory[];
162
+ }
163
+ export interface CuratorTodayResponse {
164
+ /** null when the network has no published edition yet. */
165
+ data: CuratorTodayEdition | null;
166
+ meta: {
167
+ generating: boolean;
168
+ };
169
+ }
108
170
  export declare class CuratorResource {
109
171
  private client;
110
172
  constructor(client: HttpClient);
173
+ /**
174
+ * Today: the daily front page for the caller's network. World stories
175
+ * (set by the open web, ranked by outlet count) come first; Minds
176
+ * conversation stories follow. Requires `posts:read`.
177
+ */
178
+ today(): Promise<CuratorTodayResponse>;
111
179
  /**
112
180
  * Fetch the supplied sources in parallel, annotate each top item with
113
181
  * the supplied prompt template, and insert audience-scoped posts into
@@ -3,6 +3,14 @@ export class CuratorResource {
3
3
  constructor(client) {
4
4
  this.client = client;
5
5
  }
6
+ /**
7
+ * Today: the daily front page for the caller's network. World stories
8
+ * (set by the open web, ranked by outlet count) come first; Minds
9
+ * conversation stories follow. Requires `posts:read`.
10
+ */
11
+ today() {
12
+ return this.client.get('/curator/today');
13
+ }
6
14
  /**
7
15
  * Fetch the supplied sources in parallel, annotate each top item with
8
16
  * the supplied prompt template, and insert audience-scoped posts into
@@ -82,7 +82,12 @@ export interface ProtocolQuarantinedCandidate {
82
82
  provider: string;
83
83
  /** Safe IDs are byte-exact; null means unsafe or malformed provenance was withheld. */
84
84
  external_id: string | null;
85
- state: 'quarantined';
85
+ state: ProtocolCandidateState;
86
+ /** Increments whenever the payload digest changes; decisions and verdicts are keyed by it. */
87
+ revision: number;
88
+ revision_digest: string | null;
89
+ /** Latest screening verdict on the current revision, or null when unscreened. */
90
+ latest_verdict: ProtocolCandidateVerdictSummary | null;
86
91
  title: string | null;
87
92
  content: string;
88
93
  content_format: 'plain';
@@ -99,9 +104,37 @@ export interface ProtocolQuarantinedCandidate {
99
104
  observed_at: string;
100
105
  created_at: string;
101
106
  }
107
+ export type ProtocolCandidateState = 'quarantined' | 'approved' | 'rejected' | 'escalated' | 'tombstoned';
108
+ export type ProtocolScreeningVerdict = 'pass' | 'hold' | 'fail';
109
+ export type ProtocolCandidateDecision = 'approved' | 'rejected' | 'escalated';
110
+ export interface ProtocolCandidateVerdictSummary {
111
+ verdict: ProtocolScreeningVerdict;
112
+ line: string;
113
+ category: string | null;
114
+ high_risk: boolean;
115
+ reason: string | null;
116
+ created_at: string;
117
+ }
118
+ export interface ProtocolCandidateVerdict extends ProtocolCandidateVerdictSummary {
119
+ id: string;
120
+ revision: number;
121
+ model: string | null;
122
+ confidence: number | null;
123
+ }
124
+ export interface DecideProtocolCandidateInput {
125
+ decision: ProtocolCandidateDecision;
126
+ /** Required for escalation; recorded on every decision. */
127
+ reason?: string;
128
+ }
129
+ export interface ProtocolCandidateDecisionResult {
130
+ candidate: ProtocolQuarantinedCandidate;
131
+ decision_id: string;
132
+ }
102
133
  /** App/network scope comes only from the authenticated project-bound key. */
103
134
  export interface ListProtocolCandidatesParams {
104
135
  protocol?: string;
136
+ /** Candidate state to list; the API defaults to quarantined. */
137
+ state?: ProtocolCandidateState;
105
138
  /** Page size, default 20 and maximum 50; enforced by the API. */
106
139
  limit?: number;
107
140
  /** Opaque cursor from this endpoint's previous page; never an offset. */
@@ -784,6 +817,21 @@ export declare class ProtocolsResource {
784
817
  * unsupported deployed route) propagate; they are never an empty page.
785
818
  */
786
819
  listCandidates(params?: ListProtocolCandidatesParams): Promise<ProtocolCandidateListResponse>;
820
+ /**
821
+ * Run fail-closed screening on a candidate's current revision. Requires the
822
+ * app's content_intelligence_admin binding (404 otherwise) and the same
823
+ * project-admin authorization as listCandidates. A failed or unconfigured
824
+ * classifier records hold, never pass.
825
+ */
826
+ screenCandidate(id: string): Promise<SingleResponse<ProtocolQuarantinedCandidate>>;
827
+ /** Screening verdict history for a candidate, newest first. */
828
+ listCandidateVerdicts(id: string): Promise<SingleResponse<ProtocolCandidateVerdict[]>>;
829
+ /**
830
+ * Record a decision on the candidate's current revision. Approval needs a
831
+ * signed-in human project admin and a pass verdict on the current digest;
832
+ * an agent key may only reject or escalate; escalation needs a reason.
833
+ */
834
+ decideCandidate(id: string, input: DecideProtocolCandidateInput): Promise<SingleResponse<ProtocolCandidateDecisionResult>>;
787
835
  /**
788
836
  * Read one quarantined candidate under the same authorization as listCandidates.
789
837
  * Empty and URL dot-segment IDs reject with ValidationError before any request;
@@ -282,24 +282,39 @@ export class ProtocolsResource {
282
282
  listCandidates(params) {
283
283
  return this.client.get('/protocols/candidates', params ? {
284
284
  protocol: params.protocol,
285
+ state: params.state,
285
286
  limit: params.limit,
286
287
  cursor: params.cursor,
287
288
  } : undefined);
288
289
  }
290
+ /**
291
+ * Run fail-closed screening on a candidate's current revision. Requires the
292
+ * app's content_intelligence_admin binding (404 otherwise) and the same
293
+ * project-admin authorization as listCandidates. A failed or unconfigured
294
+ * classifier records hold, never pass.
295
+ */
296
+ async screenCandidate(id) {
297
+ return this.client.post(`/protocols/candidates/${encodeURIComponent(assertCandidateId(id))}/screen`, {});
298
+ }
299
+ /** Screening verdict history for a candidate, newest first. */
300
+ async listCandidateVerdicts(id) {
301
+ return this.client.get(`/protocols/candidates/${encodeURIComponent(assertCandidateId(id))}/verdicts`);
302
+ }
303
+ /**
304
+ * Record a decision on the candidate's current revision. Approval needs a
305
+ * signed-in human project admin and a pass verdict on the current digest;
306
+ * an agent key may only reject or escalate; escalation needs a reason.
307
+ */
308
+ async decideCandidate(id, input) {
309
+ return this.client.post(`/protocols/candidates/${encodeURIComponent(assertCandidateId(id))}/decision`, input);
310
+ }
289
311
  /**
290
312
  * Read one quarantined candidate under the same authorization as listCandidates.
291
313
  * Empty and URL dot-segment IDs reject with ValidationError before any request;
292
314
  * all other IDs are encoded as one path segment and validated by the API.
293
315
  */
294
316
  async getCandidate(id) {
295
- if (typeof id !== 'string' || !id.trim() || id === '.' || id === '..') {
296
- throw new ValidationError({
297
- type: 'validation_error',
298
- code: 'invalid_candidate_id',
299
- message: 'Candidate ID must be a non-empty identifier, not a URL dot segment.',
300
- });
301
- }
302
- return this.client.get(`/protocols/candidates/${encodeURIComponent(id)}`);
317
+ return this.client.get(`/protocols/candidates/${encodeURIComponent(assertCandidateId(id))}`);
303
318
  }
304
319
  /** Cross-protocol full-text search */
305
320
  search(params) {
@@ -318,3 +333,13 @@ export class ProtocolsResource {
318
333
  return this.client.put('/protocols/settings', input);
319
334
  }
320
335
  }
336
+ function assertCandidateId(id) {
337
+ if (typeof id !== 'string' || !id.trim() || id === '.' || id === '..') {
338
+ throw new ValidationError({
339
+ type: 'validation_error',
340
+ code: 'invalid_candidate_id',
341
+ message: 'Candidate ID must be a non-empty identifier, not a URL dot segment.',
342
+ });
343
+ }
344
+ return id;
345
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@recursiv/sdk",
3
- "version": "0.7.27",
3
+ "version": "0.7.28",
4
4
  "description": "TypeScript SDK for the Recursiv API",
5
5
  "license": "FSL-1.1-ALv2",
6
6
  "type": "module",