




























@@ -1451,6 +1451,77 @@ describe("DiscordVoiceManager", () => {
14511451expectUserMessageIncludes("third answer");
14521452});
145314531454+it("does not interrupt active exact speech for a later forced agent-proxy consult", async () => {
1455+agentCommandMock
1456+.mockResolvedValueOnce({ payloads: [{ text: "first answer" }] })
1457+.mockResolvedValueOnce({ payloads: [{ text: "second answer" }] });
1458+const manager = createManager({
1459+groupPolicy: "open",
1460+voice: {
1461+enabled: true,
1462+mode: "agent-proxy",
1463+realtime: { provider: "openai" },
1464+},
1465+});
1466+1467+await manager.join({ guildId: "g1", channelId: "1001" });
1468+const entry = getSessionEntry(manager) as {
1469+realtime?: {
1470+beginSpeakerTurn: (
1471+context: { extraSystemPrompt?: string; senderIsOwner: boolean; speakerLabel: string },
1472+userId: string,
1473+) => { close: () => void; sendInputAudio: (audio: Buffer) => void };
1474+};
1475+};
1476+const player = getLastAudioPlayer();
1477+const bridgeParams = createRealtimeVoiceBridgeSessionMock.mock.calls.at(-1)?.[0] as
1478+| {
1479+audioSink?: { sendAudio: (audio: Buffer) => void };
1480+onEvent?: (event: { direction: "server"; type: string }) => void;
1481+onTranscript?: (role: "user" | "assistant", text: string, isFinal: boolean) => void;
1482+}
1483+| undefined;
1484+1485+const firstTurn = entry.realtime?.beginSpeakerTurn(
1486+{ extraSystemPrompt: undefined, senderIsOwner: true, speakerLabel: "Owner" },
1487+"u-owner",
1488+);
1489+firstTurn?.sendInputAudio(Buffer.alloc(8));
1490+bridgeParams?.onTranscript?.("user", "first question", true);
1491+1492+await new Promise((resolve) => setTimeout(resolve, 260));
1493+await vi.waitFor(() => expectUserMessageIncludes("first answer"));
1494+bridgeParams?.audioSink?.sendAudio(Buffer.alloc(480));
1495+1496+const secondTurn = entry.realtime?.beginSpeakerTurn(
1497+{ extraSystemPrompt: undefined, senderIsOwner: true, speakerLabel: "Owner" },
1498+"u-owner",
1499+);
1500+secondTurn?.sendInputAudio(Buffer.alloc(8));
1501+bridgeParams?.onTranscript?.("user", "second question", true);
1502+1503+await new Promise((resolve) => setTimeout(resolve, 260));
1504+expect(
1505+realtimeSessionMock.handleBargeIn.mock.calls.some(([arg]) => {
1506+return (arg as { force?: boolean } | undefined)?.force === true;
1507+}),
1508+).toBe(false);
1509+expect(player.stop).not.toHaveBeenCalled();
1510+expectUserMessageNotIncludes("second answer");
1511+1512+bridgeParams?.onEvent?.({ direction: "server", type: "response.done" });
1513+const firstStream = createAudioResourceMock.mock.calls.at(-1)?.[0] as PassThrough | undefined;
1514+await vi.waitFor(() => expect(firstStream?.writableEnded).toBe(true));
1515+await new Promise((resolve) => setTimeout(resolve, 0));
1516+expectUserMessageNotIncludes("second answer");
1517+1518+const idleHandler = player.on.mock.calls.find(([event]) => event === "idle")?.[1] as
1519+| (() => void)
1520+| undefined;
1521+idleHandler?.();
1522+expectUserMessageIncludes("second answer");
1523+});
1524+14541525it("matches agent-proxy consult tool calls to the pending transcript", async () => {
14551526agentCommandMock
14561527.mockResolvedValueOnce({ payloads: [{ text: "owner answer" }] })
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。