#9947: fix: preserve agent-events as standalone entry point for hook consumers
Cluster:
Hooks Enhancements and Fixes
## Summary
The tsdown migration in [`a03d852`](https://github.com/openclaw/openclaw/commit/a03d852d6526eee7d60c9499828b93b0ebc6a272) ("Migrate to tsdown, speed up JS bundling by ~10x") inadvertently broke internal hook handlers that subscribe to agent lifecycle events via `onAgentEvent`.
This PR adds `src/infra/agent-events.ts` as an explicit tsdown entry point, restoring `dist/infra/agent-events.js` as a standalone importable file.
## What changed and when
**Before `a03d852` (v2026.1.29 and earlier):**
The build used plain `tsc`, which compiled each source file individually:
```
src/infra/agent-events.ts → dist/infra/agent-events.js (standalone, ~3 KB)
```
Hook handlers could safely `import("dist/infra/agent-events.js")` and Node's ESM loader would return the **same cached module instance** the gateway had already loaded — sharing the same `listeners` Set. Events emitted by the gateway via `emitAgentEvent()` reached hook listeners correctly.
**After `a03d852` (v2026.2.x):**
The build uses tsdown, which bundles non-entry modules into shared chunks:
```
src/infra/agent-events.ts → inlined into dist/loader-BAZoAqqR.js (2.2 MB shared chunk, ~190 modules)
```
`dist/infra/agent-events.js` no longer exists. When a hook handler imports the shared chunk:
1. **Gateway crash:** The chunk contains ~190 other modules with module-level side effects (subsystem initialization, timers, state setup). Re-executing these crashes the gateway process within ~10 seconds.
2. **Separate module instance:** Even if the import didn't crash, Node creates a separate module instance with its own `listeners` Set. The hook's `onAgentEvent` listener never receives events from the gateway's `emitAgentEvent` calls.
## What this PR does
Adds a single entry to `tsdown.config.ts`:
```typescript
{
entry: "src/infra/agent-events.ts",
outDir: "dist/infra",
env,
fixedExtension: false,
platform: "node",
},
```
This restores `dist/infra/agent-events.js` as a standalone file that hook handlers can safely import, sharing the gateway's module instance via ESM caching.
## Who this affects
Any third-party internal hook that subscribes to agent lifecycle events (start/end/error), tool events, or assistant events via `onAgentEvent`. For example, [openclaw-mission-control](https://github.com/openclaw/openclaw-mission-control) uses this to sync agent activity to a dashboard.
The commit message for `a03d852` noted: *"This might break some existing users, but if it does, it's because they were using 'private' APIs."* — This PR makes `agent-events` an officially supported entry point rather than leaving it as an internal that hooks must reach into unsafely.
## Test plan
- [ ] Build with `pnpm build` and verify `dist/infra/agent-events.js` exists as a standalone file
- [ ] Verify gateway starts normally with a hook that imports `dist/infra/agent-events.js`
- [ ] Verify `onAgentEvent` listener receives lifecycle events from the gateway
- [ ] Verify existing entry points (`index.ts`, `entry.ts`, `plugin-sdk`, `extensionAPI`) are unaffected
<!-- greptile_comment -->
<h2>Greptile Overview</h2>
<h3>Greptile Summary</h3>
This PR updates the `tsdown` build configuration to treat `src/infra/agent-events.ts` as an explicit standalone entry point. That restores a dedicated `dist/infra/agent-events.js` artifact for hook consumers, instead of having `agent-events` inlined into a shared bundle chunk. The goal is to preserve stable ESM module caching behavior so hook handlers importing `dist/infra/agent-events.js` share the same module instance as the gateway and avoid re-executing unrelated bundled modules with side effects.
<h3>Confidence Score: 5/5</h3>
- This PR is safe to merge with minimal risk.
- Change is limited to adding a tsdown entry configuration; no runtime code paths are modified. The configuration is consistent with existing entries and should restore a standalone build artifact without affecting existing entry points.
- tsdown.config.ts
<!-- greptile_other_comments_section -->
<sub>(2/5) Greptile learns from your feedback when you react with thumbs up/down!</sub>
<!-- /greptile_comment -->
Most Similar PRs
#11817: fix(build): compile bundled hook handlers into dist
by AnonO6 · 2026-02-08
79.5%
#14746: fix(hooks): use globalThis for handler registry to survive bundler ...
by openperf · 2026-02-12
78.7%
#11470: fix: prevent gateway:watch race by passing --no-clean to tsdown
by apetresc · 2026-02-07
76.5%
#15571: feat: infrastructure foundation — hooks, model failover, sessions, ...
by tangcruz · 2026-02-13
76.0%
#8020: fix(build): use entry alias to output plugin-sdk in subdirectory
by coletebou · 2026-02-03
75.7%
#13415: fix(hooks): bridge agent_end events to internal/workspace hooks
by mcaxtr · 2026-02-10
75.6%
#7301: fix(hooks): use resolveAgentIdFromSessionKey instead of split(":")[0]
by tsukhani · 2026-02-02
75.5%
#23019: fix(hooks): use globalThis singleton for internal hooks handlers Map
by karmafeast · 2026-02-21
75.4%
#16915: fix: await compaction hooks with timeout to prevent cross-session d...
by maximalmargin · 2026-02-15
74.5%
#22723: fix: guard against undefined path in buildInjectedWorkspaceFiles — ...
by Fratua · 2026-02-21
74.4%