#21874: feat: add agent_end hook with message injection support
size: L
## Summary
Implements message injection for `agent_end` hooks, enabling anti-rationalization gates and auto-continue patterns.
## Changes
### ✅ Completed
**1. Type Updates (src/plugins/types.ts)**
- Add `lastAssistantMessage?: string` to `PluginHookAgentEndEvent`
- Add `PluginHookAgentEndResult { continue?: boolean; message?: string }`
- Update handler map to return `PluginHookAgentEndResult | void`
**2. Hook Runner (src/plugins/hooks.ts)**
- Change `runAgentEnd` from fire-and-forget to result aggregation
- Collect results from all hooks in parallel
- Return first result with `continue: true`
- Maintains backwards compatibility (void-returning hooks work)
### 🚧 In Progress
**3. Message Extraction** (src/agents/pi-embedded-runner/run/attempt.ts)
- Extract last assistant message text from `messagesSnapshot`
- Pass to hook event as `lastAssistantMessage`
**4. Message Injection** (src/agents/pi-embedded-runner/run/attempt.ts)
- Await hook result instead of fire-and-forget
- If `result.continue === true`:
- Inject user message with `result.message`
- Trigger another agent run
**5. Tests**
- Unit test for hook return value aggregation
- E2E test with mock plugin forcing continuation
- Anti-rationalization example plugin
## Use Case
Enables plugins like anti-rationalization gates:
```typescript
api.on('agent_end', async (event, ctx) => {
const judgment = await reviewForRationalization(event.lastAssistantMessage);
if (judgment.incomplete) {
return {
continue: true,
message: "You are rationalizing incomplete work. Finish the task."
};
}
});
```
## Prior Art
- **pi-mono**: Already has `agent_end` event (OpenClaw's upstream)
- **Claude Code**: Has `Stop` hook with prompt-based judgment
- **Trail of Bits**: Uses this pattern for anti-rationalization gates
## Status
**Draft / WIP** - Types and hook runner done, message injection needs completion.
Pausing for review before implementing message injection logic.
Most Similar PRs
#11732: feat(plugins): add injectMessages to before_agent_start hook
by antra-tess · 2026-02-08
73.5%
#7580: feat: add message:received internal hook with prompt injection
by rodrigoschott · 2026-02-03
71.9%
#19565: feat: add agent lifecycle hook events (session, message, error)
by tag-assistant · 2026-02-17
71.1%
#13415: fix(hooks): bridge agent_end events to internal/workspace hooks
by mcaxtr · 2026-02-10
69.8%
#20067: feat(plugins): add before_agent_reply hook for message interception
by JoshuaLelon · 2026-02-18
69.4%
#20268: feat(hooks): emit subagent:complete internal hook event
by AytuncYildizli · 2026-02-18
68.9%
#16618: feat: bridge message lifecycle hooks to workspace hook system
by DarlingtonDeveloper · 2026-02-14
68.8%
#14873: [Feature]: Extend before_agent_start hook context with Model, Tools...
by akv2011 · 2026-02-12
68.0%
#15577: feat(hooks): add message:preprocessed hook event
by heybeaux · 2026-02-13
67.4%
#6630: feat(hooks): add agent:turn_start and agent:turn_end lifecycle events
by drdigital13 · 2026-02-01
67.4%