@openclaw/discord 2026.7.2-beta.2 → 2026.7.2-beta.3
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/action-runtime-api.js +1 -1
- package/dist/{activities-api-D0D_87n3.js → activities-api-DS1L7KRY.js} +15 -42
- package/dist/activities-api.js +1 -1
- package/dist/{allow-list-B7qOH5xu.js → allow-list-DzKrtfah.js} +18 -7
- package/dist/{api-B88T4rFN.js → api-C43z8vUm.js} +3 -1
- package/dist/api.js +25 -16
- package/dist/{approval-handler.runtime-B7E1-x5q.js → approval-handler.runtime-BbkfPoGc.js} +5 -6
- package/dist/{conversation-identity-BPcbhCAH.js → approval-message-safety-DrLlgIPz.js} +48 -2
- package/dist/{audit-CjpnY1yb.js → audit-BC6bgPMX.js} +3 -3
- package/dist/{channel-BDfKr14H.js → channel-5MfaC2IE.js} +18 -18
- package/dist/{channel-actions-BQct-o4T.js → channel-actions-DicWTbYH.js} +2 -2
- package/dist/{channel-actions.runtime-CC4QwYVO.js → channel-actions.runtime-D9Qadn-l.js} +8 -8
- package/dist/channel-config-api.js +1 -1
- package/dist/channel-plugin-api.js +1 -1
- package/dist/{channel.setup-BS4gd8sc.js → channel.setup-CJXfFclP.js} +3 -3
- package/dist/{chunk-CHpY_lsO.js → chunk-CWzmeWf_.js} +9 -49
- package/dist/{config-schema-CoDck1GW.js → config-schema-BxrgfSMD.js} +4 -0
- package/dist/{directory-config-G1QwBIdi.js → directory-config-CmukHSms.js} +1 -1
- package/dist/directory-contract-api.js +1 -1
- package/dist/{directory-live-nfvekMil.js → directory-live-BYlCRO__.js} +3 -3
- package/dist/{doctor-Bq4pJAQk.js → doctor-Ci7USQAW.js} +1 -1
- package/dist/{handle-action.guild-admin-LYY75_Oa.js → handle-action.guild-admin-Bsk2YBPs.js} +2 -2
- package/dist/index.js +2 -2
- package/dist/{manager.runtime-w6VVYQMl.js → manager.runtime-Cc8vS-M7.js} +98 -145
- package/dist/message-handler-CWJ-mQvH.js +590 -0
- package/dist/{message-handler.preflight-DnAH04Ss.js → message-handler.preflight-DK46_qbS.js} +16 -15
- package/dist/{message-handler.process-ClgKz8I5.js → message-handler.process-BK0mjFJH.js} +37 -61
- package/dist/{message-utils-BU3gc01v.js → message-utils-Cfe6CZ6N.js} +1 -1
- package/dist/{outbound-adapter-ByoXsR-V.js → outbound-adapter-B2_oa-zV.js} +36 -5
- package/dist/{outbound-components-7wAolLuS.js → outbound-components-BGhk6HI3.js} +3 -3
- package/dist/{outbound-session-route-D4QqePZN.js → outbound-session-route-M2bEY9tI.js} +36 -6
- package/dist/{pluralkit-r4wiI2yy.js → pluralkit-CT0VRsZd.js} +1 -1
- package/dist/{probe-WKeTYyJX.js → probe-C3SCKyYU.js} +1 -1
- package/dist/{probe.runtime-CRY-qUke.js → probe.runtime-D55QqC9u.js} +1 -1
- package/dist/{provider-PRlF_lZm.js → provider-HW0G-2l6.js} +868 -794
- package/dist/{provider-session.runtime-BZPUZRjn.js → provider-session.runtime-cCfGMsF_.js} +3 -3
- package/dist/provider.runtime-BWCRsVoT.js +2 -0
- package/dist/{resolve-allowlist-common-QN7VMZaC.js → resolve-allowlist-common-CJlqw_nC.js} +2 -2
- package/dist/{resolve-channels-BO9NUPlS.js → resolve-channels-9KcG3mLg.js} +4 -4
- package/dist/{resolve-users-CU3oG5-m.js → resolve-users-GCGf3oV0.js} +3 -3
- package/dist/{runtime-CCf5nkV3.js → runtime-CD5MRZkD.js} +1 -2
- package/dist/{runtime-BsefAQ9q.js → runtime-Z2qkKakn.js} +9 -8
- package/dist/runtime-api.actions.js +2 -2
- package/dist/runtime-api.js +23 -22
- package/dist/runtime-api.lookup.js +5 -5
- package/dist/runtime-api.monitor-C3EDYJqk.js +6 -0
- package/dist/runtime-api.monitor.js +8 -8
- package/dist/runtime-api.send.js +6 -5
- package/dist/runtime-api.threads.js +3 -3
- package/dist/{secret-config-contract-BDNrRWvd.js → secret-config-contract-B1akrlyY.js} +1 -0
- package/dist/secret-contract-api.js +1 -1
- package/dist/security-audit-contract-api.js +1 -1
- package/dist/{security-audit.runtime-K0nkzjzJ.js → security-audit.runtime-DEEywczv.js} +1 -1
- package/dist/{send-JJzVo_sK.js → send-CSRxNNgq.js} +7 -86
- package/dist/{send.components-CCRW54gK.js → send.components-DLqzOEPJ.js} +5 -4
- package/dist/{send.outbound-Cvulb-k_.js → send.outbound-C1SfX20w.js} +4 -3
- package/dist/send.reactions-Cj4ugX3F.js +85 -0
- package/dist/send.receipt-CuwRrpFz.js +48 -0
- package/dist/{send.shared-XuOTO1dW.js → send.shared-Dber6X57.js} +2 -2
- package/dist/{sender-identity-C62lBe18.js → sender-identity-mG_zsdox.js} +1 -1
- package/dist/session-binding-contract-api.js +1 -1
- package/dist/setup-plugin-api.js +1 -1
- package/dist/{shared-Bngr0I-v.js → shared-BEhmAdMY.js} +5 -5
- package/dist/{subagent-hooks-mSxRv4AE.js → subagent-hooks-Wrk61DwY.js} +2 -2
- package/dist/subagent-hooks-api.js +10 -1
- package/dist/subagent-progress-B2fpHe3m.js +662 -0
- package/dist/{system-events-BANBRAe3.js → system-events-AhfcKRRb.js} +2 -2
- package/dist/{target-resolver-BaEmTxeK.js → target-resolver-vqLA450R.js} +2 -2
- package/dist/targets-BjEMIe8t.js +3 -0
- package/dist/{thread-bindings-4MV-u8vU.js → thread-bindings-CJQoXuWY.js} +4 -4
- package/dist/{thread-bindings.discord-api-DXfXsHGZ.js → thread-bindings.discord-api-mNCWlgvM.js} +4 -4
- package/dist/{thread-bindings.manager-DJ4cO5Eu.js → thread-bindings.manager-D1z8mK4b.js} +3 -3
- package/dist/{timeouts-D5lWili0.js → timeouts-B9cy6RvQ.js} +1 -6
- package/dist/{transcripts-source-TDZcMriu.js → transcripts-source-DjDbt14x.js} +1 -1
- package/dist/transcripts-source-api.js +1 -1
- package/dist/{typing-CKd2NMWG.js → typing-wxtm1NlF.js} +2 -2
- package/npm-shrinkwrap.json +3 -3
- package/openclaw.plugin.json +10 -0
- package/package.json +4 -4
- package/dist/approval-message-safety-C-8BL6Kv.js +0 -47
- package/dist/message-handler-DExaghWK.js +0 -331
- package/dist/provider.runtime-BX8GcCYx.js +0 -2
- package/dist/runtime-api.monitor-DCS_ETft.js +0 -6
- package/dist/targets-fjJt3PYG.js +0 -3
- package/dist/{security-audit-aLbWmUjl.js → security-audit-DtT0SIns.js} +1 -1
package/dist/{handle-action.guild-admin-LYY75_Oa.js → handle-action.guild-admin-Bsk2YBPs.js}
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { c as readDiscordChannelMoveParams, n as isDiscordModerationAction, o as readDiscordChannelCreateParams, r as readDiscordModerationCommand, s as readDiscordChannelEditParams, t as handleDiscordAction } from "./runtime-
|
|
1
|
+
import { c as readDiscordChannelMoveParams, n as isDiscordModerationAction, o as readDiscordChannelCreateParams, r as readDiscordModerationCommand, s as readDiscordChannelEditParams, t as handleDiscordAction } from "./runtime-Z2qkKakn.js";
|
|
2
2
|
import "./action-runtime-api.js";
|
|
3
|
-
import { n as isTrustedRequesterGuildAdminAction } from "./channel-actions-
|
|
3
|
+
import { n as isTrustedRequesterGuildAdminAction } from "./channel-actions-DicWTbYH.js";
|
|
4
4
|
import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
5
5
|
import { readNonNegativeIntegerParam, readPositiveIntegerParam, readStringArrayParam, readStringParam } from "openclaw/plugin-sdk/agent-runtime";
|
|
6
6
|
//#region extensions/discord/src/actions/handle-action.guild-admin.ts
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { t as registerDiscordActivities } from "./activities-api-
|
|
1
|
+
import { t as registerDiscordActivities } from "./activities-api-DS1L7KRY.js";
|
|
2
2
|
import { registerDiscordSubagentHooks } from "./subagent-hooks-api.js";
|
|
3
|
-
import { t as discordVoiceTranscriptsSourceProvider } from "./transcripts-source-
|
|
3
|
+
import { t as discordVoiceTranscriptsSourceProvider } from "./transcripts-source-DjDbt14x.js";
|
|
4
4
|
import "./transcripts-source-api.js";
|
|
5
5
|
import { defineBundledChannelEntry } from "openclaw/plugin-sdk/channel-entry-contract";
|
|
6
6
|
//#region extensions/discord/index.ts
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as normalizeDiscordSlug, h as resolveDiscordOwnerAccess, x as formatDiscordUserTag } from "./allow-list-B7qOH5xu.js";
|
|
1
|
+
import { O as isUnknownDiscordVoiceStateError, Rt as getGuildVoiceState, S as VoiceStateUpdateListener, b as ResumedListener, d as discord_exports, l as parseDiscordTarget, y as ReadyListener } from "./chunk-CWzmeWf_.js";
|
|
3
2
|
import { t as getDiscordRuntime } from "./runtime-DgnVQ7zW.js";
|
|
4
|
-
import { a as
|
|
5
|
-
import {
|
|
3
|
+
import { a as normalizeDiscordSlug, h as resolveDiscordOwnerAccess, x as formatDiscordUserTag } from "./allow-list-DzKrtfah.js";
|
|
4
|
+
import { a as formatMention } from "./send.outbound-C1SfX20w.js";
|
|
5
|
+
import { c as resolveDiscordVoiceAccess, h as buildDiscordGroupSystemPrompt, l as authorizeDiscordVoiceIngress, p as resolveDiscordVoiceEnabled } from "./provider-HW0G-2l6.js";
|
|
6
6
|
import { createRequire } from "node:module";
|
|
7
7
|
import { resolveAgentRoute } from "openclaw/plugin-sdk/routing";
|
|
8
8
|
import { asBoolean, normalizeOptionalString, uniqueStrings } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
@@ -16,15 +16,14 @@ import { stripInlineDirectiveTagsForDisplay } from "openclaw/plugin-sdk/text-chu
|
|
|
16
16
|
import { resolveFfmpegBin } from "openclaw/plugin-sdk/media-runtime";
|
|
17
17
|
import path from "node:path";
|
|
18
18
|
import { resolvePreferredOpenClawTmpDir, tempWorkspace } from "openclaw/plugin-sdk/temp-path";
|
|
19
|
-
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,
|
|
20
|
-
import { agentCommandFromIngress,
|
|
19
|
+
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, createRealtimeVoiceSessionHarness, createRealtimeVoiceTurnContextTracker, matchRealtimeVoiceActivationName, matchRealtimeVoiceConsultQuestions, normalizeSupportedRealtimeVoiceActivationName, parseRealtimeVoiceAgentControlToolArgs, resamplePcm, resolveConfiguredRealtimeVoiceProvider, resolveRealtimeVoiceAgentConsultToolPolicy, resolveRealtimeVoiceAgentConsultTools, resolveRealtimeVoiceAgentConsultToolsAllow, shouldAutoControlRealtimeVoiceAgentText, sortRealtimeVoiceActivationNames } from "openclaw/plugin-sdk/realtime-voice";
|
|
20
|
+
import { agentCommandFromIngress, resolveAgentDir } from "openclaw/plugin-sdk/agent-runtime";
|
|
21
21
|
import { enqueueSystemEvent } from "openclaw/plugin-sdk/system-event-runtime";
|
|
22
22
|
import { spawn } from "node:child_process";
|
|
23
23
|
import { PassThrough, Readable, Transform, pipeline } from "node:stream";
|
|
24
24
|
import { StringDecoder } from "node:string_decoder";
|
|
25
25
|
import { Application, OpusError, createDecoder, createEncoder } from "libopus-wasm";
|
|
26
26
|
import { resolveRealtimeBootstrapContextInstructions } from "openclaw/plugin-sdk/realtime-bootstrap-context";
|
|
27
|
-
import { parseTtsDirectives } from "openclaw/plugin-sdk/speech";
|
|
28
27
|
//#region extensions/discord/src/voice/audio.ts
|
|
29
28
|
const SAMPLE_RATE = 48e3;
|
|
30
29
|
const CHANNELS = 2;
|
|
@@ -926,6 +925,7 @@ const DISCORD_REALTIME_WAKE_ACKS = [
|
|
|
926
925
|
"Got it.",
|
|
927
926
|
"One sec."
|
|
928
927
|
];
|
|
928
|
+
const discordRealtimeTalkPayload = () => ({});
|
|
929
929
|
const REALTIME_PCM16_BYTES_PER_SAMPLE = 2;
|
|
930
930
|
const DISCORD_RAW_PCM_FRAME_BYTES = 3840;
|
|
931
931
|
const DISCORD_REALTIME_OUTPUT_PREROLL_FRAMES = 25;
|
|
@@ -1041,9 +1041,6 @@ function extractDiscordExactSpeechConsultText(args) {
|
|
|
1041
1041
|
function normalizeControlSpeechText(text) {
|
|
1042
1042
|
return text.toLowerCase().replace(/\s+/g, " ").trim();
|
|
1043
1043
|
}
|
|
1044
|
-
function matchesPendingAgentProxyQuestion(consultMessage, question) {
|
|
1045
|
-
return matchRealtimeVoiceConsultQuestions(consultMessage, question);
|
|
1046
|
-
}
|
|
1047
1044
|
var DiscordRealtimeVoiceSession = class {
|
|
1048
1045
|
constructor(params) {
|
|
1049
1046
|
this.params = params;
|
|
@@ -1054,17 +1051,11 @@ var DiscordRealtimeVoiceSession = class {
|
|
|
1054
1051
|
this.consultPolicy = "auto";
|
|
1055
1052
|
this.wakeNamePolicy = "never";
|
|
1056
1053
|
this.wakeNames = [];
|
|
1057
|
-
this.forcedConsults = createRealtimeVoiceForcedConsultCoordinator({
|
|
1058
|
-
limit: DISCORD_REALTIME_RECENT_AGENT_PROXY_CONSULT_LIMIT,
|
|
1059
|
-
nativeDedupeMs: DISCORD_REALTIME_RECENT_AGENT_PROXY_CONSULT_TTL_MS,
|
|
1060
|
-
questionsMatch: matchesPendingAgentProxyQuestion
|
|
1061
|
-
});
|
|
1062
1054
|
this.speakerTurns = createRealtimeVoiceTurnContextTracker({
|
|
1063
1055
|
limit: DISCORD_REALTIME_PENDING_SPEAKER_CONTEXT_LIMIT,
|
|
1064
1056
|
ignoredContextTtlMs: DISCORD_REALTIME_IGNORED_WAKE_NAME_CONTEXT_TTL_MS,
|
|
1065
1057
|
deferUntilAudio: true
|
|
1066
1058
|
});
|
|
1067
|
-
this.outputActivity = createRealtimeVoiceOutputActivityTracker();
|
|
1068
1059
|
this.outputPacedBuffer = Buffer.alloc(0);
|
|
1069
1060
|
this.queuedExactSpeechMessages = [];
|
|
1070
1061
|
this.exactSpeechResponseActive = false;
|
|
@@ -1077,21 +1068,41 @@ var DiscordRealtimeVoiceSession = class {
|
|
|
1077
1068
|
this.resetOutputStream("player-idle");
|
|
1078
1069
|
if (hadOutputAudio) this.completeExactSpeechResponse("player-idle");
|
|
1079
1070
|
};
|
|
1080
|
-
this.
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1071
|
+
this.harness = createRealtimeVoiceSessionHarness({
|
|
1072
|
+
talk: {
|
|
1073
|
+
sessionId: `discord:${this.params.entry.voiceSessionKey}:realtime`,
|
|
1074
|
+
mode: "realtime",
|
|
1075
|
+
transport: "gateway-relay",
|
|
1076
|
+
brain: "agent-consult"
|
|
1077
|
+
},
|
|
1078
|
+
talkPayloads: {
|
|
1079
|
+
turnStarted: discordRealtimeTalkPayload,
|
|
1080
|
+
turnEnded: discordRealtimeTalkPayload,
|
|
1081
|
+
inputAudioDelta: discordRealtimeTalkPayload,
|
|
1082
|
+
outputAudioStarted: discordRealtimeTalkPayload,
|
|
1083
|
+
outputAudioDelta: discordRealtimeTalkPayload,
|
|
1084
|
+
outputAudioDone: discordRealtimeTalkPayload
|
|
1085
|
+
},
|
|
1086
|
+
talkback: {
|
|
1087
|
+
debounceMs: this.realtimeConfig?.debounceMs ?? DISCORD_REALTIME_TALKBACK_DEBOUNCE_MS,
|
|
1088
|
+
logger: logger$3,
|
|
1089
|
+
logPrefix: "[discord] realtime agent",
|
|
1090
|
+
responseStyle: "Brief, natural spoken answer for a Discord voice channel.",
|
|
1091
|
+
fallbackText: DISCORD_REALTIME_FALLBACK_TEXT,
|
|
1092
|
+
consult: async ({ question, responseStyle, metadata }) => {
|
|
1093
|
+
const context = isDiscordRealtimeSpeakerContext(metadata) ? metadata : void 0;
|
|
1094
|
+
return { text: await this.runAgentTurn({
|
|
1095
|
+
context,
|
|
1096
|
+
message: formatVoiceIngressPrompt([question, responseStyle ? `Spoken style: ${responseStyle}` : void 0].filter(Boolean).join("\n\n"), context?.speakerLabel ?? "Discord voice speaker")
|
|
1097
|
+
}) };
|
|
1098
|
+
},
|
|
1099
|
+
deliver: (text) => this.enqueueExactSpeechMessage(text)
|
|
1093
1100
|
},
|
|
1094
|
-
|
|
1101
|
+
forcedConsults: {
|
|
1102
|
+
limit: DISCORD_REALTIME_RECENT_AGENT_PROXY_CONSULT_LIMIT,
|
|
1103
|
+
nativeDedupeMs: DISCORD_REALTIME_RECENT_AGENT_PROXY_CONSULT_TTL_MS,
|
|
1104
|
+
questionsMatch: matchRealtimeVoiceConsultQuestions
|
|
1105
|
+
}
|
|
1095
1106
|
});
|
|
1096
1107
|
}
|
|
1097
1108
|
async connect() {
|
|
@@ -1134,7 +1145,7 @@ var DiscordRealtimeVoiceSession = class {
|
|
|
1134
1145
|
toolPolicy,
|
|
1135
1146
|
consultPolicy
|
|
1136
1147
|
});
|
|
1137
|
-
this.bridge =
|
|
1148
|
+
this.bridge = this.harness.createBridge({
|
|
1138
1149
|
provider: resolved.provider,
|
|
1139
1150
|
cfg: this.params.cfg,
|
|
1140
1151
|
providerConfig: resolved.providerConfig,
|
|
@@ -1147,7 +1158,7 @@ var DiscordRealtimeVoiceSession = class {
|
|
|
1147
1158
|
audioSink: {
|
|
1148
1159
|
isOpen: () => !this.stopped,
|
|
1149
1160
|
sendAudio: (audio) => this.sendOutputAudio(audio),
|
|
1150
|
-
clearAudio: () => this.clearOutputAudio("provider-clear-audio")
|
|
1161
|
+
clearAudio: () => this.harness.flushOutput(() => this.clearOutputAudio("provider-clear-audio"))
|
|
1151
1162
|
},
|
|
1152
1163
|
onTranscript: (role, text, isFinal) => {
|
|
1153
1164
|
if (isFinal && text.trim()) logger$3.info(`discord voice: realtime ${role} transcript (${text.length} chars): ${formatVoiceLogPreview(text)}`);
|
|
@@ -1194,8 +1205,7 @@ var DiscordRealtimeVoiceSession = class {
|
|
|
1194
1205
|
close() {
|
|
1195
1206
|
this.stopped = true;
|
|
1196
1207
|
this.flushSuppressedRealtimeErrors();
|
|
1197
|
-
this.
|
|
1198
|
-
this.forcedConsults.clear();
|
|
1208
|
+
this.harness.close();
|
|
1199
1209
|
this.speakerTurns.clear();
|
|
1200
1210
|
this.queuedExactSpeechMessages = [];
|
|
1201
1211
|
this.exactSpeechResponseActive = false;
|
|
@@ -1278,11 +1288,11 @@ var DiscordRealtimeVoiceSession = class {
|
|
|
1278
1288
|
return;
|
|
1279
1289
|
}
|
|
1280
1290
|
if (!this.hasInterruptibleOutputAudio()) {
|
|
1281
|
-
logger$3.info(`discord voice: realtime barge-in ignored reason=${reason} outputActive=false guild=${this.params.entry.guildId} channel=${this.params.entry.channelId} playbackChunks=${this.
|
|
1291
|
+
logger$3.info(`discord voice: realtime barge-in ignored reason=${reason} outputActive=false guild=${this.params.entry.guildId} channel=${this.params.entry.channelId} playbackChunks=${this.harness.outputActivity.snapshot().chunks}`);
|
|
1282
1292
|
return;
|
|
1283
1293
|
}
|
|
1284
|
-
logger$3.info(`discord voice: realtime barge-in requested reason=${reason} guild=${this.params.entry.guildId} channel=${this.params.entry.channelId} outputAudioMs=${this.outputAudioMs()} outputActive=${this.isOutputAudioActive()} playbackChunks=${this.
|
|
1285
|
-
this.
|
|
1294
|
+
logger$3.info(`discord voice: realtime barge-in requested reason=${reason} guild=${this.params.entry.guildId} channel=${this.params.entry.channelId} outputAudioMs=${this.outputAudioMs()} outputActive=${this.isOutputAudioActive()} playbackChunks=${this.harness.outputActivity.snapshot().chunks}`);
|
|
1295
|
+
this.harness.handleBargeIn({ audioPlaybackActive: true }, () => {});
|
|
1286
1296
|
}
|
|
1287
1297
|
isBargeInEnabled() {
|
|
1288
1298
|
if (this.isWakeNameRequired()) return false;
|
|
@@ -1293,8 +1303,9 @@ var DiscordRealtimeVoiceSession = class {
|
|
|
1293
1303
|
});
|
|
1294
1304
|
}
|
|
1295
1305
|
hasInterruptibleOutputAudio() {
|
|
1296
|
-
this.
|
|
1297
|
-
|
|
1306
|
+
this.bridge?.setMediaTimestamp(this.outputAudioMs());
|
|
1307
|
+
const streamActive = Boolean(this.outputStream && !this.outputStream.destroyed);
|
|
1308
|
+
return this.harness.outputActivity.isInterruptible(streamActive);
|
|
1298
1309
|
}
|
|
1299
1310
|
get realtimeConfig() {
|
|
1300
1311
|
return this.params.discordConfig.voice?.realtime;
|
|
@@ -1308,14 +1319,14 @@ var DiscordRealtimeVoiceSession = class {
|
|
|
1308
1319
|
sendOutputAudio(realtimePcm24kMono) {
|
|
1309
1320
|
const discordPcm = convertRealtimePcm24kMonoToDiscordPcm48kStereo(realtimePcm24kMono);
|
|
1310
1321
|
if (discordPcm.length === 0) return;
|
|
1311
|
-
this.
|
|
1312
|
-
if (this.outputActivity.snapshot().streamEnding) {
|
|
1322
|
+
this.bridge?.setMediaTimestamp(this.outputAudioMs());
|
|
1323
|
+
if (this.harness.outputActivity.snapshot().streamEnding) {
|
|
1313
1324
|
logVoiceVerbose(`realtime output audio ignored after stream ending: guild ${this.params.entry.guildId} channel ${this.params.entry.channelId}`);
|
|
1314
1325
|
return;
|
|
1315
1326
|
}
|
|
1316
1327
|
const stream = this.ensureOutputStream();
|
|
1317
1328
|
if (this.exactSpeechResponseActive) this.exactSpeechAudioStarted = true;
|
|
1318
|
-
this.outputActivity.markAudio({
|
|
1329
|
+
this.harness.outputActivity.markAudio({
|
|
1319
1330
|
audioMs: pcm16MonoDurationMs(realtimePcm24kMono, REALTIME_VOICE_AUDIO_FORMAT_PCM16_24KHZ.sampleRateHz),
|
|
1320
1331
|
sourceAudioBytes: realtimePcm24kMono.length,
|
|
1321
1332
|
sinkAudioBytes: discordPcm.length
|
|
@@ -1327,9 +1338,9 @@ var DiscordRealtimeVoiceSession = class {
|
|
|
1327
1338
|
const stream = new PassThrough({ highWaterMark: DISCORD_RAW_PCM_FRAME_BYTES * 128 });
|
|
1328
1339
|
this.outputStream = stream;
|
|
1329
1340
|
this.outputPacedBuffer = Buffer.alloc(0);
|
|
1330
|
-
this.outputActivity.markStreamOpened();
|
|
1341
|
+
this.harness.outputActivity.markStreamOpened();
|
|
1331
1342
|
stream.once("close", () => {
|
|
1332
|
-
if (this.outputActivity.snapshot().playbackStarted) return;
|
|
1343
|
+
if (this.harness.outputActivity.snapshot().playbackStarted) return;
|
|
1333
1344
|
this.handleOutputStreamClosed(stream, "stream-close");
|
|
1334
1345
|
});
|
|
1335
1346
|
return stream;
|
|
@@ -1339,11 +1350,12 @@ var DiscordRealtimeVoiceSession = class {
|
|
|
1339
1350
|
this.logOutputAudioStopped(reason);
|
|
1340
1351
|
this.clearOutputPlaybackWatchdog();
|
|
1341
1352
|
this.outputStream = null;
|
|
1342
|
-
this.
|
|
1353
|
+
this.outputPacedBuffer = Buffer.alloc(0);
|
|
1354
|
+
this.harness.outputActivity.reset();
|
|
1343
1355
|
this.completeExactSpeechResponse(reason);
|
|
1344
1356
|
}
|
|
1345
1357
|
queueOutputAudio(stream, discordPcm) {
|
|
1346
|
-
if (this.outputActivity.snapshot().playbackStarted) {
|
|
1358
|
+
if (this.harness.outputActivity.snapshot().playbackStarted) {
|
|
1347
1359
|
stream.write(discordPcm);
|
|
1348
1360
|
return;
|
|
1349
1361
|
}
|
|
@@ -1351,7 +1363,7 @@ var DiscordRealtimeVoiceSession = class {
|
|
|
1351
1363
|
if (this.outputPacedBuffer.length >= DISCORD_RAW_PCM_FRAME_BYTES * DISCORD_REALTIME_OUTPUT_PREROLL_FRAMES) this.startOutputPlayback(stream);
|
|
1352
1364
|
}
|
|
1353
1365
|
startOutputPlayback(stream) {
|
|
1354
|
-
if (this.outputActivity.snapshot().playbackStarted || stream.destroyed) return;
|
|
1366
|
+
if (this.harness.outputActivity.snapshot().playbackStarted || stream.destroyed) return;
|
|
1355
1367
|
const voiceSdk = loadDiscordVoiceSdk();
|
|
1356
1368
|
const opusStream = createDiscordOpusEncodeStream();
|
|
1357
1369
|
opusStream.on("error", (err) => {
|
|
@@ -1370,7 +1382,7 @@ var DiscordRealtimeVoiceSession = class {
|
|
|
1370
1382
|
}
|
|
1371
1383
|
const resource = voiceSdk.createAudioResource(opusStream, { inputType: voiceSdk.StreamType.Opus });
|
|
1372
1384
|
this.params.entry.player.play(resource);
|
|
1373
|
-
this.outputActivity.markPlaybackStarted();
|
|
1385
|
+
this.harness.outputActivity.markPlaybackStarted();
|
|
1374
1386
|
const realtimeConfig = this.realtimeConfig;
|
|
1375
1387
|
logger$3.info(`discord voice: realtime audio playback started guild=${this.params.entry.guildId} channel=${this.params.entry.channelId} mode=${this.params.mode} model=${realtimeConfig?.model ?? "provider-default"} voice=${realtimeConfig?.voice ?? "provider-default"}`);
|
|
1376
1388
|
}
|
|
@@ -1384,15 +1396,15 @@ var DiscordRealtimeVoiceSession = class {
|
|
|
1384
1396
|
this.logOutputAudioStopped(reason);
|
|
1385
1397
|
this.outputStream = null;
|
|
1386
1398
|
this.outputPacedBuffer = Buffer.alloc(0);
|
|
1387
|
-
this.
|
|
1399
|
+
this.harness.outputActivity.reset();
|
|
1388
1400
|
stream?.end();
|
|
1389
1401
|
stream?.destroy();
|
|
1390
1402
|
}
|
|
1391
1403
|
finishOutputAudioStream(reason, { playBuffered = true } = {}) {
|
|
1392
1404
|
const stream = this.outputStream;
|
|
1393
|
-
if (!stream || stream.destroyed || this.outputActivity.snapshot().streamEnding) return;
|
|
1394
|
-
this.outputActivity.markStreamEnding();
|
|
1395
|
-
logger$3.info(`discord voice: realtime audio playback finishing reason=${reason} guild=${this.params.entry.guildId} channel=${this.params.entry.channelId} audioMs=${this.outputAudioMs()} chunks=${this.
|
|
1405
|
+
if (!stream || stream.destroyed || this.harness.outputActivity.snapshot().streamEnding) return;
|
|
1406
|
+
this.harness.outputActivity.markStreamEnding();
|
|
1407
|
+
logger$3.info(`discord voice: realtime audio playback finishing reason=${reason} guild=${this.params.entry.guildId} channel=${this.params.entry.channelId} audioMs=${this.outputAudioMs()} chunks=${this.harness.outputActivity.snapshot().chunks}`);
|
|
1396
1408
|
if (playBuffered) {
|
|
1397
1409
|
this.startOutputPlayback(stream);
|
|
1398
1410
|
this.scheduleOutputPlaybackWatchdog(reason, stream);
|
|
@@ -1406,7 +1418,7 @@ var DiscordRealtimeVoiceSession = class {
|
|
|
1406
1418
|
}
|
|
1407
1419
|
scheduleOutputPlaybackWatchdog(reason, stream) {
|
|
1408
1420
|
this.clearOutputPlaybackWatchdog();
|
|
1409
|
-
const timeoutMs = this.outputActivity.playbackWatchdogDelayMs({ marginMs: DISCORD_REALTIME_OUTPUT_PLAYBACK_WATCHDOG_MARGIN_MS });
|
|
1421
|
+
const timeoutMs = this.harness.outputActivity.playbackWatchdogDelayMs({ marginMs: DISCORD_REALTIME_OUTPUT_PLAYBACK_WATCHDOG_MARGIN_MS });
|
|
1410
1422
|
if (timeoutMs === void 0) return;
|
|
1411
1423
|
this.outputPlaybackWatchdog = setTimeout(() => {
|
|
1412
1424
|
this.outputPlaybackWatchdog = void 0;
|
|
@@ -1415,7 +1427,7 @@ var DiscordRealtimeVoiceSession = class {
|
|
|
1415
1427
|
this.completeExactSpeechResponse("playback-watchdog");
|
|
1416
1428
|
return;
|
|
1417
1429
|
}
|
|
1418
|
-
logger$3.warn(`discord voice: realtime audio playback watchdog fired reason=${reason} guild=${this.params.entry.guildId} channel=${this.params.entry.channelId} audioMs=${this.outputAudioMs()} elapsedMs=${this.outputActivity.elapsedPlaybackMs()}`);
|
|
1430
|
+
logger$3.warn(`discord voice: realtime audio playback watchdog fired reason=${reason} guild=${this.params.entry.guildId} channel=${this.params.entry.channelId} audioMs=${this.outputAudioMs()} elapsedMs=${this.harness.outputActivity.elapsedPlaybackMs()}`);
|
|
1419
1431
|
this.clearOutputAudio("playback-watchdog");
|
|
1420
1432
|
this.completeExactSpeechResponse("playback-watchdog");
|
|
1421
1433
|
}, timeoutMs);
|
|
@@ -1456,11 +1468,10 @@ var DiscordRealtimeVoiceSession = class {
|
|
|
1456
1468
|
if (this.stopped || !trimmed) return;
|
|
1457
1469
|
this.queuedExactSpeechMessages = [];
|
|
1458
1470
|
this.completeExactSpeechResponse("active-run-control", { drain: false });
|
|
1459
|
-
this.
|
|
1471
|
+
this.harness.handleBargeIn({
|
|
1460
1472
|
audioPlaybackActive: true,
|
|
1461
1473
|
force: true
|
|
1462
|
-
});
|
|
1463
|
-
this.clearOutputAudio("active-run-control");
|
|
1474
|
+
}, () => this.clearOutputAudio("active-run-control"));
|
|
1464
1475
|
this.lastControlSpeech = {
|
|
1465
1476
|
normalizedText: normalizeControlSpeechText(trimmed),
|
|
1466
1477
|
sentAt: Date.now(),
|
|
@@ -1479,11 +1490,10 @@ var DiscordRealtimeVoiceSession = class {
|
|
|
1479
1490
|
recent.assistantTranscriptCount += 1;
|
|
1480
1491
|
if (recent.assistantTranscriptCount <= 1) return;
|
|
1481
1492
|
logger$3.info(`discord voice: realtime duplicate active-run control speech suppressed guild=${this.params.entry.guildId} channel=${this.params.entry.channelId}`);
|
|
1482
|
-
this.
|
|
1493
|
+
this.harness.handleBargeIn({
|
|
1483
1494
|
audioPlaybackActive: true,
|
|
1484
1495
|
force: true
|
|
1485
|
-
});
|
|
1486
|
-
this.clearOutputAudio("duplicate-active-run-control");
|
|
1496
|
+
}, () => this.clearOutputAudio("duplicate-active-run-control"));
|
|
1487
1497
|
}
|
|
1488
1498
|
completeExactSpeechResponse(reason, options) {
|
|
1489
1499
|
if (!this.exactSpeechResponseActive && this.queuedExactSpeechMessages.length === 0) return;
|
|
@@ -1500,32 +1510,19 @@ var DiscordRealtimeVoiceSession = class {
|
|
|
1500
1510
|
this.sendExactSpeechMessage(next);
|
|
1501
1511
|
}
|
|
1502
1512
|
logOutputAudioStopped(reason) {
|
|
1503
|
-
const activity = this.outputActivity.snapshot();
|
|
1513
|
+
const activity = this.harness.outputActivity.snapshot();
|
|
1504
1514
|
const audioMs = Math.floor(activity.audioMs);
|
|
1505
1515
|
const chunks = activity.chunks;
|
|
1506
1516
|
const discordBytes = activity.sinkAudioBytes;
|
|
1507
1517
|
const realtimeBytes = activity.sourceAudioBytes;
|
|
1508
|
-
const elapsedMs = this.outputActivity.elapsedPlaybackMs();
|
|
1518
|
+
const elapsedMs = this.harness.outputActivity.elapsedPlaybackMs();
|
|
1509
1519
|
if (this.outputStream || chunks > 0 || audioMs > 0) logger$3.info(`discord voice: realtime audio playback stopped reason=${reason} guild=${this.params.entry.guildId} channel=${this.params.entry.channelId} audioMs=${audioMs} elapsedMs=${elapsedMs} chunks=${chunks} discordBytes=${discordBytes} realtimeBytes=${realtimeBytes}`);
|
|
1510
1520
|
}
|
|
1511
|
-
resetOutputAudioStats() {
|
|
1512
|
-
this.outputPacedBuffer = Buffer.alloc(0);
|
|
1513
|
-
this.outputActivity.reset();
|
|
1514
|
-
}
|
|
1515
|
-
syncOutputAudioTimestamp() {
|
|
1516
|
-
this.bridge?.setMediaTimestamp(this.outputAudioMs());
|
|
1517
|
-
}
|
|
1518
1521
|
outputAudioMs() {
|
|
1519
|
-
return Math.floor(this.outputActivity.snapshot().audioMs);
|
|
1520
|
-
}
|
|
1521
|
-
outputAudioChunks() {
|
|
1522
|
-
return this.outputActivity.snapshot().chunks;
|
|
1523
|
-
}
|
|
1524
|
-
isOutputStreamActive() {
|
|
1525
|
-
return Boolean(this.outputStream && !this.outputStream.destroyed);
|
|
1522
|
+
return Math.floor(this.harness.outputActivity.snapshot().audioMs);
|
|
1526
1523
|
}
|
|
1527
1524
|
isOutputAudioActive() {
|
|
1528
|
-
return this.outputActivity.isActive(this.
|
|
1525
|
+
return this.harness.outputActivity.isActive(Boolean(this.outputStream && !this.outputStream.destroyed));
|
|
1529
1526
|
}
|
|
1530
1527
|
logSpeakerTurnClosed(turn) {
|
|
1531
1528
|
if (turn.closed || !turn.hasAudio) return;
|
|
@@ -1573,8 +1570,8 @@ var DiscordRealtimeVoiceSession = class {
|
|
|
1573
1570
|
return;
|
|
1574
1571
|
}
|
|
1575
1572
|
logger$3.info(`discord voice: realtime consult requested call=${callId || "unknown"} voiceSession=${this.params.entry.voiceSessionKey} supervisorSession=${this.params.entry.route.sessionKey} agent=${this.params.entry.route.agentId} question=${formatVoiceLogPreview(consultMessage)}`);
|
|
1576
|
-
const nativeConsult = this.forcedConsults.recordNativeConsult(event.args, callId);
|
|
1577
|
-
if (nativeConsult.kind === "already_delivered" && this.forcedConsults.isCancelled(nativeConsult.handle)) {
|
|
1573
|
+
const nativeConsult = this.harness.forcedConsults.recordNativeConsult(event.args, callId);
|
|
1574
|
+
if (nativeConsult.kind === "already_delivered" && this.harness.forcedConsults.isCancelled(nativeConsult.handle)) {
|
|
1578
1575
|
await this.submitTerminalRealtimeToolResult(callId, session, {
|
|
1579
1576
|
status: "cancelled",
|
|
1580
1577
|
message: "OpenClaw cancelled this consult before completion. Do not restart it."
|
|
@@ -1582,7 +1579,7 @@ var DiscordRealtimeVoiceSession = class {
|
|
|
1582
1579
|
return;
|
|
1583
1580
|
}
|
|
1584
1581
|
const pendingConsult = nativeConsult.kind === "pending" ? nativeConsult.handle : void 0;
|
|
1585
|
-
if (pendingConsult) this.forcedConsults.rememberQuestion(pendingConsult, consultMessage);
|
|
1582
|
+
if (pendingConsult) this.harness.forcedConsults.rememberQuestion(pendingConsult, consultMessage);
|
|
1586
1583
|
let context = pendingConsult?.context?.speaker;
|
|
1587
1584
|
let recent = pendingConsult;
|
|
1588
1585
|
if (!context) {
|
|
@@ -1690,7 +1687,7 @@ var DiscordRealtimeVoiceSession = class {
|
|
|
1690
1687
|
logger$3.warn(`discord voice: realtime active-run control failed; falling back to normal transcript handling: ${formatErrorMessage(error)}`);
|
|
1691
1688
|
});
|
|
1692
1689
|
if (control?.handled) {
|
|
1693
|
-
if (pendingForcedConsult) this.forcedConsults.remove(pendingForcedConsult);
|
|
1690
|
+
if (pendingForcedConsult) this.harness.forcedConsults.remove(pendingForcedConsult);
|
|
1694
1691
|
this.logAgentControlResult(control.result);
|
|
1695
1692
|
if (control.speakText) this.speakControlResult(control.speakText);
|
|
1696
1693
|
return;
|
|
@@ -1700,7 +1697,7 @@ var DiscordRealtimeVoiceSession = class {
|
|
|
1700
1697
|
if (pendingForcedConsult) this.schedulePreparedForcedAgentProxyConsult(pendingForcedConsult);
|
|
1701
1698
|
return;
|
|
1702
1699
|
}
|
|
1703
|
-
this.talkback
|
|
1700
|
+
this.harness.talkback?.enqueue(acceptedText, forcedSpeakerContext ?? this.consumePendingSpeakerContext());
|
|
1704
1701
|
}
|
|
1705
1702
|
handlePartialUserTranscript(text) {
|
|
1706
1703
|
if (!this.isWakeNameRequired() || this.wakeNameAckedForTurn) return;
|
|
@@ -1787,28 +1784,28 @@ var DiscordRealtimeVoiceSession = class {
|
|
|
1787
1784
|
logger$3.warn("discord voice: realtime forced agent consult has no speaker context");
|
|
1788
1785
|
return;
|
|
1789
1786
|
}
|
|
1790
|
-
return this.forcedConsults.prepare(question, { context: { speaker: context } });
|
|
1787
|
+
return this.harness.forcedConsults.prepare(question, { context: { speaker: context } });
|
|
1791
1788
|
}
|
|
1792
1789
|
schedulePreparedForcedAgentProxyConsult(pending) {
|
|
1793
|
-
this.forcedConsults.schedule(pending, DISCORD_REALTIME_FORCED_CONSULT_FALLBACK_DELAY_MS, (handle) => void this.runForcedAgentProxyConsult(handle));
|
|
1790
|
+
this.harness.forcedConsults.schedule(pending, DISCORD_REALTIME_FORCED_CONSULT_FALLBACK_DELAY_MS, (handle) => void this.runForcedAgentProxyConsult(handle));
|
|
1794
1791
|
}
|
|
1795
1792
|
async runForcedAgentProxyConsult(pending) {
|
|
1796
|
-
this.forcedConsults.markStarted(pending);
|
|
1793
|
+
this.harness.forcedConsults.markStarted(pending);
|
|
1797
1794
|
const state = pending.context;
|
|
1798
1795
|
if (!state) {
|
|
1799
|
-
this.forcedConsults.markCancelled(pending);
|
|
1796
|
+
this.harness.forcedConsults.markCancelled(pending);
|
|
1800
1797
|
return;
|
|
1801
1798
|
}
|
|
1802
1799
|
const context = state.speaker;
|
|
1803
1800
|
const { question } = pending;
|
|
1804
1801
|
if (this.stopped) {
|
|
1805
|
-
this.forcedConsults.markCancelled(pending);
|
|
1802
|
+
this.harness.forcedConsults.markCancelled(pending);
|
|
1806
1803
|
return;
|
|
1807
1804
|
}
|
|
1808
1805
|
const startedAt = Date.now();
|
|
1809
1806
|
logger$3.info(`discord voice: realtime forced agent consult starting chars=${question.length} voiceSession=${this.params.entry.voiceSessionKey} supervisorSession=${this.params.entry.route.sessionKey} agent=${this.params.entry.route.agentId} speaker=${context.speakerLabel} owner=${context.senderIsOwner}`);
|
|
1810
1807
|
logger$3.debug(`discord voice: realtime forced agent consult reason=${DISCORD_REALTIME_FORCED_CONSULT_REASON} consultPolicy=${this.consultPolicy} wakeNamePolicy=${this.wakeNamePolicy} requireWakeName=${this.isWakeNameRequired()} voiceSession=${this.params.entry.voiceSessionKey} supervisorSession=${this.params.entry.route.sessionKey} agent=${this.params.entry.route.agentId} speaker=${context.speakerLabel}`);
|
|
1811
|
-
if (this.hasInterruptibleOutputAudio()) logger$3.info(`discord voice: realtime forced agent consult preserving active playback guild=${this.params.entry.guildId} channel=${this.params.entry.channelId} outputAudioMs=${this.outputAudioMs()} outputActive=${this.isOutputAudioActive()} playbackChunks=${this.
|
|
1808
|
+
if (this.hasInterruptibleOutputAudio()) logger$3.info(`discord voice: realtime forced agent consult preserving active playback guild=${this.params.entry.guildId} channel=${this.params.entry.channelId} outputAudioMs=${this.outputAudioMs()} outputActive=${this.isOutputAudioActive()} playbackChunks=${this.harness.outputActivity.snapshot().chunks}`);
|
|
1812
1809
|
state.handledByForcedPlayback = true;
|
|
1813
1810
|
try {
|
|
1814
1811
|
const promise = this.runAgentTurn({
|
|
@@ -1872,35 +1869,35 @@ var DiscordRealtimeVoiceSession = class {
|
|
|
1872
1869
|
return this.speakerTurns.hasAudioContext();
|
|
1873
1870
|
}
|
|
1874
1871
|
rememberRecentAgentProxyConsultContext(question, context, options = {}) {
|
|
1875
|
-
const handle = this.forcedConsults.prepare(question, {
|
|
1872
|
+
const handle = this.harness.forcedConsults.prepare(question, {
|
|
1876
1873
|
context: { speaker: context },
|
|
1877
1874
|
...options.id ? { id: options.id } : {}
|
|
1878
1875
|
});
|
|
1879
1876
|
if (!handle) throw new Error("Discord realtime consult context requires a non-empty question");
|
|
1880
|
-
if (options.started) this.forcedConsults.markStarted(handle);
|
|
1877
|
+
if (options.started) this.harness.forcedConsults.markStarted(handle);
|
|
1881
1878
|
return handle;
|
|
1882
1879
|
}
|
|
1883
1880
|
setRecentAgentProxyConsultPromise(recent, promise) {
|
|
1884
1881
|
const state = recent.context;
|
|
1885
1882
|
if (!state) return;
|
|
1886
|
-
this.forcedConsults.markStarted(recent);
|
|
1883
|
+
this.harness.forcedConsults.markStarted(recent);
|
|
1887
1884
|
state.promise = promise;
|
|
1888
1885
|
promise.then((text) => {
|
|
1889
1886
|
state.result = {
|
|
1890
1887
|
status: "fulfilled",
|
|
1891
1888
|
text
|
|
1892
1889
|
};
|
|
1893
|
-
this.forcedConsults.markDelivered(recent);
|
|
1890
|
+
this.harness.forcedConsults.markDelivered(recent);
|
|
1894
1891
|
}).catch((error) => {
|
|
1895
1892
|
state.result = {
|
|
1896
1893
|
status: "rejected",
|
|
1897
1894
|
error: formatErrorMessage(error)
|
|
1898
1895
|
};
|
|
1899
|
-
this.forcedConsults.markDelivered(recent);
|
|
1896
|
+
this.harness.forcedConsults.markDelivered(recent);
|
|
1900
1897
|
});
|
|
1901
1898
|
}
|
|
1902
1899
|
findRecentAgentProxyConsultContext(consultMessage) {
|
|
1903
|
-
return this.forcedConsults.findRecent(consultMessage);
|
|
1900
|
+
return this.harness.forcedConsults.findRecent(consultMessage);
|
|
1904
1901
|
}
|
|
1905
1902
|
async submitTerminalRealtimeToolResult(callId, session, result) {
|
|
1906
1903
|
if (session.bridge.supportsToolResultSuppression === false) {
|
|
@@ -2118,47 +2115,6 @@ function sanitizeVoiceReplyTextForSpeech(text, speakerLabel) {
|
|
|
2118
2115
|
}
|
|
2119
2116
|
//#endregion
|
|
2120
2117
|
//#region extensions/discord/src/voice/tts.ts
|
|
2121
|
-
function mergeTtsConfig(base, override) {
|
|
2122
|
-
if (!override) return base;
|
|
2123
|
-
const baseProviders = base.providers ?? {};
|
|
2124
|
-
const overrideProviders = override.providers ?? {};
|
|
2125
|
-
const mergedProviders = Object.fromEntries(uniqueStrings([...Object.keys(baseProviders), ...Object.keys(overrideProviders)]).map((providerId) => {
|
|
2126
|
-
const baseProvider = baseProviders[providerId] ?? {};
|
|
2127
|
-
const overrideProvider = overrideProviders[providerId] ?? {};
|
|
2128
|
-
return [providerId, {
|
|
2129
|
-
...baseProvider,
|
|
2130
|
-
...overrideProvider
|
|
2131
|
-
}];
|
|
2132
|
-
}));
|
|
2133
|
-
return {
|
|
2134
|
-
...base,
|
|
2135
|
-
...override,
|
|
2136
|
-
modelOverrides: {
|
|
2137
|
-
...base.modelOverrides,
|
|
2138
|
-
...override.modelOverrides
|
|
2139
|
-
},
|
|
2140
|
-
...Object.keys(mergedProviders).length === 0 ? {} : { providers: mergedProviders }
|
|
2141
|
-
};
|
|
2142
|
-
}
|
|
2143
|
-
function resolveVoiceTtsConfig(params) {
|
|
2144
|
-
if (!params.override) return {
|
|
2145
|
-
cfg: params.cfg,
|
|
2146
|
-
resolved: resolveTtsConfig(params.cfg)
|
|
2147
|
-
};
|
|
2148
|
-
const merged = mergeTtsConfig(params.cfg.messages?.tts ?? {}, params.override);
|
|
2149
|
-
const messages = params.cfg.messages ?? {};
|
|
2150
|
-
const cfg = {
|
|
2151
|
-
...params.cfg,
|
|
2152
|
-
messages: {
|
|
2153
|
-
...messages,
|
|
2154
|
-
tts: merged
|
|
2155
|
-
}
|
|
2156
|
-
};
|
|
2157
|
-
return {
|
|
2158
|
-
cfg,
|
|
2159
|
-
resolved: resolveTtsConfig(cfg)
|
|
2160
|
-
};
|
|
2161
|
-
}
|
|
2162
2118
|
async function transcribeVoiceAudio(params) {
|
|
2163
2119
|
return normalizeOptionalString((await getDiscordRuntime().mediaUnderstanding.transcribeAudioFile({
|
|
2164
2120
|
filePath: params.filePath,
|
|
@@ -2168,21 +2124,18 @@ async function transcribeVoiceAudio(params) {
|
|
|
2168
2124
|
})).text);
|
|
2169
2125
|
}
|
|
2170
2126
|
async function synthesizeVoiceReplyAudio(params) {
|
|
2171
|
-
const
|
|
2127
|
+
const runtime = getDiscordRuntime();
|
|
2128
|
+
const prepared = await runtime.tts.prepareTtsRequest({
|
|
2172
2129
|
cfg: params.cfg,
|
|
2173
|
-
override: params.override
|
|
2174
|
-
|
|
2175
|
-
const directive = parseTtsDirectives(params.replyText, ttsConfig.modelOverrides, {
|
|
2176
|
-
cfg: ttsCfg,
|
|
2177
|
-
providerConfigs: ttsConfig.providerConfigs,
|
|
2178
|
-
preferredProviderId: getTtsProvider(ttsConfig, resolveTtsPrefsPath(ttsConfig))
|
|
2130
|
+
override: params.override,
|
|
2131
|
+
text: params.replyText
|
|
2179
2132
|
});
|
|
2133
|
+
const directive = prepared.directives;
|
|
2180
2134
|
const speakText = sanitizeVoiceReplyTextForSpeech(directive.overrides.ttsText ?? directive.cleanedText.trim(), params.speakerLabel);
|
|
2181
2135
|
if (!speakText) return { status: "empty" };
|
|
2182
|
-
const runtime = getDiscordRuntime();
|
|
2183
2136
|
const streamResult = await runtime.tts.textToSpeechStream?.({
|
|
2184
2137
|
text: speakText,
|
|
2185
|
-
cfg:
|
|
2138
|
+
cfg: prepared.cfg,
|
|
2186
2139
|
channel: "discord",
|
|
2187
2140
|
overrides: directive.overrides,
|
|
2188
2141
|
disableFallback: true
|
|
@@ -2196,7 +2149,7 @@ async function synthesizeVoiceReplyAudio(params) {
|
|
|
2196
2149
|
};
|
|
2197
2150
|
const result = await runtime.tts.textToSpeech({
|
|
2198
2151
|
text: speakText,
|
|
2199
|
-
cfg:
|
|
2152
|
+
cfg: prepared.cfg,
|
|
2200
2153
|
channel: "discord",
|
|
2201
2154
|
overrides: directive.overrides
|
|
2202
2155
|
});
|