import type { VerboseLevel } from "../auto-reply/thinking.js"; import { resolveGlobalSingleton } from "../shared/global-singleton.js"; import { notifyListeners, registerListener } from "../shared/listeners.js";
export type AgentRunContext = {
sessionKey?: string;
verboseLevel?: VerboseLevel;
isHeartbeat?: boolean; /** Whether control UI clients should receive chat/agent updates for this run. */
isControlUiVisible?: boolean; /** Timestamp when this context was first registered (for TTL-based cleanup). */
registeredAt?: number; /** Timestamp of last activity (updated on every emitAgentEvent). */
lastActiveAt?: number;
};
function getAgentEventState(): AgentEventState { return resolveGlobalSingleton<AgentEventState>(AGENT_EVENT_STATE_KEY, () => ({
seqByRun: new Map<string, number>(),
listeners: new Set<(evt: AgentEventPayload) => void>(),
runContextById: new Map<string, AgentRunContext>(),
}));
}
export function registerAgentRunContext(runId: string, context: AgentRunContext) { if (!runId) { return;
} const state = getAgentEventState(); const existing = state.runContextById.get(runId); if (!existing) {
state.runContextById.set(runId, {
...context,
registeredAt: context.registeredAt ?? Date.now(),
}); return;
} if (context.sessionKey && existing.sessionKey !== context.sessionKey) {
existing.sessionKey = context.sessionKey;
} if (context.verboseLevel && existing.verboseLevel !== context.verboseLevel) {
existing.verboseLevel = context.verboseLevel;
} if (context.isControlUiVisible !== undefined) {
existing.isControlUiVisible = context.isControlUiVisible;
} if (context.isHeartbeat !== undefined && existing.isHeartbeat !== context.isHeartbeat) {
existing.isHeartbeat = context.isHeartbeat;
}
}
export function getAgentRunContext(runId: string) { return getAgentEventState().runContextById.get(runId);
}
export function clearAgentRunContext(runId: string) { const state = getAgentEventState();
state.runContextById.delete(runId);
state.seqByRun.delete(runId);
}
/** *SweepstaleruncontextsthatexceededthegivenTTL. *Guardsagainstorphanedentrieswhenlifecycle"end"/"error"eventsaremissed.
*/
export function sweepStaleRunContexts(maxAgeMs = 30 * 60 * 1000): number { const state = getAgentEventState(); const now = Date.now();
let swept = 0; for (const [runId, ctx] of state.runContextById.entries()) { // Use lastActiveAt (refreshed on every event) to avoid sweeping active runs. // Fall back to registeredAt, then treat missing timestamps as infinitely old. const lastSeen = ctx.lastActiveAt ?? ctx.registeredAt; const age = lastSeen ? now - lastSeen : Infinity; if (age > maxAgeMs) {
state.runContextById.delete(runId);
state.seqByRun.delete(runId);
swept++;
}
} return swept;
}
export function resetAgentRunContextForTest() {
getAgentEventState().runContextById.clear();
getAgentEventState().seqByRun.clear();
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.