Skip to content

fix(hooks): skip summary relay when hook agent turn already delivered#20242

Closed
artwist-polyakov wants to merge 1 commit into
openclaw:mainfrom
artwist-polyakov:fix/hooks-duplicate-delivery
Closed

fix(hooks): skip summary relay when hook agent turn already delivered#20242
artwist-polyakov wants to merge 1 commit into
openclaw:mainfrom
artwist-polyakov:fix/hooks-duplicate-delivery

Conversation

@artwist-polyakov

@artwist-polyakov artwist-polyakov commented Feb 18, 2026

Copy link
Copy Markdown

Fixes #20196

Summary

Problem

When /hooks/agent is called with deliver: true and a specific agentId + to target, the isolated agent turn delivers the response correctly via the subagent announce flow. However, dispatchAgentHook then unconditionally posts a summary to the main agent session via enqueueSystemEvent and wakes the heartbeat via requestHeartbeatNow. This causes the main agent to wake up, process the system event, and generate a second response — resulting in duplicate messages delivered to the target channel (or to the main agent's last active chat).

Fix

The RunCronAgentTurnResult.delivered flag already exists and is documented:

true when the isolated run already delivered its output to the target channel. Callers should skip posting a summary to the main session to avoid duplicate messages.

The cron service path (timer.ts) already honors this flag since ea95e88. This PR applies the same guard to the hooks handler.

Test plan

  • Trigger a hook with deliver: true targeting a non-default agent → verify response arrives only in the target chat
  • Trigger a hook with deliver: false → verify summary still posts to main session and heartbeat wakes
  • Trigger a hook that fails → verify error summary still posts to main session

🤖 Generated with Claude Code

When dispatchAgentHook calls runCronIsolatedAgentTurn with deliver:true,
the isolated run delivers its output via the subagent announce flow or
direct outbound payloads. After the run completes, the handler
unconditionally posts a summary to the main agent session via
enqueueSystemEvent and wakes the heartbeat via requestHeartbeatNow.
This causes the main agent to wake up and generate a second response
that is also delivered to the target channel, resulting in duplicate
messages.

Guard the enqueueSystemEvent + requestHeartbeatNow calls behind
`!result.delivered`, mirroring the same fix applied to executeJobCore
in the cron service path (ea95e88).

Fixes openclaw#20196
See also: openclaw#15692

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@openclaw-barnacle openclaw-barnacle Bot added gateway Gateway runtime size: XS labels Feb 18, 2026
@artwist-polyakov

Copy link
Copy Markdown
Author

The failing check (checks-windows / node, test) is unrelated to this PR — it's a pre-existing flaky test in src/infra/shell-env.test.ts that fails on Windows CI because the test expects Unix paths (/usr/local/bin:/usr/bin) which don't exist on Windows runners.

All relevant checks pass:

  • ✅ checks (node, test) — Linux
  • ✅ checks (bun, test)
  • ✅ checks (node, lint)
  • ✅ checks (node, protocol)
  • ✅ Greptile Review

This PR only changes src/gateway/server/hooks.ts and does not touch shell-env.ts.

@Glucksberg

Glucksberg commented Feb 21, 2026

Copy link
Copy Markdown
Contributor

This could be relevant to what you're working on.

PR #20199 by @zerone0x seems to address the same problem.

When a hook agent turn delivered via the announce flow, a duplicate delivery occurred because enqueueSystemEvent and requestHeartbeatNow were not gated on result.delivered.

Both approaches have merit — might be worth coordinating.

Related issue(s): #20196

If any of these links don't look right, let me know and I'll correct them.

@vincentkoc

Copy link
Copy Markdown
Member

I appreciate the work here.

I’m closing this as a duplicate of #20678. This PR covered the hooks-side guard, and the canonical PR carries that fix plus the SILENT_REPLY_TOKEN delivered-path correction.

If you see a gap that is not covered in #20678, point me to it and I can reopen right away.

@vincentkoc vincentkoc closed this Feb 22, 2026
@vincentkoc vincentkoc added dedupe:child Duplicate issue/PR child in dedupe cluster close:duplicate Closed as duplicate labels Feb 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

close:duplicate Closed as duplicate dedupe:child Duplicate issue/PR child in dedupe cluster gateway Gateway runtime size: XS

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(hooks): duplicate delivery when hook agent turn uses announce flow

3 participants