Skip to content

[Bug]: sessions_spawn direct completion delivery ignores ANNOUNCE_SKIP #25800

@astra-fer

Description

@astra-fer

Summary

When a sub-agent spawned via sessions_spawn (mode="run") replies with exactly ANNOUNCE_SKIP, the completion message is still delivered directly to the originating channel (e.g. Slack thread). The isAnnounceSkip check exists in the agent-to-agent (sessions_send) announce path but is missing from the sessions_spawn direct completion delivery path in sendSubagentAnnounceDirectly().

Steps to reproduce

  1. Trigger the main agent session from a Slack channel (or any external channel)
  2. Main agent spawns a sub-agent via sessions_spawn with mode: "run", cleanup: "delete"
  3. Sub-agent completes its task, sends confirmation via message tool, then replies with exactly ANNOUNCE_SKIP
  4. Observe that the originating Slack thread receives: ✅ Subagent main finished\n\nANNOUNCE_SKIP

Expected behavior

No completion message should be delivered to the channel when the sub-agent replies exactly ANNOUNCE_SKIP. The docs state: "If the sub-agent replies exactly ANNOUNCE_SKIP, nothing is posted."

Actual behavior

The channel receives a message like:

✅ Subagent main finished

ANNOUNCE_SKIP

This is built by buildCompletionDeliveryMessage() which wraps the raw findings text with a header. The direct delivery path at sendSubagentAnnounceDirectly() sends this completionMessage without checking isAnnounceSkip(findings).

OpenClaw version

2026.2.23

Operating system

macOS 15.3 (Darwin 25.3.0 arm64)

Install method

npm global (Homebrew)

Logs, screenshots, and evidence

Root cause traced in the minified dist source:

1. `buildCompletionDeliveryMessage()` always builds a header + findings message, even when findings === "ANNOUNCE_SKIP"
2. `sendSubagentAnnounceDirectly()` checks `completionMessage?.trim()` but never calls `isAnnounceSkip()`
3. The `isAnnounceSkip()` guard only exists in the `sessions_send` agent-to-agent path (line ~35375 in pi-embedded)

The a2a announce path correctly checks: `if (announceTarget && announceReply && announceReply.trim() && !isAnnounceSkip(announceReply))`
But the spawn direct delivery path does not.

Impact and severity

Affected: Any agent using sessions_spawn sub-agents that reply ANNOUNCE_SKIP, with delivery to Slack/Telegram/Discord channels
Severity: Medium (cosmetic but confusing — internal tokens leak to end users)
Frequency: 100% repro on every sub-agent completion
Consequence: Raw internal tokens ("ANNOUNCE_SKIP", "Subagent main finished") appear as visible messages in user-facing channels, undermining trust and professionalism

Additional information

Suggested fix: In buildCompletionDeliveryMessage(), return empty string when findings.trim() === "ANNOUNCE_SKIP":

function buildCompletionDeliveryMessage(params) {
    const findingsText = params.findings.trim();
    if (findingsText === "ANNOUNCE_SKIP") return ""; // skip delivery
    const hasFindings = findingsText.length > 0 && findingsText !== "(no output)";
    // ...
}

This causes the existing completionMessage?.trim() guard in sendSubagentAnnounceDirectly() to skip delivery. We applied this as a local monkey-patch and confirmed it resolves the issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions