Skip to content

fix(auto-reply): honor direct silent empty replies#74555

Merged
steipete merged 2 commits into
openclaw:mainfrom
vyctorbrzezowski:contrib/74409-direct-silent-reply-empty
Apr 29, 2026
Merged

fix(auto-reply): honor direct silent empty replies#74555
steipete merged 2 commits into
openclaw:mainfrom
vyctorbrzezowski:contrib/74409-direct-silent-reply-empty

Conversation

@vyctorbrzezowski

@vyctorbrzezowski vyctorbrzezowski commented Apr 29, 2026

Copy link
Copy Markdown
Member

Summary

  • Problem: silentReply.direct: "allow" told direct-chat prompts to use NO_REPLY, but empty or reasoning-only direct turns were still treated as failed empty responses.
  • Why it matters: operators who explicitly opt direct chats into silence should get the same intentional-silence handling as NO_REPLY, without changing conservative direct-chat defaults.
  • What changed: direct/dm runs now pass allowEmptyAssistantReplyAsSilent only when the resolved prompt-policy context is direct and silentReply.direct resolves to allow.
  • What did NOT change (scope boundary): group/channel behavior, default direct-chat behavior, silent rewrite behavior, and cross-session native command targeting remain conservative.

AI-assisted: yes. I understand what the code changes do.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor required for the fix
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

Root Cause (if applicable)

  • Root cause: the prompt path resolved direct silent-reply policy, but allowEmptyAssistantReplyAsSilent was gated behind isGroupChat only.
  • Missing detection / guardrail: coverage checked group empty/reasoning-only silence and direct prompt text, but not direct runner propagation for explicit silentReply.direct: "allow".
  • Contributing context (if known): direct chats intentionally default to disallow, so the fix must only apply to explicit direct policy context.

Regression Test Plan (if applicable)

  • Coverage level that should have caught this:
    • Unit test
    • Seam / integration test
    • End-to-end test
    • Existing coverage already sufficient
  • Target test or file: src/auto-reply/reply/get-reply-run.media-only.test.ts
  • Scenario the test should lock in: direct and dm runs propagate empty-assistant silence only when direct silent replies are explicitly allowed; native commands sent from direct chats do not borrow target-session silence.
  • Why this is the smallest reliable guardrail: runPreparedReply is the seam that computes and forwards allowEmptyAssistantReplyAsSilent into the runner.
  • Existing test that already covers this (if any): runner behavior for the flag itself is covered in src/agents/pi-embedded-runner/run.incomplete-turn.test.ts.
  • If no new test is added, why not: N/A

User-visible / Behavior Changes

Operators who explicitly configure silentReply.direct: "allow" can now have clean empty or reasoning-only direct/dm model turns treated as intentional silence. Direct chats still disallow this by default.

Diagram (if applicable)

N/A

Security Impact (required)

  • New permissions/capabilities? (Yes/No): No
  • Secrets/tokens handling changed? (Yes/No): No
  • New/changed network calls? (Yes/No): No
  • Command/tool execution surface changed? (Yes/No): No
  • Data access scope changed? (Yes/No): No
  • If any Yes, explain risk + mitigation: N/A

Repro + Verification

Environment

  • OS: macOS
  • Runtime/container: Node/pnpm local checkout
  • Model/provider: N/A
  • Integration/channel (if any): direct/dm auto-reply runner
  • Relevant config (redacted): agents.defaults.silentReply.direct = "allow"

Steps

  1. Configure direct silent replies with silentReply.direct: "allow".
  2. Run a direct/dm turn where the assistant produces no visible text or only reasoning.
  3. Compare with a default direct/dm config and with a native direct command targeting a different session.

Expected

  • Explicit direct allow treats clean empty/reasoning-only as intentional silence.
  • Default direct config keeps empty-response retry/fallback behavior.
  • Targeted native commands do not inherit target-session silence for direct requester delivery.

Actual

  • Before this change, direct/dm runs never enabled empty-assistant silence because the flag was gated on isGroupChat.

Evidence

  • Failing test/log before + passing after
  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

What you personally verified (not just CI), and how:

  • Verified scenarios: explicit direct/dm allow, default direct disallow, targeted native direct command guard, existing runner handling for empty/reasoning-only silence.
  • Edge cases checked: group/channel behavior remains routed through resolveGroupSilentReplyBehavior; direct allow requires direct prompt-policy context.
  • What you did not verify: live messaging-channel delivery.

Commands run:

pnpm docs:list
pnpm test src/auto-reply/reply/get-reply-run.media-only.test.ts src/agents/pi-embedded-runner/run.incomplete-turn.test.ts
pnpm exec oxfmt --check --threads=1 src/auto-reply/reply/get-reply-run.ts src/auto-reply/reply/get-reply-run.media-only.test.ts CHANGELOG.md docs/channels/groups.md
git diff --check
pnpm changed:lanes --json
pnpm check:changed
/Users/vyctor/.codex/skills/oss-pr-contributions/scripts/public_pr_preflight.sh /Users/vyctor/Documents/Codex/2026-04-29/quero-trabalhar-na-issue-74409-do/openclaw

codex review --base origin/main initially found one P2 around native direct commands targeting another session. That finding was fixed and covered with a regression test. A final retry completed successfully with no remaining findings.

Review Conversations

  • I replied to or resolved every bot review conversation I addressed in this PR.
  • I left unresolved only the conversations that still need reviewer or maintainer judgment.

Compatibility / Migration

  • Backward compatible? (Yes/No): Yes
  • Config/env changes? (Yes/No): No
  • Migration needed? (Yes/No): No
  • If yes, exact upgrade steps: N/A

Risks and Mitigations

  • Risk: direct chats could accidentally suppress empty failed turns.
    • Mitigation: the direct branch requires direct prompt-policy context plus explicit silentReply.direct: "allow"; defaults remain conservative and targeted native commands are covered.

@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation size: S labels Apr 29, 2026
@clawsweeper

clawsweeper Bot commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge.

What this changes:

The PR updates auto-reply runner setup so explicit direct/dm silentReply.direct: "allow" can pass allowEmptyAssistantReplyAsSilent, adds regression tests for explicit direct allow and native cross-session command guarding, and updates the groups docs and changelog.

Maintainer follow-up before merge:

This is already an open implementation PR with a coherent patch and no blocking review findings; the remaining action is maintainer review and CI completion, not a replacement repair job.

Security review:

Security review cleared: The diff only changes TypeScript auto-reply policy wiring plus tests, docs, and changelog; it does not add dependencies, workflows, permissions, secrets handling, downloads, or package execution paths.

Review details

Best possible solution:

Land this PR or an equivalent narrow fix after CI finishes, preserving conservative direct-chat defaults while allowing operators who explicitly set direct silent replies to get the same clean empty/reasoning-only silence handling already supported by the runner.

Acceptance criteria:

  • pnpm test src/auto-reply/reply/get-reply-run.media-only.test.ts src/agents/pi-embedded-runner/run.incomplete-turn.test.ts
  • pnpm exec oxfmt --check --threads=1 src/auto-reply/reply/get-reply-run.ts src/auto-reply/reply/get-reply-run.media-only.test.ts CHANGELOG.md docs/channels/groups.md
  • pnpm check:changed in Testbox before merge if maintainers need full changed-lane proof

What I checked:

  • Current main still group-gates empty silent replies: allowEmptyAssistantReplyAsSilent is computed as isGroupChat && resolveGroupSilentReplyBehavior(...).allowEmptyAssistantReplyAsSilent, so direct/dm runs cannot pass the runner flag on current main. (src/auto-reply/reply/get-reply-run.ts:478, 57e4994caf6d)
  • Direct silent policy is an existing real mode: Direct prompt construction already tells the model to use NO_REPLY when the resolved direct silent policy is allow, and the default policy table keeps direct conversations disallow by default. (src/auto-reply/reply/groups.ts:294, 57e4994caf6d)
  • Runner contract already supports the flag: The embedded runner treats clean empty or reasoning-only assistant turns as silent only when allowEmptyAssistantReplyAsSilent is true, matching the PR's caller-side propagation fix. (src/agents/pi-embedded-runner/run/incomplete-turn.ts:487, 57e4994caf6d)
  • PR diff applies the direct-only opt-in guard: The patch reuses resolvePromptSilentReplyConversationType, adds isDirectChat, and enables empty-assistant silence only when the prompt context is direct and the resolved policy is allow; the group branch remains behind the existing group behavior resolver. (src/auto-reply/reply/get-reply-run.ts:477, aa4b926cd09d)
  • PR adds focused regression coverage: The added tests cover direct and dm explicit allow propagation, keep default direct runs disabled, and guard native direct commands targeting another session from borrowing target-session silence. (src/auto-reply/reply/get-reply-run.media-only.test.ts:331, aa4b926cd09d)
  • Linked issue context supports the bug and scope: The linked report describes the same shipped v2026.4.26 behavior: silentReply.direct: "allow" gives direct prompts the silent-token policy, but the current runner flag remains blocked by the group-chat short-circuit. (57e4994caf6d)

Likely related people:

  • Shakker: Current blame for the silent-reply propagation block, direct prompt helper, silent policy defaults, and runner flag handling all resolve to the same current checkout boundary commit, so this is the most direct local history route for the touched behavior. (role: line-history owner in available checkout; confidence: medium; commits: fceaecd12319; files: src/auto-reply/reply/get-reply-run.ts, src/auto-reply/reply/groups.ts, src/shared/silent-reply-policy.ts)
  • steipete: Peter Steinberger authored the v2026.4.26 release consolidation commit that contains the relevant auto-reply, runner, docs, and silent-policy files in the checked-out history, and appears as the recent maintainer around current main. (role: recent maintainer / release owner; confidence: medium; commits: be8c24633aaa, 57e4994caf6d; files: CHANGELOG.md, docs/channels/groups.md, src/auto-reply/reply/get-reply-run.ts)

Remaining risk / open question:

  • I did not run tests locally because this review was explicitly read-only; broad CI check runs were still queued at inspection time, so merge should wait for the required checks to finish.

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

@steipete
steipete force-pushed the contrib/74409-direct-silent-reply-empty branch from aa4b926 to dadc67a Compare April 29, 2026 19:02
@steipete
steipete merged commit 97e2f5b into openclaw:main Apr 29, 2026
4 checks passed
@steipete

Copy link
Copy Markdown
Contributor

Landed in 97e2f5b. Thanks @vyctorbrzezowski.

Verified after rebasing onto current main:

  • pnpm test src/auto-reply/reply/get-reply-run.media-only.test.ts src/agents/pi-embedded-runner/run.incomplete-turn.test.ts src/auto-reply/reply/groups.test.ts src/infra/outbound/payloads.test.ts -- --reporter=verbose
  • pnpm exec oxfmt --check --threads=1 src/auto-reply/reply/get-reply-run.ts src/auto-reply/reply/get-reply-run.media-only.test.ts CHANGELOG.md docs/channels/groups.md
  • git diff --check

@vyctorbrzezowski
vyctorbrzezowski deleted the contrib/74409-direct-silent-reply-empty branch May 12, 2026 21:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Improvements or additions to documentation size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

silent-reply detection gated behind isGroupChat — direct chats can't honor silentReplyPolicy="allow"

2 participants