feat(coding-agent): add event bus for tool/hook communication (#431)

* feat(coding-agent): add event bus for tool/hook communication

Adds pi.events API enabling custom tools and hooks to communicate via
pub/sub. Tools can emit events, hooks can listen. Shared EventBus instance
created per session in createAgentSession().

- EventBus interface with emit() and on() methods
- on() returns unsubscribe function
- Threaded through hook and tool loaders
- Documented in hooks.md and custom-tools.md

* fix(coding-agent): wrap event handlers to catch errors

* docs: note async handler error handling for event bus

* feat(coding-agent): add sendMessage to tools, nextTurn delivery mode

- Custom tools now have pi.sendMessage() for direct agent notifications
- New deliverAs: 'nextTurn' queues messages for next user prompt
- Fix: hooks and tools now share the same eventBus (was isolated before)

* fix(coding-agent): nextTurn delivery should always queue, even when streaming
This commit is contained in:
Nico Bailon 2026-01-04 12:36:19 -08:00 committed by GitHub
parent 12805f61bd
commit 9c9e6822e3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 293 additions and 33 deletions

View file

@ -0,0 +1,33 @@
import { EventEmitter } from "node:events";
export interface EventBus {
emit(channel: string, data: unknown): void;
on(channel: string, handler: (data: unknown) => void): () => void;
}
export interface EventBusController extends EventBus {
clear(): void;
}
export function createEventBus(): EventBusController {
const emitter = new EventEmitter();
return {
emit: (channel, data) => {
emitter.emit(channel, data);
},
on: (channel, handler) => {
const safeHandler = (data: unknown) => {
try {
handler(data);
} catch (err) {
console.error(`Event handler error (${channel}):`, err);
}
};
emitter.on(channel, safeHandler);
return () => emitter.off(channel, safeHandler);
},
clear: () => {
emitter.removeAllListeners();
},
};
}