diff --git a/README.md b/README.md index 8eb0696..d86956c 100644 --- a/README.md +++ b/README.md @@ -12,28 +12,115 @@

Install · + Why Gemini 3.7 Flash? · + MCP Power Stack · Tools · Timeouts · Configuration

-**Claude Code delegates heavy tasks to the Antigravity CLI (`agy`)** — saving Claude's context window and tokens for what matters. +**Claude Code delegates heavy tasks to Google's flagship Gemini 3.7 Flash (High)** via the Antigravity CLI (`agy`) — saving Claude's context window and tokens for what matters. -Claude sends a task → the bridge routes it to the best available model via `agy` → only the answer comes back. Large files, deep git searches, and web lookups never touch Claude's context. +Claude acts as the orchestrator → `claude-agy-mcp` routes compute-heavy sub-tasks to **Gemini 3.7 Flash (High)** → only concise answers return. Large files, deep git searches, and log dumps never pollute Claude's context. ``` -User → Claude Code → claude-agy-mcp (MCP) → agy CLI → Gemini / Claude / GPT-OSS - ← ← ← +User → Claude Code → claude-agy-mcp (MCP) → agy CLI → Gemini 3.7 Flash / Pro / Claude + ← ← ← (Clean answers only) ``` +## Why Gemini 3.7 Flash (High) for Claude Code? + +
+ Gemini 3.7 Flash Benchmarks +
+ +**Gemini 3.7 Flash** is Google's most intelligent workhorse model for coding and agentic execution. It applies deep multi-step planning, rigorous terminal reasoning, and high first-pass code accuracy. + +### Benchmark Highlights + +| Benchmark / Capability | Gemini 3.7 Flash (High) | Prior Generation (3.6 Flash) | Advantage | +| ---------------------- | ----------------------- | ---------------------------- | ----------------------------------------------------------------------------------------- | +| **DeepSWE v1.1** | **65.3%** | 49.0% | **+16.3%** jump in long-horizon repository software engineering | +| **FrontierCode 1.1** | **43.6%** | 34.4% | **+9.2%** improvement in production code quality and first-pass accuracy | +| **Terminal-Bench 2.1** | **85.8%** | 78.0% | **+7.8%** higher resilience in agentic CLI execution & tool chaining | +| **WebDev Arena** | **1588 Elo** | 1538 Elo | **#1 Rank** for fullstack web application and UI design adherence | +| **AutomationBench** | **30.4%** | 17.0% | Surpasses GPT-5.6 Terra (23.6%) and Claude Sonnet 5 (10.7%) in enterprise agent workflows | +| **Context & Window** | **1,000,000 Tokens** | 1,000,000 Tokens | 64K output tokens with 97.0% retrieval on GDM-MRCR v2 (128k) | +| **Token Economics** | **$0.75 / $3.75** (1M) | $1.50 / $7.50 | Up to **10x–20x cheaper** than Claude Opus/Sonnet for background delegation | + +### The Token & Context Multiplier + +When Claude Code directly analyzes a 4,000-line database dump or greps 20 files across git history, those thousands of lines stay permanently in Claude's prompt context, inflating cost and pushing you toward compaction. + +With `claude-agy-mcp`: + +1. Claude calls `analyze_files` or `deep_search`. +2. Gemini 3.7 Flash processes the 100k+ tokens in isolation via `agy`. +3. Only the exact code-level findings and line citations return into Claude's prompt. +4. Subsequent questions reuse the same agy session with `follow_up` without re-sending any files. + +--- + +## The Ultimate AI Engineering MCP Stack + +`claude-agy-mcp` is designed to anchor a modern AI engineer's MCP toolkit alongside complementary specialized servers: + +``` +┌─────────────────────────────────────────────────────────────────────────────┐ +│ Claude Code (Agent) │ +└──────┬──────────────────────┬───────────────────────┬───────────────────────┘ + │ │ │ │ + ▼ ▼ ▼ ▼ +┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ +│claude-agy-mcp│ │ context7 │ │ firecrawl │ │ tavily │ +│ (Gemini 3.7 │ │(Official Docs│ │(Web Scraping │ │(Live Search │ +│ Delegation) │ │ & API Specs)│ │ & Crawling) │ │ & Research) │ +└──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘ +``` + +| MCP Server | Primary Superpower | When Claude Uses It | +| -------------------- | ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **`claude-agy-mcp`** | **Heavy Compute & Coding Delegation** | Analyzing files >200 lines, repo archaeology (`git log/diff/blame`), adversarial code reviews, and raw execution via Gemini 3.7 Flash. | +| **`context7`** | **Up-to-date Official Documentation** | Fetching latest version-accurate API signatures and documentation for libraries (Next.js, React, Tailwind, Prisma, Vite, etc.) to eliminate hallucinated APIs. | +| **`firecrawl`** | **Clean Web Scraping & Crawling** | Converting dynamic web pages, documentation sites, and GitHub repos into clean, LLM-ready markdown or structured JSON. | +| **`tavily`** | **Fast Live Search & Grounding** | Low-latency web search, current news, error message lookups, and technical research. | + +### Recommended MCP Configuration (`.agents/mcp_config.json` or Claude Code) + +```json +{ + "mcpServers": { + "claude-agy-mcp": { + "command": "npx", + "args": ["-y", "@pymodel/claude-agy-mcp"], + "timeout": 3600000 + }, + "context7": { + "command": "npx", + "args": ["-y", "@upstash/context7-mcp@latest"] + }, + "firecrawl": { + "command": "npx", + "args": ["-y", "firecrawl-mcp"] + }, + "tavily": { + "command": "npx", + "args": ["-y", "tavily-mcp"] + } + } +} +``` + +--- + ## Why this over claude-to-agy? | | claude-to-agy | **claude-agy-mcp** | | --------------- | --------------------------- | ------------------------------------------------------------------------------------ | | Tool surface | 1 generic `delegate_to_agy` | 6 purpose-built tools — Claude self-routes reliably | -| Model selection | none (agy default only) | per-tool routing across all `agy models`, with availability detection and fallback | +| Model selection | none (agy default only) | per-tool routing prioritizing **Gemini 3.7 Flash (High)** with quota failover | | Multi-turn | stateless | session continuity — `follow_up` resumes agy conversations without resending context | | Output safety | unbounded | configurable truncation cap protects Claude's context | | Sandbox | no | optional `--sandbox` mode | @@ -71,12 +158,12 @@ curl -o CLAUDE.md https://raw.githubusercontent.com/PyModel/claude-agy-mcp/main/ | Tool | Use for | Model routing (first available) | | -------------------- | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | -| `analyze_files` | Files >200 lines, >3 files at once, logs, dumps, generated code | Gemini 3.6 Flash (High) → Gemini 3.5 Flash (High) → Gemini 3.1 Pro (Low) | -| `deep_search` | git log/diff/blame archaeology, repo-wide greps | Gemini 3.6 Flash (Medium) → Gemini 3.6 Flash (High) → Gemini 3.5 Flash (High) | -| `web_lookup` | Docs, API references, external/current knowledge | Gemini 3.6 Flash (Medium) → Gemini 3.6 Flash (High) → Gemini 3.5 Flash (High) | -| `adversarial_review` | Plan critiques, design and code reviews | Gemini 3.1 Pro (High) → Claude Opus 4.6 (Thinking) → Gemini 3.6 Flash (High) → Gemini 3.5 Flash (High) | +| `analyze_files` | Files >200 lines, >3 files at once, logs, dumps, generated code | Gemini 3.7 Flash (High) → Gemini 3.5 Flash (High) → Gemini 3.1 Pro (Low) | +| `deep_search` | git log/diff/blame archaeology, repo-wide greps | Gemini 3.7 Flash (Medium) → Gemini 3.7 Flash (High) → Gemini 3.5 Flash (High) | +| `web_lookup` | Docs, API references, external/current knowledge | Gemini 3.7 Flash (Medium) → Gemini 3.7 Flash (High) → Gemini 3.5 Flash (High) | +| `adversarial_review` | Plan critiques, design and code reviews | Gemini 3.1 Pro (High) → Claude Opus 4.6 (Thinking) → Gemini 3.7 Flash (High) → Gemini 3.5 Flash (High) | | `follow_up` | Continue a prior session by `session_id` — no context resend | inherits the session | -| `delegate` | Anything else heavy | Gemini 3.6 Flash (High) → Gemini 3.5 Flash (High) | +| `delegate` | Anything else heavy | Gemini 3.7 Flash (High) → Gemini 3.5 Flash (High) | All tools accept optional `cwd` (project root) and `model` (exact name from `agy models`; validated, with available models listed on mismatch). @@ -84,12 +171,12 @@ Every response ends with a footer: ``` --- -[claude-agy-mcp] model: Gemini 3.6 Flash (High) | session: 1f0c…-d4 (use follow_up to continue) +[claude-agy-mcp] model: Gemini 3.7 Flash (High) | session: 1f0c…-d4 (use follow_up to continue) ``` ### Model routing -On first use the bridge runs `agy models` (cached for the process lifetime) and picks the first available model in the tool's preference chain. If none is available it falls back to `AGY_DEFAULT_MODEL`, and finally to agy's own default. agy silently ignores unknown `--model` values, so the bridge validates names up front instead of letting requests land on the wrong model. +On first use the bridge runs `agy models` (cached for the process lifetime) and picks the first available model in the tool's preference chain (defaulting to **Gemini 3.7 Flash (High)**). If none is available it falls back to `AGY_DEFAULT_MODEL`, and finally to agy's own default. agy silently ignores unknown `--model` values, so the bridge validates names up front instead of letting requests land on the wrong model. ### Quota-aware failover @@ -127,7 +214,7 @@ All optional, via environment variables: | `AGY_TIMEOUT` | `AGY_MAX_RUNTIME` | Seconds; overrides the ceiling for every tool, passed as `--print-timeout`, enforced with a 15s kill grace | | `AGY_TIMEOUT_` | `AGY_MAX_RUNTIME` | Seconds; overrides the ceiling for a single tool, e.g. `AGY_TIMEOUT_DEEP_SEARCH=900`. Wins over `AGY_TIMEOUT` | | `AGY_MAX_OUTPUT_CHARS` | `50000` | Truncation cap for tool output | -| `AGY_DEFAULT_MODEL` | Gemini 3.6 Flash (High) | Fallback model when no chain entry is available | +| `AGY_DEFAULT_MODEL` | Gemini 3.7 Flash (High) | Fallback model when no chain entry is available | | `AGY_SKIP_PERMISSIONS` | `true` | Pass `--dangerously-skip-permissions` to agy | | `AGY_SANDBOX` | `false` | Run agy with `--sandbox` | | `AGY_ON_FAILURE` | `fallback` | `strict` appends an instruction to failed-tool errors telling the calling agent not to absorb the work itself | diff --git a/assets/banner.svg b/assets/banner.svg index 618d4f7..b3b22cb 100644 --- a/assets/banner.svg +++ b/assets/banner.svg @@ -1,48 +1,175 @@ - + - - - + + + + + - - + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - claude-agy-mcp + + claude-agy-mcp + + + + v2.0.0 + + + + + Default: Gemini 3.7 Flash (High) + + + Claude Code delegates heavy coding tasks to Google's Antigravity CLI + Context-free file analysis, git archaeology, and adversarial code reviews with quota-aware failover. - - - Claude Code delegates the heavy work to the Antigravity CLI. - 6 tools · quota-aware failover · session continuity - - - - - - - - delegation session - - - - analyze_files14 files · 4,200 lines - ↳ routed → Gemini 3.6 Flash (High) - ↳ 46s · 0 lines entered Claude's context - [claude-agy-mcp] model: Gemini 3.6 Flash (High) · session: 1f0c…d4 + + + + + + + + + + + + + + + + + + claude-code — delegation session [agy bridge] + + + + + + failover ready · quota ok + + + + + + + + analyze_files + [ + "src/server.ts" + , + "src/runner.ts" + , + "src/models.ts" + ] + · 14 files, 4,200 lines (bypasses Claude context) + + + + + + routed to + + Gemini 3.7 Flash (High) + via agy CLI + ✓ 65.3% DeepSWE + · 42s cold start + + + + + + completed + Identified 3 edge cases, ranked by severity with file:line citations + + + + + + + + [ + claude-agy-mcp + ] + model: + Gemini 3.7 Flash (High) + | + session: + 1f0c4a…9d + (use follow_up to continue with 0 context overhead) + + diff --git a/assets/benchmarks.svg b/assets/benchmarks.svg new file mode 100644 index 0000000..e1eeea8 --- /dev/null +++ b/assets/benchmarks.svg @@ -0,0 +1,137 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + GOOGLE DEEPMIND · GEMINI 3.7 FLASH + + Google's Flagship Coding & Reasoning Workhorse + Engineered for long-horizon repository execution, multi-step terminal planning, and context-free Claude delegation. + + + + + + + + + + DeepSWE v1.1 + + 65.3% + + + + + + ▲ +16.3% vs 3.6 Flash (49.0%) + Repository-level issue resolution + + + + + + + Terminal-Bench 2.1 + + 85.8% + + + + + + ▲ +7.8% vs 3.6 Flash (78.0%) + Agentic CLI & command execution + + + + + + + WebDev Arena + + 1588 Elo + + + + + + #1 Leaderboard Rank (vs 1538) + Fullstack app & UI synthesis + + + + + + + AutomationBench + + 30.4% + + + + + + ▲ Ahead of Sonnet 5 (10.7%) + Multi-step enterprise workflows + + + + diff --git a/package-lock.json b/package-lock.json index 764e405..f55bc4c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@pymodel/claude-agy-mcp", - "version": "1.0.3", + "version": "2.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@pymodel/claude-agy-mcp", - "version": "1.0.3", + "version": "2.0.0", "license": "MIT", "dependencies": { "@modelcontextprotocol/sdk": "^1.12.0", diff --git a/package.json b/package.json index ba82a83..824d44c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@pymodel/claude-agy-mcp", - "version": "1.0.3", + "version": "2.0.0", "description": "MCP bridge that lets Claude Code delegate heavy tasks to the Antigravity CLI (agy) — purpose-built tools, model routing with fallback, and multi-turn session continuity.", "mcpName": "io.github.PyModel/claude-agy-mcp", "type": "module", diff --git a/server.json b/server.json index a9be23c..e75c144 100644 --- a/server.json +++ b/server.json @@ -6,12 +6,12 @@ "url": "https://github.com/PyModel/claude-agy-mcp", "source": "github" }, - "version": "1.0.3", + "version": "2.0.0", "packages": [ { "registryType": "npm", "identifier": "@pymodel/claude-agy-mcp", - "version": "1.0.3", + "version": "2.0.0", "transport": { "type": "stdio" }, @@ -45,7 +45,7 @@ "name": "AGY_MAX_OUTPUT_CHARS" }, { - "description": "Fallback model when no chain entry is available (defaults to Gemini 3.6 Flash (High))", + "description": "Fallback model when no chain entry is available (defaults to Gemini 3.7 Flash (High))", "isRequired": false, "format": "string", "isSecret": false, diff --git a/src/config.ts b/src/config.ts index 2958e5f..9149153 100644 --- a/src/config.ts +++ b/src/config.ts @@ -42,7 +42,7 @@ export function loadConfig(env: Record = process.env perToolTimeouts: loadPerToolTimeouts(env), maxRuntimeSec: positiveInt(env.AGY_MAX_RUNTIME, 3600), maxOutputChars: positiveInt(env.AGY_MAX_OUTPUT_CHARS, 50_000), - defaultModel: env.AGY_DEFAULT_MODEL || "Gemini 3.6 Flash (High)", + defaultModel: env.AGY_DEFAULT_MODEL || "Gemini 3.7 Flash (High)", skipPermissions: env.AGY_SKIP_PERMISSIONS !== "false", sandbox: env.AGY_SANDBOX === "true", onFailure: env.AGY_ON_FAILURE === "strict" ? "strict" : "fallback", diff --git a/src/server.ts b/src/server.ts index 67cce23..0761a44 100644 --- a/src/server.ts +++ b/src/server.ts @@ -12,7 +12,7 @@ import { CooldownRegistry, QuotaError } from "./quota.js"; import { TOOLS, type ToolDef } from "./tools.js"; /** Keep in sync with package.json — test/server.test.ts fails if they drift. */ -export const VERSION = "1.0.3"; +export const VERSION = "2.0.0"; interface ToolResponse { [key: string]: unknown; diff --git a/src/tools.ts b/src/tools.ts index b1ba59e..2d337a0 100644 --- a/src/tools.ts +++ b/src/tools.ts @@ -49,7 +49,7 @@ export const TOOLS: ToolDef[] = [ question: z.string().describe("What you want to know about these files."), ...commonShape, }, - chain: ["Gemini 3.6 Flash (High)", "Gemini 3.5 Flash (High)", "Gemini 3.1 Pro (Low)"], + chain: ["Gemini 3.7 Flash (High)", "Gemini 3.5 Flash (High)", "Gemini 3.1 Pro (Low)"], buildPrompt(args, cwd) { const files = resolveFiles(args.files as string[], cwd); return ( @@ -70,7 +70,7 @@ export const TOOLS: ToolDef[] = [ .describe("What to find, e.g. 'when was the auth middleware refactored and why'."), ...commonShape, }, - chain: ["Gemini 3.6 Flash (Medium)", "Gemini 3.6 Flash (High)", "Gemini 3.5 Flash (High)"], + chain: ["Gemini 3.7 Flash (Medium)", "Gemini 3.7 Flash (High)", "Gemini 3.5 Flash (High)"], buildPrompt(args) { return ( `Search this repository to answer the following. Use git log, git diff, git blame, ` + @@ -89,7 +89,7 @@ export const TOOLS: ToolDef[] = [ query: z.string().describe("What to look up on the web."), ...commonShape, }, - chain: ["Gemini 3.6 Flash (Medium)", "Gemini 3.6 Flash (High)", "Gemini 3.5 Flash (High)"], + chain: ["Gemini 3.7 Flash (Medium)", "Gemini 3.7 Flash (High)", "Gemini 3.5 Flash (High)"], buildPrompt(args) { return `Look up on the web: ${args.query}\n\nInclude source URLs for key claims. ${OUTPUT_RULES}`; }, @@ -115,7 +115,7 @@ export const TOOLS: ToolDef[] = [ chain: [ "Gemini 3.1 Pro (High)", "Claude Opus 4.6 (Thinking)", - "Gemini 3.6 Flash (High)", + "Gemini 3.7 Flash (High)", "Gemini 3.5 Flash (High)", ], buildPrompt(args, cwd) { @@ -163,7 +163,7 @@ export const TOOLS: ToolDef[] = [ prompt: z.string().describe("The complete task prompt for agy."), ...commonShape, }, - chain: ["Gemini 3.6 Flash (High)", "Gemini 3.5 Flash (High)"], + chain: ["Gemini 3.7 Flash (High)", "Gemini 3.5 Flash (High)"], buildPrompt(args) { return args.prompt as string; }, diff --git a/test/config.test.ts b/test/config.test.ts index 6f696b4..d249fb8 100644 --- a/test/config.test.ts +++ b/test/config.test.ts @@ -11,7 +11,7 @@ describe("loadConfig", () => { perToolTimeouts: {}, maxRuntimeSec: 3600, maxOutputChars: 50_000, - defaultModel: "Gemini 3.6 Flash (High)", + defaultModel: "Gemini 3.7 Flash (High)", skipPermissions: true, sandbox: false, onFailure: "fallback", diff --git a/test/models.test.ts b/test/models.test.ts index 38e6088..e780c6f 100644 --- a/test/models.test.ts +++ b/test/models.test.ts @@ -2,7 +2,7 @@ import { describe, it, expect } from "vitest"; import { parseModels, ModelRegistry } from "../src/models.js"; const LISTING = `Gemini 3.5 Flash (Medium) -Gemini 3.6 Flash (High) +Gemini 3.7 Flash (High) Gemini 3.5 Flash (High) Gemini 3.1 Pro (High) `; @@ -11,7 +11,7 @@ describe("parseModels", () => { it("returns one trimmed model per non-empty line", () => { expect(parseModels(LISTING)).toEqual([ "Gemini 3.5 Flash (Medium)", - "Gemini 3.6 Flash (High)", + "Gemini 3.7 Flash (High)", "Gemini 3.5 Flash (High)", "Gemini 3.1 Pro (High)", ]); @@ -19,11 +19,11 @@ describe("parseModels", () => { it("returns both the id and the display name from tab-separated listings", () => { const raw = - "gemini-3.6-flash-high\tGemini 3.6 Flash (High)\n" + + "gemini-3.7-flash-high\tGemini 3.7 Flash (High)\n" + "gemini-3.1-pro-low\tGemini 3.1 Pro (Low)\n"; expect(parseModels(raw)).toEqual([ - "gemini-3.6-flash-high", - "Gemini 3.6 Flash (High)", + "gemini-3.7-flash-high", + "Gemini 3.7 Flash (High)", "gemini-3.1-pro-low", "Gemini 3.1 Pro (Low)", ]); @@ -56,9 +56,9 @@ describe("ModelRegistry.resolve", () => { it("picks first available model in chain", async () => { const r = await registry(LISTING).resolve({ - chain: ["Gemini 9.9 Ultra", "Gemini 3.6 Flash (High)"], + chain: ["Gemini 9.9 Ultra", "Gemini 3.7 Flash (High)"], }); - expect(r.model).toBe("Gemini 3.6 Flash (High)"); + expect(r.model).toBe("Gemini 3.7 Flash (High)"); }); it("falls back to defaultModel when chain misses", async () => { @@ -78,7 +78,7 @@ describe("ModelRegistry.resolve", () => { const reg = registry(new Error("boom")); const a = await reg.resolve({ explicit: "Whatever", chain: [] }); expect(a.model).toBe("Whatever"); - const b = await reg.resolve({ chain: ["Gemini 3.6 Flash (High)"] }); + const b = await reg.resolve({ chain: ["Gemini 3.7 Flash (High)"] }); expect(b.model).toBeUndefined(); expect(b.note).toMatch(/could not list/i); }); @@ -120,11 +120,11 @@ describe("ModelRegistry.resolve", () => { return LISTING; }); const [a, b] = await Promise.all([ - reg.resolveChain({ chain: ["Gemini 3.6 Flash (High)"] }), + reg.resolveChain({ chain: ["Gemini 3.7 Flash (High)"] }), reg.resolveChain({ chain: ["Gemini 3.1 Pro (High)"] }), ]); expect(calls).toBe(1); - expect(a.models).toEqual(["Gemini 3.6 Flash (High)"]); + expect(a.models).toEqual(["Gemini 3.7 Flash (High)"]); expect(b.models).toEqual(["Gemini 3.1 Pro (High)"]); }); @@ -146,7 +146,7 @@ describe("ModelRegistry.resolve", () => { calls++; return LISTING; }); - await reg.resolve({ chain: ["Gemini 3.6 Flash (High)"] }); + await reg.resolve({ chain: ["Gemini 3.7 Flash (High)"] }); await reg.resolve({ chain: ["Gemini 3.1 Pro (High)"] }); expect(calls).toBe(1); }); diff --git a/test/server.test.ts b/test/server.test.ts index 44b3064..c5f2ff7 100644 --- a/test/server.test.ts +++ b/test/server.test.ts @@ -21,8 +21,8 @@ const cfg: Config = { }; const LISTING = - "Gemini 3.6 Flash (Medium)\n" + - "Gemini 3.6 Flash (High)\n" + + "Gemini 3.7 Flash (Medium)\n" + + "Gemini 3.7 Flash (High)\n" + "Gemini 3.5 Flash (High)\n" + "Gemini 3.1 Pro (High)\n"; @@ -90,13 +90,13 @@ describe("createToolHandler", () => { const res = await handlerFor("delegate", f)({ prompt: "do x" }); const text = (res.content[0] as { text: string }).text; expect(text).toContain("the answer"); - expect(text).toContain("Gemini 3.6 Flash (High)"); + expect(text).toContain("Gemini 3.7 Flash (High)"); expect(text).toContain("sess-1"); expect(f.runs[0].args).toContain("--model"); }); it("returns timeout details and does not fail over after a timed-out run", async () => { - const f = fakeDeps([], ["Gemini 3.6 Flash (High)"]); + const f = fakeDeps([], ["Gemini 3.7 Flash (High)"]); const res = await handlerFor("delegate", f, { timeoutSec: 0.05, timeoutExplicit: true, @@ -158,41 +158,41 @@ describe("createToolHandler", () => { }); it("fails over to the next chain model on quota exhaustion", async () => { - const f = fakeDeps(["Gemini 3.6 Flash (Medium)"]); + const f = fakeDeps(["Gemini 3.7 Flash (Medium)"]); const res = await handlerFor("web_lookup", f)({ query: "docs" }); const text = (res.content[0] as { text: string }).text; expect(res.isError).toBeUndefined(); expect(f.runs).toHaveLength(2); - expect(f.modelOf(f.runs[0])).toBe("Gemini 3.6 Flash (Medium)"); - expect(f.modelOf(f.runs[1])).toBe("Gemini 3.6 Flash (High)"); + expect(f.modelOf(f.runs[0])).toBe("Gemini 3.7 Flash (Medium)"); + expect(f.modelOf(f.runs[1])).toBe("Gemini 3.7 Flash (High)"); expect(text).toContain("the answer"); - expect(text).toContain("model: Gemini 3.6 Flash (High)"); - expect(text).toMatch(/failover.*Gemini 3.6 Flash \(Medium\).*quota/i); + expect(text).toContain("model: Gemini 3.7 Flash (High)"); + expect(text).toMatch(/failover.*Gemini 3.7 Flash \(Medium\).*quota/i); }); it("skips cooled-down models on subsequent calls without spawning them", async () => { - const f = fakeDeps(["Gemini 3.6 Flash (Medium)"]); + const f = fakeDeps(["Gemini 3.7 Flash (Medium)"]); const cooldowns = new CooldownRegistry(); const handler = handlerFor("web_lookup", f, {}, cooldowns); await handler({ query: "first" }); expect(f.runs).toHaveLength(2); await handler({ query: "second" }); expect(f.runs).toHaveLength(3); - expect(f.modelOf(f.runs[2])).toBe("Gemini 3.6 Flash (High)"); + expect(f.modelOf(f.runs[2])).toBe("Gemini 3.7 Flash (High)"); }); it("errors with reset times when every chain model is quota-exhausted", async () => { const f = fakeDeps([ - "Gemini 3.6 Flash (Medium)", - "Gemini 3.6 Flash (High)", + "Gemini 3.7 Flash (Medium)", + "Gemini 3.7 Flash (High)", "Gemini 3.5 Flash (High)", ]); const res = await handlerFor("web_lookup", f)({ query: "docs" }); expect(res.isError).toBe(true); const text = (res.content[0] as { text: string }).text; expect(text).toMatch(/quota/i); - expect(text).toContain("Gemini 3.6 Flash (Medium)"); - expect(text).toContain("Gemini 3.6 Flash (High)"); + expect(text).toContain("Gemini 3.7 Flash (Medium)"); + expect(text).toContain("Gemini 3.7 Flash (High)"); expect(text).toContain("Gemini 3.5 Flash (High)"); expect(text).toContain("4h24m"); });