Skip to content

fix(auto-reply): avoid silent completion when dispatch produces no sendable reply#63025

Open
Leon-llb wants to merge 5 commits into
openclaw:mainfrom
Leon-llb:codex/fix-empty-run-fallback
Open

fix(auto-reply): avoid silent completion when dispatch produces no sendable reply#63025
Leon-llb wants to merge 5 commits into
openclaw:mainfrom
Leon-llb:codex/fix-empty-run-fallback

Conversation

@Leon-llb

@Leon-llb Leon-llb commented Apr 8, 2026

Copy link
Copy Markdown

Summary

This fixes a silent completion path in dispatchReplyFromConfig.

In the current flow, a normal user message can make it through dispatch and still end with no sendable output, leaving the user with no reply and no visible error. In logs this shows up as dispatch complete (queuedFinal=false, replies=0).

What Changed

  • add a narrow fallback final reply when a normal embedded dispatch produces no sendable final, tool, or block output
  • keep existing intentional silent branches unchanged
  • add a regression test for the empty-output path

Scope

This PR fixes the "silent no-reply" behavior only.

It does not change search/tool execution behavior, and it does not solve cases where a long-running task times out after repeated upstream tool failures.

Testing

Ran:

  • ./node_modules/.bin/vitest run --config vitest.config.ts src/auto-reply/reply/dispatch-from-config.test.ts -t "sends a fallback final reply when model dispatch produces no sendable output"
  • ./node_modules/.bin/vitest run --config vitest.config.ts src/auto-reply/reply/dispatch-from-config.test.ts -t "silently short-circuits when hook returns handled without text"

Notes

AI-assisted: yes

在普通消息分发无可发送输出时补发错误提示
减少 replies=0 导致的用户侧静默无响应
保留显式静默分支与现有 hook 行为
@greptile-apps

greptile-apps Bot commented Apr 8, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a narrow fallback final reply in dispatchReplyFromConfig for the case where an embedded (non-routed) model dispatch completes with zero sendable final, tool, or block outputs, preventing silent no-reply UX. The condition is correctly guarded to be mutually exclusive with the TTS block-streaming path and intentional silent branches (before_dispatch handled without text, plugin binding short-circuits, etc.).

Confidence Score: 5/5

Safe to merge — the fallback is narrowly scoped, mutually exclusive with existing silent branches, and covered by regression tests.

All findings are P2. The logic is correct: the fallback condition guards properly with !shouldRouteToOriginating, blockCount === 0, counts.tool === 0, counts.block === 0, and counts.final === 0, making it mutually exclusive with the TTS block-streaming path and every intentional early-return. No P0/P1 issues found.

No files require special attention.

Vulnerabilities

No security concerns identified. The fallback path only emits a static error string and does not expose internal state or user data.

Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/auto-reply/reply/dispatch-from-config.ts
Line: 85-86

Comment:
**Fallback message may mislead when no tools were invoked**

`EMPTY_RUN_FALLBACK_TEXT` says "A tool may have failed, timed out, or been blocked", but the fallback fires when `counts.tool === 0` (no tool results were dispatched). In the zero-tool case the message slightly misattributes the empty output. Consider a more neutral phrasing like "I couldn't produce a reply for that request. Please try again or rephrase your message."

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix(auto-reply): 避免空回复时静默结束" | Re-trigger Greptile

Comment on lines +85 to +86
const EMPTY_RUN_FALLBACK_TEXT =
"I couldn't produce a sendable reply for that request. A tool may have failed, timed out, or been blocked. Please retry with a narrower task or a different source.";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Fallback message may mislead when no tools were invoked

EMPTY_RUN_FALLBACK_TEXT says "A tool may have failed, timed out, or been blocked", but the fallback fires when counts.tool === 0 (no tool results were dispatched). In the zero-tool case the message slightly misattributes the empty output. Consider a more neutral phrasing like "I couldn't produce a reply for that request. Please try again or rephrase your message."

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/auto-reply/reply/dispatch-from-config.ts
Line: 85-86

Comment:
**Fallback message may mislead when no tools were invoked**

`EMPTY_RUN_FALLBACK_TEXT` says "A tool may have failed, timed out, or been blocked", but the fallback fires when `counts.tool === 0` (no tool results were dispatched). In the zero-tool case the message slightly misattributes the empty output. Consider a more neutral phrasing like "I couldn't produce a reply for that request. Please try again or rephrase your message."

How can I resolve this? If you propose a fix, please make it concise.

Leon-llb added 4 commits April 8, 2026 16:24
避免在无工具输出场景中误导性归因
同步更新回归测试断言
合并 origin/main 并保留空回复兜底修复
同时保留主干新的媒体类型规范化实现
避免通用 fallback 误伤 Slack 无回复也合法的 ack 场景
仅在 direct/private 类会话且确实无可发送输出时补发提示
补充频道消息回归测试,保持现有静默分支行为不变
@Leon-llb

Leon-llb commented Apr 9, 2026

Copy link
Copy Markdown
Author

Addressed the CI regression by narrowing the fallback to direct/private chat contexts only. All checks are now passing.

@clawsweeper

clawsweeper Bot commented Apr 27, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed June 6, 2026, 12:47 AM ET / 04:47 UTC.

Summary
Review failed before ClawSweeper could summarize the requested change.

PR surface: Source +27, Tests +35. Total +62 across 2 files.

Reproducibility: unclear. The review failed before ClawSweeper could establish a reproduction path.

Review metrics: none identified.

Merge readiness
Overall: 🌊 off-meta tidepool
Proof: 🌊 off-meta tidepool
Patch quality: 🌊 off-meta tidepool
Result: rating does not apply to this item.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Risk before merge

  • [P1] No close action taken because the review did not complete.

Maintainer options:

  1. Decide the mitigation before merge
    Retry the Codex review after fixing the execution failure.
  2. Pause or close
    Do not merge this PR until maintainers decide whether the risk is worth taking.

Next step before merge

  • [P1] Review did not complete, so no work-lane recommendation was made.
Review details

Best possible solution:

Retry the Codex review after fixing the execution failure.

Do we have a high-confidence way to reproduce the issue?

Unclear. The review failed before ClawSweeper could establish a reproduction path.

Is this the best way to solve the issue?

Unclear. Retry the review first so ClawSweeper can evaluate the actual issue and fix direction.

AGENTS.md: unclear because the file could not be read completely.

Codex review notes: model gpt-5.5, reasoning high; reviewed against 9313471fa579.

Label changes

Label changes:

  • remove P2: Current review triage priority is none.
  • remove merge-risk: 🚨 compatibility: Current PR review selected no merge-risk labels.
  • remove merge-risk: 🚨 message-delivery: Current PR review selected no merge-risk labels.
  • remove merge-risk: 🚨 security-boundary: Current PR review selected no merge-risk labels.

Label justifications:

  • rating: 🌊 off-meta tidepool: Overall readiness is 🌊 off-meta tidepool; proof is 🌊 off-meta tidepool and patch quality is 🌊 off-meta tidepool.
Evidence reviewed

PR surface:

Source +27, Tests +35. Total +62 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 1 27 0 +27
Tests 1 35 0 +35
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 62 0 +62

What I checked:

  • failure reason: codex execution failed.
  • codex failure detail: Codex review failed for this PR with exit 1.
  • codex stdout: Per-item Codex failure; continuing with the rest of the shard.

Likely related people:

  • unknown: Codex failed before it could trace repository history. (role: review did not complete; confidence: low)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@clawsweeper

clawsweeper Bot commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge.

What this changes:

The PR adds a static error fallback in dispatchReplyFromConfig for direct/private embedded dispatches that produce no final, tool, or block output, plus regression tests for the fallback and channel no-op path.

Maintainer follow-up before merge:

This is an open implementation PR with a valid current-main gap, but the remaining action is maintainer review of delivery-policy and chat-type semantics rather than an automated replacement fix.

Review details

Best possible solution:

Land a narrow dispatch-level empty-output fallback only after final queued counts are known, guarded by delivery policy, direct-style conversation policy, no routed-origin delivery, no queued final/tool/block output, no block-stream/TTS output, and no intentional handled/no-text branch. Use normalizeChatType unless maintainers intentionally add private as a supported alias with tests.

Acceptance criteria:

  • pnpm test src/auto-reply/reply/dispatch-from-config.test.ts
  • pnpm test src/channels/channels-misc.test.ts

What I checked:

  • Current main still silently completes zero-output dispatch: On current main, an undefined resolver result becomes replies = []; after the guarded delivery block, dispatch reads queued counts, records completed, marks idle, and returns without queuing any fallback when final/tool/block counts are all zero. (src/auto-reply/reply/dispatch-from-config.ts:1306, acae48b790fa)
  • Delivery suppression is an explicit contract: Current main resolves sendPolicy and source reply visibility before dispatch; sendPolicy: deny or message_tool_only sets suppressDelivery, and normal final delivery is gated by if (!suppressDelivery). (src/auto-reply/reply/source-reply-delivery-mode.ts:56, acae48b790fa)
  • Existing tests protect no-delivery behavior: Current tests assert that sendPolicy: deny still processes the turn but suppresses final, tool, and block delivery, and that a handled before_dispatch hook without text remains intentionally silent. (src/auto-reply/reply/dispatch-from-config.test.ts:3866, acae48b790fa)
  • Direct versus group/channel policy supports a narrow fallback: The user-facing message docs say direct conversations disallow silence by default, while groups/channels and internal orchestration allow silence by default. Public docs: docs/concepts/messages.md. (docs/concepts/messages.md:174, acae48b790fa)
  • Canonical chat type contract does not include private: normalizeChatType currently normalizes direct and legacy dm to direct, accepts group and channel, and returns undefined for unknown values; the PR's direct/private helper would need deliberate contract alignment if private is meant to be supported. (src/channels/chat-type.ts:3, acae48b790fa)
  • Security review scope: The PR changes only src/auto-reply/reply/dispatch-from-config.ts and its test file, with no workflow, dependency, lockfile, release, or package-publishing files. The security-relevant concern is policy bypass if a fallback is sent when delivery is explicitly suppressed. (7b583d2f5c02)

Likely related people:

  • @steipete: Recent commits in the dispatch and source-reply-delivery path centralize visibility policy and visible reply behavior, including 67b16a4a6d25ea31a4cc021c3663b26aa2a6c34f and e1fd27fb24ae81e27cf4ac1297410491009a70c0. (role: recent maintainer; confidence: high; commits: 67b16a4a6d25, 249cb5437364, e1fd27fb24ae; files: src/auto-reply/reply/dispatch-from-config.ts, src/auto-reply/reply/source-reply-delivery-mode.ts, src/auto-reply/reply/dispatch-from-config.test.ts)
  • @scoootscooob: Authored the recent group/channel message-tool default change that defines where automatic source replies should stay suppressed, which is central to safely scoping this fallback. (role: adjacent recent owner; confidence: medium; commits: 3c636208b0a0; files: src/auto-reply/reply/dispatch-from-config.ts, src/auto-reply/reply/dispatch-from-config.test.ts)
  • @quotentiroler: Introduced the ChatType direct/dm normalization work that the PR's local direct|dm|private helper would need to align with or deliberately extend. (role: introduced related contract; confidence: medium; commits: 223eee0a20c4; files: src/channels/chat-type.ts)

Remaining risk / open question:

  • The PR fallback condition shown in the provided diff is outside the existing !suppressDelivery delivery block and does not explicitly check suppressDelivery, so a direct session with sendPolicy: deny could receive an outbound fallback unless the branch is updated.
  • The PR's local direct|dm|private helper may diverge from normalizeChatType, whose canonical contract currently excludes private.
  • Current main has changed source-reply delivery behavior since the PR base, so the branch needs rebase/review against the current message_tool_only group/channel policy and broader suppressed/no-op coverage.

Codex review notes: model gpt-5.5, reasoning high; reviewed against acae48b790fa.

@clawsweeper clawsweeper Bot added the mantis: telegram-visible-proof Mantis should capture Telegram visible proof. label May 13, 2026
@openclaw-barnacle openclaw-barnacle Bot added the triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup. label May 13, 2026
@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. merge-risk: 🚨 security-boundary 🚨 May affect sandboxing, authorization, credentials, or sensitive data. labels May 19, 2026
@clawsweeper

clawsweeper Bot commented May 20, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper PR egg

🎁 Pass real behavior proof to wake the egg and unlock a hatchable treat.

Where did the egg go?
  • The egg game starts only after the PR passes the real-behavior proof check.
  • Before that, no creature or rarity is rolled. The treat waits for real proof.
  • This is still just collectible flavor: proof affects review readiness, not creature quality.

@openclaw-barnacle

Copy link
Copy Markdown

This pull request has been automatically marked as stale due to inactivity.
Please add updates or it will be closed.

@openclaw-barnacle openclaw-barnacle Bot added the stale Marked as stale due to inactivity label Jun 5, 2026
@clawsweeper clawsweeper Bot added rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Jun 5, 2026
@clawsweeper clawsweeper Bot removed the mantis: telegram-visible-proof Mantis should capture Telegram visible proof. label Jun 5, 2026
@openclaw-barnacle openclaw-barnacle Bot removed the stale Marked as stale due to inactivity label Jun 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. merge-risk: 🚨 message-delivery 🚨 May drop, duplicate, misroute, suppress, or wrongly target messages. merge-risk: 🚨 security-boundary 🚨 May affect sandboxing, authorization, credentials, or sensitive data. P2 Normal backlog priority with limited blast radius. rating: 🌊 off-meta tidepool PR readiness rating does not apply to this item. size: S triage: needs-real-behavior-proof Candidate: external PR needs after-fix proof from a real setup.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant