UNPKG

naisys

Version:

NAISYS - Autonomous AI agent runner with built-in context management and cost tracking

72 lines 3.02 kB
import { unique } from "@naisys/common"; import { AgentsStatusSchema, HubEvents, NAISYS_HEARTBEAT_INTERVAL_MS, } from "@naisys/hub-protocol"; export function createHeartbeatService(hubClient, agentManager, userService) { // In hub mode, listen for agent status pushes from the hub if (hubClient) { hubClient.registerEvent(HubEvents.AGENTS_STATUS, (data) => { const parsed = AgentsStatusSchema.parse(data); userService.setActiveUsers(parsed.hostActiveAgents); }); agentManager.onHeartbeatNeeded = sendHeartbeat; } function sendHeartbeat() { // Ephemerals ride under the parent's userId so the synthetic id never // enters hub-user tracking. The subagentId still distinguishes the row. // Skip an entry rather than ever emitting a synthetic id as `userId`. const activeSessions = []; // Top-level agents only — subagents inherit via the parent's shell env. const runtimeApiKeys = []; const seenKeyUserIds = new Set(); for (const a of agentManager.runningAgents) { const user = userService.getUserById(a.agentUserId); let userId; let subagentId; if (user?.isEphemeral) { if (user.leadUserId == null) continue; // orphan ephemeral — shouldn't happen userId = user.leadUserId; subagentId = a.agentUserId; } else { // Negative agentUserId without an isEphemeral user record means we're // racing teardown (user record cleared before splice). Drop it. if (a.agentUserId < 0) continue; userId = a.agentUserId; } activeSessions.push({ userId, runId: a.getRunId(), subagentId, sessionId: a.getSessionId(), paused: a.isPaused(), state: a.getState(), tokenCount: a.getTokenCount(), }); if (subagentId === undefined && !seenKeyUserIds.has(userId)) { runtimeApiKeys.push({ userId, runtimeApiKey: a.naisysApiService.getKey(), }); seenKeyUserIds.add(userId); } } if (hubClient) { hubClient.sendMessage(HubEvents.HEARTBEAT, { activeSessions, runtimeApiKeys: runtimeApiKeys.length > 0 ? runtimeApiKeys : undefined, }); } else { const uniqueUserIds = unique(activeSessions.map((s) => s.userId)); userService.setActiveUsers({ "": uniqueUserIds }); } } // Start periodic heartbeat const interval = setInterval(sendHeartbeat, NAISYS_HEARTBEAT_INTERVAL_MS); function cleanup() { clearInterval(interval); } return { cleanup }; } //# sourceMappingURL=heartbeatService.js.map