fix: split discord voice timeouts and restore gate on main (#60345) (thanks @geekhuashan)

This commit is contained in:
Peter Steinberger
2026-04-04 02:06:40 +09:00
parent 0c575f37fd
commit be9db66533
4 changed files with 31 additions and 14 deletions

View File

@@ -78,10 +78,9 @@ Docs: https://docs.openclaw.ai
- Discord/ack reactions: keep automatic ACK reaction auth on the active hydrated Discord account so SecretRef-backed and non-default-account reactions stop falling back to stale default config resolution. (#60081) Thanks @FunJim.
- Telegram/model switching: render non-default `/model` callback confirmations with HTML formatting so Telegram shows the selected model in bold instead of raw `**...**` markers. (#60042) Thanks @GitZhangChi.
- Plugins/update: allow `openclaw plugins update` to use `--dangerously-force-unsafe-install` for built-in dangerous-code false positives during plugin updates. (#60066) Thanks @huntharo.
<<<<<<< HEAD
- Gateway/auth: disconnect shared-auth websocket sessions only for effective auth rotations on restart-capable config writes, and keep `config.set` auth edits from dropping still-valid live sessions. (#60387) Thanks @mappel-nv.
- Control UI/chat: keep the Stop button visible during tool-only execution so abortable runs do not fall back to Send while tools are still running. (#54528) thanks @chziyue.
- Gateway/auth: disconnect shared-auth websocket sessions only for effective auth rotations on restart-capable config writes, and keep `config.set` auth edits from dropping still-valid live sessions. (#60387) Thanks @mappel-nv.
- Discord/voice: make READY auto-join fire-and-forget while keeping the shorter initial voice-connect timeout separate from the longer playback-start wait. (#60345) Thanks @geekhuashan.
## 2026.4.2

View File

@@ -292,6 +292,16 @@ describe("DiscordVoiceManager", () => {
);
});
it("keeps the shorter timeout for initial voice connection readiness", async () => {
const connection = createConnectionMock();
joinVoiceChannelMock.mockReturnValueOnce(connection);
const manager = createManager();
await manager.join({ guildId: "g1", channelId: "1001" });
expect(entersStateMock).toHaveBeenCalledWith(connection, "ready", 15_000);
});
it("stores guild metadata on joined voice sessions", async () => {
const manager = createManager();

View File

@@ -32,6 +32,7 @@ const CHANNELS = 2;
const BIT_DEPTH = 16;
const MIN_SEGMENT_SECONDS = 0.35;
const SILENCE_DURATION_MS = 1_000;
const VOICE_CONNECT_READY_TIMEOUT_MS = 15_000;
const PLAYBACK_READY_TIMEOUT_MS = 60_000;
const SPEAKING_READY_TIMEOUT_MS = 60_000;
const DECRYPT_FAILURE_WINDOW_MS = 30_000;
@@ -388,7 +389,7 @@ export class DiscordVoiceManager {
await voiceSdk.entersState(
connection,
voiceSdk.VoiceConnectionStatus.Ready,
PLAYBACK_READY_TIMEOUT_MS,
VOICE_CONNECT_READY_TIMEOUT_MS,
);
logVoiceVerbose(`join: connected to guild ${guildId} channel ${channelId}`);
} catch (err) {

View File

@@ -206,26 +206,33 @@ function createStreamFnWithExtraParams(
if (typeof extraParams.openaiWsWarmup === "boolean") {
streamParams.openaiWsWarmup = extraParams.openaiWsWarmup;
}
const cacheRetention = resolveCacheRetention(
const initialCacheRetention = resolveCacheRetention(
extraParams,
provider,
typeof model?.api === "string" ? model.api : undefined,
typeof model?.id === "string" ? model.id : undefined,
);
if (cacheRetention) {
streamParams.cacheRetention = cacheRetention;
if (Object.keys(streamParams).length > 0 || initialCacheRetention) {
const debugParams = initialCacheRetention
? { ...streamParams, cacheRetention: initialCacheRetention }
: streamParams;
log.debug(`creating streamFn wrapper with params: ${JSON.stringify(debugParams)}`);
}
if (Object.keys(streamParams).length === 0) {
return undefined;
}
log.debug(`creating streamFn wrapper with params: ${JSON.stringify(streamParams)}`);
const underlying = baseStreamFn ?? streamSimple;
const wrappedStreamFn: StreamFn = (model, context, options) => {
return underlying(model, context, {
const wrappedStreamFn: StreamFn = (callModel, context, options) => {
const cacheRetention = resolveCacheRetention(
extraParams,
provider,
typeof callModel.api === "string" ? callModel.api : undefined,
typeof callModel.id === "string" ? callModel.id : undefined,
);
if (Object.keys(streamParams).length === 0 && !cacheRetention) {
return underlying(callModel, context, options);
}
return underlying(callModel, context, {
...streamParams,
...(cacheRetention ? { cacheRetention } : {}),
...options,
});
};