fix(google-meet): guide timeout recovery · openclaw/openclaw@344ee37
steipete
·
2026-04-25
·
via Recent Commits to openclaw:main
| Original file line number | Diff line number | Diff line change |
|---|
@@ -96,6 +96,7 @@ Docs: https://docs.openclaw.ai
|
96 | 96 | - Media tools: honor the configured web-fetch SSRF policy for media understanding, image/music/video generation references, and PDF inputs, so explicit RFC2544 opt-ins cover WebChat OSS uploads without weakening defaults. Fixes #71300. (#71321) Thanks @neeravmakwana. |
97 | 97 | - Agents/TTS: suppress successful spoken transcripts from verbose chat tool output when structured voice media is already queued, while preserving text output for non-builtin tool-name collisions. Fixes #71282. Thanks @neeravmakwana. |
98 | 98 | - Plugins/Google Meet: reuse existing Meet tabs and active sessions across harmless URL query differences, avoiding duplicate Chrome windows when agents retry a join. Thanks @steipete. |
| 99 | +- Plugins/Google Meet: tell agents to recover already-open Meet tabs after browser timeouts, and make the dev CLI release its build lock if compiler spawning fails. Thanks @steipete. |
99 | 100 | - Gateway/sessions: recover main-agent turns interrupted by a gateway restart from stale transcript-lock evidence, avoiding stuck `status: "running"` sessions without broad post-boot transcript scans. Fixes #70555. Thanks @bitloi. |
100 | 101 | - Codex approvals: keep command approval responses within Codex app-server `availableDecisions`, including deny/cancel fallbacks for prompts that do not offer `decline`. (#71338) Thanks @Lucenx9. |
101 | 102 | - Codex harness: reject same-thread app-server notifications without `turnId` or `turn.id` after a bound turn starts, preventing unscoped events from mutating or completing the active reply. (#71317) Thanks @Lucenx9. |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -202,8 +202,9 @@ describe("google-meet plugin", () => {
|
202 | 202 | |
203 | 203 | it("uses a provider-safe flat tool parameter schema", () => { |
204 | 204 | const { tools } = setup(); |
205 | | -const tool = tools[0] as { parameters: unknown }; |
| 205 | +const tool = tools[0] as { description?: string; parameters: unknown }; |
206 | 206 | |
| 207 | +expect(tool.description).toContain("recover_current_tab"); |
207 | 208 | expect(JSON.stringify(tool.parameters)).not.toContain("anyOf"); |
208 | 209 | expect(tool.parameters).toMatchObject({ |
209 | 210 | type: "object", |
@@ -222,6 +223,7 @@ describe("google-meet plugin", () => {
|
222 | 223 | "speak", |
223 | 224 | "test_speech", |
224 | 225 | ], |
| 226 | +description: expect.stringContaining("recover_current_tab"), |
225 | 227 | }, |
226 | 228 | transport: { type: "string", enum: ["chrome", "chrome-node", "twilio"] }, |
227 | 229 | mode: { type: "string", enum: ["realtime", "transcribe"] }, |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -150,7 +150,7 @@ const GoogleMeetToolSchema = Type.Object({
|
150 | 150 | "test_speech", |
151 | 151 | ], |
152 | 152 | description: |
153 | | -"Google Meet action to run. create creates a meeting and joins it by default; pass join=false to only mint a meeting URL.", |
| 153 | +"Google Meet action to run. create creates and joins by default; pass join=false to only mint a URL. After a timeout or unclear browser state, call recover_current_tab before retrying join.", |
154 | 154 | }), |
155 | 155 | join: Type.Optional( |
156 | 156 | Type.Boolean({ |
@@ -391,7 +391,8 @@ export default definePluginEntry({
|
391 | 391 | api.registerTool({ |
392 | 392 | name: "google_meet", |
393 | 393 | label: "Google Meet", |
394 | | -description: "Join and track Google Meet sessions through Chrome or Twilio.", |
| 394 | +description: |
| 395 | +"Join and track Google Meet sessions through Chrome or Twilio. If a Meet tab is already open after a timeout, call recover_current_tab before retrying join to report login, permission, or admission blockers without opening another tab.", |
395 | 396 | parameters: GoogleMeetToolSchema, |
396 | 397 | async execute(_toolCallId, params) { |
397 | 398 | const raw = asParamRecord(params); |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -535,8 +535,20 @@ const waitForSpawnedProcess = async (childProcess, deps) => {
|
535 | 535 | |
536 | 536 | try { |
537 | 537 | return await new Promise((resolve) => { |
| 538 | +let settled = false; |
| 539 | +const settle = (res) => { |
| 540 | +if (settled) { |
| 541 | +return; |
| 542 | +} |
| 543 | +settled = true; |
| 544 | +resolve(res); |
| 545 | +}; |
| 546 | +childProcess.on("error", (error) => { |
| 547 | +logRunner(`Spawn failed: ${error?.message ?? String(error)}`, deps); |
| 548 | +settle({ exitCode: 1, exitSignal: null, forwardedSignal }); |
| 549 | +}); |
538 | 550 | childProcess.on("exit", (exitCode, exitSignal) => { |
539 | | -resolve({ exitCode, exitSignal, forwardedSignal }); |
| 551 | +settle({ exitCode, exitSignal, forwardedSignal }); |
540 | 552 | }); |
541 | 553 | }); |
542 | 554 | } finally { |
|
| Original file line number | Diff line number | Diff line change |
|---|
@@ -807,6 +807,40 @@ describe("run-node script", () => {
|
807 | 807 | }); |
808 | 808 | }); |
809 | 809 | |
| 810 | +it("returns failure and releases the build lock when the compiler spawn errors", async () => { |
| 811 | +await withTempDir({ prefix: "openclaw-run-node-" }, async (tmp) => { |
| 812 | +const spawn = (cmd: string, args: string[] = []) => { |
| 813 | +if (cmd === process.execPath && args[0] === "scripts/tsdown-build.mjs") { |
| 814 | +const events = new EventEmitter(); |
| 815 | +queueMicrotask(() => events.emit("error", new Error("spawn failed"))); |
| 816 | +return { |
| 817 | +on: (event: string, cb: (code: number | null, signal: string | null) => void) => { |
| 818 | +events.on(event, cb); |
| 819 | +return undefined; |
| 820 | +}, |
| 821 | +}; |
| 822 | +} |
| 823 | +return createExitedProcess(0); |
| 824 | +}; |
| 825 | + |
| 826 | +const exitCode = await runNodeMain({ |
| 827 | +cwd: tmp, |
| 828 | +args: ["status"], |
| 829 | +env: { |
| 830 | + ...process.env, |
| 831 | +OPENCLAW_FORCE_BUILD: "1", |
| 832 | +OPENCLAW_RUNNER_LOG: "0", |
| 833 | +}, |
| 834 | + spawn, |
| 835 | +execPath: process.execPath, |
| 836 | +platform: process.platform, |
| 837 | +}); |
| 838 | + |
| 839 | +expect(exitCode).toBe(1); |
| 840 | +expect(fsSync.existsSync(path.join(tmp, ".artifacts", "run-node-build.lock"))).toBe(false); |
| 841 | +}); |
| 842 | +}); |
| 843 | + |
810 | 844 | it("forwards wrapper SIGTERM to the active openclaw child and returns 143", async () => { |
811 | 845 | await withTempDir({ prefix: "openclaw-run-node-" }, async (tmp) => { |
812 | 846 | await setupTrackedProject(tmp, { |
|
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。