diff --git a/src/web/public/constants.js b/src/web/public/constants.js index 1dc86bc1..5aa4dece 100644 --- a/src/web/public/constants.js +++ b/src/web/public/constants.js @@ -333,6 +333,7 @@ const SSE_EVENTS = { SUBAGENT_MESSAGE: 'subagent:message', SUBAGENT_TOOL_RESULT: 'subagent:tool_result', SUBAGENT_COMPLETED: 'subagent:completed', + AGENT_ARTIFACT: 'agent:artifact', // Images IMAGE_DETECTED: 'image:detected', diff --git a/src/web/server.ts b/src/web/server.ts index b5678534..e9d0eba5 100644 --- a/src/web/server.ts +++ b/src/web/server.ts @@ -59,6 +59,7 @@ import { type SubagentMessage, type SubagentToolResult, } from '../subagent-watcher.js'; +import { agentArtifactIndex, type AgentArtifact } from '../agent-artifact-index.js'; import { imageWatcher } from '../image-watcher.js'; import { attachmentRegistry, registerExternalAttachment } from '../attachment-registry.js'; import { TranscriptWatcher } from '../transcript-watcher.js'; @@ -248,6 +249,11 @@ export class WebServer extends EventEmitter { completed: (info: SubagentInfo) => void; error: (error: Error, agentId?: string) => void; } | null = null; + private artifactIndexHandlers: { + added: (artifact: AgentArtifact) => void; + updated: (artifact: AgentArtifact) => void; + error: (error: Error) => void; + } | null = null; private imageWatcherHandlers: { detected: (event: ImageDetectedEvent) => void; attachmentDetected: (event: AttachmentDetectedEvent) => void; @@ -412,6 +418,18 @@ export class WebServer extends EventEmitter { subagentWatcher.on('subagent:message', this.subagentWatcherHandlers.message); subagentWatcher.on('subagent:completed', this.subagentWatcherHandlers.completed); subagentWatcher.on('subagent:error', this.subagentWatcherHandlers.error); + + // Agent artifact index: attribute the files each subagent writes (from subagentWatcher's + // tool-call stream) and broadcast them as `agent:artifact` for the Agent Gallery view. + agentArtifactIndex.start(subagentWatcher); + this.artifactIndexHandlers = { + added: (artifact: AgentArtifact) => this.broadcast(SseEvent.AgentArtifact, artifact), + updated: (artifact: AgentArtifact) => this.broadcast(SseEvent.AgentArtifact, artifact), + error: (error: Error) => console.error('[AgentArtifactIndex] Error:', error.message), + }; + agentArtifactIndex.on('artifact:added', this.artifactIndexHandlers.added); + agentArtifactIndex.on('artifact:updated', this.artifactIndexHandlers.updated); + agentArtifactIndex.on('artifact:error', this.artifactIndexHandlers.error); } /** @@ -429,6 +447,13 @@ export class WebServer extends EventEmitter { subagentWatcher.off('subagent:error', this.subagentWatcherHandlers.error); this.subagentWatcherHandlers = null; } + if (this.artifactIndexHandlers) { + agentArtifactIndex.off('artifact:added', this.artifactIndexHandlers.added); + agentArtifactIndex.off('artifact:updated', this.artifactIndexHandlers.updated); + agentArtifactIndex.off('artifact:error', this.artifactIndexHandlers.error); + this.artifactIndexHandlers = null; + } + agentArtifactIndex.stop(); } /** diff --git a/src/web/sse-events.ts b/src/web/sse-events.ts index e851dfd6..edce5da2 100644 --- a/src/web/sse-events.ts +++ b/src/web/sse-events.ts @@ -212,6 +212,8 @@ export const SubagentProgress = 'subagent:progress' as const; export const SubagentMessage = 'subagent:message' as const; /** Subagent finished. */ export const SubagentCompleted = 'subagent:completed' as const; +/** A file artifact was attributed to a subagent (newly added or re-written). Powers the Agent Gallery. */ +export const AgentArtifact = 'agent:artifact' as const; // ─── Scheduled Runs ────────────────────────────────────────────────────────── @@ -442,6 +444,7 @@ export const SseEvent = { SubagentProgress, SubagentMessage, SubagentCompleted, + AgentArtifact, // Scheduled runs ScheduledCreated,