mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 09:40:43 +00:00
fix(google-meet): guide timeout recovery
This commit is contained in:
@@ -96,6 +96,7 @@ Docs: https://docs.openclaw.ai
|
||||
- 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.
|
||||
- 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.
|
||||
- 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.
|
||||
- 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.
|
||||
- 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.
|
||||
- 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.
|
||||
- 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.
|
||||
|
||||
@@ -202,8 +202,9 @@ describe("google-meet plugin", () => {
|
||||
|
||||
it("uses a provider-safe flat tool parameter schema", () => {
|
||||
const { tools } = setup();
|
||||
const tool = tools[0] as { parameters: unknown };
|
||||
const tool = tools[0] as { description?: string; parameters: unknown };
|
||||
|
||||
expect(tool.description).toContain("recover_current_tab");
|
||||
expect(JSON.stringify(tool.parameters)).not.toContain("anyOf");
|
||||
expect(tool.parameters).toMatchObject({
|
||||
type: "object",
|
||||
@@ -222,6 +223,7 @@ describe("google-meet plugin", () => {
|
||||
"speak",
|
||||
"test_speech",
|
||||
],
|
||||
description: expect.stringContaining("recover_current_tab"),
|
||||
},
|
||||
transport: { type: "string", enum: ["chrome", "chrome-node", "twilio"] },
|
||||
mode: { type: "string", enum: ["realtime", "transcribe"] },
|
||||
|
||||
@@ -150,7 +150,7 @@ const GoogleMeetToolSchema = Type.Object({
|
||||
"test_speech",
|
||||
],
|
||||
description:
|
||||
"Google Meet action to run. create creates a meeting and joins it by default; pass join=false to only mint a meeting URL.",
|
||||
"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.",
|
||||
}),
|
||||
join: Type.Optional(
|
||||
Type.Boolean({
|
||||
@@ -391,7 +391,8 @@ export default definePluginEntry({
|
||||
api.registerTool({
|
||||
name: "google_meet",
|
||||
label: "Google Meet",
|
||||
description: "Join and track Google Meet sessions through Chrome or Twilio.",
|
||||
description:
|
||||
"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.",
|
||||
parameters: GoogleMeetToolSchema,
|
||||
async execute(_toolCallId, params) {
|
||||
const raw = asParamRecord(params);
|
||||
|
||||
@@ -535,8 +535,20 @@ const waitForSpawnedProcess = async (childProcess, deps) => {
|
||||
|
||||
try {
|
||||
return await new Promise((resolve) => {
|
||||
let settled = false;
|
||||
const settle = (res) => {
|
||||
if (settled) {
|
||||
return;
|
||||
}
|
||||
settled = true;
|
||||
resolve(res);
|
||||
};
|
||||
childProcess.on("error", (error) => {
|
||||
logRunner(`Spawn failed: ${error?.message ?? String(error)}`, deps);
|
||||
settle({ exitCode: 1, exitSignal: null, forwardedSignal });
|
||||
});
|
||||
childProcess.on("exit", (exitCode, exitSignal) => {
|
||||
resolve({ exitCode, exitSignal, forwardedSignal });
|
||||
settle({ exitCode, exitSignal, forwardedSignal });
|
||||
});
|
||||
});
|
||||
} finally {
|
||||
|
||||
@@ -807,6 +807,40 @@ describe("run-node script", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("returns failure and releases the build lock when the compiler spawn errors", async () => {
|
||||
await withTempDir({ prefix: "openclaw-run-node-" }, async (tmp) => {
|
||||
const spawn = (cmd: string, args: string[] = []) => {
|
||||
if (cmd === process.execPath && args[0] === "scripts/tsdown-build.mjs") {
|
||||
const events = new EventEmitter();
|
||||
queueMicrotask(() => events.emit("error", new Error("spawn failed")));
|
||||
return {
|
||||
on: (event: string, cb: (code: number | null, signal: string | null) => void) => {
|
||||
events.on(event, cb);
|
||||
return undefined;
|
||||
},
|
||||
};
|
||||
}
|
||||
return createExitedProcess(0);
|
||||
};
|
||||
|
||||
const exitCode = await runNodeMain({
|
||||
cwd: tmp,
|
||||
args: ["status"],
|
||||
env: {
|
||||
...process.env,
|
||||
OPENCLAW_FORCE_BUILD: "1",
|
||||
OPENCLAW_RUNNER_LOG: "0",
|
||||
},
|
||||
spawn,
|
||||
execPath: process.execPath,
|
||||
platform: process.platform,
|
||||
});
|
||||
|
||||
expect(exitCode).toBe(1);
|
||||
expect(fsSync.existsSync(path.join(tmp, ".artifacts", "run-node-build.lock"))).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
it("forwards wrapper SIGTERM to the active openclaw child and returns 143", async () => {
|
||||
await withTempDir({ prefix: "openclaw-run-node-" }, async (tmp) => {
|
||||
await setupTrackedProject(tmp, {
|
||||
|
||||
Reference in New Issue
Block a user