


















@@ -15,6 +15,7 @@ import type {
1515MediaUnderstandingModelConfig,
1616} from "../config/types.tools.js";
1717import { logVerbose, shouldLogVerbose } from "../globals.js";
18+import { resolvePreferredOpenClawTmpDir } from "../infra/tmp-openclaw-dir.js";
1819import { logWarn } from "../logger.js";
1920import { resolveChannelInboundAttachmentRoots } from "../media/channel-inbound-roots.js";
2021import { mergeInboundPathRoots } from "../media/inbound-path-policy.js";
@@ -26,7 +27,6 @@ import type { ActiveMediaModel } from "./active-model.types.js";
2627import { MediaAttachmentCache, selectAttachments } from "./attachments.js";
2728import { isMediaUnderstandingSkipError } from "./errors.js";
2829import { fileExists } from "./fs.js";
29-import { extractGeminiResponse } from "./output-extract.js";
3030import { normalizeMediaExecutionProviderId, normalizeMediaProviderId } from "./provider-id.js";
3131import {
3232buildMediaUnderstandingRegistry,
@@ -304,11 +304,11 @@ export function resolveMediaAttachmentLocalRoots(params: {
304304}
305305306306const binaryCache = new Map<string, Promise<string | null>>();
307-const geminiProbeCache = new Map<string, Promise<boolean>>();
307+const antigravityCliCache = new Map<string, Promise<string | null>>();
308308309309export function clearMediaUnderstandingBinaryCacheForTests(): void {
310310binaryCache.clear();
311-geminiProbeCache.clear();
311+antigravityCliCache.clear();
312312}
313313314314function expandHomeDir(value: string): string {
@@ -406,27 +406,50 @@ async function hasBinary(name: string): Promise<boolean> {
406406return Boolean(await findBinary(name));
407407}
408408409-async function probeGeminiCli(): Promise<boolean> {
410-const cached = geminiProbeCache.get("gemini");
409+async function probeAntigravityCliCandidate(command: string): Promise<string | null> {
410+const resolved = await findBinary(command);
411+if (!resolved) {
412+return null;
413+}
414+const probeDir = await fs.mkdtemp(
415+path.join(resolvePreferredOpenClawTmpDir(), "openclaw-antigravity-probe-"),
416+);
417+try {
418+const { stdout } = await runExec(resolved, ["--help"], {
419+timeoutMs: 3000,
420+cwd: probeDir,
421+});
422+return stdout.includes("--print") &&
423+stdout.includes("--add-dir") &&
424+stdout.includes("--sandbox")
425+ ? resolved
426+ : null;
427+} catch {
428+return null;
429+} finally {
430+await fs.rm(probeDir, { recursive: true, force: true }).catch(() => {});
431+}
432+}
433+434+async function resolveAntigravityCliBinary(): Promise<string | null> {
435+const cached = antigravityCliCache.get("agy");
411436if (cached) {
412437return cached;
413438}
414439const resolved = (async () => {
415-if (!(await hasBinary("gemini"))) {
416-return false;
417-}
418-try {
419-const { stdout } = await runExec("gemini", ["--output-format", "json", "ok"], {
420-timeoutMs: 8000,
421-});
422-return Boolean(
423-extractGeminiResponse(stdout) ?? normalizeLowercaseStringOrEmpty(stdout).includes("ok"),
424-);
425-} catch {
426-return false;
440+const configured = process.env.OPENCLAW_ANTIGRAVITY_CLI?.trim();
441+const candidates = [configured, "agy", "antigravity"].filter((value): value is string =>
442+Boolean(value),
443+);
444+for (const candidate of candidates) {
445+const command = await probeAntigravityCliCandidate(candidate);
446+if (command) {
447+return command;
448+}
427449}
450+return null;
428451})();
429-geminiProbeCache.set("gemini", resolved);
452+antigravityCliCache.set("agy", resolved);
430453return resolved;
431454}
432455@@ -517,24 +540,25 @@ async function resolveLocalAudioEntry(): Promise<MediaUnderstandingModelConfig |
517540return await resolveLocalWhisperEntry();
518541}
519542520-async function resolveGeminiCliEntry(
521-_capability: MediaUnderstandingCapability,
543+async function resolveAntigravityCliEntry(
544+capability: MediaUnderstandingCapability,
522545): Promise<MediaUnderstandingModelConfig | null> {
523-if (!(await probeGeminiCli())) {
546+if (capability === "audio") {
547+return null;
548+}
549+const command = await resolveAntigravityCliBinary();
550+if (!command) {
524551return null;
525552}
526553return {
527554type: "cli",
528-command: "gemini",
555+ command,
529556args: [
530-"--output-format",
531-"json",
532-"--allowed-tools",
533-"read_many_files",
534-"--include-directories",
557+"--sandbox",
558+"--add-dir",
535559"{{MediaDir}}",
536-"{{Prompt}}",
537-"Use read_many_files to read {{MediaPath}} and respond with only the text output.",
560+"--print",
561+"{{Prompt}} Inspect {{MediaPath}} and reply with only the requested media description.",
538562],
539563};
540564}
@@ -682,14 +706,14 @@ async function resolveAutoEntries(params: {
682706return [localAudio];
683707}
684708}
685-const gemini = await resolveGeminiCliEntry(params.capability);
686-if (gemini) {
687-return [gemini];
688-}
689709const keys = await resolveKeyEntry(params);
690710if (keys) {
691711return [keys];
692712}
713+const antigravity = await resolveAntigravityCliEntry(params.capability);
714+if (antigravity) {
715+return [antigravity];
716+}
693717return [];
694718}
695719此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。