#15577: feat(hooks): add message:preprocessed hook event
stale
size: S
## Summary
Adds a `message_preprocessed` plugin hook event that fires **after** media understanding (transcription, image descriptions) and link understanding (summaries, previews), but **before** agent processing begins.
This completes the three-event message lifecycle:
1. `message:received` — raw inbound message
2. **`message:preprocessed`** — enriched with transcripts, media descriptions, link summaries *(this PR)*
3. `message:sent` — outbound reply
### Why?
Plugins that need enriched content (e.g., memory systems, analytics, logging) currently have to either re-process media themselves or hook into `message:received` which only has raw content. The `message:preprocessed` hook gives plugins access to the fully enriched message context with zero extra work.
### What's included
- `PluginHookMessagePreprocessedEvent` type with `content`, `rawContent`, `transcript`, and metadata
- `runMessagePreprocessed()` on the hook runner
- Internal hook event (`message:preprocessed`) for extension systems
- Hook fires in `getReplyFromConfig()` after `applyMediaUnderstanding()` and `applyLinkUnderstanding()`
### Builds on
PR #14196 which added `message:received` and `message:sent` hooks (shipped in 2026.2.12).
Addresses discussion #14539 (three-event lifecycle proposal).
<!-- greptile_comment -->
<h2>Greptile Overview</h2>
<h3>Greptile Summary</h3>
This PR introduces a new plugin hook event, `message_preprocessed`, intended to fire after media/link understanding but before agent processing. It updates the plugin hook type map (`src/plugins/types.ts`) and exposes a runner method (`src/plugins/hooks.ts`), then triggers the hook from `getReplyFromConfig` in `src/auto-reply/reply/get-reply.ts` (alongside an internal `message:preprocessed` hook event).
The main integration point is `getReplyFromConfig`, which now constructs a `PluginHookMessagePreprocessedEvent` and dispatches it via the global hook runner before continuing to command authorization/session/agent logic.
<h3>Confidence Score: 3/5</h3>
- Mostly safe to merge, but plugin hook payload correctness should be fixed to avoid breaking plugin expectations.
- The change is localized and adds a new hook without altering core agent behavior, but the emitted `message_preprocessed` payload currently has inconsistencies vs existing message hooks (content/rawContent source/normalization) and can produce invalid timestamps (NaN / mishandling 0). These are user-facing API contract issues for plugin authors and should be corrected before merging.
- src/auto-reply/reply/get-reply.ts
<sub>Last reviewed commit: aab320b</sub>
<!-- greptile_other_comments_section -->
<sub>(4/5) You can add custom instructions or style guidelines for the agent [here](https://app.greptile.com/review/github)!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#7545: feat(hooks): add message:received hook for pre-turn automation
by wangtian24 · 2026-02-02
83.1%
#19922: feat(hooks): add message:received and message:sent hook events
by NOVA-Openclaw · 2026-02-18
82.3%
#10109: feat(plugins): invoke message_received and message_sent hooks
by nezovskii · 2026-02-06
81.8%
#11597: feat(hooks): implement message:received hook
by gnufoo · 2026-02-08
81.0%
#11732: feat(plugins): add injectMessages to before_agent_start hook
by antra-tess · 2026-02-08
81.0%
#7580: feat: add message:received internal hook with prompt injection
by rodrigoschott · 2026-02-03
80.9%
#9859: feat(hooks): add message:transcribed and message:preprocessed inter...
by Drickon · 2026-02-05
79.6%
#16618: feat: bridge message lifecycle hooks to workspace hook system
by DarlingtonDeveloper · 2026-02-14
79.0%
#18004: feat: Add before_message_dispatch hook for blocking inbound messages
by Andy-Haigh · 2026-02-16
78.5%
#9906: feat: wire message_sending hook in outbound delivery
by teempai · 2026-02-05
78.5%