@newbase-clawchat/openclaw-clawchat 2026.4.23 → 2026.4.29
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 +40 -13
- package/index.ts +23 -11
- package/openclaw.plugin.json +69 -1
- package/package.json +3 -11
- package/skills/clawchat-account-tools/SKILL.md +26 -0
- package/skills/clawchat-activate/SKILL.md +38 -0
- package/src/api-client.test.ts +6 -5
- package/src/api-client.ts +8 -3
- package/src/buffered-stream.test.ts +4 -4
- package/src/buffered-stream.ts +16 -8
- package/src/channel.outbound.test.ts +49 -35
- package/src/channel.test.ts +45 -10
- package/src/channel.ts +15 -14
- package/src/client.test.ts +2 -1
- package/src/client.ts +37 -11
- package/src/commands.test.ts +33 -0
- package/src/commands.ts +37 -0
- package/src/config.test.ts +37 -3
- package/src/config.ts +53 -4
- package/src/inbound.test.ts +5 -5
- package/src/inbound.ts +43 -9
- package/src/login.runtime.test.ts +106 -3
- package/src/login.runtime.ts +8 -3
- package/src/manifest.test.ts +106 -4
- package/src/outbound.test.ts +7 -5
- package/src/plugin-entry.test.ts +27 -0
- package/src/protocol.ts +5 -0
- package/src/reply-dispatcher.test.ts +4 -2
- package/src/runtime.test.ts +23 -7
- package/src/runtime.ts +12 -1
- package/src/streaming.test.ts +3 -3
- package/src/streaming.ts +19 -9
- package/src/tools-schema.ts +28 -19
- package/src/tools.test.ts +115 -37
- package/src/tools.ts +137 -116
package/src/runtime.ts
CHANGED
|
@@ -107,6 +107,16 @@ function formatConversationSubject(peer: { kind: "direct" | "group"; id: string
|
|
|
107
107
|
return peer.kind === "group" ? `group:${peer.id}` : peer.id;
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
function withClawChatSessionScope(cfg: OpenClawConfig): OpenClawConfig {
|
|
111
|
+
return {
|
|
112
|
+
...cfg,
|
|
113
|
+
session: {
|
|
114
|
+
...(cfg.session ?? {}),
|
|
115
|
+
dmScope: "per-account-channel-peer",
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
110
120
|
export interface StartGatewayParams {
|
|
111
121
|
cfg: OpenClawConfig;
|
|
112
122
|
account: ResolvedOpenclawClawlingAccount;
|
|
@@ -172,8 +182,9 @@ export async function startOpenclawClawlingGateway(params: StartGatewayParams):
|
|
|
172
182
|
ingest: async (turn) => {
|
|
173
183
|
const rt = runtime.channel;
|
|
174
184
|
const storePath = rt.session.resolveStorePath(cfg.session?.store);
|
|
185
|
+
const routeCfg = withClawChatSessionScope(cfg);
|
|
175
186
|
const route = rt.routing.resolveAgentRoute({
|
|
176
|
-
cfg,
|
|
187
|
+
cfg: routeCfg,
|
|
177
188
|
channel: CHANNEL_ID,
|
|
178
189
|
accountId,
|
|
179
190
|
peer: turn.peer,
|
package/src/streaming.test.ts
CHANGED
|
@@ -37,8 +37,8 @@ describe("openclaw-clawchat streaming", () => {
|
|
|
37
37
|
"message.done",
|
|
38
38
|
]);
|
|
39
39
|
expect((client.typing as ReturnType<typeof vi.fn>).mock.calls).toEqual([
|
|
40
|
-
[
|
|
41
|
-
[
|
|
40
|
+
["u1", true, "direct"],
|
|
41
|
+
["u1", false, "direct"],
|
|
42
42
|
]);
|
|
43
43
|
});
|
|
44
44
|
|
|
@@ -110,7 +110,7 @@ describe("openclaw-clawchat streaming", () => {
|
|
|
110
110
|
expect(sent[0]!.payload.sequence).toBe(3);
|
|
111
111
|
expect(sent[0]!.payload.reason).toBe("boom");
|
|
112
112
|
expect((client.typing as ReturnType<typeof vi.fn>).mock.calls).toEqual([
|
|
113
|
-
[
|
|
113
|
+
["u1", false, "direct"],
|
|
114
114
|
]);
|
|
115
115
|
});
|
|
116
116
|
});
|
package/src/streaming.ts
CHANGED
|
@@ -10,7 +10,8 @@ import {
|
|
|
10
10
|
|
|
11
11
|
export interface StreamingSendParams {
|
|
12
12
|
client: ClawlingChatClient;
|
|
13
|
-
routing
|
|
13
|
+
routing?: EnvelopeRouting;
|
|
14
|
+
to?: { id: string; type: "direct" | "group" };
|
|
14
15
|
sender: StreamSender;
|
|
15
16
|
/**
|
|
16
17
|
* Pre-chunked text. Use `chunkMarkdownText` from
|
|
@@ -22,6 +23,12 @@ export interface StreamingSendParams {
|
|
|
22
23
|
emitTyping?: boolean;
|
|
23
24
|
}
|
|
24
25
|
|
|
26
|
+
function resolveRouting(params: { routing?: EnvelopeRouting; to?: { id: string; type: "direct" | "group" } }): EnvelopeRouting {
|
|
27
|
+
if (params.routing) return params.routing;
|
|
28
|
+
if (params.to) return { chatId: params.to.id, chatType: params.to.type };
|
|
29
|
+
throw new Error("openclaw-clawchat streaming requires routing");
|
|
30
|
+
}
|
|
31
|
+
|
|
25
32
|
/**
|
|
26
33
|
* Emit one full streaming lifecycle for a pre-chunked reply.
|
|
27
34
|
*
|
|
@@ -35,13 +42,14 @@ export interface StreamingSendParams {
|
|
|
35
42
|
* With zero chunks: typing(true) -> created -> done -> typing(false).
|
|
36
43
|
*/
|
|
37
44
|
export async function sendStreamingText(params: StreamingSendParams): Promise<void> {
|
|
45
|
+
const routing = resolveRouting(params);
|
|
38
46
|
const emitTyping = params.emitTyping !== false;
|
|
39
47
|
if (emitTyping) {
|
|
40
|
-
params.client.typing(
|
|
48
|
+
params.client.typing(routing.chatId, true, routing.chatType);
|
|
41
49
|
}
|
|
42
50
|
emitStreamCreated(params.client, {
|
|
43
51
|
messageId: params.messageId,
|
|
44
|
-
routing
|
|
52
|
+
routing,
|
|
45
53
|
});
|
|
46
54
|
let sequence = 0;
|
|
47
55
|
let fullText = "";
|
|
@@ -50,7 +58,7 @@ export async function sendStreamingText(params: StreamingSendParams): Promise<vo
|
|
|
50
58
|
fullText += chunk;
|
|
51
59
|
emitStreamAdd(params.client, {
|
|
52
60
|
messageId: params.messageId,
|
|
53
|
-
routing
|
|
61
|
+
routing,
|
|
54
62
|
sequence,
|
|
55
63
|
fullText,
|
|
56
64
|
textDelta: chunk,
|
|
@@ -58,18 +66,19 @@ export async function sendStreamingText(params: StreamingSendParams): Promise<vo
|
|
|
58
66
|
}
|
|
59
67
|
emitStreamDone(params.client, {
|
|
60
68
|
messageId: params.messageId,
|
|
61
|
-
routing
|
|
69
|
+
routing,
|
|
62
70
|
finalSequence: sequence,
|
|
63
71
|
finalText: fullText,
|
|
64
72
|
});
|
|
65
73
|
if (emitTyping) {
|
|
66
|
-
params.client.typing(
|
|
74
|
+
params.client.typing(routing.chatId, false, routing.chatType);
|
|
67
75
|
}
|
|
68
76
|
}
|
|
69
77
|
|
|
70
78
|
export interface StreamingFailureParams {
|
|
71
79
|
client: ClawlingChatClient;
|
|
72
|
-
routing
|
|
80
|
+
routing?: EnvelopeRouting;
|
|
81
|
+
to?: { id: string; type: "direct" | "group" };
|
|
73
82
|
messageId: string;
|
|
74
83
|
currentSequence: number;
|
|
75
84
|
reason?: string;
|
|
@@ -77,13 +86,14 @@ export interface StreamingFailureParams {
|
|
|
77
86
|
}
|
|
78
87
|
|
|
79
88
|
export async function sendStreamingFailure(params: StreamingFailureParams): Promise<void> {
|
|
89
|
+
const routing = resolveRouting(params);
|
|
80
90
|
emitStreamFailed(params.client, {
|
|
81
91
|
messageId: params.messageId,
|
|
82
|
-
routing
|
|
92
|
+
routing,
|
|
83
93
|
sequence: params.currentSequence + 1,
|
|
84
94
|
reason: params.reason,
|
|
85
95
|
});
|
|
86
96
|
if (params.emitTyping !== false) {
|
|
87
|
-
params.client.typing(
|
|
97
|
+
params.client.typing(routing.chatId, false, routing.chatType);
|
|
88
98
|
}
|
|
89
99
|
}
|
package/src/tools-schema.ts
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { Type, type Static } from "@sinclair/typebox";
|
|
2
2
|
|
|
3
|
-
export const
|
|
4
|
-
export type
|
|
3
|
+
export const ClawchatGetAccountProfileSchema = Type.Object({});
|
|
4
|
+
export type ClawchatGetAccountProfileParams = Static<typeof ClawchatGetAccountProfileSchema>;
|
|
5
5
|
|
|
6
|
-
export const
|
|
7
|
-
userId: Type.String({
|
|
6
|
+
export const ClawchatGetUserProfileSchema = Type.Object({
|
|
7
|
+
userId: Type.String({
|
|
8
|
+
description: "Target ClawChat user id (required). Do not infer this from a nickname.",
|
|
9
|
+
}),
|
|
8
10
|
});
|
|
9
|
-
export type
|
|
11
|
+
export type ClawchatGetUserProfileParams = Static<typeof ClawchatGetUserProfileSchema>;
|
|
10
12
|
|
|
11
|
-
export const
|
|
13
|
+
export const ClawchatListAccountFriendsSchema = Type.Object({
|
|
12
14
|
page: Type.Optional(Type.Integer({ minimum: 1, description: "1-based page number (default 1)" })),
|
|
13
15
|
pageSize: Type.Optional(
|
|
14
16
|
Type.Integer({
|
|
@@ -18,35 +20,42 @@ export const ClawchatListFriendsSchema = Type.Object({
|
|
|
18
20
|
}),
|
|
19
21
|
),
|
|
20
22
|
});
|
|
21
|
-
export type
|
|
23
|
+
export type ClawchatListAccountFriendsParams = Static<typeof ClawchatListAccountFriendsSchema>;
|
|
22
24
|
|
|
23
|
-
export const
|
|
24
|
-
nickname: Type.Optional(Type.String({ description: "New
|
|
25
|
+
export const ClawchatUpdateAccountProfileSchema = Type.Object({
|
|
26
|
+
nickname: Type.Optional(Type.String({ description: "New ClawChat account nickname" })),
|
|
25
27
|
avatar_url: Type.Optional(
|
|
26
|
-
Type.String({
|
|
28
|
+
Type.String({
|
|
29
|
+
description:
|
|
30
|
+
"Avatar URL for the ClawChat account profile (use clawchat_upload_avatar_image first to obtain one from a local image)",
|
|
31
|
+
}),
|
|
32
|
+
),
|
|
33
|
+
bio: Type.Optional(
|
|
34
|
+
Type.String({ description: "New ClawChat account self-introduction / bio text" }),
|
|
27
35
|
),
|
|
28
|
-
bio: Type.Optional(Type.String({ description: "New self-introduction / bio text" })),
|
|
29
36
|
});
|
|
30
|
-
export type
|
|
37
|
+
export type ClawchatUpdateAccountProfileParams = Static<
|
|
38
|
+
typeof ClawchatUpdateAccountProfileSchema
|
|
39
|
+
>;
|
|
31
40
|
|
|
32
|
-
export const
|
|
41
|
+
export const ClawchatUploadMediaFileSchema = Type.Object({
|
|
33
42
|
filePath: Type.String({
|
|
34
|
-
description: "Absolute local path of the file to upload (max 20MB)",
|
|
43
|
+
description: "Absolute local path of the media/file to upload to ClawChat (max 20MB)",
|
|
35
44
|
}),
|
|
36
45
|
});
|
|
37
|
-
export type
|
|
46
|
+
export type ClawchatUploadMediaFileParams = Static<typeof ClawchatUploadMediaFileSchema>;
|
|
38
47
|
|
|
39
|
-
export const
|
|
48
|
+
export const ClawchatUploadAvatarImageSchema = Type.Object({
|
|
40
49
|
filePath: Type.String({
|
|
41
|
-
description: "Absolute local path of the avatar image to upload (max 20MB)",
|
|
50
|
+
description: "Absolute local path of the avatar image to upload to ClawChat (max 20MB)",
|
|
42
51
|
}),
|
|
43
52
|
});
|
|
44
|
-
export type
|
|
53
|
+
export type ClawchatUploadAvatarImageParams = Static<typeof ClawchatUploadAvatarImageSchema>;
|
|
45
54
|
|
|
46
55
|
export const ClawchatActivateSchema = Type.Object({
|
|
47
56
|
code: Type.String({
|
|
48
57
|
description:
|
|
49
|
-
"The invite code (e.g. '
|
|
58
|
+
"The invite code (six uppercase letters/digits, e.g. 'A1B2C3') extracted from the user's message. " +
|
|
50
59
|
"Whitespace is trimmed automatically.",
|
|
51
60
|
}),
|
|
52
61
|
});
|
package/src/tools.test.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { describe, expect, it, vi } from "vitest";
|
|
2
2
|
import { registerOpenclawClawlingTools } from "./tools.ts";
|
|
3
3
|
|
|
4
|
+
const loginRuntime = vi.hoisted(() => ({
|
|
5
|
+
runOpenclawClawlingLogin: vi.fn(),
|
|
6
|
+
}));
|
|
7
|
+
|
|
8
|
+
vi.mock("./login.runtime.ts", () => loginRuntime);
|
|
9
|
+
|
|
4
10
|
interface RegisteredTool {
|
|
5
11
|
name: string;
|
|
6
12
|
execute: (callId: string, params: unknown) => Promise<unknown>;
|
|
@@ -8,6 +14,7 @@ interface RegisteredTool {
|
|
|
8
14
|
|
|
9
15
|
function buildApi(opts: {
|
|
10
16
|
configChannel?: Record<string, unknown> | null;
|
|
17
|
+
configTools?: Record<string, unknown>;
|
|
11
18
|
registerTool?: (tool: { name: string }, options?: { name: string }) => void;
|
|
12
19
|
}) {
|
|
13
20
|
const registered: RegisteredTool[] = [];
|
|
@@ -15,7 +22,10 @@ function buildApi(opts: {
|
|
|
15
22
|
config:
|
|
16
23
|
opts.configChannel === null
|
|
17
24
|
? undefined
|
|
18
|
-
: {
|
|
25
|
+
: {
|
|
26
|
+
channels: { "openclaw-clawchat": opts.configChannel ?? {} },
|
|
27
|
+
...(opts.configTools ? { tools: opts.configTools } : {}),
|
|
28
|
+
},
|
|
19
29
|
logger: {
|
|
20
30
|
info: vi.fn(),
|
|
21
31
|
debug: vi.fn(),
|
|
@@ -39,14 +49,38 @@ function configuredChannel(extra: Record<string, unknown> = {}) {
|
|
|
39
49
|
}
|
|
40
50
|
|
|
41
51
|
describe("registerOpenclawClawlingTools", () => {
|
|
42
|
-
it("registers
|
|
52
|
+
it("registers no tools when account.configured is false", () => {
|
|
53
|
+
const { api, registered } = buildApi({
|
|
54
|
+
configChannel: { websocketUrl: "wss://w" /* token / userId missing */ },
|
|
55
|
+
});
|
|
56
|
+
registerOpenclawClawlingTools(api);
|
|
57
|
+
expect(registered.map((t) => t.name)).toEqual([]);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("does not mutate tool policy during registration before account activation", () => {
|
|
61
|
+
const { api, registered } = buildApi({
|
|
62
|
+
configChannel: { websocketUrl: "wss://w" /* token / userId missing */ },
|
|
63
|
+
configTools: { profile: "coding", allow: [] },
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
registerOpenclawClawlingTools(api);
|
|
67
|
+
|
|
68
|
+
expect(registered.map((t) => t.name)).toEqual([]);
|
|
69
|
+
expect(api.config?.tools).toEqual({
|
|
70
|
+
profile: "coding",
|
|
71
|
+
allow: [],
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("does not register clawchat_activate for invite-code onboarding", async () => {
|
|
43
76
|
const { api, registered } = buildApi({
|
|
44
77
|
configChannel: { websocketUrl: "wss://w" /* token / userId missing */ },
|
|
45
78
|
});
|
|
79
|
+
|
|
46
80
|
registerOpenclawClawlingTools(api);
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
expect(
|
|
81
|
+
|
|
82
|
+
expect(registered.some((t) => t.name === "clawchat_activate")).toBe(false);
|
|
83
|
+
expect(loginRuntime.runOpenclawClawlingLogin).not.toHaveBeenCalled();
|
|
50
84
|
});
|
|
51
85
|
|
|
52
86
|
it("skips registration when api.config is undefined", () => {
|
|
@@ -55,62 +89,106 @@ describe("registerOpenclawClawlingTools", () => {
|
|
|
55
89
|
expect(registered).toHaveLength(0);
|
|
56
90
|
});
|
|
57
91
|
|
|
58
|
-
it("registers all
|
|
92
|
+
it("registers all six account tools when configured (regardless of baseUrl)", () => {
|
|
59
93
|
const { api, registered } = buildApi({
|
|
60
94
|
configChannel: configuredChannel(/* no baseUrl */),
|
|
61
95
|
});
|
|
62
96
|
registerOpenclawClawlingTools(api);
|
|
63
97
|
const names = registered.map((t) => t.name).sort();
|
|
64
98
|
expect(names).toEqual([
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
"clawchat_upload_file",
|
|
99
|
+
"clawchat_get_account_profile",
|
|
100
|
+
"clawchat_get_user_profile",
|
|
101
|
+
"clawchat_list_account_friends",
|
|
102
|
+
"clawchat_update_account_profile",
|
|
103
|
+
"clawchat_upload_avatar_image",
|
|
104
|
+
"clawchat_upload_media_file",
|
|
72
105
|
]);
|
|
73
106
|
});
|
|
74
107
|
|
|
75
|
-
it("
|
|
108
|
+
it("logs configured tool registration at debug level only", () => {
|
|
109
|
+
const { api } = buildApi({
|
|
110
|
+
configChannel: configuredChannel(),
|
|
111
|
+
});
|
|
112
|
+
const logger = api.logger as {
|
|
113
|
+
info: ReturnType<typeof vi.fn>;
|
|
114
|
+
debug: ReturnType<typeof vi.fn>;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
registerOpenclawClawlingTools(api);
|
|
118
|
+
|
|
119
|
+
expect(logger.info).not.toHaveBeenCalled();
|
|
120
|
+
expect(logger.debug).toHaveBeenCalledWith(
|
|
121
|
+
"openclaw-clawchat: registered 6 clawchat_* tools (get_account_profile, get_user_profile, list_account_friends, update_account_profile, upload_avatar_image, upload_media_file)",
|
|
122
|
+
);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("does not register clawchat_activate when configured", () => {
|
|
76
126
|
const { api, registered } = buildApi({
|
|
77
127
|
configChannel: configuredChannel(),
|
|
78
128
|
});
|
|
79
|
-
|
|
129
|
+
registerOpenclawClawlingTools(api);
|
|
130
|
+
expect(registered.some((t) => t.name === "clawchat_activate")).toBe(false);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("clawchat_update_account_profile description names account profile triggers (EN + ZH)", () => {
|
|
134
|
+
const { api } = buildApi({ configChannel: configuredChannel() });
|
|
80
135
|
const fullTools: Array<{ name: string; description?: string }> = [];
|
|
81
136
|
api.registerTool = (tool: { name: string; description?: string }) => {
|
|
82
137
|
fullTools.push(tool);
|
|
83
138
|
};
|
|
84
139
|
registerOpenclawClawlingTools(api);
|
|
85
|
-
const
|
|
86
|
-
expect(
|
|
87
|
-
|
|
88
|
-
expect(
|
|
89
|
-
expect(
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
expect(
|
|
140
|
+
const update = fullTools.find((t) => t.name === "clawchat_update_account_profile")!;
|
|
141
|
+
expect(update).toBeDefined();
|
|
142
|
+
expect(update.description).toMatch(/configured ClawChat account|logged-in ClawChat account/i);
|
|
143
|
+
expect(update.description).toMatch(/ClawChat (account )?(nickname|name)/i);
|
|
144
|
+
expect(update.description).toMatch(/ClawChat 昵称|账号昵称|账号名字/);
|
|
145
|
+
expect(update.description).toMatch(/avatar|profile picture/i);
|
|
146
|
+
expect(update.description).toMatch(/ClawChat 头像|账号头像/);
|
|
147
|
+
expect(update.description).toMatch(/clawchat_upload_avatar_image/);
|
|
148
|
+
expect(update.description).toMatch(/bio|self-introduction/i);
|
|
149
|
+
expect(update.description).toMatch(/ClawChat 简介|账号简介|个人简介/);
|
|
150
|
+
expect(update.description).not.toMatch(
|
|
151
|
+
new RegExp(["agent's own", "rename " + "yourself", "this agent's own"].join("|"), "i"),
|
|
152
|
+
);
|
|
93
153
|
});
|
|
94
154
|
|
|
95
|
-
it("
|
|
155
|
+
it("account query and upload tool descriptions include precise trigger semantics", () => {
|
|
96
156
|
const { api } = buildApi({ configChannel: configuredChannel() });
|
|
97
157
|
const fullTools: Array<{ name: string; description?: string }> = [];
|
|
98
158
|
api.registerTool = (tool: { name: string; description?: string }) => {
|
|
99
159
|
fullTools.push(tool);
|
|
100
160
|
};
|
|
101
161
|
registerOpenclawClawlingTools(api);
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
expect(
|
|
110
|
-
expect(
|
|
162
|
+
|
|
163
|
+
const accountProfile = fullTools.find((t) => t.name === "clawchat_get_account_profile")!;
|
|
164
|
+
const userProfile = fullTools.find((t) => t.name === "clawchat_get_user_profile")!;
|
|
165
|
+
const friends = fullTools.find((t) => t.name === "clawchat_list_account_friends")!;
|
|
166
|
+
const avatar = fullTools.find((t) => t.name === "clawchat_upload_avatar_image")!;
|
|
167
|
+
const media = fullTools.find((t) => t.name === "clawchat_upload_media_file")!;
|
|
168
|
+
|
|
169
|
+
expect(accountProfile.description).toMatch(/configured ClawChat account|logged-in ClawChat account/i);
|
|
170
|
+
expect(accountProfile.description).toMatch(/profile|nickname|avatar|bio/i);
|
|
171
|
+
expect(accountProfile.description).not.toMatch(/agent's own|this agent's/i);
|
|
172
|
+
|
|
173
|
+
expect(userProfile.description).toMatch(/userId/);
|
|
174
|
+
expect(userProfile.description).toMatch(/public profile/i);
|
|
175
|
+
expect(userProfile.description).toMatch(/do not guess|do not infer/i);
|
|
176
|
+
|
|
177
|
+
expect(friends.description).toMatch(/configured ClawChat account|logged-in ClawChat account/i);
|
|
178
|
+
expect(friends.description).toMatch(/friends|contacts/i);
|
|
179
|
+
expect(friends.description).toMatch(/page=1|pageSize=20/);
|
|
180
|
+
|
|
181
|
+
expect(avatar.description).toMatch(/local image/i);
|
|
182
|
+
expect(avatar.description).toMatch(/avatar URL|hosted avatar URL|public URL/i);
|
|
183
|
+
expect(avatar.description).toMatch(/clawchat_update_account_profile/);
|
|
184
|
+
expect(avatar.description).toMatch(/does not update|does not set/i);
|
|
185
|
+
|
|
186
|
+
expect(media.description).toMatch(/local file|media file/i);
|
|
187
|
+
expect(media.description).toMatch(/public URL|shareable URL/i);
|
|
188
|
+
expect(media.description).toMatch(/not.*avatar|do not use.*avatar/i);
|
|
111
189
|
});
|
|
112
190
|
|
|
113
|
-
it("
|
|
191
|
+
it("clawchat_upload_avatar_image rejects oversized files before upload", async () => {
|
|
114
192
|
const fs = await import("node:fs/promises");
|
|
115
193
|
const path = await import("node:path");
|
|
116
194
|
const os = await import("node:os");
|
|
@@ -124,7 +202,7 @@ describe("registerOpenclawClawlingTools", () => {
|
|
|
124
202
|
configChannel: configuredChannel({ baseUrl: "https://api.example.com" }),
|
|
125
203
|
});
|
|
126
204
|
registerOpenclawClawlingTools(api);
|
|
127
|
-
const tool = registered.find((t) => t.name === "
|
|
205
|
+
const tool = registered.find((t) => t.name === "clawchat_upload_avatar_image")!;
|
|
128
206
|
const result = await tool.execute("call-1", { filePath: big });
|
|
129
207
|
const text = (result as { content: { text: string }[] }).content[0]!.text;
|
|
130
208
|
const parsed = JSON.parse(text) as { error?: string; message?: string };
|
|
@@ -136,7 +214,7 @@ describe("registerOpenclawClawlingTools", () => {
|
|
|
136
214
|
});
|
|
137
215
|
|
|
138
216
|
|
|
139
|
-
it("
|
|
217
|
+
it("clawchat_upload_media_file rejects oversized files before upload", async () => {
|
|
140
218
|
const fs = await import("node:fs/promises");
|
|
141
219
|
const path = await import("node:path");
|
|
142
220
|
const os = await import("node:os");
|
|
@@ -151,7 +229,7 @@ describe("registerOpenclawClawlingTools", () => {
|
|
|
151
229
|
configChannel: configuredChannel({ baseUrl: "https://api.example.com" }),
|
|
152
230
|
});
|
|
153
231
|
registerOpenclawClawlingTools(api);
|
|
154
|
-
const tool = registered.find((t) => t.name === "
|
|
232
|
+
const tool = registered.find((t) => t.name === "clawchat_upload_media_file")!;
|
|
155
233
|
const result = await tool.execute("call-1", { filePath: big });
|
|
156
234
|
const text = (result as { content: { text: string }[] }).content[0]!.text;
|
|
157
235
|
const parsed = JSON.parse(text) as { error?: string; message?: string };
|