Skip to content

feat(hooks): emit compaction lifecycle hooks#16788

Merged
jalehman merged 23 commits intoopenclaw:mainfrom
vincentkoc:vincentkoc-code/hooks-compaction-lifecycle
Mar 6, 2026
Merged

feat(hooks): emit compaction lifecycle hooks#16788
jalehman merged 23 commits intoopenclaw:mainfrom
vincentkoc:vincentkoc-code/hooks-compaction-lifecycle

Conversation

@vincentkoc
Copy link
Member

@vincentkoc vincentkoc commented Feb 15, 2026

Why

Compaction is a critical lifecycle boundary for observability and guardrails, but runtime hook visibility was incomplete. This PR adds explicit compaction lifecycle emissions so operators and plugins can reliably track pre/post compaction state. lobster-biscuit

Split Context

This PR was split from closed umbrella PR #9761: #9761.

Detailed Changes

  • Internal hook emissions added in compaction flow:
    • session:compact:before
    • session:compact:after
  • Plugin hook wiring added in compaction flow:
    • before_compaction
    • after_compaction
  • Hook metadata now includes counts/token-oriented context (without full message snapshots)
  • Added focused test coverage for compaction hook emission and payload shape

Related Links, Issues and Resolutions

Greptile Summary

Adds internal (session:compact:before, session:compact:after) and plugin (before_compaction, after_compaction) hook emissions to the compaction flow with count/token metadata. The implementation correctly passes context including sessionId, agentId, sessionKey, workspaceDir, and messageProvider to plugin hooks, addressing previous concerns about missing sessionId. The PR also includes a subtle bug fix changing cwd: resolvedWorkspace to cwd: effectiveWorkspace to properly respect sandbox settings.

  • Internal hooks emit structured events with message counts and token estimates before/after compaction
  • Plugin hooks receive metadata without full message snapshots (by design per TODOs Feature: Pre-compaction hook for memory preservation #7175, Feature request: session:compact hook event #9611)
  • Test coverage validates hook emission, payload structure, and edge cases (empty transcripts, missing sessionKey)
  • compactedCount correctly measures total reduction from validated transcript through both history limiting and compaction
  • Previous thread concerns about missing sessionId in plugin context have been resolved

Confidence Score: 4/5

  • Safe to merge with high confidence - well-tested hook implementation with proper error handling
  • The implementation is solid with comprehensive test coverage and proper error handling. Previous concerns about missing sessionId have been addressed. The change from resolvedWorkspace to effectiveWorkspace is a legitimate bug fix. Hook payloads intentionally omit full message snapshots per design decisions documented in TODOs. Loses one point only due to the documentation formatting issue already flagged in previous threads (missing blank line before heading).
  • No files require special attention - all changes are well-structured and tested

Last reviewed commit: 85d9ced

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 files reviewed, 3 comments

Edit Code Review Agent Settings | Greptile

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

@vincentkoc
Copy link
Member Author

@greptileai i addressed your one issue on duplicate comment in the docs/readme. The PR should be ready to merge, can you please update your PR score?

@steipete steipete closed this Feb 16, 2026
@steipete steipete reopened this Feb 17, 2026
@jalehman jalehman force-pushed the vincentkoc-code/hooks-compaction-lifecycle branch from 85d9ced to 95c3953 Compare March 5, 2026 23:19
@aisle-research-bot
Copy link

aisle-research-bot bot commented Mar 5, 2026

🔒 Aisle Security Analysis

We found 1 potential security issue(s) in this PR:

# Severity Title
1 🟡 Medium Compaction awaits hook handlers while holding session write lock and lane, enabling deadlock/DoS

1. 🟡 Compaction awaits hook handlers while holding session write lock and lane, enabling deadlock/DoS

Property Value
Severity Medium
CWE CWE-833
Location src/agents/pi-embedded-runner/compact.ts:525-688

Description

In compactEmbeddedPiSessionDirect, the session JSONL write lock is acquired before triggering internal hooks and plugin hooks. The updated implementation now awaits these hook handlers.

This creates an availability risk:

  • Long-running or hung hooks stall compaction and block the session’s lane (single-concurrency queue) and the session write lock, preventing other work on that session.
  • No timeouts/cancellation are applied to hook execution:
    • triggerInternalHook awaits each handler sequentially.
    • Plugin before_compaction / after_compaction use Promise.all internally and are awaited; a single never-resolving handler blocks forever.
  • Re-entrancy/deadlock risk: while inside the session lane, a hook that calls code paths that enqueue work back onto the same lane (e.g., calling compactEmbeddedPiSession instead of the Direct variant) can deadlock because lane concurrency is 1.

Vulnerable flow (lock held across awaited hooks):

  • Lock acquired at compact.ts:525-530
  • Awaited internal hook dispatch at compact.ts:658-668 and compact.ts:763-776
  • Awaited plugin hooks at compact.ts:674-688 and compact.ts:782-797

Vulnerable code:

const sessionLock = await acquireSessionWriteLock({
  sessionFile: params.sessionFile,
  maxHoldMs: resolveSessionLockMaxHoldFromTimeout({ timeoutMs: EMBEDDED_COMPACTION_TIMEOUT_MS }),
});
try {// ...
  await triggerInternalHook(hookEvent);// ...
  await hookRunner.runBeforeCompaction(...);// ...
  await triggerInternalHook(hookEvent);
  await hookRunner.runAfterCompaction(...);
} finally {
  await sessionLock.release();
}

Impact: a slow/malicious plugin (or internal hook) can cause denial of service, prolonged lock contention, and potential deadlocks within the agent’s command lanes.

Recommendation

Reduce the lock/lane hold time and add bounded hook execution.

Option A (preferred): move hooks outside the session write lock

  • Compute metrics under the lock, release it, then run hooks.

Option B: keep hooks but enforce timeouts and isolation

  • Wrap each hook dispatch in a timeout and continue compaction if exceeded.
  • Consider running hooks in a detached task (fire-and-forget) if hooks are observational.

Example timeout wrapper:

import { withTimeout } from "../../node-host/with-timeout.js";

async function runHookWithTimeout<T>(label: string, fn: () => Promise<T>): Promise<T | undefined> {
  try {
    return await withTimeout(fn, 2_000, label);
  } catch (err) {
    log.warn(`${label} timed out/failed`, { errorMessage: String(err) });
    return undefined;
  }
}

await runHookWithTimeout("session:compact:before", () => triggerInternalHook(hookEvent));
await runHookWithTimeout("before_compaction", () => hookRunner.runBeforeCompaction(event, ctx));

Option C: prevent lane re-entrancy deadlocks

  • Document and/or enforce that hooks must call compactEmbeddedPiSessionDirect only when already inside the session lane.
  • Alternatively, dispatch hook execution onto a different lane / microtask so it cannot synchronously enqueue back onto the same lane.

Analyzed PR: #16788 at commit cbea3da

Last updated on: 2026-03-06T03:08:58Z

@jalehman jalehman force-pushed the vincentkoc-code/hooks-compaction-lifecycle branch from afe2aa0 to d05f01f Compare March 5, 2026 23:32
@openclaw-barnacle openclaw-barnacle bot added the maintainer Maintainer-authored PR label Mar 6, 2026
@jalehman jalehman force-pushed the vincentkoc-code/hooks-compaction-lifecycle branch from d05f01f to cbea3da Compare March 6, 2026 02:45
@jalehman jalehman merged commit 71ec421 into openclaw:main Mar 6, 2026
28 checks passed
thinstripe pushed a commit to thinstripe/openclaw that referenced this pull request Mar 6, 2026
aashari pushed a commit to aashari/openclaw that referenced this pull request Mar 6, 2026
handsdiff pushed a commit to handsdiff/activeclaw that referenced this pull request Mar 6, 2026
jenawant pushed a commit to jenawant/openclaw that referenced this pull request Mar 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling docs Improvements or additions to documentation maintainer Maintainer-authored PR size: L

Projects

None yet

3 participants