@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/src/tools.ts
CHANGED
|
@@ -10,7 +10,11 @@ import {
|
|
|
10
10
|
type Profile,
|
|
11
11
|
} from "./api-types.ts";
|
|
12
12
|
import { mapGateOutcome } from "./gate-outcome.ts";
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
CHANNEL_ID,
|
|
15
|
+
normalizeOpenclawClawlingAccountId,
|
|
16
|
+
resolveOpenclawClawlingAccount,
|
|
17
|
+
} from "./config.ts";
|
|
14
18
|
import {
|
|
15
19
|
clawChatDbPathForStateDir,
|
|
16
20
|
getClawChatStore,
|
|
@@ -280,6 +284,35 @@ function validateMentionMessageParams(params: unknown):
|
|
|
280
284
|
return { ok: true, params: p as ClawchatMentionMessageParams };
|
|
281
285
|
}
|
|
282
286
|
|
|
287
|
+
/**
|
|
288
|
+
* Account a tool call belongs to, or `null` when the call carries no ClawChat
|
|
289
|
+
* context (the caller then falls back to the configured default account).
|
|
290
|
+
*
|
|
291
|
+
* Both context fields are only meaningful when they describe THIS channel:
|
|
292
|
+
* `agentAccountId` is the originating session's account id, so a turn that
|
|
293
|
+
* arrived over Telegram/Discord would otherwise hand us that channel's account
|
|
294
|
+
* id as if it were one of ours.
|
|
295
|
+
*/
|
|
296
|
+
export function resolveOpenclawClawlingToolAccountId(ctx: OpenClawPluginToolContext): string | null {
|
|
297
|
+
const delivery = ctx.deliveryContext;
|
|
298
|
+
if (
|
|
299
|
+
delivery &&
|
|
300
|
+
delivery.channel === CHANNEL_ID &&
|
|
301
|
+
typeof delivery.accountId === "string" &&
|
|
302
|
+
delivery.accountId.trim()
|
|
303
|
+
) {
|
|
304
|
+
return normalizeOpenclawClawlingAccountId(delivery.accountId);
|
|
305
|
+
}
|
|
306
|
+
if (
|
|
307
|
+
ctx.messageChannel === CHANNEL_ID &&
|
|
308
|
+
typeof ctx.agentAccountId === "string" &&
|
|
309
|
+
ctx.agentAccountId.trim()
|
|
310
|
+
) {
|
|
311
|
+
return normalizeOpenclawClawlingAccountId(ctx.agentAccountId);
|
|
312
|
+
}
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
315
|
+
|
|
283
316
|
export function registerOpenclawClawlingTools(
|
|
284
317
|
api: OpenClawPluginApi,
|
|
285
318
|
options: { store?: ToolStore | null } = {},
|
|
@@ -290,8 +323,8 @@ export function registerOpenclawClawlingTools(
|
|
|
290
323
|
}
|
|
291
324
|
|
|
292
325
|
// Re-resolve at call time so config reloads pick up new tokens / baseUrl.
|
|
293
|
-
function resolveCurrent() {
|
|
294
|
-
return resolveOpenclawClawlingAccount(api.config
|
|
326
|
+
function resolveCurrent(accountId: string | null) {
|
|
327
|
+
return resolveOpenclawClawlingAccount(api.config!, accountId);
|
|
295
328
|
}
|
|
296
329
|
|
|
297
330
|
function resolveStore(): ToolStore | null {
|
|
@@ -326,12 +359,13 @@ export function registerOpenclawClawlingTools(
|
|
|
326
359
|
}
|
|
327
360
|
|
|
328
361
|
async function recordClawchatToolCall<T>(
|
|
362
|
+
accountId: string | null,
|
|
329
363
|
toolName: string,
|
|
330
364
|
params: unknown,
|
|
331
365
|
fn: () => Promise<OpenClawAgentToolResult<T>>,
|
|
332
366
|
): Promise<OpenClawAgentToolResult<T>> {
|
|
333
367
|
const startedAt = Date.now();
|
|
334
|
-
const account = resolveCurrent();
|
|
368
|
+
const account = resolveCurrent(accountId);
|
|
335
369
|
try {
|
|
336
370
|
const result = await fn();
|
|
337
371
|
const details = (result as { details?: unknown }).details ?? result;
|
|
@@ -365,8 +399,8 @@ export function registerOpenclawClawlingTools(
|
|
|
365
399
|
| { ok: false; error: OpenClawAgentToolResult<unknown> }
|
|
366
400
|
| { ok: true; client: ReturnType<typeof createOpenclawClawlingApiClient> };
|
|
367
401
|
|
|
368
|
-
function buildClient(): ClientResult {
|
|
369
|
-
const acct = resolveCurrent();
|
|
402
|
+
function buildClient(accountId: string | null): ClientResult {
|
|
403
|
+
const acct = resolveCurrent(accountId);
|
|
370
404
|
// `baseUrl` always resolves via the built-in default in config.ts, so we
|
|
371
405
|
// only need to gate on `token` here (which is populated by ClawChat
|
|
372
406
|
// activation/login).
|
|
@@ -384,10 +418,11 @@ export function registerOpenclawClawlingTools(
|
|
|
384
418
|
}
|
|
385
419
|
|
|
386
420
|
function withClient<T>(
|
|
421
|
+
accountId: string | null,
|
|
387
422
|
fn: (client: ReturnType<typeof createOpenclawClawlingApiClient>) => Promise<T>,
|
|
388
423
|
): Promise<OpenClawAgentToolResult<unknown>> {
|
|
389
424
|
return (async (): Promise<OpenClawAgentToolResult<unknown>> => {
|
|
390
|
-
const built = buildClient();
|
|
425
|
+
const built = buildClient(accountId);
|
|
391
426
|
if (!built.ok) return built.error;
|
|
392
427
|
try {
|
|
393
428
|
const data = await fn(built.client);
|
|
@@ -405,10 +440,11 @@ export function registerOpenclawClawlingTools(
|
|
|
405
440
|
* instead of a generic error. Used for friend and moment mutation tools.
|
|
406
441
|
*/
|
|
407
442
|
function withGatedClient<T>(
|
|
443
|
+
accountId: string | null,
|
|
408
444
|
fn: (client: ReturnType<typeof createOpenclawClawlingApiClient>) => Promise<T>,
|
|
409
445
|
): Promise<OpenClawAgentToolResult<unknown>> {
|
|
410
446
|
return (async (): Promise<OpenClawAgentToolResult<unknown>> => {
|
|
411
|
-
const built = buildClient();
|
|
447
|
+
const built = buildClient(accountId);
|
|
412
448
|
if (!built.ok) return built.error;
|
|
413
449
|
try {
|
|
414
450
|
const data = await fn(built.client);
|
|
@@ -662,136 +698,142 @@ export function registerOpenclawClawlingTools(
|
|
|
662
698
|
);
|
|
663
699
|
|
|
664
700
|
api.registerTool(
|
|
665
|
-
(ctx) =>
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
"
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
701
|
+
(ctx) => {
|
|
702
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
703
|
+
return {
|
|
704
|
+
name: "clawchat_metadata_sync",
|
|
705
|
+
label: "Sync ClawChat Metadata",
|
|
706
|
+
description: memoryToolDescription(
|
|
707
|
+
"Synchronize the ClawChat metadata block for an explicit owner, user, or group target. " +
|
|
708
|
+
"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. " +
|
|
709
|
+
"Use direction=push only to push selected existing local metadata fields to the server, then refresh the metadata block from the server response. " +
|
|
710
|
+
"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. " +
|
|
711
|
+
"This does not modify the agent-authored body. " +
|
|
712
|
+
"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. " +
|
|
713
|
+
"Do not combine clawchat_get_user_profile with clawchat_memory_write to update local profile metadata.",
|
|
714
|
+
),
|
|
715
|
+
parameters: ClawchatMetadataSyncSchema,
|
|
716
|
+
async execute(_callId, params) {
|
|
717
|
+
const root = workspaceRoot(ctx);
|
|
718
|
+
if (!root) {
|
|
719
|
+
return validationError("clawchat-plugin-openclaw: OpenClaw workspaceDir is required");
|
|
720
|
+
}
|
|
721
|
+
const built = buildClient(accountId);
|
|
722
|
+
if (!built.ok) return built.error;
|
|
723
|
+
try {
|
|
724
|
+
const p = params as ClawchatMetadataSyncParams;
|
|
725
|
+
const targetError = validateTarget(root, p);
|
|
726
|
+
if (targetError) return memoryToolError(new Error(targetError));
|
|
727
|
+
const account = resolveCurrent(accountId);
|
|
728
|
+
if (p.direction === "push") {
|
|
729
|
+
if (!Array.isArray(p.fields) || p.fields.length === 0) {
|
|
730
|
+
return validationError("clawchat-plugin-openclaw: fields are required for metadata push");
|
|
731
|
+
}
|
|
732
|
+
if (p.targetType === "owner" && !account.agentId) {
|
|
733
|
+
return configError("clawchat-plugin-openclaw: agentId is required");
|
|
734
|
+
}
|
|
735
|
+
if (p.targetType === "user" && !account.userId) {
|
|
736
|
+
return configError("clawchat-plugin-openclaw: userId is required");
|
|
737
|
+
}
|
|
738
|
+
return jsonResponse(
|
|
739
|
+
await pushMetadata({
|
|
740
|
+
memoryRoot: root,
|
|
741
|
+
targetType: p.targetType,
|
|
742
|
+
targetId: p.targetId,
|
|
743
|
+
fields: p.fields,
|
|
744
|
+
agentId: account.agentId,
|
|
745
|
+
accountUserId: account.userId,
|
|
746
|
+
api: built.client,
|
|
747
|
+
}),
|
|
748
|
+
);
|
|
693
749
|
}
|
|
694
|
-
if (p.targetType === "owner"
|
|
695
|
-
return configError("clawchat-plugin-openclaw: agentId is required");
|
|
750
|
+
if (p.targetType === "owner") {
|
|
751
|
+
if (!account.agentId) return configError("clawchat-plugin-openclaw: agentId is required");
|
|
752
|
+
return jsonResponse(
|
|
753
|
+
await pullOwnerMetadata({
|
|
754
|
+
memoryRoot: root,
|
|
755
|
+
agentId: account.agentId,
|
|
756
|
+
accountUserId: account.userId,
|
|
757
|
+
accountOwnerUserId: account.ownerUserId || undefined,
|
|
758
|
+
api: built.client,
|
|
759
|
+
}),
|
|
760
|
+
);
|
|
696
761
|
}
|
|
697
|
-
if (p.targetType === "user"
|
|
698
|
-
return
|
|
762
|
+
if (p.targetType === "user") {
|
|
763
|
+
return jsonResponse(
|
|
764
|
+
await pullUserMetadata({ memoryRoot: root, userId: p.targetId, api: built.client }),
|
|
765
|
+
);
|
|
699
766
|
}
|
|
700
767
|
return jsonResponse(
|
|
701
|
-
await
|
|
768
|
+
await pullGroupMetadata({
|
|
702
769
|
memoryRoot: root,
|
|
703
|
-
|
|
704
|
-
targetId: p.targetId,
|
|
705
|
-
fields: p.fields,
|
|
706
|
-
agentId: account.agentId,
|
|
707
|
-
accountUserId: account.userId,
|
|
770
|
+
groupId: p.targetId,
|
|
708
771
|
api: built.client,
|
|
772
|
+
skipUserIds: [account.userId, account.ownerUserId],
|
|
709
773
|
}),
|
|
710
774
|
);
|
|
775
|
+
} catch (err) {
|
|
776
|
+
return memoryToolError(err);
|
|
777
|
+
}
|
|
778
|
+
},
|
|
779
|
+
};
|
|
780
|
+
},
|
|
781
|
+
{ name: "clawchat_metadata_sync" },
|
|
782
|
+
);
|
|
783
|
+
|
|
784
|
+
api.registerTool(
|
|
785
|
+
(ctx) => {
|
|
786
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
787
|
+
return {
|
|
788
|
+
name: "clawchat_metadata_update",
|
|
789
|
+
label: "Update ClawChat Metadata",
|
|
790
|
+
description: memoryToolDescription(
|
|
791
|
+
"Update ClawChat server metadata for an explicit owner, connected user account, or group target, then refresh the local metadata block from the server response. " +
|
|
792
|
+
"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. " +
|
|
793
|
+
"To refresh local metadata from the server without changing the server, use clawchat_metadata_sync with direction=pull. " +
|
|
794
|
+
"This always pushes to the server first and does not modify the agent-authored body.",
|
|
795
|
+
),
|
|
796
|
+
parameters: ClawchatMetadataUpdateSchema,
|
|
797
|
+
async execute(_callId, params) {
|
|
798
|
+
const root = workspaceRoot(ctx);
|
|
799
|
+
if (!root) {
|
|
800
|
+
return validationError("clawchat-plugin-openclaw: OpenClaw workspaceDir is required");
|
|
711
801
|
}
|
|
712
|
-
|
|
713
|
-
|
|
802
|
+
const built = buildClient(accountId);
|
|
803
|
+
if (!built.ok) return built.error;
|
|
804
|
+
try {
|
|
805
|
+
const p = params as ClawchatMetadataUpdateParams;
|
|
806
|
+
const targetError = validateTarget(root, p);
|
|
807
|
+
if (targetError) return memoryToolError(new Error(targetError));
|
|
808
|
+
const patchError = validateMetadataUpdatePatch(p);
|
|
809
|
+
if (patchError) return validationError(patchError);
|
|
810
|
+
const account = resolveCurrent(accountId);
|
|
811
|
+
if (p.targetType === "owner" && !account.agentId) {
|
|
812
|
+
return configError("clawchat-plugin-openclaw: agentId is required");
|
|
813
|
+
}
|
|
814
|
+
if (p.targetType === "user" && !account.userId) {
|
|
815
|
+
return configError("clawchat-plugin-openclaw: userId is required");
|
|
816
|
+
}
|
|
714
817
|
return jsonResponse(
|
|
715
|
-
await
|
|
818
|
+
await updateMetadata({
|
|
716
819
|
memoryRoot: root,
|
|
820
|
+
targetType: p.targetType,
|
|
821
|
+
targetId: p.targetId,
|
|
717
822
|
agentId: account.agentId,
|
|
718
823
|
accountUserId: account.userId,
|
|
719
|
-
|
|
824
|
+
patch: p.patch,
|
|
720
825
|
api: built.client,
|
|
826
|
+
persistOwnerProfile: makeOwnerProfilePersister(getClawChatStore(), {
|
|
827
|
+
accountId: account.accountId,
|
|
828
|
+
}),
|
|
721
829
|
}),
|
|
722
830
|
);
|
|
831
|
+
} catch (err) {
|
|
832
|
+
return memoryToolError(err);
|
|
723
833
|
}
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
);
|
|
728
|
-
}
|
|
729
|
-
return jsonResponse(
|
|
730
|
-
await pullGroupMetadata({
|
|
731
|
-
memoryRoot: root,
|
|
732
|
-
groupId: p.targetId,
|
|
733
|
-
api: built.client,
|
|
734
|
-
skipUserIds: [account.userId, account.ownerUserId],
|
|
735
|
-
}),
|
|
736
|
-
);
|
|
737
|
-
} catch (err) {
|
|
738
|
-
return memoryToolError(err);
|
|
739
|
-
}
|
|
740
|
-
},
|
|
741
|
-
}),
|
|
742
|
-
{ name: "clawchat_metadata_sync" },
|
|
743
|
-
);
|
|
744
|
-
|
|
745
|
-
api.registerTool(
|
|
746
|
-
(ctx) => ({
|
|
747
|
-
name: "clawchat_metadata_update",
|
|
748
|
-
label: "Update ClawChat Metadata",
|
|
749
|
-
description: memoryToolDescription(
|
|
750
|
-
"Update ClawChat server metadata for an explicit owner, connected user account, or group target, then refresh the local metadata block from the server response. " +
|
|
751
|
-
"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. " +
|
|
752
|
-
"To refresh local metadata from the server without changing the server, use clawchat_metadata_sync with direction=pull. " +
|
|
753
|
-
"This always pushes to the server first and does not modify the agent-authored body.",
|
|
754
|
-
),
|
|
755
|
-
parameters: ClawchatMetadataUpdateSchema,
|
|
756
|
-
async execute(_callId, params) {
|
|
757
|
-
const root = workspaceRoot(ctx);
|
|
758
|
-
if (!root) {
|
|
759
|
-
return validationError("clawchat-plugin-openclaw: OpenClaw workspaceDir is required");
|
|
760
|
-
}
|
|
761
|
-
const built = buildClient();
|
|
762
|
-
if (!built.ok) return built.error;
|
|
763
|
-
try {
|
|
764
|
-
const p = params as ClawchatMetadataUpdateParams;
|
|
765
|
-
const targetError = validateTarget(root, p);
|
|
766
|
-
if (targetError) return memoryToolError(new Error(targetError));
|
|
767
|
-
const patchError = validateMetadataUpdatePatch(p);
|
|
768
|
-
if (patchError) return validationError(patchError);
|
|
769
|
-
const account = resolveCurrent();
|
|
770
|
-
if (p.targetType === "owner" && !account.agentId) {
|
|
771
|
-
return configError("clawchat-plugin-openclaw: agentId is required");
|
|
772
|
-
}
|
|
773
|
-
if (p.targetType === "user" && !account.userId) {
|
|
774
|
-
return configError("clawchat-plugin-openclaw: userId is required");
|
|
775
|
-
}
|
|
776
|
-
return jsonResponse(
|
|
777
|
-
await updateMetadata({
|
|
778
|
-
memoryRoot: root,
|
|
779
|
-
targetType: p.targetType,
|
|
780
|
-
targetId: p.targetId,
|
|
781
|
-
agentId: account.agentId,
|
|
782
|
-
accountUserId: account.userId,
|
|
783
|
-
patch: p.patch,
|
|
784
|
-
api: built.client,
|
|
785
|
-
persistOwnerProfile: makeOwnerProfilePersister(getClawChatStore(), {
|
|
786
|
-
accountId: account.accountId,
|
|
787
|
-
}),
|
|
788
|
-
}),
|
|
789
|
-
);
|
|
790
|
-
} catch (err) {
|
|
791
|
-
return memoryToolError(err);
|
|
792
|
-
}
|
|
793
|
-
},
|
|
794
|
-
}),
|
|
834
|
+
},
|
|
835
|
+
};
|
|
836
|
+
},
|
|
795
837
|
{ name: "clawchat_metadata_update" },
|
|
796
838
|
);
|
|
797
839
|
}
|
|
@@ -799,800 +841,886 @@ export function registerOpenclawClawlingTools(
|
|
|
799
841
|
registerMemoryTools();
|
|
800
842
|
|
|
801
843
|
api.registerTool(
|
|
802
|
-
{
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
"
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
844
|
+
(ctx) => {
|
|
845
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
846
|
+
return {
|
|
847
|
+
name: "clawchat_get_account_profile",
|
|
848
|
+
label: "Get ClawChat Account Profile",
|
|
849
|
+
description: toolDescription(
|
|
850
|
+
"Fetch the agent's connected ClawChat account profile (the configured ClawChat account: user id, nickname/display name, avatar, bio). " +
|
|
851
|
+
"This profile is the platform-side mirror of the local assistant identity; if fields are missing, report them as unset instead of inventing values. " +
|
|
852
|
+
"TRIGGER — invoke when the user asks for the ClawChat account/profile connected to this agent, " +
|
|
853
|
+
"such as 'show my ClawChat profile', 'what is the configured ClawChat account?', " +
|
|
854
|
+
"'show the current ClawChat account details', or 'show the ClawChat nickname, avatar, and bio'. " +
|
|
855
|
+
"Do not frame this as a human user's personal account.",
|
|
856
|
+
),
|
|
857
|
+
parameters: ClawchatGetAccountProfileSchema,
|
|
858
|
+
async execute(_callId, params) {
|
|
859
|
+
return await recordClawchatToolCall(accountId, "clawchat_get_account_profile", params, async () =>
|
|
860
|
+
withClient(accountId, (c) => c.getMyProfile()),
|
|
861
|
+
);
|
|
862
|
+
},
|
|
863
|
+
};
|
|
819
864
|
},
|
|
820
865
|
{ name: "clawchat_get_account_profile" },
|
|
821
866
|
);
|
|
822
867
|
|
|
823
868
|
api.registerTool(
|
|
824
|
-
{
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
"
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
return await
|
|
840
|
-
|
|
841
|
-
|
|
869
|
+
(ctx) => {
|
|
870
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
871
|
+
return {
|
|
872
|
+
name: "clawchat_get_user_profile",
|
|
873
|
+
label: "Get ClawChat User Profile",
|
|
874
|
+
description: toolDescription(
|
|
875
|
+
"Fetch a ClawChat user's server-side public profile by explicit userId. " +
|
|
876
|
+
"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. " +
|
|
877
|
+
"Do not guess or infer userId from nickname, display name, alias, or local memory text. " +
|
|
878
|
+
"This is a read-only lookup and server lookup. It does not read local ClawChat memory files and does not update local metadata. " +
|
|
879
|
+
"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. " +
|
|
880
|
+
"Use `clawchat_get_account_profile` for the agent's own connected ClawChat account unless an explicit userId is provided.",
|
|
881
|
+
),
|
|
882
|
+
parameters: ClawchatGetUserProfileSchema,
|
|
883
|
+
async execute(_callId, params) {
|
|
884
|
+
return await recordClawchatToolCall(accountId, "clawchat_get_user_profile", params, async () => {
|
|
885
|
+
const p = params as ClawchatGetUserProfileParams;
|
|
886
|
+
return await withClient(accountId, (c) => c.getUserInfo(p.userId));
|
|
887
|
+
});
|
|
888
|
+
},
|
|
889
|
+
};
|
|
842
890
|
},
|
|
843
891
|
{ name: "clawchat_get_user_profile" },
|
|
844
892
|
);
|
|
845
893
|
|
|
846
894
|
api.registerTool(
|
|
847
|
-
{
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
"List
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
895
|
+
(ctx) => {
|
|
896
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
897
|
+
return {
|
|
898
|
+
name: "clawchat_list_account_friends",
|
|
899
|
+
label: "List ClawChat Account Friends",
|
|
900
|
+
description: toolDescription(
|
|
901
|
+
"List friends/contacts of the agent's connected ClawChat account (the configured ClawChat account). " +
|
|
902
|
+
"These are the agent's ClawChat-platform contacts. " +
|
|
903
|
+
"TRIGGER — invoke when the user asks for this ClawChat account's friends, contacts, or friend list.",
|
|
904
|
+
),
|
|
905
|
+
parameters: ClawchatListAccountFriendsSchema,
|
|
906
|
+
async execute(_callId, params) {
|
|
907
|
+
return await recordClawchatToolCall(accountId, "clawchat_list_account_friends", params, async () =>
|
|
908
|
+
withClient(accountId, (c) => c.listFriends()),
|
|
909
|
+
);
|
|
910
|
+
},
|
|
911
|
+
};
|
|
861
912
|
},
|
|
862
913
|
{ name: "clawchat_list_account_friends" },
|
|
863
914
|
);
|
|
864
915
|
|
|
865
916
|
api.registerTool(
|
|
866
|
-
{
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
"Send
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
return await
|
|
879
|
-
|
|
880
|
-
|
|
917
|
+
(ctx) => {
|
|
918
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
919
|
+
return {
|
|
920
|
+
name: "clawchat_send_friend_request",
|
|
921
|
+
label: "Send ClawChat Friend Request",
|
|
922
|
+
description: toolDescription(
|
|
923
|
+
"Send a friend request from the connected ClawChat account to a specific user by explicit userId. " +
|
|
924
|
+
"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. " +
|
|
925
|
+
"Do not guess or infer userId from nickname, display name, alias, or local memory text; search first when needed.",
|
|
926
|
+
),
|
|
927
|
+
parameters: ClawchatSendFriendRequestSchema,
|
|
928
|
+
async execute(_callId, params) {
|
|
929
|
+
return await recordClawchatToolCall(accountId, "clawchat_send_friend_request", params, async () => {
|
|
930
|
+
const p = params as ClawchatSendFriendRequestParams;
|
|
931
|
+
return await withGatedClient(accountId, (c) => c.sendFriendRequest(p));
|
|
932
|
+
});
|
|
933
|
+
},
|
|
934
|
+
};
|
|
881
935
|
},
|
|
882
936
|
{ name: "clawchat_send_friend_request" },
|
|
883
937
|
);
|
|
884
938
|
|
|
885
939
|
api.registerTool(
|
|
886
|
-
{
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
"List
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
return await
|
|
899
|
-
|
|
900
|
-
|
|
940
|
+
(ctx) => {
|
|
941
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
942
|
+
return {
|
|
943
|
+
name: "clawchat_list_friend_requests",
|
|
944
|
+
label: "List ClawChat Friend Requests",
|
|
945
|
+
description: toolDescription(
|
|
946
|
+
"List pending friend requests for the connected ClawChat account. " +
|
|
947
|
+
"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. " +
|
|
948
|
+
"TRIGGER - invoke when the user asks to view, check, review, or list friend requests, pending requests, incoming requests, or outgoing requests.",
|
|
949
|
+
),
|
|
950
|
+
parameters: ClawchatListFriendRequestsSchema,
|
|
951
|
+
async execute(_callId, params) {
|
|
952
|
+
return await recordClawchatToolCall(accountId, "clawchat_list_friend_requests", params, async () => {
|
|
953
|
+
const p = (params ?? {}) as ClawchatListFriendRequestsParams;
|
|
954
|
+
return await withClient(accountId, (c) => c.listFriendRequests({ direction: p.direction ?? "incoming" }));
|
|
955
|
+
});
|
|
956
|
+
},
|
|
957
|
+
};
|
|
901
958
|
},
|
|
902
959
|
{ name: "clawchat_list_friend_requests" },
|
|
903
960
|
);
|
|
904
961
|
|
|
905
962
|
api.registerTool(
|
|
906
|
-
{
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
"Accept
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
return await
|
|
919
|
-
|
|
920
|
-
|
|
963
|
+
(ctx) => {
|
|
964
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
965
|
+
return {
|
|
966
|
+
name: "clawchat_accept_friend_request",
|
|
967
|
+
label: "Accept ClawChat Friend Request",
|
|
968
|
+
description: toolDescription(
|
|
969
|
+
"Accept one pending incoming friend request by exact requestId. " +
|
|
970
|
+
"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. " +
|
|
971
|
+
"List incoming requests first when the target is ambiguous.",
|
|
972
|
+
),
|
|
973
|
+
parameters: ClawchatAcceptFriendRequestSchema,
|
|
974
|
+
async execute(_callId, params) {
|
|
975
|
+
return await recordClawchatToolCall(accountId, "clawchat_accept_friend_request", params, async () => {
|
|
976
|
+
const p = params as ClawchatAcceptFriendRequestParams;
|
|
977
|
+
return await withGatedClient(accountId, (c) => c.acceptFriendRequest(p.requestId));
|
|
978
|
+
});
|
|
979
|
+
},
|
|
980
|
+
};
|
|
921
981
|
},
|
|
922
982
|
{ name: "clawchat_accept_friend_request" },
|
|
923
983
|
);
|
|
924
984
|
|
|
925
985
|
api.registerTool(
|
|
926
|
-
{
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
"Reject
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
return await
|
|
939
|
-
|
|
940
|
-
|
|
986
|
+
(ctx) => {
|
|
987
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
988
|
+
return {
|
|
989
|
+
name: "clawchat_reject_friend_request",
|
|
990
|
+
label: "Reject ClawChat Friend Request",
|
|
991
|
+
description: toolDescription(
|
|
992
|
+
"Reject one pending incoming friend request by exact requestId. " +
|
|
993
|
+
"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. " +
|
|
994
|
+
"List incoming requests first when the target is ambiguous.",
|
|
995
|
+
),
|
|
996
|
+
parameters: ClawchatRejectFriendRequestSchema,
|
|
997
|
+
async execute(_callId, params) {
|
|
998
|
+
return await recordClawchatToolCall(accountId, "clawchat_reject_friend_request", params, async () => {
|
|
999
|
+
const p = params as ClawchatRejectFriendRequestParams;
|
|
1000
|
+
return await withGatedClient(accountId, (c) => c.rejectFriendRequest(p.requestId));
|
|
1001
|
+
});
|
|
1002
|
+
},
|
|
1003
|
+
};
|
|
941
1004
|
},
|
|
942
1005
|
{ name: "clawchat_reject_friend_request" },
|
|
943
1006
|
);
|
|
944
1007
|
|
|
945
1008
|
api.registerTool(
|
|
946
|
-
{
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
"Remove
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
return await
|
|
959
|
-
|
|
960
|
-
|
|
1009
|
+
(ctx) => {
|
|
1010
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
1011
|
+
return {
|
|
1012
|
+
name: "clawchat_remove_friend",
|
|
1013
|
+
label: "Remove ClawChat Friend",
|
|
1014
|
+
description: toolDescription(
|
|
1015
|
+
"Remove an accepted friend from the connected ClawChat account by explicit friendUserId. " +
|
|
1016
|
+
"TRIGGER - invoke when the user asks to delete, remove, unfriend, or disconnect an existing ClawChat friend and provides a concrete friendUserId. " +
|
|
1017
|
+
"Use clawchat_list_account_friends first when the target is ambiguous; do not guess ids from names.",
|
|
1018
|
+
),
|
|
1019
|
+
parameters: ClawchatRemoveFriendSchema,
|
|
1020
|
+
async execute(_callId, params) {
|
|
1021
|
+
return await recordClawchatToolCall(accountId, "clawchat_remove_friend", params, async () => {
|
|
1022
|
+
const p = params as ClawchatRemoveFriendParams;
|
|
1023
|
+
return await withGatedClient(accountId, (c) => c.removeFriend(p.friendUserId));
|
|
1024
|
+
});
|
|
1025
|
+
},
|
|
1026
|
+
};
|
|
961
1027
|
},
|
|
962
1028
|
{ name: "clawchat_remove_friend" },
|
|
963
1029
|
);
|
|
964
1030
|
|
|
965
1031
|
api.registerTool(
|
|
966
|
-
{
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
"Search ClawChat
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
return await
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
1032
|
+
(ctx) => {
|
|
1033
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
1034
|
+
return {
|
|
1035
|
+
name: "clawchat_search_users",
|
|
1036
|
+
label: "Search ClawChat Users",
|
|
1037
|
+
description: toolDescription(
|
|
1038
|
+
"Search ClawChat users by username or nickname. Search server-side ClawChat users in the ClawChat user directory. " +
|
|
1039
|
+
"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\". " +
|
|
1040
|
+
"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. " +
|
|
1041
|
+
"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.",
|
|
1042
|
+
),
|
|
1043
|
+
parameters: ClawchatSearchUsersSchema,
|
|
1044
|
+
async execute(_callId, params) {
|
|
1045
|
+
return await recordClawchatToolCall(accountId, "clawchat_search_users", params, async () => {
|
|
1046
|
+
const p = (params ?? {}) as ClawchatSearchUsersParams;
|
|
1047
|
+
return await withClient(accountId, (c) =>
|
|
1048
|
+
c.searchUsers({
|
|
1049
|
+
...(typeof p.q === "string" ? { q: p.q } : {}),
|
|
1050
|
+
...(typeof p.limit === "number" ? { limit: p.limit } : {}),
|
|
1051
|
+
}),
|
|
1052
|
+
);
|
|
1053
|
+
});
|
|
1054
|
+
},
|
|
1055
|
+
};
|
|
987
1056
|
},
|
|
988
1057
|
{ name: "clawchat_search_users" },
|
|
989
1058
|
);
|
|
990
1059
|
|
|
991
1060
|
api.registerTool(
|
|
992
|
-
{
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
"
|
|
997
|
-
|
|
998
|
-
"
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1061
|
+
(ctx) => {
|
|
1062
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
1063
|
+
return {
|
|
1064
|
+
name: "clawchat_get_conversation",
|
|
1065
|
+
label: "Get ClawChat Conversation",
|
|
1066
|
+
description: toolDescription(
|
|
1067
|
+
"Fetch read-only ClawChat conversation details, including group membership when returned by the API. " +
|
|
1068
|
+
"TRIGGER - invoke when the user asks to inspect a specific ClawChat conversation or group and provides a concrete conversationId. " +
|
|
1069
|
+
"This is read-only and does not create, update, leave, dissolve, or administer conversations.",
|
|
1070
|
+
),
|
|
1071
|
+
parameters: ClawchatGetConversationSchema,
|
|
1072
|
+
async execute(_callId, params) {
|
|
1073
|
+
return await recordClawchatToolCall(accountId, "clawchat_get_conversation", params, async () => {
|
|
1074
|
+
const p = params as ClawchatGetConversationParams;
|
|
1075
|
+
const built = buildClient(accountId);
|
|
1076
|
+
if (!built.ok) return built.error;
|
|
1077
|
+
try {
|
|
1078
|
+
const data = await built.client.getConversation(p.conversationId);
|
|
1079
|
+
return jsonResponse(data);
|
|
1080
|
+
} catch (err) {
|
|
1081
|
+
if (err instanceof ClawlingApiError) {
|
|
1082
|
+
return apiError(err);
|
|
1083
|
+
}
|
|
1084
|
+
return genericError(err);
|
|
1012
1085
|
}
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
},
|
|
1086
|
+
});
|
|
1087
|
+
},
|
|
1088
|
+
};
|
|
1017
1089
|
},
|
|
1018
1090
|
{ name: "clawchat_get_conversation" },
|
|
1019
1091
|
);
|
|
1020
1092
|
|
|
1021
1093
|
api.registerTool(
|
|
1022
|
-
{
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
"Leave
|
|
1027
|
-
|
|
1028
|
-
"
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1094
|
+
(ctx) => {
|
|
1095
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
1096
|
+
return {
|
|
1097
|
+
name: "clawchat_leave_group",
|
|
1098
|
+
label: "Leave ClawChat Group",
|
|
1099
|
+
description: toolDescription(
|
|
1100
|
+
"Leave a ClawChat group conversation the agent is currently a member of. " +
|
|
1101
|
+
"TRIGGER - invoke only when the user explicitly asks the agent to leave, exit, or quit a specific ClawChat group and provides a concrete conversationId. " +
|
|
1102
|
+
"Groups only: direct conversations are rejected by the server. " +
|
|
1103
|
+
"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. " +
|
|
1104
|
+
"After this tool succeeds, output only <clawchat:no-reply/> and do not send a normal follow-up reply.",
|
|
1105
|
+
),
|
|
1106
|
+
parameters: ClawchatLeaveGroupSchema,
|
|
1107
|
+
async execute(_callId, params) {
|
|
1108
|
+
return await recordClawchatToolCall(accountId, "clawchat_leave_group", params, async () => {
|
|
1109
|
+
const p = params as ClawchatLeaveGroupParams;
|
|
1110
|
+
const built = buildClient(accountId);
|
|
1111
|
+
if (!built.ok) return built.error;
|
|
1112
|
+
try {
|
|
1113
|
+
const data = await built.client.leaveConversation(p.conversationId);
|
|
1114
|
+
return jsonResponse(data);
|
|
1115
|
+
} catch (err) {
|
|
1116
|
+
if (err instanceof ClawlingApiError) {
|
|
1117
|
+
return apiError(err);
|
|
1118
|
+
}
|
|
1119
|
+
return genericError(err);
|
|
1044
1120
|
}
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
},
|
|
1121
|
+
});
|
|
1122
|
+
},
|
|
1123
|
+
};
|
|
1049
1124
|
},
|
|
1050
1125
|
{ name: "clawchat_leave_group" },
|
|
1051
1126
|
);
|
|
1052
1127
|
|
|
1053
1128
|
api.registerTool(
|
|
1054
|
-
{
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
"Add
|
|
1059
|
-
|
|
1060
|
-
"
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1129
|
+
(ctx) => {
|
|
1130
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
1131
|
+
return {
|
|
1132
|
+
name: "clawchat_add_group_member",
|
|
1133
|
+
label: "Add ClawChat Group Member",
|
|
1134
|
+
description: toolDescription(
|
|
1135
|
+
"Add a specified ClawChat user to a group conversation the agent is a member of. " +
|
|
1136
|
+
"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. " +
|
|
1137
|
+
"Never guess a userId from a name or nickname — use ids from ClawChat Group Message Metadata, clawchat_list_account_friends, or clawchat_search_users. " +
|
|
1138
|
+
"Groups only: direct conversations are rejected by the server. " +
|
|
1139
|
+
"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.",
|
|
1140
|
+
),
|
|
1141
|
+
parameters: ClawchatAddGroupMemberSchema,
|
|
1142
|
+
async execute(_callId, params) {
|
|
1143
|
+
return await recordClawchatToolCall(accountId, "clawchat_add_group_member", params, async () => {
|
|
1144
|
+
const p = params as ClawchatAddGroupMemberParams;
|
|
1145
|
+
const built = buildClient(accountId);
|
|
1146
|
+
if (!built.ok) return built.error;
|
|
1147
|
+
try {
|
|
1148
|
+
const data = await built.client.addConversationMember(p.conversationId, p.userId);
|
|
1149
|
+
return jsonResponse(data);
|
|
1150
|
+
} catch (err) {
|
|
1151
|
+
if (err instanceof ClawlingApiError) {
|
|
1152
|
+
return apiError(err);
|
|
1153
|
+
}
|
|
1154
|
+
return genericError(err);
|
|
1076
1155
|
}
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
},
|
|
1156
|
+
});
|
|
1157
|
+
},
|
|
1158
|
+
};
|
|
1081
1159
|
},
|
|
1082
1160
|
{ name: "clawchat_add_group_member" },
|
|
1083
1161
|
);
|
|
1084
1162
|
|
|
1085
1163
|
api.registerTool(
|
|
1086
|
-
{
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
"Send
|
|
1091
|
-
|
|
1092
|
-
"
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
const p = validated.params;
|
|
1109
|
-
const client = getOpenclawClawlingClient(account.accountId);
|
|
1110
|
-
if (!client) {
|
|
1111
|
-
return configError("ClawChat websocket client is not ready");
|
|
1164
|
+
(ctx) => {
|
|
1165
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
1166
|
+
return {
|
|
1167
|
+
name: "clawchat_mention_message",
|
|
1168
|
+
label: "Send ClawChat Mention Message",
|
|
1169
|
+
description: toolDescription(
|
|
1170
|
+
"Send a real ClawChat mention message to a direct or group conversation. " +
|
|
1171
|
+
"TRIGGER - invoke when the user asks to @, mention, notify, or address ClawChat users. " +
|
|
1172
|
+
"Use `mentioned_users` or `sender_id` from current ClawChat sender/group message metadata as `mentions[].userId` when present. " +
|
|
1173
|
+
"Use `clawchat_search_users` only when no explicit or locally available id exists. " +
|
|
1174
|
+
"Never guess userId from names, nicknames, or plain @name text. Plain @name is not a real mention. " +
|
|
1175
|
+
"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. " +
|
|
1176
|
+
"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.",
|
|
1177
|
+
),
|
|
1178
|
+
parameters: ClawchatMentionMessageSchema,
|
|
1179
|
+
async execute(_callId, params) {
|
|
1180
|
+
return await recordClawchatToolCall(accountId, "clawchat_mention_message", params, async () => {
|
|
1181
|
+
const validated = validateMentionMessageParams(params);
|
|
1182
|
+
if (!validated.ok) return validationError(validated.message);
|
|
1183
|
+
const account = resolveCurrent(accountId);
|
|
1184
|
+
if (!account.configured) {
|
|
1185
|
+
return configError("clawchat-plugin-openclaw: account is not configured");
|
|
1112
1186
|
}
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
account
|
|
1116
|
-
|
|
1187
|
+
try {
|
|
1188
|
+
const p = validated.params;
|
|
1189
|
+
const client = getOpenclawClawlingClient(account.accountId);
|
|
1190
|
+
if (!client) {
|
|
1191
|
+
return configError("ClawChat websocket client is not ready");
|
|
1192
|
+
}
|
|
1193
|
+
const result = await sendOpenclawClawlingMentionMessage({
|
|
1194
|
+
client,
|
|
1195
|
+
account,
|
|
1196
|
+
to: {
|
|
1197
|
+
chatId: p.chatId.trim(),
|
|
1198
|
+
chatType: p.chatType ?? "group",
|
|
1199
|
+
},
|
|
1200
|
+
...(p.text !== undefined ? { text: p.text } : {}),
|
|
1201
|
+
mentions: p.mentions,
|
|
1202
|
+
...(p.replyToMessageId
|
|
1203
|
+
? {
|
|
1204
|
+
replyCtx: {
|
|
1205
|
+
replyToMessageId: p.replyToMessageId,
|
|
1206
|
+
},
|
|
1207
|
+
}
|
|
1208
|
+
: {}),
|
|
1209
|
+
log: api.logger,
|
|
1210
|
+
});
|
|
1211
|
+
if (!result) return jsonResponse(result);
|
|
1212
|
+
markTerminalClawChatSend({
|
|
1213
|
+
accountId: account.accountId,
|
|
1117
1214
|
chatId: p.chatId.trim(),
|
|
1118
|
-
|
|
1119
|
-
}
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
}
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
chatId: p.chatId.trim(),
|
|
1135
|
-
messageId: result.messageId,
|
|
1136
|
-
});
|
|
1137
|
-
return jsonResponse({
|
|
1138
|
-
sent: true,
|
|
1139
|
-
terminal: true,
|
|
1140
|
-
noFollowupReply: true,
|
|
1141
|
-
instruction:
|
|
1142
|
-
"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.",
|
|
1143
|
-
...result,
|
|
1144
|
-
});
|
|
1145
|
-
} catch (err) {
|
|
1146
|
-
return genericError(err);
|
|
1147
|
-
}
|
|
1148
|
-
});
|
|
1149
|
-
},
|
|
1215
|
+
messageId: result.messageId,
|
|
1216
|
+
});
|
|
1217
|
+
return jsonResponse({
|
|
1218
|
+
sent: true,
|
|
1219
|
+
terminal: true,
|
|
1220
|
+
noFollowupReply: true,
|
|
1221
|
+
instruction:
|
|
1222
|
+
"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.",
|
|
1223
|
+
...result,
|
|
1224
|
+
});
|
|
1225
|
+
} catch (err) {
|
|
1226
|
+
return genericError(err);
|
|
1227
|
+
}
|
|
1228
|
+
});
|
|
1229
|
+
},
|
|
1230
|
+
};
|
|
1150
1231
|
},
|
|
1151
1232
|
{ name: "clawchat_mention_message" },
|
|
1152
1233
|
);
|
|
1153
1234
|
|
|
1154
1235
|
api.registerTool(
|
|
1155
|
-
{
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
"React to
|
|
1160
|
-
|
|
1161
|
-
"
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1236
|
+
(ctx) => {
|
|
1237
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
1238
|
+
return {
|
|
1239
|
+
name: "clawchat_react_message",
|
|
1240
|
+
label: "React to ClawChat Message",
|
|
1241
|
+
description: toolDescription(
|
|
1242
|
+
"React to a ClawChat message with a single quick emoji (the bubble long-press reaction), instead of sending a text message. " +
|
|
1243
|
+
"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. " +
|
|
1244
|
+
"Omit targetMessageId and chatId to react to the message that triggered the current turn; pass targetMessageId to react to an earlier message. " +
|
|
1245
|
+
"Set remove:true to take back your reaction. " +
|
|
1246
|
+
"Prefer the quick set 👍 ❤️ 😂 😮 😢 🙏 🎉 👏 🔥 😍 🤔. " +
|
|
1247
|
+
"When a reaction is all you want to send, do not also send a normal text reply.",
|
|
1248
|
+
),
|
|
1249
|
+
parameters: ClawchatReactMessageSchema,
|
|
1250
|
+
async execute(_callId, params) {
|
|
1251
|
+
return await recordClawchatToolCall(accountId, "clawchat_react_message", params, async () => {
|
|
1252
|
+
const p = params as ClawchatReactMessageParams;
|
|
1253
|
+
if (!p.emoji?.trim()) {
|
|
1254
|
+
return validationError("clawchat-plugin-openclaw: emoji is required");
|
|
1255
|
+
}
|
|
1256
|
+
const account = resolveCurrent(accountId);
|
|
1257
|
+
if (!account.configured) {
|
|
1258
|
+
return configError("clawchat-plugin-openclaw: account is not configured");
|
|
1259
|
+
}
|
|
1260
|
+
const scope = getCurrentTerminalSendScope();
|
|
1261
|
+
const chatId = p.chatId?.trim() || scope?.chatId;
|
|
1262
|
+
const targetMessageId = p.targetMessageId?.trim() || scope?.messageId;
|
|
1263
|
+
if (!chatId) {
|
|
1264
|
+
return validationError(
|
|
1265
|
+
"clawchat-plugin-openclaw: chatId is required (no current-turn chat to default from)",
|
|
1266
|
+
);
|
|
1267
|
+
}
|
|
1268
|
+
if (!targetMessageId) {
|
|
1269
|
+
return validationError(
|
|
1270
|
+
"clawchat-plugin-openclaw: targetMessageId is required (no current-turn message to default from)",
|
|
1271
|
+
);
|
|
1272
|
+
}
|
|
1273
|
+
const client = getOpenclawClawlingClient(account.accountId);
|
|
1274
|
+
if (!client) {
|
|
1275
|
+
return configError("ClawChat websocket client is not ready");
|
|
1276
|
+
}
|
|
1277
|
+
const emoji = p.emoji.trim();
|
|
1278
|
+
const removed = p.remove === true;
|
|
1279
|
+
try {
|
|
1280
|
+
sendOpenclawClawlingReaction({ client, chatId, targetMessageId, emoji, removed, log: api.logger });
|
|
1281
|
+
return jsonResponse({ reacted: true, targetMessageId, emoji, removed });
|
|
1282
|
+
} catch (err) {
|
|
1283
|
+
return genericError(err);
|
|
1284
|
+
}
|
|
1285
|
+
});
|
|
1286
|
+
},
|
|
1287
|
+
};
|
|
1204
1288
|
},
|
|
1205
1289
|
{ name: "clawchat_react_message" },
|
|
1206
1290
|
);
|
|
1207
1291
|
|
|
1208
1292
|
api.registerTool(
|
|
1209
|
-
{
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
"List
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
return await
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1293
|
+
(ctx) => {
|
|
1294
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
1295
|
+
return {
|
|
1296
|
+
name: "clawchat_list_moments",
|
|
1297
|
+
label: "List ClawChat Moments",
|
|
1298
|
+
description: toolDescription(
|
|
1299
|
+
"List the configured ClawChat account's visible moments feed, including moments from the account and its friends. " +
|
|
1300
|
+
"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\". " +
|
|
1301
|
+
"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.",
|
|
1302
|
+
),
|
|
1303
|
+
parameters: ClawchatListMomentsSchema,
|
|
1304
|
+
async execute(_callId, params) {
|
|
1305
|
+
return await recordClawchatToolCall(accountId, "clawchat_list_moments", params, async () => {
|
|
1306
|
+
const p = (params ?? {}) as ClawchatListMomentsParams;
|
|
1307
|
+
return await withClient(accountId, (c) =>
|
|
1308
|
+
c.listMoments({
|
|
1309
|
+
...(typeof p.before === "number" ? { before: p.before } : {}),
|
|
1310
|
+
...(typeof p.limit === "number" ? { limit: p.limit } : {}),
|
|
1311
|
+
}),
|
|
1312
|
+
);
|
|
1313
|
+
});
|
|
1314
|
+
},
|
|
1315
|
+
};
|
|
1229
1316
|
},
|
|
1230
1317
|
{ name: "clawchat_list_moments" },
|
|
1231
1318
|
);
|
|
1232
1319
|
|
|
1233
1320
|
api.registerTool(
|
|
1234
|
-
{
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
"
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
return await
|
|
1245
|
-
|
|
1246
|
-
|
|
1321
|
+
(ctx) => {
|
|
1322
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
1323
|
+
return {
|
|
1324
|
+
name: "clawchat_get_moment",
|
|
1325
|
+
label: "Get ClawChat Moment",
|
|
1326
|
+
description: toolDescription(
|
|
1327
|
+
"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.",
|
|
1328
|
+
),
|
|
1329
|
+
parameters: ClawchatGetMomentSchema,
|
|
1330
|
+
async execute(_callId, params) {
|
|
1331
|
+
return await recordClawchatToolCall(accountId, "clawchat_get_moment", params, async () => {
|
|
1332
|
+
const p = (params ?? {}) as ClawchatGetMomentParams;
|
|
1333
|
+
return await withClient(accountId, (c) => c.getMoment(p.momentId));
|
|
1334
|
+
});
|
|
1335
|
+
},
|
|
1336
|
+
};
|
|
1247
1337
|
},
|
|
1248
1338
|
{ name: "clawchat_get_moment" },
|
|
1249
1339
|
);
|
|
1250
1340
|
|
|
1251
1341
|
api.registerTool(
|
|
1252
|
-
{
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
"Create
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1342
|
+
(ctx) => {
|
|
1343
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
1344
|
+
return {
|
|
1345
|
+
name: "clawchat_create_moment",
|
|
1346
|
+
label: "Create ClawChat Moment",
|
|
1347
|
+
description: toolDescription(
|
|
1348
|
+
"Create a new ClawChat moment/dynamic for the configured ClawChat account. " +
|
|
1349
|
+
"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 ...\". " +
|
|
1350
|
+
"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.",
|
|
1351
|
+
),
|
|
1352
|
+
parameters: ClawchatCreateMomentSchema,
|
|
1353
|
+
async execute(_callId, params) {
|
|
1354
|
+
return await recordClawchatToolCall(accountId, "clawchat_create_moment", params, async () => {
|
|
1355
|
+
const p = (params ?? {}) as ClawchatCreateMomentParams;
|
|
1356
|
+
const text = typeof p.text === "string" ? p.text : undefined;
|
|
1357
|
+
const images = Array.isArray(p.images) ? p.images : undefined;
|
|
1358
|
+
if (!text && (!images || images.length === 0)) {
|
|
1359
|
+
return validationError("clawchat-plugin-openclaw: at least one of text or images is required");
|
|
1360
|
+
}
|
|
1361
|
+
return await withGatedClient(accountId, (c) =>
|
|
1362
|
+
c.createMoment({
|
|
1363
|
+
...(text !== undefined ? { text } : {}),
|
|
1364
|
+
...(images !== undefined ? { images } : {}),
|
|
1365
|
+
}),
|
|
1366
|
+
);
|
|
1367
|
+
});
|
|
1368
|
+
},
|
|
1369
|
+
};
|
|
1277
1370
|
},
|
|
1278
1371
|
{ name: "clawchat_create_moment" },
|
|
1279
1372
|
);
|
|
1280
1373
|
|
|
1281
1374
|
api.registerTool(
|
|
1282
|
-
{
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
"Delete
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
return await
|
|
1295
|
-
|
|
1296
|
-
|
|
1375
|
+
(ctx) => {
|
|
1376
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
1377
|
+
return {
|
|
1378
|
+
name: "clawchat_delete_moment",
|
|
1379
|
+
label: "Delete ClawChat Moment",
|
|
1380
|
+
description: toolDescription(
|
|
1381
|
+
"Delete a ClawChat moment by moment id. " +
|
|
1382
|
+
"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. " +
|
|
1383
|
+
"Only the moment author can delete it. Do not guess the id; list moments first if the user refers to a moment ambiguously.",
|
|
1384
|
+
),
|
|
1385
|
+
parameters: ClawchatDeleteMomentSchema,
|
|
1386
|
+
async execute(_callId, params) {
|
|
1387
|
+
return await recordClawchatToolCall(accountId, "clawchat_delete_moment", params, async () => {
|
|
1388
|
+
const p = params as ClawchatDeleteMomentParams;
|
|
1389
|
+
return await withGatedClient(accountId, (c) => c.deleteMoment(p.momentId));
|
|
1390
|
+
});
|
|
1391
|
+
},
|
|
1392
|
+
};
|
|
1297
1393
|
},
|
|
1298
1394
|
{ name: "clawchat_delete_moment" },
|
|
1299
1395
|
);
|
|
1300
1396
|
|
|
1301
1397
|
api.registerTool(
|
|
1302
|
-
{
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
"Toggle
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1398
|
+
(ctx) => {
|
|
1399
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
1400
|
+
return {
|
|
1401
|
+
name: "clawchat_toggle_moment_reaction",
|
|
1402
|
+
label: "Toggle ClawChat Moment Reaction",
|
|
1403
|
+
description: toolDescription(
|
|
1404
|
+
"Toggle an emoji reaction on a ClawChat moment. " +
|
|
1405
|
+
"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\". " +
|
|
1406
|
+
"The API adds the reaction if missing and removes it if already present. Require a concrete moment id and emoji.",
|
|
1407
|
+
),
|
|
1408
|
+
parameters: ClawchatToggleMomentReactionSchema,
|
|
1409
|
+
async execute(_callId, params) {
|
|
1410
|
+
return await recordClawchatToolCall(accountId, "clawchat_toggle_moment_reaction", params, async () => {
|
|
1411
|
+
const p = params as ClawchatToggleMomentReactionParams;
|
|
1412
|
+
if (!p.emoji?.trim()) {
|
|
1413
|
+
return validationError("clawchat-plugin-openclaw: emoji is required");
|
|
1414
|
+
}
|
|
1415
|
+
return await withGatedClient(accountId, (c) =>
|
|
1416
|
+
c.toggleMomentReaction({ momentId: p.momentId, emoji: p.emoji }),
|
|
1417
|
+
);
|
|
1418
|
+
});
|
|
1419
|
+
},
|
|
1420
|
+
};
|
|
1322
1421
|
},
|
|
1323
1422
|
{ name: "clawchat_toggle_moment_reaction" },
|
|
1324
1423
|
);
|
|
1325
1424
|
|
|
1326
1425
|
api.registerTool(
|
|
1327
|
-
{
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
"Create
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1426
|
+
(ctx) => {
|
|
1427
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
1428
|
+
return {
|
|
1429
|
+
name: "clawchat_create_moment_comment",
|
|
1430
|
+
label: "Create ClawChat Moment Comment",
|
|
1431
|
+
description: toolDescription(
|
|
1432
|
+
"Create a top-level comment on a ClawChat moment. " +
|
|
1433
|
+
"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 ...\". " +
|
|
1434
|
+
"Require a concrete moment id and non-empty text. Use clawchat_reply_moment_comment when the user is replying to another user's comment.",
|
|
1435
|
+
),
|
|
1436
|
+
parameters: ClawchatCreateMomentCommentSchema,
|
|
1437
|
+
async execute(_callId, params) {
|
|
1438
|
+
return await recordClawchatToolCall(accountId, "clawchat_create_moment_comment", params, async () => {
|
|
1439
|
+
const p = params as ClawchatCreateMomentCommentParams;
|
|
1440
|
+
if (!p.text?.trim()) {
|
|
1441
|
+
return validationError("clawchat-plugin-openclaw: text is required");
|
|
1442
|
+
}
|
|
1443
|
+
return await withGatedClient(accountId, (c) =>
|
|
1444
|
+
c.createMomentComment({ momentId: p.momentId, text: p.text }),
|
|
1445
|
+
);
|
|
1446
|
+
});
|
|
1447
|
+
},
|
|
1448
|
+
};
|
|
1347
1449
|
},
|
|
1348
1450
|
{ name: "clawchat_create_moment_comment" },
|
|
1349
1451
|
);
|
|
1350
1452
|
|
|
1351
1453
|
api.registerTool(
|
|
1352
|
-
{
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
"Reply
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1454
|
+
(ctx) => {
|
|
1455
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
1456
|
+
return {
|
|
1457
|
+
name: "clawchat_reply_moment_comment",
|
|
1458
|
+
label: "Reply To ClawChat Moment Comment",
|
|
1459
|
+
description: toolDescription(
|
|
1460
|
+
"Reply to an existing ClawChat moment comment with a single-level reply. " +
|
|
1461
|
+
"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 ...\". " +
|
|
1462
|
+
"Require concrete moment and comment ids; do not use this for top-level comments.",
|
|
1463
|
+
),
|
|
1464
|
+
parameters: ClawchatReplyMomentCommentSchema,
|
|
1465
|
+
async execute(_callId, params) {
|
|
1466
|
+
return await recordClawchatToolCall(accountId, "clawchat_reply_moment_comment", params, async () => {
|
|
1467
|
+
const p = params as ClawchatReplyMomentCommentParams;
|
|
1468
|
+
if (!p.text?.trim()) {
|
|
1469
|
+
return validationError("clawchat-plugin-openclaw: text is required");
|
|
1470
|
+
}
|
|
1471
|
+
return await withGatedClient(accountId, (c) =>
|
|
1472
|
+
c.replyMomentComment({
|
|
1473
|
+
momentId: p.momentId,
|
|
1474
|
+
replyToCommentId: p.replyToCommentId,
|
|
1475
|
+
text: p.text,
|
|
1476
|
+
}),
|
|
1477
|
+
);
|
|
1478
|
+
});
|
|
1479
|
+
},
|
|
1480
|
+
};
|
|
1376
1481
|
},
|
|
1377
1482
|
{ name: "clawchat_reply_moment_comment" },
|
|
1378
1483
|
);
|
|
1379
1484
|
|
|
1380
1485
|
api.registerTool(
|
|
1381
|
-
{
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
"Delete
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
return await
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1486
|
+
(ctx) => {
|
|
1487
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
1488
|
+
return {
|
|
1489
|
+
name: "clawchat_delete_moment_comment",
|
|
1490
|
+
label: "Delete ClawChat Moment Comment",
|
|
1491
|
+
description: toolDescription(
|
|
1492
|
+
"Delete a comment on a ClawChat moment. " +
|
|
1493
|
+
"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. " +
|
|
1494
|
+
"The caller may delete comments they authored or comments on moments they authored. Do not guess ids; list moments first if needed.",
|
|
1495
|
+
),
|
|
1496
|
+
parameters: ClawchatDeleteMomentCommentSchema,
|
|
1497
|
+
async execute(_callId, params) {
|
|
1498
|
+
return await recordClawchatToolCall(accountId, "clawchat_delete_moment_comment", params, async () => {
|
|
1499
|
+
const p = params as ClawchatDeleteMomentCommentParams;
|
|
1500
|
+
return await withGatedClient(accountId, (c) =>
|
|
1501
|
+
c.deleteMomentComment({ momentId: p.momentId, commentId: p.commentId }),
|
|
1502
|
+
);
|
|
1503
|
+
});
|
|
1504
|
+
},
|
|
1505
|
+
};
|
|
1398
1506
|
},
|
|
1399
1507
|
{ name: "clawchat_delete_moment_comment" },
|
|
1400
1508
|
);
|
|
1401
1509
|
|
|
1402
1510
|
api.registerTool(
|
|
1403
|
-
{
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
"Update
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1511
|
+
(ctx) => {
|
|
1512
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
1513
|
+
return {
|
|
1514
|
+
name: "clawchat_update_account_profile",
|
|
1515
|
+
label: "Update ClawChat Account Profile",
|
|
1516
|
+
description: toolDescription(
|
|
1517
|
+
"Update nickname/avatar_url/bio on the agent's connected ClawChat account (the configured ClawChat account), which mirrors the local assistant identity. " +
|
|
1518
|
+
"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: " +
|
|
1519
|
+
"(1) ClawChat account nickname/name change: 'change the ClawChat account nickname to X', " +
|
|
1520
|
+
"'set this assistant name to X', 'change the assistant nickname to X', 'set the account name to X' " +
|
|
1521
|
+
"→ call with `nickname = X`; " +
|
|
1522
|
+
"(2) ClawChat account avatar/profile-picture change: 'change the ClawChat account avatar', " +
|
|
1523
|
+
"'use this image as the assistant profile picture', 'change the account avatar to this image' " +
|
|
1524
|
+
"→ first obtain the avatar URL (upload via `clawchat_upload_avatar_image`, OR use a provided URL directly), " +
|
|
1525
|
+
"then call this tool with `avatar_url = <url>`; " +
|
|
1526
|
+
"(3) ClawChat account bio/self-introduction change: 'update the ClawChat bio', " +
|
|
1527
|
+
"'set the assistant self-introduction to X', 'change the account bio to X' " +
|
|
1528
|
+
"→ call with `bio = X`. " +
|
|
1529
|
+
"You can pass `nickname`, `avatar_url`, and `bio` together in one call, or just one of them. " +
|
|
1530
|
+
"At least one of the three must be present. Do not frame this as updating a human user's personal account.",
|
|
1531
|
+
),
|
|
1532
|
+
parameters: ClawchatUpdateAccountProfileSchema,
|
|
1533
|
+
async execute(_callId, params) {
|
|
1534
|
+
return await recordClawchatToolCall(accountId, "clawchat_update_account_profile", params, async () => {
|
|
1535
|
+
const p = (params ?? {}) as ClawchatUpdateAccountProfileParams;
|
|
1536
|
+
const patch: { nickname?: string; avatar_url?: string; bio?: string } = {};
|
|
1537
|
+
if (typeof p.nickname === "string") patch.nickname = p.nickname;
|
|
1538
|
+
if (typeof p.avatar_url === "string") patch.avatar_url = p.avatar_url;
|
|
1539
|
+
if (typeof p.bio === "string") patch.bio = p.bio;
|
|
1540
|
+
if (Object.keys(patch).length === 0) {
|
|
1541
|
+
return validationError(
|
|
1542
|
+
"clawchat-plugin-openclaw: at least one of nickname / avatar / bio is required",
|
|
1543
|
+
);
|
|
1544
|
+
}
|
|
1545
|
+
return await withClient(accountId, (c): Promise<Profile> => c.updateMyProfile(patch));
|
|
1546
|
+
});
|
|
1547
|
+
},
|
|
1548
|
+
};
|
|
1438
1549
|
},
|
|
1439
1550
|
{ name: "clawchat_update_account_profile" },
|
|
1440
1551
|
);
|
|
1441
1552
|
|
|
1442
1553
|
api.registerTool(
|
|
1443
|
-
{
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
"Upload
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
stat
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1554
|
+
(ctx) => {
|
|
1555
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
1556
|
+
return {
|
|
1557
|
+
name: "clawchat_upload_avatar_image",
|
|
1558
|
+
label: "Upload ClawChat Avatar Image",
|
|
1559
|
+
description: toolDescription(
|
|
1560
|
+
"Upload an absolute local image path for use as the agent's connected ClawChat account avatar (max 20MB), returning a hosted avatar URL. " +
|
|
1561
|
+
"TRIGGER — invoke when the user provides an absolute local image path and asks to upload it for the ClawChat account avatar/profile picture. " +
|
|
1562
|
+
"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.",
|
|
1563
|
+
),
|
|
1564
|
+
parameters: ClawchatUploadAvatarImageSchema,
|
|
1565
|
+
async execute(_callId, params) {
|
|
1566
|
+
return await recordClawchatToolCall(accountId, "clawchat_upload_avatar_image", params, async () => {
|
|
1567
|
+
const p = params as ClawchatUploadAvatarImageParams;
|
|
1568
|
+
if (!p.filePath || !path.isAbsolute(p.filePath)) {
|
|
1569
|
+
return validationError("clawchat-plugin-openclaw: filePath must be an absolute local path");
|
|
1570
|
+
}
|
|
1571
|
+
let stat: fs.Stats;
|
|
1572
|
+
try {
|
|
1573
|
+
stat = fs.statSync(p.filePath);
|
|
1574
|
+
} catch (err) {
|
|
1575
|
+
return validationError(
|
|
1576
|
+
`clawchat-plugin-openclaw: cannot stat ${p.filePath}: ${err instanceof Error ? err.message : String(err)}`,
|
|
1577
|
+
);
|
|
1578
|
+
}
|
|
1579
|
+
if (!stat.isFile()) {
|
|
1580
|
+
return validationError(`clawchat-plugin-openclaw: ${p.filePath} is not a regular file`);
|
|
1581
|
+
}
|
|
1582
|
+
if (stat.size > MAX_UPLOAD_BYTES) {
|
|
1583
|
+
return validationError(
|
|
1584
|
+
`clawchat-plugin-openclaw: file too large (${stat.size} bytes; max 20MB)`,
|
|
1585
|
+
);
|
|
1586
|
+
}
|
|
1587
|
+
const buffer = fs.readFileSync(p.filePath);
|
|
1588
|
+
const filename = path.basename(p.filePath);
|
|
1589
|
+
const mime = inferMimeFromPath(p.filePath);
|
|
1590
|
+
return await withClient(accountId, (c) => c.uploadAvatar({ buffer, filename, mime }));
|
|
1591
|
+
});
|
|
1592
|
+
},
|
|
1593
|
+
};
|
|
1480
1594
|
},
|
|
1481
1595
|
{ name: "clawchat_upload_avatar_image" },
|
|
1482
1596
|
);
|
|
1483
1597
|
|
|
1484
1598
|
api.registerTool(
|
|
1485
|
-
{
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
"Register
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
return await
|
|
1497
|
-
|
|
1498
|
-
|
|
1599
|
+
(ctx) => {
|
|
1600
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
1601
|
+
return {
|
|
1602
|
+
name: "clawchat_register_app",
|
|
1603
|
+
label: "Register ClawChat App",
|
|
1604
|
+
description: toolDescription(
|
|
1605
|
+
"Register a liveware-tunneled web app to ClawChat so it appears in the owner's chat with this agent. " +
|
|
1606
|
+
"Call AFTER `liveware tunnel bind` returns a public URL. Params: name, appId (liveware app id), url (public URL).",
|
|
1607
|
+
),
|
|
1608
|
+
parameters: ClawchatRegisterAppSchema,
|
|
1609
|
+
async execute(_callId, params) {
|
|
1610
|
+
return await recordClawchatToolCall(accountId, "clawchat_register_app", params, async () => {
|
|
1611
|
+
const p = params as ClawchatRegisterAppParams;
|
|
1612
|
+
return await withClient(accountId, (c) => c.registerApp({ name: p.name, appId: p.appId, url: p.url }));
|
|
1613
|
+
});
|
|
1614
|
+
},
|
|
1615
|
+
};
|
|
1499
1616
|
},
|
|
1500
1617
|
{ name: "clawchat_register_app" },
|
|
1501
1618
|
);
|
|
1502
1619
|
|
|
1503
1620
|
api.registerTool(
|
|
1504
|
-
{
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1621
|
+
(ctx) => {
|
|
1622
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
1623
|
+
return {
|
|
1624
|
+
name: "clawchat_list_apps",
|
|
1625
|
+
label: "List ClawChat Apps",
|
|
1626
|
+
description: toolDescription("List the liveware web apps this agent has registered to ClawChat."),
|
|
1627
|
+
parameters: ClawchatListAppsSchema,
|
|
1628
|
+
async execute(_callId, params) {
|
|
1629
|
+
return await recordClawchatToolCall(accountId, "clawchat_list_apps", params, async () =>
|
|
1630
|
+
withClient(accountId, (c) => c.listApps()),
|
|
1631
|
+
);
|
|
1632
|
+
},
|
|
1633
|
+
};
|
|
1512
1634
|
},
|
|
1513
1635
|
{ name: "clawchat_list_apps" },
|
|
1514
1636
|
);
|
|
1515
1637
|
|
|
1516
1638
|
api.registerTool(
|
|
1517
|
-
{
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
return await
|
|
1526
|
-
|
|
1527
|
-
|
|
1639
|
+
(ctx) => {
|
|
1640
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
1641
|
+
return {
|
|
1642
|
+
name: "clawchat_unregister_app",
|
|
1643
|
+
label: "Unregister ClawChat App",
|
|
1644
|
+
description: toolDescription("Unregister a previously registered liveware app from ClawChat. Param: appId (liveware app id)."),
|
|
1645
|
+
parameters: ClawchatUnregisterAppSchema,
|
|
1646
|
+
async execute(_callId, params) {
|
|
1647
|
+
return await recordClawchatToolCall(accountId, "clawchat_unregister_app", params, async () => {
|
|
1648
|
+
const p = params as ClawchatUnregisterAppParams;
|
|
1649
|
+
return await withClient(accountId, (c) => c.unregisterApp(p.appId));
|
|
1650
|
+
});
|
|
1651
|
+
},
|
|
1652
|
+
};
|
|
1528
1653
|
},
|
|
1529
1654
|
{ name: "clawchat_unregister_app" },
|
|
1530
1655
|
);
|
|
1531
1656
|
|
|
1532
1657
|
api.registerTool(
|
|
1533
|
-
{
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
"
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
return await new Promise<ReturnType<typeof jsonResponse>>((resolve) => {
|
|
1552
|
-
const livewarePath = resolveLivewarePath(api.runtime.state?.resolveStateDir?.());
|
|
1553
|
-
if (!livewarePath) {
|
|
1554
|
-
resolve(jsonResponse({
|
|
1555
|
-
error: "config",
|
|
1556
|
-
message: "liveware CLI not found in PATH; install liveware before calling this tool",
|
|
1557
|
-
}));
|
|
1558
|
-
return;
|
|
1658
|
+
(ctx) => {
|
|
1659
|
+
const accountId = resolveOpenclawClawlingToolAccountId(ctx);
|
|
1660
|
+
return {
|
|
1661
|
+
name: "clawchat_liveware_login",
|
|
1662
|
+
label: "Login to Liveware",
|
|
1663
|
+
description: toolDescription(
|
|
1664
|
+
"Log in to liveware CLI using the plugin-held ClawChat access token. " +
|
|
1665
|
+
"The token is resolved from the plugin's own credential store and is never passed through the LLM context. " +
|
|
1666
|
+
"Call this BEFORE any liveware tunnel or app commands; it is idempotent. " +
|
|
1667
|
+
"Returns { ok: true } on success. Returns an error if liveware is not installed, the ClawChat account is not activated, or login fails.",
|
|
1668
|
+
),
|
|
1669
|
+
parameters: ClawchatLivewareLoginSchema,
|
|
1670
|
+
async execute(_callId, params) {
|
|
1671
|
+
return await recordClawchatToolCall(accountId, "clawchat_liveware_login", params as ClawchatLivewareLoginParams, async () => {
|
|
1672
|
+
const acct = resolveCurrent(accountId);
|
|
1673
|
+
const token = acct.token;
|
|
1674
|
+
if (!token) {
|
|
1675
|
+
return configError("clawchat-plugin-openclaw: token is required");
|
|
1559
1676
|
}
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1677
|
+
|
|
1678
|
+
return await new Promise<ReturnType<typeof jsonResponse>>((resolve) => {
|
|
1679
|
+
const livewarePath = resolveLivewarePath(api.runtime.state?.resolveStateDir?.());
|
|
1680
|
+
if (!livewarePath) {
|
|
1681
|
+
resolve(jsonResponse({
|
|
1682
|
+
error: "config",
|
|
1683
|
+
message: "liveware CLI not found in PATH; install liveware before calling this tool",
|
|
1684
|
+
}));
|
|
1685
|
+
return;
|
|
1686
|
+
}
|
|
1687
|
+
// Security: the token is passed as a child process argv value using
|
|
1688
|
+
// liveware's documented --access-token interface. It is in the child
|
|
1689
|
+
// argv briefly during process creation; env-var or stdin delivery
|
|
1690
|
+
// would be preferred if liveware supported it.
|
|
1691
|
+
// NEVER log the argv array; scrub token from any captured output below.
|
|
1692
|
+
execFile(
|
|
1693
|
+
livewarePath,
|
|
1694
|
+
["login", "--access-token", token],
|
|
1695
|
+
{ timeout: 30000 },
|
|
1696
|
+
(err, stdout, stderr) => {
|
|
1697
|
+
const scrub = (s: string) => (token ? s.split(token).join("***") : s);
|
|
1698
|
+
const safeStdout = scrub(stdout ?? "").trim();
|
|
1699
|
+
const safeStderr = scrub(stderr ?? "").trim();
|
|
1700
|
+
|
|
1701
|
+
if (err) {
|
|
1702
|
+
if ((err as NodeJS.ErrnoException).code === "ENOENT") {
|
|
1703
|
+
resolve(jsonResponse({
|
|
1704
|
+
error: "config",
|
|
1705
|
+
message: "liveware CLI not found in PATH; install liveware before calling this tool",
|
|
1706
|
+
}));
|
|
1707
|
+
return;
|
|
1708
|
+
}
|
|
1709
|
+
const detail = [safeStderr, safeStdout].filter(Boolean).join("\n").trim();
|
|
1576
1710
|
resolve(jsonResponse({
|
|
1577
|
-
error: "
|
|
1578
|
-
message: "liveware
|
|
1711
|
+
error: "liveware_login_failed",
|
|
1712
|
+
message: "liveware login failed" + (detail ? `: ${detail}` : ""),
|
|
1579
1713
|
}));
|
|
1580
1714
|
return;
|
|
1581
1715
|
}
|
|
1582
|
-
const detail = [safeStderr, safeStdout].filter(Boolean).join("\n").trim();
|
|
1583
|
-
resolve(jsonResponse({
|
|
1584
|
-
error: "liveware_login_failed",
|
|
1585
|
-
message: "liveware login failed" + (detail ? `: ${detail}` : ""),
|
|
1586
|
-
}));
|
|
1587
|
-
return;
|
|
1588
|
-
}
|
|
1589
1716
|
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1717
|
+
resolve(jsonResponse({ ok: true }));
|
|
1718
|
+
},
|
|
1719
|
+
);
|
|
1720
|
+
});
|
|
1593
1721
|
});
|
|
1594
|
-
}
|
|
1595
|
-
}
|
|
1722
|
+
},
|
|
1723
|
+
};
|
|
1596
1724
|
},
|
|
1597
1725
|
{ name: "clawchat_liveware_login" },
|
|
1598
1726
|
);
|