#Context survival across compactions and session resets in multi-agent setups

1 messages · Page 1 of 1 (latest)

azure drift
#

Multi-agent Mac mini setup (Opus coordinator + Sonnet sub-agents via sessions_spawn). Three related questions about context persistence:

  1. When auto-compaction fires mid-conversation, the compacted summary loses granular details (specific file paths, exact tool call sequences, partial progress on multi-step tasks). Is there a recommended pattern beyond MEMORY.md for preserving critical in-flight state? Currently I write to working-state.md before major operations, but compaction can still hit between the write and the actual work.

  2. Sub-agents spawned via sessions_spawn get a task description but no access to the coordinator's conversation history or memory files unless explicitly told to read them. Is there a way to auto-inject specific files (like a shared context file) into every spawned sub-agent's context without repeating it in every task prompt? Something like agents.defaults.contextFiles or similar?

  3. Cron jobs run in isolated sessions with no memory of previous runs. My PostHog analytics cron, for example, can't compare today's numbers to yesterday's because it has no state. The workaround is writing results to a file and having the next run read it, but that's fragile. Is there an intended pattern for cron state persistence, or is the file-based approach the best we have?

Setup: OpenClaw 2026.2.19-2, Mac mini M4, Discord. MEMORY.md ~8KB, memory/ dir with ~40 files across topics/agents/daily.

subtle lynxBOT
#

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

pallid sedgeBOT
#
  1. Auto-compaction losing “in‑flight” details (paths, exact steps, partial progress)
    The intended pattern is still “write it to disk”, but there’s a built-in mechanism specifically to reduce the “oops compaction hit before I wrote it down” problem:
  • Pre-compaction “memory flush”: OpenClaw can run a silent turn before auto-compaction to tell the agent to persist durable state to files. Config is agents.defaults.compaction.memoryFlush (enabled by default). You can customize the prompt to explicitly write your working-state.md (and to preserve exact paths / TODOs / next tool calls).

Config example (from the config reference):

{
  agents: {
    defaults: {
      compaction: {
        reserveTokensFloor: 24000,
        memoryFlush: {
          enabled: true,
          softThresholdTokens: 6000,
#
          systemPrompt: "Session nearing compaction. Store durable memories now.",
          prompt: "Update working-state.md with exact file paths, current step, next commands; reply NO_REPLY if nothing to store."
        }
      }
    }
  }
}

Docs:

Extra mitigation if tool output bloat is what’s forcing compaction: look at context pruning (agents.defaults.contextPruning) to drop old tool results from in-memory context without rewriting history, which can delay compaction pressure. (Config ref shows it right after compaction.)

  1. Auto-injecting shared files into every sessions_spawn sub-agent
#

There isn’t an agents.defaults.contextFiles (or similar) knob today.

What you can do (and what tends to work well in practice):

  • Sub-agents automatically get AGENTS.md + TOOLS.md injected (and notably not MEMORY.md, USER.md, etc.). So if you want “always load shared context”, put that instruction in AGENTS.md (or in your agent’s systemPrompt) and have it point to a single small “context index” file, e.g. shared-context.md, that links to deeper files.

Example AGENTS.md rule of thumb:

  • “At the start of any run (including subagents), read ./shared-context.md and follow it. Keep it small; it can point to other files.”

Docs (this behavior is explicitly called out):

  1. Cron isolated jobs are stateless across runs — best practice for persistence?
    By design, isolated cron runs start with a fresh session id each run (“no prior conversation carry-over”). So there’s no built-in conversational memory continuity to rely on.

Intended patterns are basically:

#
  • If you need stateful comparisons (today vs yesterday): prefer cron → main-session systemEvent, and let the heartbeat handle it with full main-session context (and you can still use wakeMode: "now" to run immediately). This is the “precise trigger, stateful execution” combo.
  • Otherwise: persist state externally (API query over a time window, DB, KV store) or file-based state in the workspace (your current approach).

Docs:

If you want, paste your current memoryFlush config + a representative cron job JSON (or CLI command) and I can suggest a concrete “main-session cron + heartbeat” wiring that preserves your analytics baselines cleanly.

azure drift
#

Thanks for the detailed answers! Here's the follow-up with our current config:

pallid sedgeBOT
#

A couple things in your follow-up are already supported in-core (memory flush), and a couple are “by design” limitations (sub-agent / cron bootstrap injection).

1) Auto-compaction eating granular details → use pre-compaction memory flush (+ tune it)

OpenClaw has a built-in “memory flush” turn that runs before auto-compaction, specifically to persist durable state to disk:

What to tune if you want it to flush earlier (more safety margin):

  • reserveTokensFloor: increases the “headroom” OpenClaw protects
  • memoryFlush.softThresholdTokens: how far before the compaction threshold it triggers

Example:

#
{
  agents: {
    defaults: {
      compaction: {
        mode: "safeguard",
        reserveTokensFloor: 24000,
        memoryFlush: {
          enabled: true,
          softThresholdTokens: 6000
        }
      }
    }
  }
}
#

Also: compaction doesn’t delete the old details from disk; it just removes them from model-visible context. If you ever need the exact old tool call sequence again, you can recover it from the transcript (or via session tools), but the “don’t lose it” pattern is: write checkpoints to a file and have the agent re-read that file after any reset/compaction.

2) “Auto-inject shared files into every spawned sub-agent” → not currently a config knob

There isn’t an agents.defaults.contextFiles-style setting for sub-agents.

This is intentional: sub-agent and cron sessions only inject AGENTS.md + TOOLS.md (minimal bootstrap). It’s enforced in code via a hard allowlist.

Docs mention this limitation explicitly: https://docs.openclaw.ai/tools/subagents

Practical workarounds people use:

  • Put a small “shared context contract” in AGENTS.md like: “When you are a sub-agent, first read ./context/shared.md and follow it.”
  • Or use the orchestrator pattern (set agents.defaults.subagents.maxSpawnDepth: 2), where a depth-1 orchestrator can read main context and then pass only the relevant excerpt into each worker’s task.

If you want true automatic file injection for subagents, today that’s a code change (worth a GitHub issue/feature request).

3) Cron state across runs → intended pattern is externalized state (file/DB), not session memory

#

You’re not imagining it: isolated cron jobs mint a fresh sessionId per run, so they do not naturally “remember yesterday” from chat history.

Docs: https://docs.openclaw.ai/concepts/session (see the cron lifecycle note)
Cron jobs overview: https://docs.openclaw.ai/automation/cron-jobs

What you can do:

  • Best current pattern: write your own durable state (append-only JSONL is great) in the workspace, e.g. data/posthog-daily.jsonl, then read/compare each run.
  • You can also leverage the built-in cron run history log at ~/.openclaw/cron/runs/<jobId>.jsonl, but it’s auto-pruned, so it’s better as “debug/history” than your only source of truth.
  • If the task really benefits from conversational continuity, consider a cron with sessionTarget: "main" + a systemEvent payload (i.e., “poke the main session to do X”). That gives you the main session’s continuity, but you still should persist important numbers to disk if you care about robustness.

What I need from your “current config”

Paste (redact tokens) these blocks from ~/.openclaw/openclaw.json and I’ll tell you exactly what to change:

  • agents.defaults.compaction (and any per-agent overrides if you have them)
#
  • agents.defaults.subagents
  • cron
  • tools (if you’re using allow/deny lists)

Also helpful: what you see from /context detail in (a) your main session and (b) a sub-agent session — it will confirm exactly which files are being injected.

drifting cipher
#

No custom memoryFlush or contextPruning config (all defaults, never customized).

PostHog cron (lens-daily-posthog):

  • Schedule: 0 9 * * * Europe/Tallinn (09:00 daily)
  • Mode: isolated, Sonnet 4.6, 600s timeout
  • Posts daily analytics summary to Discord channels
  • Problem: can't compare today vs yesterday, each run is stateless
  • Current workaround: writes results to a file, next run reads it (fragile)

Questions:

  1. What should a custom memoryFlush prompt look like for a coordinator that manages working-state.md? Should it reference specific file paths?
  2. How exactly do I wire the PostHog cron as a main-session systemEvent with wakeMode: "now" instead of isolated? What does the cron config look like?
  3. For contextPruning, what are sensible defaults to start with?
pallid sedgeBOT
#

1) Custom memoryFlush prompt for a coordinator + working-state.md

Yes—make it reference specific, deterministic paths (relative to the workspace). The whole point of the flush turn is “don’t make the model guess what to persist.”

A good pattern is: update one “current snapshot” file (working-state.md) + append durable decisions to memory/YYYY-MM-DD.md.

Example:

{
  agents: {
    defaults: {
      compaction: {
        memoryFlush: {
          enabled: true,
          // trigger a bit earlier than default if you want
          softThresholdTokens: 6000,
#

          systemPrompt:
            "Pre-compaction flush. Capture in-flight state to disk so it survives compaction. Prefer NO_REPLY.",

          prompt: [
            "Pre-compaction memory flush for the coordinator.",
            "",
            "1) Update ./working-state.md with a concise snapshot:",
            "   - Goal / success criteria",
            "   - Current step + what is blocked",
            "   - Next 5 concrete actions",
            "   - Exact file paths involved",
            "   - Any important commands/tool calls that were planned but not executed yet",
            "   - Any spawned sub-agent runIds + what each is doing",
            "",
#
            "2) Append durable decisions/insights to memory/YYYY-MM-DD.md (append-only).",
            "",
            "If you wrote anything, finish with NO_REPLY.",
            "If nothing to store, reply NO_REPLY."
          ].join(\"\\n\")
        }
      }
    }
  }
}

Notes:

#

2) Wiring the PostHog cron as main-session systemEvent + wakeMode: "now"

Main-session cron jobs must be:

  • sessionTarget: "main"
  • payload.kind: "systemEvent"
  • No channel delivery block (delivery config is only supported for isolated; main only supports webhook delivery) — so it replies wherever that session normally replies.

The important lever is sessionKey:

  • If you omit it, OpenClaw targets the agent’s main session key.
  • If you want it to run “in the context of a specific Discord channel session”, set sessionKey to that session (easiest way: run /status in the destination channel and copy the sessionKey it prints).

Gateway RPC payload example (works via openclaw gateway call):

openclaw gateway call cron.add --params '{
#
  "name": "lens-daily-posthog (main session)",
  "schedule": { "kind": "cron", "expr": "0 9 * * *", "tz": "Europe/Tallinn" },
  "sessionTarget": "main",
  "wakeMode": "now",
  "sessionKey": "agent:main:discord:channel:123456789012345678",
  "payload": {
    "kind": "systemEvent",
    "text": "PostHog daily report: fetch today + yesterday metrics, compare, and post the delta summary."
  }
}'

Tradeoffs vs your current isolated job:

  • You lose per-job model/timeoutSeconds pinning (those are for agentTurn / isolated jobs).
  • You gain continuity (it runs inside that session’s transcript/context).
#

Docs on cron jobs: https://docs.openclaw.ai/automation/cron-jobs

Pragmatic tip for your original “stateless compare” problem: even if you stay isolated, you can often make it truly stateless by querying PostHog for (today range + yesterday range) every run, instead of reading yesterday from disk.


3) contextPruning: sensible starter defaults

contextPruning is off unless you enable it. When enabled, it only targets old toolResult messages (not user/assistant text), and it’s in-memory only (doesn’t rewrite transcripts).

I’d start with the built-in defaults, but increase the TTL so it doesn’t keep re-pruning during an active “coding session”:

{
  agents: {
    defaults: {
      contextPruning: {
#
        mode: "cache-ttl",
        ttl: "30m",              // default is 5m; 30–60m is a good first try
        keepLastAssistants: 3,   // keep recent conversational coherence

        // leave these as-is initially (they’re the effective defaults):
        softTrimRatio: 0.3,
        hardClearRatio: 0.5,
        minPrunableToolChars: 50000,

        softTrim: { maxChars: 4000, headChars: 1500, tailChars: 1500 },
        hardClear: { enabled: true, placeholder: "[Old tool result content cleared]" }

        // Optional: if pruning browser/canvas breaks your flows, exclude them:
        // tools: { deny: ["browser", "canvas"] },
      }