@clawling/clawchat-plugin-openclaw 2026.6.30-3 → 2026.6.30-4
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/reply-dispatcher.js +9 -0
- package/dist/src/runtime.js +21 -16
- package/package.json +1 -1
- package/src/reply-dispatcher.ts +10 -0
- package/src/runtime.ts +21 -16
|
@@ -296,6 +296,12 @@ export function createOpenclawClawlingReplyDispatcher(options) {
|
|
|
296
296
|
let typingActive = false;
|
|
297
297
|
let terminalReplySuppressed = false;
|
|
298
298
|
let finalDeliverySeen = false;
|
|
299
|
+
// Count of outbound messages that were actually delivered to the target
|
|
300
|
+
// (a real, successful send — not a suppressed/duplicate/failed one). This is
|
|
301
|
+
// the reliable "did anything actually reach the user?" signal; host-side
|
|
302
|
+
// `counts.block`/`counts.final` only reflect what the agent emitted, not
|
|
303
|
+
// whether the send succeeded (a cancelled send still counts as a block).
|
|
304
|
+
let outboundDeliveredCount = 0;
|
|
299
305
|
const outboundEventType = () => (replyCtx ? "message.reply" : "message.send");
|
|
300
306
|
const outboundRaw = () => ({ target, replyCtx: replyCtx ?? null });
|
|
301
307
|
const terminalSendScopeId = options.terminalSendScopeId ?? null;
|
|
@@ -502,6 +508,8 @@ export function createOpenclawClawlingReplyDispatcher(options) {
|
|
|
502
508
|
log,
|
|
503
509
|
});
|
|
504
510
|
recordOutboundAck("message", messageId, result);
|
|
511
|
+
if (result)
|
|
512
|
+
outboundDeliveredCount += 1;
|
|
505
513
|
log?.info?.(`[${account.accountId}] clawchat-plugin-openclaw send complete to=${target.chatId}`);
|
|
506
514
|
openclawLlmContextDebug.writeSnapshot({
|
|
507
515
|
visibility: "host_event",
|
|
@@ -676,6 +684,7 @@ export function createOpenclawClawlingReplyDispatcher(options) {
|
|
|
676
684
|
});
|
|
677
685
|
return {
|
|
678
686
|
dispatcher: base.dispatcher,
|
|
687
|
+
getOutboundDeliveredCount: () => outboundDeliveredCount,
|
|
679
688
|
replyOptions: {
|
|
680
689
|
...base.replyOptions,
|
|
681
690
|
sourceReplyDeliveryMode: "automatic",
|
package/dist/src/runtime.js
CHANGED
|
@@ -1474,10 +1474,11 @@ export async function startOpenclawClawlingGateway(params) {
|
|
|
1474
1474
|
};
|
|
1475
1475
|
let dispatchActivationBootstrap = async () => { };
|
|
1476
1476
|
// Set by dispatchTurnToAgent when it dispatches the synthetic
|
|
1477
|
-
// `clawchat-bootstrap` turn: true only if the
|
|
1478
|
-
// outbound
|
|
1479
|
-
// whether to mark the bootstrap sent or leave it pending for
|
|
1480
|
-
|
|
1477
|
+
// `clawchat-bootstrap` turn: true only if the greeting was actually delivered
|
|
1478
|
+
// (a real successful outbound send — block or final). The bootstrap dispatcher
|
|
1479
|
+
// reads it to decide whether to mark the bootstrap sent or leave it pending for
|
|
1480
|
+
// a later retry.
|
|
1481
|
+
let bootstrapGreetingDelivered = false;
|
|
1481
1482
|
// Per-connection retry for a pending activation bootstrap (see the module-level
|
|
1482
1483
|
// constants). Re-attempts on the live connection while `wsReady`, bounded by
|
|
1483
1484
|
// ACTIVATION_BOOTSTRAP_MAX_RETRIES; the budget resets on each fresh connect.
|
|
@@ -2166,7 +2167,7 @@ export async function startOpenclawClawlingGateway(params) {
|
|
|
2166
2167
|
chatType: turn.peer.kind,
|
|
2167
2168
|
agentId: route.agentId,
|
|
2168
2169
|
});
|
|
2169
|
-
const { dispatcher, replyOptions, markDispatchIdle } = createOpenclawClawlingReplyDispatcher({
|
|
2170
|
+
const { dispatcher, replyOptions, markDispatchIdle, getOutboundDeliveredCount } = createOpenclawClawlingReplyDispatcher({
|
|
2170
2171
|
cfg,
|
|
2171
2172
|
runtime,
|
|
2172
2173
|
account,
|
|
@@ -2202,10 +2203,14 @@ export async function startOpenclawClawlingGateway(params) {
|
|
|
2202
2203
|
const counts = dispatchResult?.counts ?? {};
|
|
2203
2204
|
const queuedFinal = Boolean(dispatchResult?.queuedFinal);
|
|
2204
2205
|
if (turn.senderId === "clawchat-bootstrap") {
|
|
2205
|
-
// Did the
|
|
2206
|
-
//
|
|
2207
|
-
//
|
|
2208
|
-
|
|
2206
|
+
// Did the greeting actually reach the user? The reliable signal is a
|
|
2207
|
+
// real, successful outbound send (the agent may deliver the greeting
|
|
2208
|
+
// as a `block`, not a `final`, and a cancelled send still shows up in
|
|
2209
|
+
// host `counts.block`). Treat a queued/counted final as delivered too,
|
|
2210
|
+
// to cover deferred/human-delay finals not yet flushed at this point.
|
|
2211
|
+
const deliveredOutbound = getOutboundDeliveredCount?.() ?? 0;
|
|
2212
|
+
bootstrapGreetingDelivered =
|
|
2213
|
+
deliveredOutbound > 0 || queuedFinal || (counts.final ?? 0) > 0;
|
|
2209
2214
|
}
|
|
2210
2215
|
log?.info?.(`[${accountId}] clawchat-plugin-openclaw dispatch complete msg=${turn.messageId} queuedFinal=${queuedFinal} counts=${JSON.stringify(counts)}`);
|
|
2211
2216
|
if (!queuedFinal && Object.values(counts).every((n) => !n)) {
|
|
@@ -2473,21 +2478,21 @@ export async function startOpenclawClawlingGateway(params) {
|
|
|
2473
2478
|
const claimedBootstrap = bootstrap;
|
|
2474
2479
|
claimedInFlight = true;
|
|
2475
2480
|
incrementActivationBootstrapInFlight(accountId);
|
|
2476
|
-
|
|
2481
|
+
bootstrapGreetingDelivered = false;
|
|
2477
2482
|
const result = await handleInboundEnvelope(buildActivationBootstrapEnvelope({ account, conversationId: claimedBootstrap.conversationId }));
|
|
2478
2483
|
if (result !== "submitted") {
|
|
2479
2484
|
releaseBootstrap();
|
|
2480
2485
|
armBootstrapRetry();
|
|
2481
2486
|
return;
|
|
2482
2487
|
}
|
|
2483
|
-
// Only mark the bootstrap done once the
|
|
2484
|
-
// (a
|
|
2485
|
-
//
|
|
2486
|
-
// agent went off to write a file/note instead of replying — leave the
|
|
2488
|
+
// Only mark the bootstrap done once the greeting was actually delivered
|
|
2489
|
+
// (a real successful outbound send). If the turn was handed off but nothing
|
|
2490
|
+
// reached the user — e.g. the greeting send was cancelled by a reconnect, or
|
|
2491
|
+
// the agent went off to write a file/note instead of replying — leave the
|
|
2487
2492
|
// bootstrap pending (release the claim) and retry on the live connection,
|
|
2488
2493
|
// rather than silently consuming it forever or waiting for the next reconnect.
|
|
2489
|
-
if (!
|
|
2490
|
-
log?.info?.(`[${accountId}] clawchat-plugin-openclaw activation bootstrap dispatched but
|
|
2494
|
+
if (!bootstrapGreetingDelivered) {
|
|
2495
|
+
log?.info?.(`[${accountId}] clawchat-plugin-openclaw activation bootstrap dispatched but greeting was not delivered; leaving pending for retry`);
|
|
2491
2496
|
releaseBootstrap();
|
|
2492
2497
|
armBootstrapRetry();
|
|
2493
2498
|
return;
|
package/package.json
CHANGED
package/src/reply-dispatcher.ts
CHANGED
|
@@ -375,6 +375,8 @@ export function createOpenclawClawlingReplyDispatcher(options: ReplyDispatcherOp
|
|
|
375
375
|
dispatcher: TypedReplyDispatcherResult["dispatcher"];
|
|
376
376
|
replyOptions: ClawChatReplyOptions;
|
|
377
377
|
markDispatchIdle: TypedReplyDispatcherResult["markDispatchIdle"];
|
|
378
|
+
/** Number of outbound messages actually delivered to the target so far. */
|
|
379
|
+
getOutboundDeliveredCount: () => number;
|
|
378
380
|
} {
|
|
379
381
|
const {
|
|
380
382
|
cfg,
|
|
@@ -430,6 +432,12 @@ export function createOpenclawClawlingReplyDispatcher(options: ReplyDispatcherOp
|
|
|
430
432
|
let typingActive = false;
|
|
431
433
|
let terminalReplySuppressed = false;
|
|
432
434
|
let finalDeliverySeen = false;
|
|
435
|
+
// Count of outbound messages that were actually delivered to the target
|
|
436
|
+
// (a real, successful send — not a suppressed/duplicate/failed one). This is
|
|
437
|
+
// the reliable "did anything actually reach the user?" signal; host-side
|
|
438
|
+
// `counts.block`/`counts.final` only reflect what the agent emitted, not
|
|
439
|
+
// whether the send succeeded (a cancelled send still counts as a block).
|
|
440
|
+
let outboundDeliveredCount = 0;
|
|
433
441
|
|
|
434
442
|
const outboundEventType = () => (replyCtx ? "message.reply" : "message.send");
|
|
435
443
|
const outboundRaw = () => ({ target, replyCtx: replyCtx ?? null });
|
|
@@ -646,6 +654,7 @@ export function createOpenclawClawlingReplyDispatcher(options: ReplyDispatcherOp
|
|
|
646
654
|
log,
|
|
647
655
|
});
|
|
648
656
|
recordOutboundAck("message", messageId, result);
|
|
657
|
+
if (result) outboundDeliveredCount += 1;
|
|
649
658
|
log?.info?.(
|
|
650
659
|
`[${account.accountId}] clawchat-plugin-openclaw send complete to=${target.chatId}`,
|
|
651
660
|
);
|
|
@@ -835,6 +844,7 @@ export function createOpenclawClawlingReplyDispatcher(options: ReplyDispatcherOp
|
|
|
835
844
|
|
|
836
845
|
return {
|
|
837
846
|
dispatcher: base.dispatcher,
|
|
847
|
+
getOutboundDeliveredCount: () => outboundDeliveredCount,
|
|
838
848
|
replyOptions: {
|
|
839
849
|
...base.replyOptions,
|
|
840
850
|
sourceReplyDeliveryMode: "automatic",
|
package/src/runtime.ts
CHANGED
|
@@ -1850,10 +1850,11 @@ export async function startOpenclawClawlingGateway(params: StartGatewayParams):
|
|
|
1850
1850
|
};
|
|
1851
1851
|
let dispatchActivationBootstrap: () => Promise<void> = async () => {};
|
|
1852
1852
|
// Set by dispatchTurnToAgent when it dispatches the synthetic
|
|
1853
|
-
// `clawchat-bootstrap` turn: true only if the
|
|
1854
|
-
// outbound
|
|
1855
|
-
// whether to mark the bootstrap sent or leave it pending for
|
|
1856
|
-
|
|
1853
|
+
// `clawchat-bootstrap` turn: true only if the greeting was actually delivered
|
|
1854
|
+
// (a real successful outbound send — block or final). The bootstrap dispatcher
|
|
1855
|
+
// reads it to decide whether to mark the bootstrap sent or leave it pending for
|
|
1856
|
+
// a later retry.
|
|
1857
|
+
let bootstrapGreetingDelivered = false;
|
|
1857
1858
|
// Per-connection retry for a pending activation bootstrap (see the module-level
|
|
1858
1859
|
// constants). Re-attempts on the live connection while `wsReady`, bounded by
|
|
1859
1860
|
// ACTIVATION_BOOTSTRAP_MAX_RETRIES; the budget resets on each fresh connect.
|
|
@@ -2603,7 +2604,7 @@ export async function startOpenclawClawlingGateway(params: StartGatewayParams):
|
|
|
2603
2604
|
chatType: turn.peer.kind,
|
|
2604
2605
|
agentId: route.agentId,
|
|
2605
2606
|
});
|
|
2606
|
-
const { dispatcher, replyOptions, markDispatchIdle } =
|
|
2607
|
+
const { dispatcher, replyOptions, markDispatchIdle, getOutboundDeliveredCount } =
|
|
2607
2608
|
createOpenclawClawlingReplyDispatcher({
|
|
2608
2609
|
cfg,
|
|
2609
2610
|
runtime,
|
|
@@ -2649,10 +2650,14 @@ export async function startOpenclawClawlingGateway(params: StartGatewayParams):
|
|
|
2649
2650
|
(dispatchResult as { queuedFinal?: boolean } | undefined)?.queuedFinal,
|
|
2650
2651
|
);
|
|
2651
2652
|
if (turn.senderId === "clawchat-bootstrap") {
|
|
2652
|
-
// Did the
|
|
2653
|
-
//
|
|
2654
|
-
//
|
|
2655
|
-
|
|
2653
|
+
// Did the greeting actually reach the user? The reliable signal is a
|
|
2654
|
+
// real, successful outbound send (the agent may deliver the greeting
|
|
2655
|
+
// as a `block`, not a `final`, and a cancelled send still shows up in
|
|
2656
|
+
// host `counts.block`). Treat a queued/counted final as delivered too,
|
|
2657
|
+
// to cover deferred/human-delay finals not yet flushed at this point.
|
|
2658
|
+
const deliveredOutbound = getOutboundDeliveredCount?.() ?? 0;
|
|
2659
|
+
bootstrapGreetingDelivered =
|
|
2660
|
+
deliveredOutbound > 0 || queuedFinal || (counts.final ?? 0) > 0;
|
|
2656
2661
|
}
|
|
2657
2662
|
log?.info?.(
|
|
2658
2663
|
`[${accountId}] clawchat-plugin-openclaw dispatch complete msg=${turn.messageId} queuedFinal=${queuedFinal} counts=${JSON.stringify(counts)}`,
|
|
@@ -2961,7 +2966,7 @@ export async function startOpenclawClawlingGateway(params: StartGatewayParams):
|
|
|
2961
2966
|
const claimedBootstrap = bootstrap;
|
|
2962
2967
|
claimedInFlight = true;
|
|
2963
2968
|
incrementActivationBootstrapInFlight(accountId);
|
|
2964
|
-
|
|
2969
|
+
bootstrapGreetingDelivered = false;
|
|
2965
2970
|
const result = await handleInboundEnvelope(
|
|
2966
2971
|
buildActivationBootstrapEnvelope({ account, conversationId: claimedBootstrap.conversationId }),
|
|
2967
2972
|
);
|
|
@@ -2970,15 +2975,15 @@ export async function startOpenclawClawlingGateway(params: StartGatewayParams):
|
|
|
2970
2975
|
armBootstrapRetry();
|
|
2971
2976
|
return;
|
|
2972
2977
|
}
|
|
2973
|
-
// Only mark the bootstrap done once the
|
|
2974
|
-
// (a
|
|
2975
|
-
//
|
|
2976
|
-
// agent went off to write a file/note instead of replying — leave the
|
|
2978
|
+
// Only mark the bootstrap done once the greeting was actually delivered
|
|
2979
|
+
// (a real successful outbound send). If the turn was handed off but nothing
|
|
2980
|
+
// reached the user — e.g. the greeting send was cancelled by a reconnect, or
|
|
2981
|
+
// the agent went off to write a file/note instead of replying — leave the
|
|
2977
2982
|
// bootstrap pending (release the claim) and retry on the live connection,
|
|
2978
2983
|
// rather than silently consuming it forever or waiting for the next reconnect.
|
|
2979
|
-
if (!
|
|
2984
|
+
if (!bootstrapGreetingDelivered) {
|
|
2980
2985
|
log?.info?.(
|
|
2981
|
-
`[${accountId}] clawchat-plugin-openclaw activation bootstrap dispatched but
|
|
2986
|
+
`[${accountId}] clawchat-plugin-openclaw activation bootstrap dispatched but greeting was not delivered; leaving pending for retry`,
|
|
2982
2987
|
);
|
|
2983
2988
|
releaseBootstrap();
|
|
2984
2989
|
armBootstrapRetry();
|