@recursiv/sdk 0.7.152 → 0.7.195

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -68,7 +68,7 @@ for await (const chunk of r.agents.chatStream(agent.id, { message: 'Hello!' }))
68
68
  | `r.databases` | `ensure`, `create`, `list`, `getCredentials`, `query` |
69
69
  | `r.envVars` | `set`, `list`, `delete` (values write-only; secrets masked on read) |
70
70
  | `r.projects` | `create`, `deploy`, `executeCode`, `createSandbox`, `stopSandbox`, `deploymentLogs` |
71
- | `r.deployments` | Operator release coordination: `reserveRelease`, `getRelease`, `advanceRelease`, `startRelease`, `finishRelease`, `recoverRelease`; follow the [shared release guide](../../docs/deploy/shared-releases.md) |
71
+ | `r.deployments` | Operator release coordination: `reserveRelease` (optional initial-run `coordinator_identity_token` after compatible API rollout), `getRelease`, `advanceRelease`, `startRelease`, `finishRelease`, `recoverRelease`; follow the [shared release guide](../../docs/deploy/shared-releases.md) |
72
72
  | `r.storage` | `ensureBucket`, `getUploadUrl`, `getDownloadUrl`, `listItems`, `deleteObject` |
73
73
  | `r.telemetry` | `productCounts` — app-bound first-party aggregate counts; reviewed event names and counts only, retained at most 30 days |
74
74
  | `r.sandbox` | `execute` (anonymous, no API key needed) |
@@ -96,9 +96,10 @@ for await (const chunk of r.agents.chatStream(agent.id, { message: 'Hello!' }))
96
96
  | Resource | Key methods |
97
97
  |----------|------------|
98
98
  | `r.posts` | `create`, `update`, `list`, `get`, `getMany(ids)` (up to 25 posts by id per request, no thread; a `Map` of each id's post or `NotFoundError`; 0.7.77), `liked`, `bookmarked`, `bookmark`, `unbookmark`, `search`, `replies`, `react` |
99
- | `r.search` | `query` (blended search in sections; a thin answer steps down the search ladder, each item labelled `via`, the answer's `ladder` says how) |
99
+ | `r.search` | `query` (blended search in sections; a thin answer steps down the search ladder, each item labelled `via`, the answer's `ladder` says how). A visitor's search posts (`publicDiscovery.search`, `PublicSearchPage`) may carry `picture` (`SearchPicture`, 0.7.162): `kind`, `textHeavy`, `tileQuality` (0-3) and an optional `focal` point, layout hints from a background read of pictures the floors cleared, absent until judged and never a safety signal; on the `deep` stage `picture.showsQuery` (0.7.190) says whether the picture itself shows the query, absent unless the operator turned on `SEARCH_PICTURE_SUBJECT`. Both answers may carry `video_lead` (`SearchVideoLeadEntry[]`, 0.7.173): every clip in the order to lead and play it, the most about the words first, then the freshest, a Minds member's clip winning a tie |
100
100
  | `r.bookmarks` | `save`, `unsave`, `posts`, `list`, `keys`, `merge` — the member's saved posts, open-network posts and articles, synced across devices; `collections`: `list`, `create`, `update`, `remove`, `add`, `removeItem`, `items`, `posts` — private named collections of saves (0.7.68, where `network.getConfig()` advertises `capabilities.bookmark_collections`) |
101
- | `r.feeds` | `top` (what the member is most likely to engage with, rotated by what they saw, with open-network lanes and the lead story), `stories` (the story tail), `publicTop` (a visitor's top, keyless and edge-cacheable) |
101
+ | `r.feeds` | `top` (what the member is most likely to engage with, rotated by what they saw, with open-network lanes and the lead story), `stories` (the story tail), `publicTop` (a visitor's top, keyless and edge-cacheable), `firehose` (0.7.195: the member's live stream of the most engaged, viral and breaking posts, never repeated in a session) |
102
+ | `r.alive` | `tick({ followingSince?, posts?, thread? })` (0.7.193; the member's own app key, `users:read`; 404 `alive_disabled` until the operator sets `ALIVE_TICK_ENABLED`): whether anyone the member follows posted since the newest row they hold (`following.fresh`), the current counts of up to 30 posts on screen they may see, the new replies under the open post (their own left out), and `nextAfterMs` (when to tick next). Asked once; at most 4 a minute |
102
103
  | `r.communities` | `create`, `join`, `search`, `members` |
103
104
  | `r.chat` | `send`, `dm`, `createGroup` (`encrypted: true` for an end-to-end encrypted group, 0.7.59), `messages` (cursor paging), `acceptRequest`, `declineRequest`, `leave`, `deleteConversation`; a private group's admin: `addMembers`, `removeMember`, `updateGroup` (0.7.59); photos and voice notes: `uploadAttachment`, `signAttachments` (private chat storage, 0.7.82) |
104
105
  | `r.tags` | `create`, `list` |
@@ -118,7 +119,7 @@ for await (const chunk of r.agents.chatStream(agent.id, { message: 'Hello!' }))
118
119
  | `r.settings` | User preferences (including app-scoped content choices; per-kind notification settings in `notifications.channels`, set one kind with `updateNotifications({ channels: { post_reaction: { push: false } } })`, 0.7.72), sessions (`revokeOtherSessions` keeps this device), password management, email change (`requestConfirmationCode` for accounts with no password), deactivation (`disableAccount({ password })`, 0.7.71; 404 on an API that does not serve it) |
119
120
  | `r.billing` | Usage tracking, subscriptions, checkout |
120
121
  | `r.appSubscriptions` | Consumer app plans: `checkout`, `status`, `createPortalSession`, `previewTierChange`, `changeTier` |
121
- | `r.boosts` | Promoted posts: `create`, `mine`, `balance`, `pause`, `resume`, `cancel`, `recordEvent`, `topup`, `confirmTopup`; admin `listPendingReview`, `review`, `grant` |
122
+ | `r.boosts` | Promoted posts: `create`, `mine`, `balance`, `pause`, `resume`, `cancel`, `recordEvent`, `stats`, `topup`, `confirmTopup`, `topupWithTokens`; a feed page's boosts `slots` / `publicSlots` (splice with `spliceBoostSlots`); admin `listPendingReview`, `review`, `grant` |
122
123
  | `r.wallet` | Wallet, MINDS tokens (`getTokenBalance`, `getTokenLedger`), a wallet the member holds linked by signature (`createLinkNonce`, `linkWallet`, `listLinkedWallets`, `unlinkWallet`), USD payouts (`getPayouts`, `startPayoutOnboarding`, `payoutLoginLink`) |
123
124
  | `r.notifications` | Notification stream (`list`, `unreadCount`, `markAsRead`, `archive`, `markAllAsRead`; `category: 'mentions'` for mentions and replies only, 0.7.72) and push tokens |
124
125
  | `r.github` | GitHub integration |
@@ -130,7 +131,7 @@ for await (const chunk of r.agents.chatStream(agent.id, { message: 'Hello!' }))
130
131
  | `r.goals` | Org goals with task rollups (group dispatcher tasks) |
131
132
  | `r.media` | `sourceApprovals({ projectId, sources })` — cached public host/purpose judgments and exact owned hosts; no caller-triggered provider fetch or inference. Image/icon requests stay relayed; direct players use approved, unexpired decisions once enforcement is active |
132
133
  | `r.linkPreview` | `get(url, source?, { projectId })` opts into expiring relayed image/icon capabilities; refresh before `media_expires_at`, and preserve source authorization. `get(url)` — a public link's Open Graph card (`data: null` when it has none). For a chat link pass `{ messageId }` or `{ conversationId }` on a signed-in key with `chat:read`: the link is fetched only when it is in the named message and a person wrote that message, or the caller wrote it in that conversation, else `refused: 'ai_link'`. A source with no id rejects (`ValidationError`). Not in encrypted chats: the server cannot read them, so leave `source` out there and apply the app's own rule |
133
- | `r.protocols` | Private Minds safety: `sourceControls` (current enablement and saved choices; filter with `control`, each item may carry a `display` label), `setSourceControl`, `onSourceControlChange`, `reportSource`. Delegated accounts: `nativeSocialAccounts`, `startNativeSocialConnection`, `finishNativeSocialConnection`, `disconnectNativeSocialAccount`. Native follows: `nativeFollowStates` (every connected account's state for one source, one read), `nativeFollowState`, `prepareNativeFollow`, `submitNativeFollow`, `nativeFollowStatus` (native following or pending approval, distinct from a Minds feed subscription; preserve the complete signed Nostr list). Native reposts and undo: `nativeRepostState`, `prepareNativeRepost`, `submitNativeRepost`, `nativeRepostStatus` (public posts, explicit approval, connected account, no uncertain retries). Native likes and unlikes: `nativeLikeState`, `prepareNativeLike`, `submitNativeLike`, `nativeLikeStatus` (connected authority, app enablement and explicit approval; resume pending actions instead of duplicating them). Reviewed replies and optional linked posts: `prepareNativeReply`, `submitNativeReply`, `nativeReplyStatus`, `nativeReplyHistory`, `prepareNativeReplyRemoval`, `removeNativeReply` (require deployed API support and app enablement). Keyless shared post links: `sharedNetworks(projectId)`, `getSharedConversation(projectId, { protocol, post, readRef?, replies?: 'none' })` (`replies: 'none'`, 0.7.78: the post alone for a card, with `repliesStatus: 'not_requested'`; `getPublicConversation` and `getConnectedSocialConversation` take it too). Member public reading: `searchPublicPosts` (selected account required for Bluesky/ActivityPub; public Nostr search), `getConnectedSocialConversation` (preserve result readerAccountId/readRef), `publicNetworks`, `searchPublicProfiles`, `getPublicProfile`, `getPublicAuthorFeed`, `getFollowingFeed`, `getPublicConversation`, `getPublicTrends`, `getPublicTrendFeed`; a Fediverse community's newest threads, read-only (0.7.109): `getOpenGroupThreads({ group, limit?, cursor?, compass? })`, `group` being the `actor` of a groups read's `open_network` entry; radar stories: `getPublicRadarStory` (one story by id with its posts; one that left the radar within its minimum lifetime, or within the last half hour, still answers live with `liveUntil` and the story that continues it; one that has left for longer comes with its archived copy and the live story it continues as; the id may be a World Today edition story's, and the answer then names it in `editionStory` under the story's radar id; a story a World Today story seeded carries `volume`, the first page of the posts that story counts across Minds and the open networks, whose `cursor` goes on with `curator.storyPosts` or `curator.memberStoryPosts`), keyless `getSharedRadarStory(projectId, { cluster })` and `getSharedRadarPosts(projectId, { cluster })` (keyless reads carry images and videos only from outlets and members marked `visitorMedia`; signed-in reads keep all media, so a share card uses only those); the Viral read (fastest-moving posts across Bluesky, the Fediverse, Nostr and Minds, each against its own network's pace; 0.7.59): `getPublicViral({ network?, category?, media?: 'video', limit?: 5 | 10 | 20 | 40, cursor?, compass? })` under the member's key and keyless `getSharedViral(projectId, params)` (visitors: `compass` `all` or `hide` only; a visitor's `media: 'video'` read can carry items whose video the server withheld, so filter with the exported `viralItemHasVideo` before a video player; `item.post` narrows on `item.protocol`). `status: 'off'` resolves with no items until the app enables the read, and `getSharedViral` also resolves an older API's 401 `missing_api_key` as off; `getPublicViral` throws every 401 and 403, so a session failure is never read as off; private app subscriptions: `listSubscriptions`, `subscribeToProfile`, `unsubscribeFromProfile`, `onSubscriptionChange`. `list` returns bounded adapter `maturity`, `limitations`, and exhaustive `supported`/`planned`/`unsupported` implementation capabilities (not provider health or enablement); settings, candidate refresh/status, search, and read-only quarantined candidate review via `listCandidates` / `getCandidate` |
134
+ | `r.protocols` | Private Minds safety: `sourceControls` (current enablement and saved choices; filter with `control`, each item may carry a `display` label), `setSourceControl`, `onSourceControlChange`, `reportSource`. Delegated accounts: `nativeSocialAccounts`, `startNativeSocialConnection`, `finishNativeSocialConnection`, `disconnectNativeSocialAccount`. Native follows: `nativeFollowStates` (every connected account's state for one source, one read), `nativeFollowState`, `prepareNativeFollow`, `submitNativeFollow`, `nativeFollowStatus` (native following or pending approval, distinct from a Minds feed subscription; preserve the complete signed Nostr list). Native reposts and undo: `nativeRepostState`, `prepareNativeRepost`, `submitNativeRepost`, `nativeRepostStatus` (public posts, explicit approval, connected account, no uncertain retries). Native likes and unlikes: `nativeLikeState`, `prepareNativeLike`, `submitNativeLike`, `nativeLikeStatus` (connected authority, app enablement and explicit approval; resume pending actions instead of duplicating them). Reviewed replies and optional linked posts: `prepareNativeReply`, `submitNativeReply`, `nativeReplyStatus`, `nativeReplyHistory`, `prepareNativeReplyRemoval`, `removeNativeReply` (require deployed API support and app enablement). Keyless shared post links: `sharedNetworks(projectId)`, `getSharedConversation(projectId, { protocol, post, readRef?, replies?: 'none' })` (`replies: 'none'`, 0.7.78: the post alone for a card, with `repliesStatus: 'not_requested'`; `getPublicConversation` and `getConnectedSocialConversation` take it too). Member public reading: `searchPublicPosts` (selected account required for Bluesky/ActivityPub; public Nostr search), `getConnectedSocialConversation` (preserve result readerAccountId/readRef), `publicNetworks`, `searchPublicProfiles`, `getPublicProfile`, `getPublicAuthorFeed`, `getFollowingFeed`, `getPublicConversation`, `getPublicTrends`, `getPublicTrendFeed`; a Fediverse community's newest threads, read-only (0.7.109): `getOpenGroupThreads({ group, limit?, cursor?, compass? })`, `group` being the `actor` of a groups read's `open_network` entry; radar stories: `getPublicRadarStory` (one story by id with its posts; one that left the radar within its minimum lifetime, or within the last half hour, still answers live with `liveUntil` and the story that continues it; one that has left for longer comes with its archived copy and the live story it continues as; the id may be a World Today edition story's, and the answer then names it in `editionStory` under the story's radar id; a story a World Today story seeded carries `volume`, the first page of the posts that story counts across Minds and the open networks, whose `cursor` goes on with `curator.storyPosts` or `curator.memberStoryPosts`), keyless `getSharedRadarStory(projectId, { cluster })` and `getSharedRadarPosts(projectId, { cluster })` (keyless reads carry images and videos only from outlets and members marked `visitorMedia`; signed-in reads keep all media, so a share card uses only those); keyless category surface (0.7.165): `getSharedCategorySurface(projectId, { category, compass? })`, a category's `stories`, `network` and `clips` for a signed-out page in one read under every visitor floor (`compass` `all` or `hide`, or a side or Mix where the visitor catalog lists `compass_sides`, answered `unavailable`; never people or groups); the Viral read (fastest-moving posts across Bluesky, the Fediverse, Nostr and Minds, each against its own network's pace; 0.7.59): `getPublicViral({ network?, category?, media?: 'video', limit?: 5 | 10 | 20 | 40, cursor?, compass? })` under the member's key and keyless `getSharedViral(projectId, params)` (visitors: `compass` `all` or `hide` only; a visitor's `media: 'video'` read can carry items whose video the server withheld, so filter with the exported `viralItemHasVideo` before a video player; `item.post` narrows on `item.protocol`). `status: 'off'` resolves with no items until the app enables the read, and `getSharedViral` also resolves an older API's 401 `missing_api_key` as off; `getPublicViral` throws every 401 and 403, so a session failure is never read as off; private app subscriptions: `listSubscriptions`, `subscribeToProfile`, `unsubscribeFromProfile`, `onSubscriptionChange`. `list` returns bounded adapter `maturity`, `limitations`, and exhaustive `supported`/`planned`/`unsupported` implementation capabilities (not provider health or enablement); settings, candidate refresh/status, search, and read-only quarantined candidate review via `listCandidates` / `getCandidate` |
134
135
 
135
136
  Full reference: see [llm.md](./llm.md) (ships with this package) or the type definitions in `dist/`.
136
137
 
@@ -657,8 +658,67 @@ for (const network of data.networks) {
657
658
  await r.protocols.setNetwork('nostr', { enabled: false, removePast: true });
658
659
  ```
659
660
 
661
+ One-tap actions act as the member's own identity on that network by default (0.7.191). `engageLike`,
662
+ `engageRepost`, `engageFollow`, `engageReply` and `engageQuote` take `identity?: 'native' | 'linked'`. A
663
+ native answer is sent (`queued: false`, with the network's `delivery`) or, while the identity is still
664
+ being set up, queued (202, `queued: true`, sent in order once it is on; `pendingInteractions()` lists
665
+ them). A linked answer is the review to submit, as `prepareNativeLike` always returned. Every answer
666
+ says who acted (`actedAs`). A member who linked an account before the cutover keeps acting as it until they
667
+ choose otherwise with `setEngageDefault(network, 'native')`; the first such answer carries
668
+ `notice: 'engage_default_linked'`. An account linked later is a choice: `setEngageDefault(network, 'linked')`.
669
+ Keep the same `clientMutationId` across retries: a retry is answered from the first tap, never sent twice
670
+ (once that tap expired unsent, with `delivery.code` `interaction_expired`; a new tap takes a new id). A native
671
+ reply's or quote's `media` takes only the member's own Minds uploads, as the linked flows do.
672
+
673
+ ```ts
674
+ const { data } = await r.protocols.engageLike({ protocol: 'bluesky', nativeId: post.nativeId, clientMutationId, action: 'like' });
675
+ if (data.identity === 'native') showActedAs(data.actedAs, data.queued ? 'Sending when your account is ready' : null);
676
+ else await r.protocols.submitNativeLike(data.id, { approve: true });
677
+ ```
678
+
679
+ A member's AT Protocol account is created on the PDS Minds runs, with their verified email (0.7.191); Minds keeps only a
680
+ revocable app password named "Minds". `claimAtprotoPassword({ password })` (or `{ emailCode }` from
681
+ `POST /settings/confirmation-code` with purpose `atproto_password`, for an account with no password) has the PDS
682
+ email a reset code to that address, so the member can sign in from any AT Protocol app and revoke Minds there.
683
+ A member without a verified email reads `needs_action` with reason `needs_verified_email` on AT Protocol.
684
+
685
+ ```ts
686
+ const { data } = await r.protocols.claimAtprotoPassword({ password });
687
+ show(`We sent a reset code for ${data.handle} to ${data.sentTo}`);
688
+ ```
689
+
690
+ AT Protocol reads `needs_action` with reason `needs_atproto_grant` for an account made before Minds ran it natively,
691
+ and `error` with `app_password_revoked` after the member revoked Minds' app password. `grantAtproto({ password })`
692
+ (0.7.191), with that account's password (or a non-privileged app password the member made), lets Minds act as it again:
693
+ Minds keeps only a new revocable app password, and answers the handle and the presence after the grant.
694
+
695
+ ```ts
696
+ const { data } = await r.protocols.grantAtproto({ password: atPassword });
697
+ if (data.presence.state === 'on') show(`Minds acts as ${data.handle} again`);
698
+ ```
699
+
700
+ A member can take their Minds-held Nostr key home, in three steps of their own:
701
+ `protocols.exportNostrKey({ password })` returns it as an `nsec` (an account with no password
702
+ sends `{ emailCode }` from `settings.requestConfirmationCode('nostr_key')`); after they
703
+ import it into their signer, `nostrRebindChallenge()` and `rebindNostrKey(signedEvent)`
704
+ prove the device holds the same key; then `deleteManagedNostrCopy({ password })` deletes
705
+ Minds' copy, and every later Nostr action is signed on their device. What Minds published
706
+ with the key still comes down when they delete a post or their account (Minds signs those
707
+ takedowns before the copy goes; 409 `too_many_published` keeps the copy when there is too
708
+ much). Show the `nsec` to the member alone and never log it. None of these is retried
709
+ automatically.
710
+
660
711
  ### Native Nostr signing
661
712
 
713
+ A member's Nostr key is either their own, signed on their device, or one Minds holds for them (0.7.192).
714
+ `nativeNostrAccount()` says which: `signing: 'client'` is the flow below; `signing: 'server'` (with
715
+ `mode: 'managed'`) means Minds signs. Then `prepareNativeNostr` is the preview to show (nothing is signed
716
+ or sent), and `prepareNativeNostr(id, { ...input, send: true })` once the member approves signs and sends
717
+ in that call and answers with the `receipt` (nothing to submit). A like, repost or follow review is submitted with
718
+ `{ approve: true }` and no `event`, and `engageLike`, `engageReply`, `engageFollow` and the rest with
719
+ `identity: 'native'` act as that key in one tap. For a key Minds holds, a like or repost Minds is still sending (the
720
+ relay has not taken it yet) reads as liked or reposted with `acknowledged: false`, and an unlike or unrepost removes it.
721
+
662
722
  Use `protocols.nativeNostrAccount()` to check the app's explicit native-write
663
723
  capability. For an enabled connected key, prepare a text post with
664
724
  `prepareNativeNostr(identityId, { action: 'publish', content })`, show its
@@ -959,6 +1019,16 @@ is a visitor's top (safe items only, the same for every visitor in one 15-minute
959
1019
  read with no credential so the edge can hold it. Each answers 404 while the app's operator
960
1020
  has it off. Send `block: 'top'` and the item's position with a visible view of a top item.
961
1021
 
1022
+ `sdk.feeds.firehose({ session, limit?, compass?, network? })` (0.7.195) is the member's live
1023
+ stream: the next items of the most engaged, viral and breaking posts of Minds and the open
1024
+ networks, re-checked for the member on the server and never sent twice in one `session`. Each
1025
+ item carries `network`, `state` (`breaking`, `viral` or null), `publishedAt`, `chosenAt`, an
1026
+ in-app `target` and, for an open-network post, a `card`. Reveal them one at a time and read
1027
+ again no sooner than `meta.refillAfterMs`; remove the refs in `retract` wherever you hold them.
1028
+ 404 while the app's operator has it off; 429 and 503 carry Retry-After. A Mix or Choose sides `compass`
1029
+ spends one of the member's hourly sided Politics reads at most once per `session` and choice in ten
1030
+ minutes, and answers 429 `compass_rate_limited` once those are spent.
1031
+
962
1032
  A feed session opens with it when asked: `createFeed({ networks, top: { ref, session? }, seen,
963
1033
  seenClusters?, bucket? })` (or `new DiscoveryFeedSession(..., { top: { ref, read, post? }, seen })`
964
1034
  with your own readers). The top's read starts with the first page's reads, and the top holds
package/dist/index.d.ts CHANGED
@@ -68,6 +68,7 @@ import { LinkPreviewResource } from './resources/link-preview.js';
68
68
  import { SearchResource } from './resources/search.js';
69
69
  import { BookmarksResource } from './resources/bookmarks.js';
70
70
  import { FeedsResource } from './resources/feeds.js';
71
+ import { AliveResource } from './resources/alive.js';
71
72
  import { AccountsResource } from './resources/accounts.js';
72
73
  import { SignalsResource } from './resources/signals.js';
73
74
  import { SealedResource } from './resources/sealed.js';
@@ -133,6 +134,8 @@ export declare class Recursiv {
133
134
  readonly discovery: DiscoveryResource;
134
135
  /** The top of a feed: what the member (or a visitor) is most likely to engage with, rotated by what they have seen. */
135
136
  readonly feeds: FeedsResource;
137
+ /** The alive tick: whether people the member follows posted, current counts on screen, new replies (0.7.193). */
138
+ readonly alive: AliveResource;
136
139
  readonly video: VideoResource;
137
140
  readonly appSubscriptions: AppSubscriptionsResource;
138
141
  readonly boosts: BoostsResource;
@@ -182,7 +185,7 @@ export type { E2eeAccountReset, E2eeDevice, E2eeDevicesRealtimeEvent, E2eeEnvelo
182
185
  export { ProjectsResource } from './resources/projects.js';
183
186
  export { AgentsResource, SUPPORTED_MODELS, AGENT_DIRECTORY_SORTS, AGENT_DIRECTORY_MAX_LIMIT } from './resources/agents.js';
184
187
  export { EvidenceResource } from './resources/evidence.js';
185
- export type { EvidenceTarget, EvalType, CreateEvalInput, SimulateInput, SimulateResult, EvalRunRow, EvalResultRow, EvalRunDetail, EvidenceSummary, EvidenceEvent, EvidenceRecordDetail } from './resources/evidence.js';
188
+ export type { EvidenceTarget, EvalType, CreateEvalInput, SimulateInput, SimulateResult, EvalRunRow, EvalResultRow, EvalRunDetail, EvidenceSummary, EvidenceEvent, EvidenceRecordDetail, CreateEvalSetInput, EvalSetRow, EvalRunExport } from './resources/evidence.js';
186
189
  export type { AgentMessage, AgentMessageInput, AgentInboxParams, AgentAskInput, AgentAskResult, SupportedModel, SalesLead, SalesLeadListParams, SalesLeadStatus, AgentDirectorySort, AgentDirectoryParams, AgentDirectoryEntry, AgentDirectoryMeta, AgentDirectoryPage } from './resources/agents.js';
187
190
  export { TagsResource } from './resources/tags.js';
188
191
  export { SandboxResource } from './resources/sandbox.js';
@@ -205,7 +208,7 @@ export { EnvVarsResource } from './resources/env-vars.js';
205
208
  export type { QueryDatabaseInput, QueryResult } from './resources/databases.js';
206
209
  export { GitHubResource } from './resources/github.js';
207
210
  export { DeploymentsResource } from './resources/deployments.js';
208
- export type { DeploymentReleaseInput, DeploymentRelease, DeploymentReleaseOperation, DeploymentReleaseRecoveryInput } from './resources/deployments.js';
211
+ export type { DeploymentReleaseInput, DeploymentReleaseReservationInput, DeploymentRelease, DeploymentReleaseOperation, DeploymentReleaseRecoveryInput } from './resources/deployments.js';
209
212
  export type { PrepareProtocolServersInput, PreparedProtocolServers, ProtocolServerPreparation, ProtocolServerUpgradePreview, ProtocolServerUpgradeInput, ProtocolServerUpgradeStatus } from './resources/deployments.js';
210
213
  export { IntegrationsResource } from './resources/integrations.js';
211
214
  export type { ExecuteToolInput, ExecuteToolResult } from './resources/integrations.js';
@@ -229,10 +232,12 @@ export { TrustResource } from './resources/trust.js';
229
232
  export type { TrustEnvelope, OwnTrustEvent, TrustPublicKey, OwnTrustEvents } from './resources/trust.js';
230
233
  export { LinkPreviewResource } from './resources/link-preview.js';
231
234
  export { SEARCH_MEDIA_KINDS, SEARCH_SECTIONS, SearchResource } from './resources/search.js';
232
- export type { PublicSearchPage, PublicSearchTab, SearchAnswer, SearchGroup, SearchLadder, SearchOmitReason, SearchParams, SearchPerson, SearchPost, SearchRelated, SearchResponse, SearchSection, SearchStage, SearchStory, SearchTopic, SearchVia, SearchMedia, SearchMediaEmbed, SearchMediaItem, SearchMediaKind, SearchMediaProvider, SearchMediaProviderStatus, SearchMediaSource, } from './resources/search.js';
235
+ export type { PublicSearchPage, PublicSearchTab, SearchAnswer, SearchGroup, SearchLadder, SearchOmitReason, SearchParams, SearchPerson, SearchPicture, SearchPost, SearchRelated, SearchResponse, SearchSection, SearchStage, SearchStory, SearchTopic, SearchVia, SearchVideoLeadEntry, SearchVideoLeadSource, SearchMedia, SearchMediaEmbed, SearchMediaItem, SearchMediaKind, SearchMediaProvider, SearchMediaProviderStatus, SearchMediaSource, } from './resources/search.js';
233
236
  export { BOOKMARK_MERGE_MAX_ITEMS, BookmarkCollectionsResource, BookmarksResource } from './resources/bookmarks.js';
234
237
  export { FEED_TOP_NETWORKS, FeedsResource } from './resources/feeds.js';
235
- export type { FeedStoriesParams, FeedStoriesResponse, FeedStory, FeedTopExplain, FeedTopItem, FeedTopMeta, FeedTopNetwork, FeedTopNetworkCard, FeedTopNetworkEntry, FeedTopNetworkMap, FeedTopParams, FeedTopResponse, FeedTopTarget, PublicFeedTopMeta, PublicFeedTopParams, PublicFeedTopResponse, } from './resources/feeds.js';
238
+ export { AliveResource } from './resources/alive.js';
239
+ export type { AliveTick, AliveTickCounts, AliveTickParams } from './resources/alive.js';
240
+ export type { FeedFirehoseItem, FeedFirehoseMeta, FeedFirehoseNetwork, FeedFirehoseParams, FeedFirehoseResponse, FeedStoriesParams, FeedStoriesResponse, FeedStory, FeedTopExplain, FeedTopItem, FeedTopMeta, FeedTopNetwork, FeedTopNetworkCard, FeedTopNetworkEntry, FeedTopNetworkMap, FeedTopParams, FeedTopResponse, FeedTopTarget, PublicFeedTopMeta, PublicFeedTopParams, PublicFeedTopResponse, } from './resources/feeds.js';
236
241
  export type { BookmarkCollection, BookmarkCollectionInput, BookmarkCollectionKind, BookmarkCollectionPatch, BookmarkCollectionsList, BookmarkCollectionTarget, BookmarkCollectionVisibility, BookmarkedPost, BookmarkInput, BookmarkItem, BookmarkKey, BookmarkKeyRow, BookmarkKeysPage, BookmarkKind, BookmarkMergeItem, BookmarkMergeResult, BookmarkProtocol, BookmarkRequestOptions, BookmarksPage, FiledBookmark, RemovedBookmarkCollection, SavedBookmark, UnfiledBookmark, UnsavedBookmark, } from './resources/bookmarks.js';
237
242
  export { AccountsResource } from './resources/accounts.js';
238
243
  export { SIGNAL_BATCH_MAX, SignalsResource, newClientEventId } from './resources/signals.js';
@@ -249,11 +254,11 @@ export { UploadsResource } from './resources/uploads.js';
249
254
  export type { UploadMediaInput, UploadUrlInput, UploadUrlResult } from './resources/uploads.js';
250
255
  export { WalletResource } from './resources/wallet.js';
251
256
  export type { LinkedTokenWallet, PayoutState, TokenBalance, TokenLedgerEntry, WalletLink, WalletLinkNonce } from './resources/wallet.js';
252
- export { BoostsResource } from './resources/boosts.js';
253
- export type { BoostBalance, BoostBalanceResponse, BoostCampaign, BoostCampaignResponse, BoostEventResult, BoostLedgerEntry, BoostLedgerKind, BoostPlanCredit, BoostRejectionReason, BoostReviewStatus, BoostStatus, BoostTopupCheckout, BoostTopupConfirmation, BoostTopupLimits, } from './resources/boosts.js';
257
+ export { BoostsResource, spliceBoostSlots } from './resources/boosts.js';
258
+ export type { BoostBalance, BoostBalanceResponse, BoostCampaign, BoostCampaignResponse, BoostCampaignStats, BoostDailyStats, BoostEventResult, BoostLedgerEntry, BoostLedgerKind, BoostPlanCredit, BoostRejectionReason, BoostReviewStatus, BoostSlotItem, BoostSlotSurface, BoostSlotsParams, BoostSlotsResponse, BoostStatus, BoostTopupCheckout, BoostTopupConfirmation, BoostTopupLimits, BoostTokenTopupInfo, BoostTokenTopupResult, PublicBoostSlotsParams, } from './resources/boosts.js';
254
259
  export { ProtocolsResource } from './resources/protocols.js';
255
260
  export { PROTOCOL_ADAPTER_MATURITIES, PROTOCOL_CAPABILITY_OPERATIONS, PROTOCOL_CAPABILITY_STATUSES, SOCIAL_FOLLOWING_SOURCE_STATUSES, viralItemHasVideo, } from './resources/protocols.js';
256
- export type { SocialProfile, SocialBridge, SocialMention, SocialSubscription, SocialSubscriptionChange, SocialProfilePage, SocialRadar, SocialRadarCluster, SocialRadarMember, SocialRadarParams, SocialRadarSourceKind, SocialRadarPosts, SocialRadarStory, SocialRadarStoryArchive, ViralBaseline, ViralItem, ViralItemFields, ViralItemSpread, ViralItemVelocity, ViralLabel, ViralMindsCard, ViralMindsItem, ViralNetwork, ViralOpenProtocol, ViralProtocol, ViralProvider, ViralReadMeta, ViralReadParams, ViralReadResponse, ViralReadStatus, ViralRef, ViralSocialItem, SocialNetwork, SocialPost, SocialConversation, SocialTrend, SocialTrendProvenance, SocialTrends, SocialTrendSourceIndex, SocialTrendFeed, SocialPopularProfiles, SocialPopularProfile, SocialPopularProfilesParams, SocialPopularProfilesProtocol, SocialPopularProfilesSource, SocialAuthorFeed, OpenGroupThreads, OpenGroupThreadsGroup, OpenGroupThreadsParams, SocialFollowingFeed, SocialFollowingSource, SocialFollowingSourceStatus, SearchPublicProfilesParams, SearchPublicPostsParams, SocialPostSearch, HostedProtocolConnections, HostedProtocol, HostedProtocolIdentity, HostedNostrChallenge, HostedNostrIdentityProof, NativeNostrPrepareInput, NativeNostrPrepared, NativeNostrAccount, NativeNostrPostState, NativeNostrReceipt, NativeSocialProtocol, FederationIdentity, FederationIdentityView, NativeSocialAccount, NativeNetwork, NativeNetworkState, NativeNetworkPresence, NativeNetworkActivation, NativeNetworkOff, SetNativeNetworkInput, NativeSocialCallbackInput, NativeReplyPrepareInput, NativePostPrepareInput, NativeMediaInput, NativeMediaItem, NativePostBatch, NativePostQuote, NativePostHistory, NativeDelegatedPostHistoryItem, NativeNostrPostHistoryItem, NativeFollowSourceInput, NativeFollowPrepareInput, NativeFollowTarget, NativeFollowReceipt, NativeFollowState, NativeFollowAccount, NativeFollowStates, NativeFollowReview, NativeLikeSourceInput, NativeRepostSourceInput, NativeLikePrepareInput, NativeRepostPrepareInput, NativeLikeTarget, NativeRepostTarget, NativeLikeReceipt, NativeRepostReceipt, NativeLikeState, NativeRepostState, NativeLikeReview, NativeRepostReview, NativeReplyReceipt, NativeReplyBatch, NativeReplyHistory, NativeDelegatedReplyHistoryItem, NativeNostrReplyResult, NativeNostrReplyHistoryItem, NativeReplyRemovalReceipt, NativeReplyRemovalPreview, SocialSourceRef, SocialSourceControl, SocialSourceControlDisplay, SocialSourceControlChange, HostedPostPreview, ConnectHostedIdentityInput, CreateHostedIdentityInput, CreatedHostedProtocolIdentity, PreviewHostedPostInput, PublishHostedPostInput, ListProtocolCandidatesParams, ProtocolAdapterMaturity, ProtocolAdapterMetadata, ProtocolCandidateListResponse, ProtocolCandidatePageMeta, ProtocolCapabilityOperation, ProtocolCapabilityStatus, ProtocolCollectionStatus, ProtocolIngestionActivityReason, ProtocolIngestionCircuitHealth, ProtocolIngestionDeadLetterGroup, ProtocolIngestionHealthPosition, ProtocolIngestionHealthReport, ProtocolIngestionHealthRuntime, ProtocolIngestionHealthSection, ProtocolIngestionLaneActivity, ProtocolIngestionLaneCurrentJob, ProtocolIngestionLaneDeadLetters, ProtocolIngestionLaneHealth, ProtocolIngestionLaneJobs, ProtocolIngestionReplayResult, ProtocolIngestionStatus, ProtocolQuarantinedCandidate, ProtocolRefreshResult, ProtocolSettings, ProtocolSettingsUpdateResult, ReplayProtocolIngestionJobInput, UpdateSettingsInput as UpdateProtocolSettingsInput, } from './resources/protocols.js';
261
+ export type { SocialProfile, SocialBridge, SocialMention, SocialSubscription, SocialSubscriptionChange, SocialProfilePage, SocialRadar, SocialRadarCluster, SocialRadarMember, SocialRadarParams, SocialRadarSourceKind, SocialRadarPosts, SocialRadarStory, SharedCategorySurface, SharedCategorySurfaceParams, SocialRadarStoryArchive, ViralBaseline, ViralItem, ViralItemFields, ViralItemSpread, ViralItemVelocity, ViralLabel, ViralMindsCard, ViralMindsItem, ViralNetwork, ViralOpenProtocol, ViralProtocol, ViralProvider, ViralReadMeta, ViralReadParams, ViralReadResponse, ViralReadStatus, ViralRef, ViralSocialItem, SocialNetwork, SocialPost, SocialConversation, SocialTrend, SocialTrendProvenance, SocialTrends, SocialTrendSourceIndex, SocialTrendFeed, SocialPopularProfiles, SocialPopularProfile, SocialPopularProfilesParams, SocialPopularProfilesProtocol, SocialPopularProfilesSource, SocialAuthorFeed, OpenGroupThreads, OpenGroupThreadsGroup, OpenGroupThreadsParams, SocialFollowingFeed, SocialFollowingSource, SocialFollowingSourceStatus, SearchPublicProfilesParams, SearchPublicPostsParams, SocialPostSearch, HostedProtocolConnections, HostedProtocol, HostedProtocolIdentity, HostedNostrChallenge, HostedNostrIdentityProof, NativeNostrPrepareInput, NativeNostrPrepared, NativeNostrAccount, NativeNostrPostState, NativeNostrReceipt, NativeSocialProtocol, FederationIdentity, FederationIdentityView, NativeSocialAccount, NativeNetwork, NativeNetworkState, NativeNetworkPresence, NativeNetworkActivation, NativeNetworkOff, SetNativeNetworkInput, AtprotoPasswordClaimProof, AtprotoPasswordClaim, AtprotoGrant, EngageIdentity, EngageActedAs, EngageLinkedFields, NativeEngageSent, NativeEngageQueued, EngageAnswer, PendingNativeInteraction, EngageOneInput, NostrKeyStepUp, ManagedNostrKeyExport, NostrRebindChallenge, NostrRebindResult, ManagedNostrCopyDeleted, NativeSocialCallbackInput, NativeReplyPrepareInput, NativePostPrepareInput, NativeMediaInput, NativeMediaItem, NativePostBatch, NativePostQuote, NativePostHistory, NativeDelegatedPostHistoryItem, NativeNostrPostHistoryItem, NativeFollowSourceInput, NativeFollowPrepareInput, NativeFollowTarget, NativeFollowReceipt, NativeFollowState, NativeFollowAccount, NativeFollowStates, NativeFollowReview, NativeLikeSourceInput, NativeRepostSourceInput, NativeLikePrepareInput, NativeRepostPrepareInput, NativeLikeTarget, NativeRepostTarget, NativeLikeReceipt, NativeRepostReceipt, NativeLikeState, NativeRepostState, NativeLikeReview, NativeRepostReview, NativeReplyReceipt, NativeReplyBatch, NativeReplyHistory, NativeDelegatedReplyHistoryItem, NativeNostrReplyResult, NativeNostrReplyHistoryItem, NativeReplyRemovalReceipt, NativeReplyRemovalPreview, SocialSourceRef, SocialSourceControl, SocialSourceControlDisplay, SocialSourceControlChange, HostedPostPreview, ConnectHostedIdentityInput, CreateHostedIdentityInput, CreatedHostedProtocolIdentity, PreviewHostedPostInput, PublishHostedPostInput, ListProtocolCandidatesParams, ProtocolAdapterMaturity, ProtocolAdapterMetadata, ProtocolCandidateListResponse, ProtocolCandidatePageMeta, ProtocolCapabilityOperation, ProtocolCapabilityStatus, ProtocolCollectionStatus, ProtocolIngestionActivityReason, ProtocolIngestionCircuitHealth, ProtocolIngestionDeadLetterGroup, ProtocolIngestionHealthPosition, ProtocolIngestionHealthReport, ProtocolIngestionHealthRuntime, ProtocolIngestionHealthSection, ProtocolIngestionLaneActivity, ProtocolIngestionLaneCurrentJob, ProtocolIngestionLaneDeadLetters, ProtocolIngestionLaneHealth, ProtocolIngestionLaneJobs, ProtocolIngestionReplayResult, ProtocolIngestionStatus, ProtocolQuarantinedCandidate, ProtocolRefreshResult, ProtocolSettings, ProtocolSettingsUpdateResult, ReplayProtocolIngestionJobInput, UpdateSettingsInput as UpdateProtocolSettingsInput, } from './resources/protocols.js';
257
262
  export { InboxResource } from './resources/inbox.js';
258
263
  export { NetworkResource } from './resources/network.js';
259
264
  export { SimulatorResource } from './resources/simulator.js';
package/dist/index.js CHANGED
@@ -69,6 +69,7 @@ import { LinkPreviewResource } from './resources/link-preview.js';
69
69
  import { SearchResource } from './resources/search.js';
70
70
  import { BookmarksResource } from './resources/bookmarks.js';
71
71
  import { FeedsResource } from './resources/feeds.js';
72
+ import { AliveResource } from './resources/alive.js';
72
73
  import { AccountsResource } from './resources/accounts.js';
73
74
  import { SignalsResource } from './resources/signals.js';
74
75
  import { SealedResource } from './resources/sealed.js';
@@ -134,6 +135,8 @@ export class Recursiv {
134
135
  discovery;
135
136
  /** The top of a feed: what the member (or a visitor) is most likely to engage with, rotated by what they have seen. */
136
137
  feeds;
138
+ /** The alive tick: whether people the member follows posted, current counts on screen, new replies (0.7.193). */
139
+ alive;
137
140
  video;
138
141
  appSubscriptions;
139
142
  boosts;
@@ -212,6 +215,7 @@ export class Recursiv {
212
215
  this.jobs = new JobsResource(client);
213
216
  this.curator = new CuratorResource(client);
214
217
  this.feeds = new FeedsResource(client);
218
+ this.alive = new AliveResource(client);
215
219
  this.discovery = new DiscoveryResource(this.posts, this.protocols, this.curator, client, this.feeds);
216
220
  this.video = new VideoResource(client);
217
221
  this.appSubscriptions = new AppSubscriptionsResource(client);
@@ -287,6 +291,7 @@ export { LinkPreviewResource } from './resources/link-preview.js';
287
291
  export { SEARCH_MEDIA_KINDS, SEARCH_SECTIONS, SearchResource } from './resources/search.js';
288
292
  export { BOOKMARK_MERGE_MAX_ITEMS, BookmarkCollectionsResource, BookmarksResource } from './resources/bookmarks.js';
289
293
  export { FEED_TOP_NETWORKS, FeedsResource } from './resources/feeds.js';
294
+ export { AliveResource } from './resources/alive.js';
290
295
  export { AccountsResource } from './resources/accounts.js';
291
296
  export { SIGNAL_BATCH_MAX, SignalsResource, newClientEventId } from './resources/signals.js';
292
297
  export { SEALED_ALLOWANCE_REMAINING_HEADER, SEALED_REFUSAL_HEADER, SealedResource, SealedTransportError, SEALED_WEB_OFF, isSealedWebOff, } from './resources/sealed.js';
@@ -295,7 +300,7 @@ export { ProjectSettingsResource } from './resources/project-settings.js';
295
300
  export { OrganizationSecurityResource } from './resources/organization-security.js';
296
301
  export { UploadsResource } from './resources/uploads.js';
297
302
  export { WalletResource } from './resources/wallet.js';
298
- export { BoostsResource } from './resources/boosts.js';
303
+ export { BoostsResource, spliceBoostSlots } from './resources/boosts.js';
299
304
  export { ProtocolsResource } from './resources/protocols.js';
300
305
  export { PROTOCOL_ADAPTER_MATURITIES, PROTOCOL_CAPABILITY_OPERATIONS, PROTOCOL_CAPABILITY_STATUSES, SOCIAL_FOLLOWING_SOURCE_STATUSES, viralItemHasVideo, } from './resources/protocols.js';
301
306
  export { InboxResource } from './resources/inbox.js';
@@ -0,0 +1,61 @@
1
+ import type { HttpClient } from '../client.js';
2
+ /**
3
+ * The alive tick (0.7.193): one calm read for the member, with their own key of the app, that says whether anyone they
4
+ * follow has posted since the newest row they hold, keeps the counts of the posts on their screen current, and counts
5
+ * the new replies under the post they have open. Off until the operator turns on `ALIVE_TICK_ENABLED`: the read then
6
+ * throws `NotFoundError` (code `alive_disabled`), and the app stops ticking.
7
+ *
8
+ * Tick again after `nextAfterMs` (the server slows every client this way), only while the page is visible. One tick
9
+ * carries every open screen's interests. A tick is asked once (`retry: false`): the next tick is its retry. More than
10
+ * 4 a minute for one member throws `RateLimitError`.
11
+ */
12
+ /** What one tick asks about. Each part is answered only when asked for. */
13
+ export interface AliveTickParams {
14
+ /** The newest row the member holds on Following (a Date or an ISO 8601 instant). */
15
+ followingSince?: Date | string;
16
+ /** Ids of the Minds posts on screen, at most 30 (more is refused). */
17
+ posts?: readonly string[];
18
+ /** The open post's id and the newest reply the member holds under it (or when the thread was loaded). */
19
+ thread?: {
20
+ id: string;
21
+ since: Date | string;
22
+ };
23
+ }
24
+ /** A post's numbers, as its card shows them. */
25
+ export interface AliveTickCounts {
26
+ score: number;
27
+ upvote_count: number;
28
+ reply_count: number;
29
+ reposts_count: number;
30
+ }
31
+ export interface AliveTick {
32
+ /**
33
+ * Whether anyone the member follows posted a top-level Minds post after `followingSince` that they may be served
34
+ * (their blocks, mutes and safety setting applied), and when the newest of them was written. Load the new rows behind
35
+ * the new-posts pill; never move what the member is reading.
36
+ */
37
+ following?: {
38
+ fresh: boolean;
39
+ newestAt: string | null;
40
+ };
41
+ /**
42
+ * The current numbers of the posts asked for that the member may be served, by post id. A post left out (a repost,
43
+ * a reply, one the member may not be served) keeps the number the app has.
44
+ */
45
+ counts?: Record<string, AliveTickCounts>;
46
+ /** How many replies under the open post the member may see were written after `thread.since`, their own left out. */
47
+ thread?: {
48
+ newReplies: number;
49
+ newestAt: string | null;
50
+ };
51
+ /** When to tick next, in milliseconds. */
52
+ nextAfterMs: number;
53
+ /** When the server read it. */
54
+ asOf: string;
55
+ }
56
+ export declare class AliveResource {
57
+ private client;
58
+ constructor(client: HttpClient);
59
+ /** One tick for the member (GET /alive/tick, `users:read`, their own key of the app). */
60
+ tick(params?: AliveTickParams): Promise<AliveTick>;
61
+ }
@@ -0,0 +1,16 @@
1
+ const iso = (value) => (value instanceof Date ? value.toISOString() : value);
2
+ export class AliveResource {
3
+ client;
4
+ constructor(client) {
5
+ this.client = client;
6
+ }
7
+ /** One tick for the member (GET /alive/tick, `users:read`, their own key of the app). */
8
+ tick(params = {}) {
9
+ return this.client.get('/alive/tick', {
10
+ following_since: params.followingSince === undefined ? undefined : iso(params.followingSince),
11
+ posts: params.posts?.length ? params.posts.join(',') : undefined,
12
+ thread: params.thread?.id,
13
+ thread_since: params.thread ? iso(params.thread.since) : undefined,
14
+ }, { retry: false });
15
+ }
16
+ }
@@ -1,5 +1,5 @@
1
1
  import type { HttpClient } from '../client.js';
2
- import type { PaginatedResponse, SingleResponse } from '../types.js';
2
+ import type { PaginatedResponse, Post, SingleResponse } from '../types.js';
3
3
  /** Campaign lifecycle axis. */
4
4
  export type BoostStatus = 'active' | 'paused' | 'completed' | 'refunded';
5
5
  /** Campaign moderation axis. A campaign delivers only when `active` AND `approved`. */
@@ -59,12 +59,30 @@ export interface BoostPlanCredit {
59
59
  */
60
60
  owed_micros?: number;
61
61
  }
62
+ /** Paying for Boost with tokens. */
63
+ export interface BoostTokenTopupInfo {
64
+ /** False until the platform sets the price of a token. */
65
+ available: boolean;
66
+ /** Micro-USD of Boost balance one token buys; null when unavailable. */
67
+ usd_micros_per_token: number | null;
68
+ /** Most whole tokens one conversion takes. */
69
+ max_tokens: number;
70
+ }
71
+ export interface BoostTokenTopupResult {
72
+ /** False when this request_id was already credited: nothing new was written. */
73
+ applied: boolean;
74
+ tokens: number;
75
+ credited_micros: number;
76
+ balance_micros: number;
77
+ }
62
78
  export interface BoostBalanceResponse extends SingleResponse<BoostBalance> {
63
79
  /** `plan_credit` is null when the app's plans include no boost credit (absent from older servers). */
64
80
  meta: {
65
81
  min_cpm_micros: number;
66
82
  topup?: BoostTopupLimits;
67
83
  plan_credit?: BoostPlanCredit | null;
84
+ /** Absent from older servers. */
85
+ token_topup?: BoostTokenTopupInfo;
68
86
  };
69
87
  }
70
88
  export interface BoostCampaignResponse extends SingleResponse<BoostCampaign> {
@@ -80,6 +98,27 @@ export interface BoostEventResult {
80
98
  spent_micros: number;
81
99
  impressions_served: number;
82
100
  }
101
+ /** One UTC day of a campaign's results. */
102
+ export interface BoostDailyStats {
103
+ /** `YYYY-MM-DD`, UTC. */
104
+ date: string;
105
+ impressions: number;
106
+ clicks: number;
107
+ }
108
+ /** What a campaign delivered: totals and a per-day series (days with no events are absent). */
109
+ export interface BoostCampaignStats {
110
+ campaign: BoostCampaign;
111
+ totals: {
112
+ impressions: number;
113
+ clicks: number;
114
+ /** clicks / impressions, or null before the first view. */
115
+ click_rate: number | null;
116
+ spent_micros: number;
117
+ /** Spend per 1,000 views in micro-USD, or null before the first view. */
118
+ cost_per_1000_micros: number | null;
119
+ };
120
+ daily: BoostDailyStats[];
121
+ }
83
122
  export interface BoostTopupCheckout {
84
123
  /** Stripe Checkout URL to open. */
85
124
  url: string;
@@ -92,6 +131,47 @@ export interface BoostTopupConfirmation {
92
131
  credited_micros: number;
93
132
  balance_micros: number | null;
94
133
  }
134
+ /** A feed whose boosts the app splices into its pages itself: All, a category, or the big-screen grid of either. */
135
+ export type BoostSlotSurface = 'all' | 'category' | 'grid';
136
+ export interface BoostSlotsParams {
137
+ surface: BoostSlotSurface;
138
+ /** A browse category id: required for `category`, allowed for `grid`, refused for `all`. */
139
+ category?: string;
140
+ /** How many posts the page has before its boosts (1..50, default 20). */
141
+ limit?: number;
142
+ /** Up to 50 post ids already on the page (and the originals they carry): never served as a boost. */
143
+ exclude?: readonly string[];
144
+ /** The Politics choice the page is served under: under one that leaves posts out, only boosts judged not political. */
145
+ compass?: string;
146
+ }
147
+ export interface PublicBoostSlotsParams extends BoostSlotsParams {
148
+ /** The public app whose feed it is. */
149
+ projectId: string;
150
+ }
151
+ /** One boost of a page: where it stands in the page as served, and the boosted post (labelled `boosted: true`). */
152
+ export interface BoostSlotItem {
153
+ place: number;
154
+ post: Post;
155
+ }
156
+ export interface BoostSlotsResponse {
157
+ data: BoostSlotItem[];
158
+ meta: {
159
+ surface: BoostSlotSurface;
160
+ category: string | null;
161
+ limit: number;
162
+ /** One boost per `every` posts. */
163
+ every: number;
164
+ /** No boost before this place (0-based): the first two places are never a boost. */
165
+ first: number;
166
+ /** Why there are none: the key is bound to no app, or (a visitor's read) the app is not public. */
167
+ reason?: 'no_app' | 'not_public';
168
+ };
169
+ }
170
+ /**
171
+ * A page with its boosts in their places: each slot's post goes in at its `place`, in order, and one whose place the
172
+ * page does not reach is left out, so a short page never pulls a boost forward. The page's own posts keep their order.
173
+ */
174
+ export declare function spliceBoostSlots<T>(page: readonly T[], slots: readonly BoostSlotItem[]): Array<T | Post>;
95
175
  /**
96
176
  * Boost: pay to promote your own post into the For You feed. CPM campaigns
97
177
  * funded from a prepaid balance with an append-only ledger. Top-ups run on the
@@ -126,6 +206,35 @@ export declare class BoostsResource {
126
206
  resume(campaignId: string): Promise<BoostCampaignResponse>;
127
207
  /** Cancel a campaign and refund its unspent budget to your balance (`meta.refunded_micros`). */
128
208
  cancel(campaignId: string): Promise<BoostCampaignResponse>;
209
+ /**
210
+ * One page's boosts for a feed the app splices them into itself (All, a category, or the big-screen grid), read beside
211
+ * the page: pass the page's own post count as `limit` and its post ids as `exclude`, then put each item's post in at
212
+ * its `place` (`spliceBoostSlots`). Each post is labelled `boosted: true` with `boost_campaign_id` and
213
+ * `boost_disclosure`; report what was seen with `recordEvent`. Never the member's own campaign, one they hid or one
214
+ * walled from them. Watch's boosts come on its own pages.
215
+ */
216
+ slots(params: BoostSlotsParams, options?: {
217
+ retry?: boolean;
218
+ }): Promise<BoostSlotsResponse>;
219
+ /**
220
+ * A visitor's boosts for one page of a public app's feed (tRPC `publicDiscovery.boostSlots`), as `slots` answers a
221
+ * member: read with no credential at all (no key, no cookie: `credentials: 'omit'`). An app that is not public answers
222
+ * none (`meta.reason` `not_public`).
223
+ */
224
+ publicSlots(params: PublicBoostSlotsParams, options?: {
225
+ timeoutMs?: number;
226
+ }): Promise<BoostSlotsResponse>;
227
+ /** Results for one of your campaigns: views, clicks, click rate, spend and a daily series. */
228
+ stats(campaignId: string): Promise<SingleResponse<BoostCampaignStats>>;
229
+ /**
230
+ * Turn whole tokens into Boost balance at the platform's price (`balance().meta.token_topup`).
231
+ * `request_id` (8 to 100 of A-Z a-z 0-9 - _) makes a retry safe: the same id debits once.
232
+ * Not retried automatically.
233
+ */
234
+ topupWithTokens(input: {
235
+ tokens: number;
236
+ request_id: string;
237
+ }): Promise<SingleResponse<BoostTokenTopupResult>>;
129
238
  /** Report an impression or click on a boosted feed entry. Idempotent per viewer per UTC day. */
130
239
  recordEvent(campaignId: string, kind: 'impression' | 'click'): Promise<SingleResponse<BoostEventResult>>;
131
240
  /**
@@ -1,3 +1,17 @@
1
+ import { NotFoundError, RecursivError } from '../errors.js';
2
+ /**
3
+ * A page with its boosts in their places: each slot's post goes in at its `place`, in order, and one whose place the
4
+ * page does not reach is left out, so a short page never pulls a boost forward. The page's own posts keep their order.
5
+ */
6
+ export function spliceBoostSlots(page, slots) {
7
+ const out = [...page];
8
+ for (const slot of [...slots].sort((a, b) => a.place - b.place)) {
9
+ if (!Number.isInteger(slot.place) || slot.place < 0 || slot.place > out.length)
10
+ continue;
11
+ out.splice(slot.place, 0, slot.post);
12
+ }
13
+ return out;
14
+ }
1
15
  /**
2
16
  * Boost: pay to promote your own post into the For You feed. CPM campaigns
3
17
  * funded from a prepaid balance with an append-only ledger. Top-ups run on the
@@ -39,6 +53,72 @@ export class BoostsResource {
39
53
  cancel(campaignId) {
40
54
  return this.client.post(`/boosts/${encodeURIComponent(campaignId)}/cancel`, {});
41
55
  }
56
+ /**
57
+ * One page's boosts for a feed the app splices them into itself (All, a category, or the big-screen grid), read beside
58
+ * the page: pass the page's own post count as `limit` and its post ids as `exclude`, then put each item's post in at
59
+ * its `place` (`spliceBoostSlots`). Each post is labelled `boosted: true` with `boost_campaign_id` and
60
+ * `boost_disclosure`; report what was seen with `recordEvent`. Never the member's own campaign, one they hid or one
61
+ * walled from them. Watch's boosts come on its own pages.
62
+ */
63
+ slots(params, options) {
64
+ return this.client.get('/boosts/slots', {
65
+ surface: params.surface, category: params.category, limit: params.limit,
66
+ exclude: params.exclude?.length ? params.exclude.join(',') : undefined, compass: params.compass,
67
+ }, options);
68
+ }
69
+ /**
70
+ * A visitor's boosts for one page of a public app's feed (tRPC `publicDiscovery.boostSlots`), as `slots` answers a
71
+ * member: read with no credential at all (no key, no cookie: `credentials: 'omit'`). An app that is not public answers
72
+ * none (`meta.reason` `not_public`).
73
+ */
74
+ async publicSlots(params, options) {
75
+ const input = { projectId: params.projectId, surface: params.surface, ...(params.category ? { category: params.category } : {}),
76
+ ...(params.limit !== undefined ? { limit: params.limit } : {}), ...(params.exclude?.length ? { exclude: [...params.exclude] } : {}),
77
+ ...(params.compass ? { compass: params.compass } : {}) };
78
+ const url = `${this.client.baseOrigin}/api/trpc/publicDiscovery.boostSlots?input=${encodeURIComponent(JSON.stringify(input))}`;
79
+ const controller = new AbortController();
80
+ const timer = setTimeout(() => controller.abort(), options?.timeoutMs ?? this.client.requestTimeoutMs);
81
+ try {
82
+ let response;
83
+ try {
84
+ response = await fetch(url, { method: 'GET', headers: { Accept: 'application/json' }, credentials: 'omit', signal: controller.signal });
85
+ }
86
+ catch (error) {
87
+ throw new RecursivError(0, { type: 'api_error', code: controller.signal.aborted ? 'timeout' : 'network_error',
88
+ message: controller.signal.aborted ? 'The visitor boost slots did not answer in time.' : `The visitor boost slots could not be reached (${error instanceof Error ? error.message : String(error)}).` });
89
+ }
90
+ let body = null;
91
+ try {
92
+ body = await response.json();
93
+ }
94
+ catch {
95
+ body = null;
96
+ }
97
+ const data = body?.result?.data;
98
+ if (response.ok && data && typeof data === 'object')
99
+ return data;
100
+ const error = body?.error && typeof body.error === 'object' ? body.error : undefined;
101
+ const said = [error?.json?.message, error?.message].find((value) => typeof value === 'string' && value.length > 0);
102
+ const code = typeof error?.data?.code === 'string' ? error.data.code.toLowerCase() : 'unknown_error';
103
+ const failure = { type: response.status === 404 ? 'not_found_error' : 'api_error', code, message: said ?? `HTTP ${response.status}` };
104
+ throw response.status === 404 ? new NotFoundError(failure) : new RecursivError(response.ok ? 502 : response.status, failure);
105
+ }
106
+ finally {
107
+ clearTimeout(timer);
108
+ }
109
+ }
110
+ /** Results for one of your campaigns: views, clicks, click rate, spend and a daily series. */
111
+ stats(campaignId) {
112
+ return this.client.get(`/boosts/${encodeURIComponent(campaignId)}/stats`);
113
+ }
114
+ /**
115
+ * Turn whole tokens into Boost balance at the platform's price (`balance().meta.token_topup`).
116
+ * `request_id` (8 to 100 of A-Z a-z 0-9 - _) makes a retry safe: the same id debits once.
117
+ * Not retried automatically.
118
+ */
119
+ topupWithTokens(input) {
120
+ return this.client.post('/boosts/topup/tokens', input, { retry: false });
121
+ }
42
122
  /** Report an impression or click on a boosted feed entry. Idempotent per viewer per UTC day. */
43
123
  recordEvent(campaignId, kind) {
44
124
  return this.client.post(`/boosts/${encodeURIComponent(campaignId)}/events`, { kind });
@@ -43,6 +43,10 @@ export interface DeploymentReleaseInput {
43
43
  run_id: number;
44
44
  attempt: number;
45
45
  }
46
+ export interface DeploymentReleaseReservationInput extends DeploymentReleaseInput {
47
+ /** Optional initial-run GitHub OIDC proof. Requires compatible server support; never log it or retry a refused proof. */
48
+ coordinator_identity_token?: string;
49
+ }
46
50
  export interface DeploymentRelease {
47
51
  deployment_id: string;
48
52
  status: string;
@@ -183,7 +187,7 @@ export declare class DeploymentsResource {
183
187
  private client;
184
188
  constructor(client: HttpClient);
185
189
  /** Atomically block competing production deployments before promoting the API. Retry explicitly with the SAME capability. */
186
- reserveRelease(input: DeploymentReleaseInput): Promise<SingleResponse<DeploymentRelease>>;
190
+ reserveRelease(input: DeploymentReleaseReservationInput): Promise<SingleResponse<DeploymentRelease>>;
187
191
  getRelease(deploymentId: string, projectId: string): Promise<SingleResponse<DeploymentRelease>>;
188
192
  /** Mark immediately before API mutation, then only after the existing API health gates pass. */
189
193
  advanceRelease(deploymentId: string, input: DeploymentReleaseOperation & {
@@ -175,8 +175,11 @@ export interface CategorySurfaceSection<T> {
175
175
  ms: number;
176
176
  reason?: string;
177
177
  }
178
- /** A radar cluster themed into the category, with up to six members (headline or excerpt and link only). */
179
- export type CategoryStory = Pick<SocialRadar['clusters'][number], 'id' | 'label' | 'kind' | 'theme' | 'sources' | 'corroboration' | 'laneMix' | 'outlets' | 'velocity' | 'media' | 'freshness' | 'score' | 'members'>;
178
+ /**
179
+ * A radar cluster themed into the category, with up to six members (headline or excerpt and link only). `political`
180
+ * (0.7.165) is the radar's own mark, what Hide politics reads; absent from an older server or a copy cached before it.
181
+ */
182
+ export type CategoryStory = Pick<SocialRadar['clusters'][number], 'id' | 'label' | 'kind' | 'theme' | 'political' | 'sources' | 'corroboration' | 'laneMix' | 'outlets' | 'velocity' | 'media' | 'freshness' | 'score' | 'members'>;
180
183
  export interface CategoryClip {
181
184
  id: string;
182
185
  title: string | null;