Skip to content

[Feature]: Unified pre-session-end hook needed — manual commands bypass memory flush #12162

@dial481

Description

@dial481

Summary

There is no single hook that fires before a session ends, regardless of how it ends. Auto-compaction runs memoryFlush. Manual /compact, /new, and /reset do not. The before_compaction hook exists in the type system but is never called. Plugin authors building memory persistence have no reliable way to save state before context is destroyed.

This isn't one bug — it's a pattern across multiple code paths.

Related Issues

The first three describe the same root cause: manual session-ending commands bypass the memory persistence infrastructure that auto-compaction uses.

#12590 outlines why memoryFlush does not fire at every auto-compact.

The Problem

The memoryFlush config works correctly for auto-compaction:

"compaction": {
  "memoryFlush": {
    "enabled": true,
    "prompt": "Save important context before compaction..."
  }
}

runMemoryFlushIfNeeded() in agent-runner-memory.ts handles this path.

But every manual path skips it:

Trigger Runs memoryFlush? Fires before_compaction hook?
Auto-compaction (token threshold) Yes No
/compact No No
/new No No
/reset No No

The before_compaction hook (runBeforeCompaction() in plugins/hooks.ts line 216, typed in plugins/types.ts lines 290, 470) has zero call sites anywhere. It's dead code.

What Plugin Authors Actually Need

One hook. Fires before any session-ending or context-destroying event. Doesn't matter if the user typed /compact, /new, /reset, or the token limit was hit.

Something like:

hooks: {
  before_session_end: async (context) => {
    // context.reason: 'compact' | 'new' | 'reset' | 'auto_compact'
    // Save state, flush memory, persist to external stores
  }
}

This lets plugins do the right thing without needing to know every possible code path that destroys context.

Suggested Fix

Minimum viable fix:

  • Call runMemoryFlushIfNeeded() before compactEmbeddedPiSession() in commands-compact.ts
  • Do the same in the /new and /reset handlers
  • Wire runBeforeCompaction() into all compaction paths, or remove it from the type definitions

Better fix:

  • Introduce a before_session_end hook that dispatches from every path that destroys or compresses context
  • Pass the trigger reason so plugins can decide how to respond
  • Deprecate the unwired before_compaction / after_compaction hooks or make them aliases

Impact

Any plugin relying on memoryFlush or before_compaction to persist context to external stores (memory systems, knowledge bases, logging) silently loses data on every manual command. Users have no indication their save-before-compact config is being ignored.

This is particularly painful for plugins that manage persistent memory across sessions, the entire value proposition breaks when 3 out of 4 session-ending paths skip the save step.

Versions

Observed in OpenClaw 2026.02.6, confirmed via source audit of:

  • src/auto-reply/reply/commands-compact.ts
  • src/auto-reply/reply/agent-runner-memory.ts
  • src/auto-reply/reply/memory-flush.ts
  • src/agents/pi-embedded-runner/compact.ts
  • src/plugins/hooks.ts
  • src/plugins/types.ts

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions