speexor 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/dashboard/state.ts","../src/dashboard/server.ts"],"names":[],"mappings":"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AA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAAA,CAAA;AAqhCb,IAAA,GAAA,CAAI,SAAA,CAAU,GAAA,EAAK,EAAE,cAAA,EAAgB,aAAa,CAAA;AAClD,IAAA,GAAA,CAAI,IAAI,IAAI,CAAA;AAAA,EACd;AAAA,EAEA,KAAA,GAAc;AACZ,IAAA,IAAA,CAAK,MAAA,CAAO,MAAA,CAAO,IAAA,CAAK,IAAA,EAAM,MAAM;AAClC,MAAA,KAAA,CAAM,CAAA,2CAAA,EAA8C,IAAA,CAAK,IAAI,CAAA,CAAE,CAAA;AAAA,IACjE,CAAC,CAAA;AAED,IAAA,IAAA,CAAK,cAAA,GAAiB,YAAY,MAAM;AACtC,MAAA,MAAM,GAAA,GAAA,iBAAM,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY;AACnC,MAAA,KAAA,MAAW,MAAA,IAAU,KAAK,UAAA,EAAY;AACpC,QAAA,IAAI;AACF,UAAA,MAAA,CAAO,KAAA,CAAM,eAAe,GAAG;;AAAA,CAAM,CAAA;AAAA,QACvC,CAAA,CAAA,MAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF,GAAG,GAAK,CAAA;AAAA,EACV;AAAA,EAEA,IAAA,GAAa;AACX,IAAA,IAAI,IAAA,CAAK,cAAA,EAAgB,aAAA,CAAc,IAAA,CAAK,cAAc,CAAA;AAC1D,IAAA,KAAA,MAAW,MAAA,IAAU,KAAK,UAAA,EAAY;AACpC,MAAA,IAAI;AACF,QAAA,MAAA,CAAO,GAAA,EAAI;AAAA,MACb,CAAA,CAAA,MAAQ;AAAA,MAAC;AAAA,IACX;AACA,IAAA,IAAA,CAAK,aAAa,EAAC;AACnB,IAAA,IAAA,CAAK,OAAO,KAAA,EAAM;AAClB,IAAA,KAAA,CAAM,sBAAsB,CAAA;AAAA,EAC9B;AACF;AAEO,SAAS,qBAAA,CAAsB,SAAA,EAA6B,IAAA,GAAe,IAAA,EAAuB;AACvG,EAAA,OAAO,IAAI,eAAA,CAAgB,SAAA,EAAW,IAAI,CAAA;AAC5C","file":"chunk-GOGI3JQD.js","sourcesContent":["import type {\n AgentEvent,\n AgentSession,\n ApprovalItem,\n DecisionLogEntry,\n ProjectConfig,\n RuntimeSession,\n SpeexorState,\n TaskGraph,\n WorktreeSession,\n} from '../core/types.js'\nimport type { CostPanelData } from '../cost/panel.js'\nimport type { ActivityEntry } from '../instrumentation/activity-feed.js'\n\nexport class DashboardState {\n private state: SpeexorState = {\n sessions: [],\n worktrees: [],\n runtimes: [],\n projects: [],\n }\n\n private taskGraphs: Map<string, TaskGraph> = new Map()\n private agentEvents: Map<string, AgentEvent[]> = new Map()\n private approvals: ApprovalItem[] = []\n private activityFeed: ActivityEntry[] = []\n private costData: CostPanelData | null = null\n private decisionLogEntries: DecisionLogEntry[] = []\n private listeners: Array<() => void> = []\n\n getState(): SpeexorState {\n return { ...this.state }\n }\n\n setProjects(projects: ProjectConfig[]): void {\n this.state.projects = projects\n this.notify()\n }\n\n addSession(session: AgentSession): void {\n this.state.sessions.push(session)\n this.notify()\n }\n\n updateSession(sessionId: string, updates: Partial<AgentSession>): void {\n const idx = this.state.sessions.findIndex((s) => s.id === sessionId)\n if (idx !== -1) {\n const entry = this.state.sessions[idx]\n if (entry) {\n if (updates.id !== undefined) entry.id = updates.id\n if (updates.taskId !== undefined) entry.taskId = updates.taskId\n if (updates.provider !== undefined) entry.provider = updates.provider\n if (updates.status !== undefined) entry.status = updates.status\n if (updates.startedAt !== undefined) entry.startedAt = updates.startedAt\n if (updates.runtimeSessionId !== undefined) entry.runtimeSessionId = updates.runtimeSessionId\n this.notify()\n }\n }\n }\n\n removeSession(sessionId: string): void {\n this.state.sessions = this.state.sessions.filter((s) => s.id !== sessionId)\n this.notify()\n }\n\n addWorktree(worktree: WorktreeSession): void {\n this.state.worktrees.push(worktree)\n this.notify()\n }\n\n removeWorktree(sessionId: string): void {\n this.state.worktrees = this.state.worktrees.filter((w) => w.id !== sessionId)\n this.notify()\n }\n\n addRuntime(runtime: RuntimeSession): void {\n this.state.runtimes.push(runtime)\n this.notify()\n }\n\n removeRuntime(sessionId: string): void {\n this.state.runtimes = this.state.runtimes.filter((r) => r.id !== sessionId)\n this.notify()\n }\n\n updateTaskGraph(graph: TaskGraph): void {\n this.taskGraphs.set(graph.id, graph)\n this.notify()\n }\n\n getTaskGraph(id: string): TaskGraph | undefined {\n return this.taskGraphs.get(id)\n }\n\n getTaskGraphs(): TaskGraph[] {\n return Array.from(this.taskGraphs.values())\n }\n\n addAgentEvent(event: AgentEvent): void {\n const events = this.agentEvents.get(event.agentId) ?? []\n events.push(event)\n if (events.length > 500) events.shift()\n this.agentEvents.set(event.agentId, events)\n this.notify()\n }\n\n getAgentEvents(agentId?: string): AgentEvent[] {\n if (agentId) return this.agentEvents.get(agentId) ?? []\n const all: AgentEvent[] = []\n for (const events of this.agentEvents.values()) {\n all.push(...events)\n }\n return all.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime())\n }\n\n getAgentEventsByTask(taskId: string): AgentEvent[] {\n const result: AgentEvent[] = []\n for (const events of this.agentEvents.values()) {\n for (const e of events) {\n if (e.taskId === taskId) result.push(e)\n }\n }\n return result.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime())\n }\n\n addApproval(item: ApprovalItem): void {\n this.approvals.push(item)\n this.notify()\n }\n\n resolveApproval(id: string, status: 'approved' | 'rejected' | 'pending'): void {\n const item = this.approvals.find((a) => a.id === id)\n if (item) {\n item.status = status\n this.notify()\n }\n }\n\n getApprovals(): ApprovalItem[] {\n return [...this.approvals]\n }\n\n getPendingApprovals(): ApprovalItem[] {\n return this.approvals.filter((a) => a.status === 'pending')\n }\n\n updateActivityFeed(entry: ActivityEntry): void {\n this.activityFeed.push(entry)\n if (this.activityFeed.length > 1000) this.activityFeed.shift()\n this.notify()\n }\n\n getActivityFeed(limit?: number): ActivityEntry[] {\n const feed = [...this.activityFeed].reverse()\n return limit ? feed.slice(0, limit) : feed\n }\n\n updateCostData(data: CostPanelData): void {\n this.costData = data\n this.notify()\n }\n\n getCostData(): CostPanelData | null {\n return this.costData\n }\n\n addDecisionLogEntry(entry: DecisionLogEntry): void {\n this.decisionLogEntries.push(entry)\n this.notify()\n }\n\n getDecisionLogEntries(): DecisionLogEntry[] {\n return [...this.decisionLogEntries]\n }\n\n getDecisionLog(): DecisionLogEntry[] {\n return [...this.decisionLogEntries].sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())\n }\n\n onUpdate(listener: () => void): () => void {\n this.listeners.push(listener)\n return () => {\n this.listeners = this.listeners.filter((l) => l !== listener)\n }\n }\n\n private notify(): void {\n for (const listener of this.listeners) {\n try {\n listener()\n } catch {}\n }\n }\n\n toJSON(): Record<string, unknown> {\n return {\n ...this.state,\n taskGraphs: Array.from(this.taskGraphs.entries()).map(([, graph]) => ({\n ...graph,\n nodes: Array.from(graph.nodes.values()),\n })),\n agentEvents: Array.from(this.agentEvents.entries()).map(([agentId, events]) => ({\n agentId,\n events: events.map((e) => ({\n ...e,\n timestamp: e.timestamp.toISOString(),\n })),\n })),\n approvals: this.approvals.map((a) => ({\n ...a,\n createdAt: a.createdAt.toISOString(),\n expiresAt: a.expiresAt?.toISOString(),\n })),\n activityFeed: this.activityFeed.map((e) => ({\n ...e,\n timestamp: e.timestamp.toISOString(),\n })),\n costData: this.costData,\n decisionLogEntries: this.decisionLogEntries.map((e) => ({\n ...e,\n createdAt: e.createdAt.toISOString(),\n })),\n }\n }\n}\n","import { createServer, type IncomingMessage, type ServerResponse } from 'node:http'\r\nimport Debug from 'debug'\r\nimport type { SpeexorLifecycle } from '../core/lifecycle.js'\r\nimport type { AgentSession } from '../core/types.js'\r\nimport { DashboardState } from './state.js'\r\n\r\nconst debug = Debug('speexor:dashboard')\r\n\r\ninterface Route {\r\n method: string\r\n path: string\r\n handler: (req: IncomingMessage, res: ServerResponse, params: Record<string, string>) => void | Promise<void>\r\n}\r\n\r\nexport class DashboardServer {\r\n private server: ReturnType<typeof createServer>\r\n private lifecycle: SpeexorLifecycle\r\n private state: DashboardState\r\n private port: number\r\n private routes: Route[] = []\r\n private sseClients: ServerResponse[] = []\r\n private heartbeatTimer: ReturnType<typeof setInterval> | null = null\r\n\r\n constructor(lifecycle: SpeexorLifecycle, port: number = 7777) {\r\n this.lifecycle = lifecycle\r\n this.state = new DashboardState()\r\n this.port = port\r\n\r\n this.state.setProjects(lifecycle.getConfig().projects)\r\n\r\n lifecycle.eventBus.on('session:created', (data: unknown) => {\r\n const { session } = data as { session: AgentSession }\r\n this.state.addSession(session)\r\n this.broadcastSSE('agent-update', { session })\r\n })\r\n\r\n lifecycle.eventBus.on('session:completed', (data: unknown) => {\r\n const { sessionId } = data as { sessionId: string }\r\n this.state.removeSession(sessionId)\r\n this.broadcastSSE('agent-update', { sessionId, status: 'completed' })\r\n })\r\n\r\n lifecycle.eventBus.on('session:status-changed', (data: unknown) => {\r\n const { sessionId, status } = data as {\r\n sessionId: string\r\n status: string\r\n }\r\n this.state.updateSession(sessionId, {\r\n status: status as AgentSession['status'],\r\n })\r\n this.broadcastSSE('agent-update', { sessionId, status })\r\n })\r\n\r\n lifecycle.eventBus.on('task:created', (data: unknown) => {\r\n this.broadcastSSE('task-update', data)\r\n })\r\n\r\n lifecycle.eventBus.on('task:status-changed', (data: unknown) => {\r\n this.broadcastSSE('task-update', data)\r\n })\r\n\r\n lifecycle.eventBus.on('task:completed', (data: unknown) => {\r\n this.broadcastSSE('task-update', data)\r\n })\r\n\r\n lifecycle.eventBus.on('approval:created', (data: unknown) => {\r\n this.broadcastSSE('approval-update', data)\r\n })\r\n\r\n lifecycle.eventBus.on('approval:resolved', (data: unknown) => {\r\n this.broadcastSSE('approval-update', data)\r\n })\r\n\r\n lifecycle.eventBus.on('cost:recorded', (data: unknown) => {\r\n this.broadcastSSE('cost-update', data)\r\n })\r\n\r\n lifecycle.eventBus.on('agent:event', (data: unknown) => {\r\n this.broadcastSSE('activity', data)\r\n })\r\n\r\n this.setupRoutes()\r\n this.server = createServer((req, res) => this.handleRequest(req, res))\r\n }\r\n\r\n private setupRoutes(): void {\r\n this.routes = [\r\n {\r\n method: 'GET',\r\n path: '/api/status',\r\n handler: this.handleStatus.bind(this),\r\n },\r\n {\r\n method: 'GET',\r\n path: '/api/projects',\r\n handler: this.handleProjects.bind(this),\r\n },\r\n {\r\n method: 'GET',\r\n path: '/api/sessions',\r\n handler: this.handleSessions.bind(this),\r\n },\r\n {\r\n method: 'GET',\r\n path: '/api/health',\r\n handler: this.handleHealth.bind(this),\r\n },\r\n {\r\n method: 'GET',\r\n path: '/api/events',\r\n handler: this.handleSSE.bind(this),\r\n },\r\n {\r\n method: 'GET',\r\n path: '/api/task-graphs',\r\n handler: this.handleTaskGraphs.bind(this),\r\n },\r\n {\r\n method: 'GET',\r\n path: '/api/task-graphs/:id',\r\n handler: this.handleTaskGraphById.bind(this),\r\n },\r\n {\r\n method: 'GET',\r\n path: '/api/approvals',\r\n handler: this.handleApprovals.bind(this),\r\n },\r\n {\r\n method: 'POST',\r\n path: '/api/approvals/:id/approve',\r\n handler: this.handleApproveApproval.bind(this),\r\n },\r\n {\r\n method: 'POST',\r\n path: '/api/approvals/:id/reject',\r\n handler: this.handleRejectApproval.bind(this),\r\n },\r\n {\r\n method: 'GET',\r\n path: '/api/activity-feed',\r\n handler: this.handleActivityFeed.bind(this),\r\n },\r\n { method: 'GET', path: '/api/cost', handler: this.handleCost.bind(this) },\r\n {\r\n method: 'GET',\r\n path: '/api/eval/calibration',\r\n handler: this.handleEvalCalibration.bind(this),\r\n },\r\n {\r\n method: 'GET',\r\n path: '/api/task-graphs/:id/nodes/:nodeId',\r\n handler: this.handleTaskGraphNode.bind(this),\r\n },\r\n {\r\n method: 'GET',\r\n path: '/api/decision-log',\r\n handler: this.handleDecisionLog.bind(this),\r\n },\r\n {\r\n method: 'POST',\r\n path: '/api/approvals/:id/rollback',\r\n handler: this.handleRollbackApproval.bind(this),\r\n },\r\n ]\r\n }\r\n\r\n private async handleRequest(req: IncomingMessage, res: ServerResponse): Promise<void> {\r\n res.setHeader('Access-Control-Allow-Origin', '*')\r\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')\r\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type')\r\n\r\n if (req.method === 'OPTIONS') {\r\n res.writeHead(204)\r\n res.end()\r\n return\r\n }\r\n\r\n const url = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`)\r\n const pathname = url.pathname\r\n\r\n for (const route of this.routes) {\r\n if (req.method === route.method) {\r\n const params = this.matchPath(route.path, pathname)\r\n if (params !== null) {\r\n try {\r\n await route.handler(req, res, params)\r\n } catch (error) {\r\n this.sendJSON(res, 500, { error: String(error) })\r\n }\r\n return\r\n }\r\n }\r\n }\r\n\r\n if (pathname === '/' || pathname === '/dashboard') {\r\n this.serveDashboardHTML(res)\r\n return\r\n }\r\n\r\n this.sendJSON(res, 404, { error: 'Not found' })\r\n }\r\n\r\n private matchPath(pattern: string, pathname: string): Record<string, string> | null {\r\n const patternParts = pattern.split('/').filter(Boolean)\r\n const pathParts = pathname.split('/').filter(Boolean)\r\n if (patternParts.length !== pathParts.length) return null\r\n const params: Record<string, string> = {}\r\n for (let i = 0; i < patternParts.length; i++) {\r\n const pp = patternParts[i]\r\n if (pp && pp.startsWith(':')) {\r\n params[pp.slice(1)] = decodeURIComponent(pathParts[i] ?? '')\r\n } else if (pp !== pathParts[i]) {\r\n return null\r\n }\r\n }\r\n return params\r\n }\r\n\r\n private sendJSON(res: ServerResponse, status: number, data: unknown): void {\r\n res.writeHead(status, { 'Content-Type': 'application/json' })\r\n res.end(JSON.stringify(data))\r\n }\r\n\r\n private broadcastSSE(type: string, payload: unknown): void {\r\n const data = JSON.stringify({ type, payload })\r\n const message = `data: ${data}\\n\\n`\r\n for (const client of this.sseClients) {\r\n try {\r\n client.write(message)\r\n } catch {\r\n // client disconnected, filter on next cleanup\r\n }\r\n }\r\n }\r\n\r\n private handleStatus(_req: IncomingMessage, res: ServerResponse): void {\r\n this.sendJSON(res, 200, {\r\n status: this.lifecycle.getStatus(),\r\n projectCount: this.lifecycle.getConfig().projects.length,\r\n activeSessions: this.lifecycle.listSessions().length,\r\n uptime: process.uptime(),\r\n })\r\n }\r\n\r\n private handleProjects(_req: IncomingMessage, res: ServerResponse): void {\r\n this.sendJSON(res, 200, {\r\n projects: this.lifecycle.getConfig().projects,\r\n })\r\n }\r\n\r\n private handleSessions(_req: IncomingMessage, res: ServerResponse): void {\r\n this.sendJSON(res, 200, {\r\n sessions: this.lifecycle.listSessions(),\r\n worktrees: [],\r\n runtimes: [],\r\n })\r\n }\r\n\r\n private handleHealth(_req: IncomingMessage, res: ServerResponse): void {\r\n this.sendJSON(res, 200, {\r\n status: 'healthy',\r\n timestamp: new Date().toISOString(),\r\n memory: process.memoryUsage(),\r\n })\r\n }\r\n\r\n private handleSSE(req: IncomingMessage, res: ServerResponse): void {\r\n res.writeHead(200, {\r\n 'Content-Type': 'text/event-stream',\r\n 'Cache-Control': 'no-cache',\r\n Connection: 'keep-alive',\r\n 'Access-Control-Allow-Origin': '*',\r\n })\r\n\r\n const costData = this.state.getCostData()\r\n const initPayload = {\r\n status: this.lifecycle.getStatus(),\r\n projects: this.lifecycle.getConfig().projects,\r\n sessions: this.lifecycle.listSessions(),\r\n taskGraphs: this.state.getTaskGraphs().map((g) => ({\r\n ...g,\r\n nodes: Array.from(g.nodes.values()),\r\n })),\r\n approvals: this.state.getPendingApprovals(),\r\n allApprovals: this.state.getApprovals(),\r\n activityFeed: this.state.getActivityFeed(50),\r\n costData,\r\n decisionLog: this.state.getDecisionLog().slice(0, 50),\r\n }\r\n\r\n res.write(`data: ${JSON.stringify({ type: 'init', payload: initPayload })}\\n\\n`)\r\n\r\n this.sseClients.push(res)\r\n\r\n req.on('close', () => {\r\n this.sseClients = this.sseClients.filter((c) => c !== res)\r\n })\r\n }\r\n\r\n private handleTaskGraphs(_req: IncomingMessage, res: ServerResponse): void {\r\n const graphs = this.state.getTaskGraphs().map((g) => ({\r\n ...g,\r\n nodes: Array.from(g.nodes.values()),\r\n }))\r\n this.sendJSON(res, 200, { taskGraphs: graphs })\r\n }\r\n\r\n private handleTaskGraphById(_req: IncomingMessage, res: ServerResponse, params: Record<string, string>): void {\r\n const graph = this.state.getTaskGraph(params['id'] ?? '')\r\n if (!graph) {\r\n this.sendJSON(res, 404, { error: 'Task graph not found' })\r\n return\r\n }\r\n this.sendJSON(res, 200, {\r\n ...graph,\r\n nodes: Array.from(graph.nodes.values()).sort((a, b) => a.depth - b.depth || a.createdAt.getTime() - b.createdAt.getTime()),\r\n })\r\n }\r\n\r\n private handleApprovals(_req: IncomingMessage, res: ServerResponse): void {\r\n this.sendJSON(res, 200, {\r\n pending: this.state.getPendingApprovals(),\r\n all: this.state.getApprovals(),\r\n })\r\n }\r\n\r\n private handleApproveApproval(_req: IncomingMessage, res: ServerResponse, params: Record<string, string>): void {\r\n const id = params['id'] ?? ''\r\n this.state.resolveApproval(id, 'approved')\r\n this.broadcastSSE('approval-update', {\r\n approvalId: id,\r\n status: 'approved',\r\n })\r\n this.sendJSON(res, 200, { status: 'approved', id })\r\n }\r\n\r\n private handleRejectApproval(_req: IncomingMessage, res: ServerResponse, params: Record<string, string>): void {\r\n const id = params['id'] ?? ''\r\n this.state.resolveApproval(id, 'rejected')\r\n this.broadcastSSE('approval-update', {\r\n approvalId: id,\r\n status: 'rejected',\r\n })\r\n this.sendJSON(res, 200, { status: 'rejected', id })\r\n }\r\n\r\n private handleActivityFeed(_req: IncomingMessage, res: ServerResponse): void {\r\n const feed = this.state.getActivityFeed(100)\r\n this.sendJSON(res, 200, { entries: feed })\r\n }\r\n\r\n private handleCost(_req: IncomingMessage, res: ServerResponse): void {\r\n const data = this.state.getCostData()\r\n this.sendJSON(\r\n res,\r\n 200,\r\n data ?? {\r\n totalCost: 0,\r\n byProvider: [],\r\n byProject: [],\r\n recentEntries: [],\r\n budgetExceeded: false,\r\n },\r\n )\r\n }\r\n\r\n private handleTaskGraphNode(_req: IncomingMessage, res: ServerResponse, params: Record<string, string>): void {\r\n const graphId = params['id'] ?? ''\r\n const nodeId = params['nodeId'] ?? ''\r\n const graph = this.state.getTaskGraph(graphId)\r\n if (!graph) {\r\n this.sendJSON(res, 404, { error: 'Task graph not found' })\r\n return\r\n }\r\n const node = graph.nodes.get(nodeId)\r\n if (!node) {\r\n this.sendJSON(res, 404, { error: 'Task node not found' })\r\n return\r\n }\r\n this.sendJSON(res, 200, {\r\n ...node,\r\n createdAt: node.createdAt.toISOString(),\r\n updatedAt: node.updatedAt.toISOString(),\r\n result: node.result\r\n ? {\r\n ...node.result,\r\n }\r\n : undefined,\r\n })\r\n }\r\n\r\n private handleDecisionLog(_req: IncomingMessage, res: ServerResponse): void {\r\n const entries = this.state.getDecisionLog()\r\n this.sendJSON(res, 200, { entries })\r\n }\r\n\r\n private handleRollbackApproval(_req: IncomingMessage, res: ServerResponse, params: Record<string, string>): void {\r\n const id = params['id'] ?? ''\r\n try {\r\n this.state.resolveApproval(id, 'pending')\r\n this.broadcastSSE('approval-update', { approvalId: id, status: 'pending' })\r\n this.sendJSON(res, 200, { status: 'rolled-back', id })\r\n } catch (e) {\r\n this.sendJSON(res, 400, { error: String(e) })\r\n }\r\n }\r\n\r\n private handleEvalCalibration(_req: IncomingMessage, res: ServerResponse): void {\r\n const entries = this.state.getDecisionLogEntries()\r\n const total = entries.length\r\n const labeled = entries.filter((e) => e.outcome && e.outcome !== 'unknown')\r\n const autoExecuted = entries.filter((e) => e.wasAutoExecuted)\r\n const byConfidence: Record<string, number> = {}\r\n for (const e of entries) {\r\n const bucket = e.confidence < 0.3 ? 'low' : e.confidence < 0.7 ? 'medium' : 'high'\r\n byConfidence[bucket] = (byConfidence[bucket] ?? 0) + 1\r\n }\r\n this.sendJSON(res, 200, {\r\n totalDecisions: total,\r\n labeledCount: labeled.length,\r\n autoExecutedCount: autoExecuted.length,\r\n byConfidence,\r\n unlabeledCount: entries.filter((e) => !e.outcome || e.outcome === 'unknown').length,\r\n })\r\n }\r\n\r\n private serveDashboardHTML(res: ServerResponse): void {\r\n const html = `<!DOCTYPE html>\r\n<html lang=\"en\">\r\n<head>\r\n<meta charset=\"UTF-8\" />\r\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1.0\" />\r\n<title>Speexor Dashboard v2</title>\r\n<style>\r\n:root {\r\n --bg-primary: #0d1117;\r\n --bg-secondary: #161b22;\r\n --bg-tertiary: #1c2333;\r\n --bg-hover: #1c2128;\r\n --border: #30363d;\r\n --border-light: #21262d;\r\n --text-primary: #e6edf3;\r\n --text-secondary: #8b949e;\r\n --text-muted: #484f58;\r\n --accent-blue: #58a6ff;\r\n --accent-green: #3fb950;\r\n --accent-red: #f85149;\r\n --accent-yellow: #d29922;\r\n --accent-orange: #db6d28;\r\n --accent-purple: #bc8cff;\r\n --accent-cyan: #39d2c0;\r\n --accent-gray: #8b949e;\r\n --accent-darkred: #da3633;\r\n --status-pending: #8b949e;\r\n --status-decomposing: #d29922;\r\n --status-ready: #58a6ff;\r\n --status-in-progress: #3fb950;\r\n --status-blocked: #f85149;\r\n --status-review: #bc8cff;\r\n --status-done: #484f58;\r\n --status-failed: #da3633;\r\n --radius: 8px;\r\n --radius-sm: 4px;\r\n --font-mono: 'SF Mono','Fira Code','Cascadia Code',Consolas,monospace;\r\n --transition: 150ms ease;\r\n}\r\n* { margin: 0; padding: 0; box-sizing: border-box; }\r\nbody { font-family: -apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,sans-serif; background: var(--bg-primary); color: var(--text-primary); min-height: 100vh; }\r\n::-webkit-scrollbar { width: 8px; height: 8px; }\r\n::-webkit-scrollbar-track { background: var(--bg-primary); }\r\n::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; }\r\n::-webkit-scrollbar-thumb:hover { background: var(--text-muted); }\r\n\r\n.app-header { display: flex; align-items: center; justify-content: space-between; padding: 0.75rem 1.5rem; background: var(--bg-secondary); border-bottom: 1px solid var(--border); position: sticky; top: 0; z-index: 100; }\r\n.app-header .brand { display: flex; align-items: center; gap: 0.75rem; }\r\n.app-header .brand h1 { font-size: 1.15rem; font-weight: 600; color: var(--accent-blue); letter-spacing: -0.02em; }\r\n.app-header .brand h1 span { color: var(--text-secondary); font-weight: 400; }\r\n.app-header .brand .version { font-size: 0.65rem; color: var(--text-muted); background: var(--bg-tertiary); padding: 0.125rem 0.4rem; border-radius: 3px; font-family: var(--font-mono); }\r\n.header-meta { display: flex; align-items: center; gap: 1rem; font-size: 0.8rem; }\r\n.status-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 0.35rem; }\r\n.status-dot.online { background: var(--accent-green); box-shadow: 0 0 6px rgba(63,185,80,0.4); }\r\n.status-dot.offline { background: var(--accent-red); }\r\n.header-time { color: var(--text-secondary); font-family: var(--font-mono); font-size: 0.75rem; }\r\n\r\n.tab-bar { display: flex; gap: 0; background: var(--bg-secondary); border-bottom: 1px solid var(--border); padding: 0 1.5rem; overflow-x: auto; }\r\n.tab-btn { padding: 0.65rem 1.15rem; font-size: 0.82rem; font-weight: 500; color: var(--text-secondary); background: transparent; border: none; cursor: pointer; border-bottom: 2px solid transparent; transition: var(--transition); white-space: nowrap; }\r\n.tab-btn:hover { color: var(--text-primary); background: var(--bg-hover); }\r\n.tab-btn.active { color: var(--accent-blue); border-bottom-color: var(--accent-blue); }\r\n.tab-btn .badge { display: inline-flex; align-items: center; justify-content: center; min-width: 18px; height: 18px; padding: 0 5px; font-size: 0.65rem; font-weight: 600; border-radius: 9px; background: var(--accent-red); color: #fff; margin-left: 0.4rem; vertical-align: middle; }\r\n\r\n.panel { display: none; padding: 1.5rem; max-width: 1400px; margin: 0 auto; }\r\n.panel.active { display: block; }\r\n\r\n.stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 1rem; margin-bottom: 2rem; }\r\n.stat-card { background: var(--bg-secondary); border: 1px solid var(--border); border-radius: var(--radius); padding: 1.25rem; transition: var(--transition); }\r\n.stat-card:hover { border-color: var(--text-muted); }\r\n.stat-card .stat-label { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-secondary); margin-bottom: 0.4rem; }\r\n.stat-card .stat-value { font-size: 1.75rem; font-weight: 700; font-variant-numeric: tabular-nums; }\r\n.stat-card .stat-value.blue { color: var(--accent-blue); }\r\n.stat-card .stat-value.green { color: var(--accent-green); }\r\n.stat-card .stat-value.yellow { color: var(--accent-yellow); }\r\n.stat-card .stat-value.red { color: var(--accent-red); }\r\n.stat-card .stat-value.purple { color: var(--accent-purple); }\r\n.stat-card .stat-sub { font-size: 0.75rem; color: var(--text-muted); margin-top: 0.25rem; }\r\n\r\n.section-title { font-size: 1rem; font-weight: 600; margin-bottom: 1rem; padding-bottom: 0.5rem; border-bottom: 1px solid var(--border-light); display: flex; align-items: center; gap: 0.5rem; }\r\n.section-title .count { font-size: 0.7rem; color: var(--text-secondary); background: var(--bg-tertiary); padding: 0.1rem 0.45rem; border-radius: 3px; font-weight: 400; }\r\n\r\ntable { width: 100%; border-collapse: collapse; }\r\nth, td { text-align: left; padding: 0.7rem 0.75rem; border-bottom: 1px solid var(--border-light); font-size: 0.85rem; }\r\nth { color: var(--text-secondary); font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.04em; font-weight: 600; }\r\ntd { color: var(--text-primary); }\r\ntr:hover td { background: var(--bg-hover); }\r\ntr:last-child td { border-bottom: none; }\r\n\r\n.status-badge { display: inline-flex; align-items: center; gap: 0.3rem; font-size: 0.75rem; font-weight: 500; padding: 0.15rem 0.5rem; border-radius: 999px; }\r\n.status-badge .dot { width: 6px; height: 6px; border-radius: 50%; }\r\n.status-pending { color: var(--status-pending); }\r\n.status-pending .dot { background: var(--status-pending); }\r\n.status-decomposing { color: var(--status-decomposing); }\r\n.status-decomposing .dot { background: var(--status-decomposing); }\r\n.status-ready { color: var(--status-ready); }\r\n.status-ready .dot { background: var(--status-ready); }\r\n.status-in_progress, .status-in-progress, .status-running { color: var(--status-in-progress); }\r\n.status-in_progress .dot, .status-running .dot { background: var(--status-in-progress); }\r\n.status-blocked { color: var(--status-blocked); }\r\n.status-blocked .dot { background: var(--status-blocked); }\r\n.status-review { color: var(--status-review); }\r\n.status-review .dot { background: var(--status-review); }\r\n.status-done, .status-completed { color: var(--status-done); }\r\n.status-done .dot, .status-completed .dot { background: var(--status-done); }\r\n.status-failed { color: var(--status-failed); }\r\n.status-failed .dot { background: var(--status-failed); }\r\n\r\n.agent-tag { display: inline-block; padding: 0.1rem 0.45rem; border-radius: 3px; font-size: 0.72rem; font-weight: 500; background: var(--bg-tertiary); color: var(--accent-blue); font-family: var(--font-mono); }\r\n.agent-tag.claude-code { color: var(--accent-purple); }\r\n.agent-tag.aider { color: var(--accent-green); }\r\n.agent-tag.codex { color: var(--accent-yellow); }\r\n\r\n.task-tree { font-family: var(--font-mono); font-size: 0.82rem; }\r\n.task-node { padding: 0.45rem 0.75rem; border-left: 2px solid var(--border); margin: 0.15rem 0; border-radius: 0 var(--radius-sm) var(--radius-sm) 0; transition: var(--transition); cursor: pointer; display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }\r\n.task-node:hover { background: var(--bg-hover); border-left-color: var(--accent-blue); }\r\n.task-node .node-depth { color: var(--text-muted); font-size: 0.7rem; min-width: 1.5rem; }\r\n.task-node .node-title { flex: 1; }\r\n.task-node .node-meta { display: flex; align-items: center; gap: 0.4rem; font-size: 0.72rem; color: var(--text-secondary); }\r\n.task-node .node-skills { display: flex; gap: 0.25rem; flex-wrap: wrap; }\r\n.task-node .node-skills .skill-chip { font-size: 0.65rem; padding: 0.05rem 0.35rem; border-radius: 3px; background: var(--bg-tertiary); color: var(--accent-cyan); }\r\n.task-deps { font-size: 0.7rem; color: var(--text-muted); padding: 0.15rem 0 0.15rem 1.5rem; }\r\n.task-deps::before { content: 'depends on: '; color: var(--text-muted); }\r\n\r\n.graph-selector { margin-bottom: 1rem; display: flex; align-items: center; gap: 0.75rem; }\r\n.graph-selector select { background: var(--bg-secondary); color: var(--text-primary); border: 1px solid var(--border); padding: 0.4rem 0.75rem; border-radius: var(--radius-sm); font-size: 0.85rem; cursor: pointer; }\r\n.graph-selector select:focus { outline: none; border-color: var(--accent-blue); }\r\n.graph-status { font-size: 0.8rem; color: var(--text-secondary); }\r\n\r\n.fleet-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 1rem; }\r\n.agent-card { background: var(--bg-secondary); border: 1px solid var(--border); border-radius: var(--radius); padding: 1rem; transition: var(--transition); }\r\n.agent-card:hover { border-color: var(--text-muted); }\r\n.agent-card .card-header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 0.75rem; }\r\n.agent-card .card-header .agent-id { font-family: var(--font-mono); font-size: 0.82rem; font-weight: 600; color: var(--accent-blue); word-break: break-all; }\r\n.agent-card .card-detail { display: flex; justify-content: space-between; font-size: 0.8rem; padding: 0.25rem 0; }\r\n.agent-card .card-detail .label { color: var(--text-secondary); }\r\n.agent-card .card-detail .value { color: var(--text-primary); font-family: var(--font-mono); font-size: 0.78rem; }\r\n\r\n.approval-actions { display: flex; gap: 0.5rem; }\r\n.btn { padding: 0.35rem 0.85rem; border: none; border-radius: var(--radius-sm); font-size: 0.78rem; font-weight: 500; cursor: pointer; transition: var(--transition); display: inline-flex; align-items: center; gap: 0.25rem; }\r\n.btn-approve { background: #1a3a2a; color: var(--accent-green); border: 1px solid var(--accent-green); }\r\n.btn-approve:hover { background: #238636; color: #fff; }\r\n.btn-reject { background: #3d1c1c; color: var(--accent-red); border: 1px solid var(--accent-red); }\r\n.btn-reject:hover { background: #da3633; color: #fff; }\r\n.btn:disabled { opacity: 0.5; cursor: not-allowed; }\r\n\r\n.risk-low { color: var(--accent-green); }\r\n.risk-medium { color: var(--accent-yellow); }\r\n.risk-high { color: var(--accent-red); }\r\n\r\n.activity-feed { max-height: 600px; overflow-y: auto; border: 1px solid var(--border); border-radius: var(--radius); background: var(--bg-secondary); }\r\n.feed-entry { padding: 0.5rem 0.75rem; border-bottom: 1px solid var(--border-light); font-size: 0.8rem; display: flex; align-items: center; gap: 0.75rem; font-family: var(--font-mono); }\r\n.feed-entry:last-child { border-bottom: none; }\r\n.feed-entry .feed-time { color: var(--text-muted); font-size: 0.7rem; min-width: 7ch; flex-shrink: 0; }\r\n.feed-entry .feed-agent { color: var(--accent-blue); min-width: 12ch; }\r\n.feed-entry .feed-type { color: var(--accent-purple); min-width: 10ch; font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.03em; }\r\n.feed-entry .feed-summary { color: var(--text-primary); flex: 1; }\r\n.feed-entry .feed-skills { color: var(--accent-cyan); font-size: 0.7rem; }\r\n.feed-entry .feed-file { color: var(--accent-yellow); font-size: 0.7rem; }\r\n\r\n.cost-hero { text-align: center; padding: 2rem; background: var(--bg-secondary); border: 1px solid var(--border); border-radius: var(--radius); margin-bottom: 1.5rem; }\r\n.cost-hero .amount { font-size: 3rem; font-weight: 800; font-variant-numeric: tabular-nums; }\r\n.cost-hero .amount.under { color: var(--accent-green); }\r\n.cost-hero .amount.over { color: var(--accent-red); }\r\n.cost-hero .sub { font-size: 0.85rem; color: var(--text-secondary); margin-top: 0.25rem; }\r\n.budget-bar { max-width: 400px; margin: 1rem auto 0; height: 6px; background: var(--bg-tertiary); border-radius: 3px; overflow: hidden; }\r\n.budget-bar .fill { height: 100%; border-radius: 3px; transition: width var(--transition); }\r\n.budget-bar .fill.safe { background: var(--accent-green); }\r\n.budget-bar .fill.warn { background: var(--accent-yellow); }\r\n.budget-bar .fill.danger { background: var(--accent-red); }\r\n\r\n.cost-breakdowns { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; margin-bottom: 1.5rem; }\r\n.cost-section { background: var(--bg-secondary); border: 1px solid var(--border); border-radius: var(--radius); padding: 1rem; }\r\n.cost-section h3 { font-size: 0.82rem; color: var(--text-secondary); margin-bottom: 0.75rem; }\r\n.bar-row { display: flex; align-items: center; gap: 0.75rem; margin-bottom: 0.5rem; }\r\n.bar-row .bar-label { font-size: 0.78rem; min-width: 12ch; color: var(--text-primary); }\r\n.bar-row .bar-track { flex: 1; height: 8px; background: var(--bg-tertiary); border-radius: 4px; overflow: hidden; }\r\n.bar-row .bar-fill { height: 100%; border-radius: 4px; background: var(--accent-blue); transition: width 0.5s ease; }\r\n.bar-row .bar-fill.green { background: var(--accent-green); }\r\n.bar-row .bar-fill.purple { background: var(--accent-purple); }\r\n.bar-row .bar-fill.yellow { background: var(--accent-yellow); }\r\n.bar-row .bar-fill.orange { background: var(--accent-orange); }\r\n.bar-row .bar-value { font-size: 0.78rem; font-family: var(--font-mono); min-width: 8ch; text-align: right; color: var(--text-primary); }\r\n\r\n.cost-entries { max-height: 300px; overflow-y: auto; }\r\n.cost-entry { padding: 0.35rem 0; border-bottom: 1px solid var(--border-light); font-size: 0.78rem; display: flex; justify-content: space-between; font-family: var(--font-mono); }\r\n.cost-entry:last-child { border-bottom: none; }\r\n.cost-entry .ce-provider { color: var(--accent-blue); }\r\n.cost-entry .ce-task { color: var(--text-muted); }\r\n.cost-entry .ce-amount { color: var(--text-primary); }\r\n\r\n.empty-state { text-align: center; padding: 3rem 1rem; color: var(--text-muted); }\r\n.empty-state .icon { font-size: 2rem; margin-bottom: 0.5rem; opacity: 0.5; }\r\n.empty-state p { font-size: 0.9rem; }\r\n\r\n.footer { margin-top: 2rem; padding: 1rem 0; text-align: center; color: var(--text-muted); font-size: 0.75rem; border-top: 1px solid var(--border-light); }\r\n\r\n/* FR-80: Keyboard navigation focus */\r\n.tab-btn:focus-visible, .btn:focus-visible, select:focus-visible { outline: 2px solid var(--accent-blue); outline-offset: 2px; }\r\n.btn:focus-visible { outline: 2px solid var(--accent-blue); }\r\na:focus-visible { outline: 2px solid var(--accent-blue); outline-offset: 2px; }\r\n\r\n/* FR-81: Simple mode toggle */\r\n.simple-toggle { display: flex; align-items: center; gap: 0.5rem; font-size: 0.78rem; color: var(--text-secondary); cursor: pointer; user-select: none; }\r\n.simple-toggle input { appearance: none; width: 36px; height: 20px; background: var(--bg-tertiary); border: 1px solid var(--border); border-radius: 10px; position: relative; cursor: pointer; transition: var(--transition); }\r\n.simple-toggle input:checked { background: var(--accent-blue); border-color: var(--accent-blue); }\r\n.simple-toggle input::before { content: ''; position: absolute; width: 16px; height: 16px; border-radius: 50%; background: var(--text-primary); top: 1px; left: 1px; transition: var(--transition); }\r\n.simple-toggle input:checked::before { left: 17px; }\r\n.panel.simple-hidden { display: none !important; }\r\n.simple-summary { display: none; text-align: center; padding: 2rem; background: var(--bg-secondary); border: 1px solid var(--border); border-radius: var(--radius); margin-bottom: 1.5rem; }\r\n.simple-summary.visible { display: block; }\r\n.simple-summary .big-count { font-size: 3rem; font-weight: 800; color: var(--accent-blue); }\r\n.simple-summary .big-label { font-size: 1.1rem; color: var(--text-secondary); margin-top: 0.25rem; }\r\n\r\n/* FR-82: Status badges with Unicode icons */\r\n.status-badge-icon { display: inline-flex; align-items: center; gap: 0.35rem; font-size: 0.78rem; font-weight: 500; padding: 0.15rem 0.5rem; border-radius: 999px; }\r\n\r\n/* FR-35: Collapsible task tree */\r\n.task-tree { font-family: var(--font-mono); font-size: 0.82rem; }\r\n.task-node-row { display: flex; align-items: stretch; cursor: pointer; transition: var(--transition); }\r\n.task-node-row:hover { background: var(--bg-hover); }\r\n.task-node-content { display: flex; align-items: center; gap: 0.5rem; padding: 0.4rem 0.5rem; flex: 1; flex-wrap: wrap; border-left: 3px solid transparent; }\r\n.task-node-content.selected { border-left-color: var(--accent-blue); background: var(--bg-tertiary); }\r\n.task-node-content .node-toggle { color: var(--text-muted); font-size: 0.65rem; min-width: 1.2rem; text-align: center; user-select: none; }\r\n.task-node-content .node-title { flex: 1; word-break: break-word; }\r\n.task-node-content .node-meta { display: flex; align-items: center; gap: 0.3rem; font-size: 0.72rem; color: var(--text-secondary); }\r\n.task-node-content .node-deps { font-size: 0.7rem; color: var(--text-muted); width: 100%; padding-left: 1.5rem; }\r\n.task-node-content .node-deps::before { content: '↳ depends: '; color: var(--text-muted); }\r\n\r\n/* FR-38: Detail side panel */\r\n.graph-layout { display: flex; gap: 1rem; }\r\n.graph-tree-panel { flex: 1; min-width: 0; }\r\n.graph-detail-panel { width: 380px; flex-shrink: 0; background: var(--bg-secondary); border: 1px solid var(--border); border-radius: var(--radius); padding: 1rem; display: none; position: sticky; top: 120px; max-height: calc(100vh - 140px); overflow-y: auto; }\r\n.graph-detail-panel.visible { display: block; }\r\n.detail-header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 0.75rem; }\r\n.detail-header h3 { font-size: 0.95rem; color: var(--accent-blue); word-break: break-word; }\r\n.detail-close { background: none; border: 1px solid var(--border); color: var(--text-secondary); cursor: pointer; font-size: 0.85rem; padding: 0.15rem 0.45rem; border-radius: 3px; }\r\n.detail-close:hover { color: var(--text-primary); border-color: var(--text-muted); }\r\n.detail-field { margin-bottom: 0.6rem; font-size: 0.8rem; }\r\n.detail-field .df-label { color: var(--text-secondary); font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.04em; margin-bottom: 0.15rem; }\r\n.detail-field .df-value { color: var(--text-primary); word-break: break-word; font-family: var(--font-mono); font-size: 0.78rem; }\r\n.detail-field .df-value.multiline { white-space: pre-wrap; max-height: 120px; overflow-y: auto; }\r\n.detail-skills { display: flex; gap: 0.25rem; flex-wrap: wrap; }\r\n.detail-skills .skill-chip { font-size: 0.65rem; padding: 0.05rem 0.35rem; border-radius: 3px; background: var(--bg-tertiary); color: var(--accent-cyan); }\r\n.detail-commands { max-height: 150px; overflow-y: auto; }\r\n.detail-commands .cmd-entry { font-size: 0.72rem; padding: 0.2rem 0; border-bottom: 1px solid var(--border-light); display: flex; gap: 0.5rem; }\r\n.detail-commands .cmd-entry:last-child { border-bottom: none; }\r\n.detail-commands .cmd-code { color: var(--accent-yellow); font-family: var(--font-mono); }\r\n\r\n/* Decision Log table */\r\n.decision-log-table { max-height: 500px; overflow-y: auto; }\r\n.decision-log-table td { font-size: 0.78rem; }\r\n\r\n@media (max-width: 768px) {\r\n .app-header { flex-direction: column; gap: 0.5rem; align-items: flex-start; }\r\n .tab-bar { padding: 0 0.5rem; }\r\n .tab-btn { padding: 0.5rem 0.75rem; font-size: 0.78rem; }\r\n .panel { padding: 1rem; }\r\n .stats-grid { grid-template-columns: 1fr 1fr; }\r\n .cost-breakdowns { grid-template-columns: 1fr; }\r\n .fleet-grid { grid-template-columns: 1fr; }\r\n}\r\n</style>\r\n</head>\r\n<body>\r\n\r\n<header class=\"app-header\">\r\n <div class=\"brand\">\r\n <h1>Speexor <span>Dashboard</span></h1>\r\n <span class=\"version\">v2</span>\r\n </div>\r\n <div class=\"header-meta\">\r\n <label class=\"simple-toggle\" title=\"Toggle simple mode (WCAG)\">\r\n <input type=\"checkbox\" id=\"simpleToggle\" onchange=\"toggleSimpleMode(this.checked)\" />\r\n <span>Simple Mode</span>\r\n </label>\r\n <span><span class=\"status-dot online\" id=\"headerStatusDot\"></span><span id=\"headerStatus\">Running</span></span>\r\n <span class=\"header-time\" id=\"headerTime\"></span>\r\n </div>\r\n</header>\r\n\r\n<div class=\"simple-summary\" id=\"simpleSummary\">\r\n <div class=\"big-count\" id=\"simpleAgentCount\">0</div>\r\n <div class=\"big-label\" id=\"simpleSummaryLabel\">agents running</div>\r\n <div style=\"margin-top:1rem;color:var(--text-muted);font-size:0.9rem;\" id=\"simpleProjectCount\"></div>\r\n</div>\r\n\r\n<nav class=\"tab-bar\" id=\"tabBar\">\r\n <button class=\"tab-btn active\" data-panel=\"mission\">Mission Control</button>\r\n <button class=\"tab-btn\" data-panel=\"graph\">Task Graph</button>\r\n <button class=\"tab-btn\" data-panel=\"fleet\">Fleet</button>\r\n <button class=\"tab-btn\" data-panel=\"approvals\">Approvals <span class=\"badge\" id=\"approvalBadge\">0</span></button>\r\n <button class=\"tab-btn\" data-panel=\"cost\">Cost</button>\r\n <button class=\"tab-btn\" data-panel=\"extensions\">Extensions</button>\r\n <button class=\"tab-btn\" data-panel=\"decisionlog\">Decision Log</button>\r\n</nav>\r\n\r\n<!-- Panel: Mission Control -->\r\n<div class=\"panel active\" id=\"panel-mission\">\r\n <div class=\"stats-grid\" id=\"statsGrid\">\r\n <div class=\"stat-card\">\r\n <div class=\"stat-label\">Projects</div>\r\n <div class=\"stat-value blue\" id=\"statProjects\">-</div>\r\n <div class=\"stat-sub\" id=\"statProjectsSub\">configured</div>\r\n </div>\r\n <div class=\"stat-card\">\r\n <div class=\"stat-label\">Active Agents</div>\r\n <div class=\"stat-value green\" id=\"statAgents\">0</div>\r\n <div class=\"stat-sub\">currently running</div>\r\n </div>\r\n <div class=\"stat-card\">\r\n <div class=\"stat-label\">Task Graphs</div>\r\n <div class=\"stat-value purple\" id=\"statGraphs\">0</div>\r\n <div class=\"stat-sub\">active decompositions</div>\r\n </div>\r\n <div class=\"stat-card\">\r\n <div class=\"stat-label\">Pending Approvals</div>\r\n <div class=\"stat-value yellow\" id=\"statApprovals\">0</div>\r\n <div class=\"stat-sub\">awaiting decision</div>\r\n </div>\r\n <div class=\"stat-card\">\r\n <div class=\"stat-label\">Uptime</div>\r\n <div class=\"stat-value\" id=\"statUptime\">0s</div>\r\n <div class=\"stat-sub\" id=\"statUptimeSub\">since start</div>\r\n </div>\r\n <div class=\"stat-card\">\r\n <div class=\"stat-label\">System</div>\r\n <div class=\"stat-value\" id=\"statMemory\">-</div>\r\n <div class=\"stat-sub\">memory used</div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"section-title\">Recent Activity</div>\r\n <div class=\"activity-feed\" id=\"missionFeed\">\r\n <div class=\"empty-state\"><p>Waiting for activity...</p></div>\r\n </div>\r\n</div>\r\n\r\n<!-- Panel: Task Graph -->\r\n<div class=\"panel\" id=\"panel-graph\">\r\n <div class=\"graph-selector\">\r\n <label style=\"color:var(--text-secondary);font-size:0.82rem;\">Task Graph:</label>\r\n <select id=\"graphSelector\" onchange=\"selectGraph(this.value)\">\r\n <option value=\"\">-- No graphs --</option>\r\n </select>\r\n <span class=\"graph-status\" id=\"graphStatus\"></span>\r\n </div>\r\n <div class=\"section-title\">Task Tree <span class=\"count\" id=\"graphNodeCount\">0 nodes</span></div>\r\n <div class=\"graph-layout\">\r\n <div class=\"graph-tree-panel\">\r\n <div id=\"graphContainer\">\r\n <div class=\"empty-state\"><p>No task graph selected</p></div>\r\n </div>\r\n </div>\r\n <div class=\"graph-detail-panel\" id=\"graphDetailPanel\">\r\n <div class=\"detail-header\">\r\n <h3 id=\"detailTitle\">Task Details</h3>\r\n <button class=\"detail-close\" onclick=\"closeDetail()\">&#10005;</button>\r\n </div>\r\n <div id=\"detailBody\">\r\n <div class=\"empty-state\"><p>Select a task node to see details</p></div>\r\n </div>\r\n </div>\r\n </div>\r\n</div>\r\n\r\n<!-- Panel: Fleet -->\r\n<div class=\"panel\" id=\"panel-fleet\">\r\n <div class=\"section-title\">Agent Fleet <span class=\"count\" id=\"fleetCount\">0</span></div>\r\n <div class=\"fleet-grid\" id=\"fleetGrid\">\r\n <div class=\"empty-state\"><p>No active agents</p></div>\r\n </div>\r\n</div>\r\n\r\n<!-- Panel: Approvals -->\r\n<div class=\"panel\" id=\"panel-approvals\">\r\n <div class=\"section-title\">Approval Inbox <span class=\"count\" id=\"approvalCount\">0</span></div>\r\n <table>\r\n <thead>\r\n <tr><th>Title</th><th>Axis</th><th>Risk</th><th>Proposed By</th><th>Created</th><th>Actions</th></tr>\r\n </thead>\r\n <tbody id=\"approvalsBody\">\r\n <tr><td colspan=\"6\" class=\"empty-state\"><p>No pending approvals</p></td></tr>\r\n </tbody>\r\n </table>\r\n\r\n <div class=\"section-title\" style=\"margin-top:2rem;\">All Approvals</div>\r\n <table>\r\n <thead>\r\n <tr><th>Title</th><th>Status</th><th>Axis</th><th>Risk</th><th>Created</th><th>Actions</th></tr>\r\n </thead>\r\n <tbody id=\"allApprovalsBody\">\r\n <tr><td colspan=\"6\" class=\"empty-state\"><p>No approvals yet</p></td></tr>\r\n </tbody>\r\n </table>\r\n</div>\r\n\r\n<!-- Panel: Cost -->\r\n<div class=\"panel\" id=\"panel-cost\">\r\n <div class=\"cost-hero\" id=\"costHero\">\r\n <div class=\"amount under\" id=\"costTotal\">$0.00</div>\r\n <div class=\"sub\">total estimated cost</div>\r\n <div class=\"budget-bar\" id=\"budgetBarContainer\" style=\"display:none;\">\r\n <div class=\"fill safe\" id=\"budgetFill\" style=\"width:0%\"></div>\r\n </div>\r\n <div class=\"sub\" id=\"budgetLabel\"></div>\r\n </div>\r\n\r\n <div class=\"cost-breakdowns\">\r\n <div class=\"cost-section\">\r\n <h3>By Provider</h3>\r\n <div id=\"costByProvider\"><div class=\"empty-state\"><p>No cost data</p></div></div>\r\n </div>\r\n <div class=\"cost-section\">\r\n <h3>By Project</h3>\r\n <div id=\"costByProject\"><div class=\"empty-state\"><p>No cost data</p></div></div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"section-title\">Cost by Task Node <span class=\"count\" id=\"costNodeCount\">0 nodes</span></div>\r\n <div id=\"costByTaskNode\">\r\n <div class=\"empty-state\"><p>No per-node cost data</p></div>\r\n </div>\r\n\r\n <div class=\"section-title\">Recent Cost Entries</div>\r\n <div class=\"cost-entries\" id=\"costEntries\">\r\n <div class=\"empty-state\"><p>No entries yet</p></div>\r\n </div>\r\n</div>\r\n\r\n<!-- Panel: Extensions -->\r\n<div class=\"panel\" id=\"panel-extensions\">\r\n <div class=\"section-title\">Extensions</div>\r\n <div class=\"empty-state\">\r\n <div class=\"icon\">&#9881;</div>\r\n <p>Extension marketplace coming soon</p>\r\n </div>\r\n</div>\r\n\r\n<!-- Panel: Decision Log -->\r\n<div class=\"panel\" id=\"panel-decisionlog\">\r\n <div class=\"section-title\">Decision Log <span class=\"count\" id=\"decisionLogCount\">0</span></div>\r\n <div class=\"decision-log-table\">\r\n <table>\r\n <thead>\r\n <tr><th>Time</th><th>Agent</th><th>Action</th><th>Confidence</th><th>Auto-Exec</th><th>Outcome</th></tr>\r\n </thead>\r\n <tbody id=\"decisionLogBody\">\r\n <tr><td colspan=\"6\" class=\"empty-state\"><p>No decision log entries yet</p></td></tr>\r\n </tbody>\r\n </table>\r\n </div>\r\n</div>\r\n\r\n<footer class=\"footer\">\r\n Speexor Agent Orchestrator v2 &mdash; part of SpeexJS &mdash; <span id=\"footerTime\"></span>\r\n</footer>\r\n\r\n<script>\r\nconst sseUrl = '/api/events'\r\n\r\nfunction $(id) { return document.getElementById(id) }\r\n\r\nfunction fmtTime(d) {\r\n if (typeof d === 'string') d = new Date(d)\r\n return d.toLocaleTimeString('en-US', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' })\r\n}\r\n\r\nfunction fmtCurrency(n) { return '$' + Number(n).toFixed(4) }\r\n\r\nfunction fmtDuration(seconds) {\r\n if (seconds < 60) return Math.floor(seconds) + 's'\r\n if (seconds < 3600) return Math.floor(seconds / 60) + 'm ' + Math.floor(seconds % 60) + 's'\r\n const h = Math.floor(seconds / 3600)\r\n const m = Math.floor((seconds % 3600) / 60)\r\n return h + 'h ' + m + 'm'\r\n}\r\n\r\nfunction statBadgeHTML(status) {\r\n const cls = status.replace(/_/g, '-')\r\n return '<span class=\"status-badge status-' + cls + '\"><span class=\"dot\"></span>' + status + '</span>'\r\n}\r\n\r\nfunction statusIconHTML(status) {\r\n var icons = { pending: '\\u26AA', in_progress: '\\u25B6', running: '\\u25B6', done: '\\u2B1B', failed: '\\u2715', blocked: '\\u2298', decomposing: '\\u2699', ready: '\\u25B6', review: '\\u1F50D', completed: '\\u2B1B', active: '\\u25B6', initializing: '\\u26AA', cancelled: '\\u2715' }\r\n var icon = icons[status] || '\\u26AA'\r\n var label = status.replace(/_/g, ' ')\r\n return '<span class=\"status-badge-icon status-' + status.replace(/_/g, '-') + '\">' + icon + ' ' + label + '</span>'\r\n}\r\n\r\nfunction agentTagHTML(provider) {\r\n const cls = (provider || '').toLowerCase().replace(/[^a-z0-9]/g, '-')\r\n return '<span class=\"agent-tag ' + cls + '\">' + (provider || 'unknown') + '</span>'\r\n}\r\n\r\nfunction riskHTML(level) {\r\n if (!level) return '<span class=\"risk-low\">-</span>'\r\n return '<span class=\"risk-' + level + '\">' + level + '</span>'\r\n}\r\n\r\n// Tab switching\r\ndocument.querySelectorAll('.tab-btn').forEach(function(btn) {\r\n btn.addEventListener('click', function() {\r\n document.querySelectorAll('.tab-btn').forEach(function(b) { b.classList.remove('active') })\r\n document.querySelectorAll('.panel').forEach(function(p) { p.classList.remove('active') })\r\n btn.classList.add('active')\r\n var panel = document.getElementById('panel-' + btn.dataset.panel)\r\n if (panel) panel.classList.add('active')\r\n })\r\n})\r\n\r\n// SSE\r\nfunction connectSSE() {\r\n var source = new EventSource(sseUrl)\r\n source.onmessage = function(e) {\r\n try {\r\n var msg = JSON.parse(e.data)\r\n handleSSEMessage(msg)\r\n } catch (err) {\r\n console.error('SSE parse error:', err)\r\n }\r\n }\r\n source.onerror = function() {\r\n source.close()\r\n setTimeout(connectSSE, 2000)\r\n }\r\n}\r\n\r\nfunction handleSSEMessage(msg) {\r\n switch (msg.type) {\r\n case 'init':\r\n renderAll(msg.payload)\r\n break\r\n case 'task-update':\r\n refreshTaskGraphs()\r\n refreshStats()\r\n break\r\n case 'agent-update':\r\n refreshFleet()\r\n refreshStats()\r\n break\r\n case 'approval-update':\r\n refreshApprovals()\r\n refreshStats()\r\n break\r\n case 'cost-update':\r\n refreshCost()\r\n break\r\n case 'activity':\r\n appendActivity(msg.payload)\r\n break\r\n case 'decisionlog-update':\r\n refreshDecisionLog()\r\n break\r\n }\r\n}\r\n\r\n// Render all from initial state\r\nfunction renderAll(payload) {\r\n renderStats(payload)\r\n renderTaskGraphs(payload.taskGraphs || [])\r\n renderFleet(payload.sessions || [])\r\n renderApprovals(payload.approvals || [], payload.allApprovals || [])\r\n renderCost(payload.costData)\r\n renderActivityFeed(payload.activityFeed || [])\r\n renderDecisionLog(payload.decisionLog || [])\r\n}\r\n\r\nfunction renderStats(payload) {\r\n var status = payload.status || 'active'\r\n $('headerStatus').textContent = status.charAt(0).toUpperCase() + status.slice(1)\r\n $('headerStatusDot').className = 'status-dot ' + (status === 'active' ? 'online' : 'offline')\r\n\r\n var pCount = payload.projects ? payload.projects.length : 0\r\n var aCount = payload.sessions ? payload.sessions.length : 0\r\n var gCount = payload.taskGraphs ? payload.taskGraphs.length : 0\r\n var appCount = payload.approvals ? payload.approvals.length : 0\r\n $('statProjects').textContent = pCount\r\n $('statAgents').textContent = aCount\r\n $('statGraphs').textContent = gCount\r\n $('statApprovals').textContent = appCount\r\n $('statUptime').textContent = fmtDuration(payload.uptime || process.uptime ? Math.floor(process.uptime) : 0)\r\n}\r\n\r\nfunction refreshStats() {\r\n fetch('/api/status').then(function(r) { return r.json() }).then(function(data) {\r\n $('statProjects').textContent = data.projectCount\r\n $('statAgents').textContent = data.activeSessions\r\n $('statUptime').textContent = fmtDuration(data.uptime)\r\n }).catch(function() {})\r\n}\r\n\r\n// Task Graphs\r\nvar currentGraphs = []\r\nvar currentGraphId = null\r\nvar expandedNodes = {}\r\nvar selectedNodeId = null\r\n\r\nfunction renderTaskGraphs(graphs) {\r\n currentGraphs = graphs || []\r\n var sel = $('graphSelector')\r\n sel.innerHTML = ''\r\n if (graphs.length === 0) {\r\n sel.innerHTML = '<option value=\"\">-- No graphs --</option>'\r\n $('graphContainer').innerHTML = '<div class=\"empty-state\"><p>No task graphs yet</p></div>'\r\n $('graphNodeCount').textContent = '0 nodes'\r\n $('graphStatus').textContent = ''\r\n return\r\n }\r\n graphs.forEach(function(g, i) {\r\n var opt = document.createElement('option')\r\n opt.value = g.id\r\n opt.textContent = g.projectName + ' (' + g.status + ')'\r\n sel.appendChild(opt)\r\n })\r\n sel.value = graphs[0].id\r\n currentGraphId = graphs[0].id\r\n displayGraph(graphs[0])\r\n}\r\n\r\nfunction refreshTaskGraphs() {\r\n fetch('/api/task-graphs').then(function(r) { return r.json() }).then(function(data) {\r\n renderTaskGraphs(data.taskGraphs || [])\r\n }).catch(function() {})\r\n}\r\n\r\nfunction selectGraph(id) {\r\n var graph = currentGraphs.find(function(g) { return g.id === id })\r\n if (graph) {\r\n currentGraphId = graph.id\r\n displayGraph(graph)\r\n }\r\n}\r\n\r\nfunction toggleNode(nodeId) {\r\n expandedNodes[nodeId] = !expandedNodes[nodeId]\r\n var graph = currentGraphs.find(function(g) { return g.id === currentGraphId })\r\n if (graph) displayGraph(graph)\r\n}\r\n\r\nfunction selectNode(nodeId, graphId) {\r\n selectedNodeId = nodeId\r\n var graph = currentGraphs.find(function(g) { return g.id === (graphId || currentGraphId) })\r\n if (!graph) return\r\n displayGraph(graph)\r\n fetch('/api/task-graphs/' + (graphId || currentGraphId) + '/nodes/' + nodeId).then(function(r) { return r.json() }).then(function(node) {\r\n var body = $('detailBody')\r\n var fields = '<div class=\"detail-field\"><div class=\"df-label\">ID</div><div class=\"df-value\">' + node.id + '</div></div>'\r\n fields += '<div class=\"detail-field\"><div class=\"df-label\">Status</div><div class=\"df-value\">' + statusIconHTML(node.status) + '</div></div>'\r\n fields += '<div class=\"detail-field\"><div class=\"df-label\">Description</div><div class=\"df-value multiline\">' + (node.description || '-') + '</div></div>'\r\n fields += '<div class=\"detail-field\"><div class=\"df-label\">Agent</div><div class=\"df-value\">' + (node.assignedAgentId || 'Unassigned') + '</div></div>'\r\n fields += '<div class=\"detail-field\"><div class=\"df-label\">Created</div><div class=\"df-value\">' + fmtTime(node.createdAt) + '</div></div>'\r\n fields += '<div class=\"detail-field\"><div class=\"df-label\">Updated</div><div class=\"df-value\">' + fmtTime(node.updatedAt) + '</div></div>'\r\n fields += '<div class=\"detail-field\"><div class=\"df-label\">Depth</div><div class=\"df-value\">' + node.depth + '</div></div>'\r\n fields += '<div class=\"detail-field\"><div class=\"df-label\">Dependencies</div><div class=\"df-value\">' + ((node.dependsOn && node.dependsOn.length) ? node.dependsOn.join(', ') : 'None') + '</div></div>'\r\n if (node.skillsUsed && node.skillsUsed.length) {\r\n fields += '<div class=\"detail-field\"><div class=\"df-label\">Skills Used</div><div class=\"df-value\"><div class=\"detail-skills\">' + node.skillsUsed.map(function(s) { return '<span class=\"skill-chip\">' + s + '</span>' }).join('') + '</div></div></div>'\r\n }\r\n if (node.commandsExecuted && node.commandsExecuted.length) {\r\n fields += '<div class=\"detail-field\"><div class=\"df-label\">Commands Executed</div><div class=\"df-value\"><div class=\"detail-commands\">' + node.commandsExecuted.map(function(c) { return '<div class=\"cmd-entry\"><span class=\"cmd-code\">$ ' + c.command + '</span><span style=\"color:var(--text-muted)\">exit:' + c.exitCode + '</span></div>' }).join('') + '</div></div></div>'\r\n }\r\n if (node.result && node.result.summary) {\r\n fields += '<div class=\"detail-field\"><div class=\"df-label\">Result</div><div class=\"df-value multiline\">' + node.result.summary + '</div></div>'\r\n }\r\n if (node.result && node.result.prUrl) {\r\n fields += '<div class=\"detail-field\"><div class=\"df-label\">PR</div><div class=\"df-value\"><a href=\"' + node.result.prUrl + '\" target=\"_blank\" style=\"color:var(--accent-blue)\">' + node.result.prUrl + '</a></div></div>'\r\n }\r\n $('detailTitle').textContent = node.title\r\n $('detailBody').innerHTML = fields\r\n $('graphDetailPanel').classList.add('visible')\r\n }).catch(function() {\r\n $('detailBody').innerHTML = '<div class=\"empty-state\"><p>Failed to load details</p></div>'\r\n })\r\n}\r\n\r\nfunction closeDetail() {\r\n selectedNodeId = null\r\n $('graphDetailPanel').classList.remove('visible')\r\n var graph = currentGraphs.find(function(g) { return g.id === currentGraphId })\r\n if (graph) displayGraph(graph)\r\n}\r\n\r\nfunction displayGraph(graph) {\r\n if (!graph) return\r\n $('graphNodeCount').textContent = graph.nodes.length + ' nodes'\r\n $('graphStatus').textContent = 'Status: ' + graph.status\r\n var nodes = graph.nodes.slice().sort(function(a, b) { return a.depth - b.depth || (a.createdAt < b.createdAt ? -1 : 1) })\r\n var parentMap = {}\r\n nodes.forEach(function(n) {\r\n if (n.parentId) {\r\n if (!parentMap[n.parentId]) parentMap[n.parentId] = []\r\n parentMap[n.parentId].push(n.id)\r\n }\r\n })\r\n var rootNodes = nodes.filter(function(n) { return !n.parentId })\r\n var html = '<div class=\"task-tree\">'\r\n function renderSubtree(nodeIds, depth) {\r\n nodeIds.forEach(function(id) {\r\n var n = nodes.find(function(x) { return x.id === id })\r\n if (!n) return\r\n var hasChildren = parentMap[n.id] && parentMap[n.id].length\r\n var isExpanded = expandedNodes[n.id] !== false\r\n var isSelected = selectedNodeId === n.id\r\n var indent = n.depth * 24\r\n var deps = (n.dependsOn && n.dependsOn.length) ? '<div class=\"node-deps\">' + n.dependsOn.join(', \\u2192 ') + '</div>' : ''\r\n var skills = (n.skillsUsed && n.skillsUsed.length) ? n.skillsUsed.map(function(s) { return '<span class=\"skill-chip\">' + s + '</span>' }).join('') : ''\r\n var agent = n.assignedAgentId ? '<span class=\"agent-tag\">' + n.assignedAgentId + '</span>' : ''\r\n var toggle = hasChildren ? '<span class=\"node-toggle\" onclick=\"event.stopPropagation();toggleNode(\\'' + n.id + '\\')\">' + (isExpanded ? '\\u25BC' : '\\u25B6') + '</span>' : '<span class=\"node-toggle\" style=\"visibility:hidden\">\\u25CB</span>'\r\n html += '<div class=\"task-node-row\" style=\"padding-left:' + indent + 'px\">'\r\n html += '<div class=\"task-node-content' + (isSelected ? ' selected' : '') + '\" onclick=\"selectNode(\\'' + n.id + '\\',\\'' + graph.id + '\\')\" role=\"treeitem\" tabindex=\"0\" onkeydown=\"if(event.key===\\'Enter\\')selectNode(\\'' + n.id + '\\',\\'' + graph.id + '\\')\">'\r\n html += toggle\r\n html += statusIconHTML(n.status)\r\n html += '<span class=\"node-title\">' + n.title + '</span>'\r\n html += '<span class=\"node-meta\">' + agent + (skills ? '<span class=\"node-skills\">' + skills + '</span>' : '') + '</span>'\r\n if (deps) html += deps\r\n html += '</div></div>'\r\n if (hasChildren && isExpanded && parentMap[n.id]) {\r\n renderSubtree(parentMap[n.id], depth + 1)\r\n }\r\n })\r\n }\r\n renderSubtree(rootNodes.map(function(n) { return n.id }), 0)\r\n html += '</div>'\r\n $('graphContainer').innerHTML = html\r\n}\r\n\r\n// Fleet\r\nfunction renderFleet(sessions) {\r\n var grid = $('fleetGrid')\r\n $('fleetCount').textContent = (sessions || []).length\r\n if (!sessions || sessions.length === 0) {\r\n grid.innerHTML = '<div class=\"empty-state\"><p>No active agents</p></div>'\r\n return\r\n }\r\n var html = ''\r\n sessions.forEach(function(s) {\r\n var uptime = s.startedAt ? fmtDuration((Date.now() - new Date(s.startedAt).getTime()) / 1000) : '-'\r\n html += '<div class=\"agent-card\">'\r\n html += '<div class=\"card-header\">'\r\n html += '<span class=\"agent-id\">' + (s.id || '-') + '</span>'\r\n html += statBadgeHTML(s.status || 'pending')\r\n html += '</div>'\r\n html += '<div class=\"card-detail\"><span class=\"label\">Task</span><span class=\"value\">' + (s.taskId || '-') + '</span></div>'\r\n html += '<div class=\"card-detail\"><span class=\"label\">Provider</span><span class=\"value\">' + agentTagHTML(s.provider) + '</span></div>'\r\n html += '<div class=\"card-detail\"><span class=\"label\">Status</span><span class=\"value\">' + statBadgeHTML(s.status || 'unknown') + '</span></div>'\r\n html += '<div class=\"card-detail\"><span class=\"label\">Uptime</span><span class=\"value\">' + uptime + '</span></div>'\r\n html += '</div>'\r\n })\r\n grid.innerHTML = html\r\n}\r\n\r\nfunction refreshFleet() {\r\n fetch('/api/sessions').then(function(r) { return r.json() }).then(function(data) {\r\n renderFleet(data.sessions || [])\r\n $('statAgents').textContent = (data.sessions || []).length\r\n }).catch(function() {})\r\n}\r\n\r\n// Approvals\r\nfunction renderApprovals(pending, all) {\r\n var badge = $('approvalBadge')\r\n pending = pending || []\r\n all = all || []\r\n badge.textContent = pending.length\r\n badge.style.display = pending.length ? 'inline-flex' : 'none'\r\n $('approvalCount').textContent = pending.length\r\n\r\n var body = $('approvalsBody')\r\n if (pending.length === 0) {\r\n body.innerHTML = '<tr><td colspan=\"6\" class=\"empty-state\"><p>No pending approvals</p></td></tr>'\r\n } else {\r\n body.innerHTML = pending.map(function(a) {\r\n return '<tr>' +\r\n '<td><strong>' + a.title + '</strong><br><span style=\"font-size:0.75rem;color:var(--text-muted)\">' + (a.description || '') + '</span></td>' +\r\n '<td>' + (a.axis || '-') + '</td>' +\r\n '<td>' + riskHTML(a.riskLevel) + '</td>' +\r\n '<td>' + (a.proposedBy || '-') + '</td>' +\r\n '<td>' + fmtTime(a.createdAt) + '</td>' +\r\n '<td><div class=\"approval-actions\">' +\r\n '<button class=\"btn btn-approve\" onclick=\"approveAction('' + a.id + '')\">&#10003; Approve</button>' +\r\n '<button class=\"btn btn-reject\" onclick=\"rejectAction('' + a.id + '')\">&#10007; Reject</button>' +\r\n '</div></td></tr>'\r\n }).join('')\r\n }\r\n\r\n var allBody = $('allApprovalsBody')\r\n if (all.length === 0) {\r\n allBody.innerHTML = '<tr><td colspan=\"6\" class=\"empty-state\"><p>No approvals yet</p></td></tr>'\r\n } else {\r\n allBody.innerHTML = all.map(function(a) {\r\n var rollbackBtn = a.status === 'approved' ? '<button class=\"btn\" style=\"background:#3d1c1c;color:var(--accent-red);border:1px solid var(--accent-red);\" onclick=\"rollbackAction(\\'' + a.id + '\\')\">↩ Rollback</button>' : ''\r\n return '<tr>' +\r\n '<td><strong>' + a.title + '</strong></td>' +\r\n '<td>' + statusIconHTML(a.status) + '</td>' +\r\n '<td>' + (a.axis || '-') + '</td>' +\r\n '<td>' + riskHTML(a.riskLevel) + '</td>' +\r\n '<td>' + fmtTime(a.createdAt) + '</td>' +\r\n '<td>' + rollbackBtn + '</td></tr>'\r\n }).join('')\r\n }\r\n}\r\n\r\nfunction refreshApprovals() {\r\n fetch('/api/approvals').then(function(r) { return r.json() }).then(function(data) {\r\n renderApprovals(data.pending || [], data.all || [])\r\n $('statApprovals').textContent = (data.pending || []).length\r\n }).catch(function() {})\r\n}\r\n\r\nfunction approveAction(id) {\r\n fetch('/api/approvals/' + id + '/approve', { method: 'POST' }).then(function(r) { return r.json() }).then(function() {\r\n refreshApprovals()\r\n }).catch(function(e) { console.error(e) })\r\n}\r\n\r\nfunction rejectAction(id) {\r\n fetch('/api/approvals/' + id + '/reject', { method: 'POST' }).then(function(r) { return r.json() }).then(function() {\r\n refreshApprovals()\r\n }).catch(function(e) { console.error(e) })\r\n}\r\n\r\nfunction rollbackAction(id) {\r\n if (!confirm('Rollback this approval? The action will be reverted to pending state.')) return\r\n fetch('/api/approvals/' + id + '/rollback', { method: 'POST' }).then(function(r) { return r.json() }).then(function() {\r\n refreshApprovals()\r\n }).catch(function(e) { console.error(e) })\r\n}\r\n\r\n// Decision Log\r\nfunction renderDecisionLog(entries) {\r\n var body = $('decisionLogBody')\r\n $('decisionLogCount').textContent = (entries || []).length\r\n if (!entries || entries.length === 0) {\r\n body.innerHTML = '<tr><td colspan=\"6\" class=\"empty-state\"><p>No decision log entries yet</p></td></tr>'\r\n return\r\n }\r\n body.innerHTML = entries.slice(0, 50).map(function(e) {\r\n return '<tr>' +\r\n '<td>' + fmtTime(e.createdAt) + '</td>' +\r\n '<td>' + (e.agentId || '-') + '</td>' +\r\n '<td>' + (e.action || '-') + '</td>' +\r\n '<td>' + (Math.round(e.confidence * 100) || '-') + '%</td>' +\r\n '<td>' + (e.wasAutoExecuted ? '\\u2705' : '\\u274C') + '</td>' +\r\n '<td>' + (e.outcome || e.outcome === 'unknown' ? (e.outcome || 'unknown') : '-') + '</td></tr>'\r\n }).join('')\r\n}\r\n\r\nfunction refreshDecisionLog() {\r\n fetch('/api/decision-log').then(function(r) { return r.json() }).then(function(data) {\r\n renderDecisionLog(data.entries || [])\r\n }).catch(function() {})\r\n}\r\n\r\n// Simple Mode\r\nfunction toggleSimpleMode(enabled) {\r\n var hiddenPanels = ['graph', 'extensions']\r\n if (enabled) {\r\n hiddenPanels.forEach(function(p) {\r\n var panel = document.getElementById('panel-' + p)\r\n if (panel) panel.classList.add('simple-hidden')\r\n var btn = document.querySelector('.tab-btn[data-panel=\"' + p + '\"]')\r\n if (btn) btn.style.display = 'none'\r\n })\r\n document.getElementById('simpleSummary').classList.add('visible')\r\n var agentCount = parseInt($('statAgents').textContent) || 0\r\n var projectCount = parseInt($('statProjects').textContent) || 0\r\n $('simpleAgentCount').textContent = agentCount\r\n $('simpleSummaryLabel').textContent = agentCount === 1 ? 'agent running' : 'agents running'\r\n $('simpleProjectCount').textContent = 'across ' + projectCount + ' project' + (projectCount !== 1 ? 's' : '')\r\n } else {\r\n hiddenPanels.forEach(function(p) {\r\n var panel = document.getElementById('panel-' + p)\r\n if (panel) panel.classList.remove('simple-hidden')\r\n var btn = document.querySelector('.tab-btn[data-panel=\"' + p + '\"]')\r\n if (btn) btn.style.display = ''\r\n })\r\n document.getElementById('simpleSummary').classList.remove('visible')\r\n }\r\n}\r\n\r\n// Cost\r\nfunction renderCost(data) {\r\n if (!data) {\r\n $('costTotal').textContent = '$0.00'\r\n $('costTotal').className = 'amount under'\r\n return\r\n }\r\n\r\n var total = data.totalCost || 0\r\n var exceeded = data.budgetExceeded || false\r\n $('costTotal').textContent = '$' + total.toFixed(4)\r\n $('costTotal').className = 'amount ' + (exceeded ? 'over' : 'under')\r\n\r\n var barContainer = $('budgetBarContainer')\r\n barContainer.style.display = 'none'\r\n\r\n var providers = data.byProvider || []\r\n var projects = data.byProject || []\r\n var entries = data.recentEntries || []\r\n\r\n var maxProvider = providers.length ? Math.max.apply(null, providers.map(function(p) { return p.cost })) : 0\r\n var maxProject = projects.length ? Math.max.apply(null, projects.map(function(p) { return p.cost })) : 0\r\n\r\n var colors = ['', 'green', 'purple', 'yellow', 'orange']\r\n\r\n if (providers.length) {\r\n $('costByProvider').innerHTML = providers.map(function(p, i) {\r\n var pct = maxProvider > 0 ? (p.cost / maxProvider * 100) : 0\r\n return '<div class=\"bar-row\"><span class=\"bar-label\">' + p.name + '</span><div class=\"bar-track\"><div class=\"bar-fill ' + (colors[i] || '') + '\" style=\"width:' + pct + '%\"></div></div><span class=\"bar-value\">$' + p.cost.toFixed(4) + '</span></div>'\r\n }).join('')\r\n } else {\r\n $('costByProvider').innerHTML = '<div class=\"empty-state\"><p>No cost data</p></div>'\r\n }\r\n\r\n if (projects.length) {\r\n $('costByProject').innerHTML = projects.map(function(p, i) {\r\n var pct = maxProject > 0 ? (p.cost / maxProject * 100) : 0\r\n return '<div class=\"bar-row\"><span class=\"bar-label\">' + p.name + '</span><div class=\"bar-track\"><div class=\"bar-fill ' + (colors[i] || '') + '\" style=\"width:' + pct + '%\"></div></div><span class=\"bar-value\">$' + p.cost.toFixed(4) + '</span></div>'\r\n }).join('')\r\n } else {\r\n $('costByProject').innerHTML = '<div class=\"empty-state\"><p>No cost data</p></div>'\r\n }\r\n\r\n if (entries.length) {\r\n $('costEntries').innerHTML = entries.map(function(e) {\r\n return '<div class=\"cost-entry\"><span class=\"ce-provider\">' + (e.provider || '-') + '</span><span class=\"ce-task\">' + (e.taskId || '-') + '</span><span class=\"ce-amount\">$' + (e.estimatedCostUSD || 0).toFixed(4) + '</span></div>'\r\n }).join('')\r\n } else {\r\n $('costEntries').innerHTML = '<div class=\"empty-state\"><p>No entries yet</p></div>'\r\n }\r\n\r\n var taskNodes = data.byTaskNode || []\r\n $('costNodeCount').textContent = taskNodes.length + ' nodes'\r\n if (taskNodes.length) {\r\n var maxNodeCost = Math.max.apply(null, taskNodes.map(function(n) { return n.cost }))\r\n var taskColors = ['', 'green', 'purple', 'yellow', 'orange', 'blue']\r\n $('costByTaskNode').innerHTML = taskNodes.slice(0, 15).map(function(n, i) {\r\n var pct = maxNodeCost > 0 ? (n.cost / maxNodeCost * 100) : 0\r\n return '<div class=\"bar-row\"><span class=\"bar-label\" title=\"' + n.taskId + '\">' + n.title.substring(0, 30) + (n.title.length > 30 ? '...' : '') + '</span><div class=\"bar-track\"><div class=\"bar-fill ' + (taskColors[i % taskColors.length] || '') + '\" style=\"width:' + pct + '%\"></div></div><span class=\"bar-value\">$' + n.cost.toFixed(4) + '</span></div>'\r\n }).join('')\r\n } else {\r\n $('costByTaskNode').innerHTML = '<div class=\"empty-state\"><p>No per-node cost data</p></div>'\r\n }\r\n}\r\n\r\nfunction refreshCost() {\r\n fetch('/api/cost').then(function(r) { return r.json() }).then(function(data) {\r\n renderCost(data)\r\n }).catch(function() {})\r\n}\r\n\r\n// Activity Feed\r\nfunction renderActivityFeed(entries) {\r\n var feed = $('missionFeed')\r\n if (!entries || entries.length === 0) {\r\n feed.innerHTML = '<div class=\"empty-state\"><p>Waiting for activity...</p></div>'\r\n return\r\n }\r\n feed.innerHTML = entries.slice(0, 100).map(function(e) {\r\n return '<div class=\"feed-entry\"><span class=\"feed-time\">' + fmtTime(e.timestamp) + '</span><span class=\"feed-agent\">' + (e.agentId || '') + '</span><span class=\"feed-type\">' + (e.type || e.eventType || '') + '</span><span class=\"feed-summary\">' + (e.summary || e.title || e.description || '') + '</span></div>'\r\n }).join('')\r\n}\r\n\r\nfunction appendActivity(event) {\r\n if (!event) return\r\n var feed = $('missionFeed')\r\n var entry = document.createElement('div')\r\n entry.className = 'feed-entry'\r\n entry.innerHTML = '<span class=\"feed-time\">' + fmtTime(event.timestamp || new Date()) + '</span><span class=\"feed-agent\">' + (event.agentId || event.agentId || '') + '</span><span class=\"feed-type\">' + (event.type || event.eventType || 'event') + '</span><span class=\"feed-summary\">' + (event.summary || event.title || event.description || JSON.stringify(event.payload || '')) + '</span>'\r\n\r\n // Remove empty state\r\n var empty = feed.querySelector('.empty-state')\r\n if (empty) feed.innerHTML = ''\r\n\r\n feed.insertBefore(entry, feed.firstChild)\r\n if (feed.children.length > 200) feed.removeChild(feed.lastChild)\r\n}\r\n\r\n// Clock\r\nfunction updateClock() {\r\n var now = new Date()\r\n $('headerTime').textContent = now.toLocaleTimeString('en-US', { hour12: false })\r\n $('footerTime').textContent = now.toISOString()\r\n}\r\nsetInterval(updateClock, 1000)\r\nupdateClock()\r\n\r\n// Init health refresh\r\nfetch('/api/health').then(function(r) { return r.json() }).then(function(data) {\r\n if (data.memory) {\r\n var mb = (data.memory.heapUsed / 1024 / 1024).toFixed(1)\r\n $('statMemory').textContent = mb + ' MB'\r\n }\r\n}).catch(function() {})\r\n\r\n// Startup: fetch initial data\r\nasync function initDashboard() {\r\n try {\r\n var [status, projects, sessions, graphs, approvals, cost, decisionLog] = await Promise.all([\r\n fetch('/api/status').then(function(r) { return r.json() }),\r\n fetch('/api/projects').then(function(r) { return r.json() }),\r\n fetch('/api/sessions').then(function(r) { return r.json() }),\r\n fetch('/api/task-graphs').then(function(r) { return r.json() }),\r\n fetch('/api/approvals').then(function(r) { return r.json() }),\r\n fetch('/api/cost').then(function(r) { return r.json() }),\r\n fetch('/api/decision-log').then(function(r) { return r.json() }),\r\n ])\r\n\r\n renderStats({\r\n status: status.status,\r\n projects: projects.projects,\r\n sessions: sessions.sessions,\r\n taskGraphs: graphs.taskGraphs,\r\n approvals: approvals.pending,\r\n uptime: status.uptime,\r\n })\r\n\r\n var uptime = status.uptime || 0\r\n $('statUptime').textContent = fmtDuration(uptime)\r\n\r\n renderTaskGraphs(graphs.taskGraphs || [])\r\n renderFleet(sessions.sessions || [])\r\n renderApprovals(approvals.pending || [], approvals.all || [])\r\n renderCost(cost)\r\n renderDecisionLog(decisionLog.entries || [])\r\n } catch (e) {\r\n console.error('Init error:', e)\r\n }\r\n}\r\n\r\ninitDashboard()\r\nconnectSSE()\r\n\r\n// Periodic refresh fallback in case SSE drops\r\nsetInterval(function() {\r\n refreshStats()\r\n refreshTaskGraphs()\r\n refreshFleet()\r\n refreshApprovals()\r\n refreshCost()\r\n refreshDecisionLog()\r\n}, 15000)\r\n</script>\r\n</body>\r\n</html>`\r\n\r\n res.writeHead(200, { 'Content-Type': 'text/html' })\r\n res.end(html)\r\n }\r\n\r\n start(): void {\r\n this.server.listen(this.port, () => {\r\n debug(`Dashboard v2 listening on http://localhost:${this.port}`)\r\n })\r\n\r\n this.heartbeatTimer = setInterval(() => {\r\n const now = new Date().toISOString()\r\n for (const client of this.sseClients) {\r\n try {\r\n client.write(`: heartbeat ${now}\\n\\n`)\r\n } catch {\r\n // remove stale client\r\n }\r\n }\r\n }, 30000)\r\n }\r\n\r\n stop(): void {\r\n if (this.heartbeatTimer) clearInterval(this.heartbeatTimer)\r\n for (const client of this.sseClients) {\r\n try {\r\n client.end()\r\n } catch {}\r\n }\r\n this.sseClients = []\r\n this.server.close()\r\n debug('Dashboard v2 stopped')\r\n }\r\n}\r\n\r\nexport function createDashboardServer(lifecycle: SpeexorLifecycle, port: number = 7777): DashboardServer {\r\n return new DashboardServer(lifecycle, port)\r\n}\r\n"]}
@@ -27,12 +27,12 @@ var reactionRuleSchema = z.object({
27
27
  auto: z.boolean(),
28
28
  action: z.enum(["fix", "notify", "escalate", "skip"]),
29
29
  retries: z.number().int().min(0).max(10),
30
- escalateAfter: z.number().int().min(1).max(1440)
30
+ escalateAfter: z.number().int().min(0).max(1440)
31
31
  });
32
32
  var providerRoutingSchema = z.object({
33
33
  primary: z.enum(["opencode", "claude-code", "aider", "codex"]),
34
34
  fallback: z.array(z.enum(["opencode", "claude-code", "aider", "codex"])).optional(),
35
- concurrentLimit: z.number().int().min(1).max(20).optional(),
35
+ concurrentLimit: z.number().int().min(1).max(50).optional(),
36
36
  costLimit: z.number().int().optional()
37
37
  });
38
38
  var projectSchema = z.object({
@@ -53,14 +53,79 @@ var projectSchema = z.object({
53
53
  notifier: z.string().optional()
54
54
  }).optional()
55
55
  });
56
+ var decompositionSchema = z.object({
57
+ maxTaskGraphDepth: z.number().int().min(1).max(10).default(5),
58
+ maxAgentSpawnDepth: z.number().int().min(1).max(10).default(3),
59
+ maxNodesPerGraph: z.number().int().min(1).max(200).default(50),
60
+ plannerProvider: z.string().optional(),
61
+ plannerModel: z.string().optional()
62
+ });
63
+ var schedulerSchema = z.object({
64
+ maxConcurrentAgents: z.union([z.number().int().min(1).max(50), z.literal("auto")]).default("auto"),
65
+ retryOnFailure: z.number().int().min(0).max(10).default(3),
66
+ providerFallbackChain: z.array(z.string()).default([]),
67
+ maxAfkDurationHours: z.number().min(0.1).max(720).optional()
68
+ });
69
+ var governanceSchema = z.object({
70
+ autoApproveProposedTasks: z.boolean().default(false),
71
+ autoApproveCategories: z.array(z.string()).default([]),
72
+ duplicateSimilarityThreshold: z.number().min(0).max(1).default(0.8)
73
+ });
74
+ var worktreeHierarchySchema = z.object({
75
+ pinToCommitHash: z.boolean().default(true),
76
+ serializeMerges: z.boolean().default(true),
77
+ conflictEscalatesToApproval: z.boolean().default(true)
78
+ });
79
+ var costGuardSchema = z.object({
80
+ budgetLimitUSD: z.number().min(0).optional(),
81
+ trackByProvider: z.boolean().default(true),
82
+ trackByProject: z.boolean().default(true),
83
+ trackByTaskNode: z.boolean().default(false)
84
+ });
85
+ var extensionsSchema = z.object({
86
+ marketplaceIndex: z.string().optional(),
87
+ enabled: z.array(z.string()).default([]),
88
+ permissionsMode: z.enum(["strict", "relaxed"]).default("strict")
89
+ });
90
+ var securitySchema = z.object({
91
+ secretsBackend: z.enum(["os-keychain", "file"]).default("file"),
92
+ encryptAtRest: z.boolean().default(false),
93
+ sandboxModel: z.enum(["process", "isolated-vm"]).default("process")
94
+ });
95
+ var performanceSchema = z.object({
96
+ maxConcurrentAgents: z.union([z.number().int().min(1).max(50), z.literal("auto")]).default("auto"),
97
+ workerThreadPoolSize: z.number().int().min(1).max(16).default(4)
98
+ });
99
+ var loggingSchema = z.object({
100
+ level: z.enum(["error", "warn", "info", "debug", "trace"]).default("info")
101
+ });
102
+ var riskPolicySchema = z.object({
103
+ autoApprove: z.array(z.string()).default([]),
104
+ requireApproval: z.array(z.string()).default([]),
105
+ approvalTimeout: z.string().default("30m"),
106
+ approvalDefaultAction: z.enum(["approve", "skip", "escalate"]).default("escalate"),
107
+ budgetLimitUSD: z.number().min(0).optional(),
108
+ defaultRiskTierForUnknownActions: z.enum(["low", "medium", "high-stakes"]).default("medium")
109
+ });
56
110
  var configSchema = z.object({
57
- version: z.literal("1"),
58
- projects: z.array(projectSchema).min(1)
111
+ version: z.enum(["1", "2", "3", "4", "5"]),
112
+ projects: z.array(projectSchema).min(1),
113
+ dataDir: z.string().optional(),
114
+ decomposition: decompositionSchema.optional(),
115
+ scheduler: schedulerSchema.optional(),
116
+ governance: governanceSchema.optional(),
117
+ riskPolicy: riskPolicySchema.optional(),
118
+ worktreeHierarchy: worktreeHierarchySchema.optional(),
119
+ costGuard: costGuardSchema.optional(),
120
+ extensions: extensionsSchema.optional(),
121
+ security: securitySchema.optional(),
122
+ performance: performanceSchema.optional(),
123
+ logging: loggingSchema.optional()
59
124
  });
60
125
  var DEFAULT_REACTION_RULES = {
61
126
  "ci-failed": { auto: true, action: "fix", retries: 3, escalateAfter: 30 },
62
127
  "changes-requested": { auto: true, action: "fix", retries: 2, escalateAfter: 60 },
63
- "approved-and-green": { auto: false, action: "notify", retries: 0, escalateAfter: 0 }
128
+ "approved-and-green": { auto: false, action: "notify", retries: 0, escalateAfter: 1 }
64
129
  };
65
130
  function loadConfig(cwd) {
66
131
  const dir = cwd ?? process.cwd();
@@ -102,6 +167,13 @@ ${issues}`);
102
167
  "approved-and-green": project.reactions["approved-and-green"] ?? DEFAULT_REACTION_RULES["approved-and-green"]
103
168
  };
104
169
  }
170
+ const reactions = project.reactions;
171
+ for (const key of Object.keys(reactions)) {
172
+ const rule = reactions[key];
173
+ if (rule && rule.escalateAfter < 1) {
174
+ rule.escalateAfter = 1;
175
+ }
176
+ }
105
177
  }
106
178
  return result.data;
107
179
  }
@@ -207,6 +279,6 @@ var SpeexorLifecycle = class {
207
279
  }
208
280
  };
209
281
 
210
- export { DEFAULT_REACTION_RULES, SpeexorLifecycle, createEventBus, generateDefaultConfig, loadConfig, validateConfig };
211
- //# sourceMappingURL=chunk-2DX54KIM.js.map
212
- //# sourceMappingURL=chunk-2DX54KIM.js.map
282
+ export { DEFAULT_REACTION_RULES, SpeexorLifecycle, configSchema, costGuardSchema, createEventBus, decompositionSchema, extensionsSchema, generateDefaultConfig, governanceSchema, loadConfig, loggingSchema, performanceSchema, riskPolicySchema, schedulerSchema, securitySchema, validateConfig, worktreeHierarchySchema };
283
+ //# sourceMappingURL=chunk-VEZQT5SX.js.map
284
+ //# sourceMappingURL=chunk-VEZQT5SX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/event-bus.ts","../src/core/config.ts","../src/core/lifecycle.ts"],"names":[],"mappings":";;;;;;;;AAGO,SAAS,cAAA,GAA2B;AACzC,EAAA,MAAM,OAAA,GAAU,IAAI,YAAA,EAAa;AAEjC,EAAA,OAAO;AAAA,IACL,IAAA,CAAK,OAAO,IAAA,EAAM;AAChB,MAAA,OAAA,CAAQ,IAAA,CAAK,OAAO,IAAI,CAAA;AAAA,IAC1B,CAAA;AAAA,IACA,EAAA,CAAG,OAAO,OAAA,EAAS;AACjB,MAAA,OAAA,CAAQ,EAAA,CAAG,OAAO,OAAO,CAAA;AAAA,IAC3B,CAAA;AAAA,IACA,GAAA,CAAI,OAAO,OAAA,EAAS;AAClB,MAAA,OAAA,CAAQ,GAAA,CAAI,OAAO,OAAO,CAAA;AAAA,IAC5B,CAAA;AAAA,IACA,IAAA,CAAK,OAAO,OAAA,EAAS;AACnB,MAAA,OAAA,CAAQ,IAAA,CAAK,OAAO,OAAO,CAAA;AAAA,IAC7B;AAAA,GACF;AACF;ACdA,IAAM,kBAAA,GAAqB,EAAE,MAAA,CAAO;AAAA,EAClC,IAAA,EAAM,EAAE,OAAA,EAAQ;AAAA,EAChB,MAAA,EAAQ,EAAE,IAAA,CAAK,CAAC,OAAO,QAAA,EAAU,UAAA,EAAY,MAAM,CAAC,CAAA;AAAA,EACpD,OAAA,EAAS,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,GAAM,GAAA,CAAI,CAAC,CAAA,CAAE,GAAA,CAAI,EAAE,CAAA;AAAA,EACvC,aAAA,EAAe,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,GAAM,GAAA,CAAI,CAAC,CAAA,CAAE,GAAA,CAAI,IAAI;AACjD,CAAC,CAAA;AAED,IAAM,qBAAA,GAAwB,EAAE,MAAA,CAAO;AAAA,EACrC,OAAA,EAAS,EAAE,IAAA,CAAK,CAAC,YAAY,aAAA,EAAe,OAAA,EAAS,OAAO,CAAC,CAAA;AAAA,EAC7D,QAAA,EAAU,CAAA,CAAE,KAAA,CAAM,CAAA,CAAE,IAAA,CAAK,CAAC,UAAA,EAAY,aAAA,EAAe,OAAA,EAAS,OAAO,CAAC,CAAC,EAAE,QAAA,EAAS;AAAA,EAClF,eAAA,EAAiB,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,EAAI,CAAE,GAAA,CAAI,CAAC,CAAA,CAAE,GAAA,CAAI,EAAE,CAAA,CAAE,QAAA,EAAS;AAAA,EAC1D,WAAW,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,GAAM,QAAA;AAC9B,CAAC,CAAA;AAED,IAAM,aAAA,GAAgB,EAAE,MAAA,CAAO;AAAA,EAC7B,IAAA,EAAM,CAAA,CAAE,MAAA,EAAO,CAAE,IAAI,CAAC,CAAA;AAAA,EACtB,UAAA,EAAY,CAAA,CAAE,MAAA,EAAO,CAAE,IAAI,CAAC,CAAA;AAAA,EAC5B,IAAA,EAAM,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,EAC1B,MAAA,EAAQ,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,EAC5B,QAAA,EAAU,qBAAA;AAAA,EACV,SAAA,EAAW,EACR,MAAA,CAAO;AAAA,IACN,WAAA,EAAa,mBAAmB,QAAA,EAAS;AAAA,IACzC,mBAAA,EAAqB,mBAAmB,QAAA,EAAS;AAAA,IACjD,oBAAA,EAAsB,mBAAmB,QAAA;AAAS,GACnD,EACA,QAAA,EAAS;AAAA,EACZ,OAAA,EAAS,EACN,MAAA,CAAO;AAAA,IACN,OAAA,EAAS,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,IAC7B,GAAA,EAAK,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,IACzB,OAAA,EAAS,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,IAC7B,QAAA,EAAU,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA;AAAS,GAC/B,EACA,QAAA;AACL,CAAC,CAAA;AAED,IAAM,mBAAA,GAAsB,EAAE,MAAA,CAAO;AAAA,EACnC,iBAAA,EAAmB,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,EAAI,CAAE,GAAA,CAAI,CAAC,CAAA,CAAE,GAAA,CAAI,EAAE,CAAA,CAAE,QAAQ,CAAC,CAAA;AAAA,EAC5D,kBAAA,EAAoB,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,EAAI,CAAE,GAAA,CAAI,CAAC,CAAA,CAAE,GAAA,CAAI,EAAE,CAAA,CAAE,QAAQ,CAAC,CAAA;AAAA,EAC7D,gBAAA,EAAkB,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,EAAI,CAAE,GAAA,CAAI,CAAC,CAAA,CAAE,GAAA,CAAI,GAAG,CAAA,CAAE,QAAQ,EAAE,CAAA;AAAA,EAC7D,eAAA,EAAiB,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,EACrC,YAAA,EAAc,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA;AAC3B,CAAC;AAED,IAAM,eAAA,GAAkB,EAAE,MAAA,CAAO;AAAA,EAC/B,mBAAA,EAAqB,EAAE,KAAA,CAAM,CAAC,EAAE,MAAA,EAAO,CAAE,GAAA,EAAI,CAAE,GAAA,CAAI,CAAC,EAAE,GAAA,CAAI,EAAE,GAAG,CAAA,CAAE,OAAA,CAAQ,MAAM,CAAC,CAAC,CAAA,CAAE,OAAA,CAAQ,MAAM,CAAA;AAAA,EACjG,cAAA,EAAgB,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,EAAI,CAAE,GAAA,CAAI,CAAC,CAAA,CAAE,GAAA,CAAI,EAAE,CAAA,CAAE,QAAQ,CAAC,CAAA;AAAA,EACzD,qBAAA,EAAuB,EAAE,KAAA,CAAM,CAAA,CAAE,QAAQ,CAAA,CAAE,OAAA,CAAQ,EAAE,CAAA;AAAA,EACrD,mBAAA,EAAqB,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,CAAI,GAAG,CAAA,CAAE,GAAA,CAAI,GAAG,CAAA,CAAE,QAAA;AACpD,CAAC;AAED,IAAM,gBAAA,GAAmB,EAAE,MAAA,CAAO;AAAA,EAChC,wBAAA,EAA0B,CAAA,CAAE,OAAA,EAAQ,CAAE,QAAQ,KAAK,CAAA;AAAA,EACnD,qBAAA,EAAuB,EAAE,KAAA,CAAM,CAAA,CAAE,QAAQ,CAAA,CAAE,OAAA,CAAQ,EAAE,CAAA;AAAA,EACrD,4BAAA,EAA8B,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,CAAI,CAAC,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA,CAAE,OAAA,CAAQ,GAAG;AACpE,CAAC;AAED,IAAM,uBAAA,GAA0B,EAAE,MAAA,CAAO;AAAA,EACvC,eAAA,EAAiB,CAAA,CAAE,OAAA,EAAQ,CAAE,QAAQ,IAAI,CAAA;AAAA,EACzC,eAAA,EAAiB,CAAA,CAAE,OAAA,EAAQ,CAAE,QAAQ,IAAI,CAAA;AAAA,EACzC,2BAAA,EAA6B,CAAA,CAAE,OAAA,EAAQ,CAAE,QAAQ,IAAI;AACvD,CAAC;AAED,IAAM,eAAA,GAAkB,EAAE,MAAA,CAAO;AAAA,EAC/B,gBAAgB,CAAA,CAAE,MAAA,GAAS,GAAA,CAAI,CAAC,EAAE,QAAA,EAAS;AAAA,EAC3C,eAAA,EAAiB,CAAA,CAAE,OAAA,EAAQ,CAAE,QAAQ,IAAI,CAAA;AAAA,EACzC,cAAA,EAAgB,CAAA,CAAE,OAAA,EAAQ,CAAE,QAAQ,IAAI,CAAA;AAAA,EACxC,eAAA,EAAiB,CAAA,CAAE,OAAA,EAAQ,CAAE,QAAQ,KAAK;AAC5C,CAAC;AAED,IAAM,gBAAA,GAAmB,EAAE,MAAA,CAAO;AAAA,EAChC,gBAAA,EAAkB,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,EACtC,OAAA,EAAS,EAAE,KAAA,CAAM,CAAA,CAAE,QAAQ,CAAA,CAAE,OAAA,CAAQ,EAAE,CAAA;AAAA,EACvC,eAAA,EAAiB,EAAE,IAAA,CAAK,CAAC,UAAU,SAAS,CAAC,CAAA,CAAE,OAAA,CAAQ,QAAQ;AACjE,CAAC;AAED,IAAM,cAAA,GAAiB,EAAE,MAAA,CAAO;AAAA,EAC9B,cAAA,EAAgB,EAAE,IAAA,CAAK,CAAC,eAAe,MAAM,CAAC,CAAA,CAAE,OAAA,CAAQ,MAAM,CAAA;AAAA,EAC9D,aAAA,EAAe,CAAA,CAAE,OAAA,EAAQ,CAAE,QAAQ,KAAK,CAAA;AAAA,EACxC,YAAA,EAAc,EAAE,IAAA,CAAK,CAAC,WAAW,aAAa,CAAC,CAAA,CAAE,OAAA,CAAQ,SAAS;AACpE,CAAC;AAED,IAAM,iBAAA,GAAoB,EAAE,MAAA,CAAO;AAAA,EACjC,mBAAA,EAAqB,EAAE,KAAA,CAAM,CAAC,EAAE,MAAA,EAAO,CAAE,GAAA,EAAI,CAAE,GAAA,CAAI,CAAC,EAAE,GAAA,CAAI,EAAE,GAAG,CAAA,CAAE,OAAA,CAAQ,MAAM,CAAC,CAAC,CAAA,CAAE,OAAA,CAAQ,MAAM,CAAA;AAAA,EACjG,oBAAA,EAAsB,CAAA,CAAE,MAAA,EAAO,CAAE,GAAA,EAAI,CAAE,GAAA,CAAI,CAAC,CAAA,CAAE,GAAA,CAAI,EAAE,CAAA,CAAE,QAAQ,CAAC;AACjE,CAAC;AAED,IAAM,aAAA,GAAgB,EAAE,MAAA,CAAO;AAAA,EAC7B,KAAA,EAAO,CAAA,CAAE,IAAA,CAAK,CAAC,OAAA,EAAS,MAAA,EAAQ,MAAA,EAAQ,OAAA,EAAS,OAAO,CAAC,CAAA,CAAE,OAAA,CAAQ,MAAM;AAC3E,CAAC;AAED,IAAM,gBAAA,GAAmB,EAAE,MAAA,CAAO;AAAA,EAChC,WAAA,EAAa,EAAE,KAAA,CAAM,CAAA,CAAE,QAAQ,CAAA,CAAE,OAAA,CAAQ,EAAE,CAAA;AAAA,EAC3C,eAAA,EAAiB,EAAE,KAAA,CAAM,CAAA,CAAE,QAAQ,CAAA,CAAE,OAAA,CAAQ,EAAE,CAAA;AAAA,EAC/C,eAAA,EAAiB,CAAA,CAAE,MAAA,EAAO,CAAE,QAAQ,KAAK,CAAA;AAAA,EACzC,qBAAA,EAAuB,CAAA,CAAE,IAAA,CAAK,CAAC,SAAA,EAAW,QAAQ,UAAU,CAAC,CAAA,CAAE,OAAA,CAAQ,UAAU,CAAA;AAAA,EACjF,gBAAgB,CAAA,CAAE,MAAA,GAAS,GAAA,CAAI,CAAC,EAAE,QAAA,EAAS;AAAA,EAC3C,gCAAA,EAAkC,CAAA,CAAE,IAAA,CAAK,CAAC,KAAA,EAAO,UAAU,aAAa,CAAC,CAAA,CAAE,OAAA,CAAQ,QAAQ;AAC7F,CAAC;AAED,IAAM,YAAA,GAAe,EAAE,MAAA,CAAO;AAAA,EAC5B,OAAA,EAAS,EAAE,IAAA,CAAK,CAAC,KAAK,GAAA,EAAK,GAAA,EAAK,GAAA,EAAK,GAAG,CAAC,CAAA;AAAA,EACzC,UAAU,CAAA,CAAE,KAAA,CAAM,aAAa,CAAA,CAAE,IAAI,CAAC,CAAA;AAAA,EACtC,OAAA,EAAS,CAAA,CAAE,MAAA,EAAO,CAAE,QAAA,EAAS;AAAA,EAC7B,aAAA,EAAe,oBAAoB,QAAA,EAAS;AAAA,EAC5C,SAAA,EAAW,gBAAgB,QAAA,EAAS;AAAA,EACpC,UAAA,EAAY,iBAAiB,QAAA,EAAS;AAAA,EACtC,UAAA,EAAY,iBAAiB,QAAA,EAAS;AAAA,EACtC,iBAAA,EAAmB,wBAAwB,QAAA,EAAS;AAAA,EACpD,SAAA,EAAW,gBAAgB,QAAA,EAAS;AAAA,EACpC,UAAA,EAAY,iBAAiB,QAAA,EAAS;AAAA,EACtC,QAAA,EAAU,eAAe,QAAA,EAAS;AAAA,EAClC,WAAA,EAAa,kBAAkB,QAAA,EAAS;AAAA,EACxC,OAAA,EAAS,cAAc,QAAA;AACzB,CAAC;AAEM,IAAM,sBAAA,GAAyC;AAAA,EACpD,WAAA,EAAa,EAAE,IAAA,EAAM,IAAA,EAAM,QAAQ,KAAA,EAAO,OAAA,EAAS,CAAA,EAAG,aAAA,EAAe,EAAA,EAAG;AAAA,EACxE,mBAAA,EAAqB,EAAE,IAAA,EAAM,IAAA,EAAM,QAAQ,KAAA,EAAO,OAAA,EAAS,CAAA,EAAG,aAAA,EAAe,EAAA,EAAG;AAAA,EAChF,oBAAA,EAAsB,EAAE,IAAA,EAAM,KAAA,EAAO,QAAQ,QAAA,EAAU,OAAA,EAAS,CAAA,EAAG,aAAA,EAAe,CAAA;AACpF;AAEO,SAAS,WAAW,GAAA,EAA6B;AACtD,EAAA,MAAM,GAAA,GAAM,GAAA,IAAO,OAAA,CAAQ,GAAA,EAAI;AAE/B,EAAA,MAAM,UAAA,GAAa;AAAA,IACjB,IAAA,CAAK,KAAK,qBAAqB,CAAA;AAAA,IAC/B,IAAA,CAAK,KAAK,oBAAoB,CAAA;AAAA,IAC9B,IAAA,CAAK,KAAK,eAAe,CAAA;AAAA,IACzB,IAAA,CAAK,KAAK,cAAc,CAAA;AAAA,IACxB,IAAA,CAAK,KAAK,cAAc;AAAA,GAC1B;AAEA,EAAA,IAAI,UAAA;AACJ,EAAA,KAAA,MAAW,aAAa,UAAA,EAAY;AAClC,IAAA,IAAI,UAAA,CAAW,SAAS,CAAA,EAAG;AACzB,MAAA,UAAA,GAAa,SAAA;AACb,MAAA;AAAA,IACF;AAAA,EACF;AAEA,EAAA,IAAI,CAAC,UAAA,EAAY;AACf,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,iCAAA,EAAoC,GAAG,CAAA,6CAAA,CAAkD,CAAA;AAAA,EAC3G;AAEA,EAAA,MAAM,GAAA,GAAM,YAAA,CAAa,UAAA,EAAY,OAAO,CAAA;AAC5C,EAAA,MAAM,MAAA,GAAS,MAAM,GAAG,CAAA;AAExB,EAAA,OAAO,eAAe,MAAM,CAAA;AAC9B;AAEO,SAAS,eAAe,GAAA,EAA6B;AAC1D,EAAA,MAAM,MAAA,GAAS,YAAA,CAAa,SAAA,CAAU,GAAG,CAAA;AACzC,EAAA,IAAI,CAAC,OAAO,OAAA,EAAS;AACnB,IAAA,MAAM,SAAS,MAAA,CAAO,KAAA,CAAM,OAAO,GAAA,CAAI,CAAC,MAAM,CAAA,IAAA,EAAO,CAAA,CAAE,KAAK,IAAA,CAAK,GAAG,CAAC,CAAA,EAAA,EAAK,CAAA,CAAE,OAAO,CAAA,CAAE,CAAA,CAAE,KAAK,IAAI,CAAA;AAChG,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA;AAAA,EAA4B,MAAM,CAAA,CAAE,CAAA;AAAA,EACtD;AAEA,EAAA,KAAA,MAAW,OAAA,IAAW,MAAA,CAAO,IAAA,CAAK,QAAA,EAAU;AAC1C,IAAA,IAAI,CAAC,QAAQ,SAAA,EAAW;AACtB,MAAA,OAAA,CAAQ,SAAA,GAAY,EAAE,GAAG,sBAAA,EAAuB;AAAA,IAClD,CAAA,MAAO;AACL,MAAA,OAAA,CAAQ,SAAA,GAAY;AAAA,QAClB,aAAa,OAAA,CAAQ,SAAA,CAAU,WAAW,CAAA,IAAK,uBAAuB,WAAW,CAAA;AAAA,QACjF,qBAAqB,OAAA,CAAQ,SAAA,CAAU,mBAAmB,CAAA,IAAK,uBAAuB,mBAAmB,CAAA;AAAA,QACzG,sBAAsB,OAAA,CAAQ,SAAA,CAAU,oBAAoB,CAAA,IAAK,uBAAuB,oBAAoB;AAAA,OAC9G;AAAA,IACF;AACA,IAAA,MAAM,YAAY,OAAA,CAAQ,SAAA;AAC1B,IAAA,KAAA,MAAW,GAAA,IAAO,MAAA,CAAO,IAAA,CAAK,SAAS,CAAA,EAA+B;AACpE,MAAA,MAAM,IAAA,GAAO,UAAU,GAAG,CAAA;AAC1B,MAAA,IAAI,IAAA,IAAQ,IAAA,CAAK,aAAA,GAAgB,CAAA,EAAG;AAClC,QAAA,IAAA,CAAK,aAAA,GAAgB,CAAA;AAAA,MACvB;AAAA,IACF;AAAA,EACF;AAEA,EAAA,OAAO,MAAA,CAAO,IAAA;AAChB;AAEO,SAAS,qBAAA,CAAsB,SAAiB,WAAA,EAAqC;AAC1F,EAAA,MAAM,IAAA,GAAO,WAAA,IAAe,OAAA,CAAQ,KAAA,CAAM,GAAG,CAAA,CAAE,GAAA,EAAI,EAAG,OAAA,CAAQ,MAAA,EAAQ,EAAE,CAAA,IAAK,YAAA;AAC7E,EAAA,OAAO;AAAA,IACL,OAAA,EAAS,GAAA;AAAA,IACT,QAAA,EAAU;AAAA,MACR;AAAA,QACE,IAAA;AAAA,QACA,UAAA,EAAY,OAAA;AAAA,QACZ,QAAA,EAAU;AAAA,UACR,OAAA,EAAS;AAAA,SACX;AAAA,QACA,SAAA,EAAW,EAAE,GAAG,sBAAA;AAAuB;AACzC;AACF,GACF;AACF;AC3LA,IAAM,KAAA,GAAQ,MAAM,mBAAmB,CAAA;AAEhC,IAAM,mBAAN,MAAuB;AAAA,EACpB,MAAA;AAAA,EACD,QAAA;AAAA,EACC,OAAA,uBAA+C,GAAA,EAAI;AAAA,EACnD,QAAA,uBAA0C,GAAA,EAAI;AAAA,EAC9C,MAAA,GAAwB,cAAA;AAAA,EAEhC,YAAY,MAAA,EAAuB;AACjC,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AACd,IAAA,IAAA,CAAK,WAAW,cAAA,EAAe;AAAA,EACjC;AAAA,EAEA,MAAM,UAAA,GAA4B;AAChC,IAAA,KAAA,CAAM,gCAAgC,CAAA;AACtC,IAAA,IAAA,CAAK,MAAA,GAAS,QAAA;AACd,IAAA,IAAA,CAAK,QAAA,CAAS,KAAK,uBAAA,EAAyB,EAAE,2BAAW,IAAI,IAAA,IAAQ,CAAA;AAAA,EACvE;AAAA,EAEA,eAAe,MAAA,EAA4B;AACzC,IAAA,MAAM,WAAW,IAAA,CAAK,OAAA,CAAQ,IAAI,MAAA,CAAO,IAAI,KAAK,EAAC;AACnD,IAAA,QAAA,CAAS,KAAK,MAAM,CAAA;AACpB,IAAA,IAAA,CAAK,OAAA,CAAQ,GAAA,CAAI,MAAA,CAAO,IAAA,EAAM,QAAQ,CAAA;AACtC,IAAA,KAAA,CAAM,sBAAsB,MAAA,CAAO,IAAI,CAAA,EAAA,EAAK,MAAA,CAAO,IAAI,CAAA,CAAA,CAAG,CAAA;AAAA,EAC5D;AAAA,EAEA,WAAmC,IAAA,EAAuB;AACxD,IAAA,OAAQ,IAAA,CAAK,OAAA,CAAQ,GAAA,CAAI,IAAI,KAAK,EAAC;AAAA,EACrC;AAAA,EAEA,eAAuC,IAAA,EAAiC;AACtE,IAAA,OAAO,IAAA,CAAK,UAAA,CAAc,IAAI,CAAA,CAAE,CAAC,CAAA;AAAA,EACnC;AAAA,EAEA,MAAM,WAAW,IAAA,EAAwC;AACvD,IAAA,MAAM,WAAA,GAAc,IAAA,CAAK,cAAA,CAA4B,OAAO,CAAA;AAC5D,IAAA,IAAI,CAAC,WAAA,EAAa,MAAM,IAAI,MAAM,4BAA4B,CAAA;AAE9D,IAAA,MAAM,eAAA,GAAkB,IAAA,CAAK,cAAA,CAAgC,WAAW,CAAA;AACxE,IAAA,IAAI,CAAC,eAAA,EAAiB,MAAM,IAAI,MAAM,gCAAgC,CAAA;AAEtE,IAAA,MAAM,aAAA,GAAgB,IAAA,CAAK,cAAA,CAA8B,SAAS,CAAA;AAClE,IAAA,IAAI,CAAC,aAAA,EAAe,MAAM,IAAI,MAAM,8BAA8B,CAAA;AAElE,IAAA,MAAM,QAAA,GAAW,MAAM,eAAA,CAAgB,cAAA,CAAe,IAAI,CAAA;AAC1D,IAAA,IAAA,CAAK,QAAA,CAAS,IAAA,CAAK,kBAAA,EAAoB,EAAE,MAAA,EAAQ,KAAK,EAAA,EAAI,IAAA,EAAM,QAAA,CAAS,IAAA,EAAM,CAAA;AAE/E,IAAA,MAAM,cAAA,GAAiB,MAAM,aAAA,CAAc,aAAA,CAAc,SAAS,IAAI,CAAA;AAEtE,IAAA,MAAM,OAAA,GAAU,MAAM,WAAA,CAAY,KAAA,CAAM,MAAM,cAAc,CAAA;AAC5D,IAAA,IAAA,CAAK,QAAA,CAAS,GAAA,CAAI,OAAA,CAAQ,EAAA,EAAI,OAAO,CAAA;AAErC,IAAA,IAAA,CAAK,SAAS,IAAA,CAAK,iBAAA,EAAmB,EAAE,OAAA,EAAS,MAAM,CAAA;AACvD,IAAA,KAAA,CAAM,kBAAkB,OAAA,CAAQ,EAAE,CAAA,UAAA,EAAa,IAAA,CAAK,EAAE,CAAA,CAAE,CAAA;AAExD,IAAA,OAAO,OAAA;AAAA,EACT;AAAA,EAEA,MAAM,YAAY,SAAA,EAAkC;AAClD,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,QAAA,CAAS,GAAA,CAAI,SAAS,CAAA;AAC3C,IAAA,IAAI,CAAC,OAAA,EAAS,MAAM,IAAI,KAAA,CAAM,CAAA,QAAA,EAAW,SAAS,CAAA,UAAA,CAAY,CAAA;AAE9D,IAAA,MAAM,WAAA,GAAc,IAAA,CAAK,cAAA,CAA4B,OAAO,CAAA;AAC5D,IAAA,IAAI,WAAA,EAAa;AACf,MAAA,MAAM,WAAA,CAAY,KAAK,SAAS,CAAA;AAAA,IAClC;AAEA,IAAA,IAAA,CAAK,QAAA,CAAS,OAAO,SAAS,CAAA;AAC9B,IAAA,IAAA,CAAK,QAAA,CAAS,IAAA,CAAK,mBAAA,EAAqB,EAAE,WAAW,CAAA;AACrD,IAAA,KAAA,CAAM,CAAA,iBAAA,EAAoB,SAAS,CAAA,CAAE,CAAA;AAAA,EACvC;AAAA,EAEA,WAAW,SAAA,EAA6C;AACtD,IAAA,OAAO,IAAA,CAAK,QAAA,CAAS,GAAA,CAAI,SAAS,CAAA;AAAA,EACpC;AAAA,EAEA,YAAA,GAA+B;AAC7B,IAAA,OAAO,KAAA,CAAM,IAAA,CAAK,IAAA,CAAK,QAAA,CAAS,QAAQ,CAAA;AAAA,EAC1C;AAAA,EAEA,MAAM,OAAA,GAAyB;AAC7B,IAAA,IAAA,CAAK,MAAA,GAAS,WAAA;AAEd,IAAA,KAAA,MAAW,CAAC,EAAE,CAAA,IAAK,IAAA,CAAK,QAAA,EAAU;AAChC,MAAA,MAAM,IAAA,CAAK,WAAA,CAAY,EAAE,CAAA,CAAE,MAAM,MAAM;AAAA,MAAC,CAAC,CAAA;AAAA,IAC3C;AAEA,IAAA,MAAM,KAAA,GAAsB,CAAC,UAAA,EAAY,UAAA,EAAY,OAAO,SAAA,EAAW,WAAA,EAAa,WAAW,OAAO,CAAA;AACtG,IAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,MAAA,MAAM,UAAU,IAAA,CAAK,OAAA,CAAQ,GAAA,CAAI,IAAI,KAAK,EAAC;AAC3C,MAAA,KAAA,MAAW,MAAA,IAAU,OAAA,CAAQ,OAAA,EAAQ,EAAG;AACtC,QAAA,MAAM,MAAA,CAAO,OAAA,EAAQ,CAAE,KAAA,CAAM,MAAM;AAAA,QAAC,CAAC,CAAA;AAAA,MACvC;AAAA,IACF;AAEA,IAAA,IAAA,CAAK,QAAA,CAAS,KAAK,qBAAA,EAAuB,EAAE,2BAAW,IAAI,IAAA,IAAQ,CAAA;AACnE,IAAA,KAAA,CAAM,qBAAqB,CAAA;AAAA,EAC7B;AAAA,EAEA,SAAA,GAA2B;AACzB,IAAA,OAAO,IAAA,CAAK,MAAA;AAAA,EACd;AAAA,EAEA,SAAA,GAA2B;AACzB,IAAA,OAAO,IAAA,CAAK,MAAA;AAAA,EACd;AACF","file":"chunk-VEZQT5SX.js","sourcesContent":["import { EventEmitter } from 'eventemitter3'\r\nimport type { EventBus } from './types.js'\r\n\r\nexport function createEventBus(): EventBus {\r\n const emitter = new EventEmitter()\r\n\r\n return {\r\n emit(event, data) {\r\n emitter.emit(event, data)\r\n },\r\n on(event, handler) {\r\n emitter.on(event, handler)\r\n },\r\n off(event, handler) {\r\n emitter.off(event, handler)\r\n },\r\n once(event, handler) {\r\n emitter.once(event, handler)\r\n },\r\n }\r\n}\r\n","import { readFileSync, existsSync } from 'node:fs'\nimport { join } from 'node:path'\nimport { parse } from 'yaml'\nimport { z } from 'zod'\nimport type { SpeexorConfig, ReactionConfig } from './types.js'\n\nconst reactionRuleSchema = z.object({\n auto: z.boolean(),\n action: z.enum(['fix', 'notify', 'escalate', 'skip']),\n retries: z.number().int().min(0).max(10),\n escalateAfter: z.number().int().min(0).max(1440),\n})\n\nconst providerRoutingSchema = z.object({\n primary: z.enum(['opencode', 'claude-code', 'aider', 'codex']),\n fallback: z.array(z.enum(['opencode', 'claude-code', 'aider', 'codex'])).optional(),\n concurrentLimit: z.number().int().min(1).max(50).optional(),\n costLimit: z.number().int().optional(),\n})\n\nconst projectSchema = z.object({\n name: z.string().min(1),\n repository: z.string().min(1),\n path: z.string().optional(),\n branch: z.string().optional(),\n provider: providerRoutingSchema,\n reactions: z\n .object({\n 'ci-failed': reactionRuleSchema.optional(),\n 'changes-requested': reactionRuleSchema.optional(),\n 'approved-and-green': reactionRuleSchema.optional(),\n })\n .optional(),\n plugins: z\n .object({\n tracker: z.string().optional(),\n scm: z.string().optional(),\n runtime: z.string().optional(),\n notifier: z.string().optional(),\n })\n .optional(),\n})\n\nconst decompositionSchema = z.object({\n maxTaskGraphDepth: z.number().int().min(1).max(10).default(5),\n maxAgentSpawnDepth: z.number().int().min(1).max(10).default(3),\n maxNodesPerGraph: z.number().int().min(1).max(200).default(50),\n plannerProvider: z.string().optional(),\n plannerModel: z.string().optional(),\n})\n\nconst schedulerSchema = z.object({\n maxConcurrentAgents: z.union([z.number().int().min(1).max(50), z.literal('auto')]).default('auto'),\n retryOnFailure: z.number().int().min(0).max(10).default(3),\n providerFallbackChain: z.array(z.string()).default([]),\n maxAfkDurationHours: z.number().min(0.1).max(720).optional(),\n})\n\nconst governanceSchema = z.object({\n autoApproveProposedTasks: z.boolean().default(false),\n autoApproveCategories: z.array(z.string()).default([]),\n duplicateSimilarityThreshold: z.number().min(0).max(1).default(0.8),\n})\n\nconst worktreeHierarchySchema = z.object({\n pinToCommitHash: z.boolean().default(true),\n serializeMerges: z.boolean().default(true),\n conflictEscalatesToApproval: z.boolean().default(true),\n})\n\nconst costGuardSchema = z.object({\n budgetLimitUSD: z.number().min(0).optional(),\n trackByProvider: z.boolean().default(true),\n trackByProject: z.boolean().default(true),\n trackByTaskNode: z.boolean().default(false),\n})\n\nconst extensionsSchema = z.object({\n marketplaceIndex: z.string().optional(),\n enabled: z.array(z.string()).default([]),\n permissionsMode: z.enum(['strict', 'relaxed']).default('strict'),\n})\n\nconst securitySchema = z.object({\n secretsBackend: z.enum(['os-keychain', 'file']).default('file'),\n encryptAtRest: z.boolean().default(false),\n sandboxModel: z.enum(['process', 'isolated-vm']).default('process'),\n})\n\nconst performanceSchema = z.object({\n maxConcurrentAgents: z.union([z.number().int().min(1).max(50), z.literal('auto')]).default('auto'),\n workerThreadPoolSize: z.number().int().min(1).max(16).default(4),\n})\n\nconst loggingSchema = z.object({\n level: z.enum(['error', 'warn', 'info', 'debug', 'trace']).default('info'),\n})\n\nconst riskPolicySchema = z.object({\n autoApprove: z.array(z.string()).default([]),\n requireApproval: z.array(z.string()).default([]),\n approvalTimeout: z.string().default('30m'),\n approvalDefaultAction: z.enum(['approve', 'skip', 'escalate']).default('escalate'),\n budgetLimitUSD: z.number().min(0).optional(),\n defaultRiskTierForUnknownActions: z.enum(['low', 'medium', 'high-stakes']).default('medium'),\n})\n\nconst configSchema = z.object({\n version: z.enum(['1', '2', '3', '4', '5']),\n projects: z.array(projectSchema).min(1),\n dataDir: z.string().optional(),\n decomposition: decompositionSchema.optional(),\n scheduler: schedulerSchema.optional(),\n governance: governanceSchema.optional(),\n riskPolicy: riskPolicySchema.optional(),\n worktreeHierarchy: worktreeHierarchySchema.optional(),\n costGuard: costGuardSchema.optional(),\n extensions: extensionsSchema.optional(),\n security: securitySchema.optional(),\n performance: performanceSchema.optional(),\n logging: loggingSchema.optional(),\n})\n\nexport const DEFAULT_REACTION_RULES: ReactionConfig = {\n 'ci-failed': { auto: true, action: 'fix', retries: 3, escalateAfter: 30 },\n 'changes-requested': { auto: true, action: 'fix', retries: 2, escalateAfter: 60 },\n 'approved-and-green': { auto: false, action: 'notify', retries: 0, escalateAfter: 1 },\n}\n\nexport function loadConfig(cwd?: string): SpeexorConfig {\n const dir = cwd ?? process.cwd()\n\n const candidates = [\n join(dir, 'speexor.config.yaml'),\n join(dir, 'speexor.config.yml'),\n join(dir, '.speexor.yaml'),\n join(dir, '.speexor.yml'),\n join(dir, 'speexor.yaml'),\n ]\n\n let configPath: string | undefined\n for (const candidate of candidates) {\n if (existsSync(candidate)) {\n configPath = candidate\n break\n }\n }\n\n if (!configPath) {\n throw new Error(`speexor.config.yaml not found in ${dir}. ` + 'Run `speexor start <repo>` to initialize.')\n }\n\n const raw = readFileSync(configPath, 'utf-8')\n const parsed = parse(raw)\n\n return validateConfig(parsed)\n}\n\nexport function validateConfig(raw: unknown): SpeexorConfig {\n const result = configSchema.safeParse(raw)\n if (!result.success) {\n const issues = result.error.issues.map((i) => ` - ${i.path.join('.')}: ${i.message}`).join('\\n')\n throw new Error(`Invalid speexor config:\\n${issues}`)\n }\n\n for (const project of result.data.projects) {\n if (!project.reactions) {\n project.reactions = { ...DEFAULT_REACTION_RULES }\n } else {\n project.reactions = {\n 'ci-failed': project.reactions['ci-failed'] ?? DEFAULT_REACTION_RULES['ci-failed'],\n 'changes-requested': project.reactions['changes-requested'] ?? DEFAULT_REACTION_RULES['changes-requested'],\n 'approved-and-green': project.reactions['approved-and-green'] ?? DEFAULT_REACTION_RULES['approved-and-green'],\n }\n }\n const reactions = project.reactions as ReactionConfig\n for (const key of Object.keys(reactions) as (keyof ReactionConfig)[]) {\n const rule = reactions[key]\n if (rule && rule.escalateAfter < 1) {\n rule.escalateAfter = 1\n }\n }\n }\n\n return result.data as SpeexorConfig\n}\n\nexport function generateDefaultConfig(repoUrl: string, projectName?: string): SpeexorConfig {\n const name = projectName ?? repoUrl.split('/').pop()?.replace('.git', '') ?? 'my-project'\n return {\n version: '1',\n projects: [\n {\n name,\n repository: repoUrl,\n provider: {\n primary: 'opencode',\n },\n reactions: { ...DEFAULT_REACTION_RULES },\n },\n ],\n }\n}\n\nexport {\n decompositionSchema,\n schedulerSchema,\n governanceSchema,\n riskPolicySchema,\n worktreeHierarchySchema,\n costGuardSchema,\n extensionsSchema,\n securitySchema,\n performanceSchema,\n loggingSchema,\n configSchema,\n}\n","import type {\r\n SpeexorConfig,\r\n AgentPlugin,\r\n RuntimePlugin,\r\n WorkspacePlugin,\r\n AgentTask,\r\n AgentSession,\r\n SessionStatus,\r\n EventBus,\r\n PluginModule,\r\n PluginSlot,\r\n} from './types.js'\r\nimport { createEventBus } from './event-bus.js'\r\nimport Debug from 'debug'\r\n\r\nconst debug = Debug('speexor:lifecycle')\r\n\r\nexport class SpeexorLifecycle {\r\n private config: SpeexorConfig\r\n public eventBus: EventBus\r\n private plugins: Map<PluginSlot, PluginModule[]> = new Map()\r\n private sessions: Map<string, AgentSession> = new Map()\r\n private status: SessionStatus = 'initializing'\r\n\r\n constructor(config: SpeexorConfig) {\r\n this.config = config\r\n this.eventBus = createEventBus()\r\n }\r\n\r\n async initialize(): Promise<void> {\r\n debug('Initializing Speexor lifecycle')\r\n this.status = 'active'\r\n this.eventBus.emit('lifecycle:initialized', { timestamp: new Date() })\r\n }\r\n\r\n registerPlugin(plugin: PluginModule): void {\r\n const existing = this.plugins.get(plugin.type) ?? []\r\n existing.push(plugin)\r\n this.plugins.set(plugin.type, existing)\r\n debug(`Registered plugin: ${plugin.name} (${plugin.type})`)\r\n }\r\n\r\n getPlugins<T extends PluginModule>(slot: PluginSlot): T[] {\r\n return (this.plugins.get(slot) ?? []) as T[]\r\n }\r\n\r\n getFirstPlugin<T extends PluginModule>(slot: PluginSlot): T | undefined {\r\n return this.getPlugins<T>(slot)[0]\r\n }\r\n\r\n async spawnAgent(task: AgentTask): Promise<AgentSession> {\r\n const agentPlugin = this.getFirstPlugin<AgentPlugin>('agent')\r\n if (!agentPlugin) throw new Error('No agent plugin registered')\r\n\r\n const workspacePlugin = this.getFirstPlugin<WorkspacePlugin>('workspace')\r\n if (!workspacePlugin) throw new Error('No workspace plugin registered')\r\n\r\n const runtimePlugin = this.getFirstPlugin<RuntimePlugin>('runtime')\r\n if (!runtimePlugin) throw new Error('No runtime plugin registered')\r\n\r\n const worktree = await workspacePlugin.createWorktree(task)\r\n this.eventBus.emit('worktree:created', { taskId: task.id, path: worktree.path })\r\n\r\n const runtimeSession = await runtimePlugin.createSession(worktree.path)\r\n\r\n const session = await agentPlugin.spawn(task, runtimeSession)\r\n this.sessions.set(session.id, session)\r\n\r\n this.eventBus.emit('session:created', { session, task })\r\n debug(`Agent spawned: ${session.id} for task ${task.id}`)\r\n\r\n return session\r\n }\r\n\r\n async stopSession(sessionId: string): Promise<void> {\r\n const session = this.sessions.get(sessionId)\r\n if (!session) throw new Error(`Session ${sessionId} not found`)\r\n\r\n const agentPlugin = this.getFirstPlugin<AgentPlugin>('agent')\r\n if (agentPlugin) {\r\n await agentPlugin.kill(sessionId)\r\n }\r\n\r\n this.sessions.delete(sessionId)\r\n this.eventBus.emit('session:completed', { sessionId })\r\n debug(`Session stopped: ${sessionId}`)\r\n }\r\n\r\n getSession(sessionId: string): AgentSession | undefined {\r\n return this.sessions.get(sessionId)\r\n }\r\n\r\n listSessions(): AgentSession[] {\r\n return Array.from(this.sessions.values())\r\n }\r\n\r\n async destroy(): Promise<void> {\r\n this.status = 'completed'\r\n\r\n for (const [id] of this.sessions) {\r\n await this.stopSession(id).catch(() => {})\r\n }\r\n\r\n const slots: PluginSlot[] = ['terminal', 'notifier', 'scm', 'tracker', 'workspace', 'runtime', 'agent']\r\n for (const slot of slots) {\r\n const plugins = this.plugins.get(slot) ?? []\r\n for (const plugin of plugins.reverse()) {\r\n await plugin.destroy().catch(() => {})\r\n }\r\n }\r\n\r\n this.eventBus.emit('lifecycle:destroyed', { timestamp: new Date() })\r\n debug('Lifecycle destroyed')\r\n }\r\n\r\n getConfig(): SpeexorConfig {\r\n return this.config\r\n }\r\n\r\n getStatus(): SessionStatus {\r\n return this.status\r\n }\r\n}\r\n"]}