@newbase-clawchat/openclaw-clawchat 2026.5.12-2 → 2026.5.12-21
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 +39 -17
- package/dist/index.js +3 -1
- package/dist/src/api-client.js +71 -12
- package/dist/src/api-types.test-d.js +10 -0
- package/dist/src/channel.js +5 -5
- package/dist/src/channel.setup.js +4 -17
- package/dist/src/clawchat-memory.js +290 -0
- package/dist/src/clawchat-metadata.js +235 -0
- package/dist/src/client.js +31 -93
- package/dist/src/commands.js +3 -3
- package/dist/src/config.js +58 -3
- package/dist/src/group-message-coalescer.js +107 -0
- package/dist/src/inbound.js +24 -28
- package/dist/src/login.runtime.js +82 -19
- package/dist/src/media-runtime.js +2 -3
- package/dist/src/message-mapper.js +1 -1
- package/dist/src/mock-transport.js +31 -0
- package/dist/src/outbound.js +281 -56
- package/dist/src/plugin-prompts.js +76 -0
- package/dist/src/profile-prompt.js +150 -0
- package/dist/src/profile-sync.js +169 -0
- package/dist/src/prompt-injection.js +25 -0
- package/dist/src/protocol-types.js +63 -0
- package/dist/src/protocol-types.typecheck.js +1 -0
- package/dist/src/protocol.js +2 -2
- package/dist/src/reply-dispatcher.js +143 -40
- package/dist/src/runtime.js +813 -109
- package/dist/src/storage.js +636 -0
- package/dist/src/tools-schema.js +70 -10
- package/dist/src/tools.js +600 -112
- package/dist/src/ws-alignment.js +8 -0
- package/dist/src/ws-client.js +588 -0
- package/index.ts +6 -1
- package/openclaw.plugin.json +44 -4
- package/package.json +4 -3
- package/prompts/platform.md +7 -0
- package/skills/clawchat/SKILL.md +90 -0
- package/src/api-client.test.ts +360 -15
- package/src/api-client.ts +127 -25
- package/src/api-types.test-d.ts +12 -0
- package/src/api-types.ts +71 -4
- package/src/buffered-stream.test.ts +1 -1
- package/src/buffered-stream.ts +1 -1
- package/src/channel.outbound.test.ts +270 -60
- package/src/channel.setup.ts +9 -18
- package/src/channel.test.ts +33 -25
- package/src/channel.ts +5 -7
- package/src/clawchat-memory.test.ts +372 -0
- package/src/clawchat-memory.ts +363 -0
- package/src/clawchat-metadata.test.ts +350 -0
- package/src/clawchat-metadata.ts +352 -0
- package/src/client.test.ts +57 -48
- package/src/client.ts +37 -129
- package/src/commands.test.ts +2 -2
- package/src/commands.ts +3 -3
- package/src/config.test.ts +169 -4
- package/src/config.ts +86 -6
- package/src/group-message-coalescer.test.ts +223 -0
- package/src/group-message-coalescer.ts +154 -0
- package/src/inbound.test.ts +106 -19
- package/src/inbound.ts +31 -35
- package/src/login.runtime.test.ts +294 -11
- package/src/login.runtime.ts +90 -21
- package/src/manifest.test.ts +86 -14
- package/src/media-runtime.test.ts +31 -2
- package/src/media-runtime.ts +7 -10
- 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 +811 -95
- package/src/outbound.ts +332 -65
- package/src/plugin-entry.test.ts +3 -1
- package/src/plugin-prompts.test.ts +78 -0
- package/src/plugin-prompts.ts +92 -0
- package/src/profile-prompt.test.ts +435 -0
- package/src/profile-prompt.ts +208 -0
- package/src/profile-sync.test.ts +611 -0
- package/src/profile-sync.ts +268 -0
- package/src/prompt-injection.test.ts +39 -0
- package/src/prompt-injection.ts +45 -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.ts +2 -2
- package/src/reply-dispatcher.test.ts +720 -135
- package/src/reply-dispatcher.ts +174 -42
- package/src/runtime.test.ts +3884 -337
- package/src/runtime.ts +956 -128
- package/src/storage.test.ts +692 -0
- package/src/storage.ts +989 -0
- package/src/streaming.test.ts +1 -1
- package/src/streaming.ts +1 -1
- package/src/tools-schema.ts +115 -13
- package/src/tools.test.ts +501 -10
- package/src/tools.ts +739 -133
- package/src/ws-alignment.ts +9 -0
- package/src/ws-client.test.ts +1218 -0
- package/src/ws-client.ts +662 -0
package/dist/src/tools.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { createOpenclawClawlingApiClient } from "./api-client.js";
|
|
4
|
-
import { ClawlingApiError } from "./api-types.js";
|
|
4
|
+
import { ClawlingApiError, } from "./api-types.js";
|
|
5
5
|
import { resolveOpenclawClawlingAccount } from "./config.js";
|
|
6
|
-
import {
|
|
6
|
+
import { clawChatDbPathForStateDir, getClawChatStore, } from "./storage.js";
|
|
7
|
+
import { editClawChatMemoryBody, readClawChatMemoryFile, resolveClawChatMemoryPath, writeClawChatMemoryBody, } from "./clawchat-memory.js";
|
|
8
|
+
import { pullGroupMetadata, pullOwnerMetadata, pullUserMetadata, pushMetadata, updateMetadata, } from "./clawchat-metadata.js";
|
|
9
|
+
import { ClawchatGetAccountProfileSchema, ClawchatGetConversationSchema, ClawchatGetUserProfileSchema, ClawchatMemoryEditSchema, ClawchatMemoryReadSchema, ClawchatMemoryWriteSchema, ClawchatMetadataSyncSchema, ClawchatMetadataUpdateSchema, ClawchatCreateMomentCommentSchema, ClawchatCreateMomentSchema, ClawchatDeleteMomentCommentSchema, ClawchatDeleteMomentSchema, ClawchatListAccountFriendsSchema, ClawchatListConversationsSchema, ClawchatListMomentsSchema, ClawchatReplyMomentCommentSchema, ClawchatSearchUsersSchema, ClawchatToggleMomentReactionSchema, ClawchatUpdateAccountProfileSchema, ClawchatUploadAvatarImageSchema, ClawchatUploadMediaFileSchema, } from "./tools-schema.js";
|
|
7
10
|
const MAX_UPLOAD_BYTES = 20 * 1024 * 1024;
|
|
8
11
|
function jsonResponse(data) {
|
|
9
12
|
return {
|
|
@@ -53,14 +56,62 @@ const MIME_BY_EXT = {
|
|
|
53
56
|
".webm": "video/webm",
|
|
54
57
|
};
|
|
55
58
|
const DIRECT_TOOL_GUARD = "Use this registered ClawChat plugin tool directly. Do not use execute, shell commands, Python scripts, curl, handwritten API clients, generic fallback tools, or direct ClawChat HTTP calls for this ClawChat API action.";
|
|
59
|
+
const MEMORY_READ_DEFAULT_LIMIT = 20_000;
|
|
60
|
+
const MEMORY_READ_MAX_LIMIT = 100_000;
|
|
56
61
|
function toolDescription(...parts) {
|
|
57
62
|
return `${parts.join("")} ${DIRECT_TOOL_GUARD}`;
|
|
58
63
|
}
|
|
64
|
+
function memoryToolDescription(...parts) {
|
|
65
|
+
return parts.join("");
|
|
66
|
+
}
|
|
59
67
|
function inferMimeFromPath(filePath) {
|
|
60
68
|
const ext = path.extname(filePath).toLowerCase();
|
|
61
69
|
return MIME_BY_EXT[ext] ?? "application/octet-stream";
|
|
62
70
|
}
|
|
63
|
-
|
|
71
|
+
function parseTimestamp(value) {
|
|
72
|
+
if (typeof value !== "string")
|
|
73
|
+
return null;
|
|
74
|
+
const parsed = Date.parse(value);
|
|
75
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
76
|
+
}
|
|
77
|
+
function upsertConversationSummaryCache(store, accountId, conversation) {
|
|
78
|
+
if (!store?.upsertConversationSummary)
|
|
79
|
+
return;
|
|
80
|
+
store.upsertConversationSummary({
|
|
81
|
+
platform: "openclaw",
|
|
82
|
+
accountId,
|
|
83
|
+
conversationId: conversation.id,
|
|
84
|
+
conversationType: conversation.type,
|
|
85
|
+
lastSeenAt: parseTimestamp(conversation.updated_at),
|
|
86
|
+
lastRefreshedAt: Date.now(),
|
|
87
|
+
raw: conversation,
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
function upsertConversationDetailsCache(store, accountId, conversation) {
|
|
91
|
+
if (!store?.upsertConversationDetails)
|
|
92
|
+
return;
|
|
93
|
+
const refreshedAt = Date.now();
|
|
94
|
+
store.upsertConversationDetails({
|
|
95
|
+
platform: "openclaw",
|
|
96
|
+
accountId,
|
|
97
|
+
conversationId: conversation.id,
|
|
98
|
+
conversationType: conversation.type,
|
|
99
|
+
lastSeenAt: parseTimestamp(conversation.updated_at),
|
|
100
|
+
lastRefreshedAt: refreshedAt,
|
|
101
|
+
raw: conversation,
|
|
102
|
+
members: conversation.participants.map((participant) => ({
|
|
103
|
+
userId: participant.user_id,
|
|
104
|
+
role: participant.role,
|
|
105
|
+
raw: participant,
|
|
106
|
+
lastSeenAt: parseTimestamp(participant.joined_at),
|
|
107
|
+
})),
|
|
108
|
+
membersComplete: true,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
function isConversationNotFound(err) {
|
|
112
|
+
return err.meta?.status === 404 || err.meta?.code === 404 || err.meta?.code === 40401;
|
|
113
|
+
}
|
|
114
|
+
export function registerOpenclawClawlingTools(api, options = {}) {
|
|
64
115
|
if (!api.config) {
|
|
65
116
|
api.logger.debug?.("openclaw-clawchat: api.config missing; skipping tool registration");
|
|
66
117
|
return;
|
|
@@ -69,6 +120,81 @@ export function registerOpenclawClawlingTools(api) {
|
|
|
69
120
|
function resolveCurrent() {
|
|
70
121
|
return resolveOpenclawClawlingAccount(api.config);
|
|
71
122
|
}
|
|
123
|
+
function resolveStore() {
|
|
124
|
+
if ("store" in options)
|
|
125
|
+
return options.store ?? null;
|
|
126
|
+
try {
|
|
127
|
+
const stateDir = api.runtime.state?.resolveStateDir?.();
|
|
128
|
+
return getClawChatStore({
|
|
129
|
+
...(stateDir ? { dbPath: clawChatDbPathForStateDir(stateDir) } : {}),
|
|
130
|
+
log: { error: (message) => api.logger.error?.(message) },
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
api.logger.error?.("openclaw-clawchat sqlite tool persistence unavailable; continuing");
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
function detailsError(details) {
|
|
139
|
+
if (!details || typeof details !== "object")
|
|
140
|
+
return null;
|
|
141
|
+
const raw = details;
|
|
142
|
+
if (typeof raw.error !== "string" || !raw.error)
|
|
143
|
+
return null;
|
|
144
|
+
return typeof raw.message === "string" && raw.message
|
|
145
|
+
? `${raw.error}: ${raw.message}`
|
|
146
|
+
: raw.error;
|
|
147
|
+
}
|
|
148
|
+
function persistToolCall(input) {
|
|
149
|
+
try {
|
|
150
|
+
resolveStore()?.recordToolCall(input);
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
api.logger.error?.("openclaw-clawchat sqlite tool call insert failed; continuing");
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
function persistConversationCache(fn) {
|
|
157
|
+
try {
|
|
158
|
+
const store = resolveStore();
|
|
159
|
+
if (store)
|
|
160
|
+
fn(store);
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
api.logger.error?.("openclaw-clawchat sqlite conversation cache update failed; continuing");
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
async function recordClawchatToolCall(toolName, params, fn) {
|
|
167
|
+
const startedAt = Date.now();
|
|
168
|
+
const account = resolveCurrent();
|
|
169
|
+
try {
|
|
170
|
+
const result = await fn();
|
|
171
|
+
const details = result.details ?? result;
|
|
172
|
+
persistToolCall({
|
|
173
|
+
platform: "openclaw",
|
|
174
|
+
accountId: account.accountId,
|
|
175
|
+
toolName,
|
|
176
|
+
args: params ?? {},
|
|
177
|
+
result: details,
|
|
178
|
+
error: detailsError(details),
|
|
179
|
+
startedAt,
|
|
180
|
+
endedAt: Date.now(),
|
|
181
|
+
});
|
|
182
|
+
return result;
|
|
183
|
+
}
|
|
184
|
+
catch (err) {
|
|
185
|
+
persistToolCall({
|
|
186
|
+
platform: "openclaw",
|
|
187
|
+
accountId: account.accountId,
|
|
188
|
+
toolName,
|
|
189
|
+
args: params ?? {},
|
|
190
|
+
result: null,
|
|
191
|
+
error: err instanceof Error ? err.message : String(err),
|
|
192
|
+
startedAt,
|
|
193
|
+
endedAt: Date.now(),
|
|
194
|
+
});
|
|
195
|
+
throw err;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
72
198
|
function buildClient() {
|
|
73
199
|
const acct = resolveCurrent();
|
|
74
200
|
// `baseUrl` always resolves via the built-in default in config.ts, so we
|
|
@@ -102,6 +228,287 @@ export function registerOpenclawClawlingTools(api) {
|
|
|
102
228
|
}
|
|
103
229
|
})();
|
|
104
230
|
}
|
|
231
|
+
function workspaceRoot(ctx) {
|
|
232
|
+
const root = typeof ctx.workspaceDir === "string" ? ctx.workspaceDir.trim() : "";
|
|
233
|
+
return root || null;
|
|
234
|
+
}
|
|
235
|
+
function targetFromParams(params) {
|
|
236
|
+
return { targetType: params.targetType, targetId: params.targetId };
|
|
237
|
+
}
|
|
238
|
+
function validateTarget(root, params) {
|
|
239
|
+
try {
|
|
240
|
+
resolveClawChatMemoryPath(root, targetFromParams(params));
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
catch (err) {
|
|
244
|
+
return err instanceof Error ? err.message : String(err);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
function normalizeReadBounds(params) {
|
|
248
|
+
const offset = typeof params.offset === "number" && Number.isFinite(params.offset)
|
|
249
|
+
? Math.max(0, Math.trunc(params.offset))
|
|
250
|
+
: 0;
|
|
251
|
+
const limit = typeof params.limit === "number" && Number.isFinite(params.limit)
|
|
252
|
+
? Math.min(MEMORY_READ_MAX_LIMIT, Math.max(1, Math.trunc(params.limit)))
|
|
253
|
+
: MEMORY_READ_DEFAULT_LIMIT;
|
|
254
|
+
return { offset, limit };
|
|
255
|
+
}
|
|
256
|
+
function metadataPatchAllowedKeys(targetType) {
|
|
257
|
+
if (targetType === "group")
|
|
258
|
+
return ["title", "description"];
|
|
259
|
+
if (targetType === "owner")
|
|
260
|
+
return ["nickname", "avatar_url", "bio", "behavior"];
|
|
261
|
+
return ["nickname", "avatar_url", "bio"];
|
|
262
|
+
}
|
|
263
|
+
function validateMetadataUpdatePatch(params) {
|
|
264
|
+
const patch = params.patch;
|
|
265
|
+
if (!patch || typeof patch !== "object" || Array.isArray(patch)) {
|
|
266
|
+
return "openclaw-clawchat: metadata patch is required";
|
|
267
|
+
}
|
|
268
|
+
const keys = Object.keys(patch);
|
|
269
|
+
if (keys.length === 0) {
|
|
270
|
+
return "openclaw-clawchat: metadata patch must include at least one mutable field";
|
|
271
|
+
}
|
|
272
|
+
const allowed = new Set(metadataPatchAllowedKeys(params.targetType));
|
|
273
|
+
const invalid = keys.filter((key) => !allowed.has(key));
|
|
274
|
+
if (invalid.length > 0) {
|
|
275
|
+
return `openclaw-clawchat: metadata patch contains unsupported field(s): ${invalid.join(", ")}`;
|
|
276
|
+
}
|
|
277
|
+
const nonString = keys.filter((key) => typeof patch[key] !== "string");
|
|
278
|
+
if (nonString.length > 0) {
|
|
279
|
+
return `openclaw-clawchat: metadata patch field(s) must be strings: ${nonString.join(", ")}`;
|
|
280
|
+
}
|
|
281
|
+
return null;
|
|
282
|
+
}
|
|
283
|
+
function isUnsafeMemoryPathError(message) {
|
|
284
|
+
return /Unsafe clawchat memory|Resolved clawchat memory path is outside root/i.test(message);
|
|
285
|
+
}
|
|
286
|
+
function memoryToolError(err) {
|
|
287
|
+
if (err instanceof ClawlingApiError)
|
|
288
|
+
return apiError(err);
|
|
289
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
290
|
+
if (isUnsafeMemoryPathError(message)) {
|
|
291
|
+
return validationError("openclaw-clawchat: unsafe ClawChat memory target");
|
|
292
|
+
}
|
|
293
|
+
return validationError(message);
|
|
294
|
+
}
|
|
295
|
+
function registerMemoryTools() {
|
|
296
|
+
api.registerTool((ctx) => ({
|
|
297
|
+
name: "clawchat_memory_read",
|
|
298
|
+
label: "Read ClawChat Memory",
|
|
299
|
+
description: memoryToolDescription("Read a ClawChat memory Markdown file by explicit targetType and targetId. " +
|
|
300
|
+
"Use this when you need existing owner, user, or group memory content. " +
|
|
301
|
+
"Do not guess ids from names. " +
|
|
302
|
+
"This reads metadata and agent-authored body; it does not contact the ClawChat server."),
|
|
303
|
+
parameters: ClawchatMemoryReadSchema,
|
|
304
|
+
async execute(_callId, params) {
|
|
305
|
+
const root = workspaceRoot(ctx);
|
|
306
|
+
if (!root) {
|
|
307
|
+
return validationError("openclaw-clawchat: OpenClaw workspaceDir is required");
|
|
308
|
+
}
|
|
309
|
+
try {
|
|
310
|
+
const p = params;
|
|
311
|
+
const targetError = validateTarget(root, p);
|
|
312
|
+
if (targetError)
|
|
313
|
+
return memoryToolError(new Error(targetError));
|
|
314
|
+
const { offset, limit } = normalizeReadBounds(p);
|
|
315
|
+
const file = await readClawChatMemoryFile(root, targetFromParams(p));
|
|
316
|
+
if (!file.exists) {
|
|
317
|
+
return jsonResponse({
|
|
318
|
+
exists: false,
|
|
319
|
+
content: "",
|
|
320
|
+
offset,
|
|
321
|
+
limit,
|
|
322
|
+
truncated: false,
|
|
323
|
+
totalLength: 0,
|
|
324
|
+
nextOffset: null,
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
const totalLength = file.raw.length;
|
|
328
|
+
const end = Math.min(totalLength, offset + limit);
|
|
329
|
+
const truncated = end < totalLength;
|
|
330
|
+
return jsonResponse({
|
|
331
|
+
exists: true,
|
|
332
|
+
content: file.raw.slice(offset, end),
|
|
333
|
+
offset,
|
|
334
|
+
limit,
|
|
335
|
+
truncated,
|
|
336
|
+
totalLength,
|
|
337
|
+
nextOffset: truncated ? end : null,
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
catch (err) {
|
|
341
|
+
return memoryToolError(err);
|
|
342
|
+
}
|
|
343
|
+
},
|
|
344
|
+
}), { name: "clawchat_memory_read" });
|
|
345
|
+
api.registerTool((ctx) => ({
|
|
346
|
+
name: "clawchat_memory_write",
|
|
347
|
+
label: "Write ClawChat Memory",
|
|
348
|
+
description: memoryToolDescription("Append to or replace only the agent-authored body of a ClawChat memory Markdown file by explicit targetType and targetId. " +
|
|
349
|
+
"This never modifies the metadata block. " +
|
|
350
|
+
"Do not use this to write or refresh ClawChat profile/metadata fields such as nickname, avatar_url, bio, profile_type, title, description, or behavior. " +
|
|
351
|
+
"When the user asks to refresh, sync, or update local ClawChat owner/user/group profile information, use clawchat_metadata_sync with direction=pull instead. " +
|
|
352
|
+
"Use append for new long-term memory notes and replace only when intentionally rewriting the whole body."),
|
|
353
|
+
parameters: ClawchatMemoryWriteSchema,
|
|
354
|
+
async execute(_callId, params) {
|
|
355
|
+
const root = workspaceRoot(ctx);
|
|
356
|
+
if (!root) {
|
|
357
|
+
return validationError("openclaw-clawchat: OpenClaw workspaceDir is required");
|
|
358
|
+
}
|
|
359
|
+
try {
|
|
360
|
+
const p = params;
|
|
361
|
+
const targetError = validateTarget(root, p);
|
|
362
|
+
if (targetError)
|
|
363
|
+
return memoryToolError(new Error(targetError));
|
|
364
|
+
await writeClawChatMemoryBody(root, targetFromParams(p), p.mode, p.content);
|
|
365
|
+
return jsonResponse({ ok: true, targetType: p.targetType, targetId: p.targetId, mode: p.mode });
|
|
366
|
+
}
|
|
367
|
+
catch (err) {
|
|
368
|
+
return memoryToolError(err);
|
|
369
|
+
}
|
|
370
|
+
},
|
|
371
|
+
}), { name: "clawchat_memory_write" });
|
|
372
|
+
api.registerTool((ctx) => ({
|
|
373
|
+
name: "clawchat_memory_edit",
|
|
374
|
+
label: "Edit ClawChat Memory",
|
|
375
|
+
description: memoryToolDescription("Replace exactly one existing text span in the agent-authored body of a ClawChat memory Markdown file. " +
|
|
376
|
+
"This never modifies the metadata block. " +
|
|
377
|
+
"Do not use this to edit ClawChat profile/metadata fields such as nickname, avatar_url, bio, profile_type, title, description, or behavior. " +
|
|
378
|
+
"Use clawchat_metadata_sync or clawchat_metadata_update for metadata. " +
|
|
379
|
+
"The oldText must match exactly once; use read first when unsure."),
|
|
380
|
+
parameters: ClawchatMemoryEditSchema,
|
|
381
|
+
async execute(_callId, params) {
|
|
382
|
+
const root = workspaceRoot(ctx);
|
|
383
|
+
if (!root) {
|
|
384
|
+
return validationError("openclaw-clawchat: OpenClaw workspaceDir is required");
|
|
385
|
+
}
|
|
386
|
+
try {
|
|
387
|
+
const p = params;
|
|
388
|
+
const targetError = validateTarget(root, p);
|
|
389
|
+
if (targetError)
|
|
390
|
+
return memoryToolError(new Error(targetError));
|
|
391
|
+
await editClawChatMemoryBody(root, targetFromParams(p), p.oldText, p.newText);
|
|
392
|
+
return jsonResponse({ ok: true, targetType: p.targetType, targetId: p.targetId });
|
|
393
|
+
}
|
|
394
|
+
catch (err) {
|
|
395
|
+
return memoryToolError(err);
|
|
396
|
+
}
|
|
397
|
+
},
|
|
398
|
+
}), { name: "clawchat_memory_edit" });
|
|
399
|
+
api.registerTool((ctx) => ({
|
|
400
|
+
name: "clawchat_metadata_sync",
|
|
401
|
+
label: "Sync ClawChat Metadata",
|
|
402
|
+
description: memoryToolDescription("Synchronize the ClawChat metadata block for an explicit owner, user, or group target. " +
|
|
403
|
+
"Use direction=pull when the user asks to refresh, sync, or update local ClawChat profile information, user information, group information, or owner behavior from the server; this fetches the authoritative server record and rewrites only the metadata block. " +
|
|
404
|
+
"Use direction=push only to push selected existing local metadata fields to the server, then refresh the metadata block from the server response. " +
|
|
405
|
+
"For direction=push, pass non-empty fields containing only pushable metadata field names. " +
|
|
406
|
+
"This does not modify the agent-authored body. " +
|
|
407
|
+
"Do not combine clawchat_get_user_profile with clawchat_memory_write to update local profile metadata."),
|
|
408
|
+
parameters: ClawchatMetadataSyncSchema,
|
|
409
|
+
async execute(_callId, params) {
|
|
410
|
+
const root = workspaceRoot(ctx);
|
|
411
|
+
if (!root) {
|
|
412
|
+
return validationError("openclaw-clawchat: OpenClaw workspaceDir is required");
|
|
413
|
+
}
|
|
414
|
+
const built = buildClient();
|
|
415
|
+
if (!built.ok)
|
|
416
|
+
return built.error;
|
|
417
|
+
try {
|
|
418
|
+
const p = params;
|
|
419
|
+
const targetError = validateTarget(root, p);
|
|
420
|
+
if (targetError)
|
|
421
|
+
return memoryToolError(new Error(targetError));
|
|
422
|
+
const account = resolveCurrent();
|
|
423
|
+
if (p.direction === "push") {
|
|
424
|
+
if (!Array.isArray(p.fields) || p.fields.length === 0) {
|
|
425
|
+
return validationError("openclaw-clawchat: fields are required for metadata push");
|
|
426
|
+
}
|
|
427
|
+
if (p.targetType === "owner" && !account.agentId) {
|
|
428
|
+
return configError("openclaw-clawchat: agentId is required");
|
|
429
|
+
}
|
|
430
|
+
if (p.targetType === "user" && !account.userId) {
|
|
431
|
+
return configError("openclaw-clawchat: userId is required");
|
|
432
|
+
}
|
|
433
|
+
return jsonResponse(await pushMetadata({
|
|
434
|
+
memoryRoot: root,
|
|
435
|
+
targetType: p.targetType,
|
|
436
|
+
targetId: p.targetId,
|
|
437
|
+
fields: p.fields,
|
|
438
|
+
agentId: account.agentId,
|
|
439
|
+
accountUserId: account.userId,
|
|
440
|
+
api: built.client,
|
|
441
|
+
}));
|
|
442
|
+
}
|
|
443
|
+
if (p.targetType === "owner") {
|
|
444
|
+
if (!account.agentId)
|
|
445
|
+
return configError("openclaw-clawchat: agentId is required");
|
|
446
|
+
return jsonResponse(await pullOwnerMetadata({
|
|
447
|
+
memoryRoot: root,
|
|
448
|
+
agentId: account.agentId,
|
|
449
|
+
accountUserId: account.userId,
|
|
450
|
+
accountOwnerUserId: account.ownerUserId || undefined,
|
|
451
|
+
api: built.client,
|
|
452
|
+
}));
|
|
453
|
+
}
|
|
454
|
+
if (p.targetType === "user") {
|
|
455
|
+
return jsonResponse(await pullUserMetadata({ memoryRoot: root, userId: p.targetId, api: built.client }));
|
|
456
|
+
}
|
|
457
|
+
return jsonResponse(await pullGroupMetadata({ memoryRoot: root, groupId: p.targetId, api: built.client }));
|
|
458
|
+
}
|
|
459
|
+
catch (err) {
|
|
460
|
+
return memoryToolError(err);
|
|
461
|
+
}
|
|
462
|
+
},
|
|
463
|
+
}), { name: "clawchat_metadata_sync" });
|
|
464
|
+
api.registerTool((ctx) => ({
|
|
465
|
+
name: "clawchat_metadata_update",
|
|
466
|
+
label: "Update ClawChat Metadata",
|
|
467
|
+
description: memoryToolDescription("Update ClawChat server metadata for an explicit owner, connected user account, or group target, then refresh the local metadata block from the server response. " +
|
|
468
|
+
"Use this only when the user wants to change server-side metadata fields. " +
|
|
469
|
+
"To refresh local metadata from the server without changing the server, use clawchat_metadata_sync with direction=pull. " +
|
|
470
|
+
"This always pushes to the server first and does not modify the agent-authored body."),
|
|
471
|
+
parameters: ClawchatMetadataUpdateSchema,
|
|
472
|
+
async execute(_callId, params) {
|
|
473
|
+
const root = workspaceRoot(ctx);
|
|
474
|
+
if (!root) {
|
|
475
|
+
return validationError("openclaw-clawchat: OpenClaw workspaceDir is required");
|
|
476
|
+
}
|
|
477
|
+
const built = buildClient();
|
|
478
|
+
if (!built.ok)
|
|
479
|
+
return built.error;
|
|
480
|
+
try {
|
|
481
|
+
const p = params;
|
|
482
|
+
const targetError = validateTarget(root, p);
|
|
483
|
+
if (targetError)
|
|
484
|
+
return memoryToolError(new Error(targetError));
|
|
485
|
+
const patchError = validateMetadataUpdatePatch(p);
|
|
486
|
+
if (patchError)
|
|
487
|
+
return validationError(patchError);
|
|
488
|
+
const account = resolveCurrent();
|
|
489
|
+
if (p.targetType === "owner" && !account.agentId) {
|
|
490
|
+
return configError("openclaw-clawchat: agentId is required");
|
|
491
|
+
}
|
|
492
|
+
if (p.targetType === "user" && !account.userId) {
|
|
493
|
+
return configError("openclaw-clawchat: userId is required");
|
|
494
|
+
}
|
|
495
|
+
return jsonResponse(await updateMetadata({
|
|
496
|
+
memoryRoot: root,
|
|
497
|
+
targetType: p.targetType,
|
|
498
|
+
targetId: p.targetId,
|
|
499
|
+
agentId: account.agentId,
|
|
500
|
+
accountUserId: account.userId,
|
|
501
|
+
patch: p.patch,
|
|
502
|
+
api: built.client,
|
|
503
|
+
}));
|
|
504
|
+
}
|
|
505
|
+
catch (err) {
|
|
506
|
+
return memoryToolError(err);
|
|
507
|
+
}
|
|
508
|
+
},
|
|
509
|
+
}), { name: "clawchat_metadata_update" });
|
|
510
|
+
}
|
|
511
|
+
registerMemoryTools();
|
|
105
512
|
api.registerTool({
|
|
106
513
|
name: "clawchat_get_account_profile",
|
|
107
514
|
label: "Get ClawChat Account Profile",
|
|
@@ -112,8 +519,8 @@ export function registerOpenclawClawlingTools(api) {
|
|
|
112
519
|
"'当前 ClawChat 账号资料', or 'ClawChat 昵称头像简介'. " +
|
|
113
520
|
"Do not frame this as a human user's personal account."),
|
|
114
521
|
parameters: ClawchatGetAccountProfileSchema,
|
|
115
|
-
async execute(_callId,
|
|
116
|
-
return await withClient((c) => c.getMyProfile());
|
|
522
|
+
async execute(_callId, params) {
|
|
523
|
+
return await recordClawchatToolCall("clawchat_get_account_profile", params, async () => withClient((c) => c.getMyProfile()));
|
|
117
524
|
},
|
|
118
525
|
}, { name: "clawchat_get_account_profile" });
|
|
119
526
|
api.registerTool({
|
|
@@ -122,26 +529,26 @@ export function registerOpenclawClawlingTools(api) {
|
|
|
122
529
|
description: toolDescription("Fetch a ClawChat user's public profile by userId. " +
|
|
123
530
|
"TRIGGER — invoke when the user asks to look up, view, or inspect a specific ClawChat user's public profile " +
|
|
124
531
|
"and provides a concrete userId. Do not guess or infer userId from a nickname/display name. " +
|
|
532
|
+
"This is a read-only lookup and does not update local ClawChat Memory files. " +
|
|
533
|
+
"When the user asks to refresh, sync, or update that user's local profile information, call clawchat_metadata_sync with targetType=user, that targetId, and direction=pull. " +
|
|
125
534
|
"Use `clawchat_get_account_profile` for the agent's own connected ClawChat account unless an explicit userId is provided."),
|
|
126
535
|
parameters: ClawchatGetUserProfileSchema,
|
|
127
536
|
async execute(_callId, params) {
|
|
128
|
-
|
|
129
|
-
|
|
537
|
+
return await recordClawchatToolCall("clawchat_get_user_profile", params, async () => {
|
|
538
|
+
const p = params;
|
|
539
|
+
return await withClient((c) => c.getUserInfo(p.userId));
|
|
540
|
+
});
|
|
130
541
|
},
|
|
131
542
|
}, { name: "clawchat_get_user_profile" });
|
|
132
543
|
api.registerTool({
|
|
133
544
|
name: "clawchat_list_account_friends",
|
|
134
545
|
label: "List ClawChat Account Friends",
|
|
135
|
-
description: toolDescription("List friends/contacts of the agent's connected ClawChat account (the configured ClawChat account)
|
|
546
|
+
description: toolDescription("List friends/contacts of the agent's connected ClawChat account (the configured ClawChat account). " +
|
|
136
547
|
"These are the agent's ClawChat-platform contacts. " +
|
|
137
|
-
"TRIGGER — invoke when the user asks for this ClawChat account's friends, contacts, friend list
|
|
548
|
+
"TRIGGER — invoke when the user asks for this ClawChat account's friends, contacts, or friend list."),
|
|
138
549
|
parameters: ClawchatListAccountFriendsSchema,
|
|
139
550
|
async execute(_callId, params) {
|
|
140
|
-
|
|
141
|
-
return await withClient((c) => c.listFriends({
|
|
142
|
-
...(p.page !== undefined ? { page: p.page } : { page: 1 }),
|
|
143
|
-
...(p.pageSize !== undefined ? { pageSize: p.pageSize } : { pageSize: 20 }),
|
|
144
|
-
}));
|
|
551
|
+
return await recordClawchatToolCall("clawchat_list_account_friends", params, async () => withClient((c) => c.listFriends()));
|
|
145
552
|
},
|
|
146
553
|
}, { name: "clawchat_list_account_friends" });
|
|
147
554
|
api.registerTool({
|
|
@@ -152,13 +559,74 @@ export function registerOpenclawClawlingTools(api) {
|
|
|
152
559
|
"Empty q returns no users. Use this tool before fetching a profile when the user only provides a nickname or search term; do not guess a userId from the query text."),
|
|
153
560
|
parameters: ClawchatSearchUsersSchema,
|
|
154
561
|
async execute(_callId, params) {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
562
|
+
return await recordClawchatToolCall("clawchat_search_users", params, async () => {
|
|
563
|
+
const p = (params ?? {});
|
|
564
|
+
return await withClient((c) => c.searchUsers({
|
|
565
|
+
...(typeof p.q === "string" ? { q: p.q } : {}),
|
|
566
|
+
...(typeof p.limit === "number" ? { limit: p.limit } : {}),
|
|
567
|
+
}));
|
|
568
|
+
});
|
|
160
569
|
},
|
|
161
570
|
}, { name: "clawchat_search_users" });
|
|
571
|
+
api.registerTool({
|
|
572
|
+
name: "clawchat_list_conversations",
|
|
573
|
+
label: "List ClawChat Conversations",
|
|
574
|
+
description: toolDescription("List ClawChat direct and group conversations visible to the configured account. " +
|
|
575
|
+
"TRIGGER - invoke when the user asks to list, browse, inspect, or paginate ClawChat conversations, chats, groups, or direct messages. " +
|
|
576
|
+
"This is read-only and does not create, update, leave, dissolve, or administer conversations."),
|
|
577
|
+
parameters: ClawchatListConversationsSchema,
|
|
578
|
+
async execute(_callId, params) {
|
|
579
|
+
return await recordClawchatToolCall("clawchat_list_conversations", params, async () => {
|
|
580
|
+
const p = (params ?? {});
|
|
581
|
+
const account = resolveCurrent();
|
|
582
|
+
return await withClient(async (c) => {
|
|
583
|
+
const data = await c.listConversations({
|
|
584
|
+
...(typeof p.before === "string" ? { before: p.before } : {}),
|
|
585
|
+
...(typeof p.limit === "number" ? { limit: p.limit } : {}),
|
|
586
|
+
});
|
|
587
|
+
for (const conversation of data.conversations) {
|
|
588
|
+
persistConversationCache((store) => upsertConversationSummaryCache(store, account.accountId, conversation));
|
|
589
|
+
}
|
|
590
|
+
return data;
|
|
591
|
+
});
|
|
592
|
+
});
|
|
593
|
+
},
|
|
594
|
+
}, { name: "clawchat_list_conversations" });
|
|
595
|
+
api.registerTool({
|
|
596
|
+
name: "clawchat_get_conversation",
|
|
597
|
+
label: "Get ClawChat Conversation",
|
|
598
|
+
description: toolDescription("Fetch read-only ClawChat conversation details, including group membership when returned by the API. " +
|
|
599
|
+
"TRIGGER - invoke when the user asks to inspect a specific ClawChat conversation or group and provides a concrete conversationId. " +
|
|
600
|
+
"This is read-only and does not create, update, leave, dissolve, or administer conversations."),
|
|
601
|
+
parameters: ClawchatGetConversationSchema,
|
|
602
|
+
async execute(_callId, params) {
|
|
603
|
+
return await recordClawchatToolCall("clawchat_get_conversation", params, async () => {
|
|
604
|
+
const p = params;
|
|
605
|
+
const account = resolveCurrent();
|
|
606
|
+
const built = buildClient();
|
|
607
|
+
if (!built.ok)
|
|
608
|
+
return built.error;
|
|
609
|
+
try {
|
|
610
|
+
const data = await built.client.getConversation(p.conversationId);
|
|
611
|
+
persistConversationCache((store) => upsertConversationDetailsCache(store, account.accountId, data.conversation));
|
|
612
|
+
return jsonResponse(data);
|
|
613
|
+
}
|
|
614
|
+
catch (err) {
|
|
615
|
+
if (err instanceof ClawlingApiError) {
|
|
616
|
+
if (isConversationNotFound(err)) {
|
|
617
|
+
persistConversationCache((store) => store.deleteConversationCache?.({
|
|
618
|
+
platform: "openclaw",
|
|
619
|
+
accountId: account.accountId,
|
|
620
|
+
conversationId: p.conversationId,
|
|
621
|
+
}));
|
|
622
|
+
}
|
|
623
|
+
return apiError(err);
|
|
624
|
+
}
|
|
625
|
+
return genericError(err);
|
|
626
|
+
}
|
|
627
|
+
});
|
|
628
|
+
},
|
|
629
|
+
}, { name: "clawchat_get_conversation" });
|
|
162
630
|
api.registerTool({
|
|
163
631
|
name: "clawchat_list_moments",
|
|
164
632
|
label: "List ClawChat Moments",
|
|
@@ -167,11 +635,13 @@ export function registerOpenclawClawlingTools(api) {
|
|
|
167
635
|
"Use before/comment/reaction/delete actions when the user needs to choose a moment id. This is a friends-only feed endpoint, not a global public timeline."),
|
|
168
636
|
parameters: ClawchatListMomentsSchema,
|
|
169
637
|
async execute(_callId, params) {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
638
|
+
return await recordClawchatToolCall("clawchat_list_moments", params, async () => {
|
|
639
|
+
const p = (params ?? {});
|
|
640
|
+
return await withClient((c) => c.listMoments({
|
|
641
|
+
...(typeof p.before === "number" ? { before: p.before } : {}),
|
|
642
|
+
...(typeof p.limit === "number" ? { limit: p.limit } : {}),
|
|
643
|
+
}));
|
|
644
|
+
});
|
|
175
645
|
},
|
|
176
646
|
}, { name: "clawchat_list_moments" });
|
|
177
647
|
api.registerTool({
|
|
@@ -182,16 +652,18 @@ export function registerOpenclawClawlingTools(api) {
|
|
|
182
652
|
"At least one of text or images must be present. For local image files, upload first with the appropriate media upload tool and pass the returned URLs in images; do not pass local file paths as images."),
|
|
183
653
|
parameters: ClawchatCreateMomentSchema,
|
|
184
654
|
async execute(_callId, params) {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
655
|
+
return await recordClawchatToolCall("clawchat_create_moment", params, async () => {
|
|
656
|
+
const p = (params ?? {});
|
|
657
|
+
const text = typeof p.text === "string" ? p.text : undefined;
|
|
658
|
+
const images = Array.isArray(p.images) ? p.images : undefined;
|
|
659
|
+
if (!text && (!images || images.length === 0)) {
|
|
660
|
+
return validationError("openclaw-clawchat: at least one of text or images is required");
|
|
661
|
+
}
|
|
662
|
+
return await withClient((c) => c.createMoment({
|
|
663
|
+
...(text !== undefined ? { text } : {}),
|
|
664
|
+
...(images !== undefined ? { images } : {}),
|
|
665
|
+
}));
|
|
666
|
+
});
|
|
195
667
|
},
|
|
196
668
|
}, { name: "clawchat_create_moment" });
|
|
197
669
|
api.registerTool({
|
|
@@ -202,8 +674,10 @@ export function registerOpenclawClawlingTools(api) {
|
|
|
202
674
|
"Only the moment author can delete it. Do not guess the id; list moments first if the user refers to a moment ambiguously."),
|
|
203
675
|
parameters: ClawchatDeleteMomentSchema,
|
|
204
676
|
async execute(_callId, params) {
|
|
205
|
-
|
|
206
|
-
|
|
677
|
+
return await recordClawchatToolCall("clawchat_delete_moment", params, async () => {
|
|
678
|
+
const p = params;
|
|
679
|
+
return await withClient((c) => c.deleteMoment(p.momentId));
|
|
680
|
+
});
|
|
207
681
|
},
|
|
208
682
|
}, { name: "clawchat_delete_moment" });
|
|
209
683
|
api.registerTool({
|
|
@@ -214,11 +688,13 @@ export function registerOpenclawClawlingTools(api) {
|
|
|
214
688
|
"The API adds the reaction if missing and removes it if already present. Require a concrete moment id and emoji."),
|
|
215
689
|
parameters: ClawchatToggleMomentReactionSchema,
|
|
216
690
|
async execute(_callId, params) {
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
691
|
+
return await recordClawchatToolCall("clawchat_toggle_moment_reaction", params, async () => {
|
|
692
|
+
const p = params;
|
|
693
|
+
if (!p.emoji?.trim()) {
|
|
694
|
+
return validationError("openclaw-clawchat: emoji is required");
|
|
695
|
+
}
|
|
696
|
+
return await withClient((c) => c.toggleMomentReaction({ momentId: p.momentId, emoji: p.emoji }));
|
|
697
|
+
});
|
|
222
698
|
},
|
|
223
699
|
}, { name: "clawchat_toggle_moment_reaction" });
|
|
224
700
|
api.registerTool({
|
|
@@ -229,11 +705,13 @@ export function registerOpenclawClawlingTools(api) {
|
|
|
229
705
|
"Require a concrete moment id and non-empty text. Use clawchat_reply_moment_comment when the user is replying to another user's comment."),
|
|
230
706
|
parameters: ClawchatCreateMomentCommentSchema,
|
|
231
707
|
async execute(_callId, params) {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
708
|
+
return await recordClawchatToolCall("clawchat_create_moment_comment", params, async () => {
|
|
709
|
+
const p = params;
|
|
710
|
+
if (!p.text?.trim()) {
|
|
711
|
+
return validationError("openclaw-clawchat: text is required");
|
|
712
|
+
}
|
|
713
|
+
return await withClient((c) => c.createMomentComment({ momentId: p.momentId, text: p.text }));
|
|
714
|
+
});
|
|
237
715
|
},
|
|
238
716
|
}, { name: "clawchat_create_moment_comment" });
|
|
239
717
|
api.registerTool({
|
|
@@ -244,15 +722,17 @@ export function registerOpenclawClawlingTools(api) {
|
|
|
244
722
|
"Require concrete moment and comment ids; do not use this for top-level comments."),
|
|
245
723
|
parameters: ClawchatReplyMomentCommentSchema,
|
|
246
724
|
async execute(_callId, params) {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
725
|
+
return await recordClawchatToolCall("clawchat_reply_moment_comment", params, async () => {
|
|
726
|
+
const p = params;
|
|
727
|
+
if (!p.text?.trim()) {
|
|
728
|
+
return validationError("openclaw-clawchat: text is required");
|
|
729
|
+
}
|
|
730
|
+
return await withClient((c) => c.replyMomentComment({
|
|
731
|
+
momentId: p.momentId,
|
|
732
|
+
replyToCommentId: p.replyToCommentId,
|
|
733
|
+
text: p.text,
|
|
734
|
+
}));
|
|
735
|
+
});
|
|
256
736
|
},
|
|
257
737
|
}, { name: "clawchat_reply_moment_comment" });
|
|
258
738
|
api.registerTool({
|
|
@@ -263,8 +743,10 @@ export function registerOpenclawClawlingTools(api) {
|
|
|
263
743
|
"The caller may delete comments they authored or comments on moments they authored. Do not guess ids; list moments first if needed."),
|
|
264
744
|
parameters: ClawchatDeleteMomentCommentSchema,
|
|
265
745
|
async execute(_callId, params) {
|
|
266
|
-
|
|
267
|
-
|
|
746
|
+
return await recordClawchatToolCall("clawchat_delete_moment_comment", params, async () => {
|
|
747
|
+
const p = params;
|
|
748
|
+
return await withClient((c) => c.deleteMomentComment({ momentId: p.momentId, commentId: p.commentId }));
|
|
749
|
+
});
|
|
268
750
|
},
|
|
269
751
|
}, { name: "clawchat_delete_moment_comment" });
|
|
270
752
|
api.registerTool({
|
|
@@ -286,18 +768,20 @@ export function registerOpenclawClawlingTools(api) {
|
|
|
286
768
|
"At least one of the three must be present. Do not frame this as updating a human user's personal account."),
|
|
287
769
|
parameters: ClawchatUpdateAccountProfileSchema,
|
|
288
770
|
async execute(_callId, params) {
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
771
|
+
return await recordClawchatToolCall("clawchat_update_account_profile", params, async () => {
|
|
772
|
+
const p = (params ?? {});
|
|
773
|
+
const patch = {};
|
|
774
|
+
if (typeof p.nickname === "string")
|
|
775
|
+
patch.nickname = p.nickname;
|
|
776
|
+
if (typeof p.avatar_url === "string")
|
|
777
|
+
patch.avatar_url = p.avatar_url;
|
|
778
|
+
if (typeof p.bio === "string")
|
|
779
|
+
patch.bio = p.bio;
|
|
780
|
+
if (Object.keys(patch).length === 0) {
|
|
781
|
+
return validationError("openclaw-clawchat: at least one of nickname / avatar / bio is required");
|
|
782
|
+
}
|
|
783
|
+
return await withClient((c) => c.updateMyProfile(patch));
|
|
784
|
+
});
|
|
301
785
|
},
|
|
302
786
|
}, { name: "clawchat_update_account_profile" });
|
|
303
787
|
api.registerTool({
|
|
@@ -308,27 +792,29 @@ export function registerOpenclawClawlingTools(api) {
|
|
|
308
792
|
"This tool does not update or set the account avatar by itself; when the user asked to set or sync the avatar, call `clawchat_update_account_profile` with `avatar_url` after this tool returns a URL."),
|
|
309
793
|
parameters: ClawchatUploadAvatarImageSchema,
|
|
310
794
|
async execute(_callId, params) {
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
795
|
+
return await recordClawchatToolCall("clawchat_upload_avatar_image", params, async () => {
|
|
796
|
+
const p = params;
|
|
797
|
+
if (!p.filePath || !path.isAbsolute(p.filePath)) {
|
|
798
|
+
return validationError("openclaw-clawchat: filePath must be an absolute local path");
|
|
799
|
+
}
|
|
800
|
+
let stat;
|
|
801
|
+
try {
|
|
802
|
+
stat = fs.statSync(p.filePath);
|
|
803
|
+
}
|
|
804
|
+
catch (err) {
|
|
805
|
+
return validationError(`openclaw-clawchat: cannot stat ${p.filePath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
806
|
+
}
|
|
807
|
+
if (!stat.isFile()) {
|
|
808
|
+
return validationError(`openclaw-clawchat: ${p.filePath} is not a regular file`);
|
|
809
|
+
}
|
|
810
|
+
if (stat.size > MAX_UPLOAD_BYTES) {
|
|
811
|
+
return validationError(`openclaw-clawchat: file too large (${stat.size} bytes; max 20MB)`);
|
|
812
|
+
}
|
|
813
|
+
const buffer = fs.readFileSync(p.filePath);
|
|
814
|
+
const filename = path.basename(p.filePath);
|
|
815
|
+
const mime = inferMimeFromPath(p.filePath);
|
|
816
|
+
return await withClient((c) => c.uploadAvatar({ buffer, filename, mime }));
|
|
817
|
+
});
|
|
332
818
|
},
|
|
333
819
|
}, { name: "clawchat_upload_avatar_image" });
|
|
334
820
|
api.registerTool({
|
|
@@ -340,28 +826,30 @@ export function registerOpenclawClawlingTools(api) {
|
|
|
340
826
|
"Do not use this for account avatar changes; use `clawchat_upload_avatar_image` for avatar images. Do not use this just to mirror local assistant identity."),
|
|
341
827
|
parameters: ClawchatUploadMediaFileSchema,
|
|
342
828
|
async execute(_callId, params) {
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
829
|
+
return await recordClawchatToolCall("clawchat_upload_media_file", params, async () => {
|
|
830
|
+
const p = params;
|
|
831
|
+
if (!p.filePath || !path.isAbsolute(p.filePath)) {
|
|
832
|
+
return validationError("openclaw-clawchat: filePath must be an absolute local path");
|
|
833
|
+
}
|
|
834
|
+
let stat;
|
|
835
|
+
try {
|
|
836
|
+
stat = fs.statSync(p.filePath);
|
|
837
|
+
}
|
|
838
|
+
catch (err) {
|
|
839
|
+
return validationError(`openclaw-clawchat: cannot stat ${p.filePath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
840
|
+
}
|
|
841
|
+
if (!stat.isFile()) {
|
|
842
|
+
return validationError(`openclaw-clawchat: ${p.filePath} is not a regular file`);
|
|
843
|
+
}
|
|
844
|
+
if (stat.size > MAX_UPLOAD_BYTES) {
|
|
845
|
+
return validationError(`openclaw-clawchat: file too large (${stat.size} bytes; max 20MB)`);
|
|
846
|
+
}
|
|
847
|
+
const buffer = fs.readFileSync(p.filePath);
|
|
848
|
+
const filename = path.basename(p.filePath);
|
|
849
|
+
const mime = inferMimeFromPath(p.filePath);
|
|
850
|
+
return await withClient((c) => c.uploadMedia({ buffer, filename, mime }));
|
|
851
|
+
});
|
|
364
852
|
},
|
|
365
853
|
}, { name: "clawchat_upload_media_file" });
|
|
366
|
-
api.logger.debug?.("openclaw-clawchat: registered
|
|
854
|
+
api.logger.debug?.("openclaw-clawchat: registered 21 clawchat_* tools (get_account_profile, get_user_profile, list_account_friends, search_users, list_conversations, get_conversation, list_moments, create_moment, delete_moment, toggle_moment_reaction, create_moment_comment, reply_moment_comment, delete_moment_comment, update_account_profile, upload_avatar_image, upload_media_file, memory_read, memory_write, memory_edit, metadata_sync, metadata_update)");
|
|
367
855
|
}
|