@newbase-clawchat/openclaw-clawchat 2026.5.4 → 2026.5.12-13
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/INSTALL.md +64 -0
- package/README.md +121 -19
- package/dist/index.js +10 -19
- package/dist/setup-entry.js +3 -0
- package/dist/src/api-client.js +78 -10
- package/dist/src/api-types.test-d.js +10 -0
- package/dist/src/channel.js +25 -156
- package/dist/src/channel.setup.js +120 -0
- package/dist/src/client.js +37 -41
- package/dist/src/config.js +75 -17
- package/dist/src/inbound.js +79 -61
- package/dist/src/login.runtime.js +84 -19
- package/dist/src/media-runtime.js +8 -8
- package/dist/src/message-mapper.js +1 -1
- package/dist/src/mock-transport.js +31 -0
- package/dist/src/outbound.js +410 -26
- package/dist/src/protocol-types.js +63 -0
- package/dist/src/protocol-types.typecheck.js +1 -0
- package/dist/src/protocol.js +2 -7
- package/dist/src/reply-dispatcher.js +157 -54
- package/dist/src/runtime.js +795 -119
- package/dist/src/storage.js +689 -0
- package/dist/src/tools-schema.js +98 -16
- package/dist/src/tools.js +422 -135
- package/dist/src/ws-alignment.js +178 -0
- package/dist/src/ws-client.js +588 -0
- package/dist/src/ws-log.js +19 -0
- package/index.ts +10 -22
- package/openclaw.plugin.json +37 -2
- package/package.json +17 -4
- package/setup-entry.ts +4 -0
- package/skills/clawchat/SKILL.md +88 -0
- package/src/api-client.test.ts +274 -14
- package/src/api-client.ts +138 -23
- package/src/api-types.test-d.ts +12 -0
- package/src/api-types.ts +90 -4
- package/src/buffered-stream.test.ts +14 -12
- package/src/buffered-stream.ts +1 -1
- package/src/channel.outbound.test.ts +269 -60
- package/src/channel.setup.ts +146 -0
- package/src/channel.test.ts +130 -24
- package/src/channel.ts +30 -186
- package/src/client.test.ts +197 -11
- package/src/client.ts +50 -57
- package/src/config.test.ts +108 -6
- package/src/config.ts +95 -24
- package/src/inbound.test.ts +288 -37
- package/src/inbound.ts +96 -84
- package/src/login.runtime.test.ts +347 -13
- package/src/login.runtime.ts +105 -23
- package/src/manifest.test.ts +146 -74
- package/src/media-runtime.test.ts +57 -2
- package/src/media-runtime.ts +26 -17
- package/src/message-mapper.test.ts +2 -2
- package/src/message-mapper.ts +2 -2
- package/src/mock-transport.test.ts +35 -0
- package/src/mock-transport.ts +38 -0
- package/src/outbound.test.ts +694 -73
- package/src/outbound.ts +484 -31
- package/src/plugin-entry.test.ts +1 -0
- package/src/protocol-types.test.ts +69 -0
- package/src/protocol-types.ts +296 -0
- package/src/protocol-types.typecheck.ts +89 -0
- package/src/protocol.test.ts +1 -6
- package/src/protocol.ts +2 -7
- package/src/reply-dispatcher.test.ts +819 -119
- package/src/reply-dispatcher.ts +202 -60
- package/src/runtime.test.ts +2120 -41
- package/src/runtime.ts +935 -142
- package/src/scripts.test.ts +85 -0
- package/src/storage.test.ts +793 -0
- package/src/storage.ts +1095 -0
- package/src/streaming.test.ts +9 -8
- package/src/streaming.ts +1 -1
- package/src/tools-schema.ts +148 -20
- package/src/tools.test.ts +377 -50
- package/src/tools.ts +574 -154
- package/src/ws-alignment.test.ts +103 -0
- package/src/ws-alignment.ts +275 -0
- package/src/ws-client.test.ts +1218 -0
- package/src/ws-client.ts +662 -0
- package/src/ws-log.test.ts +32 -0
- package/src/ws-log.ts +31 -0
- package/skills/clawchat-account-tools/SKILL.md +0 -26
- package/skills/clawchat-activate/SKILL.md +0 -47
package/src/api-client.ts
CHANGED
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ClawlingApiError,
|
|
3
3
|
type AgentConnectResult,
|
|
4
|
+
type AvatarUploadResult,
|
|
5
|
+
type ConversationDetails,
|
|
6
|
+
type ConversationListResult,
|
|
4
7
|
type FriendList,
|
|
8
|
+
type MomentComment,
|
|
9
|
+
type MomentReaction,
|
|
10
|
+
type MomentView,
|
|
5
11
|
type Profile,
|
|
6
12
|
type UploadResult,
|
|
13
|
+
type UserSearchHit,
|
|
7
14
|
} from "./api-types.ts";
|
|
8
15
|
import { CHANNEL_ID } from "./config.ts";
|
|
9
16
|
|
|
10
17
|
export interface ApiClientOptions {
|
|
11
18
|
baseUrl: string;
|
|
12
19
|
token: string;
|
|
13
|
-
/**
|
|
14
|
-
* Logged-in agent's `user_id`. Required for `updateMyProfile`, which
|
|
15
|
-
* targets `/v1/agents/{userId}`. Safe to omit for unauthenticated /
|
|
16
|
-
* pre-login calls (e.g. `agentsConnect`).
|
|
17
|
-
*/
|
|
20
|
+
/** Logged-in agent's `user_id`, used by WebSocket setup but not REST `/me` calls. */
|
|
18
21
|
userId?: string;
|
|
19
22
|
/** Test override only. Defaults to global `fetch`. */
|
|
20
23
|
fetchImpl?: typeof fetch;
|
|
@@ -23,7 +26,27 @@ export interface ApiClientOptions {
|
|
|
23
26
|
export interface OpenclawClawlingApiClient {
|
|
24
27
|
getMyProfile(): Promise<Profile>;
|
|
25
28
|
getUserInfo(userId: string): Promise<Profile>;
|
|
26
|
-
listFriends(
|
|
29
|
+
listFriends(): Promise<FriendList>;
|
|
30
|
+
searchUsers(params: { q?: string; limit?: number }): Promise<{ users: UserSearchHit[] }>;
|
|
31
|
+
listMoments(params: { before?: number; limit?: number }): Promise<{ moments: MomentView[] }>;
|
|
32
|
+
createMoment(body: { text?: string; images?: string[] }): Promise<{ moment: MomentView }>;
|
|
33
|
+
deleteMoment(momentId: number): Promise<{ ok: boolean }>;
|
|
34
|
+
toggleMomentReaction(params: {
|
|
35
|
+
momentId: number;
|
|
36
|
+
emoji: string;
|
|
37
|
+
}): Promise<{ reactions: MomentReaction[] }>;
|
|
38
|
+
createMomentComment(params: {
|
|
39
|
+
momentId: number;
|
|
40
|
+
text: string;
|
|
41
|
+
}): Promise<{ comment: MomentComment }>;
|
|
42
|
+
replyMomentComment(params: {
|
|
43
|
+
momentId: number;
|
|
44
|
+
replyToCommentId: number;
|
|
45
|
+
text: string;
|
|
46
|
+
}): Promise<{ comment: MomentComment }>;
|
|
47
|
+
deleteMomentComment(params: { momentId: number; commentId: number }): Promise<{ ok: boolean }>;
|
|
48
|
+
listConversations(params: { before?: string; limit?: number }): Promise<ConversationListResult>;
|
|
49
|
+
getConversation(conversationId: string): Promise<{ conversation: ConversationDetails }>;
|
|
27
50
|
updateMyProfile(patch: { nickname?: string; avatar_url?: string; bio?: string }): Promise<Profile>;
|
|
28
51
|
uploadMedia(params: { buffer: Buffer; filename: string; mime?: string }): Promise<UploadResult>;
|
|
29
52
|
/**
|
|
@@ -39,15 +62,14 @@ export interface OpenclawClawlingApiClient {
|
|
|
39
62
|
type: string;
|
|
40
63
|
}): Promise<AgentConnectResult>;
|
|
41
64
|
/**
|
|
42
|
-
* Upload an avatar image via `POST /v1/files/upload-url`.
|
|
43
|
-
*
|
|
44
|
-
* what you then pass to `updateMyProfile({ avatar: url })`.
|
|
65
|
+
* Upload an avatar image via `POST /v1/files/upload-url`. The resulting
|
|
66
|
+
* `url` is what you then pass to `updateMyProfile({ avatar_url: url })`.
|
|
45
67
|
*/
|
|
46
68
|
uploadAvatar(params: {
|
|
47
69
|
buffer: Buffer;
|
|
48
70
|
filename: string;
|
|
49
71
|
mime?: string;
|
|
50
|
-
}): Promise<
|
|
72
|
+
}): Promise<AvatarUploadResult>;
|
|
51
73
|
}
|
|
52
74
|
|
|
53
75
|
export function createOpenclawClawlingApiClient(opts: ApiClientOptions): OpenclawClawlingApiClient {
|
|
@@ -151,6 +173,27 @@ export function createOpenclawClawlingApiClient(opts: ApiClientOptions): Opencla
|
|
|
151
173
|
return await readEnvelope<T>(res, path);
|
|
152
174
|
}
|
|
153
175
|
|
|
176
|
+
function parseUploadResult(data: unknown, path: string): UploadResult {
|
|
177
|
+
const obj = data as Partial<UploadResult> | null;
|
|
178
|
+
const validKind =
|
|
179
|
+
obj?.kind === "image" || obj?.kind === "file" || obj?.kind === "audio" || obj?.kind === "video";
|
|
180
|
+
if (
|
|
181
|
+
!obj ||
|
|
182
|
+
!validKind ||
|
|
183
|
+
typeof obj.url !== "string" ||
|
|
184
|
+
typeof obj.name !== "string" ||
|
|
185
|
+
typeof obj.mime !== "string" ||
|
|
186
|
+
typeof obj.size !== "number"
|
|
187
|
+
) {
|
|
188
|
+
throw new ClawlingApiError(
|
|
189
|
+
"api",
|
|
190
|
+
"invalid upload response: missing required media fields",
|
|
191
|
+
{ path },
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
return obj as UploadResult;
|
|
195
|
+
}
|
|
196
|
+
|
|
154
197
|
// All JSON API endpoints live under `/v1/...`. Media upload is the one
|
|
155
198
|
// intentional exception — the upstream server mounts it at `/media/upload`
|
|
156
199
|
// without the version prefix.
|
|
@@ -161,20 +204,91 @@ export function createOpenclawClawlingApiClient(opts: ApiClientOptions): Opencla
|
|
|
161
204
|
async getUserInfo(userId: string): Promise<Profile> {
|
|
162
205
|
return await call<Profile>("GET", `/v1/users/${encodeURIComponent(userId)}`);
|
|
163
206
|
},
|
|
164
|
-
async listFriends(
|
|
207
|
+
async listFriends(): Promise<FriendList> {
|
|
208
|
+
return await call<FriendList>("GET", "/v1/friendships");
|
|
209
|
+
},
|
|
210
|
+
async searchUsers(params): Promise<{ users: UserSearchHit[] }> {
|
|
165
211
|
const sp = new URLSearchParams();
|
|
166
|
-
if (typeof params.
|
|
167
|
-
if (typeof params.
|
|
212
|
+
if (typeof params.q === "string") sp.set("q", params.q);
|
|
213
|
+
if (typeof params.limit === "number") sp.set("limit", String(params.limit));
|
|
168
214
|
const q = sp.toString();
|
|
169
|
-
return await call<
|
|
215
|
+
return await call<{ users: UserSearchHit[] }>(
|
|
216
|
+
"GET",
|
|
217
|
+
q ? `/v1/users/search?${q}` : "/v1/users/search",
|
|
218
|
+
);
|
|
219
|
+
},
|
|
220
|
+
async listMoments(params): Promise<{ moments: MomentView[] }> {
|
|
221
|
+
const sp = new URLSearchParams();
|
|
222
|
+
if (typeof params.before === "number") sp.set("before", String(params.before));
|
|
223
|
+
if (typeof params.limit === "number") sp.set("limit", String(params.limit));
|
|
224
|
+
const q = sp.toString();
|
|
225
|
+
return await call<{ moments: MomentView[] }>("GET", q ? `/v1/moments?${q}` : "/v1/moments");
|
|
226
|
+
},
|
|
227
|
+
async createMoment(body): Promise<{ moment: MomentView }> {
|
|
228
|
+
return await call<{ moment: MomentView }>("POST", "/v1/moments", {
|
|
229
|
+
body: JSON.stringify(body),
|
|
230
|
+
headers: { "content-type": "application/json" },
|
|
231
|
+
});
|
|
232
|
+
},
|
|
233
|
+
async deleteMoment(momentId): Promise<{ ok: boolean }> {
|
|
234
|
+
return await call<{ ok: boolean }>("DELETE", `/v1/moments/${encodeURIComponent(String(momentId))}`);
|
|
235
|
+
},
|
|
236
|
+
async toggleMomentReaction(params): Promise<{ reactions: MomentReaction[] }> {
|
|
237
|
+
return await call<{ reactions: MomentReaction[] }>(
|
|
238
|
+
"POST",
|
|
239
|
+
`/v1/moments/${encodeURIComponent(String(params.momentId))}/reactions`,
|
|
240
|
+
{
|
|
241
|
+
body: JSON.stringify({ emoji: params.emoji }),
|
|
242
|
+
headers: { "content-type": "application/json" },
|
|
243
|
+
},
|
|
244
|
+
);
|
|
245
|
+
},
|
|
246
|
+
async createMomentComment(params): Promise<{ comment: MomentComment }> {
|
|
247
|
+
return await call<{ comment: MomentComment }>(
|
|
248
|
+
"POST",
|
|
249
|
+
`/v1/moments/${encodeURIComponent(String(params.momentId))}/comments`,
|
|
250
|
+
{
|
|
251
|
+
body: JSON.stringify({ text: params.text }),
|
|
252
|
+
headers: { "content-type": "application/json" },
|
|
253
|
+
},
|
|
254
|
+
);
|
|
255
|
+
},
|
|
256
|
+
async replyMomentComment(params): Promise<{ comment: MomentComment }> {
|
|
257
|
+
return await call<{ comment: MomentComment }>(
|
|
258
|
+
"POST",
|
|
259
|
+
`/v1/moments/${encodeURIComponent(String(params.momentId))}/comments`,
|
|
260
|
+
{
|
|
261
|
+
body: JSON.stringify({
|
|
262
|
+
text: params.text,
|
|
263
|
+
reply_to_comment_id: params.replyToCommentId,
|
|
264
|
+
}),
|
|
265
|
+
headers: { "content-type": "application/json" },
|
|
266
|
+
},
|
|
267
|
+
);
|
|
268
|
+
},
|
|
269
|
+
async deleteMomentComment(params): Promise<{ ok: boolean }> {
|
|
270
|
+
return await call<{ ok: boolean }>(
|
|
271
|
+
"DELETE",
|
|
272
|
+
`/v1/moments/${encodeURIComponent(String(params.momentId))}/comments/${encodeURIComponent(String(params.commentId))}`,
|
|
273
|
+
);
|
|
274
|
+
},
|
|
275
|
+
async listConversations(params): Promise<ConversationListResult> {
|
|
276
|
+
const sp = new URLSearchParams();
|
|
277
|
+
if (typeof params.before === "string") sp.set("before", params.before);
|
|
278
|
+
if (typeof params.limit === "number") sp.set("limit", String(params.limit));
|
|
279
|
+
const q = sp.toString();
|
|
280
|
+
return await call<ConversationListResult>(
|
|
281
|
+
"GET",
|
|
282
|
+
q ? `/v1/conversations?${q}` : "/v1/conversations",
|
|
283
|
+
);
|
|
284
|
+
},
|
|
285
|
+
async getConversation(conversationId): Promise<{ conversation: ConversationDetails }> {
|
|
286
|
+
return await call<{ conversation: ConversationDetails }>(
|
|
287
|
+
"GET",
|
|
288
|
+
`/v1/conversations/${encodeURIComponent(conversationId)}`,
|
|
289
|
+
);
|
|
170
290
|
},
|
|
171
291
|
async updateMyProfile(patch): Promise<Profile> {
|
|
172
|
-
if (!opts.userId?.trim()) {
|
|
173
|
-
throw new ClawlingApiError(
|
|
174
|
-
"validation",
|
|
175
|
-
"updateMyProfile: userId is required to target /v1/agents/{userId}",
|
|
176
|
-
);
|
|
177
|
-
}
|
|
178
292
|
return await call<Profile>(
|
|
179
293
|
"PATCH",
|
|
180
294
|
`/v1/users/me`,
|
|
@@ -213,9 +327,10 @@ export function createOpenclawClawlingApiClient(opts: ApiClientOptions): Opencla
|
|
|
213
327
|
});
|
|
214
328
|
const fd = new FormData();
|
|
215
329
|
fd.set("file", file);
|
|
216
|
-
|
|
330
|
+
const data = await call<unknown>("POST", "/media/upload", { body: fd });
|
|
331
|
+
return parseUploadResult(data, "/media/upload");
|
|
217
332
|
},
|
|
218
|
-
async uploadAvatar(params): Promise<
|
|
333
|
+
async uploadAvatar(params): Promise<AvatarUploadResult> {
|
|
219
334
|
const blob = new Blob([new Uint8Array(params.buffer)], {
|
|
220
335
|
type: params.mime ?? "application/octet-stream",
|
|
221
336
|
});
|
|
@@ -224,7 +339,7 @@ export function createOpenclawClawlingApiClient(opts: ApiClientOptions): Opencla
|
|
|
224
339
|
});
|
|
225
340
|
const fd = new FormData();
|
|
226
341
|
fd.set("file", file);
|
|
227
|
-
return await call<
|
|
342
|
+
return await call<AvatarUploadResult>("POST", "/v1/files/upload-url", { body: fd });
|
|
228
343
|
},
|
|
229
344
|
};
|
|
230
345
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ConversationListItem } from "./api-types.ts";
|
|
2
|
+
|
|
3
|
+
const listItemWithSoftDeletedPeer: ConversationListItem = {
|
|
4
|
+
id: "cnv_1",
|
|
5
|
+
type: "direct",
|
|
6
|
+
title: "Deleted peer",
|
|
7
|
+
created_at: "2026-05-20T00:00:00Z",
|
|
8
|
+
updated_at: "2026-05-20T00:01:00Z",
|
|
9
|
+
peer: null,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
void listItemWithSoftDeletedPeer;
|
package/src/api-types.ts
CHANGED
|
@@ -14,18 +14,99 @@ export interface Profile {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export interface FriendList {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
friends: Profile[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface UserSearchHit {
|
|
21
|
+
avatar_url?: string;
|
|
22
|
+
id: string;
|
|
23
|
+
nickname?: string;
|
|
24
|
+
type?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface MomentReaction {
|
|
28
|
+
count: number;
|
|
29
|
+
emoji: string;
|
|
30
|
+
mine: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface MomentComment {
|
|
34
|
+
author_id: string;
|
|
35
|
+
created_at: string;
|
|
36
|
+
id: number;
|
|
37
|
+
reply_to_author_id?: string;
|
|
38
|
+
reply_to_comment_id?: number;
|
|
39
|
+
text: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface MomentView {
|
|
43
|
+
author_id: string;
|
|
44
|
+
comments?: MomentComment[];
|
|
45
|
+
comments_total?: number;
|
|
46
|
+
created_at: string;
|
|
47
|
+
id: number;
|
|
48
|
+
images?: string[];
|
|
49
|
+
reactions?: MomentReaction[];
|
|
50
|
+
text?: string;
|
|
21
51
|
}
|
|
22
52
|
|
|
23
53
|
export interface UploadResult {
|
|
54
|
+
kind: "image" | "file" | "audio" | "video";
|
|
55
|
+
url: string;
|
|
56
|
+
name: string;
|
|
57
|
+
size: number;
|
|
58
|
+
mime: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface AvatarUploadResult {
|
|
24
62
|
url: string;
|
|
25
63
|
size: number;
|
|
26
64
|
mime: string;
|
|
27
65
|
}
|
|
28
66
|
|
|
67
|
+
export interface ConversationPeerView {
|
|
68
|
+
id: string;
|
|
69
|
+
type: string;
|
|
70
|
+
nickname: string;
|
|
71
|
+
avatar_url: string;
|
|
72
|
+
[key: string]: unknown;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export interface ConversationListItem {
|
|
76
|
+
id: string;
|
|
77
|
+
type: string;
|
|
78
|
+
title: string;
|
|
79
|
+
created_at: string;
|
|
80
|
+
updated_at: string;
|
|
81
|
+
peer: ConversationPeerView | null;
|
|
82
|
+
[key: string]: unknown;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface ConversationParticipant {
|
|
86
|
+
conversation_id: string;
|
|
87
|
+
user_id: string;
|
|
88
|
+
role: string;
|
|
89
|
+
joined_at: string;
|
|
90
|
+
[key: string]: unknown;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export interface ConversationDetails {
|
|
94
|
+
id: string;
|
|
95
|
+
type: string;
|
|
96
|
+
title: string;
|
|
97
|
+
creator_id: string;
|
|
98
|
+
created_at: string;
|
|
99
|
+
updated_at: string;
|
|
100
|
+
participants: ConversationParticipant[];
|
|
101
|
+
[key: string]: unknown;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export interface ConversationListResult {
|
|
105
|
+
conversations: ConversationListItem[];
|
|
106
|
+
next_before?: string;
|
|
107
|
+
[key: string]: unknown;
|
|
108
|
+
}
|
|
109
|
+
|
|
29
110
|
export interface AgentProfile {
|
|
30
111
|
id: string;
|
|
31
112
|
owner_id: string;
|
|
@@ -40,6 +121,10 @@ export interface AgentProfile {
|
|
|
40
121
|
created_at: string;
|
|
41
122
|
}
|
|
42
123
|
|
|
124
|
+
export interface AgentConnectConversation {
|
|
125
|
+
id: string;
|
|
126
|
+
}
|
|
127
|
+
|
|
43
128
|
/**
|
|
44
129
|
* Response payload for `POST /v1/agents/connect` — the endpoint that exchanges
|
|
45
130
|
* an invite code for the credentials this account uses to open the
|
|
@@ -51,6 +136,7 @@ export interface AgentConnectResult {
|
|
|
51
136
|
agent: AgentProfile;
|
|
52
137
|
access_token: string;
|
|
53
138
|
refresh_token: string;
|
|
139
|
+
conversation?: AgentConnectConversation;
|
|
54
140
|
}
|
|
55
141
|
|
|
56
142
|
export type ClawlingApiErrorKind =
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ClawlingChatClient } from "
|
|
1
|
+
import type { ClawlingChatClient } from "./ws-client.ts";
|
|
2
2
|
import { describe, expect, it, vi } from "vitest";
|
|
3
3
|
import { mergeStreamingText, openBufferedStreamingSession } from "./buffered-stream.ts";
|
|
4
4
|
|
|
@@ -46,7 +46,7 @@ describe("openBufferedStreamingSession", () => {
|
|
|
46
46
|
openBufferedStreamingSession({
|
|
47
47
|
client,
|
|
48
48
|
to: { id: "u1", type: "direct" },
|
|
49
|
-
sender: {
|
|
49
|
+
sender: { id: "a1", type: "direct", nick_name: "Bot" },
|
|
50
50
|
messageId: "m1",
|
|
51
51
|
flushIntervalMs: 50,
|
|
52
52
|
minChunkChars: 4,
|
|
@@ -62,7 +62,7 @@ describe("openBufferedStreamingSession", () => {
|
|
|
62
62
|
const session = openBufferedStreamingSession({
|
|
63
63
|
client,
|
|
64
64
|
to: { id: "u1", type: "direct" },
|
|
65
|
-
sender: {
|
|
65
|
+
sender: { id: "a1", type: "direct", nick_name: "Bot" },
|
|
66
66
|
messageId: "m1",
|
|
67
67
|
flushIntervalMs: 60_000,
|
|
68
68
|
minChunkChars: 4,
|
|
@@ -86,7 +86,7 @@ describe("openBufferedStreamingSession", () => {
|
|
|
86
86
|
const session = openBufferedStreamingSession({
|
|
87
87
|
client,
|
|
88
88
|
to: { id: "u1", type: "direct" },
|
|
89
|
-
sender: {
|
|
89
|
+
sender: { id: "a1", type: "direct", nick_name: "Bot" },
|
|
90
90
|
messageId: "m1",
|
|
91
91
|
flushIntervalMs: 60_000,
|
|
92
92
|
minChunkChars: 1,
|
|
@@ -111,7 +111,7 @@ describe("openBufferedStreamingSession", () => {
|
|
|
111
111
|
const session = openBufferedStreamingSession({
|
|
112
112
|
client,
|
|
113
113
|
to: { id: "u1", type: "direct" },
|
|
114
|
-
sender: {
|
|
114
|
+
sender: { id: "a1", type: "direct", nick_name: "Bot" },
|
|
115
115
|
messageId: "m1",
|
|
116
116
|
flushIntervalMs: 60_000,
|
|
117
117
|
minChunkChars: 999,
|
|
@@ -138,7 +138,7 @@ describe("openBufferedStreamingSession", () => {
|
|
|
138
138
|
const session = openBufferedStreamingSession({
|
|
139
139
|
client,
|
|
140
140
|
to: { id: "u1", type: "direct" },
|
|
141
|
-
sender: {
|
|
141
|
+
sender: { id: "a1", type: "direct", nick_name: "Bot" },
|
|
142
142
|
messageId: "m1",
|
|
143
143
|
flushIntervalMs: 60_000,
|
|
144
144
|
minChunkChars: 999,
|
|
@@ -150,12 +150,12 @@ describe("openBufferedStreamingSession", () => {
|
|
|
150
150
|
expect(doneCount).toBe(1);
|
|
151
151
|
});
|
|
152
152
|
|
|
153
|
-
it("fail() emits message.failed with
|
|
153
|
+
it("fail() emits message.failed with StreamDonePayload shape and typing(false)", async () => {
|
|
154
154
|
const { client, sent, typing } = mockClient();
|
|
155
155
|
const session = openBufferedStreamingSession({
|
|
156
156
|
client,
|
|
157
157
|
to: { id: "u1", type: "direct" },
|
|
158
|
-
sender: {
|
|
158
|
+
sender: { id: "a1", type: "direct", nick_name: "Bot" },
|
|
159
159
|
messageId: "m1",
|
|
160
160
|
flushIntervalMs: 60_000,
|
|
161
161
|
minChunkChars: 999,
|
|
@@ -163,8 +163,10 @@ describe("openBufferedStreamingSession", () => {
|
|
|
163
163
|
});
|
|
164
164
|
await session.fail("boom");
|
|
165
165
|
const failed = sent.find((s) => s.event === "message.failed")!;
|
|
166
|
-
expect(failed.payload
|
|
167
|
-
expect(failed.payload
|
|
166
|
+
expect(failed.payload).not.toHaveProperty("reason");
|
|
167
|
+
expect(failed.payload).not.toHaveProperty("sequence");
|
|
168
|
+
expect((failed.payload.streaming as { sequence: number }).sequence).toBe(0);
|
|
169
|
+
expect(failed.payload.fragments).toEqual([{ kind: "text", text: "boom" }]);
|
|
168
170
|
expect(failed.payload).toHaveProperty("completed_at");
|
|
169
171
|
expect(failed.payload).not.toHaveProperty("failed_at");
|
|
170
172
|
expect(typing.at(-1)).toEqual(["u1", false]);
|
|
@@ -176,7 +178,7 @@ describe("openBufferedStreamingSession", () => {
|
|
|
176
178
|
const session = openBufferedStreamingSession({
|
|
177
179
|
client,
|
|
178
180
|
to: { id: "u1", type: "direct" },
|
|
179
|
-
sender: {
|
|
181
|
+
sender: { id: "a1", type: "direct", nick_name: "Bot" },
|
|
180
182
|
messageId: "m1",
|
|
181
183
|
flushIntervalMs: 60_000,
|
|
182
184
|
minChunkChars: 1,
|
|
@@ -194,7 +196,7 @@ describe("openBufferedStreamingSession", () => {
|
|
|
194
196
|
const session = openBufferedStreamingSession({
|
|
195
197
|
client,
|
|
196
198
|
to: { id: "u1", type: "direct" },
|
|
197
|
-
sender: {
|
|
199
|
+
sender: { id: "a1", type: "direct", nick_name: "Bot" },
|
|
198
200
|
messageId: "m1",
|
|
199
201
|
flushIntervalMs: 60_000,
|
|
200
202
|
minChunkChars: 1,
|
package/src/buffered-stream.ts
CHANGED