openclaw pattern intermediate

Route Expensive Model Calls to Subagents with sessions_spawn

You don't need your best model for everything. You need it for the hard parts. The trick is wiring it so the orchestrator stays fast and the expensive model only shows up when it's actually earned.

Switching  Tony’s primary model from MiniMax to Claude Opus felt like an upgrade. Smarter responses, better reasoning, more nuanced task management. It was an upgrade — for about four hours.

Then the health monitor started restarting the Discord session every ten minutes.

The Problem

After switching to Claude Opus as the primary model, OpenClaw’s internal health monitor began flagging the Discord provider as stuck on a reliable ten-minute cycle:

[health-monitor] [discord:default] health-monitor: restarting (reason: stuck)

The restarts were clean —  Tony came back each time — but any in-progress response was dropped. Mid-sentence replies. Half-written task updates. The session logs told the story:

[EventQueue] Slow listener detected: DiscordMessageListener took 88.6 seconds
[EventQueue] Slow listener detected: InteractionEventListener took 65322ms

The health monitor runs on a 300-second interval with a 60-second grace window. Claude Opus responses were taking 65-90 seconds. The heartbeat couldn’t complete inside the grace window, so the monitor called it stuck and restarted.  Tony wasn’t broken — he was thinking. The health monitor couldn’t tell the difference.

Why This Happens

OpenClaw’s Discord provider and the agent’s LLM inference run in the same Node.js event loop. When an LLM call blocks for 65+ seconds, the heartbeat that the health monitor is waiting for doesn’t fire. The monitor waits 60 seconds, gives up, and restarts the provider.

This isn’t a bug in the health monitor — it’s working correctly. A truly stuck session looks the same from the outside as a session waiting on a slow model. The health monitor can’t distinguish between “processing a long inference” and “hung.”

The root fix is keeping the event loop responsive. The practical answer: don’t run slow models in the main session.

The Pattern: Orchestrator + Model-Specific Subagent

sessions_spawn accepts a model parameter that overrides the model for the spawned subagent run. The parent session stays on its primary model (fast, cheap); the subagent gets exactly the model it needs for the task.

{
  "tool": "sessions_spawn",
  "input": {
    "model": "anthropic/claude-opus-4-6",
    "label": "draft: pixelspace-manifesto",
    "task": "..."
  }
}

The subagent runs in an isolated agent:main:subagent:<uuid> session. It doesn’t share context with the parent — it gets a clean slate and whatever you put in task. After it completes, OpenClaw runs an announce step and posts the result back to the originating channel automatically.

The parent session returns { status: "accepted", runId, childSessionKey } immediately and moves on. The event loop stays clear.

Applied: A Two-Model Draft Skill

 Tony’s /draft skill was the right place to apply this. Content drafting is the one task that genuinely benefits from Opus — nuanced prose, voice matching, creative structure. Everything else (task management, morning briefings, quick responses) runs fine on MiniMax.

The skill is split into two responsibilities:

 Tony (MiniMax — the orchestrator):

  1. Determine the topic (inline input → ideas backlog → synthesis from recent notes)
  2. Read the 3-5 most relevant knowledge notes
  3. Read VOICE.md — Adam’s full writing voice profile
  4. Compose a self-contained brief with all context embedded
  5. Call sessions_spawn with model: anthropic/claude-opus-4-6
  6. Immediately reply: ✍️ Drafting "{topic}" with Opus — will post when done.

Opus subagent (the writer):

  • Receives topic, voice profile, and source material in the task string — no file access needed
  • Writes the draft to disk
  • Runs image generation
  • Runs backup.sh to commit and push
  • Returns a four-line summary that gets announced back to the channel

The key design principle: the subagent gets a tight brief with everything embedded. It doesn’t need to browse the workspace.  Tony did that work already.

## Voice Profile (follow precisely)
{full contents of VOICE.md pasted here}

## Reference Material
{relevant knowledge note excerpts pasted here}

## Writing Instructions
400-800 words, flowing prose, no headers...

This is the pattern: preparation is cheap (fast model reads files), execution is expensive (slow model writes prose). Keep them separate.

Wiring the Backup

One gotcha: make the post-writing steps explicit and unconditional in the task brief. The first run of this skill had the subagent skip backup.sh entirely after image generation failed — it saw a failure and apparently interpreted “continue” loosely.

The fix is unambiguous instruction ordering:

## After Writing

1. Attempt hero image generation (non-blocking — always continue to step 2 regardless):
   cd /path/to/site && node scripts/generate-image.mjs /path/to/draft.md

2. Run backup — mandatory, always run even if step 1 failed:
   /path/to/scripts/backup.sh

3. Reply with exactly four lines:
   ...

Subagents will skip steps that feel optional if the brief is ambiguous. Make the mandatory steps look mandatory.

The Config Change

Switching back to MiniMax as primary is a one-line edit in openclaw.json, and OpenClaw hot-reloads it without a restart:

{
  "agents": {
    "defaults": {
      "model": {
        "primary": "minimax/MiniMax-M2.5",
        "fallbacks": [
          "anthropic/claude-opus-4-6",
          "ollama/qwen3:4b"
        ]
      }
    }
  }
}

The health monitor restarts stopped immediately.  Tony’s Discord session has been stable since.

Key Takeaway

sessions_spawn’s model parameter lets you treat model selection as a routing decision, not a global setting. Keep your primary model fast enough that the event loop stays healthy. Spawn expensive models only for the tasks that justify the latency — and make sure those tasks run in subagents where the wait can’t destabilize your main session.

The orchestrator’s job is coordination, not computation. Give it a model that matches that job.

Resources

  • OpenClaw sessions_spawn docs — full parameter reference for spawning subagents, including model override, sandbox inheritance, and announce behavior.

FAQ

Why does a slow LLM response cause the OpenClaw health monitor to restart my Discord session?

OpenClaw's health monitor runs on a heartbeat interval (default: 300s) with a grace window (default: 60s). The Discord provider runs in the same Node.js event loop as your agent. When an LLM response takes longer than the grace window — Claude Opus regularly hits 65-90 seconds — the heartbeat can't complete in time and the monitor calls the session stuck, triggering a restart. The fix is either a faster primary model or a longer grace window.

How do I pass context to a subagent when spawning with sessions_spawn?

Embed it directly in the task string. sessions_spawn doesn't share file access or session history with the parent — the subagent gets a clean slate. Pack everything it needs (topic, voice guidelines, source material, instructions) into the task parameter as structured text. Think of it as writing a detailed brief, not a function call.

Can a subagent spawned with sessions_spawn commit and push to git?

Yes, if the exec tool is available (it is by default — subagents get the full tool set minus session tools). The subagent can run bash commands including git. On Linux with gh CLI installed as a credential helper, sourcing your .env file to set GITHUB_TOKEN before git push is enough. Make the backup step explicit and unconditional in your task brief — the subagent will skip optional steps if the brief is ambiguous.

What's the model parameter syntax for sessions_spawn?

Pass it as a string matching the provider/model-id format used in your openclaw.json. For example: model: 'anthropic/claude-opus-4-6'. Invalid values will error. Use agents_list to discover which agent ids are allowed if you're also routing to a different agentId.