@openclaw/discord 2026.5.25-beta.1 → 2026.5.26-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api.js +1 -1
- package/dist/{channel-CmaowH-7.js → channel-BbDUsfl7.js} +1 -1
- package/dist/channel-plugin-api.js +1 -1
- package/dist/index.js +3 -3
- package/dist/{manager.runtime-C5dZwVX1.js → manager.runtime-BfJrOq-W.js} +135 -47
- package/dist/{message-handler-DUNd0JjP.js → message-handler-YNyW4rsC.js} +2 -2
- package/dist/{message-handler.preflight-DHZfoN6e.js → message-handler.preflight-Dpfm3M4j.js} +2 -2
- package/dist/{message-handler.process-C0xo2jq3.js → message-handler.process-CDxJ-gh_.js} +1 -1
- package/dist/{provider-D-XVxGT7.js → provider-B1wc40FG.js} +579 -192
- package/dist/{provider-session.runtime-C_z8oLu7.js → provider-session.runtime-Dk2Y1FGD.js} +1 -1
- package/dist/provider.runtime-DN-gyQYd.js +2 -0
- package/dist/runtime-api.js +3 -3
- package/dist/{runtime-api.monitor-DgBpOYpt.js → runtime-api.monitor-BJCRxbKe.js} +2 -2
- package/dist/runtime-api.monitor.js +3 -3
- package/dist/test-api.js +1 -1
- package/dist/transcripts-source-api.js +2 -0
- package/dist/{meeting-notes-source-DXpiff3y.js → transcripts-source-emawQzBc.js} +12 -12
- package/node_modules/libopus-wasm/CHANGELOG.md +28 -0
- package/node_modules/libopus-wasm/README.md +202 -41
- package/node_modules/libopus-wasm/dist/discordjs.d.ts +20 -0
- package/node_modules/libopus-wasm/dist/discordjs.d.ts.map +1 -0
- package/node_modules/libopus-wasm/dist/discordjs.js +108 -0
- package/node_modules/libopus-wasm/dist/generated/libopus.generated.mjs +0 -0
- package/node_modules/libopus-wasm/dist/index.d.ts +109 -15
- package/node_modules/libopus-wasm/dist/index.d.ts.map +1 -1
- package/node_modules/libopus-wasm/dist/index.js +476 -70
- package/node_modules/libopus-wasm/package.json +14 -3
- package/npm-shrinkwrap.json +7 -8
- package/openclaw.plugin.json +1 -1
- package/package.json +6 -8
- package/dist/meeting-notes-source-api.js +0 -2
- package/dist/provider.runtime-xwgFlddx.js +0 -2
package/dist/api.js
CHANGED
|
@@ -6,7 +6,7 @@ import { n as fetchDiscord, r as requestDiscord, t as DiscordApiError } from "./
|
|
|
6
6
|
import { i as parseDiscordSendTarget, n as resolveDiscordTarget } from "./target-resolver-CVgOsap6.js";
|
|
7
7
|
import "./targets-CNDNKpqQ.js";
|
|
8
8
|
import { a as getDiscordExecApprovalApprovers, c as shouldSuppressLocalDiscordExecApprovalPrompt, o as isDiscordExecApprovalApprover, s as isDiscordExecApprovalClientEnabled } from "./conversation-identity-Be5JQPP9.js";
|
|
9
|
-
import { i as resolveDiscordGroupToolPolicy, n as collectDiscordStatusIssues, r as resolveDiscordGroupRequireMention, t as discordPlugin } from "./channel-
|
|
9
|
+
import { i as resolveDiscordGroupToolPolicy, n as collectDiscordStatusIssues, r as resolveDiscordGroupRequireMention, t as discordPlugin } from "./channel-BbDUsfl7.js";
|
|
10
10
|
import { t as normalizeExplicitDiscordSessionKey } from "./session-key-normalization-wJgsKPNF.js";
|
|
11
11
|
import { t as discordSetupPlugin } from "./channel.setup-JaozRxYz.js";
|
|
12
12
|
import { n as handleDiscordSubagentEnded, r as handleDiscordSubagentSpawning, t as handleDiscordSubagentDeliveryTarget } from "./subagent-hooks-DHA_1pBI.js";
|
|
@@ -120,7 +120,7 @@ const loadDiscordResolveUsersModule = createLazyRuntimeModule(() => import("./re
|
|
|
120
120
|
const loadDiscordThreadBindingsManagerModule = createLazyRuntimeModule(() => import("./thread-bindings.manager-UJ5FvZfO.js").then((n) => n.a));
|
|
121
121
|
const loadDiscordTargetResolverModule = createLazyRuntimeModule(() => import("./target-resolver-CVgOsap6.js").then((n) => n.r));
|
|
122
122
|
async function loadDiscordProviderRuntime() {
|
|
123
|
-
discordProviderRuntimePromise ??= import("./provider.runtime-
|
|
123
|
+
discordProviderRuntimePromise ??= import("./provider.runtime-DN-gyQYd.js");
|
|
124
124
|
return await discordProviderRuntimePromise;
|
|
125
125
|
}
|
|
126
126
|
async function loadDiscordProbeRuntime() {
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as discordPlugin } from "./channel-
|
|
1
|
+
import { t as discordPlugin } from "./channel-BbDUsfl7.js";
|
|
2
2
|
export { discordPlugin };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { t as discordVoiceMeetingNotesSourceProvider } from "./meeting-notes-source-DXpiff3y.js";
|
|
2
|
-
import "./meeting-notes-source-api.js";
|
|
3
1
|
import { registerDiscordSubagentHooks } from "./subagent-hooks-api.js";
|
|
2
|
+
import { t as discordVoiceTranscriptsSourceProvider } from "./transcripts-source-emawQzBc.js";
|
|
3
|
+
import "./transcripts-source-api.js";
|
|
4
4
|
import { defineBundledChannelEntry } from "openclaw/plugin-sdk/channel-entry-contract";
|
|
5
5
|
//#region extensions/discord/index.ts
|
|
6
6
|
var discord_default = defineBundledChannelEntry({
|
|
@@ -22,7 +22,7 @@ var discord_default = defineBundledChannelEntry({
|
|
|
22
22
|
},
|
|
23
23
|
registerFull(api) {
|
|
24
24
|
registerDiscordSubagentHooks(api);
|
|
25
|
-
api.
|
|
25
|
+
api.registerTranscriptSourceProvider(discordVoiceTranscriptsSourceProvider);
|
|
26
26
|
}
|
|
27
27
|
});
|
|
28
28
|
//#endregion
|
|
@@ -3,12 +3,13 @@ import { c as resolveDiscordAccountAllowFrom } from "./accounts-dXTfmnSZ.js";
|
|
|
3
3
|
import { a as normalizeDiscordSlug, b as formatDiscordUserTag, m as resolveDiscordOwnerAccess } from "./allow-list-BnkWtVpA.js";
|
|
4
4
|
import { i as formatMention } from "./send.outbound-BHQPWbwU.js";
|
|
5
5
|
import { t as getDiscordRuntime } from "./runtime-DgnVQ7zW.js";
|
|
6
|
-
import { o as authorizeDiscordVoiceIngress, u as resolveDiscordVoiceEnabled } from "./provider-
|
|
6
|
+
import { o as authorizeDiscordVoiceIngress, u as resolveDiscordVoiceEnabled } from "./provider-B1wc40FG.js";
|
|
7
7
|
import { t as buildDiscordGroupSystemPrompt } from "./inbound-context-B5EsqsSr.js";
|
|
8
8
|
import { createRequire } from "node:module";
|
|
9
9
|
import { asBoolean, normalizeOptionalString, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
10
10
|
import { resolveAgentRoute } from "openclaw/plugin-sdk/routing";
|
|
11
11
|
import path from "node:path";
|
|
12
|
+
import { resolveFfmpegBin } from "openclaw/plugin-sdk/media-runtime";
|
|
12
13
|
import { createSubsystemLogger, logVerbose, shouldLogVerbose } from "openclaw/plugin-sdk/runtime-env";
|
|
13
14
|
import { stripInlineDirectiveTagsForDisplay } from "openclaw/plugin-sdk/text-chunking";
|
|
14
15
|
import fs from "node:fs/promises";
|
|
@@ -17,22 +18,26 @@ import { formatErrorMessage } from "openclaw/plugin-sdk/ssrf-runtime";
|
|
|
17
18
|
import { REALTIME_VOICE_AGENT_CONSULT_TOOL_NAME, REALTIME_VOICE_AGENT_CONTROL_TOOL, REALTIME_VOICE_AGENT_CONTROL_TOOL_NAME, REALTIME_VOICE_AUDIO_FORMAT_PCM16_24KHZ, buildRealtimeVoiceAgentConsultChatMessage, buildRealtimeVoiceAgentConsultPolicyInstructions, classifySkippableRealtimeVoiceConsultTranscript, controlRealtimeVoiceAgentRun, createRealtimeVoiceAgentTalkbackQueue, createRealtimeVoiceBridgeSession, createRealtimeVoiceForcedConsultCoordinator, createRealtimeVoiceOutputActivityTracker, createRealtimeVoiceTurnContextTracker, matchRealtimeVoiceActivationName, matchRealtimeVoiceConsultQuestions, normalizeSupportedRealtimeVoiceActivationName, parseRealtimeVoiceAgentControlToolArgs, resamplePcm, resolveConfiguredRealtimeVoiceProvider, resolveRealtimeVoiceAgentConsultToolPolicy, resolveRealtimeVoiceAgentConsultTools, resolveRealtimeVoiceAgentConsultToolsAllow, shouldAutoControlRealtimeVoiceAgentText, sortRealtimeVoiceActivationNames } from "openclaw/plugin-sdk/realtime-voice";
|
|
18
19
|
import { agentCommandFromIngress, getTtsProvider, resolveAgentDir, resolveTtsConfig, resolveTtsPrefsPath } from "openclaw/plugin-sdk/agent-runtime";
|
|
19
20
|
import { escapeRegExp } from "openclaw/plugin-sdk/text-utility-runtime";
|
|
21
|
+
import { spawn } from "node:child_process";
|
|
20
22
|
import { PassThrough, Readable, Transform } from "node:stream";
|
|
21
23
|
import { Application, createDecoder, createEncoder } from "libopus-wasm";
|
|
22
|
-
import prism from "prism-media";
|
|
23
24
|
import { resolveRealtimeBootstrapContextInstructions } from "openclaw/plugin-sdk/realtime-bootstrap-context";
|
|
24
25
|
import { parseTtsDirectives } from "openclaw/plugin-sdk/speech";
|
|
25
26
|
//#region extensions/discord/src/voice/audio.ts
|
|
26
27
|
const SAMPLE_RATE = 48e3;
|
|
27
28
|
const CHANNELS = 2;
|
|
28
29
|
const BIT_DEPTH = 16;
|
|
30
|
+
const FFMPEG_ERROR_OUTPUT_BYTES = 8192;
|
|
29
31
|
const DISCORD_OPUS_FRAME_SIZE = 960;
|
|
30
32
|
const DISCORD_OPUS_FRAME_BYTES = DISCORD_OPUS_FRAME_SIZE * CHANNELS * (BIT_DEPTH / 8);
|
|
31
33
|
const FFMPEG_PCM_ARGUMENTS = [
|
|
32
34
|
"-analyzeduration",
|
|
33
35
|
"0",
|
|
34
36
|
"-loglevel",
|
|
35
|
-
"
|
|
37
|
+
"error",
|
|
38
|
+
"-vn",
|
|
39
|
+
"-sn",
|
|
40
|
+
"-dn",
|
|
36
41
|
"-f",
|
|
37
42
|
"s16le",
|
|
38
43
|
"-ar",
|
|
@@ -78,7 +83,7 @@ async function createOpusDecoder(params) {
|
|
|
78
83
|
return {
|
|
79
84
|
name: "libopus-wasm",
|
|
80
85
|
decoder: {
|
|
81
|
-
decode: (buffer) => pcmInt16ToBuffer(decoder.
|
|
86
|
+
decode: (buffer) => pcmInt16ToBuffer(decoder.decode(buffer, { maxFrameSize: DISCORD_OPUS_FRAME_SIZE })),
|
|
82
87
|
free: () => decoder.free()
|
|
83
88
|
}
|
|
84
89
|
};
|
|
@@ -87,19 +92,54 @@ function createDiscordOpusEncodeStream() {
|
|
|
87
92
|
return new DiscordOpusEncodeStream();
|
|
88
93
|
}
|
|
89
94
|
function createDiscordOpusPlaybackStream(input) {
|
|
90
|
-
const
|
|
95
|
+
const inputSource = typeof input === "string" ? input : "pipe:0";
|
|
96
|
+
const ffmpeg = spawn(resolveFfmpegBin(), [
|
|
91
97
|
"-i",
|
|
92
|
-
|
|
93
|
-
...FFMPEG_PCM_ARGUMENTS
|
|
94
|
-
|
|
98
|
+
inputSource,
|
|
99
|
+
...FFMPEG_PCM_ARGUMENTS,
|
|
100
|
+
"pipe:1"
|
|
101
|
+
], {
|
|
102
|
+
stdio: [
|
|
103
|
+
"pipe",
|
|
104
|
+
"pipe",
|
|
105
|
+
"pipe"
|
|
106
|
+
],
|
|
107
|
+
windowsHide: true
|
|
108
|
+
});
|
|
95
109
|
const opusStream = createDiscordOpusEncodeStream();
|
|
96
|
-
|
|
97
|
-
|
|
110
|
+
let stderr = "";
|
|
111
|
+
let ffmpegClosed = false;
|
|
112
|
+
ffmpeg.stderr.setEncoding("utf8");
|
|
113
|
+
ffmpeg.stderr.on("data", (chunk) => {
|
|
114
|
+
if (stderr.length < FFMPEG_ERROR_OUTPUT_BYTES) stderr = `${stderr}${chunk}`.slice(0, FFMPEG_ERROR_OUTPUT_BYTES);
|
|
115
|
+
});
|
|
116
|
+
ffmpeg.once("error", (err) => {
|
|
117
|
+
opusStream.destroy(err);
|
|
118
|
+
});
|
|
119
|
+
ffmpeg.once("close", (code, signal) => {
|
|
120
|
+
ffmpegClosed = true;
|
|
121
|
+
if (code && code !== 0) {
|
|
122
|
+
const suffix = stderr.trim() ? `: ${stderr.trim()}` : "";
|
|
123
|
+
opusStream.destroy(/* @__PURE__ */ new Error(`ffmpeg exited with code ${code}${suffix}`));
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
if (signal) opusStream.destroy(/* @__PURE__ */ new Error(`ffmpeg exited with signal ${signal}`));
|
|
127
|
+
});
|
|
128
|
+
ffmpeg.stdout.on("error", (err) => opusStream.destroy(err));
|
|
129
|
+
ffmpeg.stdin.on("error", (err) => {
|
|
130
|
+
if (err.code !== "EPIPE") opusStream.destroy(err);
|
|
131
|
+
});
|
|
132
|
+
ffmpeg.stdout.pipe(opusStream);
|
|
133
|
+
opusStream.once("close", () => {
|
|
134
|
+
if (!ffmpegClosed && !opusStream.readableEnded) ffmpeg.kill();
|
|
135
|
+
});
|
|
98
136
|
if (typeof input !== "string") {
|
|
99
|
-
input.on("error", (err) =>
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
137
|
+
input.on("error", (err) => {
|
|
138
|
+
ffmpeg.stdin.destroy(err);
|
|
139
|
+
opusStream.destroy(err);
|
|
140
|
+
});
|
|
141
|
+
input.pipe(ffmpeg.stdin);
|
|
142
|
+
} else ffmpeg.stdin.end();
|
|
103
143
|
return opusStream;
|
|
104
144
|
}
|
|
105
145
|
var DiscordOpusEncodeStream = class extends Transform {
|
|
@@ -125,7 +165,7 @@ var DiscordOpusEncodeStream = class extends Transform {
|
|
|
125
165
|
while (this.#buffer.length >= DISCORD_OPUS_FRAME_BYTES) {
|
|
126
166
|
const frame = this.#buffer.subarray(0, DISCORD_OPUS_FRAME_BYTES);
|
|
127
167
|
this.#buffer = this.#buffer.subarray(DISCORD_OPUS_FRAME_BYTES);
|
|
128
|
-
this.push(Buffer.from(encoder.
|
|
168
|
+
this.push(Buffer.from(encoder.encode(frame, { frameSize: DISCORD_OPUS_FRAME_SIZE })));
|
|
129
169
|
}
|
|
130
170
|
done();
|
|
131
171
|
} catch (err) {
|
|
@@ -139,7 +179,7 @@ var DiscordOpusEncodeStream = class extends Transform {
|
|
|
139
179
|
const frame = Buffer.alloc(DISCORD_OPUS_FRAME_BYTES);
|
|
140
180
|
this.#buffer.copy(frame);
|
|
141
181
|
this.#buffer = Buffer.alloc(0);
|
|
142
|
-
this.push(Buffer.from(encoder.
|
|
182
|
+
this.push(Buffer.from(encoder.encode(frame, { frameSize: DISCORD_OPUS_FRAME_SIZE })));
|
|
143
183
|
}
|
|
144
184
|
this.#freeEncoder();
|
|
145
185
|
done();
|
|
@@ -487,6 +527,7 @@ const DISCORD_REALTIME_PENDING_SPEAKER_CONTEXT_LIMIT = 32;
|
|
|
487
527
|
const DISCORD_REALTIME_RECENT_AGENT_PROXY_CONSULT_LIMIT = 16;
|
|
488
528
|
const DISCORD_REALTIME_RECENT_AGENT_PROXY_CONSULT_TTL_MS = 15e3;
|
|
489
529
|
const DISCORD_REALTIME_IGNORED_WAKE_NAME_CONTEXT_TTL_MS = 1e4;
|
|
530
|
+
const DISCORD_REALTIME_WAKE_NAME_FOLLOWUP_TTL_MS = 1e4;
|
|
490
531
|
const DISCORD_REALTIME_LOG_PREVIEW_CHARS = 500;
|
|
491
532
|
const DISCORD_REALTIME_DEFAULT_MIN_BARGE_IN_AUDIO_END_MS = 250;
|
|
492
533
|
const DISCORD_REALTIME_FORCED_CONSULT_FALLBACK_DELAY_MS = 200;
|
|
@@ -786,6 +827,7 @@ var DiscordRealtimeVoiceSession = class {
|
|
|
786
827
|
this.exactSpeechResponseActive = false;
|
|
787
828
|
this.exactSpeechAudioStarted = false;
|
|
788
829
|
this.resetPartialWakeNameTracking();
|
|
830
|
+
this.pendingWakeNameFollowup = void 0;
|
|
789
831
|
this.clearOutputAudio("session-close");
|
|
790
832
|
this.bridge?.close();
|
|
791
833
|
this.bridge = null;
|
|
@@ -1208,17 +1250,31 @@ var DiscordRealtimeVoiceSession = class {
|
|
|
1208
1250
|
const trimmed = text.trim();
|
|
1209
1251
|
if (!trimmed) return;
|
|
1210
1252
|
this.partialUserTranscript = "";
|
|
1211
|
-
const
|
|
1212
|
-
this.
|
|
1253
|
+
const transcriptsTurn = this.peekPendingSpeakerTurn();
|
|
1254
|
+
let transcriptAttribution = this.transcriptAttributionFromTurn(transcriptsTurn);
|
|
1213
1255
|
const wakeNameResult = this.resolveWakeNameTranscript(trimmed);
|
|
1256
|
+
let forcedSpeakerContext;
|
|
1214
1257
|
if (!wakeNameResult.allowed) {
|
|
1215
|
-
this.
|
|
1216
|
-
|
|
1258
|
+
const pendingWakeNameFollowup = this.consumePendingWakeNameFollowup();
|
|
1259
|
+
transcriptAttribution ??= pendingWakeNameFollowup;
|
|
1260
|
+
if (!pendingWakeNameFollowup) {
|
|
1261
|
+
this.recordTranscriptUtterance(trimmed, transcriptAttribution);
|
|
1262
|
+
this.rememberIgnoredWakeNameSpeakerContext(this.consumePendingSpeakerContext());
|
|
1263
|
+
logger$2.info(`discord voice: realtime wake-name gate ignored transcript chars=${trimmed.length} voiceSession=${this.params.entry.voiceSessionKey} agent=${this.params.entry.route.agentId} wakeNames=${this.wakeNames.join(",") || "none"}`);
|
|
1264
|
+
return;
|
|
1265
|
+
}
|
|
1266
|
+
forcedSpeakerContext = pendingWakeNameFollowup.context;
|
|
1267
|
+
logger$2.info(`discord voice: realtime wake-name follow-up accepted chars=${trimmed.length} speaker=${forcedSpeakerContext.speakerLabel} voiceSession=${this.params.entry.voiceSessionKey} agent=${this.params.entry.route.agentId}`);
|
|
1268
|
+
}
|
|
1269
|
+
this.recordTranscriptUtterance(trimmed, transcriptAttribution);
|
|
1270
|
+
const acceptedText = wakeNameResult.allowed ? wakeNameResult.text || trimmed : trimmed;
|
|
1271
|
+
if (wakeNameResult.allowed && !wakeNameResult.text.trim()) {
|
|
1272
|
+
this.armWakeNameFollowup();
|
|
1217
1273
|
return;
|
|
1218
1274
|
}
|
|
1219
|
-
|
|
1275
|
+
if (wakeNameResult.allowed) this.pendingWakeNameFollowup = void 0;
|
|
1220
1276
|
const usesAgentProxy = isDiscordAgentProxyVoiceMode(this.params.mode);
|
|
1221
|
-
const pendingForcedConsult = usesAgentProxy && params.usesRealtimeAgentHandoff ? this.prepareForcedAgentProxyConsult(acceptedText) : void 0;
|
|
1277
|
+
const pendingForcedConsult = usesAgentProxy && params.usesRealtimeAgentHandoff ? this.prepareForcedAgentProxyConsult(acceptedText, forcedSpeakerContext) : void 0;
|
|
1222
1278
|
const control = await maybeControlDiscordVoiceAgentRun({
|
|
1223
1279
|
entry: this.params.entry,
|
|
1224
1280
|
text: acceptedText
|
|
@@ -1236,7 +1292,7 @@ var DiscordRealtimeVoiceSession = class {
|
|
|
1236
1292
|
if (pendingForcedConsult) this.schedulePreparedForcedAgentProxyConsult(pendingForcedConsult);
|
|
1237
1293
|
return;
|
|
1238
1294
|
}
|
|
1239
|
-
this.talkback.enqueue(acceptedText, this.consumePendingSpeakerContext());
|
|
1295
|
+
this.talkback.enqueue(acceptedText, forcedSpeakerContext ?? this.consumePendingSpeakerContext());
|
|
1240
1296
|
}
|
|
1241
1297
|
handlePartialUserTranscript(text) {
|
|
1242
1298
|
if (!this.requireWakeName || this.wakeNameAckedForTurn) return;
|
|
@@ -1269,13 +1325,19 @@ var DiscordRealtimeVoiceSession = class {
|
|
|
1269
1325
|
text
|
|
1270
1326
|
};
|
|
1271
1327
|
}
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1328
|
+
transcriptAttributionFromTurn(turn) {
|
|
1329
|
+
return turn ? {
|
|
1330
|
+
context: turn.context,
|
|
1331
|
+
startedAt: turn.startedAt
|
|
1332
|
+
} : void 0;
|
|
1333
|
+
}
|
|
1334
|
+
recordTranscriptUtterance(text, attribution) {
|
|
1335
|
+
const transcripts = this.params.entry.transcripts;
|
|
1336
|
+
if (!transcripts || !attribution) return;
|
|
1337
|
+
const context = attribution.context;
|
|
1276
1338
|
const utterance = {
|
|
1277
|
-
sessionId:
|
|
1278
|
-
startedAt: new Date(
|
|
1339
|
+
sessionId: transcripts.sessionId,
|
|
1340
|
+
startedAt: new Date(attribution.startedAt).toISOString(),
|
|
1279
1341
|
final: true,
|
|
1280
1342
|
speaker: {
|
|
1281
1343
|
id: context.userId,
|
|
@@ -1289,14 +1351,14 @@ var DiscordRealtimeVoiceSession = class {
|
|
|
1289
1351
|
voiceSessionKey: this.params.entry.voiceSessionKey
|
|
1290
1352
|
}
|
|
1291
1353
|
};
|
|
1292
|
-
Promise.resolve().then(() =>
|
|
1293
|
-
logger$2.warn(`discord voice: realtime
|
|
1354
|
+
Promise.resolve().then(() => transcripts.onUtterance(utterance)).catch((error) => {
|
|
1355
|
+
logger$2.warn(`discord voice: realtime transcripts utterance failed: ${formatErrorMessage(error)}`);
|
|
1294
1356
|
});
|
|
1295
1357
|
}
|
|
1296
1358
|
logAgentControlResult(result) {
|
|
1297
1359
|
logger$2.info(`discord voice: realtime active-run control handled mode=${result.mode} ok=${result.ok} active=${result.active} reason=${result.reason ?? "none"} voiceSession=${this.params.entry.voiceSessionKey} supervisorSession=${this.params.entry.route.sessionKey} agent=${this.params.entry.route.agentId}`);
|
|
1298
1360
|
}
|
|
1299
|
-
prepareForcedAgentProxyConsult(transcript) {
|
|
1361
|
+
prepareForcedAgentProxyConsult(transcript, speakerContext) {
|
|
1300
1362
|
if (this.consultPolicy !== "always" && !this.requireWakeName) return;
|
|
1301
1363
|
const question = transcript.trim();
|
|
1302
1364
|
if (!question) return;
|
|
@@ -1306,7 +1368,7 @@ var DiscordRealtimeVoiceSession = class {
|
|
|
1306
1368
|
logger$2.info(`discord voice: realtime forced agent consult skipped reason=${skipReason} chars=${question.length} speaker=${context?.speakerLabel ?? "unknown"} transcript=${formatRealtimeLogPreview(question)}`);
|
|
1307
1369
|
return;
|
|
1308
1370
|
}
|
|
1309
|
-
let context = this.consumePendingSpeakerContext();
|
|
1371
|
+
let context = speakerContext ?? this.consumePendingSpeakerContext();
|
|
1310
1372
|
if (!context) context = this.consumeRecentIgnoredWakeNameSpeakerContext();
|
|
1311
1373
|
if (!context) {
|
|
1312
1374
|
const recent = this.findRecentAgentProxyConsultContext(question);
|
|
@@ -1357,6 +1419,32 @@ var DiscordRealtimeVoiceSession = class {
|
|
|
1357
1419
|
consumePendingSpeakerContext() {
|
|
1358
1420
|
return this.speakerTurns.consumeAudioContext();
|
|
1359
1421
|
}
|
|
1422
|
+
armWakeNameFollowup() {
|
|
1423
|
+
const turn = this.peekPendingSpeakerTurn();
|
|
1424
|
+
const context = this.consumePendingSpeakerContext();
|
|
1425
|
+
if (!context) {
|
|
1426
|
+
logger$2.warn(`discord voice: realtime wake-name follow-up has no speaker context voiceSession=${this.params.entry.voiceSessionKey} agent=${this.params.entry.route.agentId}`);
|
|
1427
|
+
return;
|
|
1428
|
+
}
|
|
1429
|
+
this.pendingWakeNameFollowup = {
|
|
1430
|
+
context,
|
|
1431
|
+
startedAt: turn?.startedAt ?? Date.now(),
|
|
1432
|
+
expiresAt: Date.now() + DISCORD_REALTIME_WAKE_NAME_FOLLOWUP_TTL_MS
|
|
1433
|
+
};
|
|
1434
|
+
logger$2.info(`discord voice: realtime wake-name follow-up armed speaker=${context.speakerLabel} voiceSession=${this.params.entry.voiceSessionKey} agent=${this.params.entry.route.agentId}`);
|
|
1435
|
+
}
|
|
1436
|
+
consumePendingWakeNameFollowup() {
|
|
1437
|
+
const pending = this.pendingWakeNameFollowup;
|
|
1438
|
+
this.pendingWakeNameFollowup = void 0;
|
|
1439
|
+
if (!pending || Date.now() > pending.expiresAt) return;
|
|
1440
|
+
const currentTurn = this.peekPendingSpeakerTurn();
|
|
1441
|
+
if (currentTurn && currentTurn.context.userId !== pending.context.userId) return;
|
|
1442
|
+
if (currentTurn) this.consumePendingSpeakerContext();
|
|
1443
|
+
return {
|
|
1444
|
+
context: pending.context,
|
|
1445
|
+
startedAt: pending.startedAt
|
|
1446
|
+
};
|
|
1447
|
+
}
|
|
1360
1448
|
rememberIgnoredWakeNameSpeakerContext(context) {
|
|
1361
1449
|
this.speakerTurns.rememberIgnoredContext(context);
|
|
1362
1450
|
}
|
|
@@ -1503,7 +1591,7 @@ function isAbortLikeReceiveError(err) {
|
|
|
1503
1591
|
if (!err || typeof err !== "object") return false;
|
|
1504
1592
|
const name = "name" in err && typeof err.name === "string" ? err.name : "";
|
|
1505
1593
|
const message = "message" in err && typeof err.message === "string" ? err.message : "";
|
|
1506
|
-
return name === "AbortError" || message.includes("The operation was aborted") || message.includes("aborted");
|
|
1594
|
+
return name === "AbortError" || message === "Premature close" || message.includes("The operation was aborted") || message.includes("aborted");
|
|
1507
1595
|
}
|
|
1508
1596
|
function analyzeVoiceReceiveError(err) {
|
|
1509
1597
|
const message = formatErrorMessage(err);
|
|
@@ -1701,9 +1789,9 @@ async function processDiscordVoiceSegment(params) {
|
|
|
1701
1789
|
}
|
|
1702
1790
|
logVoiceVerbose(`transcription ok (${transcript.length} chars): guild ${entry.guildId} channel ${entry.channelId}`);
|
|
1703
1791
|
logVoiceVerbose(`transcript from ${ingress.speakerLabel} (${userId}) in guild ${entry.guildId} channel ${entry.channelId}: ${formatVoiceTranscriptLogPreview(transcript)}`);
|
|
1704
|
-
if (params.
|
|
1705
|
-
await params.
|
|
1706
|
-
sessionId: params.
|
|
1792
|
+
if (params.transcripts) {
|
|
1793
|
+
await params.transcripts.onUtterance({
|
|
1794
|
+
sessionId: params.transcripts.sessionId,
|
|
1707
1795
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1708
1796
|
final: true,
|
|
1709
1797
|
speaker: {
|
|
@@ -2165,8 +2253,8 @@ var DiscordVoiceManager$1 = class {
|
|
|
2165
2253
|
const voiceMode = resolveDiscordVoiceMode(voiceConfig);
|
|
2166
2254
|
const existing = this.sessions.get(guildId);
|
|
2167
2255
|
if (existing && existing.channelId === channelId) {
|
|
2168
|
-
if (options?.
|
|
2169
|
-
if (!options?.
|
|
2256
|
+
if (options?.transcripts) existing.transcripts = options.transcripts;
|
|
2257
|
+
if (!options?.transcripts && isDiscordRealtimeVoiceMode(voiceMode) && !existing.realtime) {
|
|
2170
2258
|
const realtimeResult = await this.attachRealtimeSession(existing, voiceMode, { requireLiveEntry: true });
|
|
2171
2259
|
if (!realtimeResult.ok) return {
|
|
2172
2260
|
ok: false,
|
|
@@ -2342,7 +2430,7 @@ var DiscordVoiceManager$1 = class {
|
|
|
2342
2430
|
playbackQueue: Promise.resolve(),
|
|
2343
2431
|
processingQueue: Promise.resolve(),
|
|
2344
2432
|
capture: createVoiceCaptureState(),
|
|
2345
|
-
|
|
2433
|
+
transcripts: options?.transcripts,
|
|
2346
2434
|
receiveRecovery: createVoiceReceiveRecoveryState(),
|
|
2347
2435
|
isStopped: () => stopped,
|
|
2348
2436
|
stop: () => {
|
|
@@ -2352,7 +2440,7 @@ var DiscordVoiceManager$1 = class {
|
|
|
2352
2440
|
});
|
|
2353
2441
|
}
|
|
2354
2442
|
};
|
|
2355
|
-
if (!options?.
|
|
2443
|
+
if (!options?.transcripts && isDiscordRealtimeVoiceMode(voiceMode)) {
|
|
2356
2444
|
const realtimeResult = await this.attachRealtimeSession(entry, voiceMode);
|
|
2357
2445
|
if (!realtimeResult.ok) {
|
|
2358
2446
|
destroyVoiceConnectionSafely({
|
|
@@ -2489,18 +2577,18 @@ var DiscordVoiceManager$1 = class {
|
|
|
2489
2577
|
ok: false,
|
|
2490
2578
|
message: "Not connected to that voice channel."
|
|
2491
2579
|
};
|
|
2492
|
-
if (options?.
|
|
2493
|
-
if (!entry.
|
|
2580
|
+
if (options?.transcriptsSessionId) {
|
|
2581
|
+
if (!entry.transcripts || entry.transcripts.sessionId !== options.transcriptsSessionId) return {
|
|
2494
2582
|
ok: false,
|
|
2495
|
-
message: "
|
|
2583
|
+
message: "Transcripts session is not active in this voice channel.",
|
|
2496
2584
|
guildId,
|
|
2497
2585
|
channelId: entry.channelId
|
|
2498
2586
|
};
|
|
2499
2587
|
if (entry.realtime || entry.pendingRealtime) {
|
|
2500
|
-
entry.
|
|
2588
|
+
entry.transcripts = void 0;
|
|
2501
2589
|
return {
|
|
2502
2590
|
ok: true,
|
|
2503
|
-
message: `Stopped
|
|
2591
|
+
message: `Stopped transcripts for ${formatMention({ channelId: entry.channelId })}.`,
|
|
2504
2592
|
guildId,
|
|
2505
2593
|
channelId: entry.channelId
|
|
2506
2594
|
};
|
|
@@ -3024,7 +3112,7 @@ var DiscordVoiceManager$1 = class {
|
|
|
3024
3112
|
ownerAllowFrom: this.ownerAllowFrom,
|
|
3025
3113
|
runtime: this.params.runtime,
|
|
3026
3114
|
speakerContext: this.speakerContext,
|
|
3027
|
-
|
|
3115
|
+
transcripts: params.entry.transcripts,
|
|
3028
3116
|
fetchGuildName: async (guildId) => {
|
|
3029
3117
|
const guild = await this.params.client.fetchGuild(guildId).catch(() => null);
|
|
3030
3118
|
return guild && typeof guild.name === "string" && guild.name.trim() ? guild.name : void 0;
|
|
@@ -121,7 +121,7 @@ function applyImplicitReplyBatchGate(ctx, replyToMode, isBatched) {
|
|
|
121
121
|
//#region extensions/discord/src/monitor/message-run-queue.ts
|
|
122
122
|
let messageProcessRuntimePromise;
|
|
123
123
|
async function loadMessageProcessRuntime() {
|
|
124
|
-
messageProcessRuntimePromise ??= import("./message-handler.process-
|
|
124
|
+
messageProcessRuntimePromise ??= import("./message-handler.process-CDxJ-gh_.js");
|
|
125
125
|
return await messageProcessRuntimePromise;
|
|
126
126
|
}
|
|
127
127
|
async function processDiscordQueuedMessage(params) {
|
|
@@ -173,7 +173,7 @@ function createDiscordMessageRunQueue(params) {
|
|
|
173
173
|
//#region extensions/discord/src/monitor/message-handler.ts
|
|
174
174
|
let messagePreflightRuntimePromise;
|
|
175
175
|
async function loadMessagePreflightRuntime() {
|
|
176
|
-
messagePreflightRuntimePromise ??= import("./message-handler.preflight-
|
|
176
|
+
messagePreflightRuntimePromise ??= import("./message-handler.preflight-Dpfm3M4j.js");
|
|
177
177
|
return await messagePreflightRuntimePromise;
|
|
178
178
|
}
|
|
179
179
|
function isNonEmptyString(value) {
|
package/dist/{message-handler.preflight-DHZfoN6e.js → message-handler.preflight-Dpfm3M4j.js}
RENAMED
|
@@ -5,7 +5,7 @@ import { t as resolveDiscordConversationIdentity } from "./conversation-identity
|
|
|
5
5
|
import { l as isRecentlyUnboundThreadWebhookMessage } from "./thread-bindings.state-BsOnj5NX.js";
|
|
6
6
|
import { d as resolveDiscordChannelNameSafe, u as resolveDiscordChannelInfoSafe } from "./thread-bindings.discord-api-xCfun-pQ.js";
|
|
7
7
|
import "./thread-bindings-Dw4wcHWn.js";
|
|
8
|
-
import { C as resolveDiscordDmCommandAccess, f as buildDiscordRoutePeer, g as handleDiscordDmCommandDecision, h as shouldIgnoreStaleDiscordRouteBinding, m as resolveDiscordEffectiveRoute, p as resolveDiscordConversationRoute, w as resolveDiscordTextCommandAccess } from "./provider-
|
|
8
|
+
import { C as resolveDiscordDmCommandAccess, f as buildDiscordRoutePeer, g as handleDiscordDmCommandDecision, h as shouldIgnoreStaleDiscordRouteBinding, m as resolveDiscordEffectiveRoute, p as resolveDiscordConversationRoute, w as resolveDiscordTextCommandAccess } from "./provider-B1wc40FG.js";
|
|
9
9
|
import { d as resolveDiscordMessageChannelId, l as resolveDiscordMessageStickers, o as resolveMediaList, r as resolveDiscordMessageText, u as resolveDiscordChannelInfo } from "./message-utils-DxHZcpPf.js";
|
|
10
10
|
import { n as resolveDiscordWebhookId, t as resolveDiscordSenderIdentity } from "./sender-identity-BFp5w0F8.js";
|
|
11
11
|
import { normalizeOptionalString, readStringValue } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
@@ -436,7 +436,7 @@ async function loadSystemEventsRuntime() {
|
|
|
436
436
|
return await systemEventsRuntimePromise;
|
|
437
437
|
}
|
|
438
438
|
async function loadDiscordThreadingRuntime() {
|
|
439
|
-
discordThreadingRuntimePromise ??= import("./provider-
|
|
439
|
+
discordThreadingRuntimePromise ??= import("./provider-B1wc40FG.js").then((n) => n.v);
|
|
440
440
|
return await discordThreadingRuntimePromise;
|
|
441
441
|
}
|
|
442
442
|
function isPreflightAborted(abortSignal) {
|
|
@@ -9,7 +9,7 @@ import { t as beginDiscordInboundEventDeliveryCorrelation } from "./inbound-even
|
|
|
9
9
|
import { t as DISCORD_TEXT_CHUNK_LIMIT } from "./outbound-adapter-C8lzfSt6.js";
|
|
10
10
|
import { t as resolveDiscordPreviewStreamMode } from "./preview-streaming-CQ7PsV9J.js";
|
|
11
11
|
import { n as DISCORD_ATTACHMENT_TOTAL_TIMEOUT_MS, t as DISCORD_ATTACHMENT_IDLE_TIMEOUT_MS } from "./timeouts-l_PsHQvX.js";
|
|
12
|
-
import { a as resolveReplyContext, i as buildGuildLabel, n as deliverDiscordReply, r as buildDirectLabel, x as resolveDiscordThreadStarter, y as resolveDiscordAutoThreadReplyPlan } from "./provider-
|
|
12
|
+
import { a as resolveReplyContext, i as buildGuildLabel, n as deliverDiscordReply, r as buildDirectLabel, x as resolveDiscordThreadStarter, y as resolveDiscordAutoThreadReplyPlan } from "./provider-B1wc40FG.js";
|
|
13
13
|
import { a as resolveForwardedMediaList, o as resolveMediaList, r as resolveDiscordMessageText, s as resolveReferencedReplyMediaList } from "./message-utils-DxHZcpPf.js";
|
|
14
14
|
import { t as sendTyping } from "./typing-BhIpRSfR.js";
|
|
15
15
|
import { n as buildDiscordInboundAccessContext, r as createDiscordSupplementalContextAccessChecker } from "./inbound-context-B5EsqsSr.js";
|