@recursiv/sdk 0.7.69 → 0.7.70
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 +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/realtime.d.ts +12 -3
- package/dist/realtime.js +10 -3
- package/dist/resources/chat.d.ts +4 -1
- package/dist/resources/chat.js +4 -1
- package/dist/resources/e2ee.d.ts +41 -0
- package/dist/resources/e2ee.js +33 -0
- package/dist/types.d.ts +60 -5
- package/llm.md +3 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -75,7 +75,7 @@ for await (const chunk of r.agents.chatStream(agent.id, { message: 'Hello!' }))
|
|
|
75
75
|
| Resource | Key methods |
|
|
76
76
|
|----------|------------|
|
|
77
77
|
| `r.agents` | `create`, `update`, `chat`, `chatStream`, `retryChatStream`, `grantProjectAccess`, `conversations` |
|
|
78
|
-
| `r.e2ee` | `status`, `registerDevice`, `devices`, `revokeDevice` (`{ cascade }` to also revoke devices approved through it; 409 `has_dependents` otherwise), `requestResetCode` and `resetAccount` (reset encrypted chat with an emailed code where `status().accountReset`), `publishKeyPackages`, `keyPackageStatus`, `claimKeyPackage`, `appendEvent`, `events`, `acknowledge`, `conversations`; device linking by scan: `createLink`, `link`, `scannedLink`, `approveLink`, `completeLink`, `linkProgress`, `cancelLink` (flag-gated E2EE v2 transport; its doorbell is `r.realtime.onE2eeEvent`, which carries a small message's stored ciphertext to the owner's own sockets; link and device changes arrive on `r.realtime.onE2eeLink` and `onE2eeDevices`) |
|
|
78
|
+
| `r.e2ee` | `status`, `registerDevice`, `devices`, `revokeDevice` (`{ cascade }` to also revoke devices approved through it; 409 `has_dependents` otherwise), `requestResetCode` and `resetAccount` (reset encrypted chat with an emailed code where `status().accountReset`), `publishKeyPackages`, `keyPackageStatus`, `claimKeyPackage`, `appendEvent`, `events`, `acknowledge`, `conversations`, `requestStart` and `cancelStartRequest` (ask the other person to encrypt a chat when they have no device yet: a message with `metadata.kind` `e2ee_start_request`; the live ones are `ConversationDetail.encryption_requests`); device linking by scan: `createLink`, `link`, `scannedLink`, `approveLink`, `completeLink`, `linkProgress`, `cancelLink` (flag-gated E2EE v2 transport; its doorbell is `r.realtime.onE2eeEvent`, which carries a small message's stored ciphertext to the owner's own sockets; link and device changes arrive on `r.realtime.onE2eeLink` and `onE2eeDevices`) |
|
|
79
79
|
| `r.brain` | `sendMessage` |
|
|
80
80
|
| `r.commands` | `execute`, `gatePrompt` |
|
|
81
81
|
| `r.projectBrain` | `tasks`, `decisions`, `milestones`, `completeTask`, `usage` |
|
package/dist/index.d.ts
CHANGED
|
@@ -158,7 +158,7 @@ export { UsersResource } from './resources/users.js';
|
|
|
158
158
|
export { CommunitiesResource } from './resources/communities.js';
|
|
159
159
|
export { ChatResource } from './resources/chat.js';
|
|
160
160
|
export { E2eeResource } from './resources/e2ee.js';
|
|
161
|
-
export type { E2eeAccountReset, E2eeDevice, E2eeDevicesRealtimeEvent, E2eeEnvelope, E2eeEvent, E2eeHasDependentsDetails, E2eeKeyPackageStatus, E2eeLink, E2eeLinkCancelledBy, E2eeLinkProgress, E2eeLinkRealtimeEvent, E2eeLinkState, E2eeObjectDownload, E2eeObjectUpload, E2eeRealtimeEvent, E2eeResetCodeSent, E2eeResetErrorCode, E2eeRevokeResponse, E2eeStatus, E2eeStatusReason, } from './resources/e2ee.js';
|
|
161
|
+
export type { E2eeAccountReset, E2eeDevice, E2eeDevicesRealtimeEvent, E2eeEnvelope, E2eeEvent, E2eeHasDependentsDetails, E2eeKeyPackageStatus, E2eeLink, E2eeLinkCancelledBy, E2eeLinkProgress, E2eeLinkRealtimeEvent, E2eeLinkState, E2eeObjectDownload, E2eeObjectUpload, E2eeRealtimeEvent, E2eeResetCodeSent, E2eeResetErrorCode, E2eeRevokeResponse, E2eeStartRequest, E2eeStatus, E2eeStatusReason, } from './resources/e2ee.js';
|
|
162
162
|
export { ProjectsResource } from './resources/projects.js';
|
|
163
163
|
export { AgentsResource, SUPPORTED_MODELS } from './resources/agents.js';
|
|
164
164
|
export { EvidenceResource } from './resources/evidence.js';
|
package/dist/realtime.d.ts
CHANGED
|
@@ -14,6 +14,8 @@ export interface ChatMessageEvent {
|
|
|
14
14
|
isAi?: boolean;
|
|
15
15
|
};
|
|
16
16
|
createdAt: string;
|
|
17
|
+
/** The message's metadata as stored; `kind: 'e2ee_start_request'` is an encryption request (MessageMetadata). */
|
|
18
|
+
metadata?: Record<string, unknown>;
|
|
17
19
|
}
|
|
18
20
|
export interface ChatMessageEditEvent {
|
|
19
21
|
id: string;
|
|
@@ -163,11 +165,18 @@ export declare class RealtimeClient {
|
|
|
163
165
|
onAgentThinking(callback: (event: AgentThinkingEvent) => void): (() => void) | undefined;
|
|
164
166
|
/**
|
|
165
167
|
* A private group's people changed (0.7.59): `{ conversationId, change, userIds, actorId }`,
|
|
166
|
-
* change `added`, `removed`, `left` or `invites_cancelled
|
|
167
|
-
* and to the people it is about
|
|
168
|
+
* change `added`, `accepted`, `removed`, `left` or `invites_cancelled`, sent to everyone in the group
|
|
169
|
+
* and to the people it is about; or `device` (a member's device can be added to the encrypted group
|
|
170
|
+
* now), sent to the group's admin only, on a connection that may read encrypted chat for the group.
|
|
171
|
+
* Read the conversation again; in an encrypted group, check its keys.
|
|
168
172
|
*/
|
|
169
173
|
onChatMembers(callback: (event: ChatMembersEvent) => void): () => void;
|
|
170
|
-
/**
|
|
174
|
+
/**
|
|
175
|
+
* A conversation changed: a private group was renamed (0.7.59,
|
|
176
|
+
* `{ conversationId, change: 'renamed', name, actorId }`), or someone
|
|
177
|
+
* withdrew their request to encrypt a one-on-one chat
|
|
178
|
+
* (`encryption_request_withdrawn`, with `actorId`). Read it again.
|
|
179
|
+
*/
|
|
171
180
|
onChatConversation(callback: (event: ChatConversationEvent) => void): () => void;
|
|
172
181
|
/**
|
|
173
182
|
* The E2EE doorbell after each append to an encrypted conversation this
|
package/dist/realtime.js
CHANGED
|
@@ -429,13 +429,20 @@ export class RealtimeClient {
|
|
|
429
429
|
}
|
|
430
430
|
/**
|
|
431
431
|
* A private group's people changed (0.7.59): `{ conversationId, change, userIds, actorId }`,
|
|
432
|
-
* change `added`, `removed`, `left` or `invites_cancelled
|
|
433
|
-
* and to the people it is about
|
|
432
|
+
* change `added`, `accepted`, `removed`, `left` or `invites_cancelled`, sent to everyone in the group
|
|
433
|
+
* and to the people it is about; or `device` (a member's device can be added to the encrypted group
|
|
434
|
+
* now), sent to the group's admin only, on a connection that may read encrypted chat for the group.
|
|
435
|
+
* Read the conversation again; in an encrypted group, check its keys.
|
|
434
436
|
*/
|
|
435
437
|
onChatMembers(callback) {
|
|
436
438
|
return this.on('chat:members', callback);
|
|
437
439
|
}
|
|
438
|
-
/**
|
|
440
|
+
/**
|
|
441
|
+
* A conversation changed: a private group was renamed (0.7.59,
|
|
442
|
+
* `{ conversationId, change: 'renamed', name, actorId }`), or someone
|
|
443
|
+
* withdrew their request to encrypt a one-on-one chat
|
|
444
|
+
* (`encryption_request_withdrawn`, with `actorId`). Read it again.
|
|
445
|
+
*/
|
|
439
446
|
onChatConversation(callback) {
|
|
440
447
|
return this.on('chat:conversation', callback);
|
|
441
448
|
}
|
package/dist/resources/chat.d.ts
CHANGED
|
@@ -93,7 +93,10 @@ export declare class ChatResource {
|
|
|
93
93
|
* only stops listening for live events.
|
|
94
94
|
*/
|
|
95
95
|
leave(conversationId: string): Promise<SingleResponse<LeaveConversationResult>>;
|
|
96
|
-
/**
|
|
96
|
+
/**
|
|
97
|
+
* Accept a message request (`request_status: 'pending'`). Idempotent; also reverses a decline. In a
|
|
98
|
+
* private group, everyone in it gets the realtime `chat:members` event (`accepted`); a repeat sends none.
|
|
99
|
+
*/
|
|
97
100
|
acceptRequest(conversationId: string): Promise<SingleResponse<ConversationRequestResult>>;
|
|
98
101
|
/**
|
|
99
102
|
* Decline a message request. The conversation leaves the caller's lists and
|
package/dist/resources/chat.js
CHANGED
|
@@ -108,7 +108,10 @@ export class ChatResource {
|
|
|
108
108
|
leave(conversationId) {
|
|
109
109
|
return this.client.post(`/chat/conversations/${conversationId}/leave`);
|
|
110
110
|
}
|
|
111
|
-
/**
|
|
111
|
+
/**
|
|
112
|
+
* Accept a message request (`request_status: 'pending'`). Idempotent; also reverses a decline. In a
|
|
113
|
+
* private group, everyone in it gets the realtime `chat:members` event (`accepted`); a repeat sends none.
|
|
114
|
+
*/
|
|
112
115
|
acceptRequest(conversationId) {
|
|
113
116
|
return this.client.post(`/chat/conversations/${conversationId}/accept`);
|
|
114
117
|
}
|
package/dist/resources/e2ee.d.ts
CHANGED
|
@@ -232,6 +232,16 @@ export interface E2eeHasDependentsDetails {
|
|
|
232
232
|
dependents: string[];
|
|
233
233
|
root: boolean;
|
|
234
234
|
}
|
|
235
|
+
/**
|
|
236
|
+
* A live "Encrypt this chat" request (`e2ee.requestStart`): the message that
|
|
237
|
+
* carries it in the chat, and when it was made, by the relay's clock.
|
|
238
|
+
*/
|
|
239
|
+
export interface E2eeStartRequest {
|
|
240
|
+
message_id: string;
|
|
241
|
+
requested_at: string;
|
|
242
|
+
/** False when this request was live already, so nothing was sent. Absent from older relays. */
|
|
243
|
+
created?: boolean;
|
|
244
|
+
}
|
|
235
245
|
/** `revokeDevice`'s answer: the device, and every device the call revoked (the target first). */
|
|
236
246
|
export interface E2eeRevokeResponse extends SingleResponse<E2eeDevice> {
|
|
237
247
|
/** Absent on relays before 0.7.58. Empty when the device was already revoked and nothing else was. */
|
|
@@ -467,6 +477,37 @@ export declare class E2eeResource {
|
|
|
467
477
|
device_id?: string;
|
|
468
478
|
reason?: 'cancelled' | 'declined';
|
|
469
479
|
}): Promise<SingleResponse<E2eeLink>>;
|
|
480
|
+
/**
|
|
481
|
+
* "Encrypt this chat" when the other person has no encrypted-chat device
|
|
482
|
+
* yet. The relay writes the request into the chat as a message from this
|
|
483
|
+
* account, with `metadata: { kind: 'e2ee_start_request', v: 1 }` (see
|
|
484
|
+
* `Message.metadata`), so it reaches the other person as a message does:
|
|
485
|
+
* their open chat, chat list and unread count, and one push, "<name> wants
|
|
486
|
+
* to encrypt your chat" (at most one a day per chat). Their device then
|
|
487
|
+
* starts the encryption as usual, with this account's devices. So this
|
|
488
|
+
* account needs an active device first (`no_root`, 409, otherwise).
|
|
489
|
+
*
|
|
490
|
+
* Answers this account's live request, `{ message_id, requested_at,
|
|
491
|
+
* created }`; asking again changes nothing and sends nothing (`created`
|
|
492
|
+
* false). The other person's app finds live requests in
|
|
493
|
+
* `chat.conversation()`'s `encryption_requests`. Refused where encryption
|
|
494
|
+
* cannot start: `conversation_not_eligible` (409), `blocked` (403),
|
|
495
|
+
* `forbidden` (403: not a member this key reaches); `conflict` (409) when
|
|
496
|
+
* the chat is already encrypted; `request_limit` (409, with
|
|
497
|
+
* `details.retryAfterSeconds`) after three requests in a day in this chat.
|
|
498
|
+
* Relays without it answer 404.
|
|
499
|
+
*/
|
|
500
|
+
requestStart(conversationId: string): Promise<SingleResponse<E2eeStartRequest>>;
|
|
501
|
+
/**
|
|
502
|
+
* Withdraw this account's request to encrypt the chat: `{ cancelled }`,
|
|
503
|
+
* false when none of its requests was live (answered by a start, or
|
|
504
|
+
* withdrawn already). The request leaves the chat's history; open chats get
|
|
505
|
+
* `chat_message_delete`, and both people's sockets `chat:conversation`
|
|
506
|
+
* `encryption_request_withdrawn`. Relays without it answer 404.
|
|
507
|
+
*/
|
|
508
|
+
cancelStartRequest(conversationId: string): Promise<SingleResponse<{
|
|
509
|
+
cancelled: boolean;
|
|
510
|
+
}>>;
|
|
470
511
|
/**
|
|
471
512
|
* The ids of the encrypted conversations this account belongs to, in this
|
|
472
513
|
* key's scope, ordered by id: pass `next` as `after` for the next page
|
package/dist/resources/e2ee.js
CHANGED
|
@@ -178,6 +178,39 @@ export class E2eeResource {
|
|
|
178
178
|
cancelLink(id, input) {
|
|
179
179
|
return this.client.post(`/e2ee/links/${encodeURIComponent(id)}/cancel`, input ?? {});
|
|
180
180
|
}
|
|
181
|
+
/**
|
|
182
|
+
* "Encrypt this chat" when the other person has no encrypted-chat device
|
|
183
|
+
* yet. The relay writes the request into the chat as a message from this
|
|
184
|
+
* account, with `metadata: { kind: 'e2ee_start_request', v: 1 }` (see
|
|
185
|
+
* `Message.metadata`), so it reaches the other person as a message does:
|
|
186
|
+
* their open chat, chat list and unread count, and one push, "<name> wants
|
|
187
|
+
* to encrypt your chat" (at most one a day per chat). Their device then
|
|
188
|
+
* starts the encryption as usual, with this account's devices. So this
|
|
189
|
+
* account needs an active device first (`no_root`, 409, otherwise).
|
|
190
|
+
*
|
|
191
|
+
* Answers this account's live request, `{ message_id, requested_at,
|
|
192
|
+
* created }`; asking again changes nothing and sends nothing (`created`
|
|
193
|
+
* false). The other person's app finds live requests in
|
|
194
|
+
* `chat.conversation()`'s `encryption_requests`. Refused where encryption
|
|
195
|
+
* cannot start: `conversation_not_eligible` (409), `blocked` (403),
|
|
196
|
+
* `forbidden` (403: not a member this key reaches); `conflict` (409) when
|
|
197
|
+
* the chat is already encrypted; `request_limit` (409, with
|
|
198
|
+
* `details.retryAfterSeconds`) after three requests in a day in this chat.
|
|
199
|
+
* Relays without it answer 404.
|
|
200
|
+
*/
|
|
201
|
+
requestStart(conversationId) {
|
|
202
|
+
return this.client.put(`/e2ee/conversations/${encodeURIComponent(conversationId)}/start-request`, {});
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Withdraw this account's request to encrypt the chat: `{ cancelled }`,
|
|
206
|
+
* false when none of its requests was live (answered by a start, or
|
|
207
|
+
* withdrawn already). The request leaves the chat's history; open chats get
|
|
208
|
+
* `chat_message_delete`, and both people's sockets `chat:conversation`
|
|
209
|
+
* `encryption_request_withdrawn`. Relays without it answer 404.
|
|
210
|
+
*/
|
|
211
|
+
cancelStartRequest(conversationId) {
|
|
212
|
+
return this.client.delete(`/e2ee/conversations/${encodeURIComponent(conversationId)}/start-request`);
|
|
213
|
+
}
|
|
181
214
|
/**
|
|
182
215
|
* The ids of the encrypted conversations this account belongs to, in this
|
|
183
216
|
* key's scope, ordered by id: pass `next` as `after` for the next page
|
package/dist/types.d.ts
CHANGED
|
@@ -760,6 +760,25 @@ export interface ConversationDetail {
|
|
|
760
760
|
* older APIs.
|
|
761
761
|
*/
|
|
762
762
|
last_activity_at?: string;
|
|
763
|
+
/**
|
|
764
|
+
* The live "Encrypt this chat" requests (`e2ee.requestStart`) that can
|
|
765
|
+
* still be answered, oldest first, at most one per person, however far
|
|
766
|
+
* back in the history their messages are. Served by `conversation()` on a
|
|
767
|
+
* plaintext one-on-one chat only: `[]` when there is none (or the chat is
|
|
768
|
+
* not accepted by both, or a block stands). A request is listed only while
|
|
769
|
+
* both people are inside the encryption pilot and its sender has an active
|
|
770
|
+
* device. Absent on other chats and from older APIs.
|
|
771
|
+
*/
|
|
772
|
+
encryption_requests?: ConversationEncryptionRequest[];
|
|
773
|
+
}
|
|
774
|
+
/** One entry of `ConversationDetail.encryption_requests`. */
|
|
775
|
+
export interface ConversationEncryptionRequest {
|
|
776
|
+
/** Who asked. The other person answers by starting encryption as usual, from their own device. */
|
|
777
|
+
sender_id: string;
|
|
778
|
+
/** The request's message (`metadata.kind` `e2ee_start_request`). */
|
|
779
|
+
message_id: string;
|
|
780
|
+
/** When it was asked, by the relay's clock. */
|
|
781
|
+
requested_at: string;
|
|
763
782
|
}
|
|
764
783
|
export type ChatMemberRole = 'admin' | 'member';
|
|
765
784
|
/**
|
|
@@ -807,21 +826,39 @@ export interface UpdateGroupResult {
|
|
|
807
826
|
* the conversation again (and, in an encrypted group, check its keys).
|
|
808
827
|
* `actorId` is the admin for `added` and `removed`, and the leaver for `left`
|
|
809
828
|
* and `invites_cancelled` (the invites an encrypted group's admin sent, which
|
|
810
|
-
* went with them).
|
|
829
|
+
* went with them). `accepted`: the person in `userIds` (and `actorId`)
|
|
830
|
+
* accepted their invite; everyone in the group is told. `device`: one of that
|
|
831
|
+
* person's devices can be added to the encrypted group now (it has key
|
|
832
|
+
* packages after it had none); only the group's admin is told, since only the
|
|
833
|
+
* admin's device adds someone it invited, and only on a connection that may
|
|
834
|
+
* read encrypted chat for the group (a session, or a key for the group's app;
|
|
835
|
+
* never an agent's key). Read `change` defensively: an older API never sends
|
|
836
|
+
* the last two, and a newer one may add more.
|
|
811
837
|
*/
|
|
812
838
|
export interface ChatMembersEvent {
|
|
813
839
|
conversationId: string;
|
|
814
|
-
change: 'added' | 'removed' | 'left' | 'invites_cancelled';
|
|
840
|
+
change: 'added' | 'accepted' | 'removed' | 'left' | 'invites_cancelled' | 'device';
|
|
815
841
|
userIds: string[];
|
|
816
842
|
actorId: string;
|
|
817
843
|
}
|
|
818
|
-
/**
|
|
819
|
-
|
|
844
|
+
/**
|
|
845
|
+
* Realtime `chat:conversation`. Ids only, except a group's new name:
|
|
846
|
+
* - `renamed`: a private group was renamed by its admin (`name`).
|
|
847
|
+
* - `encryption_request_withdrawn`: `actorId` withdrew their request to
|
|
848
|
+
* encrypt this one-on-one chat (`e2ee.cancelStartRequest`); it left the
|
|
849
|
+
* history. Read the conversation and its messages again.
|
|
850
|
+
* Treat any other `change` as "read the conversation again".
|
|
851
|
+
*/
|
|
852
|
+
export type ChatConversationEvent = {
|
|
820
853
|
conversationId: string;
|
|
821
854
|
change: 'renamed';
|
|
822
855
|
name: string;
|
|
823
856
|
actorId: string;
|
|
824
|
-
}
|
|
857
|
+
} | {
|
|
858
|
+
conversationId: string;
|
|
859
|
+
change: 'encryption_request_withdrawn';
|
|
860
|
+
actorId: string;
|
|
861
|
+
};
|
|
825
862
|
export interface MessageReaction {
|
|
826
863
|
type: string;
|
|
827
864
|
user_id: string;
|
|
@@ -834,6 +871,20 @@ export interface MessageMedia {
|
|
|
834
871
|
mime_type: string | null;
|
|
835
872
|
file_name: string | null;
|
|
836
873
|
}
|
|
874
|
+
/**
|
|
875
|
+
* What a message the relay wrote on someone's behalf is, by `kind`. Only such
|
|
876
|
+
* messages carry it.
|
|
877
|
+
* - `e2ee_start_request` (`v` 1): the sender asked to turn on end-to-end
|
|
878
|
+
* encryption for this one-on-one chat (`e2ee.requestStart`). Show it as
|
|
879
|
+
* that request, not as its `content`, which is a fallback for apps that do
|
|
880
|
+
* not know the kind. It is live while it is in the history and the chat is
|
|
881
|
+
* not encrypted; a withdrawn one leaves the history. The person who did not
|
|
882
|
+
* ask answers it by starting encryption as usual, from their own device.
|
|
883
|
+
*/
|
|
884
|
+
export interface MessageMetadata {
|
|
885
|
+
kind: 'e2ee_start_request';
|
|
886
|
+
v: number;
|
|
887
|
+
}
|
|
837
888
|
export interface Message {
|
|
838
889
|
id: string;
|
|
839
890
|
content: string;
|
|
@@ -842,6 +893,8 @@ export interface Message {
|
|
|
842
893
|
media: MessageMedia[];
|
|
843
894
|
reactions: MessageReaction[];
|
|
844
895
|
created_at: string;
|
|
896
|
+
/** Absent on a person's own message, and from older APIs: see MessageMetadata. */
|
|
897
|
+
metadata?: MessageMetadata;
|
|
845
898
|
}
|
|
846
899
|
export interface SendMessageInput {
|
|
847
900
|
conversation_id: string;
|
|
@@ -861,6 +914,8 @@ export interface ChatMessageEvent {
|
|
|
861
914
|
isAi?: boolean;
|
|
862
915
|
};
|
|
863
916
|
createdAt: string;
|
|
917
|
+
/** The message's metadata as stored; `kind: 'e2ee_start_request'` is an encryption request (MessageMetadata). */
|
|
918
|
+
metadata?: Record<string, unknown>;
|
|
864
919
|
}
|
|
865
920
|
export interface ChatMessageEditEvent {
|
|
866
921
|
id: string;
|
package/llm.md
CHANGED
|
@@ -522,6 +522,9 @@ await r.e2ee.appendEvent(conversationId, { device_id: device.id, envelope }); //
|
|
|
522
522
|
await r.e2ee.appendEvent(conversationId, { device_id: device.id, envelope: reaction, silent: true }); // no push, no move up the chat list; every member's doorbell still rings
|
|
523
523
|
const { data: page } = await r.e2ee.events(conversationId, { device_id: device.id, after_seq: 0 });
|
|
524
524
|
await r.e2ee.acknowledge(conversationId, { device_id: device.id, seq: page.headSeq });
|
|
525
|
+
// The other person has no device yet: ask them (0.7.70). The relay writes it into the chat as a message with metadata { kind: 'e2ee_start_request', v: 1 }; their start answers it.
|
|
526
|
+
await r.e2ee.requestStart(conversationId); // → { message_id, requested_at, created }; cancelStartRequest(conversationId) withdraws it
|
|
527
|
+
// The live requests, however far back: (await r.chat.conversation(conversationId)).data.encryption_requests → [{ sender_id, message_id, requested_at }]
|
|
525
528
|
// Doorbell after every append: { conversationId, seq, event? }. `event` (a small application message, as events() returns it) comes only to a socket of the owner's session or an owner key, never an agent-scoped key.
|
|
526
529
|
// The policy is required: policyForConversationType(conversation.type) from @recursiv/e2ee ('direct' or 'group').
|
|
527
530
|
r.realtime.onE2eeEvent(({ conversationId, event }) => (event ? controller.applyPushed(conversationId, event, policy) : controller.sync(conversationId, undefined, policy)));
|