@clawling/clawchat-plugin-openclaw 2026.9.22-2 → 2026.9.23-1

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.
@@ -1,5 +1,10 @@
1
1
  import { ClawlingApiError, } from "./api-types.js";
2
2
  import { CHANNEL_ID } from "./config.js";
3
+ const ORCH = "/v1/agents/me/orchestration";
4
+ /** Body + content-type for an orchestration write, so the two never drift apart. */
5
+ function orchJson(payload) {
6
+ return { body: JSON.stringify(payload), headers: { "content-type": "application/json" } };
7
+ }
3
8
  export function buildPluginReportBody(input) {
4
9
  const body = {
5
10
  device_id: input.deviceId,
@@ -264,8 +269,13 @@ export function createOpenclawClawlingApiClient(opts) {
264
269
  }
265
270
  throw new ClawlingApiError("transport", text ? "invalid envelope: missing numeric `code`" : "non-JSON response: empty body", { status: res.status, path });
266
271
  }
267
- async function call(method, path, init) {
268
- let res;
272
+ /**
273
+ * Shared by `call<T>` and `orchCall`: build the request, invoke `fetchImpl`,
274
+ * and normalize any network-level failure into a `ClawlingApiError`. Kept
275
+ * as one function so the two callers' request-building can never drift
276
+ * apart again the way `call<T>` and `orchCall` briefly did.
277
+ */
278
+ async function doFetch(method, path, init) {
269
279
  try {
270
280
  const requestInit = {
271
281
  method,
@@ -274,13 +284,55 @@ export function createOpenclawClawlingApiClient(opts) {
274
284
  if (init?.body !== undefined) {
275
285
  requestInit.body = init.body;
276
286
  }
277
- res = await fetchImpl(url(path, init?.baseUrl || undefined), requestInit);
287
+ return await fetchImpl(url(path, init?.baseUrl || undefined), requestInit);
278
288
  }
279
289
  catch (err) {
280
290
  throw new ClawlingApiError("transport", `fetch failed: ${err instanceof Error ? err.message : String(err)}`, { path });
281
291
  }
292
+ }
293
+ async function call(method, path, init) {
294
+ const res = await doFetch(method, path, init);
282
295
  return await readEnvelope(res, path);
283
296
  }
297
+ /**
298
+ * Orchestration routes (`${ORCH}/...`) are always HTTP 200 with the
299
+ * business code inside the envelope — a `21003` permission denial is a
300
+ * normal outcome there, not a transport failure. Unlike `call<T>`, this
301
+ * does NOT unwrap `data` or throw on a non-zero `code`; it hands back the
302
+ * parsed envelope exactly as received so the caller can branch on `code`
303
+ * itself.
304
+ *
305
+ * A 401/403 is checked FIRST, before any body parsing, exactly like
306
+ * `readEnvelope` — an expired/malformed token is not a business-code
307
+ * outcome and must throw `kind: "auth"` rather than fall through to the
308
+ * empty-body branch below and be misread as success. NOTE: on the
309
+ * orchestration tool path (`tools.ts` `buildClient` → a raw client,
310
+ * consumed via `withClient`) this throw is NOT retried — `withClient`
311
+ * turns any `ClawlingApiError` straight into `apiError(err)`. Reactive
312
+ * retry-after-refresh only exists behind `getConversationApiClient`'s
313
+ * `withRefresh` wrapper (`runtime.ts`), which the twelve `orch*` tools do
314
+ * not go through; this matches the other ~34 tools already shipped on the
315
+ * same `withClient` path. Proactive rotation is still picked up here for
316
+ * free, since the token is read fresh from the account at call time.
317
+ */
318
+ async function orchCall(method, path, init) {
319
+ const res = await doFetch(method, path, init);
320
+ if (res.status === 401 || res.status === 403) {
321
+ throw new ClawlingApiError("auth", `unauthorized (status ${res.status})`, {
322
+ status: res.status,
323
+ path,
324
+ });
325
+ }
326
+ const text = await res.text().catch(() => "");
327
+ if (!text)
328
+ return undefined;
329
+ try {
330
+ return JSON.parse(text);
331
+ }
332
+ catch {
333
+ throw new ClawlingApiError("transport", "non-JSON response", { status: res.status, path });
334
+ }
335
+ }
284
336
  function parseUploadResult(data, path) {
285
337
  const obj = data;
286
338
  const validKind = obj?.kind === "image" || obj?.kind === "file" || obj?.kind === "audio" || obj?.kind === "video";
@@ -376,6 +428,51 @@ export function createOpenclawClawlingApiClient(opts) {
376
428
  assertNonBlankId(friendUserId, "removeFriend: friendUserId");
377
429
  return await call("DELETE", `/v1/friendships/${encodeURIComponent(friendUserId)}`);
378
430
  },
431
+ async orchListAgents() {
432
+ return await orchCall("GET", `${ORCH}/agents`);
433
+ },
434
+ async orchGetAgent(agentId) {
435
+ return await orchCall("GET", `${ORCH}/agents/${encodeURIComponent(agentId)}`);
436
+ },
437
+ async orchSetAgentBehavior(agentId, behavior) {
438
+ return await orchCall("PATCH", `${ORCH}/agents/${encodeURIComponent(agentId)}`, orchJson({ behavior }));
439
+ },
440
+ async orchListGroups() {
441
+ return await orchCall("GET", `${ORCH}/groups`);
442
+ },
443
+ async orchGetGroup(cid) {
444
+ return await orchCall("GET", `${ORCH}/groups/${encodeURIComponent(cid)}`);
445
+ },
446
+ async orchSetGroupPrompt(cid, description) {
447
+ return await orchCall("PATCH", `${ORCH}/groups/${encodeURIComponent(cid)}`, orchJson({ description }));
448
+ },
449
+ async orchCreateGroup(title, agentIds) {
450
+ return await orchCall("POST", `${ORCH}/groups`, orchJson({ title, agent_ids: agentIds }));
451
+ },
452
+ async orchAddGroupMember(cid, agentId) {
453
+ return await orchCall("POST", `${ORCH}/groups/${encodeURIComponent(cid)}/members`, orchJson({ agent_id: agentId }));
454
+ },
455
+ async orchRemoveGroupMember(cid, agentId) {
456
+ return await orchCall("DELETE", `${ORCH}/groups/${encodeURIComponent(cid)}/members/${encodeURIComponent(agentId)}`);
457
+ },
458
+ async orchSetGroupAgentSettings(cid, agentId, patch) {
459
+ // `!== undefined` rather than truthiness: `muted: false` is a real value
460
+ // the backend must see; an absent key means "leave unchanged".
461
+ const body = {};
462
+ if (patch.muted !== undefined)
463
+ body.muted = patch.muted;
464
+ if (patch.replyMode !== undefined)
465
+ body.reply_mode = patch.replyMode;
466
+ if (patch.batchDelaySeconds !== undefined)
467
+ body.batch_delay_seconds = patch.batchDelaySeconds;
468
+ return await orchCall("PATCH", `${ORCH}/groups/${encodeURIComponent(cid)}/agents/${encodeURIComponent(agentId)}`, orchJson(body));
469
+ },
470
+ async orchCreateConnectCode() {
471
+ return await orchCall("POST", `${ORCH}/connect-codes`);
472
+ },
473
+ async orchGetConnectCode(code) {
474
+ return await orchCall("GET", `${ORCH}/connect-codes/${encodeURIComponent(code)}`);
475
+ },
379
476
  async registerApp(params) {
380
477
  assertNonBlankId(params.appId, "registerApp: appId");
381
478
  return await call("POST", "/v1/agents/me/apps", {
@@ -66,7 +66,7 @@ export const OFFICIAL_SKILLS_BASE = "https://raw.githubusercontent.com/clawling/
66
66
  * in the install-cli repo, bump this constant, ship it. `liveware-sample.ts`
67
67
  * imports the same ref, so the `livewares` tree at that tag is pinned too.
68
68
  */
69
- export const DEFAULT_SKILLS_REF = "skills-v1.13.0";
69
+ export const DEFAULT_SKILLS_REF = "skills-v1.14.0";
70
70
  /** Refuse to treat an absurdly large response as a skill file (defence in depth). */
71
71
  export const MAX_SKILL_BYTES = 256 * 1024;
72
72
  /** This adapter's host target inside `skills/manifest.json`. */
@@ -284,3 +284,54 @@ export const ClawchatUnregisterAppSchema = Type.Object({
284
284
  appId: Type.String({ minLength: 1, description: "The liveware app id to unregister." }),
285
285
  });
286
286
  export const ClawchatLivewareLoginSchema = Type.Object({});
287
+ // --- cloud orchestration (`agent.orchestrate`) ----------------------------
288
+ // Twelve schemas, 1:1 with docs/features/agentorch.md.
289
+ const OrchAgentId = Type.String({
290
+ minLength: 1,
291
+ description: "Explicit agent id from clawchat_orchestrate_list_agents. Never guess one from a nickname.",
292
+ });
293
+ const OrchConversationId = Type.String({
294
+ minLength: 1,
295
+ description: "Explicit group conversation id from clawchat_orchestrate_list_groups.",
296
+ });
297
+ export const ClawchatOrchListAgentsSchema = Type.Object({}, { additionalProperties: false });
298
+ export const ClawchatOrchGetAgentSchema = Type.Object({ agentId: OrchAgentId }, { additionalProperties: false });
299
+ export const ClawchatOrchSetAgentBehaviorSchema = Type.Object({
300
+ agentId: OrchAgentId,
301
+ behavior: Type.String({
302
+ maxLength: 3000,
303
+ description: "The complete new system prompt. REPLACES the whole field — GET first and send the full value.",
304
+ }),
305
+ }, { additionalProperties: false });
306
+ export const ClawchatOrchListGroupsSchema = Type.Object({}, { additionalProperties: false });
307
+ export const ClawchatOrchGetGroupSchema = Type.Object({ conversationId: OrchConversationId }, { additionalProperties: false });
308
+ export const ClawchatOrchSetGroupPromptSchema = Type.Object({
309
+ conversationId: OrchConversationId,
310
+ description: Type.String({
311
+ maxLength: 3000,
312
+ description: "The complete new group system prompt. REPLACES the whole field.",
313
+ }),
314
+ }, { additionalProperties: false });
315
+ export const ClawchatOrchCreateGroupSchema = Type.Object({
316
+ title: Type.String({ minLength: 1, maxLength: 60, description: "Group title, 1-60 runes." }),
317
+ agentIds: Type.Array(Type.String({ minLength: 1 }), {
318
+ minItems: 1,
319
+ description: "Agent ids from clawchat_orchestrate_list_agents. Must be the owner's own agents.",
320
+ }),
321
+ }, { additionalProperties: false });
322
+ export const ClawchatOrchGroupMemberSchema = Type.Object({ conversationId: OrchConversationId, agentId: OrchAgentId }, { additionalProperties: false });
323
+ export const ClawchatOrchSetGroupAgentSettingsSchema = Type.Object({
324
+ conversationId: OrchConversationId,
325
+ agentId: OrchAgentId,
326
+ muted: Type.Optional(Type.Boolean({ description: "Mute or unmute this agent in this group." })),
327
+ replyMode: Type.Optional(Type.Union([Type.Literal("all"), Type.Literal("mention")], {
328
+ description: "all = speaks on every message; mention = only when @-mentioned.",
329
+ })),
330
+ batchDelaySeconds: Type.Optional(Type.Integer({
331
+ minimum: 1,
332
+ maximum: 3600,
333
+ description: "Seconds this agent batches messages before replying. Default 10. Pacing, not a limit.",
334
+ })),
335
+ }, { additionalProperties: false, minProperties: 3 });
336
+ export const ClawchatOrchCreateConnectCodeSchema = Type.Object({}, { additionalProperties: false });
337
+ export const ClawchatOrchGetConnectCodeSchema = Type.Object({ code: Type.String({ minLength: 1, description: "The connect code to look up." }) }, { additionalProperties: false });
package/dist/src/tools.js CHANGED
@@ -12,7 +12,7 @@ import { getOpenclawClawlingClient, } from "./runtime.js";
12
12
  import { markTerminalClawChatSend, getCurrentTerminalSendScope } from "./terminal-send.js";
13
13
  import { editClawChatMemoryBody, readClawChatMemoryFile, resolveClawChatMemoryPath, searchClawChatMemory, writeClawChatMemoryBody, } from "./clawchat-memory.js";
14
14
  import { pullGroupMetadata, pullOwnerMetadata, pullUserMetadata, pushMetadata, updateMetadata, } from "./clawchat-metadata.js";
15
- import { ClawchatGetAccountProfileSchema, ClawchatGetConversationSchema, ClawchatGetDirectConversationSchema, ClawchatGetMomentSchema, ClawchatGetUserProfileSchema, ClawchatLeaveGroupSchema, ClawchatAddGroupMemberSchema, ClawchatAcceptFriendRequestSchema, ClawchatMemoryEditSchema, ClawchatMemoryReadSchema, ClawchatMemorySearchSchema, ClawchatMemoryWriteSchema, ClawchatMetadataSyncSchema, ClawchatMetadataUpdateSchema, ClawchatCreateMomentCommentSchema, ClawchatCreateMomentSchema, ClawchatDeleteMomentCommentSchema, ClawchatDeleteMomentSchema, ClawchatListAccountFriendsSchema, ClawchatListFriendRequestsSchema, ClawchatListMomentsSchema, ClawchatMentionMessageSchema, ClawchatReactMessageSchema, ClawchatReplyMomentCommentSchema, ClawchatRejectFriendRequestSchema, ClawchatRemoveFriendSchema, ClawchatSearchUsersSchema, ClawchatSendFriendRequestSchema, ClawchatToggleMomentReactionSchema, ClawchatUpdateAccountProfileSchema, ClawchatUploadAvatarImageSchema, ClawchatRegisterAppSchema, ClawchatListAppsSchema, ClawchatUnregisterAppSchema, ClawchatLivewareLoginSchema, } from "./tools-schema.js";
15
+ import { ClawchatGetAccountProfileSchema, ClawchatGetConversationSchema, ClawchatGetDirectConversationSchema, ClawchatGetMomentSchema, ClawchatGetUserProfileSchema, ClawchatLeaveGroupSchema, ClawchatAddGroupMemberSchema, ClawchatAcceptFriendRequestSchema, ClawchatMemoryEditSchema, ClawchatMemoryReadSchema, ClawchatMemorySearchSchema, ClawchatMemoryWriteSchema, ClawchatMetadataSyncSchema, ClawchatMetadataUpdateSchema, ClawchatCreateMomentCommentSchema, ClawchatCreateMomentSchema, ClawchatDeleteMomentCommentSchema, ClawchatDeleteMomentSchema, ClawchatListAccountFriendsSchema, ClawchatListFriendRequestsSchema, ClawchatListMomentsSchema, ClawchatMentionMessageSchema, ClawchatReactMessageSchema, ClawchatReplyMomentCommentSchema, ClawchatRejectFriendRequestSchema, ClawchatRemoveFriendSchema, ClawchatSearchUsersSchema, ClawchatSendFriendRequestSchema, ClawchatToggleMomentReactionSchema, ClawchatUpdateAccountProfileSchema, ClawchatUploadAvatarImageSchema, ClawchatRegisterAppSchema, ClawchatListAppsSchema, ClawchatUnregisterAppSchema, ClawchatLivewareLoginSchema, ClawchatOrchListAgentsSchema, ClawchatOrchGetAgentSchema, ClawchatOrchSetAgentBehaviorSchema, ClawchatOrchListGroupsSchema, ClawchatOrchGetGroupSchema, ClawchatOrchSetGroupPromptSchema, ClawchatOrchCreateGroupSchema, ClawchatOrchGroupMemberSchema, ClawchatOrchSetGroupAgentSettingsSchema, ClawchatOrchCreateConnectCodeSchema, ClawchatOrchGetConnectCodeSchema, } from "./tools-schema.js";
16
16
  const MAX_UPLOAD_BYTES = 20 * 1024 * 1024;
17
17
  // Owner-approval gate business codes (must match the ClawChat backend's owner-approval codes).
18
18
  const CODE_PENDING_APPROVAL = 21001;
@@ -1381,5 +1381,212 @@ export function registerOpenclawClawlingTools(api, options = {}) {
1381
1381
  },
1382
1382
  };
1383
1383
  }, { name: "clawchat_liveware_login" });
1384
- 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)");
1384
+ // --- cloud orchestration (`agent.orchestrate`) ---------------------------
1385
+ // Twelve tools, 1:1 with docs/features/agentorch.md. Every response is HTTP
1386
+ // 200 with the business code in the envelope (a 21003 owner-approval
1387
+ // denial is the single most common outcome), so these use plain
1388
+ // `withClient` — never `withGatedClient` — and hand the caller the raw
1389
+ // envelope untouched. The shared orchestration skill text tells the model
1390
+ // to read `code` itself; rewriting the shape here would make the same
1391
+ // sentence behave differently than on the Hermes side.
1392
+ api.registerTool((ctx) => {
1393
+ const accountId = resolveOpenclawClawlingToolAccountId(ctx);
1394
+ return {
1395
+ name: "clawchat_orchestrate_list_agents",
1396
+ label: "List The Owner's Agents",
1397
+ description: toolDescription("List every agent the owner owns, including this one (flagged is_self). " +
1398
+ "TRIGGER — invoke first whenever the owner asks about, or asks to change, one of their OTHER agents. " +
1399
+ "This is the only way to learn a sibling's agentId; never guess one from a nickname."),
1400
+ parameters: ClawchatOrchListAgentsSchema,
1401
+ async execute(_callId, params) {
1402
+ return await recordClawchatToolCall(accountId, "clawchat_orchestrate_list_agents", params, async () => withClient(accountId, (c) => c.orchListAgents()));
1403
+ },
1404
+ };
1405
+ }, { name: "clawchat_orchestrate_list_agents" });
1406
+ api.registerTool((ctx) => {
1407
+ const accountId = resolveOpenclawClawlingToolAccountId(ctx);
1408
+ return {
1409
+ name: "clawchat_orchestrate_get_agent",
1410
+ label: "Get A Sibling Agent",
1411
+ description: toolDescription("Read one of the owner's agents by explicit agentId, including its read-only permission map. " +
1412
+ "TRIGGER — invoke before rewriting a sibling's system prompt (the PATCH replaces the whole field, so read it first), " +
1413
+ "or to explain why a sibling cannot do something instead of retrying on its behalf."),
1414
+ parameters: ClawchatOrchGetAgentSchema,
1415
+ async execute(_callId, params) {
1416
+ return await recordClawchatToolCall(accountId, "clawchat_orchestrate_get_agent", params, async () => {
1417
+ const p = params;
1418
+ return await withClient(accountId, (c) => c.orchGetAgent(p.agentId));
1419
+ });
1420
+ },
1421
+ };
1422
+ }, { name: "clawchat_orchestrate_get_agent" });
1423
+ api.registerTool((ctx) => {
1424
+ const accountId = resolveOpenclawClawlingToolAccountId(ctx);
1425
+ return {
1426
+ name: "clawchat_orchestrate_set_agent_behavior",
1427
+ label: "Rewrite A Sibling's System Prompt",
1428
+ description: toolDescription("Replace the WHOLE system prompt (behavior) of one of the owner's other agents. " +
1429
+ "TRIGGER — invoke when the owner asks to change how another agent behaves, talks, or what it is for. " +
1430
+ "This REPLACES the entire field — call clawchat_orchestrate_get_agent first, edit the text you got back, and send the full new value. " +
1431
+ "Sending a fragment deletes everything else and the owner cannot recover it. Max 3000 runes. " +
1432
+ "Nickname and bio cannot be changed here."),
1433
+ parameters: ClawchatOrchSetAgentBehaviorSchema,
1434
+ async execute(_callId, params) {
1435
+ return await recordClawchatToolCall(accountId, "clawchat_orchestrate_set_agent_behavior", params, async () => {
1436
+ const p = params;
1437
+ return await withClient(accountId, (c) => c.orchSetAgentBehavior(p.agentId, p.behavior));
1438
+ });
1439
+ },
1440
+ };
1441
+ }, { name: "clawchat_orchestrate_set_agent_behavior" });
1442
+ api.registerTool((ctx) => {
1443
+ const accountId = resolveOpenclawClawlingToolAccountId(ctx);
1444
+ return {
1445
+ name: "clawchat_orchestrate_list_groups",
1446
+ label: "List Manageable Groups",
1447
+ description: toolDescription("List the groups the owner can administer. " +
1448
+ "TRIGGER — invoke when the owner talks about a group of their agents without naming a conversation id."),
1449
+ parameters: ClawchatOrchListGroupsSchema,
1450
+ async execute(_callId, params) {
1451
+ return await recordClawchatToolCall(accountId, "clawchat_orchestrate_list_groups", params, async () => withClient(accountId, (c) => c.orchListGroups()));
1452
+ },
1453
+ };
1454
+ }, { name: "clawchat_orchestrate_list_groups" });
1455
+ api.registerTool((ctx) => {
1456
+ const accountId = resolveOpenclawClawlingToolAccountId(ctx);
1457
+ return {
1458
+ name: "clawchat_orchestrate_get_group",
1459
+ label: "Get A Managed Group",
1460
+ description: toolDescription("Read one group by explicit conversationId, including its system prompt and member agent ids. " +
1461
+ "TRIGGER — invoke before rewriting a group prompt, and after any write, to report what the server now says."),
1462
+ parameters: ClawchatOrchGetGroupSchema,
1463
+ async execute(_callId, params) {
1464
+ return await recordClawchatToolCall(accountId, "clawchat_orchestrate_get_group", params, async () => {
1465
+ const p = params;
1466
+ return await withClient(accountId, (c) => c.orchGetGroup(p.conversationId));
1467
+ });
1468
+ },
1469
+ };
1470
+ }, { name: "clawchat_orchestrate_get_group" });
1471
+ api.registerTool((ctx) => {
1472
+ const accountId = resolveOpenclawClawlingToolAccountId(ctx);
1473
+ return {
1474
+ name: "clawchat_orchestrate_set_group_prompt",
1475
+ label: "Rewrite A Group's System Prompt",
1476
+ description: toolDescription("Replace the WHOLE system prompt (description) of a group the owner administers. " +
1477
+ "TRIGGER — invoke when the owner asks to change how a group behaves as a whole. " +
1478
+ "This REPLACES the entire field — call clawchat_orchestrate_get_group first and send the full new value. " +
1479
+ "Sending a fragment deletes everything else and the owner cannot recover it. Max 3000 runes. " +
1480
+ "The group title cannot be changed here."),
1481
+ parameters: ClawchatOrchSetGroupPromptSchema,
1482
+ async execute(_callId, params) {
1483
+ return await recordClawchatToolCall(accountId, "clawchat_orchestrate_set_group_prompt", params, async () => {
1484
+ const p = params;
1485
+ return await withClient(accountId, (c) => c.orchSetGroupPrompt(p.conversationId, p.description));
1486
+ });
1487
+ },
1488
+ };
1489
+ }, { name: "clawchat_orchestrate_set_group_prompt" });
1490
+ api.registerTool((ctx) => {
1491
+ const accountId = resolveOpenclawClawlingToolAccountId(ctx);
1492
+ return {
1493
+ name: "clawchat_orchestrate_create_group",
1494
+ label: "Create A Group Of The Owner's Agents",
1495
+ description: toolDescription("Create a new group containing the owner's own agents. " +
1496
+ "TRIGGER — invoke when the owner asks to put several of their agents in a group together."),
1497
+ parameters: ClawchatOrchCreateGroupSchema,
1498
+ async execute(_callId, params) {
1499
+ return await recordClawchatToolCall(accountId, "clawchat_orchestrate_create_group", params, async () => {
1500
+ const p = params;
1501
+ return await withClient(accountId, (c) => c.orchCreateGroup(p.title, p.agentIds));
1502
+ });
1503
+ },
1504
+ };
1505
+ }, { name: "clawchat_orchestrate_create_group" });
1506
+ api.registerTool((ctx) => {
1507
+ const accountId = resolveOpenclawClawlingToolAccountId(ctx);
1508
+ return {
1509
+ name: "clawchat_orchestrate_add_group_member",
1510
+ label: "Add An Agent To A Group",
1511
+ description: toolDescription("Add one of the owner's agents to a group the owner administers. " +
1512
+ "TRIGGER — invoke when the owner asks to bring another of their agents into an existing group. " +
1513
+ "You cannot add yourself; that is rejected outright."),
1514
+ parameters: ClawchatOrchGroupMemberSchema,
1515
+ async execute(_callId, params) {
1516
+ return await recordClawchatToolCall(accountId, "clawchat_orchestrate_add_group_member", params, async () => {
1517
+ const p = params;
1518
+ return await withClient(accountId, (c) => c.orchAddGroupMember(p.conversationId, p.agentId));
1519
+ });
1520
+ },
1521
+ };
1522
+ }, { name: "clawchat_orchestrate_add_group_member" });
1523
+ api.registerTool((ctx) => {
1524
+ const accountId = resolveOpenclawClawlingToolAccountId(ctx);
1525
+ return {
1526
+ name: "clawchat_orchestrate_remove_group_member",
1527
+ label: "Remove An Agent From A Group",
1528
+ description: toolDescription("Remove one of the owner's agents from a group the owner administers. " +
1529
+ "TRIGGER — invoke when the owner asks to take an agent out of a group."),
1530
+ parameters: ClawchatOrchGroupMemberSchema,
1531
+ async execute(_callId, params) {
1532
+ return await recordClawchatToolCall(accountId, "clawchat_orchestrate_remove_group_member", params, async () => {
1533
+ const p = params;
1534
+ return await withClient(accountId, (c) => c.orchRemoveGroupMember(p.conversationId, p.agentId));
1535
+ });
1536
+ },
1537
+ };
1538
+ }, { name: "clawchat_orchestrate_remove_group_member" });
1539
+ api.registerTool((ctx) => {
1540
+ const accountId = resolveOpenclawClawlingToolAccountId(ctx);
1541
+ return {
1542
+ name: "clawchat_orchestrate_set_group_agent_settings",
1543
+ label: "Set An Agent's Speaking Settings In A Group",
1544
+ description: toolDescription("Set how one agent speaks inside one group: muted, reply mode, and batching delay. " +
1545
+ "TRIGGER — invoke FIRST when a group is flooding, echoing, or an agent will not stop. " +
1546
+ "These are the hard controls; a rule written into a prompt stops working after compaction or a restart, so pair every soft rule with one of these. " +
1547
+ "Omitted fields are left unchanged."),
1548
+ parameters: ClawchatOrchSetGroupAgentSettingsSchema,
1549
+ async execute(_callId, params) {
1550
+ return await recordClawchatToolCall(accountId, "clawchat_orchestrate_set_group_agent_settings", params, async () => {
1551
+ const p = params;
1552
+ return await withClient(accountId, (c) => c.orchSetGroupAgentSettings(p.conversationId, p.agentId, {
1553
+ muted: p.muted,
1554
+ replyMode: p.replyMode,
1555
+ batchDelaySeconds: p.batchDelaySeconds,
1556
+ }));
1557
+ });
1558
+ },
1559
+ };
1560
+ }, { name: "clawchat_orchestrate_set_group_agent_settings" });
1561
+ api.registerTool((ctx) => {
1562
+ const accountId = resolveOpenclawClawlingToolAccountId(ctx);
1563
+ return {
1564
+ name: "clawchat_orchestrate_create_connect_code",
1565
+ label: "Issue A Connect Code",
1566
+ description: toolDescription("Mint a connect code on the owner's behalf, valid 30 minutes. " +
1567
+ "TRIGGER — invoke when the owner asks for a code to connect a new agent. " +
1568
+ "The rate limit bucket belongs to the owner and is shared with their own manual issuance, so do not retry on a limit."),
1569
+ parameters: ClawchatOrchCreateConnectCodeSchema,
1570
+ async execute(_callId, params) {
1571
+ return await recordClawchatToolCall(accountId, "clawchat_orchestrate_create_connect_code", params, async () => withClient(accountId, (c) => c.orchCreateConnectCode()));
1572
+ },
1573
+ };
1574
+ }, { name: "clawchat_orchestrate_create_connect_code" });
1575
+ api.registerTool((ctx) => {
1576
+ const accountId = resolveOpenclawClawlingToolAccountId(ctx);
1577
+ return {
1578
+ name: "clawchat_orchestrate_get_connect_code",
1579
+ label: "Read A Connect Code's Status",
1580
+ description: toolDescription("Read the status of a connect code the owner minted. " +
1581
+ "TRIGGER — invoke when the owner asks whether a code was used or has expired."),
1582
+ parameters: ClawchatOrchGetConnectCodeSchema,
1583
+ async execute(_callId, params) {
1584
+ return await recordClawchatToolCall(accountId, "clawchat_orchestrate_get_connect_code", params, async () => {
1585
+ const p = params;
1586
+ return await withClient(accountId, (c) => c.orchGetConnectCode(p.code));
1587
+ });
1588
+ },
1589
+ };
1590
+ }, { name: "clawchat_orchestrate_get_connect_code" });
1591
+ api.logger.debug?.("clawchat-plugin-openclaw: registered 43 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, orchestrate_list_agents, orchestrate_get_agent, orchestrate_set_agent_behavior, orchestrate_list_groups, orchestrate_get_group, orchestrate_set_group_prompt, orchestrate_create_group, orchestrate_add_group_member, orchestrate_remove_group_member, orchestrate_set_group_agent_settings, orchestrate_create_connect_code, orchestrate_get_connect_code)");
1385
1592
  }
@@ -68,7 +68,19 @@
68
68
  "clawchat_register_app",
69
69
  "clawchat_list_apps",
70
70
  "clawchat_unregister_app",
71
- "clawchat_liveware_login"
71
+ "clawchat_liveware_login",
72
+ "clawchat_orchestrate_list_agents",
73
+ "clawchat_orchestrate_get_agent",
74
+ "clawchat_orchestrate_set_agent_behavior",
75
+ "clawchat_orchestrate_list_groups",
76
+ "clawchat_orchestrate_get_group",
77
+ "clawchat_orchestrate_set_group_prompt",
78
+ "clawchat_orchestrate_create_group",
79
+ "clawchat_orchestrate_add_group_member",
80
+ "clawchat_orchestrate_remove_group_member",
81
+ "clawchat_orchestrate_set_group_agent_settings",
82
+ "clawchat_orchestrate_create_connect_code",
83
+ "clawchat_orchestrate_get_connect_code"
72
84
  ]
73
85
  },
74
86
  "configSchema": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clawling/clawchat-plugin-openclaw",
3
- "version": "2026.9.22-2",
3
+ "version": "2026.9.23-1",
4
4
  "description": "OpenClaw ClawChat channel plugin",
5
5
  "license": "MIT",
6
6
  "author": "CLAWLING PTE. LTD.",
@@ -25,11 +25,13 @@
25
25
  "!dist/**/*.test.js",
26
26
  "!dist/**/*.test-d.js",
27
27
  "!dist/**/*.typecheck.js",
28
+ "!dist/**/tools-test-support.js",
28
29
  "index.ts",
29
30
  "setup-entry.ts",
30
31
  "src/**/*.ts",
31
32
  "!src/**/*.test.ts",
32
33
  "!src/**/*.typecheck.ts",
34
+ "!src/tools-test-support.ts",
33
35
  "prompts",
34
36
  "skills",
35
37
  "openclaw.plugin.json",
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: clawchat-core
3
- version: 1.7.0
3
+ version: 1.8.0
4
4
  description: Use when a request involves ClawChat profile, friends, user search, moments/dynamics, comments, reactions, avatar, media, memory, output visibility, read-only conversation lookup, sending an image, file, or voice/audio clip into a conversation, managing the owner's other agents or groups, or plugin install/update/activation.
5
5
  ---
6
6
 
@@ -8,17 +8,12 @@ description: Use when a request involves ClawChat profile, friends, user search,
8
8
 
9
9
  ## Overview
10
10
 
11
- This skill guides agent behavior for ClawChat-aware tasks. Use the registered ClawChat tools for profile, friends, user search, moments, comments, reactions, avatar, media, and read-only conversation lookup instead of direct HTTP calls, shell scripts, or handwritten clients. Cloud orchestration — managing the owner's *other* agents and groups — is the one ClawChat capability with no registered tool, and is called over REST; see "Managing The Owner's Other Agents" below.
11
+ This skill guides agent behavior for ClawChat-aware tasks. Use the registered ClawChat tools for profile, friends, user search, moments, comments, reactions, avatar, media, and read-only conversation lookup instead of direct HTTP calls, shell scripts, or handwritten clients. Cloud orchestration — managing the owner's *other* agents and groups — is reached through its own `clawchat_orchestrate_*` tools; see "Managing The Owner's Other Agents" below.
12
12
 
13
13
  ## Scope
14
14
 
15
15
  - Use registered ClawChat plugin tools for account/profile, friends, users, moments, comments, reactions, avatar, media, and read-only conversation lookup.
16
- - If a requested ClawChat tool is unavailable or returns a config error, report that result and stop instead of bypassing the plugin. A missing tool is never a licence to hand-roll an HTTP call: the sole ClawChat capability reached over REST is cloud orchestration, named below, and that is because it has no tool at all — not because a tool failed.
17
-
18
- Raw HTTP is permitted **only** to the twelve `/v1/agents/me/orchestration/*` paths listed in
19
- `clawchat-orchestration`. Every other ClawChat path, including the ordinary `/v1/conversations/*` and
20
- `/v1/agents/*` routes, is still off-limits — if the orchestration surface has no route for what the owner
21
- wants, say so and stop.
16
+ - If a requested ClawChat tool is unavailable or returns a config error, report that result and stop instead of bypassing the plugin. A missing tool is never a licence to hand-roll an HTTP call.
22
17
  - Use the `/clawchat-output` slash command when the user asks to change how much ClawChat runtime output is shown in the current conversation.
23
18
 
24
19
  ## Sending an Image, File, or Voice Message
@@ -137,8 +132,8 @@ Tool descriptions are authoritative. These routing hints resolve common ambiguit
137
132
  When the owner asks you to manage their **other** agents or their **groups** —
138
133
  rewrite another agent's prompt, quiet an agent that is flooding a group, build a
139
134
  group out of their agents, issue a connect code — that is **cloud
140
- orchestration**, and it is the one ClawChat capability with no registered tool.
141
- Read the `clawchat-orchestration` skill: it carries the routes, the limits, and
135
+ orchestration**, reached through the `clawchat_orchestrate_*` tools.
136
+ Read the `clawchat-orchestration` skill: it carries the tools, the limits, and
142
137
  how to decide what to change.
143
138
 
144
139
  Two things worth knowing before you open it: