@clawling/clawchat-plugin-openclaw 2026.9.11-1 → 2026.9.13-2
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/dist/src/account-config-writes.js +67 -0
- package/dist/src/account-removal.js +44 -0
- package/dist/src/channel.js +30 -1
- package/dist/src/channel.setup.js +79 -42
- package/dist/src/commands.js +50 -27
- package/dist/src/config.js +83 -13
- package/dist/src/liveware-cli.js +49 -4
- package/dist/src/liveware-sample.js +16 -2
- package/dist/src/login.runtime.js +25 -29
- package/dist/src/outbound.js +4 -4
- package/dist/src/runtime.js +35 -32
- package/dist/src/tools.js +799 -685
- package/openclaw.plugin.json +150 -0
- package/package.json +1 -1
- package/src/account-config-writes.ts +74 -0
- package/src/account-removal.ts +52 -0
- package/src/channel.setup.ts +86 -43
- package/src/channel.ts +28 -1
- package/src/commands.ts +60 -26
- package/src/config.ts +113 -14
- package/src/liveware-cli.ts +52 -4
- package/src/liveware-sample.ts +24 -5
- package/src/login.runtime.ts +30 -26
- package/src/outbound.ts +4 -4
- package/src/runtime.ts +52 -36
- package/src/tools.ts +896 -768
package/dist/src/tools.js
CHANGED
|
@@ -5,7 +5,7 @@ import { createOpenclawClawlingApiClient } from "./api-client.js";
|
|
|
5
5
|
import { resolveLivewarePath } from "./liveware-cli.js";
|
|
6
6
|
import { ClawlingApiError, } from "./api-types.js";
|
|
7
7
|
import { mapGateOutcome } from "./gate-outcome.js";
|
|
8
|
-
import { resolveOpenclawClawlingAccount } from "./config.js";
|
|
8
|
+
import { CHANNEL_ID, normalizeOpenclawClawlingAccountId, resolveOpenclawClawlingAccount, } from "./config.js";
|
|
9
9
|
import { clawChatDbPathForStateDir, getClawChatStore, makeOwnerProfilePersister, } from "./storage.js";
|
|
10
10
|
import { sendOpenclawClawlingMentionMessage, sendOpenclawClawlingReaction } from "./outbound.js";
|
|
11
11
|
import { getOpenclawClawlingClient, } from "./runtime.js";
|
|
@@ -166,14 +166,38 @@ function validateMentionMessageParams(params) {
|
|
|
166
166
|
}
|
|
167
167
|
return { ok: true, params: p };
|
|
168
168
|
}
|
|
169
|
+
/**
|
|
170
|
+
* Account a tool call belongs to, or `null` when the call carries no ClawChat
|
|
171
|
+
* context (the caller then falls back to the configured default account).
|
|
172
|
+
*
|
|
173
|
+
* Both context fields are only meaningful when they describe THIS channel:
|
|
174
|
+
* `agentAccountId` is the originating session's account id, so a turn that
|
|
175
|
+
* arrived over Telegram/Discord would otherwise hand us that channel's account
|
|
176
|
+
* id as if it were one of ours.
|
|
177
|
+
*/
|
|
178
|
+
export function resolveOpenclawClawlingToolAccountId(ctx) {
|
|
179
|
+
const delivery = ctx.deliveryContext;
|
|
180
|
+
if (delivery &&
|
|
181
|
+
delivery.channel === CHANNEL_ID &&
|
|
182
|
+
typeof delivery.accountId === "string" &&
|
|
183
|
+
delivery.accountId.trim()) {
|
|
184
|
+
return normalizeOpenclawClawlingAccountId(delivery.accountId);
|
|
185
|
+
}
|
|
186
|
+
if (ctx.messageChannel === CHANNEL_ID &&
|
|
187
|
+
typeof ctx.agentAccountId === "string" &&
|
|
188
|
+
ctx.agentAccountId.trim()) {
|
|
189
|
+
return normalizeOpenclawClawlingAccountId(ctx.agentAccountId);
|
|
190
|
+
}
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
169
193
|
export function registerOpenclawClawlingTools(api, options = {}) {
|
|
170
194
|
if (!api.config) {
|
|
171
195
|
api.logger.debug?.("clawchat-plugin-openclaw: api.config missing; skipping tool registration");
|
|
172
196
|
return;
|
|
173
197
|
}
|
|
174
198
|
// Re-resolve at call time so config reloads pick up new tokens / baseUrl.
|
|
175
|
-
function resolveCurrent() {
|
|
176
|
-
return resolveOpenclawClawlingAccount(api.config);
|
|
199
|
+
function resolveCurrent(accountId) {
|
|
200
|
+
return resolveOpenclawClawlingAccount(api.config, accountId);
|
|
177
201
|
}
|
|
178
202
|
function resolveStore() {
|
|
179
203
|
if ("store" in options)
|
|
@@ -208,9 +232,9 @@ export function registerOpenclawClawlingTools(api, options = {}) {
|
|
|
208
232
|
api.logger.error?.("clawchat-plugin-openclaw sqlite tool call insert failed; continuing");
|
|
209
233
|
}
|
|
210
234
|
}
|
|
211
|
-
async function recordClawchatToolCall(toolName, params, fn) {
|
|
235
|
+
async function recordClawchatToolCall(accountId, toolName, params, fn) {
|
|
212
236
|
const startedAt = Date.now();
|
|
213
|
-
const account = resolveCurrent();
|
|
237
|
+
const account = resolveCurrent(accountId);
|
|
214
238
|
try {
|
|
215
239
|
const result = await fn();
|
|
216
240
|
const details = result.details ?? result;
|
|
@@ -240,8 +264,8 @@ export function registerOpenclawClawlingTools(api, options = {}) {
|
|
|
240
264
|
throw err;
|
|
241
265
|
}
|
|
242
266
|
}
|
|
243
|
-
function buildClient() {
|
|
244
|
-
const acct = resolveCurrent();
|
|
267
|
+
function buildClient(accountId) {
|
|
268
|
+
const acct = resolveCurrent(accountId);
|
|
245
269
|
// `baseUrl` always resolves via the built-in default in config.ts, so we
|
|
246
270
|
// only need to gate on `token` here (which is populated by ClawChat
|
|
247
271
|
// activation/login).
|
|
@@ -257,9 +281,9 @@ export function registerOpenclawClawlingTools(api, options = {}) {
|
|
|
257
281
|
}),
|
|
258
282
|
};
|
|
259
283
|
}
|
|
260
|
-
function withClient(fn) {
|
|
284
|
+
function withClient(accountId, fn) {
|
|
261
285
|
return (async () => {
|
|
262
|
-
const built = buildClient();
|
|
286
|
+
const built = buildClient(accountId);
|
|
263
287
|
if (!built.ok)
|
|
264
288
|
return built.error;
|
|
265
289
|
try {
|
|
@@ -278,9 +302,9 @@ export function registerOpenclawClawlingTools(api, options = {}) {
|
|
|
278
302
|
* via mapGateOutcome so the agent sees a clear pending/forbidden status
|
|
279
303
|
* instead of a generic error. Used for friend and moment mutation tools.
|
|
280
304
|
*/
|
|
281
|
-
function withGatedClient(fn) {
|
|
305
|
+
function withGatedClient(accountId, fn) {
|
|
282
306
|
return (async () => {
|
|
283
|
-
const built = buildClient();
|
|
307
|
+
const built = buildClient(accountId);
|
|
284
308
|
if (!built.ok)
|
|
285
309
|
return built.error;
|
|
286
310
|
try {
|
|
@@ -502,739 +526,829 @@ export function registerOpenclawClawlingTools(api, options = {}) {
|
|
|
502
526
|
}
|
|
503
527
|
},
|
|
504
528
|
}), { name: "clawchat_memory_edit" });
|
|
505
|
-
api.registerTool((ctx) =>
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
"
|
|
510
|
-
"
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
529
|
+
api.registerTool((ctx) => {
|
|
530
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
531
|
+
return {
|
|
532
|
+
name: "clawchat_metadata_sync",
|
|
533
|
+
label: "Sync ClawChat Metadata",
|
|
534
|
+
description: memoryToolDescription("Synchronize the ClawChat metadata block for an explicit owner, user, or group target. " +
|
|
535
|
+
"Use direction=pull when the user asks to refresh, sync, or update local ClawChat current agent profile/behavior, agent-owner profile information, user information, or group information from the server; this fetches the authoritative server record and rewrites only the metadata block. " +
|
|
536
|
+
"Use direction=push only to push selected existing local metadata fields to the server, then refresh the metadata block from the server response. " +
|
|
537
|
+
"For direction=push, pass non-empty fields containing only pushable metadata field names: owner supports agent_behavior only; user supports nickname/avatar_url/bio for the connected account only; group supports group_title/group_description. " +
|
|
538
|
+
"This does not modify the agent-authored body. " +
|
|
539
|
+
"This synchronizes only the metadata block. It does not search or read agent-authored long-term memory body. For remembered aliases or local notes, use clawchat_memory_search or clawchat_memory_read. " +
|
|
540
|
+
"Do not combine clawchat_get_user_profile with clawchat_memory_write to update local profile metadata."),
|
|
541
|
+
parameters: ClawchatMetadataSyncSchema,
|
|
542
|
+
async execute(_callId, params) {
|
|
543
|
+
const root = workspaceRoot(ctx);
|
|
544
|
+
if (!root) {
|
|
545
|
+
return validationError("clawchat-plugin-openclaw: OpenClaw workspaceDir is required");
|
|
546
|
+
}
|
|
547
|
+
const built = buildClient(accountId);
|
|
548
|
+
if (!built.ok)
|
|
549
|
+
return built.error;
|
|
550
|
+
try {
|
|
551
|
+
const p = params;
|
|
552
|
+
const targetError = validateTarget(root, p);
|
|
553
|
+
if (targetError)
|
|
554
|
+
return memoryToolError(new Error(targetError));
|
|
555
|
+
const account = resolveCurrent(accountId);
|
|
556
|
+
if (p.direction === "push") {
|
|
557
|
+
if (!Array.isArray(p.fields) || p.fields.length === 0) {
|
|
558
|
+
return validationError("clawchat-plugin-openclaw: fields are required for metadata push");
|
|
559
|
+
}
|
|
560
|
+
if (p.targetType === "owner" && !account.agentId) {
|
|
561
|
+
return configError("clawchat-plugin-openclaw: agentId is required");
|
|
562
|
+
}
|
|
563
|
+
if (p.targetType === "user" && !account.userId) {
|
|
564
|
+
return configError("clawchat-plugin-openclaw: userId is required");
|
|
565
|
+
}
|
|
566
|
+
return jsonResponse(await pushMetadata({
|
|
567
|
+
memoryRoot: root,
|
|
568
|
+
targetType: p.targetType,
|
|
569
|
+
targetId: p.targetId,
|
|
570
|
+
fields: p.fields,
|
|
571
|
+
agentId: account.agentId,
|
|
572
|
+
accountUserId: account.userId,
|
|
573
|
+
api: built.client,
|
|
574
|
+
}));
|
|
575
|
+
}
|
|
576
|
+
if (p.targetType === "owner") {
|
|
577
|
+
if (!account.agentId)
|
|
578
|
+
return configError("clawchat-plugin-openclaw: agentId is required");
|
|
579
|
+
return jsonResponse(await pullOwnerMetadata({
|
|
580
|
+
memoryRoot: root,
|
|
581
|
+
agentId: account.agentId,
|
|
582
|
+
accountUserId: account.userId,
|
|
583
|
+
accountOwnerUserId: account.ownerUserId || undefined,
|
|
584
|
+
api: built.client,
|
|
585
|
+
}));
|
|
533
586
|
}
|
|
587
|
+
if (p.targetType === "user") {
|
|
588
|
+
return jsonResponse(await pullUserMetadata({ memoryRoot: root, userId: p.targetId, api: built.client }));
|
|
589
|
+
}
|
|
590
|
+
return jsonResponse(await pullGroupMetadata({
|
|
591
|
+
memoryRoot: root,
|
|
592
|
+
groupId: p.targetId,
|
|
593
|
+
api: built.client,
|
|
594
|
+
skipUserIds: [account.userId, account.ownerUserId],
|
|
595
|
+
}));
|
|
596
|
+
}
|
|
597
|
+
catch (err) {
|
|
598
|
+
return memoryToolError(err);
|
|
599
|
+
}
|
|
600
|
+
},
|
|
601
|
+
};
|
|
602
|
+
}, { name: "clawchat_metadata_sync" });
|
|
603
|
+
api.registerTool((ctx) => {
|
|
604
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
605
|
+
return {
|
|
606
|
+
name: "clawchat_metadata_update",
|
|
607
|
+
label: "Update ClawChat Metadata",
|
|
608
|
+
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. " +
|
|
609
|
+
"Use this only when the user wants to change server-side metadata fields: current agent behavior via owner target agent_behavior, connected-user nickname/avatar_url/bio, or group group_title/group_description. " +
|
|
610
|
+
"To refresh local metadata from the server without changing the server, use clawchat_metadata_sync with direction=pull. " +
|
|
611
|
+
"This always pushes to the server first and does not modify the agent-authored body."),
|
|
612
|
+
parameters: ClawchatMetadataUpdateSchema,
|
|
613
|
+
async execute(_callId, params) {
|
|
614
|
+
const root = workspaceRoot(ctx);
|
|
615
|
+
if (!root) {
|
|
616
|
+
return validationError("clawchat-plugin-openclaw: OpenClaw workspaceDir is required");
|
|
617
|
+
}
|
|
618
|
+
const built = buildClient(accountId);
|
|
619
|
+
if (!built.ok)
|
|
620
|
+
return built.error;
|
|
621
|
+
try {
|
|
622
|
+
const p = params;
|
|
623
|
+
const targetError = validateTarget(root, p);
|
|
624
|
+
if (targetError)
|
|
625
|
+
return memoryToolError(new Error(targetError));
|
|
626
|
+
const patchError = validateMetadataUpdatePatch(p);
|
|
627
|
+
if (patchError)
|
|
628
|
+
return validationError(patchError);
|
|
629
|
+
const account = resolveCurrent(accountId);
|
|
534
630
|
if (p.targetType === "owner" && !account.agentId) {
|
|
535
631
|
return configError("clawchat-plugin-openclaw: agentId is required");
|
|
536
632
|
}
|
|
537
633
|
if (p.targetType === "user" && !account.userId) {
|
|
538
634
|
return configError("clawchat-plugin-openclaw: userId is required");
|
|
539
635
|
}
|
|
540
|
-
return jsonResponse(await
|
|
636
|
+
return jsonResponse(await updateMetadata({
|
|
541
637
|
memoryRoot: root,
|
|
542
638
|
targetType: p.targetType,
|
|
543
639
|
targetId: p.targetId,
|
|
544
|
-
fields: p.fields,
|
|
545
640
|
agentId: account.agentId,
|
|
546
641
|
accountUserId: account.userId,
|
|
642
|
+
patch: p.patch,
|
|
547
643
|
api: built.client,
|
|
644
|
+
persistOwnerProfile: makeOwnerProfilePersister(getClawChatStore(), {
|
|
645
|
+
accountId: account.accountId,
|
|
646
|
+
}),
|
|
548
647
|
}));
|
|
549
648
|
}
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
return configError("clawchat-plugin-openclaw: agentId is required");
|
|
553
|
-
return jsonResponse(await pullOwnerMetadata({
|
|
554
|
-
memoryRoot: root,
|
|
555
|
-
agentId: account.agentId,
|
|
556
|
-
accountUserId: account.userId,
|
|
557
|
-
accountOwnerUserId: account.ownerUserId || undefined,
|
|
558
|
-
api: built.client,
|
|
559
|
-
}));
|
|
560
|
-
}
|
|
561
|
-
if (p.targetType === "user") {
|
|
562
|
-
return jsonResponse(await pullUserMetadata({ memoryRoot: root, userId: p.targetId, api: built.client }));
|
|
649
|
+
catch (err) {
|
|
650
|
+
return memoryToolError(err);
|
|
563
651
|
}
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
652
|
+
},
|
|
653
|
+
};
|
|
654
|
+
}, { name: "clawchat_metadata_update" });
|
|
655
|
+
}
|
|
656
|
+
registerMemoryTools();
|
|
657
|
+
api.registerTool((ctx) => {
|
|
658
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
659
|
+
return {
|
|
660
|
+
name: "clawchat_get_account_profile",
|
|
661
|
+
label: "Get ClawChat Account Profile",
|
|
662
|
+
description: toolDescription("Fetch the agent's connected ClawChat account profile (the configured ClawChat account: user id, nickname/display name, avatar, bio). " +
|
|
663
|
+
"This profile is the platform-side mirror of the local assistant identity; if fields are missing, report them as unset instead of inventing values. " +
|
|
664
|
+
"TRIGGER — invoke when the user asks for the ClawChat account/profile connected to this agent, " +
|
|
665
|
+
"such as 'show my ClawChat profile', 'what is the configured ClawChat account?', " +
|
|
666
|
+
"'show the current ClawChat account details', or 'show the ClawChat nickname, avatar, and bio'. " +
|
|
667
|
+
"Do not frame this as a human user's personal account."),
|
|
668
|
+
parameters: ClawchatGetAccountProfileSchema,
|
|
669
|
+
async execute(_callId, params) {
|
|
670
|
+
return await recordClawchatToolCall(accountId, "clawchat_get_account_profile", params, async () => withClient(accountId, (c) => c.getMyProfile()));
|
|
574
671
|
},
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
672
|
+
};
|
|
673
|
+
}, { name: "clawchat_get_account_profile" });
|
|
674
|
+
api.registerTool((ctx) => {
|
|
675
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
676
|
+
return {
|
|
677
|
+
name: "clawchat_get_user_profile",
|
|
678
|
+
label: "Get ClawChat User Profile",
|
|
679
|
+
description: toolDescription("Fetch a ClawChat user's server-side public profile by explicit userId. " +
|
|
680
|
+
"TRIGGER — invoke when the user asks to view or inspect a specific ClawChat user's public profile and provides a concrete userId, or after clawchat_search_users returns a userId. " +
|
|
681
|
+
"Do not guess or infer userId from nickname, display name, alias, or local memory text. " +
|
|
682
|
+
"This is a read-only lookup and server lookup. It does not read local ClawChat memory files and does not update local metadata. " +
|
|
683
|
+
"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. " +
|
|
684
|
+
"Use `clawchat_get_account_profile` for the agent's own connected ClawChat account unless an explicit userId is provided."),
|
|
685
|
+
parameters: ClawchatGetUserProfileSchema,
|
|
584
686
|
async execute(_callId, params) {
|
|
585
|
-
|
|
586
|
-
if (!root) {
|
|
587
|
-
return validationError("clawchat-plugin-openclaw: OpenClaw workspaceDir is required");
|
|
588
|
-
}
|
|
589
|
-
const built = buildClient();
|
|
590
|
-
if (!built.ok)
|
|
591
|
-
return built.error;
|
|
592
|
-
try {
|
|
687
|
+
return await recordClawchatToolCall(accountId, "clawchat_get_user_profile", params, async () => {
|
|
593
688
|
const p = params;
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
return memoryToolError(new Error(targetError));
|
|
597
|
-
const patchError = validateMetadataUpdatePatch(p);
|
|
598
|
-
if (patchError)
|
|
599
|
-
return validationError(patchError);
|
|
600
|
-
const account = resolveCurrent();
|
|
601
|
-
if (p.targetType === "owner" && !account.agentId) {
|
|
602
|
-
return configError("clawchat-plugin-openclaw: agentId is required");
|
|
603
|
-
}
|
|
604
|
-
if (p.targetType === "user" && !account.userId) {
|
|
605
|
-
return configError("clawchat-plugin-openclaw: userId is required");
|
|
606
|
-
}
|
|
607
|
-
return jsonResponse(await updateMetadata({
|
|
608
|
-
memoryRoot: root,
|
|
609
|
-
targetType: p.targetType,
|
|
610
|
-
targetId: p.targetId,
|
|
611
|
-
agentId: account.agentId,
|
|
612
|
-
accountUserId: account.userId,
|
|
613
|
-
patch: p.patch,
|
|
614
|
-
api: built.client,
|
|
615
|
-
persistOwnerProfile: makeOwnerProfilePersister(getClawChatStore(), {
|
|
616
|
-
accountId: account.accountId,
|
|
617
|
-
}),
|
|
618
|
-
}));
|
|
619
|
-
}
|
|
620
|
-
catch (err) {
|
|
621
|
-
return memoryToolError(err);
|
|
622
|
-
}
|
|
689
|
+
return await withClient(accountId, (c) => c.getUserInfo(p.userId));
|
|
690
|
+
});
|
|
623
691
|
},
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
registerMemoryTools();
|
|
627
|
-
api.registerTool({
|
|
628
|
-
name: "clawchat_get_account_profile",
|
|
629
|
-
label: "Get ClawChat Account Profile",
|
|
630
|
-
description: toolDescription("Fetch the agent's connected ClawChat account profile (the configured ClawChat account: user id, nickname/display name, avatar, bio). " +
|
|
631
|
-
"This profile is the platform-side mirror of the local assistant identity; if fields are missing, report them as unset instead of inventing values. " +
|
|
632
|
-
"TRIGGER — invoke when the user asks for the ClawChat account/profile connected to this agent, " +
|
|
633
|
-
"such as 'show my ClawChat profile', 'what is the configured ClawChat account?', " +
|
|
634
|
-
"'show the current ClawChat account details', or 'show the ClawChat nickname, avatar, and bio'. " +
|
|
635
|
-
"Do not frame this as a human user's personal account."),
|
|
636
|
-
parameters: ClawchatGetAccountProfileSchema,
|
|
637
|
-
async execute(_callId, params) {
|
|
638
|
-
return await recordClawchatToolCall("clawchat_get_account_profile", params, async () => withClient((c) => c.getMyProfile()));
|
|
639
|
-
},
|
|
640
|
-
}, { name: "clawchat_get_account_profile" });
|
|
641
|
-
api.registerTool({
|
|
642
|
-
name: "clawchat_get_user_profile",
|
|
643
|
-
label: "Get ClawChat User Profile",
|
|
644
|
-
description: toolDescription("Fetch a ClawChat user's server-side public profile by explicit userId. " +
|
|
645
|
-
"TRIGGER — invoke when the user asks to view or inspect a specific ClawChat user's public profile and provides a concrete userId, or after clawchat_search_users returns a userId. " +
|
|
646
|
-
"Do not guess or infer userId from nickname, display name, alias, or local memory text. " +
|
|
647
|
-
"This is a read-only lookup and server lookup. It does not read local ClawChat memory files and does not update local metadata. " +
|
|
648
|
-
"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. " +
|
|
649
|
-
"Use `clawchat_get_account_profile` for the agent's own connected ClawChat account unless an explicit userId is provided."),
|
|
650
|
-
parameters: ClawchatGetUserProfileSchema,
|
|
651
|
-
async execute(_callId, params) {
|
|
652
|
-
return await recordClawchatToolCall("clawchat_get_user_profile", params, async () => {
|
|
653
|
-
const p = params;
|
|
654
|
-
return await withClient((c) => c.getUserInfo(p.userId));
|
|
655
|
-
});
|
|
656
|
-
},
|
|
692
|
+
};
|
|
657
693
|
}, { name: "clawchat_get_user_profile" });
|
|
658
|
-
api.registerTool({
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
"
|
|
663
|
-
"
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
694
|
+
api.registerTool((ctx) => {
|
|
695
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
696
|
+
return {
|
|
697
|
+
name: "clawchat_list_account_friends",
|
|
698
|
+
label: "List ClawChat Account Friends",
|
|
699
|
+
description: toolDescription("List friends/contacts of the agent's connected ClawChat account (the configured ClawChat account). " +
|
|
700
|
+
"These are the agent's ClawChat-platform contacts. " +
|
|
701
|
+
"TRIGGER — invoke when the user asks for this ClawChat account's friends, contacts, or friend list."),
|
|
702
|
+
parameters: ClawchatListAccountFriendsSchema,
|
|
703
|
+
async execute(_callId, params) {
|
|
704
|
+
return await recordClawchatToolCall(accountId, "clawchat_list_account_friends", params, async () => withClient(accountId, (c) => c.listFriends()));
|
|
705
|
+
},
|
|
706
|
+
};
|
|
668
707
|
}, { name: "clawchat_list_account_friends" });
|
|
669
|
-
api.registerTool({
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
"
|
|
674
|
-
"
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
return await
|
|
680
|
-
|
|
681
|
-
|
|
708
|
+
api.registerTool((ctx) => {
|
|
709
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
710
|
+
return {
|
|
711
|
+
name: "clawchat_send_friend_request",
|
|
712
|
+
label: "Send ClawChat Friend Request",
|
|
713
|
+
description: toolDescription("Send a friend request from the connected ClawChat account to a specific user by explicit userId. " +
|
|
714
|
+
"TRIGGER - invoke when the user asks to add, friend, connect with, or send a friend request to a ClawChat user and provides a concrete userId, or after clawchat_search_users returns a selected userId. " +
|
|
715
|
+
"Do not guess or infer userId from nickname, display name, alias, or local memory text; search first when needed."),
|
|
716
|
+
parameters: ClawchatSendFriendRequestSchema,
|
|
717
|
+
async execute(_callId, params) {
|
|
718
|
+
return await recordClawchatToolCall(accountId, "clawchat_send_friend_request", params, async () => {
|
|
719
|
+
const p = params;
|
|
720
|
+
return await withGatedClient(accountId, (c) => c.sendFriendRequest(p));
|
|
721
|
+
});
|
|
722
|
+
},
|
|
723
|
+
};
|
|
682
724
|
}, { name: "clawchat_send_friend_request" });
|
|
683
|
-
api.registerTool({
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
"
|
|
688
|
-
"
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
return await
|
|
694
|
-
|
|
695
|
-
|
|
725
|
+
api.registerTool((ctx) => {
|
|
726
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
727
|
+
return {
|
|
728
|
+
name: "clawchat_list_friend_requests",
|
|
729
|
+
label: "List ClawChat Friend Requests",
|
|
730
|
+
description: toolDescription("List pending friend requests for the connected ClawChat account. " +
|
|
731
|
+
"Use direction='incoming' for requests other users sent to this account, and direction='outgoing' for requests this account sent to other users. Defaults to incoming. " +
|
|
732
|
+
"TRIGGER - invoke when the user asks to view, check, review, or list friend requests, pending requests, incoming requests, or outgoing requests."),
|
|
733
|
+
parameters: ClawchatListFriendRequestsSchema,
|
|
734
|
+
async execute(_callId, params) {
|
|
735
|
+
return await recordClawchatToolCall(accountId, "clawchat_list_friend_requests", params, async () => {
|
|
736
|
+
const p = (params ?? {});
|
|
737
|
+
return await withClient(accountId, (c) => c.listFriendRequests({ direction: p.direction ?? "incoming" }));
|
|
738
|
+
});
|
|
739
|
+
},
|
|
740
|
+
};
|
|
696
741
|
}, { name: "clawchat_list_friend_requests" });
|
|
697
|
-
api.registerTool({
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
"
|
|
702
|
-
"
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
return await
|
|
708
|
-
|
|
709
|
-
|
|
742
|
+
api.registerTool((ctx) => {
|
|
743
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
744
|
+
return {
|
|
745
|
+
name: "clawchat_accept_friend_request",
|
|
746
|
+
label: "Accept ClawChat Friend Request",
|
|
747
|
+
description: toolDescription("Accept one pending incoming friend request by exact requestId. " +
|
|
748
|
+
"TRIGGER - invoke when the user asks to accept, approve, or pass a friend request and provides or selects a concrete requestId returned by clawchat_list_friend_requests. " +
|
|
749
|
+
"List incoming requests first when the target is ambiguous."),
|
|
750
|
+
parameters: ClawchatAcceptFriendRequestSchema,
|
|
751
|
+
async execute(_callId, params) {
|
|
752
|
+
return await recordClawchatToolCall(accountId, "clawchat_accept_friend_request", params, async () => {
|
|
753
|
+
const p = params;
|
|
754
|
+
return await withGatedClient(accountId, (c) => c.acceptFriendRequest(p.requestId));
|
|
755
|
+
});
|
|
756
|
+
},
|
|
757
|
+
};
|
|
710
758
|
}, { name: "clawchat_accept_friend_request" });
|
|
711
|
-
api.registerTool({
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
"
|
|
716
|
-
"
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
return await
|
|
722
|
-
|
|
723
|
-
|
|
759
|
+
api.registerTool((ctx) => {
|
|
760
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
761
|
+
return {
|
|
762
|
+
name: "clawchat_reject_friend_request",
|
|
763
|
+
label: "Reject ClawChat Friend Request",
|
|
764
|
+
description: toolDescription("Reject one pending incoming friend request by exact requestId. " +
|
|
765
|
+
"TRIGGER - invoke when the user asks to reject, decline, refuse, or ignore a friend request and provides or selects a concrete requestId returned by clawchat_list_friend_requests. " +
|
|
766
|
+
"List incoming requests first when the target is ambiguous."),
|
|
767
|
+
parameters: ClawchatRejectFriendRequestSchema,
|
|
768
|
+
async execute(_callId, params) {
|
|
769
|
+
return await recordClawchatToolCall(accountId, "clawchat_reject_friend_request", params, async () => {
|
|
770
|
+
const p = params;
|
|
771
|
+
return await withGatedClient(accountId, (c) => c.rejectFriendRequest(p.requestId));
|
|
772
|
+
});
|
|
773
|
+
},
|
|
774
|
+
};
|
|
724
775
|
}, { name: "clawchat_reject_friend_request" });
|
|
725
|
-
api.registerTool({
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
"
|
|
730
|
-
"
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
return await
|
|
736
|
-
|
|
737
|
-
|
|
776
|
+
api.registerTool((ctx) => {
|
|
777
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
778
|
+
return {
|
|
779
|
+
name: "clawchat_remove_friend",
|
|
780
|
+
label: "Remove ClawChat Friend",
|
|
781
|
+
description: toolDescription("Remove an accepted friend from the connected ClawChat account by explicit friendUserId. " +
|
|
782
|
+
"TRIGGER - invoke when the user asks to delete, remove, unfriend, or disconnect an existing ClawChat friend and provides a concrete friendUserId. " +
|
|
783
|
+
"Use clawchat_list_account_friends first when the target is ambiguous; do not guess ids from names."),
|
|
784
|
+
parameters: ClawchatRemoveFriendSchema,
|
|
785
|
+
async execute(_callId, params) {
|
|
786
|
+
return await recordClawchatToolCall(accountId, "clawchat_remove_friend", params, async () => {
|
|
787
|
+
const p = params;
|
|
788
|
+
return await withGatedClient(accountId, (c) => c.removeFriend(p.friendUserId));
|
|
789
|
+
});
|
|
790
|
+
},
|
|
791
|
+
};
|
|
738
792
|
}, { name: "clawchat_remove_friend" });
|
|
739
|
-
api.registerTool({
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
"
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
return await
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
793
|
+
api.registerTool((ctx) => {
|
|
794
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
795
|
+
return {
|
|
796
|
+
name: "clawchat_search_users",
|
|
797
|
+
label: "Search ClawChat Users",
|
|
798
|
+
description: toolDescription("Search ClawChat users by username or nickname. Search server-side ClawChat users in the ClawChat user directory. " +
|
|
799
|
+
"TRIGGER - invoke when the user asks to search, find, or look up ClawChat users in the server directory by a typed query, username, or public nickname, such as \"search ClawChat users named Alice\", \"find ClawChat user Alice\", or \"look up nickname Alice\". " +
|
|
800
|
+
"This does not search local ClawChat memory files, aliases, known_as notes, relationship notes, group notes, or agent-authored Markdown memory. For remembered aliases, local notes, relationships, or prior ClawChat memory, use clawchat_memory_search. " +
|
|
801
|
+
"Empty q returns no users. Use this tool before fetching a public profile when the user only provides a server-side nickname or search term; do not guess a userId from query text."),
|
|
802
|
+
parameters: ClawchatSearchUsersSchema,
|
|
803
|
+
async execute(_callId, params) {
|
|
804
|
+
return await recordClawchatToolCall(accountId, "clawchat_search_users", params, async () => {
|
|
805
|
+
const p = (params ?? {});
|
|
806
|
+
return await withClient(accountId, (c) => c.searchUsers({
|
|
807
|
+
...(typeof p.q === "string" ? { q: p.q } : {}),
|
|
808
|
+
...(typeof p.limit === "number" ? { limit: p.limit } : {}),
|
|
809
|
+
}));
|
|
810
|
+
});
|
|
811
|
+
},
|
|
812
|
+
};
|
|
756
813
|
}, { name: "clawchat_search_users" });
|
|
757
|
-
api.registerTool({
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
"
|
|
762
|
-
"
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
if (err instanceof ClawlingApiError) {
|
|
776
|
-
return apiError(err);
|
|
814
|
+
api.registerTool((ctx) => {
|
|
815
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
816
|
+
return {
|
|
817
|
+
name: "clawchat_get_conversation",
|
|
818
|
+
label: "Get ClawChat Conversation",
|
|
819
|
+
description: toolDescription("Fetch read-only ClawChat conversation details, including group membership when returned by the API. " +
|
|
820
|
+
"TRIGGER - invoke when the user asks to inspect a specific ClawChat conversation or group and provides a concrete conversationId. " +
|
|
821
|
+
"This is read-only and does not create, update, leave, dissolve, or administer conversations."),
|
|
822
|
+
parameters: ClawchatGetConversationSchema,
|
|
823
|
+
async execute(_callId, params) {
|
|
824
|
+
return await recordClawchatToolCall(accountId, "clawchat_get_conversation", params, async () => {
|
|
825
|
+
const p = params;
|
|
826
|
+
const built = buildClient(accountId);
|
|
827
|
+
if (!built.ok)
|
|
828
|
+
return built.error;
|
|
829
|
+
try {
|
|
830
|
+
const data = await built.client.getConversation(p.conversationId);
|
|
831
|
+
return jsonResponse(data);
|
|
777
832
|
}
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
833
|
+
catch (err) {
|
|
834
|
+
if (err instanceof ClawlingApiError) {
|
|
835
|
+
return apiError(err);
|
|
836
|
+
}
|
|
837
|
+
return genericError(err);
|
|
838
|
+
}
|
|
839
|
+
});
|
|
840
|
+
},
|
|
841
|
+
};
|
|
782
842
|
}, { name: "clawchat_get_conversation" });
|
|
783
|
-
api.registerTool({
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
"
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
if (err instanceof ClawlingApiError) {
|
|
804
|
-
return apiError(err);
|
|
843
|
+
api.registerTool((ctx) => {
|
|
844
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
845
|
+
return {
|
|
846
|
+
name: "clawchat_leave_group",
|
|
847
|
+
label: "Leave ClawChat Group",
|
|
848
|
+
description: toolDescription("Leave a ClawChat group conversation the agent is currently a member of. " +
|
|
849
|
+
"TRIGGER - invoke only when the user explicitly asks the agent to leave, exit, or quit a specific ClawChat group and provides a concrete conversationId. " +
|
|
850
|
+
"Groups only: direct conversations are rejected by the server. " +
|
|
851
|
+
"If the agent is the group owner, leaving auto-transfers ownership to the earliest human member; if no human member remains, the group is dissolved. " +
|
|
852
|
+
"After this tool succeeds, output only <clawchat:no-reply/> and do not send a normal follow-up reply."),
|
|
853
|
+
parameters: ClawchatLeaveGroupSchema,
|
|
854
|
+
async execute(_callId, params) {
|
|
855
|
+
return await recordClawchatToolCall(accountId, "clawchat_leave_group", params, async () => {
|
|
856
|
+
const p = params;
|
|
857
|
+
const built = buildClient(accountId);
|
|
858
|
+
if (!built.ok)
|
|
859
|
+
return built.error;
|
|
860
|
+
try {
|
|
861
|
+
const data = await built.client.leaveConversation(p.conversationId);
|
|
862
|
+
return jsonResponse(data);
|
|
805
863
|
}
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
864
|
+
catch (err) {
|
|
865
|
+
if (err instanceof ClawlingApiError) {
|
|
866
|
+
return apiError(err);
|
|
867
|
+
}
|
|
868
|
+
return genericError(err);
|
|
869
|
+
}
|
|
870
|
+
});
|
|
871
|
+
},
|
|
872
|
+
};
|
|
810
873
|
}, { name: "clawchat_leave_group" });
|
|
811
|
-
api.registerTool({
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
"
|
|
816
|
-
"
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
if (err instanceof ClawlingApiError) {
|
|
832
|
-
return apiError(err);
|
|
874
|
+
api.registerTool((ctx) => {
|
|
875
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
876
|
+
return {
|
|
877
|
+
name: "clawchat_add_group_member",
|
|
878
|
+
label: "Add ClawChat Group Member",
|
|
879
|
+
description: toolDescription("Add a specified ClawChat user to a group conversation the agent is a member of. " +
|
|
880
|
+
"TRIGGER - invoke only when the user explicitly asks the agent to add, invite, or pull a specific person into a specific ClawChat group, and both a concrete conversationId and a concrete userId are available. " +
|
|
881
|
+
"Never guess a userId from a name or nickname — use ids from ClawChat Group Message Metadata, clawchat_list_account_friends, or clawchat_search_users. " +
|
|
882
|
+
"Groups only: direct conversations are rejected by the server. " +
|
|
883
|
+
"The target must already be the agent's ClawChat friend, and the agent needs the group-management permission granted by its owner; if the call fails for either reason, explain that to the user and do not retry."),
|
|
884
|
+
parameters: ClawchatAddGroupMemberSchema,
|
|
885
|
+
async execute(_callId, params) {
|
|
886
|
+
return await recordClawchatToolCall(accountId, "clawchat_add_group_member", params, async () => {
|
|
887
|
+
const p = params;
|
|
888
|
+
const built = buildClient(accountId);
|
|
889
|
+
if (!built.ok)
|
|
890
|
+
return built.error;
|
|
891
|
+
try {
|
|
892
|
+
const data = await built.client.addConversationMember(p.conversationId, p.userId);
|
|
893
|
+
return jsonResponse(data);
|
|
833
894
|
}
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
895
|
+
catch (err) {
|
|
896
|
+
if (err instanceof ClawlingApiError) {
|
|
897
|
+
return apiError(err);
|
|
898
|
+
}
|
|
899
|
+
return genericError(err);
|
|
900
|
+
}
|
|
901
|
+
});
|
|
902
|
+
},
|
|
903
|
+
};
|
|
838
904
|
}, { name: "clawchat_add_group_member" });
|
|
839
|
-
api.registerTool({
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
"
|
|
844
|
-
"
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
905
|
+
api.registerTool((ctx) => {
|
|
906
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
907
|
+
return {
|
|
908
|
+
name: "clawchat_mention_message",
|
|
909
|
+
label: "Send ClawChat Mention Message",
|
|
910
|
+
description: toolDescription("Send a real ClawChat mention message to a direct or group conversation. " +
|
|
911
|
+
"TRIGGER - invoke when the user asks to @, mention, notify, or address ClawChat users. " +
|
|
912
|
+
"Use `mentioned_users` or `sender_id` from current ClawChat sender/group message metadata as `mentions[].userId` when present. " +
|
|
913
|
+
"Use `clawchat_search_users` only when no explicit or locally available id exists. " +
|
|
914
|
+
"Never guess userId from names, nicknames, or plain @name text. Plain @name is not a real mention. " +
|
|
915
|
+
"Pass the visible label as `mentions[].display` when known. Do not copy the visible @mention into `text`; put only the message body in `text`. If display is unknown but a concrete userId is available and a human-readable label matters, call clawchat_get_user_profile first and use the returned nickname. " +
|
|
916
|
+
"After this tool succeeds, the mention message has already been sent; output only <clawchat:no-reply/> and do not send a normal follow-up reply."),
|
|
917
|
+
parameters: ClawchatMentionMessageSchema,
|
|
918
|
+
async execute(_callId, params) {
|
|
919
|
+
return await recordClawchatToolCall(accountId, "clawchat_mention_message", params, async () => {
|
|
920
|
+
const validated = validateMentionMessageParams(params);
|
|
921
|
+
if (!validated.ok)
|
|
922
|
+
return validationError(validated.message);
|
|
923
|
+
const account = resolveCurrent(accountId);
|
|
924
|
+
if (!account.configured) {
|
|
925
|
+
return configError("clawchat-plugin-openclaw: account is not configured");
|
|
926
|
+
}
|
|
927
|
+
try {
|
|
928
|
+
const p = validated.params;
|
|
929
|
+
const client = getOpenclawClawlingClient(account.accountId);
|
|
930
|
+
if (!client) {
|
|
931
|
+
return configError("ClawChat websocket client is not ready");
|
|
932
|
+
}
|
|
933
|
+
const result = await sendOpenclawClawlingMentionMessage({
|
|
934
|
+
client,
|
|
935
|
+
account,
|
|
936
|
+
to: {
|
|
937
|
+
chatId: p.chatId.trim(),
|
|
938
|
+
chatType: p.chatType ?? "group",
|
|
939
|
+
},
|
|
940
|
+
...(p.text !== undefined ? { text: p.text } : {}),
|
|
941
|
+
mentions: p.mentions,
|
|
942
|
+
...(p.replyToMessageId
|
|
943
|
+
? {
|
|
944
|
+
replyCtx: {
|
|
945
|
+
replyToMessageId: p.replyToMessageId,
|
|
946
|
+
},
|
|
947
|
+
}
|
|
948
|
+
: {}),
|
|
949
|
+
log: api.logger,
|
|
950
|
+
});
|
|
951
|
+
if (!result)
|
|
952
|
+
return jsonResponse(result);
|
|
953
|
+
markTerminalClawChatSend({
|
|
954
|
+
accountId: account.accountId,
|
|
955
|
+
chatId: p.chatId.trim(),
|
|
956
|
+
messageId: result.messageId,
|
|
957
|
+
});
|
|
958
|
+
return jsonResponse({
|
|
959
|
+
sent: true,
|
|
960
|
+
terminal: true,
|
|
961
|
+
noFollowupReply: true,
|
|
962
|
+
instruction: "The mention message has already been sent to ClawChat. If a follow-up response is required, output only <clawchat:no-reply/> and do not send a normal follow-up reply.",
|
|
963
|
+
...result,
|
|
964
|
+
});
|
|
965
|
+
}
|
|
966
|
+
catch (err) {
|
|
967
|
+
return genericError(err);
|
|
968
|
+
}
|
|
969
|
+
});
|
|
970
|
+
},
|
|
971
|
+
};
|
|
972
|
+
}, { name: "clawchat_mention_message" });
|
|
973
|
+
api.registerTool((ctx) => {
|
|
974
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
975
|
+
return {
|
|
976
|
+
name: "clawchat_react_message",
|
|
977
|
+
label: "React to ClawChat Message",
|
|
978
|
+
description: toolDescription("React to a ClawChat message with a single quick emoji (the bubble long-press reaction), instead of sending a text message. " +
|
|
979
|
+
"TRIGGER - invoke when a short acknowledgement or emotional beat (agreement, thanks, laughter, celebration, sympathy) reads more naturally as an emoji on the message than as a sentence, e.g. a simple 👍 to \"done\" or ❤️ to good news. " +
|
|
980
|
+
"Omit targetMessageId and chatId to react to the message that triggered the current turn; pass targetMessageId to react to an earlier message. " +
|
|
981
|
+
"Set remove:true to take back your reaction. " +
|
|
982
|
+
"Prefer the quick set 👍 ❤️ 😂 😮 😢 🙏 🎉 👏 🔥 😍 🤔. " +
|
|
983
|
+
"When a reaction is all you want to send, do not also send a normal text reply."),
|
|
984
|
+
parameters: ClawchatReactMessageSchema,
|
|
985
|
+
async execute(_callId, params) {
|
|
986
|
+
return await recordClawchatToolCall(accountId, "clawchat_react_message", params, async () => {
|
|
987
|
+
const p = params;
|
|
988
|
+
if (!p.emoji?.trim()) {
|
|
989
|
+
return validationError("clawchat-plugin-openclaw: emoji is required");
|
|
990
|
+
}
|
|
991
|
+
const account = resolveCurrent(accountId);
|
|
992
|
+
if (!account.configured) {
|
|
993
|
+
return configError("clawchat-plugin-openclaw: account is not configured");
|
|
994
|
+
}
|
|
995
|
+
const scope = getCurrentTerminalSendScope();
|
|
996
|
+
const chatId = p.chatId?.trim() || scope?.chatId;
|
|
997
|
+
const targetMessageId = p.targetMessageId?.trim() || scope?.messageId;
|
|
998
|
+
if (!chatId) {
|
|
999
|
+
return validationError("clawchat-plugin-openclaw: chatId is required (no current-turn chat to default from)");
|
|
1000
|
+
}
|
|
1001
|
+
if (!targetMessageId) {
|
|
1002
|
+
return validationError("clawchat-plugin-openclaw: targetMessageId is required (no current-turn message to default from)");
|
|
1003
|
+
}
|
|
861
1004
|
const client = getOpenclawClawlingClient(account.accountId);
|
|
862
1005
|
if (!client) {
|
|
863
1006
|
return configError("ClawChat websocket client is not ready");
|
|
864
1007
|
}
|
|
865
|
-
const
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
replyToMessageId: p.replyToMessageId,
|
|
878
|
-
},
|
|
879
|
-
}
|
|
880
|
-
: {}),
|
|
881
|
-
log: api.logger,
|
|
882
|
-
});
|
|
883
|
-
if (!result)
|
|
884
|
-
return jsonResponse(result);
|
|
885
|
-
markTerminalClawChatSend({
|
|
886
|
-
accountId: account.accountId,
|
|
887
|
-
chatId: p.chatId.trim(),
|
|
888
|
-
messageId: result.messageId,
|
|
889
|
-
});
|
|
890
|
-
return jsonResponse({
|
|
891
|
-
sent: true,
|
|
892
|
-
terminal: true,
|
|
893
|
-
noFollowupReply: true,
|
|
894
|
-
instruction: "The mention message has already been sent to ClawChat. If a follow-up response is required, output only <clawchat:no-reply/> and do not send a normal follow-up reply.",
|
|
895
|
-
...result,
|
|
896
|
-
});
|
|
897
|
-
}
|
|
898
|
-
catch (err) {
|
|
899
|
-
return genericError(err);
|
|
900
|
-
}
|
|
901
|
-
});
|
|
902
|
-
},
|
|
903
|
-
}, { name: "clawchat_mention_message" });
|
|
904
|
-
api.registerTool({
|
|
905
|
-
name: "clawchat_react_message",
|
|
906
|
-
label: "React to ClawChat Message",
|
|
907
|
-
description: toolDescription("React to a ClawChat message with a single quick emoji (the bubble long-press reaction), instead of sending a text message. " +
|
|
908
|
-
"TRIGGER - invoke when a short acknowledgement or emotional beat (agreement, thanks, laughter, celebration, sympathy) reads more naturally as an emoji on the message than as a sentence, e.g. a simple 👍 to \"done\" or ❤️ to good news. " +
|
|
909
|
-
"Omit targetMessageId and chatId to react to the message that triggered the current turn; pass targetMessageId to react to an earlier message. " +
|
|
910
|
-
"Set remove:true to take back your reaction. " +
|
|
911
|
-
"Prefer the quick set 👍 ❤️ 😂 😮 😢 🙏 🎉 👏 🔥 😍 🤔. " +
|
|
912
|
-
"When a reaction is all you want to send, do not also send a normal text reply."),
|
|
913
|
-
parameters: ClawchatReactMessageSchema,
|
|
914
|
-
async execute(_callId, params) {
|
|
915
|
-
return await recordClawchatToolCall("clawchat_react_message", params, async () => {
|
|
916
|
-
const p = params;
|
|
917
|
-
if (!p.emoji?.trim()) {
|
|
918
|
-
return validationError("clawchat-plugin-openclaw: emoji is required");
|
|
919
|
-
}
|
|
920
|
-
const account = resolveCurrent();
|
|
921
|
-
if (!account.configured) {
|
|
922
|
-
return configError("clawchat-plugin-openclaw: account is not configured");
|
|
923
|
-
}
|
|
924
|
-
const scope = getCurrentTerminalSendScope();
|
|
925
|
-
const chatId = p.chatId?.trim() || scope?.chatId;
|
|
926
|
-
const targetMessageId = p.targetMessageId?.trim() || scope?.messageId;
|
|
927
|
-
if (!chatId) {
|
|
928
|
-
return validationError("clawchat-plugin-openclaw: chatId is required (no current-turn chat to default from)");
|
|
929
|
-
}
|
|
930
|
-
if (!targetMessageId) {
|
|
931
|
-
return validationError("clawchat-plugin-openclaw: targetMessageId is required (no current-turn message to default from)");
|
|
932
|
-
}
|
|
933
|
-
const client = getOpenclawClawlingClient(account.accountId);
|
|
934
|
-
if (!client) {
|
|
935
|
-
return configError("ClawChat websocket client is not ready");
|
|
936
|
-
}
|
|
937
|
-
const emoji = p.emoji.trim();
|
|
938
|
-
const removed = p.remove === true;
|
|
939
|
-
try {
|
|
940
|
-
sendOpenclawClawlingReaction({ client, chatId, targetMessageId, emoji, removed, log: api.logger });
|
|
941
|
-
return jsonResponse({ reacted: true, targetMessageId, emoji, removed });
|
|
942
|
-
}
|
|
943
|
-
catch (err) {
|
|
944
|
-
return genericError(err);
|
|
945
|
-
}
|
|
946
|
-
});
|
|
947
|
-
},
|
|
1008
|
+
const emoji = p.emoji.trim();
|
|
1009
|
+
const removed = p.remove === true;
|
|
1010
|
+
try {
|
|
1011
|
+
sendOpenclawClawlingReaction({ client, chatId, targetMessageId, emoji, removed, log: api.logger });
|
|
1012
|
+
return jsonResponse({ reacted: true, targetMessageId, emoji, removed });
|
|
1013
|
+
}
|
|
1014
|
+
catch (err) {
|
|
1015
|
+
return genericError(err);
|
|
1016
|
+
}
|
|
1017
|
+
});
|
|
1018
|
+
},
|
|
1019
|
+
};
|
|
948
1020
|
}, { name: "clawchat_react_message" });
|
|
949
|
-
api.registerTool({
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
"
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
return await
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
1021
|
+
api.registerTool((ctx) => {
|
|
1022
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
1023
|
+
return {
|
|
1024
|
+
name: "clawchat_list_moments",
|
|
1025
|
+
label: "List ClawChat Moments",
|
|
1026
|
+
description: toolDescription("List the configured ClawChat account's visible moments feed, including moments from the account and its friends. " +
|
|
1027
|
+
"TRIGGER - invoke when the user asks to view, browse, refresh, or paginate ClawChat moments/dynamics/feed, such as \"show my ClawChat moments\", \"view dynamics\", \"show friend moments\", or \"more moments\". " +
|
|
1028
|
+
"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."),
|
|
1029
|
+
parameters: ClawchatListMomentsSchema,
|
|
1030
|
+
async execute(_callId, params) {
|
|
1031
|
+
return await recordClawchatToolCall(accountId, "clawchat_list_moments", params, async () => {
|
|
1032
|
+
const p = (params ?? {});
|
|
1033
|
+
return await withClient(accountId, (c) => c.listMoments({
|
|
1034
|
+
...(typeof p.before === "number" ? { before: p.before } : {}),
|
|
1035
|
+
...(typeof p.limit === "number" ? { limit: p.limit } : {}),
|
|
1036
|
+
}));
|
|
1037
|
+
});
|
|
1038
|
+
},
|
|
1039
|
+
};
|
|
965
1040
|
}, { name: "clawchat_list_moments" });
|
|
966
|
-
api.registerTool({
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
return await
|
|
975
|
-
|
|
976
|
-
|
|
1041
|
+
api.registerTool((ctx) => {
|
|
1042
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
1043
|
+
return {
|
|
1044
|
+
name: "clawchat_get_moment",
|
|
1045
|
+
label: "Get ClawChat Moment",
|
|
1046
|
+
description: toolDescription("Fetch a single ClawChat moment by id together with the comments visible to this agent. TRIGGER - after a moment.comment.created / moment.comment.replied awareness note, call this with the moment id to read the new comment before deciding whether to reply."),
|
|
1047
|
+
parameters: ClawchatGetMomentSchema,
|
|
1048
|
+
async execute(_callId, params) {
|
|
1049
|
+
return await recordClawchatToolCall(accountId, "clawchat_get_moment", params, async () => {
|
|
1050
|
+
const p = (params ?? {});
|
|
1051
|
+
return await withClient(accountId, (c) => c.getMoment(p.momentId));
|
|
1052
|
+
});
|
|
1053
|
+
},
|
|
1054
|
+
};
|
|
977
1055
|
}, { name: "clawchat_get_moment" });
|
|
978
|
-
api.registerTool({
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
"
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
1056
|
+
api.registerTool((ctx) => {
|
|
1057
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
1058
|
+
return {
|
|
1059
|
+
name: "clawchat_create_moment",
|
|
1060
|
+
label: "Create ClawChat Moment",
|
|
1061
|
+
description: toolDescription("Create a new ClawChat moment/dynamic for the configured ClawChat account. " +
|
|
1062
|
+
"TRIGGER - invoke when the user asks to publish, post, or send a ClawChat moment/dynamic, such as \"post a ClawChat moment saying ...\", \"publish a dynamic ...\", or \"send a friend moment ...\". " +
|
|
1063
|
+
"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."),
|
|
1064
|
+
parameters: ClawchatCreateMomentSchema,
|
|
1065
|
+
async execute(_callId, params) {
|
|
1066
|
+
return await recordClawchatToolCall(accountId, "clawchat_create_moment", params, async () => {
|
|
1067
|
+
const p = (params ?? {});
|
|
1068
|
+
const text = typeof p.text === "string" ? p.text : undefined;
|
|
1069
|
+
const images = Array.isArray(p.images) ? p.images : undefined;
|
|
1070
|
+
if (!text && (!images || images.length === 0)) {
|
|
1071
|
+
return validationError("clawchat-plugin-openclaw: at least one of text or images is required");
|
|
1072
|
+
}
|
|
1073
|
+
return await withGatedClient(accountId, (c) => c.createMoment({
|
|
1074
|
+
...(text !== undefined ? { text } : {}),
|
|
1075
|
+
...(images !== undefined ? { images } : {}),
|
|
1076
|
+
}));
|
|
1077
|
+
});
|
|
1078
|
+
},
|
|
1079
|
+
};
|
|
999
1080
|
}, { name: "clawchat_create_moment" });
|
|
1000
|
-
api.registerTool({
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
"
|
|
1005
|
-
"
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
return await
|
|
1011
|
-
|
|
1012
|
-
|
|
1081
|
+
api.registerTool((ctx) => {
|
|
1082
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
1083
|
+
return {
|
|
1084
|
+
name: "clawchat_delete_moment",
|
|
1085
|
+
label: "Delete ClawChat Moment",
|
|
1086
|
+
description: toolDescription("Delete a ClawChat moment by moment id. " +
|
|
1087
|
+
"TRIGGER - invoke when the user asks to delete/remove one of the configured account's ClawChat moments/dynamics and provides or selects a concrete moment id. " +
|
|
1088
|
+
"Only the moment author can delete it. Do not guess the id; list moments first if the user refers to a moment ambiguously."),
|
|
1089
|
+
parameters: ClawchatDeleteMomentSchema,
|
|
1090
|
+
async execute(_callId, params) {
|
|
1091
|
+
return await recordClawchatToolCall(accountId, "clawchat_delete_moment", params, async () => {
|
|
1092
|
+
const p = params;
|
|
1093
|
+
return await withGatedClient(accountId, (c) => c.deleteMoment(p.momentId));
|
|
1094
|
+
});
|
|
1095
|
+
},
|
|
1096
|
+
};
|
|
1013
1097
|
}, { name: "clawchat_delete_moment" });
|
|
1014
|
-
api.registerTool({
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
"
|
|
1019
|
-
"
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1098
|
+
api.registerTool((ctx) => {
|
|
1099
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
1100
|
+
return {
|
|
1101
|
+
name: "clawchat_toggle_moment_reaction",
|
|
1102
|
+
label: "Toggle ClawChat Moment Reaction",
|
|
1103
|
+
description: toolDescription("Toggle an emoji reaction on a ClawChat moment. " +
|
|
1104
|
+
"TRIGGER - invoke when the user asks to react, like, unlike, emoji-react, or remove the same emoji reaction on a specific ClawChat moment, such as \"like moment 123 with thumbs up\", \"add a thumbs-up reaction to moment 123\", or \"remove this reaction\". " +
|
|
1105
|
+
"The API adds the reaction if missing and removes it if already present. Require a concrete moment id and emoji."),
|
|
1106
|
+
parameters: ClawchatToggleMomentReactionSchema,
|
|
1107
|
+
async execute(_callId, params) {
|
|
1108
|
+
return await recordClawchatToolCall(accountId, "clawchat_toggle_moment_reaction", params, async () => {
|
|
1109
|
+
const p = params;
|
|
1110
|
+
if (!p.emoji?.trim()) {
|
|
1111
|
+
return validationError("clawchat-plugin-openclaw: emoji is required");
|
|
1112
|
+
}
|
|
1113
|
+
return await withGatedClient(accountId, (c) => c.toggleMomentReaction({ momentId: p.momentId, emoji: p.emoji }));
|
|
1114
|
+
});
|
|
1115
|
+
},
|
|
1116
|
+
};
|
|
1030
1117
|
}, { name: "clawchat_toggle_moment_reaction" });
|
|
1031
|
-
api.registerTool({
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
"
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1118
|
+
api.registerTool((ctx) => {
|
|
1119
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
1120
|
+
return {
|
|
1121
|
+
name: "clawchat_create_moment_comment",
|
|
1122
|
+
label: "Create ClawChat Moment Comment",
|
|
1123
|
+
description: toolDescription("Create a top-level comment on a ClawChat moment. " +
|
|
1124
|
+
"TRIGGER - invoke when the user asks to comment/reply directly to a moment/dynamic, not to another comment, such as \"comment on moment 123: ...\", \"leave a comment on dynamic 123 ...\", or \"write under this moment ...\". " +
|
|
1125
|
+
"Require a concrete moment id and non-empty text. Use clawchat_reply_moment_comment when the user is replying to another user's comment."),
|
|
1126
|
+
parameters: ClawchatCreateMomentCommentSchema,
|
|
1127
|
+
async execute(_callId, params) {
|
|
1128
|
+
return await recordClawchatToolCall(accountId, "clawchat_create_moment_comment", params, async () => {
|
|
1129
|
+
const p = params;
|
|
1130
|
+
if (!p.text?.trim()) {
|
|
1131
|
+
return validationError("clawchat-plugin-openclaw: text is required");
|
|
1132
|
+
}
|
|
1133
|
+
return await withGatedClient(accountId, (c) => c.createMomentComment({ momentId: p.momentId, text: p.text }));
|
|
1134
|
+
});
|
|
1135
|
+
},
|
|
1136
|
+
};
|
|
1047
1137
|
}, { name: "clawchat_create_moment_comment" });
|
|
1048
|
-
api.registerTool({
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
"
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1138
|
+
api.registerTool((ctx) => {
|
|
1139
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
1140
|
+
return {
|
|
1141
|
+
name: "clawchat_reply_moment_comment",
|
|
1142
|
+
label: "Reply To ClawChat Moment Comment",
|
|
1143
|
+
description: toolDescription("Reply to an existing ClawChat moment comment with a single-level reply. " +
|
|
1144
|
+
"TRIGGER - invoke when the user asks to reply to another user's comment on a moment/dynamic, such as \"reply to comment 456 on moment 123: ...\", \"reply to comment 456 ...\", or \"reply to that comment ...\". " +
|
|
1145
|
+
"Require concrete moment and comment ids; do not use this for top-level comments."),
|
|
1146
|
+
parameters: ClawchatReplyMomentCommentSchema,
|
|
1147
|
+
async execute(_callId, params) {
|
|
1148
|
+
return await recordClawchatToolCall(accountId, "clawchat_reply_moment_comment", params, async () => {
|
|
1149
|
+
const p = params;
|
|
1150
|
+
if (!p.text?.trim()) {
|
|
1151
|
+
return validationError("clawchat-plugin-openclaw: text is required");
|
|
1152
|
+
}
|
|
1153
|
+
return await withGatedClient(accountId, (c) => c.replyMomentComment({
|
|
1154
|
+
momentId: p.momentId,
|
|
1155
|
+
replyToCommentId: p.replyToCommentId,
|
|
1156
|
+
text: p.text,
|
|
1157
|
+
}));
|
|
1158
|
+
});
|
|
1159
|
+
},
|
|
1160
|
+
};
|
|
1068
1161
|
}, { name: "clawchat_reply_moment_comment" });
|
|
1069
|
-
api.registerTool({
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
"
|
|
1074
|
-
"
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
return await
|
|
1080
|
-
|
|
1081
|
-
|
|
1162
|
+
api.registerTool((ctx) => {
|
|
1163
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
1164
|
+
return {
|
|
1165
|
+
name: "clawchat_delete_moment_comment",
|
|
1166
|
+
label: "Delete ClawChat Moment Comment",
|
|
1167
|
+
description: toolDescription("Delete a comment on a ClawChat moment. " +
|
|
1168
|
+
"TRIGGER - invoke when the user asks to delete/remove a specific comment or reply from a ClawChat moment/dynamic and provides concrete moment and comment ids. " +
|
|
1169
|
+
"The caller may delete comments they authored or comments on moments they authored. Do not guess ids; list moments first if needed."),
|
|
1170
|
+
parameters: ClawchatDeleteMomentCommentSchema,
|
|
1171
|
+
async execute(_callId, params) {
|
|
1172
|
+
return await recordClawchatToolCall(accountId, "clawchat_delete_moment_comment", params, async () => {
|
|
1173
|
+
const p = params;
|
|
1174
|
+
return await withGatedClient(accountId, (c) => c.deleteMomentComment({ momentId: p.momentId, commentId: p.commentId }));
|
|
1175
|
+
});
|
|
1176
|
+
},
|
|
1177
|
+
};
|
|
1082
1178
|
}, { name: "clawchat_delete_moment_comment" });
|
|
1083
|
-
api.registerTool({
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
"
|
|
1088
|
-
"
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
patch
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1179
|
+
api.registerTool((ctx) => {
|
|
1180
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
1181
|
+
return {
|
|
1182
|
+
name: "clawchat_update_account_profile",
|
|
1183
|
+
label: "Update ClawChat Account Profile",
|
|
1184
|
+
description: toolDescription("Update nickname/avatar_url/bio on the agent's connected ClawChat account (the configured ClawChat account), which mirrors the local assistant identity. " +
|
|
1185
|
+
"TRIGGER — invoke this tool whenever the user's message asks to change the ClawChat account profile or local assistant name/profile while ClawChat is connected: " +
|
|
1186
|
+
"(1) ClawChat account nickname/name change: 'change the ClawChat account nickname to X', " +
|
|
1187
|
+
"'set this assistant name to X', 'change the assistant nickname to X', 'set the account name to X' " +
|
|
1188
|
+
"→ call with `nickname = X`; " +
|
|
1189
|
+
"(2) ClawChat account avatar/profile-picture change: 'change the ClawChat account avatar', " +
|
|
1190
|
+
"'use this image as the assistant profile picture', 'change the account avatar to this image' " +
|
|
1191
|
+
"→ first obtain the avatar URL (upload via `clawchat_upload_avatar_image`, OR use a provided URL directly), " +
|
|
1192
|
+
"then call this tool with `avatar_url = <url>`; " +
|
|
1193
|
+
"(3) ClawChat account bio/self-introduction change: 'update the ClawChat bio', " +
|
|
1194
|
+
"'set the assistant self-introduction to X', 'change the account bio to X' " +
|
|
1195
|
+
"→ call with `bio = X`. " +
|
|
1196
|
+
"You can pass `nickname`, `avatar_url`, and `bio` together in one call, or just one of them. " +
|
|
1197
|
+
"At least one of the three must be present. Do not frame this as updating a human user's personal account."),
|
|
1198
|
+
parameters: ClawchatUpdateAccountProfileSchema,
|
|
1199
|
+
async execute(_callId, params) {
|
|
1200
|
+
return await recordClawchatToolCall(accountId, "clawchat_update_account_profile", params, async () => {
|
|
1201
|
+
const p = (params ?? {});
|
|
1202
|
+
const patch = {};
|
|
1203
|
+
if (typeof p.nickname === "string")
|
|
1204
|
+
patch.nickname = p.nickname;
|
|
1205
|
+
if (typeof p.avatar_url === "string")
|
|
1206
|
+
patch.avatar_url = p.avatar_url;
|
|
1207
|
+
if (typeof p.bio === "string")
|
|
1208
|
+
patch.bio = p.bio;
|
|
1209
|
+
if (Object.keys(patch).length === 0) {
|
|
1210
|
+
return validationError("clawchat-plugin-openclaw: at least one of nickname / avatar / bio is required");
|
|
1211
|
+
}
|
|
1212
|
+
return await withClient(accountId, (c) => c.updateMyProfile(patch));
|
|
1213
|
+
});
|
|
1214
|
+
},
|
|
1215
|
+
};
|
|
1117
1216
|
}, { name: "clawchat_update_account_profile" });
|
|
1118
|
-
api.registerTool({
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
"
|
|
1123
|
-
"
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
stat
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1217
|
+
api.registerTool((ctx) => {
|
|
1218
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
1219
|
+
return {
|
|
1220
|
+
name: "clawchat_upload_avatar_image",
|
|
1221
|
+
label: "Upload ClawChat Avatar Image",
|
|
1222
|
+
description: toolDescription("Upload an absolute local image path for use as the agent's connected ClawChat account avatar (max 20MB), returning a hosted avatar URL. " +
|
|
1223
|
+
"TRIGGER — invoke when the user provides an absolute local image path and asks to upload it for the ClawChat account avatar/profile picture. " +
|
|
1224
|
+
"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."),
|
|
1225
|
+
parameters: ClawchatUploadAvatarImageSchema,
|
|
1226
|
+
async execute(_callId, params) {
|
|
1227
|
+
return await recordClawchatToolCall(accountId, "clawchat_upload_avatar_image", params, async () => {
|
|
1228
|
+
const p = params;
|
|
1229
|
+
if (!p.filePath || !path.isAbsolute(p.filePath)) {
|
|
1230
|
+
return validationError("clawchat-plugin-openclaw: filePath must be an absolute local path");
|
|
1231
|
+
}
|
|
1232
|
+
let stat;
|
|
1233
|
+
try {
|
|
1234
|
+
stat = fs.statSync(p.filePath);
|
|
1235
|
+
}
|
|
1236
|
+
catch (err) {
|
|
1237
|
+
return validationError(`clawchat-plugin-openclaw: cannot stat ${p.filePath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
1238
|
+
}
|
|
1239
|
+
if (!stat.isFile()) {
|
|
1240
|
+
return validationError(`clawchat-plugin-openclaw: ${p.filePath} is not a regular file`);
|
|
1241
|
+
}
|
|
1242
|
+
if (stat.size > MAX_UPLOAD_BYTES) {
|
|
1243
|
+
return validationError(`clawchat-plugin-openclaw: file too large (${stat.size} bytes; max 20MB)`);
|
|
1244
|
+
}
|
|
1245
|
+
const buffer = fs.readFileSync(p.filePath);
|
|
1246
|
+
const filename = path.basename(p.filePath);
|
|
1247
|
+
const mime = inferMimeFromPath(p.filePath);
|
|
1248
|
+
return await withClient(accountId, (c) => c.uploadAvatar({ buffer, filename, mime }));
|
|
1249
|
+
});
|
|
1250
|
+
},
|
|
1251
|
+
};
|
|
1150
1252
|
}, { name: "clawchat_upload_avatar_image" });
|
|
1151
|
-
api.registerTool({
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
return await
|
|
1161
|
-
|
|
1162
|
-
|
|
1253
|
+
api.registerTool((ctx) => {
|
|
1254
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
1255
|
+
return {
|
|
1256
|
+
name: "clawchat_register_app",
|
|
1257
|
+
label: "Register ClawChat App",
|
|
1258
|
+
description: toolDescription("Register a liveware-tunneled web app to ClawChat so it appears in the owner's chat with this agent. " +
|
|
1259
|
+
"Call AFTER `liveware tunnel bind` returns a public URL. Params: name, appId (liveware app id), url (public URL)."),
|
|
1260
|
+
parameters: ClawchatRegisterAppSchema,
|
|
1261
|
+
async execute(_callId, params) {
|
|
1262
|
+
return await recordClawchatToolCall(accountId, "clawchat_register_app", params, async () => {
|
|
1263
|
+
const p = params;
|
|
1264
|
+
return await withClient(accountId, (c) => c.registerApp({ name: p.name, appId: p.appId, url: p.url }));
|
|
1265
|
+
});
|
|
1266
|
+
},
|
|
1267
|
+
};
|
|
1163
1268
|
}, { name: "clawchat_register_app" });
|
|
1164
|
-
api.registerTool({
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1269
|
+
api.registerTool((ctx) => {
|
|
1270
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
1271
|
+
return {
|
|
1272
|
+
name: "clawchat_list_apps",
|
|
1273
|
+
label: "List ClawChat Apps",
|
|
1274
|
+
description: toolDescription("List the liveware web apps this agent has registered to ClawChat."),
|
|
1275
|
+
parameters: ClawchatListAppsSchema,
|
|
1276
|
+
async execute(_callId, params) {
|
|
1277
|
+
return await recordClawchatToolCall(accountId, "clawchat_list_apps", params, async () => withClient(accountId, (c) => c.listApps()));
|
|
1278
|
+
},
|
|
1279
|
+
};
|
|
1172
1280
|
}, { name: "clawchat_list_apps" });
|
|
1173
|
-
api.registerTool({
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
return await
|
|
1182
|
-
|
|
1183
|
-
|
|
1281
|
+
api.registerTool((ctx) => {
|
|
1282
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
1283
|
+
return {
|
|
1284
|
+
name: "clawchat_unregister_app",
|
|
1285
|
+
label: "Unregister ClawChat App",
|
|
1286
|
+
description: toolDescription("Unregister a previously registered liveware app from ClawChat. Param: appId (liveware app id)."),
|
|
1287
|
+
parameters: ClawchatUnregisterAppSchema,
|
|
1288
|
+
async execute(_callId, params) {
|
|
1289
|
+
return await recordClawchatToolCall(accountId, "clawchat_unregister_app", params, async () => {
|
|
1290
|
+
const p = params;
|
|
1291
|
+
return await withClient(accountId, (c) => c.unregisterApp(p.appId));
|
|
1292
|
+
});
|
|
1293
|
+
},
|
|
1294
|
+
};
|
|
1184
1295
|
}, { name: "clawchat_unregister_app" });
|
|
1185
|
-
api.registerTool({
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
"
|
|
1190
|
-
"
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
const livewarePath = resolveLivewarePath(api.runtime.state?.resolveStateDir?.());
|
|
1202
|
-
if (!livewarePath) {
|
|
1203
|
-
resolve(jsonResponse({
|
|
1204
|
-
error: "config",
|
|
1205
|
-
message: "liveware CLI not found in PATH; install liveware before calling this tool",
|
|
1206
|
-
}));
|
|
1207
|
-
return;
|
|
1296
|
+
api.registerTool((ctx) => {
|
|
1297
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
1298
|
+
return {
|
|
1299
|
+
name: "clawchat_liveware_login",
|
|
1300
|
+
label: "Login to Liveware",
|
|
1301
|
+
description: toolDescription("Log in to liveware CLI using the plugin-held ClawChat access token. " +
|
|
1302
|
+
"The token is resolved from the plugin's own credential store and is never passed through the LLM context. " +
|
|
1303
|
+
"Call this BEFORE any liveware tunnel or app commands; it is idempotent. " +
|
|
1304
|
+
"Returns { ok: true } on success. Returns an error if liveware is not installed, the ClawChat account is not activated, or login fails."),
|
|
1305
|
+
parameters: ClawchatLivewareLoginSchema,
|
|
1306
|
+
async execute(_callId, params) {
|
|
1307
|
+
return await recordClawchatToolCall(accountId, "clawchat_liveware_login", params, async () => {
|
|
1308
|
+
const acct = resolveCurrent(accountId);
|
|
1309
|
+
const token = acct.token;
|
|
1310
|
+
if (!token) {
|
|
1311
|
+
return configError("clawchat-plugin-openclaw: token is required");
|
|
1208
1312
|
}
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
// would be preferred if liveware supported it.
|
|
1213
|
-
// NEVER log the argv array; scrub token from any captured output below.
|
|
1214
|
-
execFile(livewarePath, ["login", "--access-token", token], { timeout: 30000 }, (err, stdout, stderr) => {
|
|
1215
|
-
const scrub = (s) => (token ? s.split(token).join("***") : s);
|
|
1216
|
-
const safeStdout = scrub(stdout ?? "").trim();
|
|
1217
|
-
const safeStderr = scrub(stderr ?? "").trim();
|
|
1218
|
-
if (err) {
|
|
1219
|
-
if (err.code === "ENOENT") {
|
|
1220
|
-
resolve(jsonResponse({
|
|
1221
|
-
error: "config",
|
|
1222
|
-
message: "liveware CLI not found in PATH; install liveware before calling this tool",
|
|
1223
|
-
}));
|
|
1224
|
-
return;
|
|
1225
|
-
}
|
|
1226
|
-
const detail = [safeStderr, safeStdout].filter(Boolean).join("\n").trim();
|
|
1313
|
+
return await new Promise((resolve) => {
|
|
1314
|
+
const livewarePath = resolveLivewarePath(api.runtime.state?.resolveStateDir?.());
|
|
1315
|
+
if (!livewarePath) {
|
|
1227
1316
|
resolve(jsonResponse({
|
|
1228
|
-
error: "
|
|
1229
|
-
message: "liveware
|
|
1317
|
+
error: "config",
|
|
1318
|
+
message: "liveware CLI not found in PATH; install liveware before calling this tool",
|
|
1230
1319
|
}));
|
|
1231
1320
|
return;
|
|
1232
1321
|
}
|
|
1233
|
-
|
|
1322
|
+
// Security: the token is passed as a child process argv value using
|
|
1323
|
+
// liveware's documented --access-token interface. It is in the child
|
|
1324
|
+
// argv briefly during process creation; env-var or stdin delivery
|
|
1325
|
+
// would be preferred if liveware supported it.
|
|
1326
|
+
// NEVER log the argv array; scrub token from any captured output below.
|
|
1327
|
+
execFile(livewarePath, ["login", "--access-token", token], { timeout: 30000 }, (err, stdout, stderr) => {
|
|
1328
|
+
const scrub = (s) => (token ? s.split(token).join("***") : s);
|
|
1329
|
+
const safeStdout = scrub(stdout ?? "").trim();
|
|
1330
|
+
const safeStderr = scrub(stderr ?? "").trim();
|
|
1331
|
+
if (err) {
|
|
1332
|
+
if (err.code === "ENOENT") {
|
|
1333
|
+
resolve(jsonResponse({
|
|
1334
|
+
error: "config",
|
|
1335
|
+
message: "liveware CLI not found in PATH; install liveware before calling this tool",
|
|
1336
|
+
}));
|
|
1337
|
+
return;
|
|
1338
|
+
}
|
|
1339
|
+
const detail = [safeStderr, safeStdout].filter(Boolean).join("\n").trim();
|
|
1340
|
+
resolve(jsonResponse({
|
|
1341
|
+
error: "liveware_login_failed",
|
|
1342
|
+
message: "liveware login failed" + (detail ? `: ${detail}` : ""),
|
|
1343
|
+
}));
|
|
1344
|
+
return;
|
|
1345
|
+
}
|
|
1346
|
+
resolve(jsonResponse({ ok: true }));
|
|
1347
|
+
});
|
|
1234
1348
|
});
|
|
1235
1349
|
});
|
|
1236
|
-
}
|
|
1237
|
-
}
|
|
1350
|
+
},
|
|
1351
|
+
};
|
|
1238
1352
|
}, { name: "clawchat_liveware_login" });
|
|
1239
1353
|
api.logger.debug?.("clawchat-plugin-openclaw: registered 31 clawchat_* tools (get_account_profile, get_user_profile, list_account_friends, send_friend_request, list_friend_requests, accept_friend_request, reject_friend_request, remove_friend, search_users, get_conversation, mention_message, list_moments, get_moment, create_moment, delete_moment, toggle_moment_reaction, create_moment_comment, reply_moment_comment, delete_moment_comment, update_account_profile, upload_avatar_image, memory_search, memory_read, memory_write, memory_edit, metadata_sync, metadata_update, register_app, list_apps, unregister_app, liveware_login)");
|
|
1240
1354
|
}
|