@recursiv/sdk 0.7.70 → 0.7.71
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 +77 -9
- package/dist/client.d.ts +5 -0
- package/dist/client.js +7 -1
- package/dist/index.d.ts +13 -3
- package/dist/index.js +11 -1
- package/dist/resources/agents.d.ts +48 -0
- package/dist/resources/agents.js +22 -0
- package/dist/resources/curator.d.ts +9 -3
- package/dist/resources/discovery-fallover.d.ts +8 -0
- package/dist/resources/discovery-fallover.js +26 -0
- package/dist/resources/discovery-first-screen.d.ts +30 -0
- package/dist/resources/discovery-first-screen.js +149 -0
- package/dist/resources/discovery-merge.d.ts +144 -0
- package/dist/resources/discovery-merge.js +479 -0
- package/dist/resources/discovery-progressive.d.ts +117 -0
- package/dist/resources/discovery-progressive.js +414 -0
- package/dist/resources/discovery-safety.d.ts +32 -0
- package/dist/resources/discovery-safety.js +521 -0
- package/dist/resources/discovery.d.ts +59 -16
- package/dist/resources/discovery.js +147 -47
- package/dist/resources/feeds.d.ts +3 -1
- package/dist/resources/judgments.d.ts +252 -0
- package/dist/resources/judgments.js +79 -0
- package/dist/resources/protocols.d.ts +196 -15
- package/dist/resources/protocols.js +16 -2
- package/dist/resources/search.d.ts +9 -1
- package/dist/resources/settings.d.ts +8 -1
- package/dist/resources/settings.js +9 -0
- package/dist/resources/signals.d.ts +58 -0
- package/dist/resources/signals.js +32 -0
- package/dist/types.d.ts +30 -0
- package/llm.md +22 -6
- package/package.json +1 -1
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import type { HttpClient } from '../client.js';
|
|
2
|
+
import { ConflictError } from '../errors.js';
|
|
3
|
+
import type { SingleResponse } from '../types.js';
|
|
4
|
+
/** A Minds post (`post`, its uuid) or an open-network post (`network_post`, its subject id). */
|
|
5
|
+
export type JudgmentSubjectKind = 'post' | 'network_post';
|
|
6
|
+
/** Who wrote the label: Jev (`model`), or staff who overrode it (`review`). */
|
|
7
|
+
export type JudgmentSource = 'model' | 'review';
|
|
8
|
+
/** Whether the server's judgment store answered. `'unavailable'`: only the category index's label is known; hide label actions. */
|
|
9
|
+
export type PostLabelsStatus = 'ok' | 'unavailable';
|
|
10
|
+
/** How an override met its quorum: one reviewer (a safety appeal), two agreeing reviewers, or one beside an agreeing Jev re-judge. */
|
|
11
|
+
export type JudgmentOverrideMethod = 'reviewer' | 'two_reviewers' | 'reviewer_and_rejudge';
|
|
12
|
+
export interface PostLabelsQuery {
|
|
13
|
+
kind: JudgmentSubjectKind;
|
|
14
|
+
/** A Minds post id, or an open-network post's subject id. */
|
|
15
|
+
id: string;
|
|
16
|
+
}
|
|
17
|
+
export interface PostLabelsSubject {
|
|
18
|
+
kind: JudgmentSubjectKind;
|
|
19
|
+
id: string;
|
|
20
|
+
}
|
|
21
|
+
export interface PostLabelsCategory {
|
|
22
|
+
id: string;
|
|
23
|
+
name: string;
|
|
24
|
+
}
|
|
25
|
+
/** The compass band in words. Never a probability or a cell code. */
|
|
26
|
+
export interface PostLabelsPolitics {
|
|
27
|
+
/** The judgment a member disputes with `dispute(judgment_id, { question_key: 'placement', proposed })`. */
|
|
28
|
+
judgment_id: string;
|
|
29
|
+
politics: 'Takes a side' | 'Maybe' | 'No';
|
|
30
|
+
/** Each placed axis in words, e.g. `{ axis: 'Liberty ↔ Order', band: 'Order' }`. Empty unless the post takes a side. */
|
|
31
|
+
axes: Array<{
|
|
32
|
+
axis: string;
|
|
33
|
+
band: string;
|
|
34
|
+
}>;
|
|
35
|
+
model: string;
|
|
36
|
+
rubric: string;
|
|
37
|
+
source: JudgmentSource;
|
|
38
|
+
judged_at: string | null;
|
|
39
|
+
}
|
|
40
|
+
/** One stored judgment, as the post's author and staff see it. */
|
|
41
|
+
export interface PostJudgmentRecord {
|
|
42
|
+
id: string;
|
|
43
|
+
rubric: string;
|
|
44
|
+
model: string;
|
|
45
|
+
source: JudgmentSource;
|
|
46
|
+
judged_at: string | null;
|
|
47
|
+
/** The typed answers, every probability included. */
|
|
48
|
+
answers: Record<string, unknown>;
|
|
49
|
+
political: number | null;
|
|
50
|
+
cell: string | null;
|
|
51
|
+
reason: 'hate' | 'spam' | null;
|
|
52
|
+
}
|
|
53
|
+
/** A rubric's current judgment in the full view. */
|
|
54
|
+
export interface PostJudgment extends PostJudgmentRecord {
|
|
55
|
+
/** How many staff reviewers wrote it (0 for Jev's own). */
|
|
56
|
+
reviewers: number;
|
|
57
|
+
/** Staff only. */
|
|
58
|
+
reviewer_ids?: string[];
|
|
59
|
+
/** Staff only. */
|
|
60
|
+
review_note?: string | null;
|
|
61
|
+
/** Whether `dispute()` takes it: political-compass and safety labels (safety: the author only). */
|
|
62
|
+
disputable: boolean;
|
|
63
|
+
/** Jev's judgment a staff review replaced, for the same words. */
|
|
64
|
+
superseded: PostJudgmentRecord | null;
|
|
65
|
+
}
|
|
66
|
+
/** The safety verdict the Discover floor applies to the post now, with who judged it (full view, Minds posts). */
|
|
67
|
+
export type PostSafetyAttribution = {
|
|
68
|
+
status: 'judged';
|
|
69
|
+
reason: 'hate' | 'spam' | null;
|
|
70
|
+
unsafe: boolean;
|
|
71
|
+
hate: number;
|
|
72
|
+
spam: number;
|
|
73
|
+
rubric: string;
|
|
74
|
+
/** The judging model, `'review'` for a staff review; null when only the cache holds the verdict. */
|
|
75
|
+
model: string | null;
|
|
76
|
+
source: JudgmentSource | 'cache';
|
|
77
|
+
judgedAt: string | null;
|
|
78
|
+
/** A staff review the floor does not apply yet (the cache still holds another verdict). */
|
|
79
|
+
unappliedReview?: {
|
|
80
|
+
reason: 'hate' | 'spam' | null;
|
|
81
|
+
unsafe: boolean;
|
|
82
|
+
hate: number;
|
|
83
|
+
spam: number;
|
|
84
|
+
judgedAt: string;
|
|
85
|
+
};
|
|
86
|
+
} | {
|
|
87
|
+
status: 'pending';
|
|
88
|
+
} | {
|
|
89
|
+
status: 'not_judgeable';
|
|
90
|
+
};
|
|
91
|
+
/** A visitor's view (`getPublic`): the category only. */
|
|
92
|
+
export interface PostLabelsCategoryView {
|
|
93
|
+
subject: PostLabelsSubject;
|
|
94
|
+
view: 'category';
|
|
95
|
+
category: PostLabelsCategory | null;
|
|
96
|
+
status: PostLabelsStatus;
|
|
97
|
+
}
|
|
98
|
+
/** Any other member's view: the category and the compass band in words, with the id to dispute it. */
|
|
99
|
+
export interface PostLabelsSummaryView {
|
|
100
|
+
subject: PostLabelsSubject;
|
|
101
|
+
view: 'summary';
|
|
102
|
+
category: PostLabelsCategory | null;
|
|
103
|
+
politics: PostLabelsPolitics | null;
|
|
104
|
+
status: PostLabelsStatus;
|
|
105
|
+
}
|
|
106
|
+
/** The post's author's and staff's view: every rubric's current judgment, the safety verdict and Viral. */
|
|
107
|
+
export interface PostLabelsFullView {
|
|
108
|
+
subject: PostLabelsSubject;
|
|
109
|
+
view: 'full';
|
|
110
|
+
category: PostLabelsCategory | null;
|
|
111
|
+
politics: PostLabelsPolitics | null;
|
|
112
|
+
judgments: PostJudgment[];
|
|
113
|
+
/** null for an open-network post. */
|
|
114
|
+
safety: PostSafetyAttribution | null;
|
|
115
|
+
/** Why the post is withheld from Viral ("Withheld from Viral: <reason>"), or null. */
|
|
116
|
+
viral: {
|
|
117
|
+
withheld: string | null;
|
|
118
|
+
};
|
|
119
|
+
status: PostLabelsStatus;
|
|
120
|
+
}
|
|
121
|
+
/** One post's labels, in the view the caller may see: switch on `view`. */
|
|
122
|
+
export type PostLabels = PostLabelsFullView | PostLabelsSummaryView | PostLabelsCategoryView;
|
|
123
|
+
export interface DisputeJudgmentInput {
|
|
124
|
+
/** The label's question: `'placement'` for a political-compass label, `'safety'` for a safety label (its author only). */
|
|
125
|
+
question_key: string;
|
|
126
|
+
/** The answer it should be: a compass cell (`'lo'`, `'cm'`, …) or `'not_political'`; for safety `'none'`, `'hate'` or `'spam'`. */
|
|
127
|
+
proposed: string;
|
|
128
|
+
/** At most 500 characters. */
|
|
129
|
+
note?: string;
|
|
130
|
+
}
|
|
131
|
+
/** A filed dispute. The author's is an appeal and goes to the moderation queue; anyone else's feeds the evaluation. */
|
|
132
|
+
export interface JudgmentDispute {
|
|
133
|
+
id: string;
|
|
134
|
+
judgment_id: string;
|
|
135
|
+
/** True when the caller wrote the post: an appeal. */
|
|
136
|
+
appeal: boolean;
|
|
137
|
+
/** Whether it waits in the moderation queue now. */
|
|
138
|
+
queued: boolean;
|
|
139
|
+
/** Present on a repeat: the member's first dispute of this judgment, unchanged. */
|
|
140
|
+
existing?: true;
|
|
141
|
+
}
|
|
142
|
+
export type ReviewJudgmentInput = {
|
|
143
|
+
outcome: 'uphold';
|
|
144
|
+
note?: string;
|
|
145
|
+
} | {
|
|
146
|
+
outcome: 'override';
|
|
147
|
+
/** The answer the label becomes. */
|
|
148
|
+
proposed: string;
|
|
149
|
+
/** At most 2,000 characters. */
|
|
150
|
+
note?: string;
|
|
151
|
+
/** A safety override to `'none'` that frees other posts with exactly these words: send true after a 409 `shared_words`. */
|
|
152
|
+
apply_to_shared?: boolean;
|
|
153
|
+
};
|
|
154
|
+
/** 200: the label stands, and its queue items are closed. */
|
|
155
|
+
export interface JudgmentReviewUpheld {
|
|
156
|
+
status: 'upheld';
|
|
157
|
+
resolved: number;
|
|
158
|
+
}
|
|
159
|
+
/** 202: this reviewer's vote is recorded; a compass label needs another reviewer (or an agreeing Jev re-judge). */
|
|
160
|
+
export interface JudgmentReviewAwaitingReviewer {
|
|
161
|
+
status: 'awaiting_reviewer';
|
|
162
|
+
vote_id: string;
|
|
163
|
+
agreeing: number;
|
|
164
|
+
needed: number;
|
|
165
|
+
}
|
|
166
|
+
/** 200: the override is written and in the public moderation log. */
|
|
167
|
+
export interface JudgmentReviewOverridden {
|
|
168
|
+
status: 'overridden';
|
|
169
|
+
review_id: string;
|
|
170
|
+
moderation_action_id: string;
|
|
171
|
+
method: JudgmentOverrideMethod;
|
|
172
|
+
reviewers: number;
|
|
173
|
+
resolved: number;
|
|
174
|
+
trust_log: 'appended' | 'disabled' | 'duplicate';
|
|
175
|
+
/** A safety review: whether the Discover floor applies it now; null for a compass label. */
|
|
176
|
+
applied_to_discover: boolean | null;
|
|
177
|
+
/** A safety review of words the post no longer has: recorded, never applied. */
|
|
178
|
+
words_changed: boolean | null;
|
|
179
|
+
shared_posts: number | null;
|
|
180
|
+
}
|
|
181
|
+
export type JudgmentReview = JudgmentReviewUpheld | JudgmentReviewAwaitingReviewer | JudgmentReviewOverridden;
|
|
182
|
+
/** The `details` of a 409 `shared_words` from `review()`. */
|
|
183
|
+
export interface JudgmentSharedWordsDetails {
|
|
184
|
+
shared_posts: number;
|
|
185
|
+
}
|
|
186
|
+
/** The 409 `review()` throws before a safety override frees other posts with exactly these words. */
|
|
187
|
+
export type JudgmentSharedWordsError = ConflictError & {
|
|
188
|
+
code: 'shared_words';
|
|
189
|
+
details: JudgmentSharedWordsDetails;
|
|
190
|
+
};
|
|
191
|
+
/**
|
|
192
|
+
* Whether `error` is the 409 `shared_words` of `review()`: nothing was recorded, and
|
|
193
|
+
* `error.details.shared_posts` other posts have exactly these words. Resend with
|
|
194
|
+
* `apply_to_shared: true` once the reviewer agrees to change them all.
|
|
195
|
+
*/
|
|
196
|
+
export declare function isJudgmentSharedWordsError(error: unknown): error is JudgmentSharedWordsError;
|
|
197
|
+
/**
|
|
198
|
+
* Jev's labels of one post: read them, dispute or appeal one, and (staff)
|
|
199
|
+
* decide a dispute.
|
|
200
|
+
*
|
|
201
|
+
* Budgets: 60 label reads per member an hour (25 of one author's posts) and
|
|
202
|
+
* 20 disputes a day. Past one, the server answers 429 with
|
|
203
|
+
* `X-RateLimit-Scope: action`: the SDK throws `RateLimitError` at once
|
|
204
|
+
* (`scope === 'action'`, `resetAt`, a member-facing `message`) and never
|
|
205
|
+
* retries it. Disputes and reviews answer 503 until the server's judgment
|
|
206
|
+
* store is applied; reads answer `status: 'unavailable'` instead.
|
|
207
|
+
*/
|
|
208
|
+
export declare class JudgmentsResource {
|
|
209
|
+
private client;
|
|
210
|
+
constructor(client: HttpClient);
|
|
211
|
+
/**
|
|
212
|
+
* One post's labels as the caller may see them. The post's author and staff
|
|
213
|
+
* get `view: 'full'` (every judgment with its answers, the safety verdict,
|
|
214
|
+
* Viral); any other member who can see the post gets `view: 'summary'` (the
|
|
215
|
+
* category and the compass band in words). `status: 'unavailable'` is
|
|
216
|
+
* returned, not thrown. A post the caller cannot see throws `NotFoundError`.
|
|
217
|
+
* Open-network posts need a key bound to an app (400 `app_required`).
|
|
218
|
+
*
|
|
219
|
+
* @example
|
|
220
|
+
* const { data } = await r.judgments.get({ kind: 'post', id: postId });
|
|
221
|
+
* if (data.status === 'ok' && data.view !== 'category' && data.politics) showBand(data.politics);
|
|
222
|
+
*/
|
|
223
|
+
get(query: PostLabelsQuery): Promise<SingleResponse<PostLabels>>;
|
|
224
|
+
/**
|
|
225
|
+
* A signed-out visitor's view of one post's labels: the category only.
|
|
226
|
+
* Keyless: use an anonymous client (`new Recursiv({ anonymous: true })`).
|
|
227
|
+
*/
|
|
228
|
+
getPublic(projectId: string, query: PostLabelsQuery): Promise<SingleResponse<PostLabelsCategoryView>>;
|
|
229
|
+
/**
|
|
230
|
+
* Disagree with a label. The post's author's dispute is an appeal and goes
|
|
231
|
+
* to the moderation queue; anyone else's feeds the evaluation and changes
|
|
232
|
+
* nothing on its own. One per member and judgment: a repeat returns the
|
|
233
|
+
* first with `existing: true`. Sent once, never retried automatically.
|
|
234
|
+
*
|
|
235
|
+
* @example
|
|
236
|
+
* await r.judgments.dispute(labels.politics.judgment_id, { question_key: 'placement', proposed: 'not_political' });
|
|
237
|
+
*/
|
|
238
|
+
dispute(judgmentId: string, input: DisputeJudgmentInput): Promise<SingleResponse<JudgmentDispute>>;
|
|
239
|
+
/**
|
|
240
|
+
* Staff: uphold a disputed label, or vote to override it. A compass label
|
|
241
|
+
* changes when two reviewers propose the same answer (or one, beside an
|
|
242
|
+
* agreeing Jev re-judge): the first gets `status: 'awaiting_reviewer'`
|
|
243
|
+
* (HTTP 202). Every override is in the public moderation log.
|
|
244
|
+
*
|
|
245
|
+
* Sent once, never retried: an override that met its quorum but lost its
|
|
246
|
+
* response would otherwise be recorded again as a fresh vote. A safety
|
|
247
|
+
* override to `'none'` that would free other posts with exactly these words
|
|
248
|
+
* throws a 409 (`isJudgmentSharedWordsError`, `details.shared_posts`) until
|
|
249
|
+
* resent with `apply_to_shared: true`.
|
|
250
|
+
*/
|
|
251
|
+
review(judgmentId: string, input: ReviewJudgmentInput): Promise<SingleResponse<JudgmentReview>>;
|
|
252
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { ConflictError } from '../errors.js';
|
|
2
|
+
/**
|
|
3
|
+
* Whether `error` is the 409 `shared_words` of `review()`: nothing was recorded, and
|
|
4
|
+
* `error.details.shared_posts` other posts have exactly these words. Resend with
|
|
5
|
+
* `apply_to_shared: true` once the reviewer agrees to change them all.
|
|
6
|
+
*/
|
|
7
|
+
export function isJudgmentSharedWordsError(error) {
|
|
8
|
+
if (!(error instanceof ConflictError) || error.code !== 'shared_words')
|
|
9
|
+
return false;
|
|
10
|
+
const count = error.details?.shared_posts;
|
|
11
|
+
return typeof count === 'number' && Number.isFinite(count);
|
|
12
|
+
}
|
|
13
|
+
const path = (judgmentId, action) => `/content-judgments/${encodeURIComponent(judgmentId)}/${action}`;
|
|
14
|
+
/**
|
|
15
|
+
* Jev's labels of one post: read them, dispute or appeal one, and (staff)
|
|
16
|
+
* decide a dispute.
|
|
17
|
+
*
|
|
18
|
+
* Budgets: 60 label reads per member an hour (25 of one author's posts) and
|
|
19
|
+
* 20 disputes a day. Past one, the server answers 429 with
|
|
20
|
+
* `X-RateLimit-Scope: action`: the SDK throws `RateLimitError` at once
|
|
21
|
+
* (`scope === 'action'`, `resetAt`, a member-facing `message`) and never
|
|
22
|
+
* retries it. Disputes and reviews answer 503 until the server's judgment
|
|
23
|
+
* store is applied; reads answer `status: 'unavailable'` instead.
|
|
24
|
+
*/
|
|
25
|
+
export class JudgmentsResource {
|
|
26
|
+
client;
|
|
27
|
+
constructor(client) {
|
|
28
|
+
this.client = client;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* One post's labels as the caller may see them. The post's author and staff
|
|
32
|
+
* get `view: 'full'` (every judgment with its answers, the safety verdict,
|
|
33
|
+
* Viral); any other member who can see the post gets `view: 'summary'` (the
|
|
34
|
+
* category and the compass band in words). `status: 'unavailable'` is
|
|
35
|
+
* returned, not thrown. A post the caller cannot see throws `NotFoundError`.
|
|
36
|
+
* Open-network posts need a key bound to an app (400 `app_required`).
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* const { data } = await r.judgments.get({ kind: 'post', id: postId });
|
|
40
|
+
* if (data.status === 'ok' && data.view !== 'category' && data.politics) showBand(data.politics);
|
|
41
|
+
*/
|
|
42
|
+
get(query) {
|
|
43
|
+
return this.client.get('/content-judgments', { kind: query.kind, id: query.id });
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* A signed-out visitor's view of one post's labels: the category only.
|
|
47
|
+
* Keyless: use an anonymous client (`new Recursiv({ anonymous: true })`).
|
|
48
|
+
*/
|
|
49
|
+
getPublic(projectId, query) {
|
|
50
|
+
return this.client.get(`/protocols/public/${encodeURIComponent(projectId)}/content-judgments`, { kind: query.kind, id: query.id });
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Disagree with a label. The post's author's dispute is an appeal and goes
|
|
54
|
+
* to the moderation queue; anyone else's feeds the evaluation and changes
|
|
55
|
+
* nothing on its own. One per member and judgment: a repeat returns the
|
|
56
|
+
* first with `existing: true`. Sent once, never retried automatically.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* await r.judgments.dispute(labels.politics.judgment_id, { question_key: 'placement', proposed: 'not_political' });
|
|
60
|
+
*/
|
|
61
|
+
dispute(judgmentId, input) {
|
|
62
|
+
return this.client.post(path(judgmentId, 'disputes'), input, { retry: false });
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Staff: uphold a disputed label, or vote to override it. A compass label
|
|
66
|
+
* changes when two reviewers propose the same answer (or one, beside an
|
|
67
|
+
* agreeing Jev re-judge): the first gets `status: 'awaiting_reviewer'`
|
|
68
|
+
* (HTTP 202). Every override is in the public moderation log.
|
|
69
|
+
*
|
|
70
|
+
* Sent once, never retried: an override that met its quorum but lost its
|
|
71
|
+
* response would otherwise be recorded again as a fresh vote. A safety
|
|
72
|
+
* override to `'none'` that would free other posts with exactly these words
|
|
73
|
+
* throws a 409 (`isJudgmentSharedWordsError`, `details.shared_posts`) until
|
|
74
|
+
* resent with `apply_to_shared: true`.
|
|
75
|
+
*/
|
|
76
|
+
review(judgmentId, input) {
|
|
77
|
+
return this.client.post(path(judgmentId, 'reviews'), input, { retry: false });
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -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
|
-
/**
|
|
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
|
}>;
|
|
@@ -1761,7 +1931,7 @@ export declare class ProtocolsResource {
|
|
|
1761
1931
|
}): Promise<SingleResponse<NativeRepostReview>>;
|
|
1762
1932
|
/** Reconcile the saved action without sending another native write. */
|
|
1763
1933
|
nativeRepostStatus(id: string): Promise<SingleResponse<NativeRepostReview>>;
|
|
1764
|
-
/** Prepare exact public text
|
|
1934
|
+
/** Prepare exact public text, reviewed images and an optional native quote for selected connected Bluesky/ActivityPub accounts. */
|
|
1765
1935
|
prepareNativePost(input: NativePostPrepareInput): Promise<SingleResponse<NativePostBatch>>;
|
|
1766
1936
|
/** Explicitly approve selected deliveries; never auto-retry an uncertain write. */
|
|
1767
1937
|
submitNativePost(id: string, input: {
|
|
@@ -2005,6 +2175,17 @@ export declare class ProtocolsResource {
|
|
|
2005
2175
|
* project-admin authorization as listCandidates.
|
|
2006
2176
|
*/
|
|
2007
2177
|
ingestionStatus(): Promise<SingleResponse<ProtocolIngestionStatus>>;
|
|
2178
|
+
/**
|
|
2179
|
+
* Read counts-only ingestion health for the key's app: per lane, whether it
|
|
2180
|
+
* reaches done or dies part way (the open job's position, failures in a row,
|
|
2181
|
+
* starvation, last success and failure, attempts by reason, dead letters by
|
|
2182
|
+
* reason and position), plus open or counting circuits. Read-only. Needs a
|
|
2183
|
+
* project-bound admin key and current project-admin authority, but not the
|
|
2184
|
+
* content_intelligence_admin binding. Scope comes only from the key, so no
|
|
2185
|
+
* query is sent (the API rejects one). A section that does not answer in its
|
|
2186
|
+
* budget reads as null and is listed in `unavailable`.
|
|
2187
|
+
*/
|
|
2188
|
+
ingestionHealth(): Promise<SingleResponse<ProtocolIngestionHealthReport>>;
|
|
2008
2189
|
/** Turn the app's ingestion kill switch on: no reservation, screening or approval proceeds. */
|
|
2009
2190
|
killIngestion(): Promise<SingleResponse<{
|
|
2010
2191
|
kill_switch: true;
|
|
@@ -327,7 +327,7 @@ export class ProtocolsResource {
|
|
|
327
327
|
hostedIdentityPath(id);
|
|
328
328
|
return this.client.get(`/protocols/social/reposts/${encodeURIComponent(id)}`);
|
|
329
329
|
}
|
|
330
|
-
/** Prepare exact public text
|
|
330
|
+
/** Prepare exact public text, reviewed images and an optional native quote for selected connected Bluesky/ActivityPub accounts. */
|
|
331
331
|
prepareNativePost(input) {
|
|
332
332
|
return this.client.post('/protocols/social/posts/prepare', { clientMutationId: input.clientMutationId,
|
|
333
333
|
text: input.text, accountIds: input.accountIds,
|
|
@@ -479,7 +479,8 @@ export class ProtocolsResource {
|
|
|
479
479
|
prepareNativeNostr(id, input) {
|
|
480
480
|
hostedIdentityPath(id);
|
|
481
481
|
const body = input.action === 'publish' ? { action: input.action, content: input.content,
|
|
482
|
-
...(input.crossPostOf !== undefined ? { crossPostOf: input.crossPostOf } : {})
|
|
482
|
+
...(input.crossPostOf !== undefined ? { crossPostOf: input.crossPostOf } : {}),
|
|
483
|
+
...(input.quote ? { quote: { protocol: input.quote.protocol, nativeId: input.quote.nativeId } } : {}) }
|
|
483
484
|
: input.action === 'reply' ? { action: input.action, content: input.content, parentId: input.parentId }
|
|
484
485
|
: input.action === 'linked_post' ? { action: input.action, content: input.content,
|
|
485
486
|
source: { protocol: input.source.protocol, nativeId: input.source.nativeId,
|
|
@@ -619,6 +620,19 @@ export class ProtocolsResource {
|
|
|
619
620
|
ingestionStatus() {
|
|
620
621
|
return this.client.get('/protocols/ingestion/status');
|
|
621
622
|
}
|
|
623
|
+
/**
|
|
624
|
+
* Read counts-only ingestion health for the key's app: per lane, whether it
|
|
625
|
+
* reaches done or dies part way (the open job's position, failures in a row,
|
|
626
|
+
* starvation, last success and failure, attempts by reason, dead letters by
|
|
627
|
+
* reason and position), plus open or counting circuits. Read-only. Needs a
|
|
628
|
+
* project-bound admin key and current project-admin authority, but not the
|
|
629
|
+
* content_intelligence_admin binding. Scope comes only from the key, so no
|
|
630
|
+
* query is sent (the API rejects one). A section that does not answer in its
|
|
631
|
+
* budget reads as null and is listed in `unavailable`.
|
|
632
|
+
*/
|
|
633
|
+
ingestionHealth() {
|
|
634
|
+
return this.client.get('/protocols/ingestion/health');
|
|
635
|
+
}
|
|
622
636
|
/** Turn the app's ingestion kill switch on: no reservation, screening or approval proceeds. */
|
|
623
637
|
killIngestion() {
|
|
624
638
|
return this.client.post('/protocols/ingestion/kill', {});
|