Skip to content

fix: cron announce response prefix, issue 29600#29687

Closed
sahilsatralkar wants to merge 3 commits intoopenclaw:mainfrom
sahilsatralkar:fix/cron-announce-response-prefix
Closed

fix: cron announce response prefix, issue 29600#29687
sahilsatralkar wants to merge 3 commits intoopenclaw:mainfrom
sahilsatralkar:fix/cron-announce-response-prefix

Conversation

@sahilsatralkar
Copy link
Copy Markdown
Contributor

Summary

Describe the problem and fix in 2–5 bullets:

  • Problem: Cron jobs with delivery.mode: "announce" do not apply the responsePrefix (e.g., [Thames]) to messages, while interactive replies correctly apply it.
  • Why it matters: In group chats with multiple agents delivering to the same channel, the lack of prefix makes it unclear which agent sent the message.
  • What changed: Modified src/cron/isolated-agent/delivery-dispatch.ts to resolve and apply responsePrefix using the existing resolveResponsePrefix() function in both the direct delivery and announce delivery paths.
  • What did NOT change (scope boundary): Other delivery mechanisms, interactive reply flow, heartbeat delivery, webhook delivery.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor
  • 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

User-visible / Behavior Changes

Cron jobs with delivery.mode: "announce" will now prepend the configured responsePrefix (e.g., [AgentName]) to the message text, matching interactive reply behavior.

  • Global messages.responsePrefix: "[BotName]" now applies to cron announce delivery
  • Channel-level channels..responsePrefix now applies to cron announce delivery
  • responsePrefix: "auto" resolves to [AgentName] for cron delivery
  • Backward compatible: no prefix configured = no change in behavior

Security Impact (required)

  • New permissions/capabilities? (No)
  • Secrets/tokens handling changed? (No)
  • New/changed network calls? (No)
  • Command/tool execution surface changed? (No)
  • Data access scope changed? (No)

Repro + Verification

Environment

  • OS: macOS (tested locally)
  • Runtime/container: Node 25.5.0
  • Model/provider: N/A (unit tests)
  • Integration/channel (if any): Telegram, WhatsApp (tested in mocks)
  • Relevant config: messages.responsePrefix, channels..responsePrefix

Steps

  1. Configure cron job with delivery.mode: "announce" and messages.responsePrefix: "[BotName]"
  2. Run cron job that produces text output
  3. Observe message delivered to channel

Expected

Message prefixed with [BotName] (or [AgentName] if auto)

Actual

Before fix: Raw text without prefix
After fix: Text with prefix applied

Evidence

Attach at least one:

  • Failing test/log before + passing after
    Test Results
    5 new tests added in src/cron/isolated-agent.delivery-response-prefix.test.ts:
  1. applies global responsePrefix to announce delivery
    • Config: messages: { responsePrefix: "[CronBot]" }
    • Input: "Hello from cron job"
    • Expected output: "[CronBot] Hello from cron job"
    • Status: ✅ Passed
  2. resolves 'auto' responsePrefix to identity name
    • Config: messages: { responsePrefix: "auto" } with identity.name: "Thames"
    • Input: "Hello from cron job"
    • Expected output: "[Thames] Hello from cron job"
    • Status: ✅ Passed
  3. does not duplicate prefix if already present in response
    • Config: messages: { responsePrefix: "[CronBot]" }
    • Input: "[CronBot] Already has prefix"
    • Expected output: "[CronBot] Already has prefix" (no duplication)
    • Status: ✅ Passed
  4. works without responsePrefix configured
    • Config: {} (no responsePrefix)
    • Input: "Hello without prefix"
    • Expected output: "Hello without prefix" (unchanged)
    • Status: ✅ Passed
  5. applies channel-level responsePrefix
    • Config: messages: { responsePrefix: "[Global]" }, channels.telegram.responsePrefix: "[TelegramBot]"
    • Input: "Hello from cron job"
    • Expected output: "[TelegramBot] Hello from cron job" (channel overrides global)
    • Status: ✅ Passed

Test Output
✓ src/cron/isolated-agent.delivery-response-prefix.test.ts (5 tests) 40ms
Full test suite: 959 tests passed (274 cron tests)

  • Trace/log snippets
  • Screenshot/recording
  • Perf numbers (if relevant)

Human Verification (required)

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

  • Verified scenarios:
    • Global responsePrefix: "[BotName]" applies to announce delivery
    • responsePrefix: "auto" resolves to [AgentName]
    • Channel-level responsePrefix takes precedence over global
    • Prefix not duplicated if already present in response
    • Works without responsePrefix configured (backward compatible)
  • Edge cases checked:
    • Empty responsePrefix (no prefix applied)
    • Prefix already present in text (not duplicated)
  • What you did NOT verify:
    • Live cron job execution with real messaging channel
    • Integration with actual Telegram/WhatsApp APIs

Compatibility / Migration

  • Backward compatible? (Yes)
  • Config/env changes? (No)
  • Migration needed? (No)

Failure Recovery (if this breaks)

  • How to disable/revert this change quickly: Revert commit fe95144 or e8bfb3d
  • Files/config to restore: src/cron/isolated-agent/delivery-dispatch.ts
  • Known bad symptoms reviewers should watch for: Cron jobs failing to deliver messages, messages appearing without expected prefix

Risks and Mitigations

List only real risks for this PR. Add/remove entries as needed. If none, write None.

  • Risk: None
  • Mitigation: N/A

Built with OpenCode and MiniMax M2.5

Build prompt-

Implementation Plan: Fix Cron Announce Delivery responsePrefix (Issue #29600)

Issue Summary

When delivery.mode: "announce" sends a cron job's response text to a chat channel, the responsePrefix (e.g., [Thames]) is not applied to the message. Interactive replies correctly resolve responsePrefix: "auto" via resolveIdentityNamePrefix(), but cron delivery sends raw response text.


Baseline Verification

Before starting implementation, establish a test baseline:

  • Run pnpm test to establish current test status
  • Verify project builds with pnpm build
  • Verify type checks pass with pnpm tsgo
  • Verify lint/format passes with pnpm check

Step-by-Step Implementation Plan

Step 1: Fork and Branch Setup

  • Ensure you are on the fix/cron-announce-response-prefix branch created earlier
  • Verify branch is clean: git status

Step 2: Add Import for resolveResponsePrefix

  • Modify src/cron/isolated-agent/delivery-dispatch.ts
  • Add import: import { resolveResponsePrefix } from "../../agents/identity.js";
  • Verify TypeScript compiles: pnpm tsgo
  • Run relevant tests: pnpm test src/cron -- --run
  • Commit: git add src/cron/isolated-agent/delivery-dispatch.ts && git commit -m "imports: add resolveResponsePrefix for cron delivery"

Step 3: Fix deliverViaDirect Function (Direct Delivery Path)

  • Modify src/cron/isolated-agent/delivery-dispatch.ts
  • In deliverViaDirect function (around line 154), add responsePrefix resolution:
    const responsePrefix = resolveResponsePrefix(params.cfgWithAgentDefaults, params.agentId, {
      channel: delivery.channel,
      accountId: delivery.accountId,
    });
  • Apply prefix to synthesizedText before creating payloads (around line 159):
    const textWithPrefix = responsePrefix && synthesizedText && !synthesizedText.startsWith(responsePrefix)
      ? `${responsePrefix} ${synthesizedText}`
      : synthesizedText;
    const payloadsForDelivery =
      deliveryPayloads.length > 0
        ? deliveryPayloads
        : textWithPrefix
          ? [{ text: textWithPrefix }]
          : [];
  • Verify TypeScript compiles: pnpm tsgo
  • Run relevant tests: pnpm test src/cron -- --run
  • Commit: git add src/cron/isolated-agent/delivery-dispatch.ts && git commit -m "fix(cron): apply responsePrefix to direct delivery path"

Step 4: Fix deliverViaAnnounce Function (Announce Delivery Path)

  • Modify src/cron/isolated-agent/delivery-dispatch.ts
  • In deliverViaAnnounce function (around line 229), add responsePrefix resolution after resolving announceSessionKey:
    const responsePrefix = resolveResponsePrefix(params.cfgWithAgentDefaults, params.agentId, {
      channel: delivery.channel,
      accountId: delivery.accountId,
    });
  • Apply prefix to synthesizedText before passing to runSubagentAnnounceFlow (around line 311):
    const textWithPrefix = responsePrefix && synthesizedText && !synthesizedText.startsWith(responsePrefix)
      ? `${responsePrefix} ${synthesizedText}`
      : synthesizedText;
    const didAnnounce = await runSubagentAnnounceFlow({
      ...
      roundOneReply: textWithPrefix,
      ...
    });
  • Verify TypeScript compiles: pnpm tsgo
  • Run relevant tests: pnpm test src/cron -- --run
  • Commit: git add src/cron/isolated-agent/delivery-dispatch.ts && git commit -m "fix(cron): apply responsePrefix to announce delivery path"

Step 5: Add Test Coverage

  • Create new test file: src/cron/isolated-agent/delivery-dispatch.response-prefix.test.ts
  • Test cases to cover:
    • Cron announce delivery with global responsePrefix: "[AgentName]" applies prefix
    • Cron announce delivery with responsePrefix: "auto" resolves to [AgentName]
    • Cron direct delivery applies responsePrefix
    • Cron delivery does not duplicate prefix if already present
    • Cron delivery with no responsePrefix configured works as before
  • Run tests: pnpm test src/cron -- --run
  • Verify test coverage: pnpm test:coverage -- --run src/cron
  • Commit: git add src/cron/isolated-agent/delivery-dispatch.response-prefix.test.ts && git commit -m "test(cron): add responsePrefix coverage for delivery-dispatch"

Step 6: Full Verification

  • Run full test suite: pnpm test
  • Run build: pnpm build
  • Run type checks: pnpm tsgo
  • Run lint/format: pnpm check
  • Commit any final changes

Step 7: Push to Remote Fork

  • Add your fork as remote: git remote add fork [email protected]:<your-fork>/openclaw.git
  • Push branch to fork: git push -u fork fix/cron-announce-response-prefix
  • Verify push was successful

Notes

  • The fix uses the existing resolveResponsePrefix() function from src/agents/identity.ts which handles:
    • Per-channel account level configuration (L1)
    • Channel level configuration (L2)
    • Global messages.responsePrefix configuration (L4)
    • "auto" value which resolves to [AgentName] via resolveIdentityNamePrefix()
  • Only prepends prefix if:
    • responsePrefix is defined
    • synthesizedText exists
    • synthesizedText doesn't already start with the prefix (to avoid duplicates)

Related Files

  • Modified: src/cron/isolated-agent/delivery-dispatch.ts
  • Added: src/cron/isolated-agent/delivery-dispatch.response-prefix.test.ts
  • Reference: src/agents/identity.ts (contains resolveResponsePrefix)
  • Reference: src/agents/identity.per-channel-prefix.test.ts (example test patterns)

GitHub Issue

#29600

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Feb 28, 2026

Greptile Summary

Applied responsePrefix configuration to cron job delivery, fixing the issue where cron jobs with delivery.mode: "announce" were not applying the configured prefix (e.g., [Thames]) to messages.

Key changes:

  • Imported and used existing resolveResponsePrefix() function in both direct and announce delivery paths
  • Applied prefix consistently with the same logic used in interactive replies: only prepend if prefix is configured, text exists, and text doesn't already start with the prefix
  • Maintains backward compatibility: no prefix configured = no change in behavior

Implementation quality:

  • Follows existing codebase patterns (matches logic in src/auto-reply/reply/normalize-reply.ts)
  • Handles edge cases properly (auto resolution, duplication prevention, empty values)
  • Added 5 comprehensive tests covering global prefix, auto resolution, deduplication, no-prefix scenario, and channel-level override
  • Both delivery paths updated consistently

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk
  • Clean bug fix that reuses existing, well-tested infrastructure (resolveResponsePrefix). The changes are minimal, focused, and follow established patterns in the codebase. Comprehensive test coverage verifies the fix works as expected. No security concerns, no breaking changes, and maintains backward compatibility.
  • No files require special attention

Last reviewed commit: f5426fd

@sahilsatralkar
Copy link
Copy Markdown
Contributor Author

Hi @onutc , the CI checks-windows is failing because of likely Feishu integration bug that's unrelated to my PR. Would you be able to take a look ? Happy to help if needed

thomasxm pushed a commit to thomasxm/openclaw that referenced this pull request Feb 28, 2026
…cation, hook bridge

Comprehensive cron subsystem reliability overhaul addressing 9 open issues:

**Configurable retry policy (openclaw#29527, openclaw#29546)**
- Extract backoff logic into `retry-policy.ts` with configurable `cron.retryBackoff`
  schedule and `cron.stuckRunTimeoutMs` config fields
- Classify execution errors as transient (retry with backoff) or terminal
  (disable job immediately) to prevent futile retry storms
- Add Zod validation for new config fields

**Fix delivery status reporting (openclaw#29660)**
- `resolveDeliveryStatus` now accepts `deliveryAttempted` and returns
  "not-delivered" when delivery was attempted but not confirmed
- Add explicit `delivered: false` to all error paths in delivery-dispatch.ts
- Pass `deliveryAttempted` through the full result chain

**Configurable response prefix (openclaw#29687)**
- Add `responsePrefix` to CronServiceDeps (defaults to "Cron")
- Use dep-injected prefix for main session summary messages

**Strip directive tags from cron output (openclaw#29646)**
- Strip `[[reply_to_current]]`, `[[reply_to:<id>]]`, `[[audio_as_voice]]`
  from isolated cron run output before delivery

**Startup schedule preservation (openclaw#29690)**
- Use maintenance-only recompute in `start()` to avoid advancing past-due
  `nextRunAtMs` values for jobs that `runMissedJobs` intentionally skipped
- Clear `nextRunAtMs` for interrupted jobs so they get fresh schedule computation

**Internal hook bridge (openclaw#29682)**
- Add "cron" to `InternalHookEventType` with `CronExecutionHookEvent` type
- Bridge `emitJobFinished` to `triggerInternalHook` so extensions can react
  to cron job completions (alerting, chaining, etc.)
- Add `isCronExecutionEvent` type guard

**Per-job skip-if-active flag (openclaw#29659)**
- Add `skipIfRunActive?: boolean` to CronJob type for semantic overlap prevention

Tests: 290 cron tests + 21 new retry-policy tests pass.

Fixes: openclaw#29527, openclaw#29546, openclaw#29601, openclaw#29646, openclaw#29659, openclaw#29660, openclaw#29682, openclaw#29687, openclaw#29690
@Takhoffman
Copy link
Copy Markdown
Contributor

Thanks for the work and tests.

Closing this PR for now because the change is not aligned with this hygiene workstream and needs broader delivery-surface validation before merge (all delivery modes/channels and existing prefix behavior).

Please reopen as a narrowly scoped follow-up with explicit compatibility coverage if this is still needed.

@Takhoffman Takhoffman closed this Mar 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cron announce delivery does not apply responsePrefix

2 participants