feat: broadcast agent events over control channel

This commit is contained in:
Peter Steinberger
2025-12-09 00:28:03 +01:00
parent 3114dfd39b
commit 40dd23337c
4 changed files with 161 additions and 12 deletions

31
src/infra/agent-events.ts Normal file
View File

@@ -0,0 +1,31 @@
export type AgentEventPayload = {
runId: string;
seq: number;
stream: "job" | "tool" | string;
ts: number;
data: Record<string, unknown>;
};
let seq = 0;
const listeners = new Set<(evt: AgentEventPayload) => void>();
export function emitAgentEvent(event: Omit<AgentEventPayload, "seq" | "ts">) {
const enriched: AgentEventPayload = {
...event,
seq: ++seq,
ts: Date.now(),
};
for (const listener of listeners) {
try {
listener(enriched);
} catch {
/* ignore */
}
}
}
export function onAgentEvent(listener: (evt: AgentEventPayload) => void) {
listeners.add(listener);
return () => listeners.delete(listener);
}

View File

@@ -9,6 +9,7 @@ import {
type HeartbeatEventPayload,
onHeartbeatEvent,
} from "./heartbeat-events.js";
import { onAgentEvent, type AgentEventPayload } from "./agent-events.js";
type ControlRequest = {
type: "request";
@@ -38,6 +39,7 @@ type Handlers = {
type ControlServer = {
close: () => Promise<void>;
broadcastHeartbeat: (evt: HeartbeatEventPayload) => void;
broadcastAgentEvent: (evt: AgentEventPayload) => void;
};
const DEFAULT_PORT = 18789;
@@ -91,6 +93,7 @@ export async function startControlChannel(
});
const stopHeartbeat = onHeartbeatEvent((evt) => broadcast("heartbeat", evt));
const stopAgent = onAgentEvent((evt) => broadcast("agent", evt));
const handleLine = async (socket: net.Socket, line: string) => {
if (!line) return;
@@ -184,6 +187,7 @@ export async function startControlChannel(
return {
close: async () => {
stopHeartbeat();
stopAgent();
await new Promise<void>((resolve) => server.close(() => resolve()));
for (const client of [...clients]) {
client.destroy();
@@ -194,5 +198,8 @@ export async function startControlChannel(
emitHeartbeatEvent(evt);
broadcast("heartbeat", evt);
},
broadcastAgentEvent: (evt: AgentEventPayload) => {
broadcast("agent", evt);
},
};
}