import type { GatewayRequest, OutboundMessage } from '../protocol.js'; import { makeResponse, makeError, ErrorCode } from '../protocol.js'; import type { MetricsSnapshot, EventEntry, ActiveRequestInfo } from '../metrics.js'; import type { ServiceInfo } from './services.js'; import type { NodeLocation, NodeStatus, NodePushToken } from './node.js'; import type { SessionAnalyticsSnapshot } from '../../session/index.js'; import type { LocalBackendAction, LocalBackendControlResult, LocalBackendStatus } from './localBackends.js'; /** Per-session token usage report returned by system.tokenUsage. */ export interface TokenUsageEntry { sessionId: string; primary: { inputTokens: number; outputTokens: number; calls: number }; delegation: Record; total: { inputTokens: number; outputTokens: number; calls: number; estimatedCost: number }; } /** Per-session context budget report returned by system.contextUsage. */ export interface ContextUsageEntry { sessionId: string; budget: { estimatedTokens: number; contextWindow: number; remainingTokens: number; usagePct: number; thresholdPct: number; thresholdTokens: number; shouldCompact: boolean; }; } export interface PresenceEntry { channel: string; senderId: string; senderName?: string; firstSeenAt: number; lastSeenAt: number; messageCount: number; status: 'online' | 'offline'; } export interface NodeLocationEntry { nodeId: string; role: string; connectionId: string; location: NodeLocation; } export interface NodeEntry { connectionId: string; nodeId: string; role: string; identity?: string; protocolVersion: number; capabilities: string[]; registeredAt: number; location?: NodeLocation; status?: NodeStatus; push?: NodePushTokenSummary; } export interface NodePushTokenSummary { provider: NodePushToken['provider']; tokenPreview: string; topic?: string; environment?: NodePushToken['environment']; registeredAt: number; } export interface SystemHandlerDeps { startTime: number; version: string; getSessionCount: () => number; getToolCount: () => number; getConnectionCount: () => number; /** Optional callback to trigger a graceful restart. If not provided, system.restart returns an error. */ restart?: () => Promise; getChannels?: () => Array<{ name: string; status: string; error?: string; lastErrorAt?: number }>; getUsage?: () => { totalSessions: number; activeConnections: number }; /** Optional callback to retrieve per-session token usage data. */ getTokenUsage?: () => TokenUsageEntry[]; /** Optional callback to retrieve per-session context budget data. */ getContextUsage?: () => ContextUsageEntry[]; /** Optional callback to retrieve aggregated metrics snapshot. */ getMetrics?: () => MetricsSnapshot; /** Optional callback to retrieve session analytics. */ getSessionAnalytics?: (opts?: { days?: number; topLimit?: number }) => SessionAnalyticsSnapshot; /** Optional callback to retrieve recent events. */ getEvents?: (opts?: { level?: string; limit?: number }) => EventEntry[]; /** Optional callback to retrieve active requests. */ getActiveRequests?: () => ActiveRequestInfo[]; /** Optional callback to retrieve all services (channels + automation). */ getServices?: () => ServiceInfo[]; /** Optional callback to retrieve tracked sender presence. */ getPresence?: (opts?: { channel?: string; status?: 'online' | 'offline'; limit?: number }) => PresenceEntry[]; /** Optional callback to retrieve latest node location data. */ getNodeLocations?: (opts?: { role?: string; nodeId?: string; limit?: number }) => NodeLocationEntry[]; /** Optional callback to retrieve registered node connection snapshots. */ getNodes?: (opts?: { role?: string; platform?: string; limit?: number }) => NodeEntry[]; /** Optional callback to retrieve provider model catalogs. */ getModelCatalog?: (opts?: { provider?: string; forceRefresh?: boolean }) => Promise>; /** Optional callback to retrieve local backend daemon statuses. */ getLocalBackends?: () => Promise | LocalBackendStatus[]; /** Optional callback to control local backend daemons. */ controlLocalBackend?: (backend: string, action: string) => Promise; } function normalizeErrorMessage(error: unknown): string { if (error instanceof Error && error.message.trim().length > 0) { return error.message; } if (typeof error === 'string' && error.trim().length > 0) { return error; } return 'Unknown error'; } export function createSystemHandlers(deps: SystemHandlerDeps) { return { 'system.health': async (request: GatewayRequest): Promise => { return makeResponse(request.id, { status: 'ok', uptime: Math.floor((Date.now() - deps.startTime) / 1000), version: deps.version, sessions: deps.getSessionCount(), tools: deps.getToolCount(), connections: deps.getConnectionCount(), }); }, 'system.restart': async (request: GatewayRequest): Promise => { const restart = deps.restart; if (!restart) { return makeError(request.id, ErrorCode.InternalError, 'Restart not available in this environment'); } // Send response before initiating restart (client receives confirmation) const response = makeResponse(request.id, { restarting: true }); // Schedule restart after response is sent (next tick) queueMicrotask(() => { restart().catch((err) => { console.error('Restart failed:', err); }); }); return response; }, 'system.channels': async (request: GatewayRequest): Promise => { if (!deps.getChannels) { return makeResponse(request.id, { channels: [] }); } return makeResponse(request.id, { channels: deps.getChannels() }); }, 'system.services': async (request: GatewayRequest): Promise => { if (!deps.getServices) { return makeResponse(request.id, { services: [] }); } return makeResponse(request.id, { services: deps.getServices() }); }, 'system.presence': async (request: GatewayRequest): Promise => { if (!deps.getPresence) { return makeResponse(request.id, { presence: [], summary: { total: 0, online: 0, offline: 0 } }); } const params = request.params as { channel?: string; status?: 'online' | 'offline'; limit?: number } | undefined; const presence = deps.getPresence({ channel: params?.channel, status: params?.status, limit: params?.limit, }); const online = presence.filter((entry) => entry.status === 'online').length; return makeResponse(request.id, { presence, summary: { total: presence.length, online, offline: presence.length - online, }, }); }, 'system.location': async (request: GatewayRequest): Promise => { if (!deps.getNodeLocations) { return makeResponse(request.id, { locations: [], summary: { total: 0 } }); } const params = request.params as { role?: string; nodeId?: string; limit?: number } | undefined; const locations = deps.getNodeLocations({ role: params?.role, nodeId: params?.nodeId, limit: params?.limit, }); return makeResponse(request.id, { locations, summary: { total: locations.length, }, }); }, 'system.nodes': async (request: GatewayRequest): Promise => { if (!deps.getNodes) { return makeResponse(request.id, { nodes: [], summary: { total: 0 } }); } const params = request.params as { role?: string; platform?: string; limit?: number } | undefined; const nodes = deps.getNodes({ role: params?.role, platform: params?.platform, limit: params?.limit, }); return makeResponse(request.id, { nodes, summary: { total: nodes.length, }, }); }, 'system.usage': async (request: GatewayRequest): Promise => { const uptime = Math.floor((Date.now() - deps.startTime) / 1000); const usage = deps.getUsage?.() ?? { totalSessions: 0, activeConnections: 0 }; return makeResponse(request.id, { uptime, ...usage, tools: deps.getToolCount(), }); }, 'system.tokenUsage': async (request: GatewayRequest): Promise => { const sessions = deps.getTokenUsage?.() ?? []; return makeResponse(request.id, { sessions }); }, 'system.contextUsage': async (request: GatewayRequest): Promise => { const sessions = deps.getContextUsage?.() ?? []; return makeResponse(request.id, { sessions }); }, 'system.metrics': async (request: GatewayRequest): Promise => { if (!deps.getMetrics) { return makeResponse(request.id, {}); } return makeResponse(request.id, deps.getMetrics()); }, 'system.sessionAnalytics': async (request: GatewayRequest): Promise => { if (!deps.getSessionAnalytics) { return makeResponse(request.id, { daily: [], topSessions: [], topTools: [], topTopics: [], averageMessagesPerSession: 0, totalSessions: 0, totalMessages: 0, }); } const params = request.params as { days?: number; topLimit?: number } | undefined; const snapshot = deps.getSessionAnalytics({ days: params?.days, topLimit: params?.topLimit, }); return makeResponse(request.id, snapshot); }, 'system.events': async (request: GatewayRequest): Promise => { if (!deps.getEvents) { return makeResponse(request.id, { events: [] }); } const params = request.params as { level?: string; limit?: number } | undefined; const events = deps.getEvents({ level: params?.level, limit: params?.limit }); return makeResponse(request.id, { events }); }, 'system.activeRequests': async (request: GatewayRequest): Promise => { if (!deps.getActiveRequests) { return makeResponse(request.id, { requests: [] }); } return makeResponse(request.id, { requests: deps.getActiveRequests() }); }, 'system.modelCatalog': async (request: GatewayRequest): Promise => { if (!deps.getModelCatalog) { return makeResponse(request.id, { providers: [] }); } const params = request.params as { provider?: string; forceRefresh?: boolean } | undefined; const providers = await deps.getModelCatalog({ provider: params?.provider, forceRefresh: params?.forceRefresh === true, }); return makeResponse(request.id, { providers }); }, 'system.localBackends': async (request: GatewayRequest): Promise => { if (!deps.getLocalBackends) { return makeResponse(request.id, { backends: [] }); } try { const backends = await deps.getLocalBackends(); return makeResponse(request.id, { backends }); } catch (error) { return makeError(request.id, ErrorCode.InternalError, `Failed to load local backends: ${normalizeErrorMessage(error)}`); } }, 'system.localBackendControl': async (request: GatewayRequest): Promise => { if (!deps.controlLocalBackend) { return makeError(request.id, ErrorCode.InternalError, 'Local backend control is not available in this environment'); } const params = request.params as { backend?: string; action?: LocalBackendAction } | undefined; if (!params?.backend || typeof params.backend !== 'string') { return makeError(request.id, ErrorCode.InvalidRequest, 'backend is required'); } if (!params?.action || typeof params.action !== 'string') { return makeError(request.id, ErrorCode.InvalidRequest, 'action is required'); } if (!['start', 'restart', 'stop', 'update'].includes(params.action)) { return makeError(request.id, ErrorCode.InvalidRequest, 'action must be one of: start, restart, stop, update'); } try { const result = await deps.controlLocalBackend(params.backend, params.action); return makeResponse(request.id, result); } catch (error) { return makeError(request.id, ErrorCode.InternalError, `Local backend control failed: ${normalizeErrorMessage(error)}`); } }, }; }