Skip to content

feat(hooks): add lifecycle events for sessions and agent turns#6885

Closed
mverrilli wants to merge 2 commits intoopenclaw:mainfrom
mverrilli:session_events
Closed

feat(hooks): add lifecycle events for sessions and agent turns#6885
mverrilli wants to merge 2 commits intoopenclaw:mainfrom
mverrilli:session_events

Conversation

@mverrilli
Copy link
Contributor

@mverrilli mverrilli commented Feb 2, 2026

Summary

Adds comprehensive session lifecycle and agent execution hooks, enabling event-driven automation for session transitions, agent turns, memory management, and session maintenance operations.

Hooks Added

Session Lifecycle Events

  • session:start - Fires when a new persisted session begins (user-initiated /new//reset, or auto-recovery)
  • session:end - Fires when a persisted session terminates (all transition types: user commands, idle timeout, daily reset, auto-recovery)
  • session:reset - Fires after session:end during resets, providing transition context (old/new session IDs and entries)

Session Maintenance Events

  • session:compaction - Fires after session history compaction completes (inline or during memory flush)

Agent Execution Events

  • agent:reply - Fires after each agent turn completes with user input and/or assistant output
  • agent:flush - Fires when memory flush starts (context approaching token limit)
  • agent:bootstrap - Fires before workspace bootstrap files are injected (hooks can mutate bootstrap files)

Key Features

Complete Session Coverage

  • All transition types: user resets (/new, /reset), idle timeout, daily reset, and auto-recovery (compaction failure, role-ordering conflicts)
  • Correct execution order: Commands execute on the old session before transition occurs
  • Session guards: All session/agent hooks require valid sessionKey to prevent empty-key emissions

Rich Event Context

session:end and session:reset provide:

  • Full session entries (old and new)
  • Reset reason enum: user_command, idle_timeout, daily_reset, compaction_failure, role_ordering_conflict
  • Reason-specific metadata (idle duration, daily reset timestamps, failure details)

agent:reply provides:

  • User input and assistant output
  • Session ID, turn ID, sender ID

agent:flush provides:

  • Session ID
  • Context token count
  • Flush phase and reason

session:compaction provides:

  • Session ID
  • Trigger type (auto_compaction or memory_flush)
  • Updated compaction count
  • Pre-compaction token count

Hook Message System

  • Prepending: Hooks can push messages to event.messages array; messages are prepended to agent responses
  • Best-effort delivery: Messages sent when routing context is available
  • Command hooks always fire: command:* events emit regardless of persistence, but message delivery depends on routing context

Lifecycle Patterns

User-initiated reset (/new or /reset):

  1. command:new or command:reset fires immediately
  2. session:endsession:reset (if replacing existing session)
  3. session:start fires during agent turn

Auto-recovery reset (compaction failure/role conflicts):

  1. session:endsession:reset fire during reset
  2. session:start fires when agent turn runs

Idle timeout / Daily reset:

  1. session:endsession:reset fire on expiration
  2. session:start fires during next agent turn

Bug Fixes

  • Session entry preservation: Fixed memory flush no-flush paths to preserve session entries
  • Hook reliability: Improved message ordering and delivery consistency
  • Execution order: Fixed hook firing order (commands now execute on correct session before transitions)

Documentation

Comprehensive documentation in docs/hooks.md covering:

  • All event types with detailed context schemas
  • Lifecycle patterns for each scenario (user resets, timeouts, auto-recovery)
  • Hook creation guide with examples
  • Message delivery semantics
  • Event ordering guarantees

Testing

Verified event sequences for all session transitions:

  • User-initiated resets (/new, /reset)
  • Idle timeout expiration
  • Daily reset triggers
  • Auto-recovery scenarios (compaction failures, role conflicts)
  • Memory flush operations
  • Session compaction (inline and during flush)

Use Cases

  • Session snapshots: Save session context before resets
  • Audit trails: Log all commands, turns, and session transitions
  • External automation: Trigger workflows on session/agent events
  • Bootstrap customization: Inject dynamic content during agent startup
  • Memory monitoring: Track flush patterns and compaction frequency
  • Compliance: Record all agent interactions with full context

Changes: 883 additions, 77 deletions across 10 files
Main files: agent-runner.ts (383+), commands-session.ts (78+), session.ts (81+), get-reply.ts (78+), hooks.md (81+)


  • This was AI assisted. I understand what the code does.
  • Tested locally with comprehensive event verification.

@openclaw-barnacle openclaw-barnacle bot added the docs Improvements or additions to documentation label Feb 2, 2026
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5cecd487d7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

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, 5 comments

Edit Code Review Agent Settings | Greptile

@mverrilli mverrilli force-pushed the session_events branch 2 times, most recently from 17b3219 to 1a44949 Compare February 3, 2026 11:45
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

https://github.com/openclaw/openclaw/blob/1a44949f241e347280f5e90dab11e721ea0b570e/src/auto-reply/reply/agent-runner.ts#L352-L354
P2 Badge Emit lifecycle hooks even on final run outcomes

The early return for runOutcome.kind === "final" skips the new session:start and agent:reply hook emissions that happen later in this function. This matters in the reset/error paths where runAgentTurnWithFallback returns kind: "final" after resetting the session (e.g., compaction failure, role ordering conflict), so hooks never observe the session start or the warning reply that is actually sent. If hooks are meant to track all session starts and replies, consider emitting them before returning in this branch or handling the final outcome alongside the normal response flow.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@mverrilli
Copy link
Contributor Author

Re: Emit lifecycle hooks even on final run outcomes

Fixed this and similar edge cases. Also ran across a small bug that returned max tokens instead of actual tokens which is now fixed.

@codex review

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b24b09ae80

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6d58c0f210

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 503c092c7c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector
Copy link

Codex Review: Didn't find any major issues. Nice work!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

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, 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

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 3, 2026

Additional Comments (1)

src/auto-reply/reply/commands-session.ts
This can emit a command:stop hook with an empty sessionKey via abortTarget.key ?? params.sessionKey ?? "". If both are unset for a given command source, hook handlers that route/index by sessionKey won’t be able to associate the event.

Consider skipping hook emission when there’s no session key, or ensuring a stable fallback key is always available.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/auto-reply/reply/commands-session.ts
Line: 328:333

Comment:
This can emit a `command:stop` hook with an empty `sessionKey` via `abortTarget.key ?? params.sessionKey ?? ""`. If both are unset for a given command source, hook handlers that route/index by `sessionKey` won’t be able to associate the event.

Consider skipping hook emission when there’s no session key, or ensuring a stable fallback key is always available.

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

@openclaw-barnacle openclaw-barnacle bot added channel: bluebubbles Channel integration: bluebubbles channel: discord Channel integration: discord channel: googlechat Channel integration: googlechat channel: imessage Channel integration: imessage channel: line Channel integration: line channel: matrix Channel integration: matrix channel: mattermost Channel integration: mattermost channel: msteams Channel integration: msteams channel: nextcloud-talk Channel integration: nextcloud-talk channel: nostr Channel integration: nostr channel: signal Channel integration: signal channel: slack Channel integration: slack channel: telegram Channel integration: telegram labels Feb 3, 2026
@mverrilli
Copy link
Contributor Author

@greptileai review

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

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 5, 2026

Additional Comments (1)

src/auto-reply/reply/agent-runner.ts
Wrong session file resolver

resetSession() builds nextSessionFile using resolveSessionTranscriptPath(nextSessionId, agentId, sessionCtx.MessageThreadId) (agent-runner.ts:311-315), but elsewhere the code treats sessionFile as the session JSONL path resolved via resolveSessionFilePath(...) (and uses resolveSessionTranscriptPath(...) for transcript/derived paths). Storing a transcript path into SessionEntry.sessionFile can break later reads/writes that expect the JSONL session file (e.g., compaction/memory tooling that opens the session file). This should use the same resolver used in initSessionState when setting sessionEntry.sessionFile.

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/auto-reply/reply/agent-runner.ts
Line: 311:315

Comment:
**Wrong session file resolver**

`resetSession()` builds `nextSessionFile` using `resolveSessionTranscriptPath(nextSessionId, agentId, sessionCtx.MessageThreadId)` (`agent-runner.ts:311-315`), but elsewhere the code treats `sessionFile` as the session JSONL path resolved via `resolveSessionFilePath(...)` (and uses `resolveSessionTranscriptPath(...)` for transcript/derived paths). Storing a transcript path into `SessionEntry.sessionFile` can break later reads/writes that expect the JSONL session file (e.g., compaction/memory tooling that opens the session file). This should use the same resolver used in `initSessionState` when setting `sessionEntry.sessionFile`.

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

@mverrilli
Copy link
Contributor Author

@greptileai The current implementation is correct and matches initSessionState.

Evidence from session.ts:331-335:
if (!sessionEntry.sessionFile) {
sessionEntry.sessionFile = resolveSessionTranscriptPath(
sessionEntry.sessionId,
agentId,
ctx.MessageThreadId,
);
}

Both use the same pattern:

  • initSessionState (session.ts:331): Uses resolveSessionTranscriptPath to SET sessionFile
  • resetSession (agent-runner.ts:311): Uses resolveSessionTranscriptPath to SET sessionFile

The distinction:

  • resolveSessionTranscriptPath: Generates the path for a NEW session (used when SETTING)
  • resolveSessionFilePath: Reads the path from an EXISTING entry (used when READING)

The current code correctly uses resolveSessionTranscriptPath when creating a new session file path, consistent with how sessions are initialized throughout the codebase.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 5, 2026

You're correct. After reviewing session.ts:331-335, the implementation is indeed consistent with initSessionState. Both use resolveSessionTranscriptPath when initializing or resetting a session to set the sessionFile field.

The naming is a bit confusing (sessionFile stores the transcript path), but the pattern is applied consistently throughout the codebase. My earlier concern was misplaced.

@mverrilli
Copy link
Contributor Author

@greptileai review

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
Copy link
Contributor

@mverrilli do you need a hand with this? I need this PR for a few things im working through atm.

@mverrilli mverrilli force-pushed the session_events branch 2 times, most recently from a9b16ff to 167ee17 Compare February 5, 2026 05:08
@mverrilli
Copy link
Contributor Author

@mverrilli do you need a hand with this? I need this PR for a few things im working through atm.

Hey @vincentkoc I did some refactoring and have just been plugging away at edge cases and minor inconsistencies throughout the day although I let the code reviewer take me astray for a bit. You can test what I have checked in right now. I just fixed the ordering and alignment of events. Event hooks are on a "best effort" basis as implemented here. Let me know if you have any trouble.

@vincentkoc
Copy link
Contributor

@mverrilli i decided to take a more comprehensive approach as there are more deep routed issues with just adding the events to ensure full coverage, i decided to add changes in my own PR #9761

@mverrilli
Copy link
Contributor Author

@vincentkoc alright, yeah.. I was avoiding some of the bigger issues I noticed as I was trying to keep the PR focused on what I needed for my hooks. I did add compactions events today and typed them. was thinking to add the end phase as well.

@vincentkoc
Copy link
Contributor

@vincentkoc alright, yeah.. I was avoiding some of the bigger issues I noticed as I was trying to keep the PR focused on what I needed for my hooks. I did add compactions events today and typed them. was thinking to add the end phase as well.

Do you have a PR link open/closed for the compaction?

@mverrilli
Copy link
Contributor Author

@vincentkoc 009d561 That's the commit, I hadn't made a PR specifically for it, I just added it to this one.

mverrilli and others added 2 commits February 12, 2026 23:26
Add internal hook events for session and agent lifecycle transitions:

- session:start — fires when a new session begins (user-initiated or auto-recovery)
- session:end — fires when a session ends (reset, idle timeout, daily rollover)
- session:reset — fires on session transitions with old/new session context
- session:compaction — fires after auto-compaction or memory flush compaction
- agent:reply — fires after each agent turn with input/output/turnId
- agent:flush — fires before memory flush runs
- command:stop — fires when /stop is issued with persistence status

Shared helpers extracted to hook-helpers.ts to deduplicate emission logic
across agent-runner, followup-runner, get-reply, and command handlers.
Session reset reason tracking added to session.ts for hook context.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Add documentation for all new internal hook events including event names,
trigger conditions, context payloads, and delivery semantics.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@mverrilli
Copy link
Contributor Author

mverrilli commented Feb 13, 2026

Abandoning as there are many hook PRs now along with some just merged that overlaps with this.

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

Labels

docs Improvements or additions to documentation size: L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Comments