@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/src/tools.ts
CHANGED
|
@@ -1,18 +1,49 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import type { OpenClawAgentToolResult } from "openclaw/plugin-sdk/agent-harness-runtime";
|
|
4
|
-
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/core";
|
|
4
|
+
import type { OpenClawPluginApi, OpenClawPluginToolContext } from "openclaw/plugin-sdk/core";
|
|
5
5
|
import { createOpenclawClawlingApiClient } from "./api-client.ts";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
ClawlingApiError,
|
|
8
|
+
type ConversationDetails,
|
|
9
|
+
type ConversationListItem,
|
|
10
|
+
type Profile,
|
|
11
|
+
} from "./api-types.ts";
|
|
7
12
|
import { resolveOpenclawClawlingAccount } from "./config.ts";
|
|
13
|
+
import {
|
|
14
|
+
clawChatDbPathForStateDir,
|
|
15
|
+
getClawChatStore,
|
|
16
|
+
type ClawChatStore,
|
|
17
|
+
} from "./storage.ts";
|
|
18
|
+
import {
|
|
19
|
+
editClawChatMemoryBody,
|
|
20
|
+
readClawChatMemoryFile,
|
|
21
|
+
resolveClawChatMemoryPath,
|
|
22
|
+
writeClawChatMemoryBody,
|
|
23
|
+
type ClawChatMemoryTargetType,
|
|
24
|
+
} from "./clawchat-memory.ts";
|
|
25
|
+
import {
|
|
26
|
+
pullGroupMetadata,
|
|
27
|
+
pullOwnerMetadata,
|
|
28
|
+
pullUserMetadata,
|
|
29
|
+
pushMetadata,
|
|
30
|
+
updateMetadata,
|
|
31
|
+
} from "./clawchat-metadata.ts";
|
|
8
32
|
import {
|
|
9
33
|
ClawchatGetAccountProfileSchema,
|
|
34
|
+
ClawchatGetConversationSchema,
|
|
10
35
|
ClawchatGetUserProfileSchema,
|
|
36
|
+
ClawchatMemoryEditSchema,
|
|
37
|
+
ClawchatMemoryReadSchema,
|
|
38
|
+
ClawchatMemoryWriteSchema,
|
|
39
|
+
ClawchatMetadataSyncSchema,
|
|
40
|
+
ClawchatMetadataUpdateSchema,
|
|
11
41
|
ClawchatCreateMomentCommentSchema,
|
|
12
42
|
ClawchatCreateMomentSchema,
|
|
13
43
|
ClawchatDeleteMomentCommentSchema,
|
|
14
44
|
ClawchatDeleteMomentSchema,
|
|
15
45
|
ClawchatListAccountFriendsSchema,
|
|
46
|
+
ClawchatListConversationsSchema,
|
|
16
47
|
ClawchatListMomentsSchema,
|
|
17
48
|
ClawchatReplyMomentCommentSchema,
|
|
18
49
|
ClawchatSearchUsersSchema,
|
|
@@ -20,12 +51,18 @@ import {
|
|
|
20
51
|
ClawchatUpdateAccountProfileSchema,
|
|
21
52
|
ClawchatUploadAvatarImageSchema,
|
|
22
53
|
ClawchatUploadMediaFileSchema,
|
|
54
|
+
type ClawchatMemoryEditParams,
|
|
55
|
+
type ClawchatMemoryReadParams,
|
|
56
|
+
type ClawchatMemoryWriteParams,
|
|
57
|
+
type ClawchatMetadataSyncParams,
|
|
58
|
+
type ClawchatMetadataUpdateParams,
|
|
23
59
|
type ClawchatCreateMomentCommentParams,
|
|
24
60
|
type ClawchatCreateMomentParams,
|
|
25
61
|
type ClawchatDeleteMomentCommentParams,
|
|
26
62
|
type ClawchatDeleteMomentParams,
|
|
63
|
+
type ClawchatGetConversationParams,
|
|
27
64
|
type ClawchatGetUserProfileParams,
|
|
28
|
-
type
|
|
65
|
+
type ClawchatListConversationsParams,
|
|
29
66
|
type ClawchatListMomentsParams,
|
|
30
67
|
type ClawchatReplyMomentCommentParams,
|
|
31
68
|
type ClawchatSearchUsersParams,
|
|
@@ -93,16 +130,89 @@ const MIME_BY_EXT: Record<string, string> = {
|
|
|
93
130
|
const DIRECT_TOOL_GUARD =
|
|
94
131
|
"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.";
|
|
95
132
|
|
|
133
|
+
const MEMORY_READ_DEFAULT_LIMIT = 20_000;
|
|
134
|
+
const MEMORY_READ_MAX_LIMIT = 100_000;
|
|
135
|
+
|
|
136
|
+
type ToolStore = Pick<
|
|
137
|
+
ClawChatStore,
|
|
138
|
+
"recordToolCall"
|
|
139
|
+
> &
|
|
140
|
+
Partial<
|
|
141
|
+
Pick<
|
|
142
|
+
ClawChatStore,
|
|
143
|
+
"upsertConversationSummary" | "upsertConversationDetails" | "deleteConversationCache"
|
|
144
|
+
>
|
|
145
|
+
>;
|
|
146
|
+
|
|
96
147
|
function toolDescription(...parts: string[]): string {
|
|
97
148
|
return `${parts.join("")} ${DIRECT_TOOL_GUARD}`;
|
|
98
149
|
}
|
|
99
150
|
|
|
151
|
+
function memoryToolDescription(...parts: string[]): string {
|
|
152
|
+
return parts.join("");
|
|
153
|
+
}
|
|
154
|
+
|
|
100
155
|
function inferMimeFromPath(filePath: string): string {
|
|
101
156
|
const ext = path.extname(filePath).toLowerCase();
|
|
102
157
|
return MIME_BY_EXT[ext] ?? "application/octet-stream";
|
|
103
158
|
}
|
|
104
159
|
|
|
105
|
-
|
|
160
|
+
function parseTimestamp(value: unknown): number | null {
|
|
161
|
+
if (typeof value !== "string") return null;
|
|
162
|
+
const parsed = Date.parse(value);
|
|
163
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function upsertConversationSummaryCache(
|
|
167
|
+
store: ToolStore | null,
|
|
168
|
+
accountId: string,
|
|
169
|
+
conversation: ConversationListItem,
|
|
170
|
+
): void {
|
|
171
|
+
if (!store?.upsertConversationSummary) return;
|
|
172
|
+
store.upsertConversationSummary({
|
|
173
|
+
platform: "openclaw",
|
|
174
|
+
accountId,
|
|
175
|
+
conversationId: conversation.id,
|
|
176
|
+
conversationType: conversation.type,
|
|
177
|
+
lastSeenAt: parseTimestamp(conversation.updated_at),
|
|
178
|
+
lastRefreshedAt: Date.now(),
|
|
179
|
+
raw: conversation,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function upsertConversationDetailsCache(
|
|
184
|
+
store: ToolStore | null,
|
|
185
|
+
accountId: string,
|
|
186
|
+
conversation: ConversationDetails,
|
|
187
|
+
): void {
|
|
188
|
+
if (!store?.upsertConversationDetails) return;
|
|
189
|
+
const refreshedAt = Date.now();
|
|
190
|
+
store.upsertConversationDetails({
|
|
191
|
+
platform: "openclaw",
|
|
192
|
+
accountId,
|
|
193
|
+
conversationId: conversation.id,
|
|
194
|
+
conversationType: conversation.type,
|
|
195
|
+
lastSeenAt: parseTimestamp(conversation.updated_at),
|
|
196
|
+
lastRefreshedAt: refreshedAt,
|
|
197
|
+
raw: conversation,
|
|
198
|
+
members: conversation.participants.map((participant) => ({
|
|
199
|
+
userId: participant.user_id,
|
|
200
|
+
role: participant.role,
|
|
201
|
+
raw: participant,
|
|
202
|
+
lastSeenAt: parseTimestamp(participant.joined_at),
|
|
203
|
+
})),
|
|
204
|
+
membersComplete: true,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function isConversationNotFound(err: ClawlingApiError): boolean {
|
|
209
|
+
return err.meta?.status === 404 || err.meta?.code === 404 || err.meta?.code === 40401;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export function registerOpenclawClawlingTools(
|
|
213
|
+
api: OpenClawPluginApi,
|
|
214
|
+
options: { store?: ToolStore | null } = {},
|
|
215
|
+
): void {
|
|
106
216
|
if (!api.config) {
|
|
107
217
|
api.logger.debug?.("openclaw-clawchat: api.config missing; skipping tool registration");
|
|
108
218
|
return;
|
|
@@ -113,6 +223,82 @@ export function registerOpenclawClawlingTools(api: OpenClawPluginApi): void {
|
|
|
113
223
|
return resolveOpenclawClawlingAccount(api.config!);
|
|
114
224
|
}
|
|
115
225
|
|
|
226
|
+
function resolveStore(): ToolStore | null {
|
|
227
|
+
if ("store" in options) return options.store ?? null;
|
|
228
|
+
try {
|
|
229
|
+
const stateDir = api.runtime.state?.resolveStateDir?.();
|
|
230
|
+
return getClawChatStore({
|
|
231
|
+
...(stateDir ? { dbPath: clawChatDbPathForStateDir(stateDir) } : {}),
|
|
232
|
+
log: { error: (message) => api.logger.error?.(message) },
|
|
233
|
+
});
|
|
234
|
+
} catch {
|
|
235
|
+
api.logger.error?.("openclaw-clawchat sqlite tool persistence unavailable; continuing");
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function detailsError(details: unknown): string | null {
|
|
241
|
+
if (!details || typeof details !== "object") return null;
|
|
242
|
+
const raw = details as { error?: unknown; message?: unknown };
|
|
243
|
+
if (typeof raw.error !== "string" || !raw.error) return null;
|
|
244
|
+
return typeof raw.message === "string" && raw.message
|
|
245
|
+
? `${raw.error}: ${raw.message}`
|
|
246
|
+
: raw.error;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function persistToolCall(input: Parameters<ToolStore["recordToolCall"]>[0]): void {
|
|
250
|
+
try {
|
|
251
|
+
resolveStore()?.recordToolCall(input);
|
|
252
|
+
} catch {
|
|
253
|
+
api.logger.error?.("openclaw-clawchat sqlite tool call insert failed; continuing");
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function persistConversationCache(fn: (store: ToolStore) => void): void {
|
|
258
|
+
try {
|
|
259
|
+
const store = resolveStore();
|
|
260
|
+
if (store) fn(store);
|
|
261
|
+
} catch {
|
|
262
|
+
api.logger.error?.("openclaw-clawchat sqlite conversation cache update failed; continuing");
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
async function recordClawchatToolCall<T>(
|
|
267
|
+
toolName: string,
|
|
268
|
+
params: unknown,
|
|
269
|
+
fn: () => Promise<OpenClawAgentToolResult<T>>,
|
|
270
|
+
): Promise<OpenClawAgentToolResult<T>> {
|
|
271
|
+
const startedAt = Date.now();
|
|
272
|
+
const account = resolveCurrent();
|
|
273
|
+
try {
|
|
274
|
+
const result = await fn();
|
|
275
|
+
const details = (result as { details?: unknown }).details ?? result;
|
|
276
|
+
persistToolCall({
|
|
277
|
+
platform: "openclaw",
|
|
278
|
+
accountId: account.accountId,
|
|
279
|
+
toolName,
|
|
280
|
+
args: params ?? {},
|
|
281
|
+
result: details,
|
|
282
|
+
error: detailsError(details),
|
|
283
|
+
startedAt,
|
|
284
|
+
endedAt: Date.now(),
|
|
285
|
+
});
|
|
286
|
+
return result;
|
|
287
|
+
} catch (err) {
|
|
288
|
+
persistToolCall({
|
|
289
|
+
platform: "openclaw",
|
|
290
|
+
accountId: account.accountId,
|
|
291
|
+
toolName,
|
|
292
|
+
args: params ?? {},
|
|
293
|
+
result: null,
|
|
294
|
+
error: err instanceof Error ? err.message : String(err),
|
|
295
|
+
startedAt,
|
|
296
|
+
endedAt: Date.now(),
|
|
297
|
+
});
|
|
298
|
+
throw err;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
116
302
|
type ClientResult =
|
|
117
303
|
| { ok: false; error: OpenClawAgentToolResult<unknown> }
|
|
118
304
|
| { ok: true; client: ReturnType<typeof createOpenclawClawlingApiClient> };
|
|
@@ -151,6 +337,327 @@ export function registerOpenclawClawlingTools(api: OpenClawPluginApi): void {
|
|
|
151
337
|
})();
|
|
152
338
|
}
|
|
153
339
|
|
|
340
|
+
function workspaceRoot(ctx: OpenClawPluginToolContext): string | null {
|
|
341
|
+
const root = typeof ctx.workspaceDir === "string" ? ctx.workspaceDir.trim() : "";
|
|
342
|
+
return root || null;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function targetFromParams(params: {
|
|
346
|
+
targetType: ClawChatMemoryTargetType;
|
|
347
|
+
targetId: string;
|
|
348
|
+
}) {
|
|
349
|
+
return { targetType: params.targetType, targetId: params.targetId };
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function validateTarget(root: string, params: {
|
|
353
|
+
targetType: ClawChatMemoryTargetType;
|
|
354
|
+
targetId: string;
|
|
355
|
+
}): string | null {
|
|
356
|
+
try {
|
|
357
|
+
resolveClawChatMemoryPath(root, targetFromParams(params));
|
|
358
|
+
return null;
|
|
359
|
+
} catch (err) {
|
|
360
|
+
return err instanceof Error ? err.message : String(err);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function normalizeReadBounds(params: ClawchatMemoryReadParams): {
|
|
365
|
+
offset: number;
|
|
366
|
+
limit: number;
|
|
367
|
+
} {
|
|
368
|
+
const offset = typeof params.offset === "number" && Number.isFinite(params.offset)
|
|
369
|
+
? Math.max(0, Math.trunc(params.offset))
|
|
370
|
+
: 0;
|
|
371
|
+
const limit = typeof params.limit === "number" && Number.isFinite(params.limit)
|
|
372
|
+
? Math.min(MEMORY_READ_MAX_LIMIT, Math.max(1, Math.trunc(params.limit)))
|
|
373
|
+
: MEMORY_READ_DEFAULT_LIMIT;
|
|
374
|
+
return { offset, limit };
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function metadataPatchAllowedKeys(targetType: ClawChatMemoryTargetType): readonly string[] {
|
|
378
|
+
if (targetType === "group") return ["title", "description"];
|
|
379
|
+
if (targetType === "owner") return ["nickname", "avatar_url", "bio", "behavior"];
|
|
380
|
+
return ["nickname", "avatar_url", "bio"];
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function validateMetadataUpdatePatch(params: ClawchatMetadataUpdateParams): string | null {
|
|
384
|
+
const patch = params.patch as Record<string, unknown> | undefined;
|
|
385
|
+
if (!patch || typeof patch !== "object" || Array.isArray(patch)) {
|
|
386
|
+
return "openclaw-clawchat: metadata patch is required";
|
|
387
|
+
}
|
|
388
|
+
const keys = Object.keys(patch);
|
|
389
|
+
if (keys.length === 0) {
|
|
390
|
+
return "openclaw-clawchat: metadata patch must include at least one mutable field";
|
|
391
|
+
}
|
|
392
|
+
const allowed = new Set(metadataPatchAllowedKeys(params.targetType));
|
|
393
|
+
const invalid = keys.filter((key) => !allowed.has(key));
|
|
394
|
+
if (invalid.length > 0) {
|
|
395
|
+
return `openclaw-clawchat: metadata patch contains unsupported field(s): ${invalid.join(", ")}`;
|
|
396
|
+
}
|
|
397
|
+
const nonString = keys.filter((key) => typeof patch[key] !== "string");
|
|
398
|
+
if (nonString.length > 0) {
|
|
399
|
+
return `openclaw-clawchat: metadata patch field(s) must be strings: ${nonString.join(", ")}`;
|
|
400
|
+
}
|
|
401
|
+
return null;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function isUnsafeMemoryPathError(message: string): boolean {
|
|
405
|
+
return /Unsafe clawchat memory|Resolved clawchat memory path is outside root/i.test(message);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function memoryToolError(err: unknown) {
|
|
409
|
+
if (err instanceof ClawlingApiError) return apiError(err);
|
|
410
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
411
|
+
if (isUnsafeMemoryPathError(message)) {
|
|
412
|
+
return validationError("openclaw-clawchat: unsafe ClawChat memory target");
|
|
413
|
+
}
|
|
414
|
+
return validationError(message);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function registerMemoryTools(): void {
|
|
418
|
+
api.registerTool(
|
|
419
|
+
(ctx) => ({
|
|
420
|
+
name: "clawchat_memory_read",
|
|
421
|
+
label: "Read ClawChat Memory",
|
|
422
|
+
description: memoryToolDescription(
|
|
423
|
+
"Read a ClawChat memory Markdown file by explicit targetType and targetId. " +
|
|
424
|
+
"Use this when you need existing owner, user, or group memory content. " +
|
|
425
|
+
"Do not guess ids from names. " +
|
|
426
|
+
"This reads metadata and agent-authored body; it does not contact the ClawChat server.",
|
|
427
|
+
),
|
|
428
|
+
parameters: ClawchatMemoryReadSchema,
|
|
429
|
+
async execute(_callId, params) {
|
|
430
|
+
const root = workspaceRoot(ctx);
|
|
431
|
+
if (!root) {
|
|
432
|
+
return validationError("openclaw-clawchat: OpenClaw workspaceDir is required");
|
|
433
|
+
}
|
|
434
|
+
try {
|
|
435
|
+
const p = params as ClawchatMemoryReadParams;
|
|
436
|
+
const targetError = validateTarget(root, p);
|
|
437
|
+
if (targetError) return memoryToolError(new Error(targetError));
|
|
438
|
+
const { offset, limit } = normalizeReadBounds(p);
|
|
439
|
+
const file = await readClawChatMemoryFile(root, targetFromParams(p));
|
|
440
|
+
if (!file.exists) {
|
|
441
|
+
return jsonResponse({
|
|
442
|
+
exists: false,
|
|
443
|
+
content: "",
|
|
444
|
+
offset,
|
|
445
|
+
limit,
|
|
446
|
+
truncated: false,
|
|
447
|
+
totalLength: 0,
|
|
448
|
+
nextOffset: null,
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
const totalLength = file.raw.length;
|
|
452
|
+
const end = Math.min(totalLength, offset + limit);
|
|
453
|
+
const truncated = end < totalLength;
|
|
454
|
+
return jsonResponse({
|
|
455
|
+
exists: true,
|
|
456
|
+
content: file.raw.slice(offset, end),
|
|
457
|
+
offset,
|
|
458
|
+
limit,
|
|
459
|
+
truncated,
|
|
460
|
+
totalLength,
|
|
461
|
+
nextOffset: truncated ? end : null,
|
|
462
|
+
});
|
|
463
|
+
} catch (err) {
|
|
464
|
+
return memoryToolError(err);
|
|
465
|
+
}
|
|
466
|
+
},
|
|
467
|
+
}),
|
|
468
|
+
{ name: "clawchat_memory_read" },
|
|
469
|
+
);
|
|
470
|
+
|
|
471
|
+
api.registerTool(
|
|
472
|
+
(ctx) => ({
|
|
473
|
+
name: "clawchat_memory_write",
|
|
474
|
+
label: "Write ClawChat Memory",
|
|
475
|
+
description: memoryToolDescription(
|
|
476
|
+
"Append to or replace only the agent-authored body of a ClawChat memory Markdown file by explicit targetType and targetId. " +
|
|
477
|
+
"This never modifies the metadata block. " +
|
|
478
|
+
"Do not use this to write or refresh ClawChat profile/metadata fields such as nickname, avatar_url, bio, profile_type, title, description, or behavior. " +
|
|
479
|
+
"When the user asks to refresh, sync, or update local ClawChat owner/user/group profile information, use clawchat_metadata_sync with direction=pull instead. " +
|
|
480
|
+
"Use append for new long-term memory notes and replace only when intentionally rewriting the whole body.",
|
|
481
|
+
),
|
|
482
|
+
parameters: ClawchatMemoryWriteSchema,
|
|
483
|
+
async execute(_callId, params) {
|
|
484
|
+
const root = workspaceRoot(ctx);
|
|
485
|
+
if (!root) {
|
|
486
|
+
return validationError("openclaw-clawchat: OpenClaw workspaceDir is required");
|
|
487
|
+
}
|
|
488
|
+
try {
|
|
489
|
+
const p = params as ClawchatMemoryWriteParams;
|
|
490
|
+
const targetError = validateTarget(root, p);
|
|
491
|
+
if (targetError) return memoryToolError(new Error(targetError));
|
|
492
|
+
await writeClawChatMemoryBody(root, targetFromParams(p), p.mode, p.content);
|
|
493
|
+
return jsonResponse({ ok: true, targetType: p.targetType, targetId: p.targetId, mode: p.mode });
|
|
494
|
+
} catch (err) {
|
|
495
|
+
return memoryToolError(err);
|
|
496
|
+
}
|
|
497
|
+
},
|
|
498
|
+
}),
|
|
499
|
+
{ name: "clawchat_memory_write" },
|
|
500
|
+
);
|
|
501
|
+
|
|
502
|
+
api.registerTool(
|
|
503
|
+
(ctx) => ({
|
|
504
|
+
name: "clawchat_memory_edit",
|
|
505
|
+
label: "Edit ClawChat Memory",
|
|
506
|
+
description: memoryToolDescription(
|
|
507
|
+
"Replace exactly one existing text span in the agent-authored body of a ClawChat memory Markdown file. " +
|
|
508
|
+
"This never modifies the metadata block. " +
|
|
509
|
+
"Do not use this to edit ClawChat profile/metadata fields such as nickname, avatar_url, bio, profile_type, title, description, or behavior. " +
|
|
510
|
+
"Use clawchat_metadata_sync or clawchat_metadata_update for metadata. " +
|
|
511
|
+
"The oldText must match exactly once; use read first when unsure.",
|
|
512
|
+
),
|
|
513
|
+
parameters: ClawchatMemoryEditSchema,
|
|
514
|
+
async execute(_callId, params) {
|
|
515
|
+
const root = workspaceRoot(ctx);
|
|
516
|
+
if (!root) {
|
|
517
|
+
return validationError("openclaw-clawchat: OpenClaw workspaceDir is required");
|
|
518
|
+
}
|
|
519
|
+
try {
|
|
520
|
+
const p = params as ClawchatMemoryEditParams;
|
|
521
|
+
const targetError = validateTarget(root, p);
|
|
522
|
+
if (targetError) return memoryToolError(new Error(targetError));
|
|
523
|
+
await editClawChatMemoryBody(root, targetFromParams(p), p.oldText, p.newText);
|
|
524
|
+
return jsonResponse({ ok: true, targetType: p.targetType, targetId: p.targetId });
|
|
525
|
+
} catch (err) {
|
|
526
|
+
return memoryToolError(err);
|
|
527
|
+
}
|
|
528
|
+
},
|
|
529
|
+
}),
|
|
530
|
+
{ name: "clawchat_memory_edit" },
|
|
531
|
+
);
|
|
532
|
+
|
|
533
|
+
api.registerTool(
|
|
534
|
+
(ctx) => ({
|
|
535
|
+
name: "clawchat_metadata_sync",
|
|
536
|
+
label: "Sync ClawChat Metadata",
|
|
537
|
+
description: memoryToolDescription(
|
|
538
|
+
"Synchronize the ClawChat metadata block for an explicit owner, user, or group target. " +
|
|
539
|
+
"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. " +
|
|
540
|
+
"Use direction=push only to push selected existing local metadata fields to the server, then refresh the metadata block from the server response. " +
|
|
541
|
+
"For direction=push, pass non-empty fields containing only pushable metadata field names. " +
|
|
542
|
+
"This does not modify the agent-authored body. " +
|
|
543
|
+
"Do not combine clawchat_get_user_profile with clawchat_memory_write to update local profile metadata.",
|
|
544
|
+
),
|
|
545
|
+
parameters: ClawchatMetadataSyncSchema,
|
|
546
|
+
async execute(_callId, params) {
|
|
547
|
+
const root = workspaceRoot(ctx);
|
|
548
|
+
if (!root) {
|
|
549
|
+
return validationError("openclaw-clawchat: OpenClaw workspaceDir is required");
|
|
550
|
+
}
|
|
551
|
+
const built = buildClient();
|
|
552
|
+
if (!built.ok) return built.error;
|
|
553
|
+
try {
|
|
554
|
+
const p = params as ClawchatMetadataSyncParams;
|
|
555
|
+
const targetError = validateTarget(root, p);
|
|
556
|
+
if (targetError) return memoryToolError(new Error(targetError));
|
|
557
|
+
const account = resolveCurrent();
|
|
558
|
+
if (p.direction === "push") {
|
|
559
|
+
if (!Array.isArray(p.fields) || p.fields.length === 0) {
|
|
560
|
+
return validationError("openclaw-clawchat: fields are required for metadata push");
|
|
561
|
+
}
|
|
562
|
+
if (p.targetType === "owner" && !account.agentId) {
|
|
563
|
+
return configError("openclaw-clawchat: agentId is required");
|
|
564
|
+
}
|
|
565
|
+
if (p.targetType === "user" && !account.userId) {
|
|
566
|
+
return configError("openclaw-clawchat: userId is required");
|
|
567
|
+
}
|
|
568
|
+
return jsonResponse(
|
|
569
|
+
await pushMetadata({
|
|
570
|
+
memoryRoot: root,
|
|
571
|
+
targetType: p.targetType,
|
|
572
|
+
targetId: p.targetId,
|
|
573
|
+
fields: p.fields,
|
|
574
|
+
agentId: account.agentId,
|
|
575
|
+
accountUserId: account.userId,
|
|
576
|
+
api: built.client,
|
|
577
|
+
}),
|
|
578
|
+
);
|
|
579
|
+
}
|
|
580
|
+
if (p.targetType === "owner") {
|
|
581
|
+
if (!account.agentId) return configError("openclaw-clawchat: agentId is required");
|
|
582
|
+
return jsonResponse(
|
|
583
|
+
await pullOwnerMetadata({
|
|
584
|
+
memoryRoot: root,
|
|
585
|
+
agentId: account.agentId,
|
|
586
|
+
accountUserId: account.userId,
|
|
587
|
+
accountOwnerUserId: account.ownerUserId || undefined,
|
|
588
|
+
api: built.client,
|
|
589
|
+
}),
|
|
590
|
+
);
|
|
591
|
+
}
|
|
592
|
+
if (p.targetType === "user") {
|
|
593
|
+
return jsonResponse(
|
|
594
|
+
await pullUserMetadata({ memoryRoot: root, userId: p.targetId, api: built.client }),
|
|
595
|
+
);
|
|
596
|
+
}
|
|
597
|
+
return jsonResponse(
|
|
598
|
+
await pullGroupMetadata({ memoryRoot: root, groupId: p.targetId, api: built.client }),
|
|
599
|
+
);
|
|
600
|
+
} catch (err) {
|
|
601
|
+
return memoryToolError(err);
|
|
602
|
+
}
|
|
603
|
+
},
|
|
604
|
+
}),
|
|
605
|
+
{ name: "clawchat_metadata_sync" },
|
|
606
|
+
);
|
|
607
|
+
|
|
608
|
+
api.registerTool(
|
|
609
|
+
(ctx) => ({
|
|
610
|
+
name: "clawchat_metadata_update",
|
|
611
|
+
label: "Update ClawChat Metadata",
|
|
612
|
+
description: memoryToolDescription(
|
|
613
|
+
"Update ClawChat server metadata for an explicit owner, connected user account, or group target, then refresh the local metadata block from the server response. " +
|
|
614
|
+
"Use this only when the user wants to change server-side metadata fields. " +
|
|
615
|
+
"To refresh local metadata from the server without changing the server, use clawchat_metadata_sync with direction=pull. " +
|
|
616
|
+
"This always pushes to the server first and does not modify the agent-authored body.",
|
|
617
|
+
),
|
|
618
|
+
parameters: ClawchatMetadataUpdateSchema,
|
|
619
|
+
async execute(_callId, params) {
|
|
620
|
+
const root = workspaceRoot(ctx);
|
|
621
|
+
if (!root) {
|
|
622
|
+
return validationError("openclaw-clawchat: OpenClaw workspaceDir is required");
|
|
623
|
+
}
|
|
624
|
+
const built = buildClient();
|
|
625
|
+
if (!built.ok) return built.error;
|
|
626
|
+
try {
|
|
627
|
+
const p = params as ClawchatMetadataUpdateParams;
|
|
628
|
+
const targetError = validateTarget(root, p);
|
|
629
|
+
if (targetError) return memoryToolError(new Error(targetError));
|
|
630
|
+
const patchError = validateMetadataUpdatePatch(p);
|
|
631
|
+
if (patchError) return validationError(patchError);
|
|
632
|
+
const account = resolveCurrent();
|
|
633
|
+
if (p.targetType === "owner" && !account.agentId) {
|
|
634
|
+
return configError("openclaw-clawchat: agentId is required");
|
|
635
|
+
}
|
|
636
|
+
if (p.targetType === "user" && !account.userId) {
|
|
637
|
+
return configError("openclaw-clawchat: userId is required");
|
|
638
|
+
}
|
|
639
|
+
return jsonResponse(
|
|
640
|
+
await updateMetadata({
|
|
641
|
+
memoryRoot: root,
|
|
642
|
+
targetType: p.targetType,
|
|
643
|
+
targetId: p.targetId,
|
|
644
|
+
agentId: account.agentId,
|
|
645
|
+
accountUserId: account.userId,
|
|
646
|
+
patch: p.patch,
|
|
647
|
+
api: built.client,
|
|
648
|
+
}),
|
|
649
|
+
);
|
|
650
|
+
} catch (err) {
|
|
651
|
+
return memoryToolError(err);
|
|
652
|
+
}
|
|
653
|
+
},
|
|
654
|
+
}),
|
|
655
|
+
{ name: "clawchat_metadata_update" },
|
|
656
|
+
);
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
registerMemoryTools();
|
|
660
|
+
|
|
154
661
|
api.registerTool(
|
|
155
662
|
{
|
|
156
663
|
name: "clawchat_get_account_profile",
|
|
@@ -164,8 +671,10 @@ export function registerOpenclawClawlingTools(api: OpenClawPluginApi): void {
|
|
|
164
671
|
"Do not frame this as a human user's personal account.",
|
|
165
672
|
),
|
|
166
673
|
parameters: ClawchatGetAccountProfileSchema,
|
|
167
|
-
async execute(_callId,
|
|
168
|
-
return await
|
|
674
|
+
async execute(_callId, params) {
|
|
675
|
+
return await recordClawchatToolCall("clawchat_get_account_profile", params, async () =>
|
|
676
|
+
withClient((c) => c.getMyProfile()),
|
|
677
|
+
);
|
|
169
678
|
},
|
|
170
679
|
},
|
|
171
680
|
{ name: "clawchat_get_account_profile" },
|
|
@@ -179,12 +688,16 @@ export function registerOpenclawClawlingTools(api: OpenClawPluginApi): void {
|
|
|
179
688
|
"Fetch a ClawChat user's public profile by userId. " +
|
|
180
689
|
"TRIGGER — invoke when the user asks to look up, view, or inspect a specific ClawChat user's public profile " +
|
|
181
690
|
"and provides a concrete userId. Do not guess or infer userId from a nickname/display name. " +
|
|
691
|
+
"This is a read-only lookup and does not update local ClawChat Memory files. " +
|
|
692
|
+
"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. " +
|
|
182
693
|
"Use `clawchat_get_account_profile` for the agent's own connected ClawChat account unless an explicit userId is provided.",
|
|
183
694
|
),
|
|
184
695
|
parameters: ClawchatGetUserProfileSchema,
|
|
185
696
|
async execute(_callId, params) {
|
|
186
|
-
|
|
187
|
-
|
|
697
|
+
return await recordClawchatToolCall("clawchat_get_user_profile", params, async () => {
|
|
698
|
+
const p = params as ClawchatGetUserProfileParams;
|
|
699
|
+
return await withClient((c) => c.getUserInfo(p.userId));
|
|
700
|
+
});
|
|
188
701
|
},
|
|
189
702
|
},
|
|
190
703
|
{ name: "clawchat_get_user_profile" },
|
|
@@ -195,18 +708,14 @@ export function registerOpenclawClawlingTools(api: OpenClawPluginApi): void {
|
|
|
195
708
|
name: "clawchat_list_account_friends",
|
|
196
709
|
label: "List ClawChat Account Friends",
|
|
197
710
|
description: toolDescription(
|
|
198
|
-
"List friends/contacts of the agent's connected ClawChat account (the configured ClawChat account)
|
|
711
|
+
"List friends/contacts of the agent's connected ClawChat account (the configured ClawChat account). " +
|
|
199
712
|
"These are the agent's ClawChat-platform contacts. " +
|
|
200
|
-
"TRIGGER — invoke when the user asks for this ClawChat account's friends, contacts, friend list
|
|
713
|
+
"TRIGGER — invoke when the user asks for this ClawChat account's friends, contacts, or friend list.",
|
|
201
714
|
),
|
|
202
715
|
parameters: ClawchatListAccountFriendsSchema,
|
|
203
716
|
async execute(_callId, params) {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
c.listFriends({
|
|
207
|
-
...(p.page !== undefined ? { page: p.page } : { page: 1 }),
|
|
208
|
-
...(p.pageSize !== undefined ? { pageSize: p.pageSize } : { pageSize: 20 }),
|
|
209
|
-
}),
|
|
717
|
+
return await recordClawchatToolCall("clawchat_list_account_friends", params, async () =>
|
|
718
|
+
withClient((c) => c.listFriends()),
|
|
210
719
|
);
|
|
211
720
|
},
|
|
212
721
|
},
|
|
@@ -224,18 +733,95 @@ export function registerOpenclawClawlingTools(api: OpenClawPluginApi): void {
|
|
|
224
733
|
),
|
|
225
734
|
parameters: ClawchatSearchUsersSchema,
|
|
226
735
|
async execute(_callId, params) {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
c
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
736
|
+
return await recordClawchatToolCall("clawchat_search_users", params, async () => {
|
|
737
|
+
const p = (params ?? {}) as ClawchatSearchUsersParams;
|
|
738
|
+
return await withClient((c) =>
|
|
739
|
+
c.searchUsers({
|
|
740
|
+
...(typeof p.q === "string" ? { q: p.q } : {}),
|
|
741
|
+
...(typeof p.limit === "number" ? { limit: p.limit } : {}),
|
|
742
|
+
}),
|
|
743
|
+
);
|
|
744
|
+
});
|
|
234
745
|
},
|
|
235
746
|
},
|
|
236
747
|
{ name: "clawchat_search_users" },
|
|
237
748
|
);
|
|
238
749
|
|
|
750
|
+
api.registerTool(
|
|
751
|
+
{
|
|
752
|
+
name: "clawchat_list_conversations",
|
|
753
|
+
label: "List ClawChat Conversations",
|
|
754
|
+
description: toolDescription(
|
|
755
|
+
"List ClawChat direct and group conversations visible to the configured account. " +
|
|
756
|
+
"TRIGGER - invoke when the user asks to list, browse, inspect, or paginate ClawChat conversations, chats, groups, or direct messages. " +
|
|
757
|
+
"This is read-only and does not create, update, leave, dissolve, or administer conversations.",
|
|
758
|
+
),
|
|
759
|
+
parameters: ClawchatListConversationsSchema,
|
|
760
|
+
async execute(_callId, params) {
|
|
761
|
+
return await recordClawchatToolCall("clawchat_list_conversations", params, async () => {
|
|
762
|
+
const p = (params ?? {}) as ClawchatListConversationsParams;
|
|
763
|
+
const account = resolveCurrent();
|
|
764
|
+
return await withClient(async (c) => {
|
|
765
|
+
const data = await c.listConversations({
|
|
766
|
+
...(typeof p.before === "string" ? { before: p.before } : {}),
|
|
767
|
+
...(typeof p.limit === "number" ? { limit: p.limit } : {}),
|
|
768
|
+
});
|
|
769
|
+
for (const conversation of data.conversations) {
|
|
770
|
+
persistConversationCache((store) =>
|
|
771
|
+
upsertConversationSummaryCache(store, account.accountId, conversation),
|
|
772
|
+
);
|
|
773
|
+
}
|
|
774
|
+
return data;
|
|
775
|
+
});
|
|
776
|
+
});
|
|
777
|
+
},
|
|
778
|
+
},
|
|
779
|
+
{ name: "clawchat_list_conversations" },
|
|
780
|
+
);
|
|
781
|
+
|
|
782
|
+
api.registerTool(
|
|
783
|
+
{
|
|
784
|
+
name: "clawchat_get_conversation",
|
|
785
|
+
label: "Get ClawChat Conversation",
|
|
786
|
+
description: toolDescription(
|
|
787
|
+
"Fetch read-only ClawChat conversation details, including group membership when returned by the API. " +
|
|
788
|
+
"TRIGGER - invoke when the user asks to inspect a specific ClawChat conversation or group and provides a concrete conversationId. " +
|
|
789
|
+
"This is read-only and does not create, update, leave, dissolve, or administer conversations.",
|
|
790
|
+
),
|
|
791
|
+
parameters: ClawchatGetConversationSchema,
|
|
792
|
+
async execute(_callId, params) {
|
|
793
|
+
return await recordClawchatToolCall("clawchat_get_conversation", params, async () => {
|
|
794
|
+
const p = params as ClawchatGetConversationParams;
|
|
795
|
+
const account = resolveCurrent();
|
|
796
|
+
const built = buildClient();
|
|
797
|
+
if (!built.ok) return built.error;
|
|
798
|
+
try {
|
|
799
|
+
const data = await built.client.getConversation(p.conversationId);
|
|
800
|
+
persistConversationCache((store) =>
|
|
801
|
+
upsertConversationDetailsCache(store, account.accountId, data.conversation),
|
|
802
|
+
);
|
|
803
|
+
return jsonResponse(data);
|
|
804
|
+
} catch (err) {
|
|
805
|
+
if (err instanceof ClawlingApiError) {
|
|
806
|
+
if (isConversationNotFound(err)) {
|
|
807
|
+
persistConversationCache((store) =>
|
|
808
|
+
store.deleteConversationCache?.({
|
|
809
|
+
platform: "openclaw",
|
|
810
|
+
accountId: account.accountId,
|
|
811
|
+
conversationId: p.conversationId,
|
|
812
|
+
}),
|
|
813
|
+
);
|
|
814
|
+
}
|
|
815
|
+
return apiError(err);
|
|
816
|
+
}
|
|
817
|
+
return genericError(err);
|
|
818
|
+
}
|
|
819
|
+
});
|
|
820
|
+
},
|
|
821
|
+
},
|
|
822
|
+
{ name: "clawchat_get_conversation" },
|
|
823
|
+
);
|
|
824
|
+
|
|
239
825
|
api.registerTool(
|
|
240
826
|
{
|
|
241
827
|
name: "clawchat_list_moments",
|
|
@@ -247,13 +833,15 @@ export function registerOpenclawClawlingTools(api: OpenClawPluginApi): void {
|
|
|
247
833
|
),
|
|
248
834
|
parameters: ClawchatListMomentsSchema,
|
|
249
835
|
async execute(_callId, params) {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
c
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
836
|
+
return await recordClawchatToolCall("clawchat_list_moments", params, async () => {
|
|
837
|
+
const p = (params ?? {}) as ClawchatListMomentsParams;
|
|
838
|
+
return await withClient((c) =>
|
|
839
|
+
c.listMoments({
|
|
840
|
+
...(typeof p.before === "number" ? { before: p.before } : {}),
|
|
841
|
+
...(typeof p.limit === "number" ? { limit: p.limit } : {}),
|
|
842
|
+
}),
|
|
843
|
+
);
|
|
844
|
+
});
|
|
257
845
|
},
|
|
258
846
|
},
|
|
259
847
|
{ name: "clawchat_list_moments" },
|
|
@@ -270,18 +858,20 @@ export function registerOpenclawClawlingTools(api: OpenClawPluginApi): void {
|
|
|
270
858
|
),
|
|
271
859
|
parameters: ClawchatCreateMomentSchema,
|
|
272
860
|
async execute(_callId, params) {
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
c
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
861
|
+
return await recordClawchatToolCall("clawchat_create_moment", params, async () => {
|
|
862
|
+
const p = (params ?? {}) as ClawchatCreateMomentParams;
|
|
863
|
+
const text = typeof p.text === "string" ? p.text : undefined;
|
|
864
|
+
const images = Array.isArray(p.images) ? p.images : undefined;
|
|
865
|
+
if (!text && (!images || images.length === 0)) {
|
|
866
|
+
return validationError("openclaw-clawchat: at least one of text or images is required");
|
|
867
|
+
}
|
|
868
|
+
return await withClient((c) =>
|
|
869
|
+
c.createMoment({
|
|
870
|
+
...(text !== undefined ? { text } : {}),
|
|
871
|
+
...(images !== undefined ? { images } : {}),
|
|
872
|
+
}),
|
|
873
|
+
);
|
|
874
|
+
});
|
|
285
875
|
},
|
|
286
876
|
},
|
|
287
877
|
{ name: "clawchat_create_moment" },
|
|
@@ -298,8 +888,10 @@ export function registerOpenclawClawlingTools(api: OpenClawPluginApi): void {
|
|
|
298
888
|
),
|
|
299
889
|
parameters: ClawchatDeleteMomentSchema,
|
|
300
890
|
async execute(_callId, params) {
|
|
301
|
-
|
|
302
|
-
|
|
891
|
+
return await recordClawchatToolCall("clawchat_delete_moment", params, async () => {
|
|
892
|
+
const p = params as ClawchatDeleteMomentParams;
|
|
893
|
+
return await withClient((c) => c.deleteMoment(p.momentId));
|
|
894
|
+
});
|
|
303
895
|
},
|
|
304
896
|
},
|
|
305
897
|
{ name: "clawchat_delete_moment" },
|
|
@@ -316,13 +908,15 @@ export function registerOpenclawClawlingTools(api: OpenClawPluginApi): void {
|
|
|
316
908
|
),
|
|
317
909
|
parameters: ClawchatToggleMomentReactionSchema,
|
|
318
910
|
async execute(_callId, params) {
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
911
|
+
return await recordClawchatToolCall("clawchat_toggle_moment_reaction", params, async () => {
|
|
912
|
+
const p = params as ClawchatToggleMomentReactionParams;
|
|
913
|
+
if (!p.emoji?.trim()) {
|
|
914
|
+
return validationError("openclaw-clawchat: emoji is required");
|
|
915
|
+
}
|
|
916
|
+
return await withClient((c) =>
|
|
917
|
+
c.toggleMomentReaction({ momentId: p.momentId, emoji: p.emoji }),
|
|
918
|
+
);
|
|
919
|
+
});
|
|
326
920
|
},
|
|
327
921
|
},
|
|
328
922
|
{ name: "clawchat_toggle_moment_reaction" },
|
|
@@ -339,13 +933,15 @@ export function registerOpenclawClawlingTools(api: OpenClawPluginApi): void {
|
|
|
339
933
|
),
|
|
340
934
|
parameters: ClawchatCreateMomentCommentSchema,
|
|
341
935
|
async execute(_callId, params) {
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
936
|
+
return await recordClawchatToolCall("clawchat_create_moment_comment", params, async () => {
|
|
937
|
+
const p = params as ClawchatCreateMomentCommentParams;
|
|
938
|
+
if (!p.text?.trim()) {
|
|
939
|
+
return validationError("openclaw-clawchat: text is required");
|
|
940
|
+
}
|
|
941
|
+
return await withClient((c) =>
|
|
942
|
+
c.createMomentComment({ momentId: p.momentId, text: p.text }),
|
|
943
|
+
);
|
|
944
|
+
});
|
|
349
945
|
},
|
|
350
946
|
},
|
|
351
947
|
{ name: "clawchat_create_moment_comment" },
|
|
@@ -362,17 +958,19 @@ export function registerOpenclawClawlingTools(api: OpenClawPluginApi): void {
|
|
|
362
958
|
),
|
|
363
959
|
parameters: ClawchatReplyMomentCommentSchema,
|
|
364
960
|
async execute(_callId, params) {
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
c
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
961
|
+
return await recordClawchatToolCall("clawchat_reply_moment_comment", params, async () => {
|
|
962
|
+
const p = params as ClawchatReplyMomentCommentParams;
|
|
963
|
+
if (!p.text?.trim()) {
|
|
964
|
+
return validationError("openclaw-clawchat: text is required");
|
|
965
|
+
}
|
|
966
|
+
return await withClient((c) =>
|
|
967
|
+
c.replyMomentComment({
|
|
968
|
+
momentId: p.momentId,
|
|
969
|
+
replyToCommentId: p.replyToCommentId,
|
|
970
|
+
text: p.text,
|
|
971
|
+
}),
|
|
972
|
+
);
|
|
973
|
+
});
|
|
376
974
|
},
|
|
377
975
|
},
|
|
378
976
|
{ name: "clawchat_reply_moment_comment" },
|
|
@@ -389,10 +987,12 @@ export function registerOpenclawClawlingTools(api: OpenClawPluginApi): void {
|
|
|
389
987
|
),
|
|
390
988
|
parameters: ClawchatDeleteMomentCommentSchema,
|
|
391
989
|
async execute(_callId, params) {
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
990
|
+
return await recordClawchatToolCall("clawchat_delete_moment_comment", params, async () => {
|
|
991
|
+
const p = params as ClawchatDeleteMomentCommentParams;
|
|
992
|
+
return await withClient((c) =>
|
|
993
|
+
c.deleteMomentComment({ momentId: p.momentId, commentId: p.commentId }),
|
|
994
|
+
);
|
|
995
|
+
});
|
|
396
996
|
},
|
|
397
997
|
},
|
|
398
998
|
{ name: "clawchat_delete_moment_comment" },
|
|
@@ -420,17 +1020,19 @@ export function registerOpenclawClawlingTools(api: OpenClawPluginApi): void {
|
|
|
420
1020
|
),
|
|
421
1021
|
parameters: ClawchatUpdateAccountProfileSchema,
|
|
422
1022
|
async execute(_callId, params) {
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
1023
|
+
return await recordClawchatToolCall("clawchat_update_account_profile", params, async () => {
|
|
1024
|
+
const p = (params ?? {}) as ClawchatUpdateAccountProfileParams;
|
|
1025
|
+
const patch: { nickname?: string; avatar_url?: string; bio?: string } = {};
|
|
1026
|
+
if (typeof p.nickname === "string") patch.nickname = p.nickname;
|
|
1027
|
+
if (typeof p.avatar_url === "string") patch.avatar_url = p.avatar_url;
|
|
1028
|
+
if (typeof p.bio === "string") patch.bio = p.bio;
|
|
1029
|
+
if (Object.keys(patch).length === 0) {
|
|
1030
|
+
return validationError(
|
|
1031
|
+
"openclaw-clawchat: at least one of nickname / avatar / bio is required",
|
|
1032
|
+
);
|
|
1033
|
+
}
|
|
1034
|
+
return await withClient((c): Promise<Profile> => c.updateMyProfile(patch));
|
|
1035
|
+
});
|
|
434
1036
|
},
|
|
435
1037
|
},
|
|
436
1038
|
{ name: "clawchat_update_account_profile" },
|
|
@@ -447,30 +1049,32 @@ export function registerOpenclawClawlingTools(api: OpenClawPluginApi): void {
|
|
|
447
1049
|
),
|
|
448
1050
|
parameters: ClawchatUploadAvatarImageSchema,
|
|
449
1051
|
async execute(_callId, params) {
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
1052
|
+
return await recordClawchatToolCall("clawchat_upload_avatar_image", params, async () => {
|
|
1053
|
+
const p = params as ClawchatUploadAvatarImageParams;
|
|
1054
|
+
if (!p.filePath || !path.isAbsolute(p.filePath)) {
|
|
1055
|
+
return validationError("openclaw-clawchat: filePath must be an absolute local path");
|
|
1056
|
+
}
|
|
1057
|
+
let stat: fs.Stats;
|
|
1058
|
+
try {
|
|
1059
|
+
stat = fs.statSync(p.filePath);
|
|
1060
|
+
} catch (err) {
|
|
1061
|
+
return validationError(
|
|
1062
|
+
`openclaw-clawchat: cannot stat ${p.filePath}: ${err instanceof Error ? err.message : String(err)}`,
|
|
1063
|
+
);
|
|
1064
|
+
}
|
|
1065
|
+
if (!stat.isFile()) {
|
|
1066
|
+
return validationError(`openclaw-clawchat: ${p.filePath} is not a regular file`);
|
|
1067
|
+
}
|
|
1068
|
+
if (stat.size > MAX_UPLOAD_BYTES) {
|
|
1069
|
+
return validationError(
|
|
1070
|
+
`openclaw-clawchat: file too large (${stat.size} bytes; max 20MB)`,
|
|
1071
|
+
);
|
|
1072
|
+
}
|
|
1073
|
+
const buffer = fs.readFileSync(p.filePath);
|
|
1074
|
+
const filename = path.basename(p.filePath);
|
|
1075
|
+
const mime = inferMimeFromPath(p.filePath);
|
|
1076
|
+
return await withClient((c) => c.uploadAvatar({ buffer, filename, mime }));
|
|
1077
|
+
});
|
|
474
1078
|
},
|
|
475
1079
|
},
|
|
476
1080
|
{ name: "clawchat_upload_avatar_image" },
|
|
@@ -488,36 +1092,38 @@ export function registerOpenclawClawlingTools(api: OpenClawPluginApi): void {
|
|
|
488
1092
|
),
|
|
489
1093
|
parameters: ClawchatUploadMediaFileSchema,
|
|
490
1094
|
async execute(_callId, params) {
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
1095
|
+
return await recordClawchatToolCall("clawchat_upload_media_file", params, async () => {
|
|
1096
|
+
const p = params as ClawchatUploadMediaFileParams;
|
|
1097
|
+
if (!p.filePath || !path.isAbsolute(p.filePath)) {
|
|
1098
|
+
return validationError("openclaw-clawchat: filePath must be an absolute local path");
|
|
1099
|
+
}
|
|
1100
|
+
let stat: fs.Stats;
|
|
1101
|
+
try {
|
|
1102
|
+
stat = fs.statSync(p.filePath);
|
|
1103
|
+
} catch (err) {
|
|
1104
|
+
return validationError(
|
|
1105
|
+
`openclaw-clawchat: cannot stat ${p.filePath}: ${err instanceof Error ? err.message : String(err)}`,
|
|
1106
|
+
);
|
|
1107
|
+
}
|
|
1108
|
+
if (!stat.isFile()) {
|
|
1109
|
+
return validationError(`openclaw-clawchat: ${p.filePath} is not a regular file`);
|
|
1110
|
+
}
|
|
1111
|
+
if (stat.size > MAX_UPLOAD_BYTES) {
|
|
1112
|
+
return validationError(
|
|
1113
|
+
`openclaw-clawchat: file too large (${stat.size} bytes; max 20MB)`,
|
|
1114
|
+
);
|
|
1115
|
+
}
|
|
1116
|
+
const buffer = fs.readFileSync(p.filePath);
|
|
1117
|
+
const filename = path.basename(p.filePath);
|
|
1118
|
+
const mime = inferMimeFromPath(p.filePath);
|
|
1119
|
+
return await withClient((c) => c.uploadMedia({ buffer, filename, mime }));
|
|
1120
|
+
});
|
|
515
1121
|
},
|
|
516
1122
|
},
|
|
517
1123
|
{ name: "clawchat_upload_media_file" },
|
|
518
1124
|
);
|
|
519
1125
|
|
|
520
1126
|
api.logger.debug?.(
|
|
521
|
-
"openclaw-clawchat: registered
|
|
1127
|
+
"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)",
|
|
522
1128
|
);
|
|
523
1129
|
}
|