diff --git a/packages/coding-agent/docs/sdk.md b/packages/coding-agent/docs/sdk.md index a2692e9d..ba0e656c 100644 --- a/packages/coding-agent/docs/sdk.md +++ b/packages/coding-agent/docs/sdk.md @@ -463,10 +463,14 @@ const { session } = await createAgentSession({ customTools: [{ tool: myTool }], }); -// Merge with discovered tools -const discovered = await discoverCustomTools(); +// Merge with discovered tools (share eventBus for tool.events communication) +import { createEventBus } from "@mariozechner/pi-coding-agent"; + +const eventBus = createEventBus(); +const discovered = await discoverCustomTools(eventBus); const { session } = await createAgentSession({ customTools: [...discovered, { tool: myTool }], + eventBus, }); // Add paths without replacing discovery @@ -528,10 +532,14 @@ const { session } = await createAgentSession({ hooks: [], }); -// Merge with discovered -const discovered = await discoverHooks(); +// Merge with discovered (share eventBus for pi.events communication) +import { createEventBus } from "@mariozechner/pi-coding-agent"; + +const eventBus = createEventBus(); +const discovered = await discoverHooks(eventBus); const { session } = await createAgentSession({ hooks: [...discovered, { factory: loggingHook }], + eventBus, }); // Add paths without replacing @@ -540,8 +548,12 @@ const { session } = await createAgentSession({ }); ``` +**Event Bus:** If hooks or tools use `pi.events` for inter-component communication, pass the same `eventBus` to `discoverHooks()`, `discoverCustomTools()`, and `createAgentSession()`. Otherwise each gets an isolated bus and events won't be shared. + Hook API methods: -- `api.on(event, handler)` - Subscribe to events +- `api.on(event, handler)` - Subscribe to lifecycle events +- `api.events.emit(channel, data)` - Emit to shared event bus +- `api.events.on(channel, handler)` - Listen on shared event bus - `api.sendMessage(message, triggerTurn?)` - Inject message (creates `CustomMessageEntry`) - `api.appendEntry(customType, data?)` - Persist hook state (not in LLM context) - `api.registerCommand(name, options)` - Register custom slash command @@ -778,10 +790,12 @@ const builtIn = getModel("anthropic", "claude-opus-4-5"); // Built-in only const skills = discoverSkills(cwd, agentDir, skillsSettings); // Hooks (async - loads TypeScript) -const hooks = await discoverHooks(cwd, agentDir); +// Pass eventBus to share pi.events across hooks/tools +const eventBus = createEventBus(); +const hooks = await discoverHooks(eventBus, cwd, agentDir); // Custom tools (async - loads TypeScript) -const tools = await discoverCustomTools(cwd, agentDir); +const tools = await discoverCustomTools(eventBus, cwd, agentDir); // Context files const contextFiles = discoverContextFiles(cwd, agentDir); @@ -951,6 +965,9 @@ discoverCustomTools discoverContextFiles discoverSlashCommands +// Event Bus (for shared hook/tool communication) +createEventBus + // Helpers loadSettings buildSystemPrompt diff --git a/packages/coding-agent/src/core/agent-session.ts b/packages/coding-agent/src/core/agent-session.ts index d99c9d03..59e6883d 100644 --- a/packages/coding-agent/src/core/agent-session.ts +++ b/packages/coding-agent/src/core/agent-session.ts @@ -865,6 +865,7 @@ export class AgentSession { this.sessionManager.newSession(options); this._steeringMessages = []; this._followUpMessages = []; + this._pendingNextTurnMessages = []; this._reconnectToAgent(); // Emit session_switch event with reason "new" to hooks @@ -1653,6 +1654,7 @@ export class AgentSession { await this.abort(); this._steeringMessages = []; this._followUpMessages = []; + this._pendingNextTurnMessages = []; // Set new session this.sessionManager.setSessionFile(sessionPath); @@ -1728,6 +1730,9 @@ export class AgentSession { skipConversationRestore = result?.skipConversationRestore ?? false; } + // Clear pending messages (bound to old session state) + this._pendingNextTurnMessages = []; + if (!selectedEntry.parentId) { this.sessionManager.newSession(); } else { diff --git a/packages/coding-agent/src/core/event-bus.ts b/packages/coding-agent/src/core/event-bus.ts index a82bfd09..a4c87b9f 100644 --- a/packages/coding-agent/src/core/event-bus.ts +++ b/packages/coding-agent/src/core/event-bus.ts @@ -16,9 +16,9 @@ export function createEventBus(): EventBusController { emitter.emit(channel, data); }, on: (channel, handler) => { - const safeHandler = (data: unknown) => { + const safeHandler = async (data: unknown) => { try { - handler(data); + await handler(data); } catch (err) { console.error(`Event handler error (${channel}):`, err); } diff --git a/packages/coding-agent/src/core/sdk.ts b/packages/coding-agent/src/core/sdk.ts index 022f7f92..5f21348a 100644 --- a/packages/coding-agent/src/core/sdk.ts +++ b/packages/coding-agent/src/core/sdk.ts @@ -203,14 +203,14 @@ export function discoverModels(authStorage: AuthStorage, agentDir: string = getD /** * Discover hooks from cwd and agentDir. + * @param eventBus - Shared event bus for pi.events communication. Pass to createAgentSession too. * @param cwd - Current working directory * @param agentDir - Agent configuration directory - * @param eventBus - Optional shared event bus (creates isolated bus if not provided) */ export async function discoverHooks( + eventBus: EventBus, cwd?: string, agentDir?: string, - eventBus?: EventBus, ): Promise> { const resolvedCwd = cwd ?? process.cwd(); const resolvedAgentDir = agentDir ?? getDefaultAgentDir(); @@ -230,14 +230,14 @@ export async function discoverHooks( /** * Discover custom tools from cwd and agentDir. + * @param eventBus - Shared event bus for tool.events communication. Pass to createAgentSession too. * @param cwd - Current working directory * @param agentDir - Agent configuration directory - * @param eventBus - Optional shared event bus (creates isolated bus if not provided) */ export async function discoverCustomTools( + eventBus: EventBus, cwd?: string, agentDir?: string, - eventBus?: EventBus, ): Promise> { const resolvedCwd = cwd ?? process.cwd(); const resolvedAgentDir = agentDir ?? getDefaultAgentDir();