Skip to content

feat(hooks): emit compaction lifecycle hooks#12580

Closed
vincentkoc wants to merge 312 commits intoopenclaw:mainfrom
vincentkoc:vincentkoc-code/hooks-compaction-lifecycle
Closed

feat(hooks): emit compaction lifecycle hooks#12580
vincentkoc wants to merge 312 commits intoopenclaw:mainfrom
vincentkoc:vincentkoc-code/hooks-compaction-lifecycle

Conversation

@vincentkoc
Copy link
Member

@vincentkoc vincentkoc commented Feb 9, 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 Overview

Greptile Summary

This PR adds compaction lifecycle observability by emitting two new internal hook events (session:compact:before and session:compact:after) from the embedded runner compaction path (src/agents/pi-embedded-runner/compact.ts). It also wires corresponding plugin hooks (before_compaction / after_compaction) through the global plugin hook runner, providing token/message-count metadata and post-compaction summary fields.

To keep the emitted counts consistent with the actual transcript used for compaction, the implementation applies the sanitized/validated transcript to the live session prior to limiting, hook emission, and session.compact(). Focused Vitest coverage (src/agents/pi-embedded-runner/compact.hooks.test.ts) verifies internal + plugin hook emission, sessionKey fallback behavior, and the payload shape under sanitization and history limiting. Documentation (docs/automation/hooks.md) is updated to describe the new events and how handler registration keys are matched (${type}:${action}).

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk.
  • Changes are localized to the compaction path and are covered by focused tests that validate both internal and plugin hook emissions. Hook naming/registration aligns with the internal hook dispatcher’s ${type}:${action} matching, and the validated transcript is applied before metrics/hook emission so counts correspond to the compaction input.
  • No files require special attention

@openclaw-barnacle openclaw-barnacle bot added the agents Agent runtime and tooling label Feb 9, 2026
@vincentkoc vincentkoc marked this pull request as ready for review February 9, 2026 10:40
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.

1 file reviewed, 2 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.

1 file reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

@vincentkoc vincentkoc marked this pull request as draft February 9, 2026 16:44
@openclaw-barnacle openclaw-barnacle bot added the docs Improvements or additions to documentation label Feb 9, 2026
@vincentkoc
Copy link
Member Author

@greptileai Updated plugin hook contexts to use the same non-empty session key used for internal hooks. We should be good to go! Hooks are used in a few places so don't want to overly complicate the PR with more changes unless absolutley necessary.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 9, 2026

Looking at the diff, I can see the session key handling:

const missingSessionKey = !params.sessionKey || !params.sessionKey.trim();
const hookSessionKey = params.sessionKey?.trim() || `compact:${params.sessionId}`;

This ensures hookSessionKey is never empty - it either uses the provided sessionKey (if present and non-empty after trimming), or falls back to compact:${params.sessionId}.

Both internal hooks and plugin hooks now use this same hookSessionKey:

  • Internal: createInternalHookEvent("session", "compact:before", hookSessionKey, ...)
  • Plugin: { sessionKey: hookSessionKey, ... }

This looks good! The session key is now guaranteed to be non-empty for both hook types, which solves the observability/correlation issue from the previous review. The fallback to compact:${sessionId} provides a consistent identifier even when callers don't supply an explicit session key. ✅

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.

1 file reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

@vincentkoc vincentkoc marked this pull request as ready for review February 9, 2026 19:50
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.

1 file reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

@vincentkoc vincentkoc marked this pull request as draft February 9, 2026 20:00
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.

1 file reviewed, 1 comment

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.

2 files reviewed, 2 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.

1 file reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 9, 2026

Additional Comments (1)

src/agents/pi-embedded-runner/compact.ts
Sandbox cwd mismatch

createAgentSession is passed cwd: resolvedWorkspace here, but compaction runs after computing effectiveWorkspace (sandbox remap) and process.chdir(effectiveWorkspace). In sandboxed setups where effectiveWorkspace !== resolvedWorkspace (e.g. workspaceAccess: ro/none), any pi-coding-agent logic that relies on the provided cwd (not process.cwd()) will use the wrong directory, which can break compaction I/O or bypass the intended sandbox path. This should use effectiveWorkspace for consistency with the rest of the compaction flow.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/agents/pi-embedded-runner/compact.ts
Line: 404:406

Comment:
**Sandbox cwd mismatch**

`createAgentSession` is passed `cwd: resolvedWorkspace` here, but compaction runs after computing `effectiveWorkspace` (sandbox remap) and `process.chdir(effectiveWorkspace)`. In sandboxed setups where `effectiveWorkspace !== resolvedWorkspace` (e.g. `workspaceAccess: ro/none`), any pi-coding-agent logic that relies on the provided `cwd` (not `process.cwd()`) will use the wrong directory, which can break compaction I/O or bypass the intended sandbox path. This should use `effectiveWorkspace` for consistency with the rest of the compaction flow.

How can I resolve this? If you propose a fix, please make it concise.

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.

1 file reviewed, 1 comment

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.

2 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.

2 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

@vincentkoc vincentkoc marked this pull request as draft February 9, 2026 22:36
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.

2 files reviewed, 4 comments

Edit Code Review Agent Settings | Greptile

@vincentkoc
Copy link
Member Author

@greptileai addressed all your small issues by updating docs and hardening assertions. PR is ready to merge please update score.

@vincentkoc vincentkoc marked this pull request as ready for review February 9, 2026 23:11
@tomismeta
Copy link

Hi Vincent, nice work here. Curious about your thoughts on this PR I put together that addresses lifecycle hooks as well. #12082

@vincentkoc
Copy link
Member Author

vincentkoc commented Feb 10, 2026

Hi Vincent, nice work here. Curious about your thoughts on this PR I put together that addresses lifecycle hooks as well. #12082

@tomismeta your PR is toooo big. I had this issue before #9761 (comment) and had to break it out into much smaller PR's and go part by part. Due to the high volume of code changes unfortunatley I agree with much smaller changes.

Esp with API changes you propose at that scale could have impact you might not know. At quick glance the code coverage/tests volume for that amount of hooks seems off. I don't think all your hooks are wired and maybe just "placeholders".

@openclaw-barnacle
Copy link

Closing this PR because it looks dirty (too many unrelated commits). Please recreate the PR from a clean branch.

21 similar comments
@openclaw-barnacle
Copy link

Closing this PR because it looks dirty (too many unrelated commits). Please recreate the PR from a clean branch.

@openclaw-barnacle
Copy link

Closing this PR because it looks dirty (too many unrelated commits). Please recreate the PR from a clean branch.

@openclaw-barnacle
Copy link

Closing this PR because it looks dirty (too many unrelated commits). Please recreate the PR from a clean branch.

@openclaw-barnacle
Copy link

Closing this PR because it looks dirty (too many unrelated commits). Please recreate the PR from a clean branch.

@openclaw-barnacle
Copy link

Closing this PR because it looks dirty (too many unrelated commits). Please recreate the PR from a clean branch.

@openclaw-barnacle
Copy link

Closing this PR because it looks dirty (too many unrelated commits). Please recreate the PR from a clean branch.

@openclaw-barnacle
Copy link

Closing this PR because it looks dirty (too many unrelated commits). Please recreate the PR from a clean branch.

@openclaw-barnacle
Copy link

Closing this PR because it looks dirty (too many unrelated commits). Please recreate the PR from a clean branch.

@openclaw-barnacle
Copy link

Closing this PR because it looks dirty (too many unrelated commits). Please recreate the PR from a clean branch.

@openclaw-barnacle
Copy link

Closing this PR because it looks dirty (too many unrelated commits). Please recreate the PR from a clean branch.

@openclaw-barnacle
Copy link

Closing this PR because it looks dirty (too many unrelated commits). Please recreate the PR from a clean branch.

@openclaw-barnacle
Copy link

Closing this PR because it looks dirty (too many unrelated commits). Please recreate the PR from a clean branch.

@openclaw-barnacle
Copy link

Closing this PR because it looks dirty (too many unrelated commits). Please recreate the PR from a clean branch.

@openclaw-barnacle
Copy link

Closing this PR because it looks dirty (too many unrelated commits). Please recreate the PR from a clean branch.

@openclaw-barnacle
Copy link

Closing this PR because it looks dirty (too many unrelated commits). Please recreate the PR from a clean branch.

@openclaw-barnacle
Copy link

Closing this PR because it looks dirty (too many unrelated commits). Please recreate the PR from a clean branch.

@openclaw-barnacle
Copy link

Closing this PR because it looks dirty (too many unrelated commits). Please recreate the PR from a clean branch.

@openclaw-barnacle
Copy link

Closing this PR because it looks dirty (too many unrelated commits). Please recreate the PR from a clean branch.

@openclaw-barnacle
Copy link

Closing this PR because it looks dirty (too many unrelated commits). Please recreate the PR from a clean branch.

@openclaw-barnacle
Copy link

Closing this PR because it looks dirty (too many unrelated commits). Please recreate the PR from a clean branch.

@openclaw-barnacle
Copy link

Closing this PR because it looks dirty (too many unrelated commits). Please recreate the PR from a clean branch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling app: macos App: macos app: web-ui App: web-ui channel: bluebubbles Channel integration: bluebubbles channel: discord Channel integration: discord channel: imessage Channel integration: imessage channel: matrix Channel integration: matrix channel: nostr Channel integration: nostr channel: signal Channel integration: signal channel: slack Channel integration: slack channel: telegram Channel integration: telegram channel: tlon Channel integration: tlon channel: voice-call Channel integration: voice-call channel: whatsapp-web Channel integration: whatsapp-web channel: zalo Channel integration: zalo cli CLI command changes commands Command implementations docker Docker and sandbox tooling docs Improvements or additions to documentation extensions: lobster Extension: lobster extensions: memory-lancedb Extension: memory-lancedb gateway Gateway runtime scripts Repository scripts size: XL

Projects

None yet