import { buildSingleTurnPromptLog } from "../promptLogging.ts"; import { clamp } from "../utils.ts"; import { OPENAI_ASR_SESSION_IDLE_TTL_MS, OPENAI_TOOL_CALL_EVENT_MAX, RECENT_ENGAGEMENT_WINDOW_MS, VOICE_DECIDER_HISTORY_MAX_TURNS, VOICE_MEMBERSHIP_EVENT_PROMPT_LIMIT, VOICE_TRANSCRIPT_TIMELINE_MAX_TURNS } from "./voiceSessionManager.constants.ts"; import { listActiveNativeDiscordScreenSharers } from "./nativeDiscordScreenShare.ts"; import { isRealtimeMode, resolveRealtimeProvider } from "./voiceSessionHelpers.ts"; import type { StreamWatchNoteEntry, VoiceAddressingState, VoiceConversationContext, VoiceLivePromptSnapshotEntry, VoiceMembershipPromptEntry, VoiceSession, VoiceSessionDurableContextCategory } from "./voiceSessionTypes.ts";
type RuntimeSnapshotClientLike = { users?: { cache?: { get?: (userId: string) => { displayName?: string | null; globalName?: string | null; username?: string | null; } | null; } | null; } | null; } | null;
type RuntimeSnapshotReplyManagerLike = { syncAssistantOutputState: ( session: VoiceSession, source: string ) => { phase?: string | null; } | null; };
type RuntimeSnapshotDeferredActionQueueLike = { getDeferredQueuedUserTurns: (session: VoiceSession) => unknown[]; };
type StreamWatchNotePayloadLike = { prompt?: string | null; notes?: unknown[]; lastAt?: number; provider?: string | null; model?: string | null; } | null;
type VoiceRuntimeSnapshotDurableContextEntry = { text: string; category: VoiceSessionDurableContextCategory; at: string | null; };
interface VoiceRuntimeSnapshotDeps { client?: RuntimeSnapshotClientLike; replyManager: RuntimeSnapshotReplyManagerLike; deferredActionQueue: RuntimeSnapshotDeferredActionQueueLike; getVoiceChannelParticipants: ( session: VoiceSession ) => Array<{ userId: string; displayName: string }>; getRecentVoiceMembershipEvents: ( session: VoiceSession, args: { maxItems: number } ) => VoiceMembershipPromptEntry[]; buildVoiceConversationContext: (args: { session: VoiceSession; now: number; }) => VoiceConversationContext | null; buildVoiceAddressingState: (args: { session: VoiceSession; userId?: string | null; now?: number; maxItems?: number; }) => VoiceAddressingState | null; getStreamWatchNotesForPrompt: ( session: VoiceSession, settings: Record<string, unknown> | null ) => StreamWatchNotePayloadLike; snapshotMusicRuntimeState: (session: VoiceSession) => unknown; }
function toIsoOrNull(value: number | null | undefined) { return value ? new Date(value).toISOString() : null; }
function normalizeLoggedPromptBundle(value: unknown) { if (!value || typeof value !== "object" || Array.isArray(value)) return null; const bundle = value as { hiddenByDefault?: unknown; systemPrompt?: unknown; initialUserPrompt?: unknown; followupUserPrompts?: unknown; followupSteps?: unknown; tools?: unknown; }; const followupUserPrompts = Array.isArray(bundle.followupUserPrompts) ? bundle.followupUserPrompts.map((entry) => String(entry || "")) : []; const followupSteps = Number(bundle.followupSteps); const tools = Array.isArray(bundle.tools) ? bundle.tools .map((t) => { if (!t || typeof t !== "object") return null; const tool = t as { name?: unknown; description?: unknown; parameters?: unknown }; const name = String(tool.name || "").trim(); return name ? { name, description: String(tool.description || ""), parameters: tool.parameters && typeof tool.parameters === "object" ? tool.parameters as Record<string, unknown> : null } : null; }) .filter((t): t is { name: string; description: string; parameters: Record<string, unknown> | null } => t !== null) : [];
return { hiddenByDefault: bundle.hiddenByDefault !== false, systemPrompt: String(bundle.systemPrompt || ""), initialUserPrompt: String(bundle.initialUserPrompt || ""), followupUserPrompts, followupSteps: Number.isFinite(followupSteps) ? Math.max(0, Math.floor(followupSteps)) : followupUserPrompts.length, tools }; }
function buildPromptSnapshotEntry(entry: VoiceLivePromptSnapshotEntry | null | undefined) { if (!entry || typeof entry !== "object") return null; const replyPrompts = normalizeLoggedPromptBundle(entry.replyPrompts); if (!replyPrompts) return null; return { updatedAt: toIsoOrNull(entry.updatedAt), source: String(entry.source || "").trim() || null, replyPrompts }; }
function buildRecentTurnAddressing(row: VoiceSession["transcriptTurns"][number]) { return row?.addressing && typeof row.addressing === "object" ? { talkingTo: row.addressing.talkingTo || null, directedConfidence: Number.isFinite(Number(row.addressing.directedConfidence)) ? Number(clamp(Number(row.addressing.directedConfidence), 0, 1).toFixed(3)) : 0, source: row.addressing.source || null, reason: row.addressing.reason || null } : null; }
function buildMemoryFactSnapshot(row: unknown) { if (!row || typeof row !== "object" || Array.isArray(row)) return null; const record = row as { id?: unknown; created_at?: unknown; updated_at?: unknown; guild_id?: unknown; channel_id?: unknown; subject?: unknown; fact?: unknown; fact_type?: unknown; evidence_text?: unknown; source_message_id?: unknown; confidence?: unknown; }; const fact = String(record.fact || "").trim(); if (!fact) return null; const confidence = Number(record.confidence); return { id: Number.isInteger(Number(record.id)) ? Number(record.id) : null, createdAt: record.created_at ? String(record.created_at) : null, updatedAt: record.updated_at ? String(record.updated_at) : null, guildId: record.guild_id ? String(record.guild_id) : null, channelId: record.channel_id ? String(record.channel_id) : null, subject: String(record.subject || "").trim() || null, fact, factType: String(record.fact_type || "").trim() || null, evidenceText: record.evidence_text ? String(record.evidence_text) : null, sourceMessageId: record.source_message_id ? String(record.source_message_id) : null, confidence: Number.isFinite(confidence) ? Number(clamp(confidence, 0, 1).toFixed(3)) : null }; }
function buildLatencySnapshot(session: VoiceSession) { const stages = Array.isArray(session.latencyStages) ? session.latencyStages : []; if (stages.length === 0) return null;
const recentTurns = stages.slice(-8).reverse().map((entry) => ({ at: new Date(entry.at).toISOString(), finalizedToAsrStartMs: entry.finalizedToAsrStartMs ?? null, asrToGenerationStartMs: entry.asrToGenerationStartMs ?? null, generationToReplyRequestMs: entry.generationToReplyRequestMs ?? null, replyRequestToAudioStartMs: entry.replyRequestToAudioStartMs ?? null, totalMs: entry.totalMs ?? null, queueWaitMs: entry.queueWaitMs ?? null, pendingQueueDepth: entry.pendingQueueDepth ?? null })); const avg = (field: "finalizedToAsrStartMs" | "asrToGenerationStartMs" | "generationToReplyRequestMs" | "replyRequestToAudioStartMs" | "totalMs") => { const values = stages .map((entry) => entry[field]) .filter((value): value is number => Number.isFinite(value) && value >= 0); return values.length > 0 ? Math.round(values.reduce((sum, value) => sum + value, 0) / values.length) : null; };
return { recentTurns, averages: { finalizedToAsrStartMs: avg("finalizedToAsrStartMs"), asrToGenerationStartMs: avg("asrToGenerationStartMs"), generationToReplyRequestMs: avg("generationToReplyRequestMs"), replyRequestToAudioStartMs: avg("replyRequestToAudioStartMs"), totalMs: avg("totalMs") }, turnCount: stages.length }; }
function buildAsrSessionsSnapshot( session: VoiceSession, { now, participantDisplayByUserId }: { now: number; participantDisplayByUserId: Map<string, string>; } ) { const asrMap = session.openAiAsrSessions instanceof Map ? session.openAiAsrSessions : null; if (!asrMap || asrMap.size === 0) return null;
return [...asrMap.entries()].map(([uid, asr]) => { const ws = asr?.client?.ws; const connected = Boolean(ws && ws.readyState === 1); const idleTtlMs = Math.max( 1_000, Number(session.openAiAsrSessionIdleTtlMs || OPENAI_ASR_SESSION_IDLE_TTL_MS) ); const lastActivityMs = Math.max( Number(asr.lastAudioAt || 0), Number(asr.lastTranscriptAt || 0) ); const idleMs = lastActivityMs > 0 ? Math.max(0, now - lastActivityMs) : null; return { userId: String(uid || ""), displayName: participantDisplayByUserId.get(String(uid || "")) || null, connected, phase: String(asr.phase || "idle"), connectedAt: asr.connectedAt > 0 ? new Date(asr.connectedAt).toISOString() : null, lastAudioAt: asr.lastAudioAt > 0 ? new Date(asr.lastAudioAt).toISOString() : null, lastTranscriptAt: asr.lastTranscriptAt > 0 ? new Date(asr.lastTranscriptAt).toISOString() : null, idleMs, idleTtlMs, hasIdleTimer: Boolean(asr.idleTimer), pendingAudioBytes: Number(asr.pendingAudioBytes || 0), pendingAudioChunks: Array.isArray(asr.pendingAudioChunks) ? asr.pendingAudioChunks.length : 0, utterance: asr.utterance ? { partialText: String(asr.utterance.partialText || "").slice(0, 200), finalSegments: Array.isArray(asr.utterance.finalSegments) ? asr.utterance.finalSegments.length : 0, bytesSent: Number(asr.utterance.bytesSent || 0) } : null, model: String( asr.client?.sessionConfig?.inputTranscriptionModel || session.openAiPerUserAsrModel || "" ).trim() || null, sessionId: asr.client?.sessionId || null }; }); }
function buildSharedAsrSessionSnapshot( session: VoiceSession, { now, participantDisplayByUserId }: { now: number; participantDisplayByUserId: Map<string, string>; } ) { const shared = session.openAiSharedAsrState && typeof session.openAiSharedAsrState === "object" ? session.openAiSharedAsrState : null; if (!shared) return null;
const ws = shared.client?.ws; const connected = Boolean(ws && ws.readyState === 1); const idleTtlMs = Math.max( 1_000, Number(session.openAiAsrSessionIdleTtlMs || OPENAI_ASR_SESSION_IDLE_TTL_MS) ); const lastActivityMs = Math.max( Number(shared.lastAudioAt || 0), Number(shared.lastTranscriptAt || 0) ); const idleMs = lastActivityMs > 0 ? Math.max(0, now - lastActivityMs) : null; const activeUserId = String(shared.userId || "").trim(); return { connected, phase: String(shared.phase || "idle"), userId: activeUserId || null, displayName: activeUserId ? participantDisplayByUserId.get(activeUserId) || null : null, connectedAt: shared.connectedAt > 0 ? new Date(shared.connectedAt).toISOString() : null, lastAudioAt: shared.lastAudioAt > 0 ? new Date(shared.lastAudioAt).toISOString() : null, lastTranscriptAt: shared.lastTranscriptAt > 0 ? new Date(shared.lastTranscriptAt).toISOString() : null, idleMs, idleTtlMs, hasIdleTimer: Boolean(shared.idleTimer), pendingAudioBytes: Number(shared.pendingAudioBytes || 0), pendingAudioChunks: Array.isArray(shared.pendingAudioChunks) ? shared.pendingAudioChunks.length : 0, pendingCommitResolvers: Array.isArray(shared.pendingCommitResolvers) ? shared.pendingCommitResolvers.length : 0, pendingCommitRequests: Array.isArray(shared.pendingCommitRequests) ? shared.pendingCommitRequests.length : 0, transcriptByItemIds: shared.finalTranscriptsByItemId instanceof Map ? shared.finalTranscriptsByItemId.size : 0, speakerByItemIds: shared.itemIdToUserId instanceof Map ? shared.itemIdToUserId.size : 0, utterance: shared.utterance ? { partialText: String(shared.utterance.partialText || "").slice(0, 200), finalSegments: Array.isArray(shared.utterance.finalSegments) ? shared.utterance.finalSegments.length : 0, bytesSent: Number(shared.utterance.bytesSent || 0) } : null, model: String( shared.client?.sessionConfig?.inputTranscriptionModel || session.openAiPerUserAsrModel || "" ).trim() || null, sessionId: shared.client?.sessionId || null }; }
export function buildVoiceRuntimeSnapshot( sessions: Map<string, VoiceSession>, deps: VoiceRuntimeSnapshotDeps ) { const runtimeSessions = [...sessions.values()].map((session) => { const now = Date.now(); const participants = deps.getVoiceChannelParticipants(session); const participantDisplayByUserId = new Map( participants.map((entry) => [String(entry?.userId || ""), String(entry?.displayName || "")]) ); const membershipEvents = deps.getRecentVoiceMembershipEvents(session, { maxItems: VOICE_MEMBERSHIP_EVENT_PROMPT_LIMIT }); const activeCaptureEntries = session.userCaptures instanceof Map ? [...session.userCaptures.entries()] : []; const activeCaptures = activeCaptureEntries .map(([rawUserId, rawCapture]) => { const userId = String(rawUserId || "").trim(); if (!userId) return null; const capture = rawCapture && typeof rawCapture === "object" ? rawCapture : null; const startedAtMs = Number(capture && "startedAt" in capture ? capture.startedAt : 0); const startedAt = Number.isFinite(startedAtMs) && startedAtMs > 0 ? new Date(startedAtMs).toISOString() : null; const ageMs = Number.isFinite(startedAtMs) && startedAtMs > 0 ? Math.max(0, Math.round(now - startedAtMs)) : null; const participantDisplayName = String(participantDisplayByUserId.get(userId) || "").trim(); const membershipDisplayName = String( membershipEvents .slice() .reverse() .find((entry) => String(entry?.userId || "") === userId) ?.displayName || "" ).trim(); const cachedUser = deps.client?.users?.cache?.get?.(userId) || null; const cachedDisplayName = String( cachedUser?.displayName || cachedUser?.globalName || cachedUser?.username || "" ).trim(); const displayName = participantDisplayName || membershipDisplayName || cachedDisplayName || null; return { userId, displayName, startedAt, ageMs }; }) .filter((entry) => entry !== null); const sessionFactProfiles = session.factProfiles instanceof Map ? [...session.factProfiles.entries()] : []; const memoryFactProfiles = sessionFactProfiles .map(([rawUserId, rawProfile]) => { const userId = String(rawUserId || "").trim(); if (!userId) return null; const profile = rawProfile && typeof rawProfile === "object" ? rawProfile : null; const participantDisplayName = String(participantDisplayByUserId.get(userId) || "").trim(); const membershipDisplayName = String( membershipEvents .slice() .reverse() .find((entry) => String(entry?.userId || "") === userId) ?.displayName || "" ).trim(); const cachedUser = deps.client?.users?.cache?.get?.(userId) || null; const cachedDisplayName = String( cachedUser?.displayName || cachedUser?.globalName || cachedUser?.username || "" ).trim(); const displayName = participantDisplayName || membershipDisplayName || cachedDisplayName || null; const loadedAtMs = Number(profile && "loadedAt" in profile ? profile.loadedAt : 0); return { userId, displayName, loadedAt: Number.isFinite(loadedAtMs) && loadedAtMs > 0 ? new Date(loadedAtMs).toISOString() : null, userFacts: Array.isArray(profile && "userFacts" in profile ? profile.userFacts : null) ? (profile.userFacts as unknown[]) .map((row) => buildMemoryFactSnapshot(row)) .filter((row) => row !== null) : [] }; }) .filter((entry) => entry !== null); const guildFactProfile = session.guildFactProfile && typeof session.guildFactProfile === "object" ? { loadedAt: Number(session.guildFactProfile.loadedAt || 0) > 0 ? new Date(Number(session.guildFactProfile.loadedAt)).toISOString() : null, selfFacts: Array.isArray(session.guildFactProfile.selfFacts) ? session.guildFactProfile.selfFacts .map((row) => buildMemoryFactSnapshot(row)) .filter((row) => row !== null) : [], loreFacts: Array.isArray(session.guildFactProfile.loreFacts) ? session.guildFactProfile.loreFacts .map((row) => buildMemoryFactSnapshot(row)) .filter((row) => row !== null) : [] } : null; const wakeContext = deps.buildVoiceConversationContext({ session, now }); const addressingState = deps.buildVoiceAddressingState({ session, now }); const modelTurns = Array.isArray(session.recentVoiceTurns) ? session.recentVoiceTurns : []; const transcriptTurns = Array.isArray(session.transcriptTurns) ? session.transcriptTurns : []; const deferredQueue = deps.deferredActionQueue.getDeferredQueuedUserTurns(session); const generationSummary = session.modelContextSummary && typeof session.modelContextSummary === "object" ? session.modelContextSummary.generation || null : null; const deciderSummary = session.modelContextSummary && typeof session.modelContextSummary === "object" ? session.modelContextSummary.decider || null : null; const streamWatchRawEntries: StreamWatchNoteEntry[] = Array.isArray(session.streamWatch?.noteEntries) ? session.streamWatch.noteEntries : []; const streamWatchVisualFeed = streamWatchRawEntries .map((entry) => { if (!entry || typeof entry !== "object") return null; const text = String(entry.text || "").trim(); if (!text) return null; const atMs = Number(entry.at || 0); return { text: text.slice(0, 220), at: Number.isFinite(atMs) && atMs > 0 ? new Date(atMs).toISOString() : null, provider: String(entry.provider || "").trim() || null, model: String(entry.model || "").trim() || null, speakerName: String(entry.speakerName || "").trim() || null }; }) .filter((entry) => entry !== null); const streamWatchNotes = deps.getStreamWatchNotesForPrompt( session, session.settingsSnapshot || null ); const activeRealtimeInstructions = String(session.lastRealtimeInstructions || session.baseVoiceInstructions || "").trim(); const instructionsPromptState = activeRealtimeInstructions ? { updatedAt: toIsoOrNull(session.lastRealtimeInstructionsAt || session.startedAt), source: session.lastRealtimeInstructionsAt > 0 ? "realtime_instruction_refresh" : "session_start", replyPrompts: normalizeLoggedPromptBundle( buildSingleTurnPromptLog({ systemPrompt: activeRealtimeInstructions, userPrompt: "" }) ) } : null; const durableContext: VoiceRuntimeSnapshotDurableContextEntry[] = (Array.isArray(session.durableContext) ? session.durableContext : []) .map((entry) => { const text = String(entry?.text || "").replace(/\s+/g, " ").trim(); if (!text) return null; const rawCategory = String(entry?.category || "").trim().toLowerCase(); const category: VoiceSessionDurableContextCategory = rawCategory === "plan" || rawCategory === "preference" || rawCategory === "relationship" ? rawCategory : "fact"; const atMs = Number(entry?.at || 0); return { text: text.slice(0, 240), category, at: Number.isFinite(atMs) && atMs > 0 ? new Date(atMs).toISOString() : null }; }) .filter((entry): entry is VoiceRuntimeSnapshotDurableContextEntry => entry !== null) .slice(-50); const streamWatchLatestFrameDataBase64 = String(session.streamWatch?.latestFrameDataBase64 || "").trim(); const streamWatchLatestFrameApproxBytes = streamWatchLatestFrameDataBase64 ? Math.max(0, Math.floor((streamWatchLatestFrameDataBase64.length * 3) / 4)) : 0; const nativeDiscordScreenSharers = listActiveNativeDiscordScreenSharers(session).map((entry) => { const displayName = String(participantDisplayByUserId.get(entry.userId) || "").trim() || null; return { userId: entry.userId, displayName, codec: entry.codec || null, streamCount: entry.streams.length, lastFrameAt: toIsoOrNull(entry.lastFrameAt), updatedAt: toIsoOrNull(entry.updatedAt) }; });
return {
sessionId: session.id,
guildId: session.guildId,
voiceChannelId: session.voiceChannelId,
textChannelId: session.textChannelId,
startedAt: new Date(session.startedAt).toISOString(),
lastActivityAt: new Date(session.lastActivityAt).toISOString(),
maxEndsAt: toIsoOrNull(session.maxEndsAt),
inactivityEndsAt: toIsoOrNull(session.inactivityEndsAt),
activeInputStreams: session.userCaptures.size,
activeCaptures,
soundboard: {
playCount: session.soundboard?.playCount || 0,
lastPlayedAt: toIsoOrNull(session.soundboard?.lastPlayedAt)
},
mode: session.mode || "voice_agent",
realtimeToolOwnership: session.realtimeToolOwnership || "transport_only",
botTurnOpen: Boolean(session.botTurnOpen),
assistantOutput: {
phase: deps.replyManager.syncAssistantOutputState(session, "runtime_state")?.phase || "idle",
reason: session.assistantOutput?.reason || null,
lastTrigger: session.assistantOutput?.lastTrigger || null,
phaseEnteredAt: Number(session.assistantOutput?.phaseEnteredAt || 0) > 0
? new Date(Number(session.assistantOutput?.phaseEnteredAt || 0)).toISOString()
: null,
requestId: Number.isFinite(Number(session.assistantOutput?.requestId))
? Math.round(Number(session.assistantOutput?.requestId))
: null,
ttsPlaybackState: session.assistantOutput?.ttsPlaybackState || "idle",
ttsBufferedSamples: Math.max(0, Number(session.assistantOutput?.ttsBufferedSamples || 0))
},
playbackArm: {
armed: Boolean(session.playbackArmed),
reason: session.playbackArmedReason || null,
armedAt: toIsoOrNull(session.playbackArmedAt)
},
conversation: {
lastAssistantReplyAt: toIsoOrNull(session.lastAssistantReplyAt),
lastDirectAddressAt: toIsoOrNull(session.lastDirectAddressAt),
lastDirectAddressUserId: session.lastDirectAddressUserId || null,
musicWakeLatchedUntil: Number(session.musicWakeLatchedUntil || 0) > 0
? new Date(Number(session.musicWakeLatchedUntil)).toISOString()
: null,
musicWakeLatchedByUserId: session.musicWakeLatchedByUserId || null,
wake: {
attentionMode: wakeContext?.attentionMode || "AMBIENT",
active: wakeContext?.attentionMode === "ACTIVE",
currentSpeakerActive: Boolean(wakeContext?.currentSpeakerActive),
recentAssistantReply: Boolean(wakeContext?.recentAssistantReply),
recentDirectAddress: Boolean(wakeContext?.recentDirectAddress),
msSinceAssistantReply: Number.isFinite(wakeContext?.msSinceAssistantReply)
? Math.round(wakeContext.msSinceAssistantReply)
: null,
msSinceDirectAddress: Number.isFinite(wakeContext?.msSinceDirectAddress)
? Math.round(wakeContext.msSinceDirectAddress)
: null,
windowMs: RECENT_ENGAGEMENT_WINDOW_MS
},
thoughtEngine: {
busy: Boolean(session.thoughtLoopBusy),
nextAttemptAt: toIsoOrNull(session.nextThoughtAt),
lastAttemptAt: toIsoOrNull(session.lastThoughtAttemptAt),
lastSpokenAt: toIsoOrNull(session.lastThoughtSpokenAt),
pendingThought: session.pendingAmbientThought
? {
id: String(session.pendingAmbientThought.id || ""),
status: session.pendingAmbientThought.status || "queued",
text: String(session.pendingAmbientThought.currentText || ""),
draftText: String(session.pendingAmbientThought.draftText || ""),
trigger: String(session.pendingAmbientThought.trigger || ""),
createdAt: toIsoOrNull(session.pendingAmbientThought.createdAt),
updatedAt: toIsoOrNull(session.pendingAmbientThought.updatedAt),
basisAt: toIsoOrNull(session.pendingAmbientThought.basisAt),
notBeforeAt: toIsoOrNull(session.pendingAmbientThought.notBeforeAt),
expiresAt: toIsoOrNull(session.pendingAmbientThought.expiresAt),
ageMs: Math.max(0, Math.round(now - Number(session.pendingAmbientThought.createdAt || now))),
revision: Math.max(1, Number(session.pendingAmbientThought.revision || 1)),
lastDecisionReason: session.pendingAmbientThought.lastDecisionReason || null,
lastDecisionAction: session.pendingAmbientThought.lastDecisionAction || null,
memoryFactCount: Math.max(0, Number(session.pendingAmbientThought.memoryFactCount || 0)),
usedMemory: Boolean(session.pendingAmbientThought.usedMemory),
invalidatedAt: toIsoOrNull(session.pendingAmbientThought.invalidatedAt),
invalidatedByUserId: session.pendingAmbientThought.invalidatedByUserId || null,
invalidationReason: session.pendingAmbientThought.invalidationReason || null
}
: null
},
addressing: addressingState,
modelContext: {
generation: generationSummary,
decider: deciderSummary,
trackedTurns: modelTurns.length,
trackedTurnLimit: VOICE_DECIDER_HISTORY_MAX_TURNS,
trackedTranscriptTurns: transcriptTurns.length
}
},
participants: participants.map((participant) => ({
userId: participant.userId,
displayName: participant.displayName
})),
participantCount: participants.length,
memory: {
factProfiles: memoryFactProfiles,
guildFactProfile
},
membershipEvents: membershipEvents.map((entry) => ({
userId: entry.userId,
displayName: entry.displayName,
eventType: entry.eventType,
at: new Date(entry.at).toISOString(),
ageMs: Math.max(0, Math.round(entry.ageMs))
})),
pendingDeferredTurns: deferredQueue.length,
recentTurns: transcriptTurns.slice(-VOICE_TRANSCRIPT_TIMELINE_MAX_TURNS).map((turn) => ({
kind: turn.kind || "speech",
role: turn.role,
speakerName: turn.speakerName || "",
text: String(turn.text || ""),
at: turn.at ? new Date(turn.at).toISOString() : null,
addressing: buildRecentTurnAddressing(turn)
})),
durableContext,
lastGenerationContext: session.lastGenerationContext || null,
promptState: {
instructions: instructionsPromptState,
classifier: buildPromptSnapshotEntry(session.livePromptState?.classifier),
generation: buildPromptSnapshotEntry(session.livePromptState?.generation),
bridge: buildPromptSnapshotEntry(session.livePromptState?.bridge)
},
streamWatch: {
active: Boolean(session.streamWatch?.active),
targetUserId: session.streamWatch?.targetUserId || null,
requestedByUserId: session.streamWatch?.requestedByUserId || null,
lastFrameAt: toIsoOrNull(session.streamWatch?.lastFrameAt),
lastCommentaryAt: toIsoOrNull(session.streamWatch?.lastCommentaryAt),
lastCommentaryNote: session.streamWatch?.lastCommentaryNote || null,
lastMemoryRecapAt: toIsoOrNull(session.streamWatch?.lastMemoryRecapAt),
lastMemoryRecapText: session.streamWatch?.lastMemoryRecapText || null,
lastMemoryRecapDurableSaved: Boolean(session.streamWatch?.lastMemoryRecapDurableSaved),
lastMemoryRecapReason: session.streamWatch?.lastMemoryRecapReason || null,
latestFrameAt: toIsoOrNull(session.streamWatch?.latestFrameAt),
latestFrameMimeType: session.streamWatch?.latestFrameMimeType || null,
latestFrameApproxBytes: streamWatchLatestFrameApproxBytes,
acceptedFrameCountInWindow: Number(session.streamWatch?.acceptedFrameCountInWindow || 0),
frameWindowStartedAt: toIsoOrNull(session.streamWatch?.frameWindowStartedAt),
lastNoteAt: toIsoOrNull(session.streamWatch?.lastNoteAt),
lastNoteProvider: session.streamWatch?.lastNoteProvider || null,
lastNoteModel: session.streamWatch?.lastNoteModel || null,
noteCount: Array.isArray(session.streamWatch?.noteEntries)
? session.streamWatch.noteEntries.length
: 0,
ingestedFrameCount: Number(session.streamWatch?.ingestedFrameCount || 0),
visualFeed: streamWatchVisualFeed,
notePayload: streamWatchNotes
? {
prompt: String(streamWatchNotes.prompt || "").trim(),
notes: Array.isArray(streamWatchNotes.notes)
? streamWatchNotes.notes
.map((note) => String(note || "").trim())
.filter(Boolean)
.slice(-24)
: [],
lastAt: Number(streamWatchNotes.lastAt || 0)
? new Date(Number(streamWatchNotes.lastAt || 0)).toISOString()
: null,
provider: streamWatchNotes.provider || null,
model: streamWatchNotes.model || null
}
: null,
nativeDiscord: {
activeSharerCount: nativeDiscordScreenSharers.length,
subscribedTargetUserId: session.nativeScreenShare?.subscribedTargetUserId || null,
decodeInFlight: Boolean(session.nativeScreenShare?.decodeInFlight),
lastDecodeAttemptAt: toIsoOrNull(session.nativeScreenShare?.lastDecodeAttemptAt),
lastDecodeSuccessAt: toIsoOrNull(session.nativeScreenShare?.lastDecodeSuccessAt),
lastDecodeFailureAt: toIsoOrNull(session.nativeScreenShare?.lastDecodeFailureAt),
lastDecodeFailureReason: session.nativeScreenShare?.lastDecodeFailureReason || null,
ffmpegAvailable:
typeof session.nativeScreenShare?.ffmpegAvailable === "boolean"
? session.nativeScreenShare.ffmpegAvailable
: null,
activeSharers: nativeDiscordScreenSharers
}
},
asrSessions: buildAsrSessionsSnapshot(session, {
now,
participantDisplayByUserId
}),
sharedAsrSession: buildSharedAsrSessionSnapshot(session, {
now,
participantDisplayByUserId
}),
brainTools: (() => {
if (session.realtimeToolOwnership !== "provider_native") return null;
const tools = Array.isArray(session.realtimeToolDefinitions) ? session.realtimeToolDefinitions : [];
if (!tools.length) return null;
return tools.map((tool) => ({
name: String(tool?.name || ""),
toolType: tool?.toolType === "mcp" ? "mcp" : "function",
serverName: tool?.serverName || null,
description: String(tool?.description || "")
}));
})(),
toolCalls: (() => {
const events = Array.isArray(session.toolCallEvents) ? session.toolCallEvents : [];
if (!events.length) return null;
return events.slice(-OPENAI_TOOL_CALL_EVENT_MAX).map((entry) => ({
callId: String(entry?.callId || ""),
toolName: String(entry?.toolName || ""),
toolType: entry?.toolType === "mcp" ? "mcp" : "function",
arguments: entry?.arguments && typeof entry.arguments === "object" ? entry.arguments : {},
startedAt: String(entry?.startedAt || ""),
completedAt: entry?.completedAt ? String(entry.completedAt) : null,
runtimeMs: Number.isFinite(Number(entry?.runtimeMs)) ? Math.round(Number(entry.runtimeMs)) : null,
success: Boolean(entry?.success),
outputSummary:
entry?.outputSummary && typeof entry.outputSummary === "object"
? entry.outputSummary
: entry?.outputSummary
? String(entry.outputSummary)
: null,
error: entry?.error ? String(entry.error) : null
}));
})(),
mcpStatus: (() => {
const rows = Array.isArray(session.mcpStatus) ? session.mcpStatus : [];
if (!rows.length) return null;
return rows.map((row) => ({
serverName: String(row?.serverName || ""),
connected: Boolean(row?.connected),
tools: Array.isArray(row?.tools)
? row.tools.map((tool) => ({
name: String(tool?.name || ""),
description: String(tool?.description || "")
}))
: [],
lastError: row?.lastError ? String(row.lastError) : null,
lastConnectedAt: row?.lastConnectedAt ? String(row.lastConnectedAt) : null,
lastCallAt: row?.lastCallAt ? String(row.lastCallAt) : null
}));
})(),
music: deps.snapshotMusicRuntimeState(session),
batchAsr: Number(session.pendingFileAsrTurns || 0) > 0
? {
pendingTurns: Number(session.pendingFileAsrTurns || 0),
contextMessages: modelTurns.length
}
: null,
realtime: isRealtimeMode(session.mode)
? {
provider: session.realtimeProvider || resolveRealtimeProvider(session.mode),
inputSampleRateHz: Number(session.realtimeInputSampleRateHz) || 24000,
outputSampleRateHz: Number(session.realtimeOutputSampleRateHz) || 24000,
recentVoiceTurns: modelTurns.length,
replySuperseded: Math.max(0, Number(session.realtimeReplySupersededCount || 0)),
pendingTurns:
(session.realtimeTurnDrainActive ? 1 : 0) +
(Array.isArray(session.pendingRealtimeTurns) ? session.pendingRealtimeTurns.length : 0),
drainActive: Boolean(session.realtimeTurnDrainActive),
coalesceActive: Boolean(session.realtimeTurnCoalesceTimer),
state: session.realtimeClient?.getState?.() || null
}
: null,
latency: buildLatencySnapshot(session)
};
});
return { activeCount: runtimeSessions.length, sessions: runtimeSessions }; }
