← Back to PRs

#16618: feat: bridge message lifecycle hooks to workspace hook system

by DarlingtonDeveloper open 2026-02-14 23:27 View on GitHub →
docs agents size: M
## Summary Bridges three message lifecycle events to the internal workspace hook system via `triggerInternalHook`, enabling workspace hooks (HOOK.md-based) to receive per-message events. ## Events Added | Event | When | Modifying | |-------|------|-----------| | `message:received` | Inbound message parsed | No | | `message:before` | Before agent processes | Yes (prependContext, systemPrompt) | | `message:sent` | After response sent | No | ## Motivation Workspace hooks currently only receive `command:new`, `command:stop`, and `gateway:startup`. Per-message events are dispatched through the plugin hook runner but never bridged to workspace hooks, making them inaccessible without building a full plugin. This enables use cases like: - Real-time transcript publishing to NATS/message buses - Context injection from external knowledge stores - Sentiment detection and adaptive prompting - Slack thread context injection (eliminates tool call round-trip) ## Implementation - Added `"message"` to `InternalHookEventType` union - Added `InternalHookResult` interface with `prependContext` and `systemPrompt` fields - Modified `triggerInternalHook` to collect and merge handler results (`prependContext` concatenated with `\n\n`, `systemPrompt` last-wins) - Three `triggerInternalHook` calls added to the inbound message handling path: - `dispatch-from-config.ts`: fire-and-forget `message:received` for channel messages - `attempt.ts`: awaited `message:received`, modifying `message:before`, fire-and-forget `message:sent` for agent runner - `message:before` follows the same modifying pattern as `before_agent_start` in the embedded runner - No loader changes needed — `src/hooks/loader.ts` already registers handlers for arbitrary event strings from HOOK.md metadata ## Test Plan - [x] Added 6 new unit tests for result-merging behavior in `internal-hooks.test.ts` - [x] `pnpm build` passes (pre-existing type errors in discord/memory modules unchanged) - [x] `pnpm test:fast` — all 4247 tests pass, 0 regressions - [x] Linter: 0 warnings, 0 errors Closes #7067, #15442, #14735, #12914, #12867, #8807 Partially addresses #7724, #11485, #10502 <!-- greptile_comment --> <h3>Greptile Summary</h3> This PR bridges three message lifecycle events (`message:received`, `message:before`, `message:sent`) from the agent runtime into the workspace hook system, enabling HOOK.md-based workspace hooks to observe and modify per-message events. The implementation adds result-merging capabilities to `triggerInternalHook` for modifying hooks, properly handles `prependContext` concatenation and last-wins `systemPrompt` merging, and includes comprehensive test coverage. The core changes are well-structured and follow existing patterns from the plugin hook system. The `message:before` hook correctly applies both `prependContext` and `systemPrompt` modifications to the session. Test coverage is thorough with 6 new tests covering result merging behavior. One issue was previously flagged but has since been resolved - the `systemPrompt` field from `message:before` is now properly applied via `applySystemPromptOverrideToSession` (attempt.ts:916-921). <h3>Confidence Score: 4/5</h3> - Safe to merge with one minor data mapping issue in the embedded runner path - The implementation is solid with proper type safety, comprehensive tests (all 4247 passing), and follows existing patterns. The main concern is the incorrect `from` field mapping in attempt.ts which uses `messageTo` instead of sender info, though this doesn't affect core functionality since the field is only used for hook context. The `systemPrompt` application that was previously flagged has been correctly implemented. - src/agents/pi-embedded-runner/run/attempt.ts requires attention for sender info mapping <sub>Last reviewed commit: 1a6da0d</sub> <!-- greptile_other_comments_section --> <!-- /greptile_comment -->

Most Similar PRs