
























@@ -22,6 +22,7 @@ import { buildMeetDtmfSequence, normalizeDialInNumber } from "./src/transports/t
22222323const voiceCallMocks = vi.hoisted(() => ({
2424joinMeetViaVoiceCallGateway: vi.fn(async () => ({ callId: "call-1", dtmfSent: true })),
25+endMeetVoiceCallGatewayCall: vi.fn(async () => {}),
2526}));
26272728const fetchGuardMocks = vi.hoisted(() => ({
@@ -45,6 +46,7 @@ vi.mock("openclaw/plugin-sdk/ssrf-runtime", () => ({
45464647vi.mock("./src/voice-call-gateway.js", () => ({
4748joinMeetViaVoiceCallGateway: voiceCallMocks.joinMeetViaVoiceCallGateway,
49+endMeetVoiceCallGatewayCall: voiceCallMocks.endMeetVoiceCallGatewayCall,
4850}));
49515052const noopLogger = {
@@ -168,6 +170,24 @@ describe("google-meet plugin", () => {
168170});
169171});
170172173+it("uses a provider-safe flat tool parameter schema", () => {
174+const { tools } = setup();
175+const tool = tools[0] as { parameters: unknown };
176+177+expect(JSON.stringify(tool.parameters)).not.toContain("anyOf");
178+expect(tool.parameters).toMatchObject({
179+type: "object",
180+properties: {
181+action: {
182+type: "string",
183+enum: ["join", "status", "setup_status", "resolve_space", "preflight", "leave"],
184+},
185+transport: { type: "string", enum: ["chrome", "twilio"] },
186+mode: { type: "string", enum: ["realtime", "transcribe"] },
187+},
188+});
189+});
190+171191it("normalizes Meet URLs, codes, and space names for the Meet API", () => {
172192expect(normalizeGoogleMeetSpaceName("spaces/abc-defg-hij")).toBe("spaces/abc-defg-hij");
173193expect(normalizeGoogleMeetSpaceName("abc-defg-hij")).toBe("spaces/abc-defg-hij");
@@ -323,6 +343,26 @@ describe("google-meet plugin", () => {
323343});
324344});
325345346+it("hangs up delegated Twilio calls on leave", async () => {
347+const { tools } = setup({ defaultTransport: "twilio" });
348+const tool = tools[0] as {
349+execute: (id: string, params: unknown) => Promise<{ details: { session: { id: string } } }>;
350+};
351+const joined = await tool.execute("id", {
352+action: "join",
353+url: "https://meet.google.com/abc-defg-hij",
354+dialInNumber: "+15551234567",
355+pin: "123456",
356+});
357+358+await tool.execute("id", { action: "leave", sessionId: joined.details.session.id });
359+360+expect(voiceCallMocks.endMeetVoiceCallGatewayCall).toHaveBeenCalledWith({
361+config: expect.objectContaining({ defaultTransport: "twilio" }),
362+callId: "call-1",
363+});
364+});
365+326366it("reports setup status through the tool", async () => {
327367const { tools } = setup({
328368chrome: {
@@ -415,6 +455,13 @@ describe("google-meet plugin", () => {
415455| {
416456onAudio: (audio: Buffer) => void;
417457onMark?: (markName: string) => void;
458+onToolCall?: (event: {
459+itemId: string;
460+callId: string;
461+name: string;
462+args: unknown;
463+}) => void;
464+tools?: unknown[];
418465}
419466| undefined;
420467const sendAudio = vi.fn();
@@ -464,12 +511,33 @@ describe("google-meet plugin", () => {
464511const inputProcess = makeProcess({ stdout: inputStdout, stdin: null });
465512const outputProcess = makeProcess({ stdin: outputStdin, stdout: null });
466513const spawnMock = vi.fn().mockReturnValueOnce(outputProcess).mockReturnValueOnce(inputProcess);
514+const sessionStore: Record<string, unknown> = {};
515+const runtime = {
516+agent: {
517+resolveAgentDir: vi.fn(() => "/tmp/agent"),
518+resolveAgentWorkspaceDir: vi.fn(() => "/tmp/workspace"),
519+ensureAgentWorkspace: vi.fn(async () => {}),
520+session: {
521+resolveStorePath: vi.fn(() => "/tmp/sessions.json"),
522+loadSessionStore: vi.fn(() => sessionStore),
523+saveSessionStore: vi.fn(async () => {}),
524+resolveSessionFilePath: vi.fn(() => "/tmp/session.json"),
525+},
526+runEmbeddedPiAgent: vi.fn(async () => ({
527+payloads: [{ text: "Use the Portugal launch data." }],
528+meta: {},
529+})),
530+resolveAgentTimeoutMs: vi.fn(() => 1000),
531+},
532+};
467533468534const handle = await startCommandRealtimeAudioBridge({
469535config: resolveGoogleMeetConfig({
470536realtime: { provider: "openai", model: "gpt-realtime" },
471537}),
472538fullConfig: {} as never,
539+runtime: runtime as never,
540+meetingSessionId: "meet-1",
473541inputCommand: ["capture-meet"],
474542outputCommand: ["play-meet"],
475543logger: noopLogger,
@@ -480,6 +548,12 @@ describe("google-meet plugin", () => {
480548inputStdout.write(Buffer.from([1, 2, 3]));
481549callbacks?.onAudio(Buffer.from([4, 5]));
482550callbacks?.onMark?.("mark-1");
551+callbacks?.onToolCall?.({
552+itemId: "item-1",
553+callId: "tool-call-1",
554+name: "openclaw_agent_consult",
555+args: { question: "What should I say about launch timing?" },
556+});
483557484558expect(spawnMock).toHaveBeenNthCalledWith(1, "play-meet", [], {
485559stdio: ["pipe", "ignore", "pipe"],
@@ -490,6 +564,25 @@ describe("google-meet plugin", () => {
490564expect(sendAudio).toHaveBeenCalledWith(Buffer.from([1, 2, 3]));
491565expect(outputStdinWrites).toEqual([Buffer.from([4, 5])]);
492566expect(bridge.acknowledgeMark).toHaveBeenCalled();
567+expect(callbacks).toMatchObject({
568+tools: [
569+expect.objectContaining({
570+name: "openclaw_agent_consult",
571+}),
572+],
573+});
574+await vi.waitFor(() => {
575+expect(bridge.submitToolResult).toHaveBeenCalledWith("tool-call-1", {
576+text: "Use the Portugal launch data.",
577+});
578+});
579+expect(runtime.agent.runEmbeddedPiAgent).toHaveBeenCalledWith(
580+expect.objectContaining({
581+messageProvider: "google-meet",
582+thinkLevel: "high",
583+toolsAllow: ["read", "web_search", "web_fetch", "x_search", "memory_search", "memory_get"],
584+}),
585+);
493586494587await handle.stop();
495588expect(bridge.close).toHaveBeenCalled();
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。