
























@@ -1998,6 +1998,9 @@ describe("google-meet plugin", () => {
19981998details: {
19991999manualActionRequired?: boolean;
20002000manualActionReason?: string;
2001+speechReady?: boolean;
2002+speechBlockedReason?: string;
2003+spoken?: boolean;
20012004session?: { chrome?: { health?: { manualActionRequired?: boolean } } };
20022005};
20032006}>;
@@ -2012,17 +2015,157 @@ describe("google-meet plugin", () => {
20122015expect(result.details).toMatchObject({
20132016manualActionRequired: true,
20142017manualActionReason: "google-login-required",
2018+spoken: false,
2019+speechReady: false,
2020+speechBlockedReason: "google-login-required",
20152021session: {
20162022chrome: {
20172023health: {
20182024manualActionRequired: true,
20192025manualActionReason: "google-login-required",
2026+speechReady: false,
2027+speechBlockedReason: "google-login-required",
20202028},
20212029},
20222030},
20232031});
20242032});
202520332034+it("refreshes browser health before blocking an explicit speech retry", async () => {
2035+let openedTab = false;
2036+let browserReady = false;
2037+const { methods, nodesInvoke } = setup(
2038+{
2039+defaultTransport: "chrome-node",
2040+defaultMode: "realtime",
2041+},
2042+{
2043+nodesInvokeHandler: async ({ command, params }) => {
2044+const raw = params as { path?: string; body?: { url?: string; targetId?: string } };
2045+if (command === "browser.proxy") {
2046+if (raw.path === "/tabs") {
2047+return {
2048+payload: {
2049+result: {
2050+running: true,
2051+tabs: openedTab
2052+ ? [
2053+{
2054+targetId: "tab-1",
2055+title: "Meet",
2056+url: "https://meet.google.com/abc-defg-hij",
2057+},
2058+]
2059+ : [],
2060+},
2061+},
2062+};
2063+}
2064+if (raw.path === "/tabs/open") {
2065+openedTab = true;
2066+return {
2067+payload: {
2068+result: {
2069+targetId: "tab-1",
2070+title: "Meet",
2071+url: raw.body?.url ?? "https://meet.google.com/abc-defg-hij",
2072+},
2073+},
2074+};
2075+}
2076+if (raw.path === "/tabs/focus" || raw.path === "/permissions/grant") {
2077+return { payload: { result: { ok: true } } };
2078+}
2079+if (raw.path === "/act") {
2080+return {
2081+payload: {
2082+result: {
2083+ok: true,
2084+targetId: raw.body?.targetId ?? "tab-1",
2085+result: JSON.stringify(
2086+browserReady
2087+ ? {
2088+inCall: true,
2089+micMuted: false,
2090+manualActionRequired: false,
2091+title: "Meet call",
2092+url: "https://meet.google.com/abc-defg-hij",
2093+}
2094+ : {
2095+inCall: false,
2096+manualActionRequired: true,
2097+manualActionReason: "google-login-required",
2098+manualActionMessage:
2099+"Sign in to Google in the OpenClaw browser profile, then retry the Meet join.",
2100+title: "Sign in - Google Accounts",
2101+url: "https://accounts.google.com/signin",
2102+},
2103+),
2104+},
2105+},
2106+};
2107+}
2108+}
2109+if (command === "googlemeet.chrome") {
2110+return { payload: { launched: true } };
2111+}
2112+throw new Error(`unexpected invoke ${command}`);
2113+},
2114+},
2115+);
2116+2117+const join = (await invokeGoogleMeetGatewayMethodForTest(methods, "googlemeet.join", {
2118+url: "https://meet.google.com/abc-defg-hij",
2119+message: "Say exactly: hello.",
2120+})) as {
2121+session: { id: string; chrome?: { health?: { speechBlockedReason?: string } } };
2122+spoken: boolean;
2123+};
2124+expect(join.spoken).toBe(false);
2125+expect(join.session.chrome?.health?.speechBlockedReason).toBe("google-login-required");
2126+2127+browserReady = true;
2128+const retry = (await invokeGoogleMeetGatewayMethodForTest(methods, "googlemeet.speak", {
2129+sessionId: join.session.id,
2130+message: "Say exactly: hello again.",
2131+})) as {
2132+found: boolean;
2133+spoken: boolean;
2134+session?: {
2135+chrome?: {
2136+health?: {
2137+inCall?: boolean;
2138+manualActionRequired?: boolean;
2139+speechBlockedReason?: string;
2140+};
2141+};
2142+};
2143+};
2144+2145+expect(retry).toMatchObject({
2146+found: true,
2147+spoken: false,
2148+session: {
2149+chrome: {
2150+health: {
2151+inCall: true,
2152+manualActionRequired: false,
2153+speechBlockedReason: "audio-bridge-unavailable",
2154+},
2155+},
2156+},
2157+});
2158+expect(nodesInvoke).toHaveBeenCalledWith(
2159+expect.objectContaining({
2160+command: "browser.proxy",
2161+params: expect.objectContaining({
2162+path: "/tabs/focus",
2163+body: { targetId: "tab-1" },
2164+}),
2165+}),
2166+);
2167+});
2168+20262169it("explains when chrome-node has no capable paired node", async () => {
20272170const { tools } = setup(
20282171{
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。