import { lookupContextTokens } from "../agents/context.js"; import { DEFAULT_CONTEXT_TOKENS, DEFAULT_MODEL, DEFAULT_PROVIDER } from "../agents/defaults.js"; import { resolveConfiguredModelRef } from "../agents/model-selection.js"; import { loadConfig } from "../config/config.js"; import { loadSessionStore, resolveFreshSessionTotalTokens, resolveMainSessionKey, resolveStorePath, type SessionEntry, } from "../config/sessions.js"; import { classifySessionKey, listAgentsForGateway, resolveSessionModelRef, } from "../gateway/session-utils.js"; import { buildChannelSummary } from "../infra/channel-summary.js"; import { resolveHeartbeatSummaryForAgent } from "../infra/heartbeat-runner.js"; import { peekSystemEvents } from "../infra/system-events.js"; import { parseAgentSessionKey } from "../routing/session-key.js"; import { resolveLinkChannelContext } from "./status.link-channel.js"; import type { HeartbeatStatus, SessionStatus, StatusSummary } from "./status.types.js"; const buildFlags = (entry?: SessionEntry): string[] => { if (!entry) { return []; } const flags: string[] = []; const think = entry?.thinkingLevel; if (typeof think === "string" && think.length > 0) { flags.push(`think:${think}`); } const verbose = entry?.verboseLevel; if (typeof verbose === "string" && verbose.length > 0) { flags.push(`verbose:${verbose}`); } const reasoning = entry?.reasoningLevel; if (typeof reasoning === "string" && reasoning.length > 0) { flags.push(`reasoning:${reasoning}`); } const elevated = entry?.elevatedLevel; if (typeof elevated === "string" && elevated.length > 0) { flags.push(`elevated:${elevated}`); } if (entry?.systemSent) { flags.push("system"); } if (entry?.abortedLastRun) { flags.push("aborted"); } const sessionId = entry?.sessionId as unknown; if (typeof sessionId === "string" && sessionId.length > 0) { flags.push(`id:${sessionId}`); } return flags; }; export function redactSensitiveStatusSummary(summary: StatusSummary): StatusSummary { return { ...summary, sessions: { ...summary.sessions, paths: [], defaults: { model: null, contextTokens: null, }, recent: [], byAgent: summary.sessions.byAgent.map((entry) => ({ ...entry, path: "[redacted]", recent: [], })), }, }; } export async function getStatusSummary( options: { includeSensitive?: boolean } = {}, ): Promise { const { includeSensitive = true } = options; const cfg = loadConfig(); const linkContext = await resolveLinkChannelContext(cfg); const agentList = listAgentsForGateway(cfg); const heartbeatAgents: HeartbeatStatus[] = agentList.agents.map((agent) => { const summary = resolveHeartbeatSummaryForAgent(cfg, agent.id); return { agentId: agent.id, enabled: summary.enabled, every: summary.every, everyMs: summary.everyMs, } satisfies HeartbeatStatus; }); const channelSummary = await buildChannelSummary(cfg, { colorize: true, includeAllowFrom: true, }); const mainSessionKey = resolveMainSessionKey(cfg); const queuedSystemEvents = peekSystemEvents(mainSessionKey); const resolved = resolveConfiguredModelRef({ cfg, defaultProvider: DEFAULT_PROVIDER, defaultModel: DEFAULT_MODEL, }); const configModel = resolved.model ?? DEFAULT_MODEL; const configContextTokens = cfg.agents?.defaults?.contextTokens ?? lookupContextTokens(configModel) ?? DEFAULT_CONTEXT_TOKENS; const now = Date.now(); const storeCache = new Map>(); const loadStore = (storePath: string) => { const cached = storeCache.get(storePath); if (cached) { return cached; } const store = loadSessionStore(storePath); storeCache.set(storePath, store); return store; }; const buildSessionRows = ( store: Record, opts: { agentIdOverride?: string } = {}, ) => Object.entries(store) .filter(([key]) => key !== "global" && key !== "unknown") .map(([key, entry]) => { const updatedAt = entry?.updatedAt ?? null; const age = updatedAt ? now - updatedAt : null; const resolvedModel = resolveSessionModelRef(cfg, entry, opts.agentIdOverride); const model = resolvedModel.model ?? configModel ?? null; const contextTokens = entry?.contextTokens ?? lookupContextTokens(model) ?? configContextTokens ?? null; const total = resolveFreshSessionTotalTokens(entry); const totalTokensFresh = typeof entry?.totalTokens === "number" ? entry?.totalTokensFresh !== false : false; const remaining = contextTokens != null && total !== undefined ? Math.max(0, contextTokens - total) : null; const pct = contextTokens && contextTokens > 0 && total !== undefined ? Math.min(999, Math.round((total / contextTokens) * 100)) : null; const parsedAgentId = parseAgentSessionKey(key)?.agentId; const agentId = opts.agentIdOverride ?? parsedAgentId; return { agentId, key, kind: classifySessionKey(key, entry), sessionId: entry?.sessionId, updatedAt, age, thinkingLevel: entry?.thinkingLevel, verboseLevel: entry?.verboseLevel, reasoningLevel: entry?.reasoningLevel, elevatedLevel: entry?.elevatedLevel, systemSent: entry?.systemSent, abortedLastRun: entry?.abortedLastRun, inputTokens: entry?.inputTokens, outputTokens: entry?.outputTokens, cacheRead: entry?.cacheRead, cacheWrite: entry?.cacheWrite, totalTokens: total ?? null, totalTokensFresh, remainingTokens: remaining, percentUsed: pct, model, contextTokens, flags: buildFlags(entry), } satisfies SessionStatus; }) .sort((a, b) => (b.updatedAt ?? 0) - (a.updatedAt ?? 0)); const paths = new Set(); const byAgent = agentList.agents.map((agent) => { const storePath = resolveStorePath(cfg.session?.store, { agentId: agent.id }); paths.add(storePath); const store = loadStore(storePath); const sessions = buildSessionRows(store, { agentIdOverride: agent.id }); return { agentId: agent.id, path: storePath, count: sessions.length, recent: sessions.slice(0, 10), }; }); const allSessions = Array.from(paths) .flatMap((storePath) => buildSessionRows(loadStore(storePath))) .toSorted((a, b) => (b.updatedAt ?? 0) - (a.updatedAt ?? 0)); const recent = allSessions.slice(0, 10); const totalSessions = allSessions.length; const summary: StatusSummary = { linkChannel: linkContext ? { id: linkContext.plugin.id, label: linkContext.plugin.meta.label ?? "Channel", linked: linkContext.linked, authAgeMs: linkContext.authAgeMs, } : undefined, heartbeat: { defaultAgentId: agentList.defaultId, agents: heartbeatAgents, }, channelSummary, queuedSystemEvents, sessions: { paths: Array.from(paths), count: totalSessions, defaults: { model: configModel ?? null, contextTokens: configContextTokens ?? null, }, recent, byAgent, }, }; return includeSensitive ? summary : redactSensitiveStatusSummary(summary); }