@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.
Files changed (85) hide show
  1. package/INSTALL.md +64 -0
  2. package/README.md +121 -19
  3. package/dist/index.js +10 -19
  4. package/dist/setup-entry.js +3 -0
  5. package/dist/src/api-client.js +78 -10
  6. package/dist/src/api-types.test-d.js +10 -0
  7. package/dist/src/channel.js +25 -156
  8. package/dist/src/channel.setup.js +120 -0
  9. package/dist/src/client.js +37 -41
  10. package/dist/src/config.js +75 -17
  11. package/dist/src/inbound.js +79 -61
  12. package/dist/src/login.runtime.js +84 -19
  13. package/dist/src/media-runtime.js +8 -8
  14. package/dist/src/message-mapper.js +1 -1
  15. package/dist/src/mock-transport.js +31 -0
  16. package/dist/src/outbound.js +410 -26
  17. package/dist/src/protocol-types.js +63 -0
  18. package/dist/src/protocol-types.typecheck.js +1 -0
  19. package/dist/src/protocol.js +2 -7
  20. package/dist/src/reply-dispatcher.js +157 -54
  21. package/dist/src/runtime.js +795 -119
  22. package/dist/src/storage.js +689 -0
  23. package/dist/src/tools-schema.js +98 -16
  24. package/dist/src/tools.js +422 -135
  25. package/dist/src/ws-alignment.js +178 -0
  26. package/dist/src/ws-client.js +588 -0
  27. package/dist/src/ws-log.js +19 -0
  28. package/index.ts +10 -22
  29. package/openclaw.plugin.json +37 -2
  30. package/package.json +17 -4
  31. package/setup-entry.ts +4 -0
  32. package/skills/clawchat/SKILL.md +88 -0
  33. package/src/api-client.test.ts +274 -14
  34. package/src/api-client.ts +138 -23
  35. package/src/api-types.test-d.ts +12 -0
  36. package/src/api-types.ts +90 -4
  37. package/src/buffered-stream.test.ts +14 -12
  38. package/src/buffered-stream.ts +1 -1
  39. package/src/channel.outbound.test.ts +269 -60
  40. package/src/channel.setup.ts +146 -0
  41. package/src/channel.test.ts +130 -24
  42. package/src/channel.ts +30 -186
  43. package/src/client.test.ts +197 -11
  44. package/src/client.ts +50 -57
  45. package/src/config.test.ts +108 -6
  46. package/src/config.ts +95 -24
  47. package/src/inbound.test.ts +288 -37
  48. package/src/inbound.ts +96 -84
  49. package/src/login.runtime.test.ts +347 -13
  50. package/src/login.runtime.ts +105 -23
  51. package/src/manifest.test.ts +146 -74
  52. package/src/media-runtime.test.ts +57 -2
  53. package/src/media-runtime.ts +26 -17
  54. package/src/message-mapper.test.ts +2 -2
  55. package/src/message-mapper.ts +2 -2
  56. package/src/mock-transport.test.ts +35 -0
  57. package/src/mock-transport.ts +38 -0
  58. package/src/outbound.test.ts +694 -73
  59. package/src/outbound.ts +484 -31
  60. package/src/plugin-entry.test.ts +1 -0
  61. package/src/protocol-types.test.ts +69 -0
  62. package/src/protocol-types.ts +296 -0
  63. package/src/protocol-types.typecheck.ts +89 -0
  64. package/src/protocol.test.ts +1 -6
  65. package/src/protocol.ts +2 -7
  66. package/src/reply-dispatcher.test.ts +819 -119
  67. package/src/reply-dispatcher.ts +202 -60
  68. package/src/runtime.test.ts +2120 -41
  69. package/src/runtime.ts +935 -142
  70. package/src/scripts.test.ts +85 -0
  71. package/src/storage.test.ts +793 -0
  72. package/src/storage.ts +1095 -0
  73. package/src/streaming.test.ts +9 -8
  74. package/src/streaming.ts +1 -1
  75. package/src/tools-schema.ts +148 -20
  76. package/src/tools.test.ts +377 -50
  77. package/src/tools.ts +574 -154
  78. package/src/ws-alignment.test.ts +103 -0
  79. package/src/ws-alignment.ts +275 -0
  80. package/src/ws-client.test.ts +1218 -0
  81. package/src/ws-client.ts +662 -0
  82. package/src/ws-log.test.ts +32 -0
  83. package/src/ws-log.ts +31 -0
  84. package/skills/clawchat-account-tools/SKILL.md +0 -26
  85. 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(params: { page?: number; pageSize?: number }): Promise<FriendList>;
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`. Returns the
43
- * same `UploadResult` shape as `uploadMedia` the resulting `url` is
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<UploadResult>;
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(params): Promise<FriendList> {
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.page === "number") sp.set("page", String(params.page));
167
- if (typeof params.pageSize === "number") sp.set("pageSize", String(params.pageSize));
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<FriendList>("GET", q ? `/v1/friends?${q}` : "/v1/friends");
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
- return await call<UploadResult>("POST", "/media/upload", { body: fd });
330
+ const data = await call<unknown>("POST", "/media/upload", { body: fd });
331
+ return parseUploadResult(data, "/media/upload");
217
332
  },
218
- async uploadAvatar(params): Promise<UploadResult> {
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<UploadResult>("POST", "/v1/files/upload-url", { body: fd });
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
- items: Profile[];
18
- total?: number;
19
- page: number;
20
- pageSize: number;
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 "@newbase-clawchat/sdk";
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: { sender_id: "a1", type: "direct", display_name: "Bot" },
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: { sender_id: "a1", type: "direct", display_name: "Bot" },
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: { sender_id: "a1", type: "direct", display_name: "Bot" },
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: { sender_id: "a1", type: "direct", display_name: "Bot" },
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: { sender_id: "a1", type: "direct", display_name: "Bot" },
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 reason and typing(false)", async () => {
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: { sender_id: "a1", type: "direct", display_name: "Bot" },
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.reason).toBe("boom");
167
- expect(failed.payload.sequence).toBe(0);
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: { sender_id: "a1", type: "direct", display_name: "Bot" },
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: { sender_id: "a1", type: "direct", display_name: "Bot" },
199
+ sender: { id: "a1", type: "direct", nick_name: "Bot" },
198
200
  messageId: "m1",
199
201
  flushIntervalMs: 60_000,
200
202
  minChunkChars: 1,
@@ -1,4 +1,4 @@
1
- import type { ClawlingChatClient } from "@newbase-clawchat/sdk";
1
+ import type { ClawlingChatClient } from "./ws-client.ts";
2
2
  import {
3
3
  emitStreamAdd,
4
4
  emitStreamCreated,