@recursiv/sdk 0.7.82 → 0.7.89
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 +128 -1
- package/dist/assistant/fixedCardData.d.ts +91 -0
- package/dist/assistant/fixedCardData.js +372 -0
- package/dist/assistant/fixedCards.d.ts +35 -0
- package/dist/assistant/fixedCards.js +904 -0
- package/dist/assistant/hiddenText.d.ts +24 -0
- package/dist/assistant/hiddenText.js +263 -0
- package/dist/assistant/index.d.ts +30 -0
- package/dist/assistant/index.js +30 -0
- package/dist/assistant/render.d.ts +31 -0
- package/dist/assistant/render.js +434 -0
- package/dist/assistant/subjectBundle.d.ts +30 -0
- package/dist/assistant/subjectBundle.js +535 -0
- package/dist/assistant/turnCaps.d.ts +3 -0
- package/dist/assistant/turnCaps.js +141 -0
- package/dist/assistant/types.d.ts +569 -0
- package/dist/assistant/types.js +26 -0
- package/dist/assistant-surfaces.d.ts +42 -0
- package/dist/assistant-surfaces.js +113 -0
- package/dist/index.d.ts +15 -6
- package/dist/index.js +8 -1
- package/dist/privacy/facts.generated.d.ts +79 -0
- package/dist/privacy/facts.generated.js +93 -0
- package/dist/privacy/index.d.ts +48 -0
- package/dist/privacy/index.js +42 -0
- package/dist/realtime.d.ts +10 -3
- package/dist/realtime.js +11 -2
- package/dist/resources/agents.d.ts +6 -1
- package/dist/resources/agents.js +12 -2
- package/dist/resources/discovery-merge.d.ts +17 -1
- package/dist/resources/discovery-merge.js +84 -4
- package/dist/resources/discovery-progressive.d.ts +4 -3
- package/dist/resources/discovery-progressive.js +11 -8
- package/dist/resources/discovery.d.ts +9 -1
- package/dist/resources/discovery.js +62 -15
- package/dist/resources/e2ee.d.ts +56 -0
- package/dist/resources/e2ee.js +21 -0
- package/dist/resources/feeds.d.ts +15 -6
- package/dist/resources/feeds.js +2 -1
- package/dist/resources/integrations.d.ts +3 -0
- package/dist/resources/link-preview.d.ts +42 -1
- package/dist/resources/link-preview.js +36 -2
- package/dist/resources/network.d.ts +6 -0
- package/dist/resources/privacy.d.ts +13 -0
- package/dist/resources/privacy.js +14 -0
- package/dist/resources/project-brain.d.ts +8 -0
- package/dist/resources/protocols.d.ts +99 -11
- package/dist/resources/protocols.js +45 -0
- package/dist/resources/sealed.d.ts +127 -7
- package/dist/resources/sealed.js +33 -6
- package/dist/resources/settings.d.ts +3 -1
- package/dist/resources/settings.js +3 -1
- package/dist/resources/users.d.ts +11 -2
- package/dist/resources/users.js +1 -1
- package/dist/social-protocol-ids.d.ts +28 -0
- package/dist/social-protocol-ids.js +30 -0
- package/dist/types.d.ts +71 -0
- package/llm.md +45 -4
- package/package.json +11 -1
|
@@ -17,6 +17,30 @@ export interface LinkPreviewResponse {
|
|
|
17
17
|
*/
|
|
18
18
|
data: LinkPreview | null;
|
|
19
19
|
cached?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* `'ai_link'`: the link is not in the named message in a person's own words,
|
|
22
|
+
* and the caller did not write the same address in that conversation, so the
|
|
23
|
+
* server did not fetch it (it may be an AI's). Show the plain link, and do not
|
|
24
|
+
* ask again for this message. Only on a request that named its message or
|
|
25
|
+
* conversation (`source`).
|
|
26
|
+
*/
|
|
27
|
+
refused?: 'ai_link';
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Where a chat link came from. Name the message when you have its id; the
|
|
31
|
+
* conversation alone when you do not have it yet (an AI reply still being
|
|
32
|
+
* saved). With the conversation alone, a link is previewed only when the caller
|
|
33
|
+
* wrote it there, so another member's message not yet saved waits for its id.
|
|
34
|
+
* At least one of the two must be a non-empty id: `get` rejects otherwise and
|
|
35
|
+
* never falls back to a request without a source.
|
|
36
|
+
*
|
|
37
|
+
* Not for an encrypted conversation: the server has no readable messages there,
|
|
38
|
+
* so every link would be refused, the member's own included. There the app
|
|
39
|
+
* decides by its own rule (no preview of a link the member did not write).
|
|
40
|
+
*/
|
|
41
|
+
export interface LinkPreviewSource {
|
|
42
|
+
messageId?: string;
|
|
43
|
+
conversationId?: string;
|
|
20
44
|
}
|
|
21
45
|
export declare class LinkPreviewResource {
|
|
22
46
|
private client;
|
|
@@ -27,6 +51,23 @@ export declare class LinkPreviewResource {
|
|
|
27
51
|
* preview (`data: null`). No auth required — the endpoint is intentionally
|
|
28
52
|
* public so unauthenticated UIs can render link previews. A URL that is not
|
|
29
53
|
* http(s), or points at a private or internal address, still rejects (422).
|
|
54
|
+
*
|
|
55
|
+
* For a link in a chat message, pass `source`: the server then previews the
|
|
56
|
+
* link only when it is in the named message and a person wrote that message,
|
|
57
|
+
* or when the caller wrote the same address in that conversation (in a
|
|
58
|
+
* message, or as the card link of a Minds post they shared there). Otherwise
|
|
59
|
+
* it answers `{ data: null, refused: 'ai_link' }` without fetching it. A
|
|
60
|
+
* request with `source` is the caller's own: make it on a signed-in client
|
|
61
|
+
* whose key has `chat:read` (a keyless one gets 401). A message or
|
|
62
|
+
* conversation the caller cannot read rejects with 404. A `source` with no
|
|
63
|
+
* id in it (an AI reply whose id is not known yet, say) rejects with a
|
|
64
|
+
* ValidationError and sends nothing. Do not pass `source` in an encrypted
|
|
65
|
+
* conversation (see LinkPreviewSource). Servers before this change ignore
|
|
66
|
+
* `source` and preview the link, so an app keeps its own rule too: no
|
|
67
|
+
* preview of an AI-written link the member did not write.
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* const { data, refused } = await r.linkPreview.get(url, { messageId: message.id });
|
|
30
71
|
*/
|
|
31
|
-
get(url: string): Promise<LinkPreviewResponse>;
|
|
72
|
+
get(url: string, source?: LinkPreviewSource | null): Promise<LinkPreviewResponse>;
|
|
32
73
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ValidationError } from '../errors.js';
|
|
1
2
|
export class LinkPreviewResource {
|
|
2
3
|
client;
|
|
3
4
|
constructor(client) {
|
|
@@ -9,8 +10,41 @@ export class LinkPreviewResource {
|
|
|
9
10
|
* preview (`data: null`). No auth required — the endpoint is intentionally
|
|
10
11
|
* public so unauthenticated UIs can render link previews. A URL that is not
|
|
11
12
|
* http(s), or points at a private or internal address, still rejects (422).
|
|
13
|
+
*
|
|
14
|
+
* For a link in a chat message, pass `source`: the server then previews the
|
|
15
|
+
* link only when it is in the named message and a person wrote that message,
|
|
16
|
+
* or when the caller wrote the same address in that conversation (in a
|
|
17
|
+
* message, or as the card link of a Minds post they shared there). Otherwise
|
|
18
|
+
* it answers `{ data: null, refused: 'ai_link' }` without fetching it. A
|
|
19
|
+
* request with `source` is the caller's own: make it on a signed-in client
|
|
20
|
+
* whose key has `chat:read` (a keyless one gets 401). A message or
|
|
21
|
+
* conversation the caller cannot read rejects with 404. A `source` with no
|
|
22
|
+
* id in it (an AI reply whose id is not known yet, say) rejects with a
|
|
23
|
+
* ValidationError and sends nothing. Do not pass `source` in an encrypted
|
|
24
|
+
* conversation (see LinkPreviewSource). Servers before this change ignore
|
|
25
|
+
* `source` and preview the link, so an app keeps its own rule too: no
|
|
26
|
+
* preview of an AI-written link the member did not write.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* const { data, refused } = await r.linkPreview.get(url, { messageId: message.id });
|
|
12
30
|
*/
|
|
13
|
-
get(url) {
|
|
14
|
-
|
|
31
|
+
async get(url, source) {
|
|
32
|
+
const params = { url };
|
|
33
|
+
if (source != null) {
|
|
34
|
+
// A source with no id must not turn into a bare request: the server would
|
|
35
|
+
// then fetch the link with no check at all.
|
|
36
|
+
if (!source.messageId && !source.conversationId) {
|
|
37
|
+
throw new ValidationError({
|
|
38
|
+
type: 'validation_error',
|
|
39
|
+
code: 'link_preview_source_missing',
|
|
40
|
+
message: 'linkPreview.get: source needs a messageId or a conversationId. Wait for the id, or show the plain link.',
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
if (source.messageId)
|
|
44
|
+
params.message_id = source.messageId;
|
|
45
|
+
if (source.conversationId)
|
|
46
|
+
params.conversation_id = source.conversationId;
|
|
47
|
+
}
|
|
48
|
+
return this.client.get('/link-preview', params);
|
|
15
49
|
}
|
|
16
50
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { HttpClient } from '../client.js';
|
|
2
2
|
import type { SingleResponse } from '../types.js';
|
|
3
|
+
import type { AssistantSettings } from '../assistant-surfaces.js';
|
|
3
4
|
interface NetworkConfig {
|
|
4
5
|
name: string;
|
|
5
6
|
slug: string | null;
|
|
@@ -25,6 +26,11 @@ interface NetworkConfig {
|
|
|
25
26
|
auth_methods: string;
|
|
26
27
|
default_template_name: string | null;
|
|
27
28
|
locale: string;
|
|
29
|
+
/**
|
|
30
|
+
* The assistant surface switches for the app this key is bound to (absent on servers from before George plan G7a).
|
|
31
|
+
* Read them with `assistantSurfaceOn(config, id)`, which is off for anything but an explicit true.
|
|
32
|
+
*/
|
|
33
|
+
assistant?: AssistantSettings;
|
|
28
34
|
}
|
|
29
35
|
export declare class NetworkResource {
|
|
30
36
|
private client;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { HttpClient } from '../client.js';
|
|
2
|
+
import type { PrivacyCapabilities } from '../privacy/index.js';
|
|
3
|
+
import type { SingleResponse } from '../types.js';
|
|
4
|
+
/**
|
|
5
|
+
* What the deployed API enforces. Pair it with `@recursiv/sdk/privacy`, which says what each fact means and whether
|
|
6
|
+
* the installed SDK marks it enforced: render a privacy sentence only when both do (`isFactLive`).
|
|
7
|
+
*/
|
|
8
|
+
export declare class PrivacyResource {
|
|
9
|
+
private client;
|
|
10
|
+
constructor(client: HttpClient);
|
|
11
|
+
/** GET /privacy/capabilities: the platform facts this deployment's build enforces, and its commit. Any API key. */
|
|
12
|
+
capabilities(): Promise<SingleResponse<PrivacyCapabilities>>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the deployed API enforces. Pair it with `@recursiv/sdk/privacy`, which says what each fact means and whether
|
|
3
|
+
* the installed SDK marks it enforced: render a privacy sentence only when both do (`isFactLive`).
|
|
4
|
+
*/
|
|
5
|
+
export class PrivacyResource {
|
|
6
|
+
client;
|
|
7
|
+
constructor(client) {
|
|
8
|
+
this.client = client;
|
|
9
|
+
}
|
|
10
|
+
/** GET /privacy/capabilities: the platform facts this deployment's build enforces, and its commit. Any API key. */
|
|
11
|
+
capabilities() {
|
|
12
|
+
return this.client.get('/privacy/capabilities');
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { HttpClient } from '../client.js';
|
|
2
2
|
import type { SingleResponse } from '../types.js';
|
|
3
|
+
import type { AssistantSettings, AssistantSettingsUpdate } from '../assistant-surfaces.js';
|
|
3
4
|
export interface BrainTask {
|
|
4
5
|
id: string;
|
|
5
6
|
title: string;
|
|
@@ -153,6 +154,8 @@ export interface ProjectBrainSettings {
|
|
|
153
154
|
maxStepsPerRun?: number;
|
|
154
155
|
heartbeatTimeoutMinutes?: number;
|
|
155
156
|
dailySpendingLimitUsd?: number;
|
|
157
|
+
/** The assistant surface switches: every surface id, off unless stored as true (absent on older servers). */
|
|
158
|
+
assistant?: AssistantSettings;
|
|
156
159
|
[key: string]: unknown;
|
|
157
160
|
}
|
|
158
161
|
export interface UpdateProjectSettingsInput {
|
|
@@ -161,6 +164,11 @@ export interface UpdateProjectSettingsInput {
|
|
|
161
164
|
max_steps_per_run?: number;
|
|
162
165
|
heartbeat_timeout_minutes?: number;
|
|
163
166
|
daily_spending_limit_usd?: number;
|
|
167
|
+
/**
|
|
168
|
+
* Turn assistant surfaces on or off for this app (George plan G7a): the ids given are set, the rest kept. The
|
|
169
|
+
* project's owner or an admin only (403 `project_admin_required` otherwise); an unknown id is a 400.
|
|
170
|
+
*/
|
|
171
|
+
assistant?: AssistantSettingsUpdate;
|
|
164
172
|
}
|
|
165
173
|
export interface BrowserUseRunTaskInput {
|
|
166
174
|
task: string;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { HttpClient } from '../client.js';
|
|
2
2
|
import type { CompassFilterMeta, DiscoveryLayout, PaginatedResponse, PaginationParams, SingleResponse, StoryOutlets } from '../types.js';
|
|
3
3
|
import type { CuratorStoryPostsPage } from './curator.js';
|
|
4
|
+
import type { NativeWriteProtocol, SocialProtocolId } from '../social-protocol-ids.js';
|
|
4
5
|
/** Canonical protocol operation names returned by the adapter registry. */
|
|
5
6
|
export declare const PROTOCOL_CAPABILITY_OPERATIONS: readonly ["discover", "stream", "backfill", "search", "resolveIdentity", "fetchProfile", "fetchThread", "publish", "reply", "react", "repost", "follow", "delete", "health", "checkpoint"];
|
|
6
7
|
export declare const PROTOCOL_CAPABILITY_STATUSES: readonly ["supported", "planned", "unsupported"];
|
|
@@ -426,8 +427,55 @@ export interface FederationIdentityView {
|
|
|
426
427
|
origin: string | null;
|
|
427
428
|
identity: FederationIdentity | null;
|
|
428
429
|
}
|
|
430
|
+
/** A native network every eligible member is on by rule (Farcaster joins later). */
|
|
431
|
+
export type NativeNetwork = 'activitypub' | 'atproto' | 'nostr';
|
|
432
|
+
/**
|
|
433
|
+
* A member's state on one native network. `ready`: on by rule, set up on their first public post, first interaction
|
|
434
|
+
* there, or `activateNetwork` ("Will be set up when you first post or interact"). `pending`: being set up, or waiting
|
|
435
|
+
* for the setup budget (`retryAt`). `off`: the member turned it off (`reason: 'opted_out'`), it is not available to them
|
|
436
|
+
* yet (`not_rolled_out`), or their profile is private (`private_profile`, AT Protocol and Nostr only; `setNetwork`
|
|
437
|
+
* with `enabled: true` turns it on). `needs_action`: the member must act first (`age_band_required`,
|
|
438
|
+
* `needs_verified_email`). `locked`: held off by a rule the member cannot change.
|
|
439
|
+
*/
|
|
440
|
+
export type NativeNetworkState = 'ready' | 'on' | 'pending' | 'off' | 'error' | 'locked' | 'needs_action';
|
|
441
|
+
export interface NativeNetworkPresence {
|
|
442
|
+
network: NativeNetwork;
|
|
443
|
+
state: NativeNetworkState;
|
|
444
|
+
/** Why, as a code: `opted_out`, `not_rolled_out`, `private_profile`, `age_band_required`, `needs_verified_email`, `activation_failed`... */
|
|
445
|
+
reason: string | null;
|
|
446
|
+
/** `hosted` (a Minds-run ActivityPub actor or AT Protocol account), `managed` (a Minds-held Nostr key) or `attached`. */
|
|
447
|
+
mode: 'hosted' | 'managed' | 'attached';
|
|
448
|
+
/** The member's address on the network, set up or not, when the server can say it. */
|
|
449
|
+
handle: string | null;
|
|
450
|
+
/** Which identity a one-tap like, repost, reply or follow uses when the member also linked an outside account. */
|
|
451
|
+
engageDefault: 'native' | 'linked';
|
|
452
|
+
activatedAt: string | null;
|
|
453
|
+
optedOutAt: string | null;
|
|
454
|
+
removePast: boolean;
|
|
455
|
+
}
|
|
456
|
+
export interface NativeNetworkActivation {
|
|
457
|
+
outcome: 'activated' | 'already_on' | 'pending' | 'in_flight' | 'needs_action' | 'error' | 'not_eligible' | 'refused' | 'unavailable';
|
|
458
|
+
presence: NativeNetworkPresence;
|
|
459
|
+
/** When the setup budget frees, for `pending`. */
|
|
460
|
+
retryAt: string | null;
|
|
461
|
+
}
|
|
462
|
+
export interface NativeNetworkOff {
|
|
463
|
+
outcome: 'off';
|
|
464
|
+
presence: NativeNetworkPresence;
|
|
465
|
+
/** `failed`: the member is off, but removing what was shared did not start; call `setNetwork` again to retry it. */
|
|
466
|
+
retraction: 'queued' | 'failed' | 'none';
|
|
467
|
+
}
|
|
468
|
+
export interface SetNativeNetworkInput {
|
|
469
|
+
enabled: boolean;
|
|
470
|
+
/** Turning off: also remove what was already shared there (the Settings sheet checks this by default). */
|
|
471
|
+
removePast?: boolean;
|
|
472
|
+
}
|
|
429
473
|
export interface SocialSourceRef {
|
|
430
|
-
|
|
474
|
+
/**
|
|
475
|
+
* 0.7.87: any SOCIAL_PROTOCOL_IDS network, Farcaster (an FID, or a `<fid>:0x<hash>` CastId) before its reader serves a
|
|
476
|
+
* post. A source to show, mute, hide or report; a native write takes only a NativeWriteProtocol network.
|
|
477
|
+
*/
|
|
478
|
+
protocol: SocialProtocolId;
|
|
431
479
|
kind: 'actor' | 'post';
|
|
432
480
|
nativeId: string;
|
|
433
481
|
}
|
|
@@ -474,7 +522,7 @@ export interface NativeReplyPrepareInput {
|
|
|
474
522
|
clientMutationId: string;
|
|
475
523
|
text: string;
|
|
476
524
|
parent: {
|
|
477
|
-
protocol:
|
|
525
|
+
protocol: NativeWriteProtocol;
|
|
478
526
|
nativeId: string;
|
|
479
527
|
readRef?: string;
|
|
480
528
|
};
|
|
@@ -491,7 +539,7 @@ export interface NativeReplyPrepareInput {
|
|
|
491
539
|
}
|
|
492
540
|
export interface NativeFollowSourceInput {
|
|
493
541
|
accountId: string;
|
|
494
|
-
protocol:
|
|
542
|
+
protocol: NativeWriteProtocol;
|
|
495
543
|
nativeId: string;
|
|
496
544
|
}
|
|
497
545
|
export interface NativeFollowPrepareInput extends NativeFollowSourceInput {
|
|
@@ -536,7 +584,7 @@ export interface NativeFollowState extends NativeFollowReceipt {
|
|
|
536
584
|
/** One connected account on the source's network, as the account lists describe it. */
|
|
537
585
|
export interface NativeFollowAccount {
|
|
538
586
|
id: string;
|
|
539
|
-
protocol:
|
|
587
|
+
protocol: NativeWriteProtocol;
|
|
540
588
|
nativeId: string;
|
|
541
589
|
handle: string;
|
|
542
590
|
serverUrl: string;
|
|
@@ -545,7 +593,7 @@ export interface NativeFollowAccount {
|
|
|
545
593
|
}
|
|
546
594
|
/** Every connected account's relationship with one source, from one read. */
|
|
547
595
|
export interface NativeFollowStates {
|
|
548
|
-
protocol:
|
|
596
|
+
protocol: NativeWriteProtocol;
|
|
549
597
|
nativeId: string;
|
|
550
598
|
/** False when native follows are off for this app; accounts are still listed, and no state is read. */
|
|
551
599
|
enabled: boolean;
|
|
@@ -578,7 +626,7 @@ export interface NativeFollowReview {
|
|
|
578
626
|
}
|
|
579
627
|
export interface NativeLikeSourceInput {
|
|
580
628
|
accountId: string;
|
|
581
|
-
protocol:
|
|
629
|
+
protocol: NativeWriteProtocol;
|
|
582
630
|
nativeId: string;
|
|
583
631
|
}
|
|
584
632
|
export interface NativeLikePrepareInput extends NativeLikeSourceInput {
|
|
@@ -644,7 +692,7 @@ export interface NativeLikeReview {
|
|
|
644
692
|
}
|
|
645
693
|
export interface NativeRepostSourceInput {
|
|
646
694
|
accountId: string;
|
|
647
|
-
protocol:
|
|
695
|
+
protocol: NativeWriteProtocol;
|
|
648
696
|
nativeId: string;
|
|
649
697
|
}
|
|
650
698
|
export interface NativeRepostPrepareInput extends NativeRepostSourceInput {
|
|
@@ -773,7 +821,7 @@ export interface NativeReplyBatch {
|
|
|
773
821
|
serverUrl: string;
|
|
774
822
|
} | null;
|
|
775
823
|
parent: {
|
|
776
|
-
protocol:
|
|
824
|
+
protocol: NativeWriteProtocol;
|
|
777
825
|
nativeId: string;
|
|
778
826
|
sourceUrl: string;
|
|
779
827
|
authorId: string;
|
|
@@ -910,7 +958,7 @@ export interface NativeNostrReplyResult {
|
|
|
910
958
|
text: string;
|
|
911
959
|
/** `readRef`: a linked ActivityPub source's public reader id, for the conversation read (replies saved before it existed have none). */
|
|
912
960
|
source: {
|
|
913
|
-
protocol:
|
|
961
|
+
protocol: NativeWriteProtocol;
|
|
914
962
|
nativeId: string;
|
|
915
963
|
sourceUrl: string;
|
|
916
964
|
readRef?: string;
|
|
@@ -1321,7 +1369,8 @@ export interface SocialTrendFeed extends SocialTrendProvenance {
|
|
|
1321
1369
|
};
|
|
1322
1370
|
}
|
|
1323
1371
|
/** A member of a radar cluster: a headline or excerpt and a link only, never a body. */
|
|
1324
|
-
|
|
1372
|
+
/** 0.7.87: an open-network member's kind is any SOCIAL_PROTOCOL_IDS network, Farcaster before the radar holds any. */
|
|
1373
|
+
export type SocialRadarSourceKind = 'outlet' | 'hackernews' | SocialProtocolId | 'minds';
|
|
1325
1374
|
export interface SocialRadarMember {
|
|
1326
1375
|
kind: SocialRadarSourceKind;
|
|
1327
1376
|
/** outlet: the article url; hackernews: the `hn:<id>` candidate id; social: JSON `[protocol, nativeId]`; minds: the post id. */
|
|
@@ -1748,12 +1797,27 @@ export interface SocialConversation {
|
|
|
1748
1797
|
hiddenByViewer?: true;
|
|
1749
1798
|
viewerControls?: Array<Pick<SocialSourceControl, 'source' | 'control'>>;
|
|
1750
1799
|
}
|
|
1800
|
+
/**
|
|
1801
|
+
* A followed account's status on a Following page, in the server's order (SocialFollowingFeed.ts
|
|
1802
|
+
* FOLLOWING_SOURCE_STATUSES). `late` (added after 0.7.82): the account's read had not answered at the page's deadline;
|
|
1803
|
+
* it is read again on a later page and is never a failure. APIs before it name such an account `unavailable` with
|
|
1804
|
+
* `late: true`.
|
|
1805
|
+
*/
|
|
1806
|
+
export declare const SOCIAL_FOLLOWING_SOURCE_STATUSES: readonly ["ok", "late", "unavailable", "rate_limited", "unsupported_protocol"];
|
|
1807
|
+
export type SocialFollowingSourceStatus = typeof SOCIAL_FOLLOWING_SOURCE_STATUSES[number];
|
|
1751
1808
|
export interface SocialFollowingSource {
|
|
1752
1809
|
subscriptionId: string;
|
|
1753
1810
|
protocol: string;
|
|
1754
1811
|
nativeId: string;
|
|
1755
|
-
|
|
1812
|
+
/**
|
|
1813
|
+
* `late`: the account's read has not answered yet; it is read again on a later page. `ok` with `late: true`: the
|
|
1814
|
+
* account's last-held first page, served while its fresh read goes on. `unavailable` is a read that failed, or one
|
|
1815
|
+
* still late after its reads again.
|
|
1816
|
+
*/
|
|
1817
|
+
status: SocialFollowingSourceStatus;
|
|
1756
1818
|
moreAvailable: boolean | null;
|
|
1819
|
+
/** The account is read again on a later page: never show it as a failure; read the next page for its posts. */
|
|
1820
|
+
late?: true;
|
|
1757
1821
|
}
|
|
1758
1822
|
/** A bounded batch of recent author windows. Continue the cursor to read more followed accounts. */
|
|
1759
1823
|
export interface SocialFollowingFeed {
|
|
@@ -1764,6 +1828,10 @@ export interface SocialFollowingFeed {
|
|
|
1764
1828
|
totalAccounts: number;
|
|
1765
1829
|
postsPerAccount: number;
|
|
1766
1830
|
coverage: 'recent_original_posts';
|
|
1831
|
+
/** The networks with an account named late on this page (absent from older APIs). */
|
|
1832
|
+
lateNetworks?: string[];
|
|
1833
|
+
/** How long this page waited for an account's first read on each network, in ms (absent from older APIs). */
|
|
1834
|
+
deadlineMs?: number;
|
|
1767
1835
|
}
|
|
1768
1836
|
export declare class ProtocolsResource {
|
|
1769
1837
|
private client;
|
|
@@ -2134,6 +2202,26 @@ export declare class ProtocolsResource {
|
|
|
2134
2202
|
disconnected: true;
|
|
2135
2203
|
providerRevocation: 'acknowledged' | 'unconfirmed';
|
|
2136
2204
|
}>>;
|
|
2205
|
+
/**
|
|
2206
|
+
* The signed-in member's native networks in the key's app (ActivityPub, AT Protocol, Nostr): each one's state, reason
|
|
2207
|
+
* and address. Read-only: it never sets anything up. Needs `users:read`.
|
|
2208
|
+
*/
|
|
2209
|
+
networks(): Promise<SingleResponse<{
|
|
2210
|
+
networks: NativeNetworkPresence[];
|
|
2211
|
+
}>>;
|
|
2212
|
+
/**
|
|
2213
|
+
* "Set up now": sets up a network that is `ready` (or `pending`, or failed) at once, instead of on the member's first
|
|
2214
|
+
* post. Never undoes their off switch (409 `network_off`) or a private profile's rule (409 `private_profile`): use
|
|
2215
|
+
* `setNetwork` for those. Over the setup budget it answers `pending` with `retryAt`. Needs `users:write`.
|
|
2216
|
+
*/
|
|
2217
|
+
activateNetwork(network: NativeNetwork): Promise<SingleResponse<NativeNetworkActivation>>;
|
|
2218
|
+
/**
|
|
2219
|
+
* The member's switch for one network. `{ enabled: false, removePast: true }` turns it off and removes what was
|
|
2220
|
+
* shared there (reversible: turning it back on serves the same identity; remote followers may need to follow again).
|
|
2221
|
+
* `{ enabled: true }` turns it back on or sets it up. Refusals: 403 `network_locked`, 409 `not_rolled_out` or
|
|
2222
|
+
* `age_band_required`. Needs `users:write`.
|
|
2223
|
+
*/
|
|
2224
|
+
setNetwork(network: NativeNetwork, input: SetNativeNetworkInput): Promise<SingleResponse<NativeNetworkActivation | NativeNetworkOff>>;
|
|
2137
2225
|
/** Current Minds-hosted ActivityPub state for this app user; never provisions. */
|
|
2138
2226
|
federationIdentity(): Promise<SingleResponse<FederationIdentityView>>;
|
|
2139
2227
|
/** Explicit opt-in; the platform provisions once and never rotates keys or re-enables a disabled identity on retry. */
|
|
@@ -28,6 +28,13 @@ export const PROTOCOL_ADAPTER_MATURITIES = Object.freeze([
|
|
|
28
28
|
'beta',
|
|
29
29
|
'stable',
|
|
30
30
|
]);
|
|
31
|
+
const NATIVE_NETWORKS = ['activitypub', 'atproto', 'nostr'];
|
|
32
|
+
function nativeNetworkPath(network) {
|
|
33
|
+
if (!NATIVE_NETWORKS.includes(network)) {
|
|
34
|
+
throw new ValidationError({ type: 'validation_error', code: 'invalid_network', message: 'network must be activitypub, atproto or nostr' });
|
|
35
|
+
}
|
|
36
|
+
return `/protocols/networks/${network}`;
|
|
37
|
+
}
|
|
31
38
|
function hostedIdentityPath(id) {
|
|
32
39
|
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id)) {
|
|
33
40
|
throw new ValidationError({ type: 'validation_error', code: 'invalid_identity_id', message: 'A valid hosted identity connection ID is required' });
|
|
@@ -42,6 +49,19 @@ function hostedIdentityPath(id) {
|
|
|
42
49
|
export function viralItemHasVideo(item) {
|
|
43
50
|
return item.post.media.some(media => media.type === 'video');
|
|
44
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* A followed account's status on a Following page, in the server's order (SocialFollowingFeed.ts
|
|
54
|
+
* FOLLOWING_SOURCE_STATUSES). `late` (added after 0.7.82): the account's read had not answered at the page's deadline;
|
|
55
|
+
* it is read again on a later page and is never a failure. APIs before it name such an account `unavailable` with
|
|
56
|
+
* `late: true`.
|
|
57
|
+
*/
|
|
58
|
+
export const SOCIAL_FOLLOWING_SOURCE_STATUSES = Object.freeze([
|
|
59
|
+
'ok',
|
|
60
|
+
'late',
|
|
61
|
+
'unavailable',
|
|
62
|
+
'rate_limited',
|
|
63
|
+
'unsupported_protocol',
|
|
64
|
+
]);
|
|
45
65
|
/** Only the radar's own fields travel; caller scope or destination extras never reach the query string. */
|
|
46
66
|
/** Forwarded only when given, so a default read makes exactly the call it always made. */
|
|
47
67
|
const readOptions = (options) => options ? [options] : [];
|
|
@@ -488,6 +508,31 @@ export class ProtocolsResource {
|
|
|
488
508
|
hostedIdentityPath(id);
|
|
489
509
|
return this.client.delete(`/protocols/social/accounts/${encodeURIComponent(id)}`);
|
|
490
510
|
}
|
|
511
|
+
/**
|
|
512
|
+
* The signed-in member's native networks in the key's app (ActivityPub, AT Protocol, Nostr): each one's state, reason
|
|
513
|
+
* and address. Read-only: it never sets anything up. Needs `users:read`.
|
|
514
|
+
*/
|
|
515
|
+
networks() {
|
|
516
|
+
return this.client.get('/protocols/networks');
|
|
517
|
+
}
|
|
518
|
+
/**
|
|
519
|
+
* "Set up now": sets up a network that is `ready` (or `pending`, or failed) at once, instead of on the member's first
|
|
520
|
+
* post. Never undoes their off switch (409 `network_off`) or a private profile's rule (409 `private_profile`): use
|
|
521
|
+
* `setNetwork` for those. Over the setup budget it answers `pending` with `retryAt`. Needs `users:write`.
|
|
522
|
+
*/
|
|
523
|
+
activateNetwork(network) {
|
|
524
|
+
return this.client.post(`${nativeNetworkPath(network)}/activate`, {});
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* The member's switch for one network. `{ enabled: false, removePast: true }` turns it off and removes what was
|
|
528
|
+
* shared there (reversible: turning it back on serves the same identity; remote followers may need to follow again).
|
|
529
|
+
* `{ enabled: true }` turns it back on or sets it up. Refusals: 403 `network_locked`, 409 `not_rolled_out` or
|
|
530
|
+
* `age_band_required`. Needs `users:write`.
|
|
531
|
+
*/
|
|
532
|
+
setNetwork(network, input) {
|
|
533
|
+
const body = input.enabled ? { enabled: true } : { enabled: false, ...(input.removePast !== undefined ? { remove_past: input.removePast } : {}) };
|
|
534
|
+
return this.client.put(nativeNetworkPath(network), body);
|
|
535
|
+
}
|
|
491
536
|
/** Current Minds-hosted ActivityPub state for this app user; never provisions. */
|
|
492
537
|
federationIdentity() {
|
|
493
538
|
return this.client.get('/protocols/social/federation/identity');
|
|
@@ -25,6 +25,12 @@ export interface SealedTarget {
|
|
|
25
25
|
/** Exact origin, e.g. `https://router-0.tinfoil.sh`. */
|
|
26
26
|
origin: string;
|
|
27
27
|
kind: SealedTargetKind;
|
|
28
|
+
/**
|
|
29
|
+
* Web search works through this target: seal a message that may search only to a target that says true. Set on
|
|
30
|
+
* `SealedConfig.targets` only, never in a model's own list (look the origin up there). Absent (an older gateway)
|
|
31
|
+
* means no.
|
|
32
|
+
*/
|
|
33
|
+
search?: boolean;
|
|
28
34
|
}
|
|
29
35
|
/** A model the app offers in Sealed. */
|
|
30
36
|
export interface SealedModel {
|
|
@@ -36,6 +42,8 @@ export interface SealedModel {
|
|
|
36
42
|
* when one has moved to another target, so read it per model.
|
|
37
43
|
*/
|
|
38
44
|
targets: SealedTarget[];
|
|
45
|
+
/** The app may send pictures to this model. Absent (an older gateway) means no. */
|
|
46
|
+
vision?: boolean;
|
|
39
47
|
}
|
|
40
48
|
/** What `config()` returns. */
|
|
41
49
|
export interface SealedConfig {
|
|
@@ -45,11 +53,73 @@ export interface SealedConfig {
|
|
|
45
53
|
attestation_path: string;
|
|
46
54
|
/** Every target the gateway forwards to, chat and attestation alike. */
|
|
47
55
|
targets: SealedTarget[];
|
|
48
|
-
/** The models the app offers in Sealed, each with the targets that serve it. */
|
|
56
|
+
/** The models the app offers to chat with in Sealed, each with the targets that serve it. */
|
|
49
57
|
models: SealedModel[];
|
|
58
|
+
/**
|
|
59
|
+
* The models the app's sealed checks run on (a safeguard model for the message and answer checks, a vision model for
|
|
60
|
+
* the picture check), each with the targets that serve it. Never offer one of these as a chat model unless `models`
|
|
61
|
+
* lists it too. Send a check with `send({ purpose: 'check' })`. Absent on an older gateway.
|
|
62
|
+
*/
|
|
63
|
+
checks?: SealedModel[];
|
|
64
|
+
/**
|
|
65
|
+
* Server switch (George plan G7a): web search through the router. Send a message that may search with
|
|
66
|
+
* `send({ search: true })`, which marks it `X-Sealed-Search: 1`; the gateway refuses a marked message while this is
|
|
67
|
+
* off (403 `sealed_search_off`), and pauses the member's day when an unmarked one searches. Off unless the gateway
|
|
68
|
+
* says true: treat it absent, or a config you could not read, as off.
|
|
69
|
+
*/
|
|
70
|
+
search?: boolean;
|
|
71
|
+
/** Server switch (G7a): pictures from the phone, off unless true. Post pictures need only the model's `vision`. */
|
|
72
|
+
phone_pictures?: boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Server switch: on-phone memory, off unless true. The memory lives on the phone and the gateway never sees it; this
|
|
75
|
+
* is the server's switch for the feature, so it can be turned off for everyone at once. Absent (an older gateway), or
|
|
76
|
+
* a config you could not read, means off.
|
|
77
|
+
*/
|
|
78
|
+
memory?: boolean;
|
|
50
79
|
/** The attestation policy version the app's verifier must pin; an older pinned policy means the app needs updating. */
|
|
51
80
|
verifier_policy_version: number;
|
|
81
|
+
/** The member's allowance: its daily numbers are their plan's (`plan`). */
|
|
52
82
|
allowance: SealedAllowance;
|
|
83
|
+
/** The plan the daily numbers in `allowance` follow. Free when the gateway could not read it. Absent on an older gateway. */
|
|
84
|
+
plan?: SealedPlan;
|
|
85
|
+
/** False when the gateway could not read the member's plan and applied Free's numbers: do not tell them they are on Free. */
|
|
86
|
+
plan_known?: boolean;
|
|
87
|
+
/** What is left of each daily allowance now; `checks` only while checks count apart. Absent when it could not be read. */
|
|
88
|
+
remaining_today?: {
|
|
89
|
+
requests: number;
|
|
90
|
+
searches: number;
|
|
91
|
+
checks?: number;
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* Every plan's daily numbers, so the app can say truthfully whether a plan gives more. It lists plans the gateway
|
|
95
|
+
* knows, Max among them, whether or not anyone sells them: offer only the plans the app sells.
|
|
96
|
+
*/
|
|
97
|
+
plan_allowances?: Record<SealedPlan, {
|
|
98
|
+
requests_per_day: number;
|
|
99
|
+
searches_per_day: number;
|
|
100
|
+
checks_per_day?: number;
|
|
101
|
+
}>;
|
|
102
|
+
/**
|
|
103
|
+
* `budget` while the app's monthly Sealed budget is used up (sends get 503 `sealed_paused_budget` until the next UTC
|
|
104
|
+
* month), `unavailable` while the gateway cannot tell (sends get 503 `sealed_unavailable`), null otherwise.
|
|
105
|
+
*/
|
|
106
|
+
paused?: 'budget' | 'unavailable' | null;
|
|
107
|
+
}
|
|
108
|
+
/** The plans the gateway knows, lowest first. */
|
|
109
|
+
export type SealedPlan = 'free' | 'plus' | 'pro' | 'max';
|
|
110
|
+
/**
|
|
111
|
+
* `details` on a refused send whose allowance ran out (RateLimitError.details): which limit, the member's plan, whether
|
|
112
|
+
* that plan was read (`plan_known`; false: Free's numbers were applied because the read failed), that plan's daily
|
|
113
|
+
* number (null for `rate`, the per-minute and open-request caps no plan changes), and the plans that give more of it
|
|
114
|
+
* (empty for `rate` and while the plan is not known). Say "upgrade for more" only when a plan the app sells is in
|
|
115
|
+
* `higher_plans`.
|
|
116
|
+
*/
|
|
117
|
+
export interface SealedQuotaDetails {
|
|
118
|
+
limit: 'messages' | 'searches' | 'checks' | 'rate';
|
|
119
|
+
plan: SealedPlan;
|
|
120
|
+
plan_known: boolean;
|
|
121
|
+
per_day: number | null;
|
|
122
|
+
higher_plans: SealedPlan[];
|
|
53
123
|
}
|
|
54
124
|
export interface SealedAllowance {
|
|
55
125
|
requests_per_day: number;
|
|
@@ -57,7 +127,25 @@ export interface SealedAllowance {
|
|
|
57
127
|
max_concurrent: number;
|
|
58
128
|
/** The cap on one sealed request body, in bytes. */
|
|
59
129
|
max_request_bytes: number;
|
|
130
|
+
/**
|
|
131
|
+
* Checks (`send({ purpose: 'check' })`) per UTC day, counted instead of `requests_per_day`. Absent while the gateway
|
|
132
|
+
* counts checks as messages (its check allowance is off), and on an older gateway: a check then spends a message.
|
|
133
|
+
*/
|
|
134
|
+
checks_per_day?: number;
|
|
135
|
+
/** Checks per minute, counted instead of `requests_per_minute`. Absent when `checks_per_day` is. */
|
|
136
|
+
checks_per_minute?: number;
|
|
137
|
+
/**
|
|
138
|
+
* Messages marked as searches per UTC day. A marked search spends a search and no message; a gateway from before plans
|
|
139
|
+
* counted it against `requests_per_day` as well.
|
|
140
|
+
*/
|
|
141
|
+
searches_per_day?: number;
|
|
60
142
|
}
|
|
143
|
+
/**
|
|
144
|
+
* What a sealed request is for, sent as `X-Sealed-Purpose`, which the gateway reads and never forwards. `check`: one of
|
|
145
|
+
* the app's sealed checks, counted against the check allowance instead of the message allowance while the gateway has
|
|
146
|
+
* one (`config().allowance.checks_per_day`), and as a message otherwise.
|
|
147
|
+
*/
|
|
148
|
+
export type SealedPurpose = 'check';
|
|
61
149
|
export interface SealedSendInput {
|
|
62
150
|
/** The router the app verified and sealed to: the origin of a `router` target from `config()`, exactly. */
|
|
63
151
|
enclaveOrigin: string;
|
|
@@ -67,6 +155,19 @@ export interface SealedSendInput {
|
|
|
67
155
|
body: Uint8Array | ArrayBuffer | ReadableStream<Uint8Array>;
|
|
68
156
|
/** Ask for a streamed (server-sent events) answer. */
|
|
69
157
|
stream?: boolean;
|
|
158
|
+
/**
|
|
159
|
+
* `check` for one of the app's sealed checks (a model from `config().checks`): sent as `X-Sealed-Purpose: check`, so
|
|
160
|
+
* it counts against the check allowance while the gateway lists one. Omit it for a message.
|
|
161
|
+
*/
|
|
162
|
+
purpose?: SealedPurpose;
|
|
163
|
+
/**
|
|
164
|
+
* True for a message that may search the web: sent as `X-Sealed-Search: 1`, which the gateway reads and never
|
|
165
|
+
* forwards. It counts against the day's searches instead of its messages, and the gateway refuses it while
|
|
166
|
+
* `config().search` is off. Only a marked message may carry search options in its sealed body: the gateway pauses the
|
|
167
|
+
* member's day when an unmarked request searches. Never with `purpose: 'check'` (a check never searches): that is
|
|
168
|
+
* refused before anything is sent.
|
|
169
|
+
*/
|
|
170
|
+
search?: boolean;
|
|
70
171
|
/** The targets from a config already read. When omitted, `send()` reads the config first. */
|
|
71
172
|
targets?: readonly SealedTarget[];
|
|
72
173
|
signal?: AbortSignal;
|
|
@@ -93,6 +194,10 @@ export type SealedTransportErrorCode =
|
|
|
93
194
|
| 'bad_encapsulated_key'
|
|
94
195
|
/** The attestation nonce is not 32 bytes of hex: nothing was sent. */
|
|
95
196
|
| 'bad_nonce'
|
|
197
|
+
/** The purpose is not one the gateway takes: nothing was sent. */
|
|
198
|
+
| 'bad_purpose'
|
|
199
|
+
/** The marks do not go together (`search` on a check), or `search` is not a boolean: nothing was sent. */
|
|
200
|
+
| 'bad_marks'
|
|
96
201
|
/** A 2xx answer without a valid Ehbp-Response-Nonce: its body was discarded unread. */
|
|
97
202
|
| 'unsealed_answer';
|
|
98
203
|
export declare class SealedTransportError extends Error {
|
|
@@ -102,8 +207,10 @@ export declare class SealedTransportError extends Error {
|
|
|
102
207
|
/** Set on a refusal the gateway wrote itself; a relayed router answer never carries it. */
|
|
103
208
|
export declare const SEALED_REFUSAL_HEADER = "x-sealed-gateway-refusal";
|
|
104
209
|
/**
|
|
105
|
-
*
|
|
106
|
-
* no other, and the router cannot set it, so an error without it is one the API (or an
|
|
210
|
+
* Messages left in the member's allowance today, on a check as on a message. The gateway sets it on every answer it
|
|
211
|
+
* relayed from the router and on no other, and the router cannot set it, so an error without it is one the API (or an
|
|
212
|
+
* edge in front of it) wrote. A relayed answer to a check also carries `X-Sealed-Checks-Remaining` while the gateway
|
|
213
|
+
* has a check allowance, and one to a marked search `X-Sealed-Searches-Remaining`.
|
|
107
214
|
*/
|
|
108
215
|
export declare const SEALED_ALLOWANCE_REMAINING_HEADER = "x-sealed-allowance-remaining";
|
|
109
216
|
export declare class SealedResource {
|
|
@@ -135,10 +242,23 @@ export declare class SealedResource {
|
|
|
135
242
|
* has no nonce, never to be shown as the model's words.
|
|
136
243
|
*
|
|
137
244
|
* Throws a RecursivError for every error the API wrote rather than relayed, whether or not it is marked
|
|
138
|
-
* X-Sealed-Gateway-Refusal: RateLimitError for an allowance used up (`code` `sealed_allowance_used
|
|
139
|
-
* `
|
|
140
|
-
*
|
|
141
|
-
* is
|
|
245
|
+
* X-Sealed-Gateway-Refusal: RateLimitError for an allowance used up (`code` `sealed_allowance_used`,
|
|
246
|
+
* `sealed_search_allowance_used` or `sealed_too_many_requests`, with SealedQuotaDetails in `details`) or the API's own
|
|
247
|
+
* per-IP limit (`rate_limited`), a RecursivError with status 503 and `code` `sealed_paused_budget` while the app's
|
|
248
|
+
* monthly Sealed budget is used up (until the next UTC month), AuthenticationError for a revoked or expired key,
|
|
249
|
+
* NotFoundError while Sealed is off or not on for this app, AuthorizationError (`sealed_search_off`) for a marked
|
|
250
|
+
* search while search is off. Throws SealedTransportError when the router is not listed, the key, purpose or marks
|
|
251
|
+
* are malformed, or a 2xx answer came back unsealed.
|
|
252
|
+
*
|
|
253
|
+
* With `purpose: 'check'` the request counts against the check allowance while the gateway has one (`code`
|
|
254
|
+
* `sealed_check_allowance_used` when it is used up), and as a message otherwise. With `search: true` it counts
|
|
255
|
+
* against the day's searches (`sealed_search_allowance_used`) and spends no message. While the gateway's day lock is
|
|
256
|
+
* on, a member is refused until midnight UTC (`sealed_paused_today`) after a request of theirs that searched the web
|
|
257
|
+
* unmarked (or marked a check), that the router answered with a model `config()` does not offer for it (a check's must
|
|
258
|
+
* be in `checks`; any other's in `models`, or in `checks` while the gateway lets checks sent unmarked through), or
|
|
259
|
+
* that passed the day's count of answers left before their usage line (searches included). The lists can change on
|
|
260
|
+
* the server: read `config()` again from time to time, not once for the app's whole run, or a member whose app still
|
|
261
|
+
* sends to a model taken out of them is paused.
|
|
142
262
|
*/
|
|
143
263
|
send(input: SealedSendInput): Promise<Response>;
|
|
144
264
|
}
|