feat(FN-4824): forward task assignment events across remote runtime

Fusion-Task-Id: FN-4824
Fusion-Task-Lineage: 65f53a33-0939-4b6f-b62b-4a255ad14d9e
This commit is contained in:
Fusion (runfusion.ai)
2026-05-16 21:28:06 -07:00
committed by gsxdsm
parent 39a87a8925
commit 46a50c123f
7 changed files with 200 additions and 2 deletions

View File

@@ -325,6 +325,36 @@ describe("createSSE client cleanup", () => {
expect(getActiveSSEConnections()).toBe(baseline);
});
it("emits task:assigned SSE event from agent assignments", () => {
let onAssigned: ((agent: unknown, taskId: string) => void) | undefined;
const store = createMockStore();
const agentStore = {
on: vi.fn((event: string, handler: (agent: unknown, taskId: string) => void) => {
if (event === "agent:assigned") onAssigned = handler;
}),
off: vi.fn(),
};
const socket = new MockSocket();
const req = new EventEmitter() as Request & { query: Record<string, string>; socket: MockSocket };
req.query = { clientId: "assigned-client" };
req.socket = socket;
const res = new MockResponse(socket);
createSSE(store, undefined, undefined, undefined, undefined, agentStore as never)(
req,
res as unknown as Response
);
onAssigned?.({ id: "agent-1" }, "FN-1");
expect(res.write).toHaveBeenCalledWith(
expect.stringContaining("event: task:assigned")
);
expect(res.write).toHaveBeenCalledWith(
expect.stringContaining('"taskId":"FN-1"')
);
});
it("closes the connection when the outbound buffer exceeds the backpressure threshold", () => {
// Capture the task:created listener so we can fire a send after the
// socket buffer has been bloated past the threshold.

View File

@@ -389,6 +389,17 @@ export function createSSE(
const onUpdated = (task: unknown) => {
send(`event: task:updated\ndata: ${JSON.stringify(stripTaskListHeavyFields(task))}\n\n`);
};
const onTaskAssigned = (agent: unknown, taskId: string) => {
const payload = {
taskId,
agentId:
agent && typeof agent === "object" && "id" in agent
? String((agent as { id: unknown }).id)
: "",
assignedAt: new Date().toISOString(),
};
send(`event: task:assigned\ndata: ${JSON.stringify(payload)}\n\n`);
};
const onDeleted = (task: unknown) => {
send(`event: task:deleted\ndata: ${JSON.stringify(stripTaskListHeavyFields(task))}\n\n`);
};
@@ -713,6 +724,7 @@ export function createSSE(
agentStore.off("agent:updated", onAgentUpdated);
agentStore.off("agent:deleted", onAgentDeleted);
agentStore.off("agent:stateChanged", onAgentStateChanged);
agentStore.off("agent:assigned", onTaskAssigned);
}
if (messageStore) {
messageStore.off("message:sent", onMessageSent);
@@ -822,6 +834,7 @@ export function createSSE(
agentStore.on("agent:updated", onAgentUpdated);
agentStore.on("agent:deleted", onAgentDeleted);
agentStore.on("agent:stateChanged", onAgentStateChanged);
agentStore.on("agent:assigned", onTaskAssigned);
}
if (messageStore) {