@recursiv/sdk 0.7.81 → 0.7.83
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 +45 -2
- 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 +22 -0
- package/dist/assistant/index.js +22 -0
- package/dist/assistant/render.d.ts +8 -0
- package/dist/assistant/render.js +253 -0
- package/dist/assistant/subjectBundle.d.ts +30 -0
- package/dist/assistant/subjectBundle.js +518 -0
- package/dist/assistant/types.d.ts +353 -0
- package/dist/assistant/types.js +11 -0
- package/dist/assistant-surfaces.d.ts +42 -0
- package/dist/assistant-surfaces.js +113 -0
- package/dist/index.d.ts +8 -4
- package/dist/index.js +2 -1
- 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/chat.d.ts +73 -1
- package/dist/resources/chat.js +83 -1
- package/dist/resources/e2ee.d.ts +56 -0
- package/dist/resources/e2ee.js +21 -0
- package/dist/resources/link-preview.d.ts +42 -1
- package/dist/resources/link-preview.js +36 -2
- package/dist/resources/network.d.ts +12 -0
- package/dist/resources/project-brain.d.ts +8 -0
- package/dist/resources/reports.d.ts +24 -0
- package/dist/resources/reports.js +12 -0
- package/dist/resources/users.d.ts +11 -2
- package/dist/resources/users.js +1 -1
- package/dist/types.d.ts +34 -0
- package/llm.md +35 -3
- package/package.json +6 -1
package/llm.md
CHANGED
|
@@ -214,6 +214,27 @@ Methods: `tasks`, `stats`, `decisions`, `milestones`, `agents`, `teamActivity`,
|
|
|
214
214
|
|
|
215
215
|
---
|
|
216
216
|
|
|
217
|
+
## Assistant subjects — `@recursiv/sdk/assistant` (unreleased)
|
|
218
|
+
|
|
219
|
+
Pure, no network: the same input on a server, a phone or the web. Build the bundle from data you already hold, then render it; never paste post text into a prompt by hand.
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
import { buildSubjectBundle, renderBundleForModel } from '@recursiv/sdk/assistant'
|
|
223
|
+
const { bundle, refused, excluded } = buildSubjectBundle({ mode: 'standard', asker: { id, blocked_ids, muted_ids }, post, parent, quoted, replies }) // server: also blocked_by_ids
|
|
224
|
+
if (bundle) {
|
|
225
|
+
const { system, user, references } = renderBundleForModel(bundle, 'summarise', { boundary }) // boundary: fresh random, 16+ chars, per call
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
- `bundle` is null when the post itself may not be read: author asks off (`ai_ask_allowed` `'off'`, or `'sealed_only'` in Standard), the asker blocked or muted the author or the author blocked the asker, known-minor author (unless asker is author), child-safety hold, moderator suppression, `assist_readable: 'none'`. Make no model call.
|
|
230
|
+
- `refused` and `excluded` (`{ role, reason, count }[]`, never ids) are safe to show the asker: blocks, minors, holds and suppression all read `'unavailable'`. `audit` has the detail: server log only, never to a client.
|
|
231
|
+
- `known_minor`, `child_safety_hold`, `assistant_suppressed`, media `held`, `blocked_by_ids`: server-side inputs, never sent to another member's client; a client passes the server's per-viewer `assist_readable` instead. Flags fail closed (anything but false/null/absent is set); an unreadable id list throws.
|
|
232
|
+
- Replies, parent and quote are left out by the same rules. Media go as alt text and stored caption only; picture and media addresses never reach `references`. Hidden text is stripped; top 20 replies; per-field and total caps (lower them with `limits`, never raise).
|
|
233
|
+
- Intents: `explain`, `fact_context` (post, parent, quote), `summarise` (plus replies), `translate` (needs `language`, a BCP 47 tag), `describe` (text only). Output filters allow only `references` ids, handles and URLs, as plain links (never render an image the model wrote). Render only bundles you built from your own reads.
|
|
234
|
+
- Fixed cards, before any model call: `matchFixedCard(memberWords, { country, locale })` → `{ kind: 'crisis' | 'election', card } | null`. Crisis (self-harm, suicide risk, immediate danger) always shows the card first, then the turn may answer; election logistics only (never candidates, results, fraud claims, votes on a post or poll), and `fixedCardAnswer('election', Date.now())` is `'card_only'` until 2026-11-04T12:00Z. English and Spanish; every number and URL is sourced data (`CRISIS_LINES`, `ELECTION_SITES`), never generated. Pass the member's words, not the post they ask about.
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
217
238
|
## Evidence — Measure agents (evals, accuracy, cost)
|
|
218
239
|
|
|
219
240
|
### r.evidence — Author evals, run simulations, read graded results
|
|
@@ -504,7 +525,9 @@ const { data: group } = await r.chat.createGroup({ name: 'Team', member_ids: ['u
|
|
|
504
525
|
const { data: msgs } = await r.chat.messages(dm.id, { limit: 50 })
|
|
505
526
|
```
|
|
506
527
|
|
|
507
|
-
Methods: `conversations`, `communityConversation`, `conversation`, `messages`, `send`, `dm`, `createGroup`, `addMembers`, `removeMember`, `updateGroup`, `deleteConversation`, `leave`, `acceptRequest`, `declineRequest`, `reactToMessage`, `editMessage`, `deleteMessage`, `markAsRead`, `unreadCount`
|
|
528
|
+
Methods: `conversations`, `communityConversation`, `conversation`, `messages`, `send`, `dm`, `createGroup`, `addMembers`, `removeMember`, `updateGroup`, `deleteConversation`, `leave`, `acceptRequest`, `declineRequest`, `reactToMessage`, `editMessage`, `deleteMessage`, `markAsRead`, `unreadCount`, `attachmentUploadUrl`, `uploadAttachment`, `signAttachments`
|
|
529
|
+
|
|
530
|
+
Chat photos and voice notes (0.7.82): `const { ref } = await r.chat.uploadAttachment(dm.id, { blob })`, then send `ref`. With the server's private chat storage on, `ref` is `chat-attachment:<key>`: render it with `(await r.chat.signAttachments(dm.id, chatAttachmentKeysIn(message.content))).data.attachments` (URLs live about five minutes, `private, no-store`; `url: null` for a key the caller may not open). `editMessage` changes a message's words only: an edit that drops, adds or swaps a `chat-attachment:` reference gets 400 `attachment_refs_changed` (delete the message instead). Otherwise `ref` is a public URL, as before. A group's chat tied to a community always gets a public URL. Staff reviewing a member's report of a message open its private attachments with `r.reports.signAttachments(reportId)` (a live admin's own key; an AI account or agent key gets 403 `human_required`; only a report filed by a person in that chat, other than the sender and the caller, opens anything: 403 `own_report` or `reporter_not_a_recipient` otherwise).
|
|
508
531
|
|
|
509
532
|
Scroll back with `messages(id, { cursor: page.meta.next_cursor })`, not a growing `offset`. Conversations carry `can_delete` / `can_leave`; check them before offering Delete or Leave.
|
|
510
533
|
|
|
@@ -562,6 +585,14 @@ const { data: reset } = await r.e2ee.resetAccount({ email_code, device_name, rea
|
|
|
562
585
|
// → { reset, resetAt, retiredRoot, revokedDeviceIds, device (the new main device), signedOut: { sessions, keys, connectedApps } }; a retry after a lost answer gets the same result
|
|
563
586
|
```
|
|
564
587
|
|
|
588
|
+
A contact's reset (D2 R2, 0.7.83; where `accountReset` is on). Each member of an encrypted conversation the account shares (a direct chat, or a group both accepted) gets `e2ee:peer` `{ userId, reason: 'root_changed', conversationId }`, once per shared conversation, and each direct-chat contact one push (at most one a day per person who reset; it does not name them): "A contact reset encrypted chat. Open Minds to keep chatting." `peerChanges({ since })` lists the accounts whose main device a reset replaced since then (default and floor 7 days, oldest first, at most 200; a relay without it answers 404: treat it as none). An engine that can accept a contact's new main device sends `trust_change: true` with its messages; in a direct chat with someone who reset, such a message gets 409 `peer_not_joined` (`details.userId`) until a device of this account added their new device, and 409 `stale_epoch` under a key generation before the chat's last key update. Messages without it are accepted as before.
|
|
589
|
+
|
|
590
|
+
```ts
|
|
591
|
+
r.realtime.onE2eePeer(({ userId, conversationId }) => {}); // look at userId's devices again
|
|
592
|
+
const { data: changes } = await r.e2ee.peerChanges({ since }); // [{ userId, at }] after time in the background
|
|
593
|
+
await r.e2ee.appendEvent(conversationId, { device_id, envelope, trust_change: true }); // ConflictError 'peer_not_joined' | 'stale_epoch'
|
|
594
|
+
```
|
|
595
|
+
|
|
565
596
|
### r.realtime — WebSocket Chat Client
|
|
566
597
|
|
|
567
598
|
Provides persistent real-time streaming and chat presence, useful for building frontends (e.g. React Native, Web).
|
|
@@ -595,7 +626,7 @@ unsub()
|
|
|
595
626
|
const off = r.realtime.on('notification', (n) => refreshBadge(n))
|
|
596
627
|
```
|
|
597
628
|
|
|
598
|
-
Events: `onMessage`, `onMessageEdit`, `onMessageDelete`, `onTyping`, `onAgentThinking`, `onE2eeEvent`, `onE2eeLink`, `onE2eeDevices`, `on(event, fn)`
|
|
629
|
+
Events: `onMessage`, `onMessageEdit`, `onMessageDelete`, `onTyping`, `onAgentThinking`, `onE2eeEvent`, `onE2eeLink`, `onE2eeDevices`, `onE2eePeer`, `on(event, fn)`
|
|
599
630
|
|
|
600
631
|
Reconnects on its own (transport drops, token expiry, refused handshakes) with jittered backoff and a fresh single-use token per attempt; listeners and joined rooms carry over, so no app-side reconnect shim is needed. It stops only on `disconnect()` or a 401/403 from the token source. `connect()` is safe to call repeatedly.
|
|
601
632
|
|
|
@@ -792,7 +823,7 @@ Methods: `list`, `get`, `create`, `delete`
|
|
|
792
823
|
|
|
793
824
|
## Platform Resources
|
|
794
825
|
|
|
795
|
-
- `r.settings` — User preferences, sessions, password changes, account deletion. `revokeOtherSessions({ keep_session_token? })` signs out every other device and keeps this key (and the proven session); `revokeAllSessions()` also revokes this key. Accounts with no password confirm `requestEmailChange({ new_email, email_code })` with a code from `requestConfirmationCode('email_change')`. With a project-bound key, `getPreferences()` includes app-scoped `content`; persist changes with `updateContent({ show_nsfw?, autoplay_video?, ai_enabled?, default_feed? })
|
|
826
|
+
- `r.settings` — User preferences, sessions, password changes, account deletion. `revokeOtherSessions({ keep_session_token? })` signs out every other device and keeps this key (and the proven session); `revokeAllSessions()` also revokes this key. Accounts with no password confirm `requestEmailChange({ new_email, email_code })` with a code from `requestConfirmationCode('email_change')`. With a project-bound key, `getPreferences()` includes app-scoped `content`; persist changes with `updateContent({ show_nsfw?, autoplay_video?, ai_enabled?, default_feed? })`; `content.default_feed_chosen` is true once the member set `default_feed` themselves (absent on older APIs). Notifications (0.7.72): `getPreferences()` returns `notifications.channels`, each kind (`post_mention`, `post_reply`, `post_repost`, `post_reaction`, `follow`, `message`, and the operator kinds) with `{ inApp, push, email }`; `updateNotifications({ channels: { post_reaction: { push: false } } })` changes that kind only, while `in_app`/`push`/`email` stay master switches over every kind. `disableAccount({ password })` (0.7.71) deactivates the account (hidden, nothing deleted; signing back in restores it), asked once; an API that does not serve deactivation answers 404 `NotFoundError`, so keep the control hidden until yours does.
|
|
796
827
|
- `r.signals` (0.7.71) — The member's engagement signals for their own ranking (For You), under their app-bound key (`agents:write`): `batch(events, { keepalive?, retry? })` sends up to `SIGNAL_BATCH_MAX` (100) `{ event_type, post_id?, dwell_ms?, metadata? }` to POST /signals/batch; `record(event)` sends one to POST /signals/post (for an API before the batch route, which answers that one 404). `event_type`: `view`, `click`, `dwell`, `react_up`, `react_down`, `save`, `hide`, `share`, `mute_source`, `open_chat`, `ask_agent`, `more_like`, `unhide`. Best-effort: a signal the server cannot store answers 200 with `recorded` 0 (or false) and a `reason` (`no_project_scope`, `project_db_unavailable`). `keepalive: true` is for a flush as the page is hidden or unloads: the request is on the wire before the call returns and is asked once.
|
|
797
828
|
|
|
798
829
|
```typescript
|
|
@@ -853,6 +884,7 @@ await r.appSubscriptions.changeTier({ tier: 'pro', proration_date: quote.prorati
|
|
|
853
884
|
- `r.dispatcher` — Task queue: `tasks()`, `create()`, `claimNext()`, `complete()`, `release()`, `stats()`, `signals()`, `outcomes()`, `staleItems()`, `stuckItems()`, `discoveries()`
|
|
854
885
|
- `r.dispatcher` (webhooks): `createWebhook()`, `listWebhooks()`, `updateWebhook()`, `deleteWebhook()`, `testWebhook()`, `webhookDeliveries()`, `cleanupWebhookLogs()`
|
|
855
886
|
- `r.goals` — Org goals that dispatcher tasks roll up into: `list()`, `get()`, `create()`, `update()`, `archive()`. Tasks link via `goal_id` on `r.dispatcher.create()/update()`; `tasks({ goal_id })` filters (use `'none'` for goal-less tasks).
|
|
887
|
+
- `r.linkPreview` — Open Graph card for a public link: `get(url)` answers `{ data: LinkPreview | null }` (null: no preview, show the plain link); keyless clients may call it. For a link in a chat message pass where it came from, `get(url, { messageId })` (or `{ conversationId }` before the message has an id), 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 when the caller wrote the same address in that conversation (a message, or the card link of a Minds post they shared there), else `{ data: null, refused: 'ai_link' }` with nothing fetched; 401 without a key, 404 for a message or conversation the caller cannot read. A `source` with no id rejects with `ValidationError` and sends nothing. Never pass `source` in an encrypted conversation: the server cannot read it, so every link would be refused; the app's own rule decides there.
|
|
856
888
|
|
|
857
889
|
```typescript
|
|
858
890
|
// Create a goal and group tasks under it (personal keys: pass organization_id)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@recursiv/sdk",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.83",
|
|
4
4
|
"description": "TypeScript SDK for the Recursiv API",
|
|
5
5
|
"license": "FSL-1.1-ALv2",
|
|
6
6
|
"type": "module",
|
|
@@ -13,6 +13,11 @@
|
|
|
13
13
|
"import": "./dist/index.js",
|
|
14
14
|
"default": "./dist/index.js"
|
|
15
15
|
},
|
|
16
|
+
"./assistant": {
|
|
17
|
+
"types": "./dist/assistant/index.d.ts",
|
|
18
|
+
"import": "./dist/assistant/index.js",
|
|
19
|
+
"default": "./dist/assistant/index.js"
|
|
20
|
+
},
|
|
16
21
|
"./react": {
|
|
17
22
|
"types": "./dist/react/index.d.ts",
|
|
18
23
|
"import": "./dist/react/index.js",
|