#cron one shot isolated, message tool

1 messages · Page 1 of 1 (latest)

shut isle
#

We’re testing OpenClaw one-shot cron jobs that should send a WhatsApp message directly via the message tool.

Current setup:

• one-shot cron job
• sessionTarget: "isolated"
• payload uses agentTurn
• toolsAllow: ["message"]
• agent tool profile is already full
• cross-platform messaging is enabled
• session send policy was relaxed to allow cron sends

But the cron run still fails with:
tool message not available

We also tested this outside WhatsApp-specific logic, and isolated cron still could not access message.

Question:
Is this expected by design for isolated cron runs, or is this a bug / config issue?

More specifically:

  1. Can an isolated cron agentTurn directly use the message tool?
  2. Does isolated cron require a live chat route

/ delivery context before message becomes available?
3. If direct send from isolated cron is not supported, what is the recommended architecture for scheduled outbound messaging?

• trigger cron -> route to normal agent session
• persistent session instead of isolated
• or another official pattern?

Please answer based on official behavior, not workaround assumptions.

toxic nexusBOT
#

To help others find answers, you can mark your question as solved via Right click solution message -> Apps -> âś… Mark Solution

fluid prawnBOT
#

Short answer: on current intended behavior, an isolated agentTurn cron can use message. If you’re seeing tool message not available, that’s not the desired current behavior unless you’re on an older build, webhook delivery, or the model/runtime/tool policy path is stripping tools.

Answers:

  1. Yes, isolated cron can directly use message when delivery is chat/shared (announce or none). toolsAllow: ["message"] should keep it available, but it won’t resurrect it if the run policy disabled it earlier.

  2. It does not require a live inbound chat, but it needs either:

    • a resolved cron delivery route (channel/to/accountId), so “current chat” exists, or
    • an explicit channel + target in the message tool call.

    Isolated cron does not inherit ambient chat route/send policy from old session context by design.

  3. Recommended official pattern:

    • For normal scheduled outbound: use isolated cron with announce + explicit channel/to and let the cron runner deliver the final text.
    • If the agent itself must send: keep isolated cron, set explicit delivery target, and allow message.
    • If the job should build on chat/session history: use current or session:<id> instead of isolated.
shut isle
#

What exact runtime/config path can still cause the message tool to be stripped from an isolated cron agentTurn, even when:

• tool profile is full
• toolsAllow explicitly includes message
• explicit delivery target exists
• cross-provider messaging is enabled
• cron send-policy deny was removed

Is there another policy layer, runtime gate, or known bug in current builds that disables message specifically for isolated cron sessions?

fluid prawnBOT
#

Yep — there is another path.

For normal embedded/API-model isolated cron, current intended path is:

cron isolated run → resolveCronToolPolicy() → runEmbeddedPiAgent() → build tools → apply payload.toolsAllow

In that path, message should survive when:

  • delivery mode is not webhook
  • toolsAllow includes "message"
  • no earlier tool policy removed it

The exact things that can still strip it:

  1. delivery.mode: "webhook"
    Cron intentionally disables message there because webhook delivery has no chat target.
#
  1. agents.defaults.experimental.localModelLean: true
    This explicitly drops browser, cron, and message before toolsAllow is applied. toolsAllow is only a final allowlist; it does not resurrect a tool already removed.

  2. Any deny layer wins over toolsAllow
    Check all of these:

    • tools.deny
    • agents.list[].tools.deny
    • tools.byProvider.<provider/model>.deny or restrictive provider profile
    • agents.list[].tools.byProvider...
    • sandbox tool policy
    • group/channel tool policy, if the session resolves as a group/channel context
  3. CLI backend / bundled MCP path — this is the big suspect.
    If the cron model resolves to a CLI backend like Codex CLI / Claude CLI / Gemini CLI, OpenClaw does not inject tools through the embedded runtime. It exposes tools through the MCP loopback:

    runCliAgent() → MCP loopback → resolveGatewayScopedTools()

#

That path is separate from the isolated cron embedded path and does not get the cron forceMessageTool / toolsAllow policy in the same way. It also applies gateway.tools.deny. So if the error is exactly like Tool not available: message, I’d look here first.

  1. gateway.tools.deny: ["message"]
    Despite the docs mostly describing this around HTTP tool invoke, the current gateway scoped tool resolver also applies configured gateway.tools.deny on the loopback surface.

Cross-provider messaging is not the inventory gate. If disabled, it should fail at message execution/routing, not make the message tool disappear.

So: if this is an API/embedded model and localModelLean + deny layers are clear, this is likely a bug. If this is a CLI backend cron run, the current MCP-loopback policy path is the most likely cause.

Docs:
https://docs.openclaw.ai/automation/cron-jobs
https://docs.openclaw.ai/gateway/config-tools
https://docs.openclaw.ai/gateway/cli-backends

Please include openclaw --version and whether the resolved model is a CLI backend if filing: https://github.com/openclaw/openclaw/issues

shut isle
#

Thanks, this narrowed it down.

We have already ruled out these causes:

• not delivery.mode: "webhook"
• agents.defaults.experimental.localModelLean is not enabled
• no global tools.deny
• no provider-specific deny
• no per-agent provider deny
• target agent deny is only gateway
• toolsAllow: ["message"] is present
• explicit delivery target was tested
• isolated cron failed both for WhatsApp and for Telegram

So the main remaining suspect is that the isolated cron run is going through a different runtime/backend path, possibly the CLI/MCP loopback path instead of the embedded runtime path.

Question:

  1. How can we determine which runtime path this cron job is actually using?
    • embedded/API path: resolveCronToolPolicy() -> runEmbeddedPiAgent()

• or CLI/MCP path:
runCliAgent() -> MCP loopback -> resolveGatewayScopedTools()

  1. If it is going through the CLI/MCP path, what exact condition can cause message to be absent there, even though it is available in normal non-cron sessions?
  2. Is there any other known isolated-cron-specific tool gate that can remove message before payload.toolsAllow is applied?

If possible, please answer with the exact rule or config selector that decides the runtime branch.

fluid prawnBOT
#

Yep — the runtime branch is decided by the resolved provider, not by cron itself.

Exact rule in current source:

src/cron/isolated-agent/run-executor.ts

if (isCliProvider(providerOverride, params.cfgWithAgentDefaults)) {
  runCliAgent(...)
} else {
  runEmbeddedPiAgent(...)
}

And isCliProvider() returns true when the normalized provider matches:

  • a key in agents.defaults.cliBackends
#
  • a runtime-registered CLI backend
  • a plugin setup CLI backend

So if the resolved model is something like codex-cli/..., claude-cli/..., google-gemini-cli/..., or any custom provider key under agents.defaults.cliBackends, cron goes through CLI/MCP, not embedded.

To verify:

openclaw cron runs --id <job-id> --limit 1 --json

Check provider / model. Also tail logs while forcing the job:

openclaw logs --follow
openclaw cron run <job-id>
#

Look for:

  • CLI path: cli exec: provider=... and, with CLI debug enabled, cli env mcp: ... messageChannel=...
  • embedded path: embedded run start: ... messageChannel=...

If it’s CLI/MCP, the important difference is: cron payload.toolsAllow and cron’s forceMessageTool are not what populates MCP loopback tools. MCP loopback recomputes tools via:

runCliAgent() → MCP loopback → resolveGatewayScopedTools()

That path uses effective session/agent/provider/group/gateway policy. So the specific remaining selectors to check are:

gateway.tools.deny
agents.defaults.cliBackends.<provider>.bundleMcp

If bundleMcp is not enabled for that CLI backend, OpenClaw tools are not exposed to the CLI as tools. If gateway.tools.deny includes message, loopback strips it even if normal embedded sessions have it.

#

For embedded isolated cron, there isn’t another cron-specific message gate before payload.toolsAllow besides:

  • delivery.mode === "webhook" → disables message
  • general policy layers / localModelLean / provider profile / deny lists

Given what you ruled out, I’d strongly suspect CLI/MCP path + loopback tool resolution, or a build before the recent cron forceMessageTool fixes. Please grab openclaw --version too.

shut isle
#

Thanks, that helps a lot.

We checked the cron run metadata we already have, and the isolated cron runs show:

• provider: openai-codex
• model: gpt-5.4

So this does not appear to be going through a CLI backend path.

Our OpenClaw version is:

• 2026.4.15

Given that:

  1. does 2026.4.15 include the recent isolated-cron / forceMessageTool fixes?
  2. if not, is this likely a known bug fixed in a newer build?
  3. if yes, then what exact embedded-runtime condition could still remove message in this version?

At this point, embedded path seems more likely than CLI/MCP path.

Verdict gue

Sekarang posisi paling kuat:

• bukan config utama
• bukan sendPolicy
• bukan webhook
• bukan CLI backend path
• paling mungkin: **build/version

issue atau regression di embedded isolated cron**

fluid prawnBOT
#

Yep — with 2026.4.15, this is almost certainly the old isolated-cron behavior.

I checked the version/tag history: the relevant fixes are not in 2026.4.15.

The old 2026.4.15 rule was effectively:

disableMessageTool:
  deliveryContract === "cron-owned" ? true : deliveryRequested

And isolated cron defaulted to deliveryContract: "cron-owned".

So in 2026.4.15, isolated cron could disable message before payload.toolsAllow was applied. Then:

#
toolsAllow: ["message"]

only filtered the already-built tool list — it could not bring message back.

That matches your symptoms exactly:

  • provider openai-codex
  • model gpt-5.4
  • embedded/API path, not CLI/MCP
  • explicit target present
  • not webhook
  • toolsAllow: ["message"]
  • still tool message not available

The relevant fixes landed later: