Skip to content

feat(hooks): add session:patch hook event to listen for session changes#23910

Closed
graciegould wants to merge 1864 commits intoopenclaw:mainfrom
graciegould:feature/session-patch-hook
Closed

feat(hooks): add session:patch hook event to listen for session changes#23910
graciegould wants to merge 1864 commits intoopenclaw:mainfrom
graciegould:feature/session-patch-hook

Conversation

@graciegould
Copy link
Copy Markdown
Contributor

@graciegould graciegould commented Feb 22, 2026

Summary

  • Problem: No event hook exists for sessions.patch calls, preventing integrations from reacting to session property changes
  • Why it matters: Enables users to create custom hooks to stream session property changes such as models, labels, thinkingLevels, and other properties defined in typeSessionsPatchParamsSchema. This hook allow users to sync session metadata to their dashboards, databases, and workflows.
  • What changed: Added session:patch internal hook event that fires when sessions.patch RPC is called, following a similar pattern as message:* events (feat(hooks): bridge plugin message hooks to internal hooks system #9387)
  • What did NOT change: No changes to session storage or patch behavior. Event has zero overhead if no hooks listen to it.

Change Type (select all)

  • Bug fix
  • Feature
  • Refactor
  • Docs
  • Security hardening
  • Chore/infra

Scope (select all touched areas)

  • Gateway / orchestration
  • Skills / tool execution
  • Auth / tokens
  • Memory / storage
  • Integrations
  • API / contracts
  • UI / DX
  • CI/CD / infra

Linked Issue/PR

User-visible / Behavior Changes

Users can now create hooks that listen for session:patch events.

Hook event context:

{
  sessionEntry: SessionEntry,       // The updated session entry
  patch: SessionsPatchParams,       // The patch object with changes
  cfg: OpenClawConfig              // Current gateway config
}

Available patch fields (from SessionsPatchParams):

  • label, model, thinkingLevel, verboseLevel, reasoningLevel
  • responseUsage, elevatedLevel
  • execHost, execSecurity, execAsk, execNode
  • spawnedBy, spawnDepth, sendPolicy, groupActivation

Example hook:

import type { HookHandler } from "../../src/hooks/hooks.js";

const handler: HookHandler = async (event) => {
  if (event.type !== "session" || event.action !== "patch") return;
  
  const { patch } = event.context;
  
  if (patch.model) {
    console.log(`[session-patch] sesssion ${patch.key} changed model to: ${patch.model}`);
  }
  if (patch.label) {
    console.log(`[session-patch] sesssion ${patch.key} changed label to: ${patch.label}`);
  }
};

export default handler;

Common use cases:

  • Sync session metadata to external databases
  • Track model changes for cost allocation
  • Audit configuration changes for compliance
  • Build custom dashboards and UI tools
  • Trigger workflows on session state changes

Security Impact

  • No new permissions or capabilities
  • Hook fires after the existing security check (PR security(gateway): block webchat session mutators #20800) that blocks WebChat Control UI from patching sessions
  • Hook handlers run server-side alongside other hooks
  • Agents have the ability to edit sessions, this helps track those changes happening in the background.

Environment

  • OS: macOS 14.2.1
  • Runtime/container: Node.js 22.0.0, no container
  • Model/provider: any
  • Integration/channel (if any): Desktop app
  • Relevant config (redacted):

Evidence

Code changes:

  • src/gateway/server-methods/sessions.ts - 9 lines (hook trigger)
  • docs/automation/hooks.md - ~60 lines (documentation + examples)
  • src/gateway/server.sessions.gateway-server-sessions-a.e2e.test.ts - 150 lines (3 new tests)

Tests:

  • Hook fires with correct context (sessionEntry, patch, cfg)
  • WebChat clients blocked (hook doesn't fire)
  • Only fires after successful patch (not on failures)
  • All 20 e2e tests passing

Verified:

  • Session label/model/thinkingLevel changes trigger event correctly
  • Multiple hooks can listen without conflicts
  • WebChat security boundary preserved

Compatibility

  • Backward compatible: Yes - purely additive
  • Breaking changes: None
  • Migration needed: No

Risks

Risk: User hooks could call sessions.patch recursively

  1. User patches session label → hook fires
  2. Hook patches session label → hook fires again
  3. Hook patches session label → hook fires again
    Mitigation: Documented in hooks.md to avoid (same as message:sent pattern)

Risk: Expensive hook operations could slow patch responses
Mitigation: Hooks run async, don't block RPC response


Meta

  • This PR was prepared with assistance from Claude (AI), as encouraged in CONTRIBUTING.md. I have tested the changes locally and reviewed the code to ensure I understand all modifications.

Greptile Summary

This PR adds a session:patch internal hook event that fires when session properties are modified via the sessions.patch RPC. The implementation follows the established pattern for hook events (similar to message:sent/message:received from #9387) and correctly integrates with the existing security boundary that blocks WebChat clients from patching sessions (PR #20800).

Key changes:

  • Added hook trigger in src/gateway/server-methods/sessions.ts after successful patch (9 lines)
  • Comprehensive documentation in docs/automation/hooks.md with TypeScript types and examples (~60 lines)
  • Three well-structured e2e tests covering: hook context validation, WebChat blocking, and success-only triggering (150 lines)

Implementation is sound:

  • Hook fires only after successful patch (not on validation failures)
  • Security check happens before hook, so blocked clients never trigger events
  • Hook runs asynchronously without blocking the RPC response
  • Follows the same pattern as existing message hooks

Minor observation:
The PR description example code shows patch.key, but since event.sessionKey is available directly on the event object, that would be the cleaner approach. However, this is only in the PR description - the actual documentation example correctly avoids this pattern.

Confidence Score: 5/5

  • This PR is safe to merge with no risk - purely additive feature with comprehensive tests
  • The implementation is clean, follows established patterns, includes thorough test coverage (3 comprehensive e2e tests), and is backward compatible. The hook only fires after successful patches, respects existing security boundaries, and runs async without blocking responses. No changes to core session logic or storage.
  • No files require special attention

Last reviewed commit: f831d03

(2/5) Greptile learns from your feedback when you react with thumbs up/down!

Loading
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: android App: android app: ios App: ios app: macos App: macos channel: bluebubbles Channel integration: bluebubbles channel: discord Channel integration: discord channel: feishu Channel integration: feishu channel: googlechat Channel integration: googlechat channel: imessage Channel integration: imessage channel: irc 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 channel: tlon Channel integration: tlon channel: twitch Channel integration: twitch channel: voice-call Channel integration: voice-call channel: whatsapp-web Channel integration: whatsapp-web channel: zalo Channel integration: zalo channel: zalouser Channel integration: zalouser cli CLI command changes docker Docker and sandbox tooling docs Improvements or additions to documentation extensions: acpx extensions: anthropic extensions: byteplus extensions: cloudflare-ai-gateway extensions: copilot-proxy Extension: copilot-proxy extensions: deepseek extensions: device-pair extensions: diagnostics-otel Extension: diagnostics-otel extensions: duckduckgo extensions: fal extensions: huggingface extensions: kilocode extensions: kimi-coding extensions: llm-task Extension: llm-task extensions: lobster Extension: lobster extensions: memory-core Extension: memory-core extensions: memory-lancedb Extension: memory-lancedb extensions: minimax extensions: modelstudio extensions: moonshot extensions: nvidia extensions: open-prose Extension: open-prose extensions: openai extensions: phone-control extensions: qianfan extensions: qwen-portal-auth Extension: qwen-portal-auth extensions: synthetic extensions: talk-voice extensions: tavily extensions: together extensions: venice extensions: vercel-ai-gateway extensions: volcengine extensions: xiaomi gateway Gateway runtime scripts Repository scripts security Security documentation size: XL stale Marked as stale due to inactivity

Projects

None yet

Development

Successfully merging this pull request may close these issues.