@clawling/clawchat-plugin-openclaw 2026.6.30-1 → 2026.6.30-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.
@@ -279,12 +279,17 @@ function withClawChatSessionScope(cfg) {
279
279
  };
280
280
  }
281
281
  function buildActivationBootstrapText() {
282
+ // Worded to make the greeting the single required action. Earlier wording
283
+ // ("ClawChat activation bootstrap: ... do both ...") led some agents to treat
284
+ // it as a setup task and write a `BOOTSTRAP.md` file instead of replying, so
285
+ // no greeting reached the user. Lead with the chat reply, forbid file/tool
286
+ // detours, and keep the profile update strictly optional and secondary.
282
287
  return [
283
- "ClawChat activation bootstrap: You are now connected to this ClawChat direct conversation.",
284
- "Please do both:",
285
- "1. Send a brief, friendly greeting to the user in this ClawChat direct conversation.",
286
- "2. If you have local profile information for yourself, such as display name, bio, or avatar, update the connected ClawChat account profile using the available ClawChat tools. Use `clawchat_update_account_profile` for display name/bio/avatar URL, and use `clawchat_upload_avatar_image` first if the avatar is only available as a local image path. If you do not have local profile information, skip profile updates and only greet the user.",
287
- "Do not ask the user for profile information just for this bootstrap.",
288
+ "You are now connected to a ClawChat direct conversation with your user.",
289
+ "Reply now with one short, friendly greeting message in this conversation: introduce yourself and say you are connected and ready.",
290
+ "Send it as a normal chat reply. Do not write or create any files or notes, and do not call tools just to greet.",
291
+ "Only if you already have your own profile details (display name, bio, or avatar) may you also call `clawchat_update_account_profile` (use `clawchat_upload_avatar_image` first for a local avatar image); otherwise skip that and just greet.",
292
+ "Do not ask the user for profile information.",
288
293
  ].join("\n");
289
294
  }
290
295
  function buildActivationBootstrapEnvelope(params) {
@@ -1440,6 +1445,11 @@ export async function startOpenclawClawlingGateway(params) {
1440
1445
  }));
1441
1446
  };
1442
1447
  let dispatchActivationBootstrap = async () => { };
1448
+ // Set by dispatchTurnToAgent when it dispatches the synthetic
1449
+ // `clawchat-bootstrap` turn: true only if the agent actually produced a final
1450
+ // outbound message (the greeting). The bootstrap dispatcher reads it to decide
1451
+ // whether to mark the bootstrap sent or leave it pending for a later retry.
1452
+ let bootstrapProducedFinal = false;
1443
1453
  client.on("state", ({ from, to }) => {
1444
1454
  log?.info?.(`[${accountId}] clawchat-plugin-openclaw state ${from} -> ${to}`);
1445
1455
  wsReady = to === "connected";
@@ -2131,6 +2141,12 @@ export async function startOpenclawClawlingGateway(params) {
2131
2141
  });
2132
2142
  const counts = dispatchResult?.counts ?? {};
2133
2143
  const queuedFinal = Boolean(dispatchResult?.queuedFinal);
2144
+ if (turn.senderId === "clawchat-bootstrap") {
2145
+ // Did the agent actually emit a greeting? A final outbound message
2146
+ // means yes; block/tool-only (or nothing) means the greeting never
2147
+ // reached the user, so the bootstrap dispatcher will keep it pending.
2148
+ bootstrapProducedFinal = queuedFinal || (counts.final ?? 0) > 0;
2149
+ }
2134
2150
  log?.info?.(`[${accountId}] clawchat-plugin-openclaw dispatch complete msg=${turn.messageId} queuedFinal=${queuedFinal} counts=${JSON.stringify(counts)}`);
2135
2151
  if (!queuedFinal && Object.values(counts).every((n) => !n)) {
2136
2152
  log?.info?.(`[${accountId}] clawchat-plugin-openclaw NO reply was produced (no final / block / tool dispatched). ` +
@@ -2388,11 +2404,22 @@ export async function startOpenclawClawlingGateway(params) {
2388
2404
  if (!bootstrap)
2389
2405
  return;
2390
2406
  const claimedBootstrap = bootstrap;
2407
+ bootstrapProducedFinal = false;
2391
2408
  const result = await handleInboundEnvelope(buildActivationBootstrapEnvelope({ account, conversationId: claimedBootstrap.conversationId }));
2392
2409
  if (result !== "submitted") {
2393
2410
  releaseBootstrap();
2394
2411
  return;
2395
2412
  }
2413
+ // Only mark the bootstrap done once the agent has actually sent a greeting
2414
+ // (a final outbound message). If the turn was handed off but produced no
2415
+ // final reply — e.g. the agent went off to write a file/note instead of
2416
+ // replying — leave the bootstrap pending (release the claim) so the next
2417
+ // ready re-dispatches it, rather than silently consuming it forever.
2418
+ if (!bootstrapProducedFinal) {
2419
+ log?.info?.(`[${accountId}] clawchat-plugin-openclaw activation bootstrap dispatched but produced no final reply; leaving pending for retry`);
2420
+ releaseBootstrap();
2421
+ return;
2422
+ }
2396
2423
  recordConnection("activation bootstrap sent", () => store.markActivationBootstrapSent?.({
2397
2424
  platform: "openclaw",
2398
2425
  accountId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clawling/clawchat-plugin-openclaw",
3
- "version": "2026.6.30-1",
3
+ "version": "2026.6.30-2",
4
4
  "description": "OpenClaw ClawChat channel plugin",
5
5
  "license": "MIT",
6
6
  "author": "CLAWLING PTE. LTD.",
package/src/runtime.ts CHANGED
@@ -427,12 +427,17 @@ function withClawChatSessionScope(cfg: OpenClawConfig): OpenClawConfig {
427
427
  }
428
428
 
429
429
  function buildActivationBootstrapText(): string {
430
+ // Worded to make the greeting the single required action. Earlier wording
431
+ // ("ClawChat activation bootstrap: ... do both ...") led some agents to treat
432
+ // it as a setup task and write a `BOOTSTRAP.md` file instead of replying, so
433
+ // no greeting reached the user. Lead with the chat reply, forbid file/tool
434
+ // detours, and keep the profile update strictly optional and secondary.
430
435
  return [
431
- "ClawChat activation bootstrap: You are now connected to this ClawChat direct conversation.",
432
- "Please do both:",
433
- "1. Send a brief, friendly greeting to the user in this ClawChat direct conversation.",
434
- "2. If you have local profile information for yourself, such as display name, bio, or avatar, update the connected ClawChat account profile using the available ClawChat tools. Use `clawchat_update_account_profile` for display name/bio/avatar URL, and use `clawchat_upload_avatar_image` first if the avatar is only available as a local image path. If you do not have local profile information, skip profile updates and only greet the user.",
435
- "Do not ask the user for profile information just for this bootstrap.",
436
+ "You are now connected to a ClawChat direct conversation with your user.",
437
+ "Reply now with one short, friendly greeting message in this conversation: introduce yourself and say you are connected and ready.",
438
+ "Send it as a normal chat reply. Do not write or create any files or notes, and do not call tools just to greet.",
439
+ "Only if you already have your own profile details (display name, bio, or avatar) may you also call `clawchat_update_account_profile` (use `clawchat_upload_avatar_image` first for a local avatar image); otherwise skip that and just greet.",
440
+ "Do not ask the user for profile information.",
436
441
  ].join("\n");
437
442
  }
438
443
 
@@ -1816,6 +1821,11 @@ export async function startOpenclawClawlingGateway(params: StartGatewayParams):
1816
1821
  );
1817
1822
  };
1818
1823
  let dispatchActivationBootstrap: () => Promise<void> = async () => {};
1824
+ // Set by dispatchTurnToAgent when it dispatches the synthetic
1825
+ // `clawchat-bootstrap` turn: true only if the agent actually produced a final
1826
+ // outbound message (the greeting). The bootstrap dispatcher reads it to decide
1827
+ // whether to mark the bootstrap sent or leave it pending for a later retry.
1828
+ let bootstrapProducedFinal = false;
1819
1829
 
1820
1830
  client.on("state", ({ from, to }) => {
1821
1831
  log?.info?.(`[${accountId}] clawchat-plugin-openclaw state ${from} -> ${to}`);
@@ -2579,6 +2589,12 @@ export async function startOpenclawClawlingGateway(params: StartGatewayParams):
2579
2589
  const queuedFinal = Boolean(
2580
2590
  (dispatchResult as { queuedFinal?: boolean } | undefined)?.queuedFinal,
2581
2591
  );
2592
+ if (turn.senderId === "clawchat-bootstrap") {
2593
+ // Did the agent actually emit a greeting? A final outbound message
2594
+ // means yes; block/tool-only (or nothing) means the greeting never
2595
+ // reached the user, so the bootstrap dispatcher will keep it pending.
2596
+ bootstrapProducedFinal = queuedFinal || (counts.final ?? 0) > 0;
2597
+ }
2582
2598
  log?.info?.(
2583
2599
  `[${accountId}] clawchat-plugin-openclaw dispatch complete msg=${turn.messageId} queuedFinal=${queuedFinal} counts=${JSON.stringify(counts)}`,
2584
2600
  );
@@ -2877,6 +2893,7 @@ export async function startOpenclawClawlingGateway(params: StartGatewayParams):
2877
2893
  );
2878
2894
  if (!bootstrap) return;
2879
2895
  const claimedBootstrap = bootstrap;
2896
+ bootstrapProducedFinal = false;
2880
2897
  const result = await handleInboundEnvelope(
2881
2898
  buildActivationBootstrapEnvelope({ account, conversationId: claimedBootstrap.conversationId }),
2882
2899
  );
@@ -2884,6 +2901,18 @@ export async function startOpenclawClawlingGateway(params: StartGatewayParams):
2884
2901
  releaseBootstrap();
2885
2902
  return;
2886
2903
  }
2904
+ // Only mark the bootstrap done once the agent has actually sent a greeting
2905
+ // (a final outbound message). If the turn was handed off but produced no
2906
+ // final reply — e.g. the agent went off to write a file/note instead of
2907
+ // replying — leave the bootstrap pending (release the claim) so the next
2908
+ // ready re-dispatches it, rather than silently consuming it forever.
2909
+ if (!bootstrapProducedFinal) {
2910
+ log?.info?.(
2911
+ `[${accountId}] clawchat-plugin-openclaw activation bootstrap dispatched but produced no final reply; leaving pending for retry`,
2912
+ );
2913
+ releaseBootstrap();
2914
+ return;
2915
+ }
2887
2916
  recordConnection("activation bootstrap sent", () =>
2888
2917
  store.markActivationBootstrapSent?.({
2889
2918
  platform: "openclaw",