← Back to PRs

#13415: fix(hooks): bridge agent_end events to internal/workspace hooks

by mcaxtr open 2026-02-10 13:50 View on GitHub →
agents size: S trusted-contributor experienced-contributor
## Summary Fixes #10778 When plugin hooks fire `agent_end` via `hookRunner.runAgentEnd()`, the event is not bridged to the internal/workspace hook system (`triggerInternalHook`). This means workspace hooks registered on `agent:end` never fire. **Root cause**: `attempt.ts` only calls `hookRunner.runAgentEnd()` (plugin hooks) but never dispatches to `triggerInternalHook()` (internal hooks). The `agent:bootstrap` event already has this bridging via `bootstrap-hooks.ts`, but `agent:end` was missing the equivalent. **Fix**: - New `agent-end-hooks.ts` module (following the exact pattern of `bootstrap-hooks.ts`) that creates an `InternalHookEvent` with type `"agent"`, action `"end"`, and context containing messages, success, error, durationMs, agentId, and workspaceDir - Call `triggerAgentEndHook()` from `attempt.ts` as fire-and-forget, outside the `hookRunner?.hasHooks("agent_end")` guard so internal hooks fire regardless of plugin hooks ## Test plan - [x] 3 new tests in `agent-end-hooks.test.ts` (TDD: all 3 fail before, pass after) - [x] Fires `agent:end` internal hook with correct context - [x] Includes error in context when provided - [x] Does not throw when no handlers are registered - [x] All 18 existing `internal-hooks.test.ts` tests still pass - [x] `pnpm build && pnpm check` passes clean <!-- greptile_comment --> <h2>Greptile Overview</h2> <h3>Greptile Summary</h3> This change adds a new internal hook bridge for the `agent:end` lifecycle event. A new `src/agents/agent-end-hooks.ts` helper builds an `InternalHookEvent` (`type: "agent"`, `action: "end"`) with a context containing the final message snapshot, success/error, duration, agentId, and workspaceDir, and then calls `triggerInternalHook`. `runEmbeddedAttempt` (`src/agents/pi-embedded-runner/run/attempt.ts`) now invokes this bridge after plugin `agent_end` hooks run, ensuring workspace/internal handlers registered on `agent:end` fire even when no plugins are present. New Vitest coverage (`src/agents/agent-end-hooks.test.ts`) validates emitted context, error inclusion, and no-op behavior when no handlers are registered. <h3>Confidence Score: 5/5</h3> - This PR is safe to merge with minimal risk. - Changes are small and consistent with existing internal hook bridging patterns. The new fire-and-forget internal hook invocation properly attaches a `.catch()` to avoid unhandled promise rejections, and the new tests validate context wiring and no-handler behavior. - No files require special attention <!-- greptile_other_comments_section --> <!-- /greptile_comment -->

Most Similar PRs