




















@@ -1,11 +1,12 @@
11import { afterEach, describe, expect, it, vi } from "vitest";
2+import type { RunEmbeddedPiAgentParams } from "../agents/pi-embedded-runner/run/params.js";
23import {
34__setRealtimeVoiceAgentConsultDepsForTest,
45consultRealtimeVoiceAgent,
56resolveRealtimeVoiceAgentConsultTools,
67resolveRealtimeVoiceAgentConsultToolsAllow,
78} from "./agent-consult-runtime.js";
8-import { REALTIME_VOICE_AGENT_CONSULT_TOOL_NAME } from "./agent-consult-tool.js";
9+import { REALTIME_VOICE_AGENT_CONSULT_TOOL } from "./agent-consult-tool.js";
9101011function createAgentRuntime(payloads: unknown[] = [{ text: "Speak this." }]) {
1112const sessionStore: Record<
@@ -64,14 +65,34 @@ function createAgentRuntime(payloads: unknown[] = [{ text: "Speak this." }]) {
6465};
6566}
666768+function requireEmbeddedPiAgentCall(runEmbeddedPiAgent: {
69+mock: { calls: unknown[][] };
70+}): RunEmbeddedPiAgentParams {
71+const call = runEmbeddedPiAgent.mock.calls[0]?.[0] as RunEmbeddedPiAgentParams | undefined;
72+if (!call) {
73+throw new Error("Expected embedded PI agent call");
74+}
75+return call;
76+}
77+78+function expectPositiveTimestamp(value: unknown) {
79+expect(typeof value).toBe("number");
80+expect(value as number).toBeGreaterThan(0);
81+}
82+83+function expectNonEmptyString(value: unknown) {
84+expect(typeof value).toBe("string");
85+expect((value as string).trim()).not.toBe("");
86+}
87+6788describe("realtime voice agent consult runtime", () => {
6889afterEach(() => {
6990__setRealtimeVoiceAgentConsultDepsForTest(null);
7091});
71927293it("exposes the shared consult tool based on policy", () => {
73-expect(resolveRealtimeVoiceAgentConsultTools("safe-read-only")).toEqual([
74-expect.objectContaining({ name: REALTIME_VOICE_AGENT_CONSULT_TOOL_NAME }),
94+expect(resolveRealtimeVoiceAgentConsultTools("safe-read-only")).toStrictEqual([
95+REALTIME_VOICE_AGENT_CONSULT_TOOL,
7596]);
7697expect(resolveRealtimeVoiceAgentConsultTools("none")).toStrictEqual([]);
7798expect(resolveRealtimeVoiceAgentConsultToolsAllow("safe-read-only")).toEqual([
@@ -114,23 +135,23 @@ describe("realtime voice agent consult runtime", () => {
114135if (!voiceSession) {
115136throw new Error("Expected voice consult session entry");
116137}
117-expect(voiceSession.sessionId).toEqual(expect.stringMatching(/\S/));
118-expect(runEmbeddedPiAgent).toHaveBeenCalledWith(
119- expect.objectContaining({
120- sessionKey: "voice:15550001234",
121- sandboxSessionKey: "agent:main:voice:15550001234",
122- agentId: "main",
123- messageProvider: "voice",
124- lane: "voice",
125- toolsAllow: ["read"],
126- provider: "openai",
127- model: "gpt-5.4",
128- thinkLevel: "high",
129- timeoutMs: 10_000,
130- prompt: expect.stringContaining("Caller: Can you check this?"),
131- extraSystemPrompt: expect.stringContaining("delegated requests"),
132- }),
133-);
138+expect(Object.keys(voiceSession).toSorted()).toStrictEqual(["sessionId", "updatedAt"]);
139+expectNonEmptyString(voiceSession.sessionId);
140+expectPositiveTimestamp(voiceSession.updatedAt);
141+const call = requireEmbeddedPiAgentCall(runEmbeddedPiAgent);
142+expect(call.sessionId).toBe(voiceSession.sessionId);
143+expect(call.sessionKey).toBe("voice:15550001234");
144+expect(call.sandboxSessionKey).toBe("agent:main:voice:15550001234");
145+expect(call.agentId).toBe("main");
146+expect(call.messageProvider).toBe("voice");
147+expect(call.lane).toBe("voice");
148+expect(call.toolsAllow).toStrictEqual(["read"]);
149+expect(call.provider).toBe("openai");
150+expect(call.model).toBe("gpt-5.4");
151+expect(call.thinkLevel).toBe("high");
152+expect(call.timeoutMs).toBe(10_000);
153+expect(call.prompt).toContain("Caller: Can you check this?");
154+expect(call.extraSystemPrompt).toContain("delegated requests");
134155});
135156136157it("scopes sandbox resolution to the configured consult agent", async () => {
@@ -151,13 +172,10 @@ describe("realtime voice agent consult runtime", () => {
151172userLabel: "Caller",
152173});
153174154-expect(runEmbeddedPiAgent).toHaveBeenCalledWith(
155-expect.objectContaining({
156-sessionKey: "voice:15550001234",
157-sandboxSessionKey: "agent:voice:voice:15550001234",
158-agentId: "voice",
159-}),
160-);
175+const call = requireEmbeddedPiAgentCall(runEmbeddedPiAgent);
176+expect(call.sessionKey).toBe("voice:15550001234");
177+expect(call.sandboxSessionKey).toBe("agent:voice:voice:15550001234");
178+expect(call.agentId).toBe("voice");
161179});
162180163181it("returns a speakable fallback when the embedded agent has no visible text", async () => {
@@ -233,19 +251,22 @@ describe("realtime voice agent consult runtime", () => {
233251agentId: "main",
234252sessionsDir: "/tmp",
235253});
236-expect(sessionStore["agent:main:subagent:google-meet:meet-1"]).toMatchObject({
254+const forkedEntry = sessionStore["agent:main:subagent:google-meet:meet-1"];
255+if (!forkedEntry) {
256+throw new Error("Expected forked consult session entry");
257+}
258+expect(forkedEntry).toStrictEqual({
237259sessionId: "forked-session",
238260sessionFile: "/tmp/forked.jsonl",
239261spawnedBy: "agent:main:main",
240262forkedFromParent: true,
263+updatedAt: forkedEntry.updatedAt,
241264});
242-expect(runEmbeddedPiAgent).toHaveBeenCalledWith(
243-expect.objectContaining({
244-sessionId: "forked-session",
245-sessionFile: "/tmp/forked.jsonl",
246-spawnedBy: "agent:main:main",
247-}),
248-);
265+expectPositiveTimestamp(forkedEntry.updatedAt);
266+const call = requireEmbeddedPiAgentCall(runEmbeddedPiAgent);
267+expect(call.sessionId).toBe("forked-session");
268+expect(call.sessionFile).toBe("/tmp/forked.jsonl");
269+expect(call.spawnedBy).toBe("agent:main:main");
249270});
250271251272it("inherits requester message routing for forked consult sessions", async () => {
@@ -277,17 +298,20 @@ describe("realtime voice agent consult runtime", () => {
277298userLabel: "Caller",
278299});
279300280-expect(runEmbeddedPiAgent).toHaveBeenCalledWith(
281-expect.objectContaining({
282-sessionKey: "voice:google-meet:meet-1",
283-spawnedBy: "agent:main:discord:channel:123",
284-messageProvider: "discord",
285-agentAccountId: "default",
286-messageTo: "channel:123",
287-currentChannelId: "channel:123",
288-}),
289-);
290-expect(sessionStore["voice:google-meet:meet-1"]).toMatchObject({
301+const call = requireEmbeddedPiAgentCall(runEmbeddedPiAgent);
302+expect(call.sessionKey).toBe("voice:google-meet:meet-1");
303+expect(call.spawnedBy).toBe("agent:main:discord:channel:123");
304+expect(call.messageProvider).toBe("discord");
305+expect(call.agentAccountId).toBe("default");
306+expect(call.messageTo).toBe("channel:123");
307+expect(call.currentChannelId).toBe("channel:123");
308+const voiceEntry = sessionStore["voice:google-meet:meet-1"];
309+if (!voiceEntry) {
310+throw new Error("Expected voice consult session entry");
311+}
312+expect(voiceEntry).toStrictEqual({
313+sessionId: voiceEntry.sessionId,
314+spawnedBy: "agent:main:discord:channel:123",
291315deliveryContext: {
292316channel: "discord",
293317to: "channel:123",
@@ -296,7 +320,11 @@ describe("realtime voice agent consult runtime", () => {
296320lastChannel: "discord",
297321lastTo: "channel:123",
298322lastAccountId: "default",
323+lastThreadId: undefined,
324+updatedAt: voiceEntry.updatedAt,
299325});
326+expectNonEmptyString(voiceEntry.sessionId);
327+expectPositiveTimestamp(voiceEntry.updatedAt);
300328});
301329302330it("reuses the call session delivery context when requester metadata is absent", async () => {
@@ -327,17 +355,14 @@ describe("realtime voice agent consult runtime", () => {
327355userLabel: "Caller",
328356});
329357330-expect(runEmbeddedPiAgent).toHaveBeenCalledWith(
331-expect.objectContaining({
332-sessionId: "call-session",
333-sessionKey: "voice:google-meet:meet-1",
334-messageProvider: "discord",
335-agentAccountId: "default",
336-messageTo: "channel:123",
337-messageThreadId: "thread-456",
338-currentChannelId: "channel:123",
339-currentThreadTs: "thread-456",
340-}),
341-);
358+const call = requireEmbeddedPiAgentCall(runEmbeddedPiAgent);
359+expect(call.sessionId).toBe("call-session");
360+expect(call.sessionKey).toBe("voice:google-meet:meet-1");
361+expect(call.messageProvider).toBe("discord");
362+expect(call.agentAccountId).toBe("default");
363+expect(call.messageTo).toBe("channel:123");
364+expect(call.messageThreadId).toBe("thread-456");
365+expect(call.currentChannelId).toBe("channel:123");
366+expect(call.currentThreadTs).toBe("thread-456");
342367});
343368});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。