diff --git a/.agents/skills/agent-transcript/scripts/agent-transcript b/.agents/skills/agent-transcript/scripts/agent-transcript index 9ae470ee02bf..9ff72dd28aaa 100755 --- a/.agents/skills/agent-transcript/scripts/agent-transcript +++ b/.agents/skills/agent-transcript/scripts/agent-transcript @@ -56,10 +56,19 @@ function openClawSessionRoots() { const agentsDir = path.join(stateDir, "agents"); if (!fs.existsSync(agentsDir)) return []; try { - return fs + const roots = fs .readdirSync(agentsDir, { withFileTypes: true }) .filter((entry) => entry.isDirectory()) - .map((entry) => path.join(agentsDir, entry.name, "sessions")); + .flatMap((entry) => { + const agentDir = path.join(agentsDir, entry.name); + return [ + path.join(agentDir, "sessions"), + path.join(agentDir, "agent", "sessions"), + path.join(agentDir, "agent", "codex-home", "sessions"), + ]; + }) + .filter((root) => fs.existsSync(root)); + return [...new Set(roots)]; } catch { return []; } diff --git a/extensions/discord/npm-shrinkwrap.json b/extensions/discord/npm-shrinkwrap.json index d02e2c61c100..398411f50df7 100644 --- a/extensions/discord/npm-shrinkwrap.json +++ b/extensions/discord/npm-shrinkwrap.json @@ -11,7 +11,7 @@ "@discordjs/voice": "0.19.2", "discord-api-types": "0.38.48", "https-proxy-agent": "9.0.0", - "libopus-wasm": "0.0.1", + "libopus-wasm": "0.1.0", "prism-media": "1.3.5", "typebox": "1.1.38", "undici": "8.3.0", @@ -424,9 +424,9 @@ } }, "node_modules/libopus-wasm": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/libopus-wasm/-/libopus-wasm-0.0.1.tgz", - "integrity": "sha512-w//dxORfJfLl4quaA5nEA0xZlbbtS/Gba+NrZyCDPmAQh2THX4KI6aI600f+jpxf6v3p9ApGCx0/DCCe4Xftug==", + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/libopus-wasm/-/libopus-wasm-0.1.0.tgz", + "integrity": "sha512-/aurGcAVgy0GcBEUzFaX9pm9qv7zYcy8W5hBXFiK+cyqOXAX4lOS6rlFogkY9CcSIajhjnuXyixsbmziSHCDMQ==", "license": "MIT", "engines": { "node": ">=20" diff --git a/extensions/discord/package.json b/extensions/discord/package.json index 1e767127b9f0..b1d0493c8fe6 100644 --- a/extensions/discord/package.json +++ b/extensions/discord/package.json @@ -11,7 +11,7 @@ "@discordjs/voice": "0.19.2", "discord-api-types": "0.38.48", "https-proxy-agent": "9.0.0", - "libopus-wasm": "0.0.1", + "libopus-wasm": "0.1.0", "prism-media": "1.3.5", "typebox": "1.1.38", "undici": "8.3.0", diff --git a/extensions/discord/src/voice/audio.ts b/extensions/discord/src/voice/audio.ts index cd6ab09b5298..d29ec81cfb52 100644 --- a/extensions/discord/src/voice/audio.ts +++ b/extensions/discord/src/voice/audio.ts @@ -4,8 +4,8 @@ import { Application, createDecoder as createLibopusDecoder, createEncoder as createLibopusEncoder, - type OpusDecoder as LibopusDecoder, - type OpusEncoder as LibopusEncoder, + type OpusDecoderHandle as LibopusDecoder, + type OpusEncoderHandle as LibopusEncoder, } from "libopus-wasm"; import { resamplePcm } from "openclaw/plugin-sdk/realtime-voice"; import { logVerbose, shouldLogVerbose } from "openclaw/plugin-sdk/runtime-env"; @@ -80,7 +80,12 @@ async function createOpusDecoder(params: { return { name: "libopus-wasm", decoder: { - decode: (buffer) => pcmInt16ToBuffer(decoder.decodeFrame(buffer, DISCORD_OPUS_FRAME_SIZE)), + decode: (buffer) => + pcmInt16ToBuffer( + decoder.decode(buffer, { + maxFrameSize: DISCORD_OPUS_FRAME_SIZE, + }), + ), free: () => decoder.free(), }, }; @@ -141,7 +146,13 @@ class DiscordOpusEncodeStream extends Transform { while (this.#buffer.length >= DISCORD_OPUS_FRAME_BYTES) { const frame = this.#buffer.subarray(0, DISCORD_OPUS_FRAME_BYTES); this.#buffer = this.#buffer.subarray(DISCORD_OPUS_FRAME_BYTES); - this.push(Buffer.from(encoder.encodePcm16(frame, DISCORD_OPUS_FRAME_SIZE))); + this.push( + Buffer.from( + encoder.encode(frame, { + frameSize: DISCORD_OPUS_FRAME_SIZE, + }), + ), + ); } done(); } catch (err) { @@ -156,7 +167,13 @@ class DiscordOpusEncodeStream extends Transform { const frame = Buffer.alloc(DISCORD_OPUS_FRAME_BYTES); this.#buffer.copy(frame); this.#buffer = Buffer.alloc(0); - this.push(Buffer.from(encoder.encodePcm16(frame, DISCORD_OPUS_FRAME_SIZE))); + this.push( + Buffer.from( + encoder.encode(frame, { + frameSize: DISCORD_OPUS_FRAME_SIZE, + }), + ), + ); } this.#freeEncoder(); done(); diff --git a/extensions/discord/src/voice/receive-recovery.test.ts b/extensions/discord/src/voice/receive-recovery.test.ts index d052269c530a..ad11d4371c1d 100644 --- a/extensions/discord/src/voice/receive-recovery.test.ts +++ b/extensions/discord/src/voice/receive-recovery.test.ts @@ -29,6 +29,15 @@ describe("voice receive recovery", () => { }); }); + it("treats premature stream close as an expected receive end", () => { + expect(analyzeVoiceReceiveError(new Error("Premature close"))).toEqual({ + message: "Premature close", + isAbortLike: true, + shouldAttemptPassthrough: false, + countsAsDecryptFailure: false, + }); + }); + it("gates recovery after repeated decrypt failures in the same window", () => { const state = createVoiceReceiveRecoveryState(); diff --git a/extensions/discord/src/voice/receive-recovery.ts b/extensions/discord/src/voice/receive-recovery.ts index 2379c02e7619..87d45c3dc3be 100644 --- a/extensions/discord/src/voice/receive-recovery.ts +++ b/extensions/discord/src/voice/receive-recovery.ts @@ -74,6 +74,7 @@ function isAbortLikeReceiveError(err: unknown): boolean { : ""; return ( name === "AbortError" || + message === "Premature close" || message.includes("The operation was aborted") || message.includes("aborted") ); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index de5a80017c6e..d5748782f77a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -616,8 +616,8 @@ importers: specifier: 9.0.0 version: 9.0.0 libopus-wasm: - specifier: 0.0.1 - version: 0.0.1 + specifier: 0.1.0 + version: 0.1.0 prism-media: specifier: 1.3.5 version: 1.3.5 @@ -5479,8 +5479,8 @@ packages: resolution: {integrity: sha512-s6WVJyEZrbm6jhBpiKHsGHyePMrVQKJ85wZCFCr9W4QHv6WTjWIrdvTmO9hDEA3bNK0xkrE2DqrHsXMLWuZpQg==} engines: {node: '>=22.0.0'} - libopus-wasm@0.0.1: - resolution: {integrity: sha512-w//dxORfJfLl4quaA5nEA0xZlbbtS/Gba+NrZyCDPmAQh2THX4KI6aI600f+jpxf6v3p9ApGCx0/DCCe4Xftug==} + libopus-wasm@0.1.0: + resolution: {integrity: sha512-/aurGcAVgy0GcBEUzFaX9pm9qv7zYcy8W5hBXFiK+cyqOXAX4lOS6rlFogkY9CcSIajhjnuXyixsbmziSHCDMQ==} engines: {node: '>=20'} libsignal@6.0.0: @@ -11294,7 +11294,7 @@ snapshots: kysely@0.29.2: {} - libopus-wasm@0.0.1: {} + libopus-wasm@0.1.0: {} libsignal@6.0.0: dependencies: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index f523226c1af2..d4531db95398 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -16,7 +16,7 @@ minimumReleaseAgeExclude: - "basic-ftp" - "baileys@7.0.0-rc13" - "hono" - - "libopus-wasm@0.0.1" + - "libopus-wasm@0.1.0" - "libsignal@6.0.0" - "openclaw" - "protobufjs"