@newbase-clawchat/openclaw-clawchat 2026.4.24 → 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/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
- : { channels: { "openclaw-clawchat": opts.configChannel ?? {} } },
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 ONLY clawchat_activate when account.configured is false (onboarding path)", () => {
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
- // clawchat_activate must be available before a token exists — it's the
48
- // tool the agent calls to onboard.
49
- expect(registered.map((t) => t.name)).toEqual(["clawchat_activate"]);
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 seven tools when configured (regardless of baseUrl)", () => {
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
- "clawchat_activate",
66
- "clawchat_get_my_profile",
67
- "clawchat_get_user_info",
68
- "clawchat_list_friends",
69
- "clawchat_update_my_profile",
70
- "clawchat_upload_avatar",
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("clawchat_activate description names the `clawchat <code>` trigger", () => {
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
- // Re-capture full tool objects so we can see description.
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 activate = fullTools.find((t) => t.name === "clawchat_activate")!;
86
- expect(activate).toBeDefined();
87
- // Must tell the LLM exactly how to spot + parse the trigger.
88
- expect(activate.description).toMatch(/clawchat\s*<code>/i);
89
- expect(activate.description).toMatch(/INV-/);
90
- // Must spell out the verbatim-extraction rule so the model doesn't
91
- // re-case / prefix the code.
92
- expect(activate.description).toMatch(/verbatim/i);
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("clawchat_update_my_profile description names name + avatar + bio triggers (EN + ZH)", () => {
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
- const update = fullTools.find((t) => t.name === "clawchat_update_my_profile")!;
103
- expect(update).toBeDefined();
104
- expect(update.description).toMatch(/change your name/i);
105
- expect(update.description).toMatch(/你叫/);
106
- expect(update.description).toMatch(/avatar/i);
107
- expect(update.description).toMatch(/生成头像|换个头像/);
108
- expect(update.description).toMatch(/clawchat_upload_avatar/);
109
- expect(update.description).toMatch(/bio|self introduction/i);
110
- expect(update.description).toMatch(/自我介绍|个人简介/);
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("clawchat_upload_avatar rejects oversized files before upload", async () => {
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 === "clawchat_upload_avatar")!;
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("clawchat_upload_file rejects oversized files before upload", async () => {
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 === "clawchat_upload_file")!;
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 };
package/src/tools.ts CHANGED
@@ -1,29 +1,29 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
- import type { AgentToolResult } from "@mariozechner/pi-agent-core";
3
+ import type { OpenClawAgentToolResult } from "openclaw/plugin-sdk/agent-harness-runtime";
4
4
  import type { OpenClawPluginApi } from "openclaw/plugin-sdk/feishu";
5
5
  import { createOpenclawClawlingApiClient } from "./api-client.ts";
6
6
  import { ClawlingApiError, type Profile } from "./api-types.ts";
7
7
  import { resolveOpenclawClawlingAccount } from "./config.ts";
8
8
  import {
9
- ClawchatActivateSchema,
10
- ClawchatGetMyProfileSchema,
11
- ClawchatGetUserInfoSchema,
12
- ClawchatListFriendsSchema,
13
- ClawchatUpdateMyProfileSchema,
14
- ClawchatUploadAvatarSchema,
15
- ClawchatUploadFileSchema,
16
- type ClawchatActivateParams,
17
- type ClawchatGetUserInfoParams,
18
- type ClawchatListFriendsParams,
19
- type ClawchatUpdateMyProfileParams,
20
- type ClawchatUploadAvatarParams,
21
- type ClawchatUploadFileParams,
9
+ // ClawchatActivateSchema,
10
+ ClawchatGetAccountProfileSchema,
11
+ ClawchatGetUserProfileSchema,
12
+ ClawchatListAccountFriendsSchema,
13
+ ClawchatUpdateAccountProfileSchema,
14
+ ClawchatUploadAvatarImageSchema,
15
+ ClawchatUploadMediaFileSchema,
16
+ // type ClawchatActivateParams,
17
+ type ClawchatGetUserProfileParams,
18
+ type ClawchatListAccountFriendsParams,
19
+ type ClawchatUpdateAccountProfileParams,
20
+ type ClawchatUploadAvatarImageParams,
21
+ type ClawchatUploadMediaFileParams,
22
22
  } from "./tools-schema.ts";
23
23
 
24
24
  const MAX_UPLOAD_BYTES = 20 * 1024 * 1024;
25
25
 
26
- function jsonResponse(data: unknown): AgentToolResult<unknown> {
26
+ function jsonResponse(data: unknown): OpenClawAgentToolResult<unknown> {
27
27
  return {
28
28
  content: [{ type: "text" as const, text: JSON.stringify(data, null, 2) }],
29
29
  details: data,
@@ -46,6 +46,13 @@ function validationError(message: string) {
46
46
  return jsonResponse({ error: "validation", message });
47
47
  }
48
48
 
49
+ // function resolveActivateCode(params: ClawchatActivateParams & { command?: unknown }): string {
50
+ // const explicit = typeof params.code === "string" ? params.code.trim() : "";
51
+ // if (explicit) return explicit;
52
+ // const command = typeof params.command === "string" ? params.command.trim() : "";
53
+ // return command.match(/\b[A-Z0-9]{6}\b/u)?.[0] ?? "";
54
+ // }
55
+
49
56
  function genericError(err: unknown) {
50
57
  return jsonResponse({
51
58
  error: "unknown",
@@ -88,59 +95,60 @@ export function registerOpenclawClawlingTools(api: OpenClawPluginApi): void {
88
95
  }
89
96
 
90
97
  // -----------------------------------------------------------------------
91
- // `clawchat_activate` is registered UNCONDITIONALLY it is the tool the
92
- // agent invokes to onboard this plugin, so it must be available even when
93
- // `account.configured === false`. All other tools below require a valid
94
- // token and are gated on `account.configured`.
98
+ // `clawchat_activate` tool disabled: activation now goes through OpenClaw
99
+ // channel login.
95
100
  // -----------------------------------------------------------------------
96
- api.registerTool(
97
- {
98
- name: "clawchat_activate",
99
- label: "Clawling: Activate (Login with Invite Code)",
100
- description:
101
- "Activate this agent on ClawChat by exchanging an invite code for a token. " +
102
- "TRIGGER invoke this tool whenever the user's message matches ANY of: " +
103
- "(1) the pattern `clawchat <code>` or `clawchat: <code>` e.g. 'clawchat INV-ABC123' " +
104
- "means: call this tool with `code = \"INV-ABC123\"`; " +
105
- "(2) phrases like 'activate clawchat', 'login to clawchat', 'use invite code XYZ', " +
106
- "'connect clawchat with XYZ'; " +
107
- "(3) the user pastes anything that looks like an invite code (e.g. `INV-…`, `CC-…`). " +
108
- "Extract the code verbatim do NOT normalize / lowercase / add prefixes. " +
109
- "On success the tool persists the resulting token + userId to the config, so " +
110
- "subsequent `clawchat_*` calls work without any other setup.",
111
- parameters: ClawchatActivateSchema,
112
- async execute(_callId, params) {
113
- const p = params as ClawchatActivateParams;
114
- const code = p.code?.trim();
115
- if (!code) {
116
- return validationError("openclaw-clawchat: code is required");
117
- }
118
- try {
119
- const { runOpenclawClawlingLogin } = await import("./login.runtime.ts");
120
- await runOpenclawClawlingLogin({
121
- cfg: api.config!,
122
- accountId: null,
123
- runtime: { log: (message: string) => api.logger.info?.(message) },
124
- readInviteCode: async () => code,
125
- });
126
- return jsonResponse({
127
- ok: true,
128
- message: "✅ ClawChat activated successfully.",
129
- });
130
- } catch (err) {
131
- if (err instanceof ClawlingApiError) return apiError(err);
132
- return genericError(err);
133
- }
134
- },
135
- },
136
- { name: "clawchat_activate" },
137
- );
101
+ // api.registerTool(
102
+ // {
103
+ // name: "clawchat_activate",
104
+ // label: "Clawling: Activate (Login with Invite Code)",
105
+ // description:
106
+ // "Activate this OpenClaw plugin on ClawChat by exchanging an invite code for a token. " +
107
+ // "Invite codes use six uppercase letters/digits, e.g. A1B2C3. " +
108
+ // "TRIGGER invoke this tool whenever the user's message matches ANY of: " +
109
+ // "(1) activation intent with an embedded invite code, such as 'activate ClawChat with invite code A1B2C3', " +
110
+ // "'login to ClawChat with invite code A1B2C3', 'connect ClawChat using invite code A1B2C3', " +
111
+ // "or '绑定 ClawChat,邀请码 A1B2C3' — call this tool with `code = \"A1B2C3\"`; " +
112
+ // "(2) generic activation intent without an embedded code, such as 'activate ClawChat' or " +
113
+ // "'login to ClawChat'ask for invite code before calling the tool; " +
114
+ // "(3) activation intent with an embedded code, such as 'use invite code A1B2C3', " +
115
+ // "or the user pasting an invite code in the context of ClawChat activation. " +
116
+ // "Do not ask the user to enter a bare ClawChat command; if an invite code is present, call this tool directly. " +
117
+ // "Extract the code verbatim — do NOT normalize / lowercase / add prefixes. " +
118
+ // "On success the tool persists the resulting token + userId to the config, so " +
119
+ // "subsequent `clawchat_*` calls work without any other setup.",
120
+ // parameters: ClawchatActivateSchema,
121
+ // async execute(_callId, params) {
122
+ // const code = resolveActivateCode(params as ClawchatActivateParams & { command?: unknown });
123
+ // if (!code) {
124
+ // return validationError("openclaw-clawchat: code is required");
125
+ // }
126
+ // try {
127
+ // const { runOpenclawClawlingLogin } = await import("./login.runtime.ts");
128
+ // await runOpenclawClawlingLogin({
129
+ // cfg: api.config!,
130
+ // accountId: null,
131
+ // runtime: { log: (message: string) => api.logger.info?.(message) },
132
+ // readInviteCode: async () => code,
133
+ // });
134
+ // return jsonResponse({
135
+ // ok: true,
136
+ // message: "✅ ClawChat activated successfully.",
137
+ // });
138
+ // } catch (err) {
139
+ // if (err instanceof ClawlingApiError) return apiError(err);
140
+ // return genericError(err);
141
+ // }
142
+ // },
143
+ // },
144
+ // { name: "clawchat_activate" },
145
+ // );
138
146
 
139
147
  const account = resolveOpenclawClawlingAccount(api.config);
140
148
  if (!account.configured) {
141
149
  api.logger.debug?.(
142
- "openclaw-clawchat: account not yet configured; only clawchat_activate is registered. " +
143
- "The remaining tools will register on the next config reload after activation.",
150
+ "openclaw-clawchat: account not yet configured; account tools will register " +
151
+ "on the next config reload after channel login.",
144
152
  );
145
153
  return;
146
154
  }
@@ -151,7 +159,7 @@ export function registerOpenclawClawlingTools(api: OpenClawPluginApi): void {
151
159
  }
152
160
 
153
161
  type ClientResult =
154
- | { error: AgentToolResult<unknown>; client?: never }
162
+ | { error: OpenClawAgentToolResult<unknown>; client?: never }
155
163
  | { client: ReturnType<typeof createOpenclawClawlingApiClient>; error?: never };
156
164
 
157
165
  function buildClient(): ClientResult {
@@ -173,8 +181,8 @@ export function registerOpenclawClawlingTools(api: OpenClawPluginApi): void {
173
181
 
174
182
  function withClient<T>(
175
183
  fn: (client: ReturnType<typeof createOpenclawClawlingApiClient>) => Promise<T>,
176
- ): Promise<AgentToolResult<unknown>> {
177
- return (async (): Promise<AgentToolResult<unknown>> => {
184
+ ): Promise<OpenClawAgentToolResult<unknown>> {
185
+ return (async (): Promise<OpenClawAgentToolResult<unknown>> => {
178
186
  const built = buildClient();
179
187
  if (built.error !== undefined) return built.error;
180
188
  try {
@@ -189,39 +197,50 @@ export function registerOpenclawClawlingTools(api: OpenClawPluginApi): void {
189
197
 
190
198
  api.registerTool(
191
199
  {
192
- name: "clawchat_get_my_profile",
193
- label: "Get Profile",
194
- description: "Fetch the agent's own Clawling profile (id, display name, avatar, bio).",
195
- parameters: ClawchatGetMyProfileSchema,
200
+ name: "clawchat_get_account_profile",
201
+ label: "Get ClawChat Account Profile",
202
+ description:
203
+ "Fetch the configured ClawChat account profile (user id, nickname/display name, avatar, bio). " +
204
+ "TRIGGER — invoke when the user asks for the ClawChat account/profile connected to this plugin, " +
205
+ "such as 'show my ClawChat profile', 'what is the configured ClawChat account?', " +
206
+ "'当前 ClawChat 账号资料', or 'ClawChat 昵称头像简介'. " +
207
+ "Do not use this for OpenClaw agent persona/profile questions unless the user explicitly means the ClawChat account.",
208
+ parameters: ClawchatGetAccountProfileSchema,
196
209
  async execute(_callId, _params) {
197
210
  return await withClient((c) => c.getMyProfile());
198
211
  },
199
212
  },
200
- { name: "clawchat_get_my_profile" },
213
+ { name: "clawchat_get_account_profile" },
201
214
  );
202
215
 
203
216
  api.registerTool(
204
217
  {
205
- name: "clawchat_get_user_info",
206
- label: "Get User Info",
207
- description: "Fetch a Clawling user's public profile by userId.",
208
- parameters: ClawchatGetUserInfoSchema,
218
+ name: "clawchat_get_user_profile",
219
+ label: "Get ClawChat User Profile",
220
+ description:
221
+ "Fetch a ClawChat user's public profile by userId. " +
222
+ "TRIGGER — invoke when the user asks to look up, view, or inspect a specific ClawChat user's public profile " +
223
+ "and provides a concrete userId. Do not guess or infer userId from a nickname/display name.",
224
+ parameters: ClawchatGetUserProfileSchema,
209
225
  async execute(_callId, params) {
210
- const p = params as ClawchatGetUserInfoParams;
226
+ const p = params as ClawchatGetUserProfileParams;
211
227
  return await withClient((c) => c.getUserInfo(p.userId));
212
228
  },
213
229
  },
214
- { name: "clawchat_get_user_info" },
230
+ { name: "clawchat_get_user_profile" },
215
231
  );
216
232
 
217
233
  api.registerTool(
218
234
  {
219
- name: "clawchat_list_friends",
220
- label: "List Friends",
221
- description: "List the agent's friends, paginated (page=1, pageSize=20 by default).",
222
- parameters: ClawchatListFriendsSchema,
235
+ name: "clawchat_list_account_friends",
236
+ label: "List ClawChat Account Friends",
237
+ description:
238
+ "List the configured ClawChat account's friends/contacts, paginated (page=1, pageSize=20 by default). " +
239
+ "TRIGGER — invoke when the user asks for this ClawChat account's friends, contacts, friend list, " +
240
+ "or asks to show more friends with pagination.",
241
+ parameters: ClawchatListAccountFriendsSchema,
223
242
  async execute(_callId, params) {
224
- const p = (params ?? {}) as ClawchatListFriendsParams;
243
+ const p = (params ?? {}) as ClawchatListAccountFriendsParams;
225
244
  return await withClient((c) =>
226
245
  c.listFriends({
227
246
  ...(p.page !== undefined ? { page: p.page } : { page: 1 }),
@@ -230,32 +249,31 @@ export function registerOpenclawClawlingTools(api: OpenClawPluginApi): void {
230
249
  );
231
250
  },
232
251
  },
233
- { name: "clawchat_list_friends" },
252
+ { name: "clawchat_list_account_friends" },
234
253
  );
235
254
 
236
255
  api.registerTool(
237
256
  {
238
- name: "clawchat_update_my_profile",
239
- label: "Update Profile",
257
+ name: "clawchat_update_account_profile",
258
+ label: "Update ClawChat Account Profile",
240
259
  description:
241
- "Update this agent's own ClawChat profile (nickname and/or avatar and/or bio). " +
242
- "TRIGGER — invoke this tool whenever the user's message matches ANY of: " +
243
- "(1) nickname/name change: 'change your name to X', 'your name is X', 'rename yourself to X', " +
244
- "'I'll call you X', 'from now on you are X', '你叫 X', '改名为 X', '我叫你 X', " +
245
- "'你的新名字是 X' → call with `nickname = X`; " +
246
- "(2) avatar change or generation: 'change your avatar', 'update your profile picture', " +
247
- "'generate a new avatar', 'use this image as your avatar', '换个头像', '生成头像', " +
248
- "'把头像改为 …' → first obtain the avatar URL (generate + upload via " +
249
- "`clawchat_upload_avatar`, OR use a provided URL directly), then call this tool " +
250
- "with `avatar_url = <url>`; " +
251
- "(3) bio/self-introduction change: 'update your bio', 'set your profile bio to X', " +
252
- "'change your self introduction', '把简介改成 X', '更新自我介绍', '个人简介改为 X' " +
260
+ "Update the configured ClawChat account profile (nickname and/or avatar and/or bio). " +
261
+ "TRIGGER — invoke this tool whenever the user's message explicitly asks to change the ClawChat account profile: " +
262
+ "(1) ClawChat account nickname/name change: 'change the ClawChat account nickname to X', " +
263
+ "'set this ClawChat account name to X', 'ClawChat 昵称改为 X', '账号昵称改成 X', '账号名字叫 X' " +
264
+ "→ call with `nickname = X`; " +
265
+ "(2) ClawChat account avatar/profile-picture change: 'change the ClawChat account avatar', " +
266
+ "'use this image as the ClawChat profile picture', 'ClawChat 头像改为 …', '账号头像换成 …' " +
267
+ "→ first obtain the avatar URL (upload via `clawchat_upload_avatar_image`, OR use a provided URL directly), " +
268
+ "then call this tool with `avatar_url = <url>`; " +
269
+ "(3) ClawChat account bio/self-introduction change: 'update the ClawChat bio', " +
270
+ "'set the ClawChat account self-introduction to X', 'ClawChat 简介改成 X', '账号简介改为 X', '个人简介改为 X' " +
253
271
  "→ call with `bio = X`. " +
254
272
  "You can pass `nickname`, `avatar_url`, and `bio` together in one call, or just one of them. " +
255
- "At least one of the three must be present.",
256
- parameters: ClawchatUpdateMyProfileSchema,
273
+ "At least one of the three must be present. Do not use this for OpenClaw agent persona changes unless the user explicitly refers to the ClawChat account.",
274
+ parameters: ClawchatUpdateAccountProfileSchema,
257
275
  async execute(_callId, params) {
258
- const p = (params ?? {}) as ClawchatUpdateMyProfileParams;
276
+ const p = (params ?? {}) as ClawchatUpdateAccountProfileParams;
259
277
  const patch: { nickname?: string; avatar_url?: string; bio?: string } = {};
260
278
  if (typeof p.nickname === "string") patch.nickname = p.nickname;
261
279
  if (typeof p.avatar_url === "string") patch.avatar_url = p.avatar_url;
@@ -268,19 +286,20 @@ export function registerOpenclawClawlingTools(api: OpenClawPluginApi): void {
268
286
  return await withClient((c): Promise<Profile> => c.updateMyProfile(patch));
269
287
  },
270
288
  },
271
- { name: "clawchat_update_my_profile" },
289
+ { name: "clawchat_update_account_profile" },
272
290
  );
273
291
 
274
292
  api.registerTool(
275
293
  {
276
- name: "clawchat_upload_avatar",
277
- label: "Upload Avatar",
294
+ name: "clawchat_upload_avatar_image",
295
+ label: "Upload ClawChat Avatar Image",
278
296
  description:
279
- "Upload a local avatar image to Clawling avatar storage (max 20MB) and return the public URL. " +
280
- "Use this before `clawchat_update_my_profile` when changing the profile picture.",
281
- parameters: ClawchatUploadAvatarSchema,
297
+ "Upload a local image file to ClawChat avatar storage (max 20MB) and return the hosted avatar URL. " +
298
+ "TRIGGER invoke when the user provides an absolute local image path and asks to upload it for the ClawChat account avatar/profile picture. " +
299
+ "This tool does not update or set the account avatar by itself; call `clawchat_update_account_profile` with `avatar_url` after this tool returns a URL.",
300
+ parameters: ClawchatUploadAvatarImageSchema,
282
301
  async execute(_callId, params) {
283
- const p = params as ClawchatUploadAvatarParams;
302
+ const p = params as ClawchatUploadAvatarImageParams;
284
303
  if (!p.filePath || !path.isAbsolute(p.filePath)) {
285
304
  return validationError("openclaw-clawchat: filePath must be an absolute local path");
286
305
  }
@@ -306,18 +325,20 @@ export function registerOpenclawClawlingTools(api: OpenClawPluginApi): void {
306
325
  return await withClient((c) => c.uploadAvatar({ buffer, filename, mime }));
307
326
  },
308
327
  },
309
- { name: "clawchat_upload_avatar" },
328
+ { name: "clawchat_upload_avatar_image" },
310
329
  );
311
330
 
312
331
  api.registerTool(
313
332
  {
314
- name: "clawchat_upload_file",
315
- label: "Upload File",
333
+ name: "clawchat_upload_media_file",
334
+ label: "Upload ClawChat Media File",
316
335
  description:
317
- "Upload a local file to Clawling media storage (max 20MB) and return the public URL.",
318
- parameters: ClawchatUploadFileSchema,
336
+ "Upload a local file or media file to ClawChat media storage (max 20MB) and return the public URL/shareable URL. " +
337
+ "TRIGGER — invoke when the user provides an absolute local file path and asks to upload, share, or create a ClawChat-accessible link for that file. " +
338
+ "Do not use this for account avatar changes; use `clawchat_upload_avatar_image` for avatar images.",
339
+ parameters: ClawchatUploadMediaFileSchema,
319
340
  async execute(_callId, params) {
320
- const p = params as ClawchatUploadFileParams;
341
+ const p = params as ClawchatUploadMediaFileParams;
321
342
  if (!p.filePath || !path.isAbsolute(p.filePath)) {
322
343
  return validationError("openclaw-clawchat: filePath must be an absolute local path");
323
344
  }
@@ -343,10 +364,10 @@ export function registerOpenclawClawlingTools(api: OpenClawPluginApi): void {
343
364
  return await withClient((c) => c.uploadMedia({ buffer, filename, mime }));
344
365
  },
345
366
  },
346
- { name: "clawchat_upload_file" },
367
+ { name: "clawchat_upload_media_file" },
347
368
  );
348
369
 
349
- api.logger.info?.(
350
- "openclaw-clawchat: registered 7 clawchat_* tools (activate, get_my_profile, get_user_info, list_friends, update_my_profile, upload_avatar, upload_file)",
370
+ api.logger.debug?.(
371
+ "openclaw-clawchat: registered 6 clawchat_* tools (get_account_profile, get_user_profile, list_account_friends, update_account_profile, upload_avatar_image, upload_media_file)",
351
372
  );
352
373
  }