fix(gateway): bound realtime relay session expiry

This commit is contained in:
Peter Steinberger
2026-05-30 12:13:10 -04:00
parent 23e1aac9b2
commit 6ac7564918
2 changed files with 36 additions and 3 deletions

View File

@@ -41,6 +41,22 @@ describe("talk realtime gateway relay", () => {
};
}
it("rejects session creation when relay expiry would exceed Date range", () => {
vi.useFakeTimers();
vi.setSystemTime(new Date(8_640_000_000_000_000));
expect(() =>
createTalkRealtimeRelaySession({
context: {} as never,
connId: "conn-1",
provider: createIdleRelayProvider(),
providerConfig: {},
instructions: "brief",
tools: [],
}),
).toThrow("Realtime relay session expiry is outside the supported Date range");
});
function createAbortableRelayRunFixture(provider = createIdleRelayProvider()) {
const abortController = new AbortController();
const broadcast = vi.fn();

View File

@@ -1,6 +1,7 @@
import { randomUUID } from "node:crypto";
import type { OpenClawConfig } from "../config/types.js";
import type { RealtimeVoiceProviderPlugin } from "../plugins/types.js";
import { asDateTimestampMs, resolveExpiresAtMsFromDurationMs } from "../shared/number-coercion.js";
import {
REALTIME_VOICE_AGENT_CONSULT_TOOL_NAME,
buildRealtimeVoiceAgentConsultWorkingResponse,
@@ -275,8 +276,13 @@ function closeRelaySession(session: RelaySession, reason: "completed" | "error")
}
function pruneExpiredRelaySessions(nowMs = Date.now()): void {
const validNowMs = asDateTimestampMs(nowMs);
if (validNowMs === undefined) {
return;
}
for (const session of relaySessions.values()) {
if (nowMs > session.expiresAtMs) {
const expiresAtMs = asDateTimestampMs(session.expiresAtMs);
if (expiresAtMs === undefined || validNowMs > expiresAtMs) {
closeRelaySession(session, "completed");
}
}
@@ -308,7 +314,10 @@ export function createTalkRealtimeRelaySession(
enforceRelaySessionLimits(params.connId);
const forceAgentConsultOnFinalTranscript = params.forceAgentConsultOnFinalTranscript === true;
const relaySessionId = randomUUID();
const expiresAtMs = Date.now() + RELAY_SESSION_TTL_MS;
const expiresAtMs = resolveExpiresAtMsFromDurationMs(RELAY_SESSION_TTL_MS);
if (expiresAtMs === undefined) {
throw new Error("Realtime relay session expiry is outside the supported Date range");
}
const talk = createTalkSessionController(
{
sessionId: relaySessionId,
@@ -688,7 +697,15 @@ function ensureRelayTurn(session: RelaySession): string {
function getRelaySession(relaySessionId: string, connId: string): RelaySession {
const session = relaySessions.get(relaySessionId);
if (!session || session.connId !== connId || Date.now() > session.expiresAtMs) {
const nowMs = asDateTimestampMs(Date.now());
const expiresAtMs = session ? asDateTimestampMs(session.expiresAtMs) : undefined;
if (
!session ||
session.connId !== connId ||
nowMs === undefined ||
expiresAtMs === undefined ||
nowMs > expiresAtMs
) {
if (session) {
closeRelaySession(session, "completed");
}