Skip to content

Commit 3257620

Browse files
[AI-assisted] fix(reply): wait for block replies before tools
1 parent d8c410c commit 3257620

4 files changed

Lines changed: 46 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ Docs: https://docs.openclaw.ai
2424
### Fixes
2525

2626
- CLI/agents: allow `openclaw agent --session-key` to target explicit session keys, including agent-scoped legacy keys. (#85121) Thanks @Kaspre.
27+
- Auto-reply/ACP: wait for same-channel block reply delivery before starting tool work, while still honoring ACP dispatch aborts so stopped turns do not wait on slow channel sends. (#83722) Thanks @IWhatsskill.
2728
- Agents/subagents: surface blocked child-run completions as errors instead of successful subagent finishes. (#80886) Thanks @TurboTheTurtle.
2829
- Agents/Pi: treat accepted embedded `sessions_spawn` child-session handoffs as terminal progress so parent turns no longer report false non-deliverable failures. (#85054) Thanks @samzong.
2930
- WhatsApp: update Baileys to `7.0.0-rc13` and drop the obsolete logger type patch.

src/auto-reply/reply/dispatch-acp-delivery.test.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,48 @@ describe("createAcpDispatchDeliveryCoordinator", () => {
285285
expect(deliverySettled).toBe(true);
286286
});
287287

288+
it("stops waiting for direct block delivery when the ACP dispatch aborts", async () => {
289+
const delivered: unknown[] = [];
290+
const controller = new AbortController();
291+
let releaseDelivery: (() => void) | undefined;
292+
let markDeliveryStarted: (() => void) | undefined;
293+
const deliveryStarted = new Promise<void>((resolve) => {
294+
markDeliveryStarted = resolve;
295+
});
296+
const deliveryGate = new Promise<void>((resolve) => {
297+
releaseDelivery = resolve;
298+
});
299+
const dispatcher = createReplyDispatcher({
300+
deliver: async (payload) => {
301+
delivered.push(payload);
302+
markDeliveryStarted?.();
303+
await deliveryGate;
304+
},
305+
});
306+
const coordinator = createAcpDispatchDeliveryCoordinator({
307+
cfg: createAcpTestConfig(),
308+
ctx: buildTestCtx({
309+
Provider: "visiblechat",
310+
Surface: "visiblechat",
311+
SessionKey: "agent:codex-acp:session-1",
312+
}),
313+
dispatcher,
314+
inboundAudio: false,
315+
shouldRouteToOriginating: false,
316+
abortSignal: controller.signal,
317+
});
318+
319+
const deliveryPromise = coordinator.deliver("block", { text: "hello" }, { skipTts: true });
320+
await deliveryStarted;
321+
controller.abort();
322+
323+
await expect(deliveryPromise).resolves.toBe(true);
324+
expect(delivered).toEqual([{ text: "hello" }]);
325+
326+
releaseDelivery?.();
327+
await dispatcher.waitForIdle();
328+
});
329+
288330
it("strips split TTS directives from visible ACP block delivery", async () => {
289331
const dispatcher = createDispatcher();
290332
const coordinator = createAcpDispatchDeliveryCoordinator({

src/auto-reply/reply/dispatch-acp-delivery.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ export function createAcpDispatchDeliveryCoordinator(params: {
192192
originatingChannel?: string;
193193
originatingTo?: string;
194194
onReplyStart?: () => Promise<void> | void;
195+
abortSignal?: AbortSignal;
195196
}): AcpDispatchDeliveryCoordinator {
196197
const directChannel = normalizeOptionalLowercaseString(params.ctx.Provider ?? params.ctx.Surface);
197198
const routedChannel = normalizeOptionalLowercaseString(params.originatingChannel);
@@ -460,7 +461,7 @@ export function createAcpDispatchDeliveryCoordinator(params: {
460461
state.failedVisibleTextDelivery = true;
461462
}
462463
if (kind === "block" && delivered) {
463-
await waitForReplyDispatcherIdle(params.dispatcher);
464+
await waitForReplyDispatcherIdle(params.dispatcher, params.abortSignal);
464465
}
465466
return delivered;
466467
};

src/auto-reply/reply/dispatch-acp.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,7 @@ export async function tryDispatchAcpReply(params: {
386386
originatingChannel: params.originatingChannel,
387387
originatingTo: params.originatingTo,
388388
onReplyStart: params.onReplyStart,
389+
abortSignal: params.abortSignal,
389390
});
390391

391392
const identityPendingBeforeTurn = isSessionIdentityPending(

0 commit comments

Comments
 (0)