openclaw - 💡(How to fix) Fix [Feature]: agent:turn:before / agent:turn:after hook events for pre-LLM-call middleware [1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#84051Fetched 2026-05-20 03:44:44
View on GitHub
Comments
1
Participants
2
Timeline
4
Reactions
1
Author
Timeline (top)
labeled ×2closed ×1commented ×1

Add per-turn hook events that fire before and after each LLM call, complementing the existing session:compact:* events.

Root Cause

Add per-turn hook events that fire before and after each LLM call, complementing the existing session:compact:* events.

Code Example

type AgentTurnBeforeHookContext = {
  transcript?: AnyMessage[];   // OR sessionFile: string (consistent with bundled compaction-notifier pattern)
  sessionKey: string;
  sessionId?: string;
  agentId?: string;
};

type AgentTurnBeforeHookEvent = InternalHookEvent & {
  type: "agent";
  action: "turn:before";       // fires after user prompt is received, before LLM call
  context: AgentTurnBeforeHookContext;
};

type AgentTurnAfterHookEvent = InternalHookEvent & {
  type: "agent";
  action: "turn:after";        // fires after LLM response, before delivery to channel
  context: AgentTurnBeforeHookContext & { response?: string };
};
RAW_BUFFERClick to expand / collapse

Summary

Add per-turn hook events that fire before and after each LLM call, complementing the existing session:compact:* events.

Problem to solve

Current internal hook events (session:compact:before/after, command:*, agent:bootstrap, etc.) only fire at OpenClaw's native compaction trigger or session boundaries. There is no event that fires per turn — i.e., after a user prompt is received but before the LLM call.

For middleware consumers building context compression, prompt-injection detection, or per-turn observability, this means:

  • Cost-saving compression cannot kick in until OpenClaw decides to compact. On 1M-token models like Claude Opus 4-7, native compaction rarely triggers during a typical session — users incur full token cost for the entire warm-up.
  • Injection-protection middleware cannot inspect user prompts before they reach the LLM.
  • Any per-turn enrichment (memory recall, RAG augmentation, etc.) requires invasive integration patterns instead of a clean hook surface.

The existing message:received / message:sent events are scoped to provider channels (WhatsApp, Slack, etc. — context has from, channelId, conversationId), not the agent → LLM pipeline. They cannot fill this gap.

Proposed solution

Add two new internal hook events:

type AgentTurnBeforeHookContext = {
  transcript?: AnyMessage[];   // OR sessionFile: string (consistent with bundled compaction-notifier pattern)
  sessionKey: string;
  sessionId?: string;
  agentId?: string;
};

type AgentTurnBeforeHookEvent = InternalHookEvent & {
  type: "agent";
  action: "turn:before";       // fires after user prompt is received, before LLM call
  context: AgentTurnBeforeHookContext;
};

type AgentTurnAfterHookEvent = InternalHookEvent & {
  type: "agent";
  action: "turn:after";        // fires after LLM response, before delivery to channel
  context: AgentTurnBeforeHookContext & { response?: string };
};

Implementation would add fire points in the agent turn lifecycle (around delegate-*.js / init-*.js).

Open design questions for the team:

  1. Naming: agent:turn:before vs agent:prompt:submit vs other preference?
  2. Transcript surface: inline transcript array, or sessionFile path (consistent with compaction-notifier)?
  3. Mutation semantics: observe-only, or hook output can replace context (similar to Claude Code's UserPromptSubmit stdout-as-context)?

Alternatives considered

  1. Use session:compact:before only (current approach). Limitation: fires only at compaction time. On large-context models (Opus 4-7 = 1M tokens), this is rarely — sometimes never — during a typical user session. Users pay for the full warm-up.

  2. Use message:received. Wrong layer — scoped to provider channels (WhatsApp, Slack, etc.), not the agent → LLM pipeline. Context shape (from, channelId) is for channels, not agent turns.

  3. Poll the session JSONL externally. Possible but inefficient, racy, and bypasses the hook system entirely. Defeats the purpose of hooks.

  4. Drop-in proxy via base_url. Works for some clients but blocked by SSRF allowlist (extensions/anthropic/openclaw.plugin.json restricts to api.anthropic.com). Override is a separate concern.

Impact

  • Affected users/systems: Any third-party hook author building middleware (compression, observability, injection detection, memory recall, prompt enrichment). Example: Compresh ships @compresh/openclaw-hook, currently subscribing to session:compact:before.
  • Severity: Workflow-blocking for the per-turn middleware category. Compaction-only firing limits middleware utility to a small fraction of the session lifetime on large-context models.
  • Frequency: Always, on every long-running session with large-context models (Opus 4-7, 1M-context Sonnet variants, etc.).
  • Consequence: Users pay full token cost for compressible context; prompt-injection cannot be filtered per-turn; per-turn observability requires invasive code paths.

Evidence/examples

  • Compresh OpenClaw hook: @compresh/openclaw-hook v0.1.5 — currently working but compaction-only. End-to-end clean-install walkthrough verified 69.5% token saving on a 31-message session (docs).
  • Claude Code parity: Anthropic's Claude Code CLI (v2.1.141+) ships 27 distinct hook events including UserPromptSubmit — fires per-turn, with transcript_path in stdin context. Reference: Claude Code Hooks. OpenClaw parity here would enable a shared middleware ecosystem across both platforms.
  • Large-context era: Anthropic shipped Opus 4-7 with 1M-token context. Native compaction triggers rarely in typical conversations, so compaction-coupled events fire too late for meaningful cost saving.

Additional information

No breaking change — pure additive. Existing hooks would continue to work unchanged. New event types added in internal-hook-types.ts, fired by the agent turn lifecycle.

Willing to contribute a PR if the team is open to this. Would appreciate guidance on:

  • Preferred event naming convention
  • Transcript inline vs. sessionFile path
  • Mutation semantics (observe-only vs. context replacement)

Happy to discuss here or on Discord — would be great to align on direction before drafting the implementation.

— Abdullah, founder Compresh

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - 💡(How to fix) Fix [Feature]: agent:turn:before / agent:turn:after hook events for pre-LLM-call middleware [1 comments, 2 participants]