Antigravity-Manager Documentation

Professional Antigravity Account Manager & Switcher. One-click seamless account switching for Antigravity Tools. Built with Tauri v2 + React (Rust).专业的...

32,367 stars Rust Rust 1 file · ~118,090 tokens #account-manager#antigravity
RAW Doc

Docker & Self-Hosted Deployment Verified OCI

Deploy Antigravity-Manager on any server, homelab, or cloud container platform.

1-Click Terminal Command
bash (docker run)
docker run -d --name antigravity-manager --restart always lbjlaq/antigravity-manager:latest
Configured Services & Ports
⚙️ antigravity-manager
image: build locally
docker-compose.yml
yaml (docker-compose.yml)
services:
  antigravity-manager:
    build:
      dockerfile: docker/Dockerfile.backend
      args:
        FRONTEND_IMAGE: antigravity-manager:latest
    logging:
      driver: json-file
      options:
        max-size: "100m"
        max-file: "3"

Technical Documentation: lbjlaq/Antigravity-Manager

ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference

Overview (`README.md`)

Antigravity Tools 🚀

Professional Account Management & Protocol Proxy System for AI Services (v4.9.8)


Antigravity Tools is an all-in-one desktop application designed for developers and AI enthusiasts. It perfectly combines multi-account management, protocol conversion, and smart request scheduling to provide you with a stable, high-speed, and low-cost Local AI Relay Station.

By leveraging this app, you can transform common Web Sessions (Google/Anthropic) into standardized API interfaces, completely eliminating the protocol gap between different providers.

💖 Sponsors

Sponsor Description
Thanks to PackyCode for sponsoring this project! PackyCode is a reliable and efficient API relay service provider, offering relays for various services such as Claude Code, Codex, and Gemini. PackyCode provides a special offer for users of this project: Register using this link and enter the "Ctrler" coupon code when topping up to enjoy a 10% discount.
Thanks to APIKEY.FUN for sponsoring this project! APIKEY.FUN is a professional enterprise-grade AI relay station, dedicated to providing stable, efficient, and low-cost AI model API access services for enterprise and individual developers. The platform supports mainstream popular models such as Claude, OpenAI, and Gemini, with prices as low as 7% of the official original price. Register through this exclusive link for this project to enjoy an exclusive offer of up to permanent 5% off on top-ups.
Thanks to Claude API for supporting this project! claudeapi.com is a Claude API relay station built on official and AWS channels, focused exclusively on Claude, delivering high stability and low latency with full support for Claude Code. Exclusive offer: register via this exclusive link to get free trial credits — zero setup, get started instantly; enjoy an extra 5% off when you top up (Contact Support).
Thanks to AICodeMirror for sponsoring this project! AICodeMirror provides official high-stability relay services for Claude Code / Codex / Gemini CLI, supporting enterprise-grade concurrency, fast invoicing, and 24/7 dedicated technical support. Claude Code / Codex / Gemini official channels at 38% / 2% / 9% of original price, with extra discounts on top-ups! AICodeMirror offers special benefits for Antigravity-Manager users: register via this link to enjoy 20% off your first top-up, and enterprise customers can get up to 25% off!

☕ Support

If you find this project helpful, feel free to buy me a coffee!

Alipay WeChat Pay Buy Me a Coffee

If you like this project, you might also be interested in:

  • Antigravity-Tools-LS: A Language Server Protocol (LSP) designed for AI protocols, providing you with smarter code completion, diagnostics, and protocol debugging experiences.

🌟 Detailed Feature Matrix

1. 🎛️ Smart Account Dashboard

  • Global Real-time Monitoring: Instant insight into the health of all accounts, including average remaining quotas for Gemini Pro, Gemini Flash, Claude, and Gemini Image generation.
  • Smart Recommendation: The system uses a real-time algorithm to filter and recommend the "Best Account" based on quota redundancy, supporting one-click switching.
  • Active Account Snapshot: Visually displays the specific quota percentage and the last synchronization time of the currently active account.

2. 🔐 Powerful Account Management

  • OAuth 2.0 Authorization (Auto/Manual): Pre-generates a copyable authorization URL so you can finish auth in any browser; after the callback, the app auto-completes and saves the account (use “I already authorized, continue” if needed).
  • Multi-dimensional Import: Supports single token entry, JSON batch import, and automatic hot migration from V1 legacy databases.
  • Gateway-level Views: Supports switching between "List" and "Grid" views. Provides 403 Forbidden detection, automatically marking and skipping accounts with permission anomalies.

3. 🔌 Protocol Conversion & Relay (API Proxy)

  • Multi-Protocol Adaptation (Multi-Sink):
    • OpenAI Format: Provides /v1/chat/completions endpoint, compatible with 99% of existing AI apps.
    • Anthropic Format: Provides native /v1/messages interface, supporting all features of Claude Code CLI (e.g., chain-of-thought, system prompts).
    • Gemini Format: Supports direct calls from official Google AI SDKs.
  • Smart Self-healing: When a request encounters 429 (Too Many Requests) or 401 (Expired), the backend triggers millisecond-level automatic retry and silent rotation, ensuring business continuity.

4. 🔀 Model Router Center

  • Series-based Mapping: Classify complex original model IDs into "Series Groups" (e.g., routing all GPT-4 requests uniformly to gemini-3-pro-high).
  • Expert Redirection: Supports custom regex-level model mapping for precise control over every request's landing model.
  • Tiered Routing [New]: Automatically prioritizes models based on account tiers (Ultra/Pro/Free) and reset frequencies to ensure stability for high-volume users.
  • Silent Background Downgrading [New]: Intelligently identifies background tasks (e.g., Claude CLI title generation) and reroutes them to Flash models to preserve premium quota.

5. 🎨 Multimodal & Imagen 3 Support

  • Advanced Image Control: Supports precise control over image generation tasks via OpenAI size (e.g., 1024x1024, 16:9) parameters or model name suffixes.
  • Enhanced Payload Support: The backend supports payloads up to 100MB (configurable), more than enough for 4K HD image recognition and processing.

📸 GUI Overview

Dashboard Account List
About Page API Proxy
Settings

💡 Usage Examples

Claude Code Web Search Cherry Studio Integration
Imagen 3 Advanced Drawing Kilo Code Integration

🏗️ Architecture

mermaid
graph TD
    Client([External Apps: Claude Code/NextChat]) -->|OpenAI/Anthropic| Gateway[Antigravity Axum Server]
    Gateway --> Middleware[Middleware: Auth/Rate Limit/Logs]
    Middleware --> Router[Model Router: ID Mapping]
    Router --> Dispatcher[Dispatcher: Rotation/Weights]
    Dispatcher --> Mapper[Request Mapper]
    Mapper --> Upstream[Upstream: Google/Anthropic API]
    Upstream --> ResponseMapper[Response Mapper]
    ResponseMapper --> Client

📥 Installation

Cross-Platform One-Line Install Scripts

Automatically detects your OS, architecture, and package manager — one command to download and install.

Linux / macOS:

bash
curl -fsSL https://raw.githubusercontent.com/lbjlaq/Antigravity-Manager/main/install.sh | bash

Windows (PowerShell):

powershell
irm https://raw.githubusercontent.com/lbjlaq/Antigravity-Manager/main/install.ps1 | iex

Supported formats: Linux (.deb / .rpm / .AppImage) | macOS (.dmg) | Windows (NSIS .exe)

Advanced usage: Install a specific version curl -fsSL https://raw.githubusercontent.com/lbjlaq/Antigravity-Manager/main/install.sh | bash -s -- --version 4.6.8, dry-run mode curl -fsSL https://raw.githubusercontent.com/lbjlaq/Antigravity-Manager/main/install.sh | bash -s -- --dry-run

macOS - Homebrew

If you have Homebrew installed, you can also install via:

bash
# 1. Tap the repository
brew tap lbjlaq/antigravity-manager https://github.com/lbjlaq/Antigravity-Manager

# 2. Install the app
brew install --cask antigravity-tools

Arch Linux

You can choose to install via the one-click script or Homebrew:

Option 1: One-click script (Recommended)

bash
curl -sSL https://raw.githubusercontent.com/lbjlaq/Antigravity-Manager/main/deploy/arch/install.sh | bash

Option 2: via Homebrew (If you have Linuxbrew installed)

bash
brew tap lbjlaq/antigravity-manager https://github.com/lbjlaq/Antigravity-Manager
brew install --cask antigravity-tools

Other Linux Distributions

The AppImage will be automatically symlinked to your binary path with executable permissions.

Option B: Manual Download

Download from GitHub Releases:

  • macOS: .dmg (Universal, Apple Silicon & Intel)
  • Windows: .msi or portable .zip
  • Linux: .deb or AppImage

If you prefer running in a containerized environment, we provide a native Docker image. This image supports the v4.0.3 Native Headless architecture, automatically hosts frontend static resources, and allows for direct browser-based management.

  • API_KEY: Required. Used for AI request authentication.
  • WEB_PASSWORD: Optional. Used for Web UI login. Defaults to API_KEY if NOT set.
bash
docker run -d --name antigravity-manager \
  -p 8045:8045 \
  -e API_KEY=sk-your-api-key \
  -e WEB_PASSWORD=your-login-password \
  -e ABV_MAX_BODY_SIZE=104857600 \
  -v ~/.antigravity_tools:/root/.antigravity_tools \
  lbjlaq/antigravity-manager:latest

# Forgot keys? Run `docker logs antigravity-manager` or `grep -E '"api_key"|"admin_password"' ~/.antigravity_tools/gui_config.json`

[!TIP]
🧪 Pulling Beta / Preview Images:
To test the latest Beta pre-release features, specify the corresponding Beta version tag (pre-releases are published independently and will never overwrite the latest stable tag):

bash
# Pull a specific Beta pre-release version (check Docker Hub for all tags)
docker pull lbjlaq/antigravity-manager:v4.8.2-beta.0

# Run Beta container
docker run -d --name antigravity-manager-beta \
  -p 8045:8045 \
  -e API_KEY=sk-your-api-key \
  -e WEB_PASSWORD=your-login-password \
  -e ABV_MAX_BODY_SIZE=104857600 \
  -v ~/.antigravity_tools:/root/.antigravity_tools \
  lbjlaq/antigravity-manager:v4.8.2-beta.0

View all published Beta tags on Docker Hub Tags. If you wish to run the unreleased bleeding-edge beta branch directly, build locally: docker build -t lbjlaq/antigravity-manager:beta -f docker/Dockerfile ..

🔐 Authentication Scenarios

  • Scenario A: Only API_KEY is set
    • Web Login: Use API_KEY to access the dashboard.
    • API Calls: Use API_KEY for AI request authentication.
  • Scenario B: Both API_KEY and WEB_PASSWORD are set (Recommended)
    • Web Login: Must use WEB_PASSWORD. Using API Key will be rejected (more secure).
    • API Calls: Continue to use API_KEY. This allows you to share the API Key with team members while keeping the password for administrative access only.

🆙 Upgrade Guide for Older Versions

If you are upgrading from v4.0.1 or earlier, your installation won't have a WEB_PASSWORD set by default. You can add one using any of these methods:

  1. Web UI (Recommended): Log in using your existing API_KEY, go to the API Proxy Settings page, find the Web UI Management Password section below the API Key, set your new password, and save.
  2. Environment Variable (Docker): Stop the old container and start the new one with the added parameter -e WEB_PASSWORD=your_new_password. Note: Environment variables have the highest priority and will override any changes in the UI.
  3. Config File (Persistent): Directly edit ~/.antigravity_tools/gui_config.json and add/modify "admin_password": "your_new_password" inside the proxy object.
    • Note: WEB_PASSWORD is the environment variable name, while admin_password is the JSON key in the config file.

[!TIP]
Priority Logic:

  • Environment Variable (WEB_PASSWORD) has the highest priority. If set, the application will always use it and ignore values in the configuration file.
  • Configuration File (gui_config.json) is used for persistent storage. When you change the password via Web UI and save, it is written here.
  • Fallback: If neither is set, it falls back to API_KEY; if even API_KEY is missing, a random one is generated.

Option 2: Use Docker Compose

  1. Enter the docker directory and start the service:
bash
cd docker
docker compose up -d

Log rotation: Compose limits JSON logs to 100m per file and keeps 3 files by default to prevent unbounded growth.
Access URL: http://localhost:8045 (Admin Console) | http://localhost:8045/v1 (API Base)
System Requirements:

  • RAM: 1GB recommended (minimum 256MB).
  • Persistence: Mount /root/.antigravity_tools to persist your data.
  • Architecture: Supports x86_64 and ARM64.
    See: Docker Deployment Guide (docker)

🛠️ Troubleshooting - Click to expand

macOS says "App is damaged"?

Due to macOS security gatekeeper, non-App Store apps might show this. You can fix it with:

  1. Terminal Command (Recommended):
    bash
    sudo xattr -rd com.apple.quarantine "/Applications/Antigravity Tools.app"
  2. Homebrew Installation Advantage:
    When installing via Homebrew (brew install --cask antigravity-tools), quarantine attributes are automatically cleared upon installation for an out-of-the-box experience.

Linux window is black or empty?

On niri, Hyprland, Sway, and similar compositors, older builds forced GDK_BACKEND=x11 whenever DISPLAY was set, and WebKit then drew a black window. Update to a build that includes this fix, or launch once with:

bash
env WEBKIT_DISABLE_DMABUF_RENDERER=1 ANTIGRAVITY_FORCE_WAYLAND=1 antigravity-tools
  • ANTIGRAVITY_FORCE_WAYLAND=1: keep native Wayland (do not force X11)
  • ANTIGRAVITY_FORCE_X11=1: force X11 if you still need it
  • WEBKIT_DISABLE_DMABUF_RENDERER=1: disable the WebKit DMA-BUF renderer

🔌 Quick Integration Examples

🔐 OAuth Authorization Flow (Add Account)

  1. Go to Accounts → Add Account → OAuth.
  2. The dialog pre-generates an authorization URL before you click any button. Click the URL to copy it to the system clipboard, then open it in the browser you prefer and complete authorization.
  3. After consent, the browser opens a local callback page and shows “✅ Authorized successfully!”.
  4. The app automatically continues the flow and saves the account; if it doesn’t, click “I already authorized, continue” to finish manually.

Note: the auth URL contains a one-time local callback port. Always use the latest URL shown in the dialog. If the app isn’t running or the dialog is closed during auth, the browser may show localhost refused connection.

JeikCode is a modern terminal AI Coding Agent crafted by the core maintainer of this project, featuring native deep integration with Antigravity-Manager, delivering 95%+ KV-Cache hit rate and seamless reasoning level control.

  1. One-Click Sync via GUI (Recommended):
    • Launch Antigravity-Manager and ensure the API Proxy service is running.
    • Switch to the >_ Agent Tools One-Click Configuration tab.
    • Locate the JeikCode card, choose your desired default model (e.g. gemini-3.8-flash-high), and click 🔄 Sync Now.
    • Simply run jeikcode in your terminal for the TUI, and type /webui to instantly launch the modern WebUI in your browser for a richer visual coding experience out of the box!
  2. Temporary Environment Variables:
bash
export ANTHROPIC_BASE_URL="http://127.0.0.1:8045"
export ANTHROPIC_API_KEY="sk-antigravity"
jeikcode --model claude-sonnet-4-6

For complete manual configuration and tuning, see the JeikCode Integration Guide.

How to use with Claude Code CLI?

Recommended Client Versions:

  • Stable Production Recommendation: Claude Code v2.1.28x (more permissive stream termination verification, highest resilience under network jitter);
  • Latest Versions Support: Claude Code v2.1.293 ~ v2.1.295 (requires deploying the gateway version with stream hang prevention and keepalive optimizations, see Diagnosis Guide).

Install recommended stable version: npm install @anthropic-ai/[email protected]

  1. Start Antigravity service in the "API Proxy" tab.
  2. In your terminal:
bash
export ANTHROPIC_API_KEY="sk-antigravity"
export ANTHROPIC_BASE_URL="http://127.0.0.1:8045"
claude

How to use with OpenCode?

  1. Go to API Proxy → External Providers → click the OpenCode Sync card.
  2. Click Sync to generate ~/.config/opencode/opencode.json:
    • Creates a dedicated provider antigravity-manager (does not overwrite google/anthropic providers)
    • Optional: Check Sync accounts to export antigravity-accounts.json (plugin-compatible v3 format) for the OpenCode plugin
  3. Click Clear Config to remove Manager configuration and clean up legacy entries; click Restore to revert from backup.
  4. On Windows, the path is C:\Users\\.config\opencode\ (same ~/.config/opencode rule).

Quick verification commands:

bash
# Test antigravity-manager provider (supports --variant)
opencode run "test" --model antigravity-manager/claude-sonnet-4-5-thinking --variant high

# If opencode-antigravity-auth is installed, verify google provider still works independently
opencode run "test" --model google/antigravity-claude-sonnet-4-5-thinking --variant max

How to use in Python?

python
import openai

client = openai.OpenAI(
    api_key="sk-antigravity",
    base_url="http://127.0.0.1:8045/v1"
)

response = client.chat.completions.create(
    model="gemini-3-flash",
    messages=[{"role": "user", "content": "Hello, please introduce yourself"}]
)
print(response.choices[0].message.content)

How to use with Kilo Code?

  1. Protocol Selection: We recommend using the Gemini protocol.
  2. Base URL: Set it to http://127.0.0.1:8045.
  3. Note:
    • OpenAI Protocol Limitation: When using OpenAI mode, Kilo Code's request path will append /v1/chat/completions/responses, a non-standard path that will return 404 from Antigravity. Make sure to enter the Base URL and select Gemini mode.
    • Model Mapping: Model names in Kilo Code may differ from Antigravity's defaults. If you encounter connection issues, set up custom mappings on the "Model Mapping" page and check the log files for debugging.

How to use Image Generation (Imagen 3)?

python
import openai

client = openai.OpenAI(
    api_key="sk-antigravity",
    base_url="http://127.0.0.1:8045/v1"
)

# Generate image
response = client.images.generate(
    model="gemini-3-pro-image",
    prompt="A futuristic cyberpunk city with neon lights",
    size="1920x1080",      # Supports any WIDTHxHEIGHT format, auto-calculates aspect ratio
    quality="hd",          # "standard" | "hd" | "medium"
    n=1,
    response_format="b64_json"
)

# Save image
import base64
image_data = base64.b64decode(response.data[0].b64_json)
with open("output.png", "wb") as f:
    f.write(image_data)

Supported parameters:

  • size: Any WIDTHxHEIGHT format (e.g. 1280x720, 1024x1024, 1920x1080), auto-calculates and maps to standard aspect ratios (21:9, 16:9, 9:16, 4:3, 3:4, 1:1)
  • quality:
    • "hd" → 4K resolution (high quality)
    • "medium" → 2K resolution (medium quality)
    • "standard" → Default resolution (standard quality)
  • n: Number of images to generate (1-10)
  • response_format: "b64_json" or "url" (Data URI)

🎨 Expand to view more image generation methods & parameter mapping rules (Chat API / Model Suffix / Cherry Studio)

Method 2: Chat API + Parameters (✨ New)

All protocols (OpenAI, Claude) Chat APIs now support direct size and quality parameters:

python
# OpenAI Chat API
response = client.chat.completions.create(
    model="gemini-3-pro-image",
    size="1920x1080",      # ✅ Supports any WIDTHxHEIGHT format
    quality="hd",          # ✅ "standard" | "hd" | "medium"
    messages=[{"role": "user", "content": "A futuristic city"}]
)
bash
# Claude Messages API
curl -X POST http://127.0.0.1:8045/v1/messages \
  -H "Content-Type: application/json" \
  -H "x-api-key: sk-antigravity" \
  -d '{
    "model": "gemini-3-pro-image",
    "size": "1280x720",
    "quality": "hd",
    "messages": [{"role": "user", "content": "A cute cat"}]
  }'

Parameter priority: imageSize parameter > quality parameter > model suffix

✨ New imageSize parameter support:

In addition to the quality parameter, you can now also use Gemini's native imageSize parameter:

python
# Using imageSize parameter (highest priority)
response = client.chat.completions.create(
    model="gemini-3-pro-image",
    size="16:9",           # Aspect ratio
    imageSize="4K",        # ✨ Direct resolution: "1K" | "2K" | "4K"
    messages=[{"role": "user", "content": "A futuristic city"}]
)
bash
# Claude Messages API also supports imageSize
curl -X POST http://127.0.0.1:8045/v1/messages \
  -H "Content-Type: application/json" \
  -H "x-api-key: sk-antigravity" \
  -d '{
    "model": "gemini-3-pro-image",
    "size": "1280x720",
    "imageSize": "4K",
    "messages": [{"role": "user", "content": "A cute cat"}]
  }'

Parameter descriptions:

  • imageSize: Direct resolution specification ("1K" / "2K" / "4K")
  • quality: Infers resolution from quality level ("standard" → 1K, "medium" → 2K, "hd" → 4K)
  • Priority: If both imageSize and quality are specified, the system prioritizes imageSize

Method 3: Chat API + Model Suffix

python
response = client.chat.completions.create(
    model="gemini-3-pro-image-16-9-4k",  # Format: gemini-3-pro-image-[ratio]-[quality]
    messages=[{"role": "user", "content": "A futuristic city"}]
)

Model suffix explanation:

  • Aspect ratio: -16-9, -9-16, -4-3, -3-4, -21-9, -1-1
  • Quality: -4k (4K), -2k (2K), no suffix (standard)
  • Example: gemini-3-pro-image-16-9-4k → 16:9 ratio + 4K resolution

Method 4: Cherry Studio & Other Client Settings

In clients that support OpenAI protocol (e.g., Cherry Studio), you can configure image generation parameters via the Model Settings page:

  1. Enter Model Settings: Select the gemini-3-pro-image model
  2. Configure Parameters:
    • Size: Enter any WIDTHxHEIGHT format (e.g. 1920x1080, 1024x1024)
    • Quality: Choose standard / hd / medium
    • Number: Set the number of images (1-10)
  3. Send Request: Simply type your image description in the chat dialog

Parameter mapping rules:

  • size: "1920x1080" → Auto-calculated as 16:9 aspect ratio
  • quality: "hd" → Mapped to 4K resolution
  • quality: "medium" → Mapped to 2K resolution

📝 Changelog

Latest version v4.9.8 (2026-10-17): Automatic seamless hot-switch for Antigravity Classic upon quota depletion or threshold breach with streamlined dual-pool cards (Ref #3509); native evolution supporting Antigravity CLI (agy) with automatic shell wrapper injection, thinking effort disassembly, and independent 3-client active account detection with contextual badges (Ref #3686, Ref #3630); comprehensive 403 error body capture and action URL extraction (captcha verification/appeal) with closed-loop self-healing recovery (Ref #3630, Ref #3684); Claude Code 2.1.301 protocol alignment with anti-WAF identity sanitization, full-link request-id injection, and Claude 5 model family integration (PR #3689, Ref #3684, Ref #3686); active reference tracking in JSON Schema recursive flattening to eliminate exponential replication and Linux VPS OOM (SIGKILL 137) crashes, array schema normalization with terminal ARRAY items guards to prevent Gemini 400 errors, adaptive first-chunk Peek timeout extension to 60s/90s/120s for reasoning models, zero-downtime atomic pool reload eliminating 503 errors, and anti-regression rate limit protection (PR #3694, Fixes #3690, Ref #3682, Ref #3693); complete removal of 100% 429-yielding Prod endpoints, relaxed streaming first-chunk watchdog to 120s baseline, multi-turn role preservation for Responses array inputs, and elimination of 0ms optimistic reset storms (PR #3691, PR #3672, PR #3687, PR #3685, Fixes #3506, Ref #3525); traffic monitor inspector overhaul with Postman-style single-tab view, structured vs raw code views, key-value headers tables with Bearer masking, standard dual-link HAR 1.2 export, and 12-language localization coverage (thanks to @cubelikeplayDaniel, @EricZhou05, @blackdm666, @jeikl).

👉 View Full Changelog → CHANGELOG_EN.md

👥 Contributors - Click to expand































Special thanks to all developers who have contributed to this project.

🤝 Special Thanks - Click to expand

This project has referenced or learned from the ideas or code of the following excellent open-source projects during its development (in no particular order):



Overview (`docker/README.md`)

🐋 Antigravity Manager 原生 Docker 部署手冊

本目錄包含 Antigravity Manager 的原生 Headless Docker 部署方案。該方案支持完整的 Web 管理界面、API 反代以及數據持久化,無需複雜的 VNC 或桌面環境。

🆕 本版本部署方案(本地前端構建復用)

適用於「前端近期不改、後端經常調整」的場景。思路是先在本地生成 dist/,Docker 只編譯後端並直接拷貝 dist/,大幅縮短構建時間並降低前端構建風險。

步驟

  1. 本地生成前端靜態資源:
bash
npm ci --legacy-peer-deps
npm run build
  1. 使用本方案構建與啟動(後端-only + 復用 dist/):
bash
docker compose -f docker/docker-compose.yml -f docker/docker-compose.localdist.yml build
docker compose -f docker/docker-compose.yml -f docker/docker-compose.localdist.yml up -d

或合併為單條命令:

bash
docker compose -f docker/docker-compose.yml -f docker/docker-compose.localdist.yml up -d --build

啟動後動態查看日誌:

bash
docker compose -f docker/docker-compose.yml -f docker/docker-compose.localdist.yml logs -f --tail=200

更新方式

  • 後端有改動:重跑上面的 build + up -d
  • 前端有改動:先在本地重新 npm run build,再重跑 build + up -d

Git 部署提醒

  • 若服務器不在本地構建前端,請確保 dist/ 已提交到倉庫(本版本已從 .gitignore 移除)。

🚀 快速開始

1. 直接拉取鏡像 (推薦)

您可以直接從 Docker Hub 拉取已構建好的鏡像並啟动,無需獲取源碼:

[!IMPORTANT]
安全警告:從 v4.0.3 開始,Docker 版支持 管理密碼與 API Key 分離:

  • API Key:通過 -e API_KEY=xxx 設置,用於所有 AI 協議的 API 調用鑒權。
  • Web 管理密碼:通過 -e WEB_PASSWORD=xxx 設置,僅用於 Web UI 登錄。
  • 默認行為:若未設置 WEB_PASSWORD,系統會自動回退使用 API_KEY 作為登錄密碼。若兩者皆未設置,則生成隨機 Key。
  • 查看方式:執行 docker logs antigravity-manager 尋找 Current API Key 或 Web UI Password,或執行 grep -E '"api_key"|"admin_password"' ~/.antigravity_tools/gui_config.json 查看。
bash
# 啟動容器 (請替换 your-secret-key 為強密鑰)
docker run -d \
  --name antigravity-manager \
  -p 8045:8045 \
  -e API_KEY=your-api-key \
  -e WEB_PASSWORD=your-login-password \
  -e ABV_MAX_BODY_SIZE=104857600 \
  -v ~/.antigravity_tools:/root/.antigravity_tools \
  lbjlaq/antigravity-manager:latest

[!TIP]
🧪 體驗 Beta / 預覽版鏡像:
若需使用最新的 Beta 預發布特性,請拉取對應的 Beta 版本 Tag(預發布版本獨立構建發布,不會覆蓋 latest 穩定版標籤):

bash
# 拉取指定 Beta 預發布版本
docker pull lbjlaq/antigravity-manager:v4.8.2-beta.0

# 運行 Beta 容器
docker run -d --name antigravity-manager-beta \
  -p 8045:8045 \
  -e API_KEY=your-api-key \
  -e WEB_PASSWORD=your-login-password \
  -e ABV_MAX_BODY_SIZE=104857600 \
  -v ~/.antigravity_tools:/root/.antigravity_tools \
  lbjlaq/antigravity-manager:v4.8.2-beta.0

完整版本標籤請查看 Docker Hub Tags。若需直接運行未發版 Tag 的當前最新 beta 分支源碼,可在本地構建:docker build -t lbjlaq/antigravity-manager:beta -f docker/Dockerfile .。

🔐 鑒權邏輯 (Security Scenarios)

  • 場景 A:僅設置了 API_KEY
    • Web 登錄:使用 API_KEY 即可進入後台。
    • API 調用:使用 API_KEY 進行 AI 請求鑒權。
  • 場景 B:同時設置了 API_KEY 和 WEB_PASSWORD (推薦)
    • Web 登錄:必須使用 WEB_PASSWORD。此時輸入 API Key 將被拒絕,確保管理權限與調用權限隔離。
    • API 調用:繼續使用 API_KEY。您可以放心地將 API Key 分發給團隊成員,而保留密碼僅供管理員使用。

🆙 舊版本升級指引

如果您是從舊版本升級,默認沒有設置 WEB_PASSWORD。您可以通過以下方式添加:

  1. Web UI (推薦):使用原有的 API_KEY 登錄,在 API 反代 設置頁面中設置新的管理密碼。
  2. 環境變量:停止舊容器,啟動新容器時增加 -e WEB_PASSWORD=您的新密碼。

[!TIP]
優先級邏輯 (Priority):

  • 環境變量 (ABV_WEB_PASSWORD / WEB_PASSWORD) 具有最高優先級。如果設置了環境變量,程序將始終使用它,忽略配置文件中的值。
  • 配置文件 (gui_config.json) 用於持久化存儲。當您通過 Web UI 修改密碼並保存時,新密碼會寫入此文件(JSON 字段名為 admin_password)。
  • 回退機制: 如果上述兩者皆未設置,則回退使用 API_KEY;若連 API_KEY 也未設置,則隨機生成。

2. 使用 Docker Compose

在 docker 目錄下執行:

bash
docker compose up -d

3. 手動構建鏡像 (開發者 / 二改版)

如果您需要修改代碼或自定義構建,請在項目根目錄下執行:

Windows PowerShell(推薦)

powershell
# 一鍵構建二改版鏡像(標籤 antigravity-manager:local + 版本-fix)
.\docker\build.ps1

# 国内網絡加速
.\docker\build.ps1 -UseMirror

# 構建並推送到你自己的倉庫
.\docker\build.ps1 -UseMirror -Push -Registry "yourname/antigravity-manager"

手動 docker build

bash
# 默認構建最新標籤
docker build -t antigravity-manager:local -f docker/Dockerfile .

# 二改版 Compose 啟動(Windows 可用端口映射,不用 host 網絡)
docker compose -f docker/docker-compose.yml -f docker/docker-compose.fork.yml up -d --build

💡 構建參數

本鏡像支持自動鏡像源切換,以提升国内構建速度:

  • USE_MIRROR:
    • auto (默認): 自動檢測網絡環境,若無法訪問 Google 則切換至国内镜像(阿里云/NPM Mirror)。
    • true: 強制使用国内镜像源。
    • false: 強制使用官方默認源。

示例:

bash
# 強制使用国内镜像加速構建
docker build --build-arg USE_MIRROR=true -t antigravity-manager:latest -f docker/Dockerfile .

⚙️ 環境變量配置

變量名 默認值 說明
PORT 8045 容器內服務監聽端口
ABV_API_KEY - [重要] 代理 API 密鑰。客戶端(如 Claude Code)訪問時需提供的 Key
ABV_WEB_PASSWORD - [安全] Web 管理後台登錄密碼。若不設置則回退使用 API Key
ABV_MAX_BODY_SIZE 104857600 [性能] 最大請求體限制 (Byte)。默認 100MB,用於解決大圖傳輸 413 錯誤
LOG_LEVEL info 日志等級 (debug, info, warn, error)
ABV_DIST_PATH /app/dist 前端靜態資源託管路徑 (Dockerfile 已內置)
ABV_PUBLIC_URL - 用於遠程 OAuth 回調的公網 URL (可選)

📂 數據持久化

請務必將宿主機目錄掛載至容器內的 /root/.antigravity_tools,否則賬號和配置在容器重啟後會丟失。

🌐 訪問位址

📦 Docker Hub 分發 (推薦)

若要推送至你的倉庫:

bash
# 打上版本標籤並推送
docker tag antigravity-manager:latest lbjlaq/antigravity-manager:latest
docker tag antigravity-manager:latest lbjlaq/antigravity-manager:4.3.0
docker push lbjlaq/antigravity-manager:latest
docker push lbjlaq/antigravity-manager:4.3.0

Overview (`docs/README.md`)

Documentation index

This folder contains developer-focused documentation (architecture, implementation details, and validation steps).

Proxy

  • docs/proxy/auth.md — proxy authorization modes, expected client behavior, and implementation pointers.
  • docs/proxy/accounts.md — account lifecycle in the proxy pool (including auto-disable on invalid_grant) and UI behavior.

z.ai (GLM) integration

Agent Integrations


API REFERENCE (`docs/API_REFERENCE.md`)

API Reference (v4.3.0)

本文档详细介绍了 Antigravity Tools 暴露的 HTTP API 接口。

注意: 在 v4.0.1 版本中,所有的服务(包括 AI 反代和系统管理)均已整合至统一端口 8045。原有的 19527 端口已废弃。

1. 概览 (Overview)

Antigravity Gateway 是一个双重角色的服务器:

  1. AI Proxy Interface: 兼容 OpenAI/Anthropic/Google 官方 SDK 的标准接口。
  2. Management Admin API: 用于管理账号、配置系统、监控流量的 RESTful 接口。

鉴权体系 (Authentication)

接口类型 路径前缀 鉴权方式 Header 示例 说明
AI Protocol /v1/*, /v1beta/* API Key Authorization: Bearer <API_KEY> 用于 AI 客户端调用
Admin API /api/* Admin Token x-admin-token: <TOKEN> 用于管理后台或脚本控制

提示: 默认情况下,Admin Token 与 API Key 是同一个值(即您在 .env 或 Docker 环境变量中设置的 API_KEY)。


2. 管理接口 (Management API)

Base URL: http://<host>:8045/api

2.1 账号管理 (Account Management)

方法 路径 说明 参数示例
GET /accounts 获取账号列表 -
GET /accounts/current 获取当前活跃账号 -
POST /accounts 添加账号 (OAuth Refresh Token) {"refreshToken": "..."}
DELETE /accounts/:id 删除账号 -
POST /accounts/switch 切换活跃账号 {"accountId": "acc_123", "targetIde": "agy"} (targetIde 可选,如传 "agy" 则仅写入凭据免重启 IDE)
POST /accounts/refresh 刷新所有账号配额 -
GET /accounts/:id/quota 查询特定账号配额 -
POST /accounts/:id/toggle-proxy 禁用/启用账号代理 -
POST /accounts/:id/bind-device 绑定设备指纹 {"mode": "generate"}
POST /accounts/bulk-delete 批量删除账号 {"accountIds": ["id1", "id2"]}
POST /accounts/reorder 账号排序 {"accountIds": [...]}

2.2 系统配置 (System Config)

方法 路径 说明
GET /config 获取全量配置
POST /config 保存全量配置
GET /proxy/status 获取反代服务运行状态
POST /proxy/start 启动反代服务
POST /proxy/stop 停止反代服务
POST /proxy/mapping 更新模型映射规则
GET /health 系统健康检查

2.3 监控与统计 (Monitoring & Stats)

流量日志

  • GET /logs: 获取日志列表 (支持 limit, offset, filter, errorsOnly 参数)
  • GET /logs/count: 获取日志总数
  • GET /logs/:id: 获取日志详情
  • POST /logs/clear: 清空日志

Token 统计 (v4.0.1 New)

  • GET /stats/token/summary: 获取 Token 消耗摘要 (今日/本周/总量)
  • GET /stats/token/hourly: 获取按小时统计数据
  • GET /stats/token/daily: 获取按日统计数据
  • GET /stats/token/by-account: 按账号统计消耗占比
  • GET /stats/token/by-model: 按模型统计消耗占比
  • POST /stats/token/clear: 重置统计数据

2.4 高级功能 (Advanced)

  • POST /proxy/cli/sync: 执行 CLI (Claude/Codex) 配置文件同步
  • POST /accounts/import/db: 从 v1 旧数据库导入账号
  • POST /accounts/oauth/start: 发起 OAuth 授权流程 (Headless)
  • POST /proxy/cloudflared/start: 启动 Cloudflare Tunnel

3. AI 协议接口 (AI Protocol Interface)

Base URL: http://<host>:8045

本服务完全兼容主流 AI 厂商的官方协议规范。您可以直接将本服务的地址填入到支持 OpenAI / Claude 的客户端中。

OpenAI Compatible

  • 对话生成 (Chat Completions)

    • POST /v1/chat/completions
    • 支持模型: 任何映射后的模型 ID (如 gpt-4o, gemini-1.5-pro)
    • 兼容性: 完全兼容 OpenAI 官方 Response 格式 (包括流式 SSE)。
  • 图片生成 (Image Generation)

    • POST /v1/images/generations
    • 支持模型: gemini-3-pro-image (自动映射到 Imagen 3)
    • 参数扩展: 支持 size: "1920x1080", quality: "hd" 等高级参数。

Anthropic Compatible

  • Claude Messages
    • POST /v1/messages
    • 用途: 支持 Claude CLI (claude), Cursor, Cherry Studio 等客户端。
    • 特性: 完整支持 Tool Use (工具调用) 和 Thinking (思维链) 模式。

Gemini Native

  • Google AI Studio
    • GET/POST /v1beta/models/*
    • 用途: 供使用 Google 官方 SDK (Python/Node.js) 的应用调用。

CLAUDE CODE 10 VERSIONS DIFF (`docs/CLAUDE_CODE_10_VERSIONS_DIFF.md`)

跨 10 个版本 (v2.1.284 ~ v2.1.295) Claude Code 流控制与状态机演进全景对比报告

研究对象:Anthropic 官方发布的 10 个 Darwin ARM64 原生发行版本:
v2.1.284、v2.1.285、v2.1.286、v2.1.288、v2.1.289、v2.1.291、v2.1.292、v2.1.293、v2.1.294、v2.1.295
逆向技术:Mach-O Mach 头部解构、Bun standalone __BUN.__bun 压缩脚本解包、AST 语义比对与状态机拓扑分析


一、版本演进总览与里程碑划分

从 v2.1.284 到 v2.1.295,Claude Code 的流控制体系经历了一场由“松散容错”转向“极度严苛的有限状态机校验”的重大演进。

text
[v2.1.284 ~ v2.1.286] 宽松网络容错期
  └── 允许未完全闭合的流截断,基础网络重试,无复杂思考块拦截
          │
          ▼
[v2.1.288 ~ v2.1.289] 空闲超时看门狗期
  └── 引入 outlastedNonStreamingTimeout,新增 timedOut 错误枚举,流超时硬性判定
          │
          ▼
[v2.1.291 ~ v2.1.293] 状态机契约收紧与中毒爆发期 (当前排查重点)
  └── 引入严苛的三元组契约 (qu !== null && zb && El === null)
  └── 引入 resumedFromIncompleteThinking 脏状态标记
  └── 引入 CLAUDE_CODE_OVERLOADED_RETRY_BASE_DELAY_MS 与 qz 拦截位,引爆 27 分钟死锁
          │
          ▼
[v2.1.294 ~ v2.1.295] 契约固化与内容审查期
  └── 状态机完全冻结,新增 cli_output_filter 遥测与更严格的 SSE 帧序校验

二、逐版本核心状态机代码与函数 Diff 深度比对

1. v2.1.284 ~ v2.1.286:宽松容错基线

  • Qnn 核心实现:
    javascript
    function Qnn(e) {
      let n = e.anyBlockFinished ? (e.anyOutputShown ? "output" : "thinkingOnly") : (e.anyOutputShown ? "partialOutput" : (e.anyEvent ? "started" : "nothing")),
          r = ufr(e);
      if (e.complete) {
        if (r === "connectionLost" || r === "truncated" || r === "stalled" || (r === "overloaded" || r === "serverError") && n === "output")
          return;
      }
      return { kind: "streamFailed", cause: r, progress: n, ... };
    }
  • 核心特征:
    • ufr 仅包含基础的 connectionLost、truncated、stalled、overloaded、serverError 映射;
    • 缺乏对系统睡眠挂起(suspended)与空闲超时的区分;
    • 对思考块未闭合中断没有特殊的脏标记写入机制,会话容错度极高。

2. v2.1.288 ~ v2.1.289:超时看门狗与 `timedOut` 枚举引入

  • Diff 变动点:
    1. 在 Qnn 守卫中引入 timedOut:
      javascript
      - (r === "overloaded" || r === "serverError") && n === "output"
      + (r === "overloaded" || r === "serverError" || r === "timedOut") && n === "output"
    2. 在 ufr 中将 timeout_error 与非流式超时区分:
      javascript
      if (e.isServerError) {
        return e.error instanceof xt && e.error.type === "timeout_error" ? "timedOut" : "serverError";
      }
    3. 引入客户端活跃计时器:outlastedNonStreamingTimeout = monotonicNow() - Cy >= uon()。
  • 影响分析:
    网关一旦发生超过客户端 uon() 的长静默(如模型复杂推理未推流),客户端开始单方面定性为 timedOut,不再简单归类为网络断连。

3. v2.1.291 ~ v2.1.293:严苛三元组契约、会话中毒与 27 分钟死锁的引爆点

  • Diff 变动点 1:引入严格的三元组不变式:
    javascript
    + complete: qu !== null && zb && El === null
    • El 首次作为打开块的悬挂索引深度绑定在流完成判断中。
    • 如果网关在流结束时遗留了任何未发送 content_block_stop 的块(如 Thinking 块或 Text 块),El 保持为非空,导致 complete 判定永远为 false!
  • Diff 变动点 2:引入会话永久中毒脏标记 resumedFromIncompleteThinking:
    javascript
    + if ($e && E.type === "assistant" && !E.isApiErrorMessage && !hr([E])) {
    +   E.resumedFromIncompleteThinking = !0;
    + }
    • 当流在中途中断且仅有思考块时,该轮助手消息被打上 resumedFromIncompleteThinking: true 并持久化落盘;
    • 下一轮提问时,残缺思考块因缺失 Google Gemini Thought Signature 导致上游持续报 400,造成会话永久瘫痪。
  • Diff 变动点 3:引入过载基础延迟与 qz 拦截位:
    javascript
    + qz = Ru?.progress === "thinkingOnly" && Ru.cause === "overloaded" && !Ru.stopReasonReceived;
    + if (Ru === void 0 || Ru.progress === "output" || Ru.progress === "thinkingOnly" && !qz) { ... }
    • 新增环境变量与参数 CLAUDE_CODE_OVERLOADED_RETRY_BASE_DELAY_MS;
    • 若在思考阶段断流且被误判为 overloaded,qz 直接阻断正常 keepPartial 退出,直接坠入 10 次指数退避阶梯,累计卡死长达 27~29 分钟!
  • Diff 变动点 4:引入 suspended 挂起态与 outputFiltered:
    • 增强对操作系统休眠的识别与内容过滤拦截。

4. v2.1.294 ~ v2.1.295:状态机固化与遥测审查强化

  • Diff 变动点:
    • 状态机完全继承 2.1.293 的三元组契约与 keepPartial 分支逻辑,未做回退;
    • 新增敏感内容审查遥测指标:cli_output_filter_retry_outcome 与 tengu_output_filtered;
    • 强化了对 SSE 异常帧序的捕获:在 message_stop 后若收到任何数据,TJ("closed") 的抛出点更加前置。

三、跨版本 10 大关键判定指标演进对照矩阵

判定维度 v2.1.284 ~ 2.1.286 v2.1.288 ~ 2.1.289 v2.1.291 ~ 2.1.293 v2.1.294 ~ 2.1.295
complete 判定契约 qu !== null && zb qu !== null && zb qu !== null && zb && El === null qu !== null && zb && El === null
未闭合块容忍度 (El) 允许静默截断收尾 允许静默截断收尾 抛出 StreamTruncatedError 抛出 StreamTruncatedError
纯思考断流处理 允许重试或截断 允许重试或截断 打上 resumedFromIncompleteThinking 脏标记 持续维持脏标记逻辑
529 过载退避算法 常规指数退避 (10次) 常规指数退避 (10次) 惩罚性 30 分钟冷冻 + qz 强阻断 (27m+) 惩罚性 30 分钟冷冻 + qz 强阻断 (27m+)
超时错误枚举 仅 serverError 新增 timedOut 维持 timedOut (thinking 仅重试 1 次) 维持 timedOut
系统挂起处理 视作 connectionLost 视作 connectionLost 新增 suspended 识别 维持 suspended
429 容忍硬上限 60 秒 (Bur = 60000) 60 秒 (Bur = 60000) 60 秒 (Bur = 60000) 抛硬崩溃 60 秒 (Bur = 60000) 抛硬崩溃
转录本存储架构 JSONL + .meta.json JSONL + .meta.json JSONL + .meta.json (n6t 刷盘拦截) JSONL + .meta.json (n6t 刷盘拦截)
思考恢复判定 (bp) 无限制 初步试验特性 严格限制 content.length === 1 严格限制 content.length === 1
对网关收尾的要求 仅需 end_turn 需 end_turn + 心跳 必须闭合所有块 + 补正文 + end_turn + EOF 必须闭合所有块 + 补正文 + end_turn + EOF

四、历史调试反思与 PR #3634 的数学必然性

在早期的排查调试中,由于未能洞察 v2.1.291 ~ v2.1.293 发生的核心状态机突变:

  1. 回退 9 月方案时的失误:
    曾误认为“应该把底层的网络连接重置和超时透传给客户端,由客户端自身处理重试”。然而在 2.1.293 的严格规则下,裸透传网络异常或合成 overloaded_error 直接命中了 PU(e) 与 qz 拦截位,反而亲手引爆了 27 分钟死锁;
  2. 残缺思考块的副作用:
    断流时未能闭合思考块并注入正文,使得消息长度停留在 content.length === 1,精准激活了 bp(e) 触发链,导致大量旧会话被打上脏标记永久中毒。

PR #3634 的数学收敛必然性:
PR #3634 实现的四大防御支柱:

  1. 强制闭合未完成块(满足 El === null);
  2. 纯思考断流自动注入正文文本块(使助手消息 content.length >= 2,破除 bp 判定,杜绝会话中毒;同时升级进度为 "output");
  3. 下发 message_delta(stop_reason: "end_turn") 与 message_stop(满足 qu !== null && zb === true);
  4. 移除 overloaded_error 并前置 message_stop_sent 静默防火墙(彻底绕过 PU(e) 与 TJ("closed"))。

上述实现在数学逻辑与状态机转移图上,100% 完整覆盖并契合了从 v2.1.284 至 v2.1.295 的全部演进规则,是真正意义上的根因级、未来导向型稳态解决方案。


CLAUDE CODE STREAM DIAGNOSIS (`docs/CLAUDE_CODE_STREAM_DIAGNOSIS.md`)

Claude Code 与 Antigravity 网关长流断裂、重试风暴与会话中毒深度排查诊断全书

文档版本:v2.0 (全量反编译深度演化版)
逆向目标:Claude Code 官方发行版 v2.1.284 ~ v2.1.295(重点解构本地运行环境 v2.1.293 原生 Darwin ARM64 Bun 内嵌运行时)
对齐分支:beta (PR #3634)
关联 Issue:Issue #3633, Issue #3635


目录

  1. 错误症状全景总结 (Symptoms Catalog)
  2. 客户端架构逆向解构:14 核心机制证据链
  3. Antigravity 网关交互准则与绝对红线
  4. 版本选型建议与用户自愈指引
  5. 9 月历史回退的反思与现代融合架构

一、错误症状全景总结 (Symptoms Catalog)

在长耗时大算力生成任务(典型如高细节坐标 SVG、深度系统架构推理、批量代码重构)中,中间网络或 Google GFE 往往在 100~116 秒发生 TCP 静默或断连。在未适配最新 Claude Code 状态机时,客户端会呈现以下 4 大恶性症状:

症状代号 客户端表象 触发场景 根本技术诱因 严重程度
S1: 重试风暴死循环 终端出现橙色警告并倒计时:Retrying 6/10 · 27m 24s 或 Retrying 8/10 · 16m+ 长耗时流中途中断,网关透传底层 Err 或合成下发 overloaded_error 触发客户端 PU(e) 过载判定,激活 ofr = 1800000ms(30分钟)冷冻期,叠加 vC() 10 阶指数退避 致命(卡死会话近半小时)
S2: 会话永久中毒 当前轮次中断后,用户输入任何新问题,该会话持续秒报 400 错误;但新建空白对话完全正常 断流发生在思考(Thinking)块之后、正文尚未产生或未合法闭合 客户端未收齐三元组,标记 resumedFromIncompleteThinking: true 落盘,后续请求缺失 Thought Signature 遭 Gemini 拒答 致命(本地历史上下文报废)
S3: 幽灵 429 与秒级崩溃 终端弹出 api_request_retry_after_too_long 异常硬崩溃退出,或报账户额度超限 网关向客户端下发的 429 响应中 Retry-After 大于 60 秒 命中客户端非 Watchdog 模式下的硬上限常量 Bur = 60000ms(60s),客户端直接抛错拒绝等待 严重(客户端进程异常崩溃)
S4: 客户端主动掐线 (Client RST) 终端在无报错提示下突然截断停止输出,网关收到下游 Broken pipe 网关等待上游模型计算期间长时间静默未推流 客户端空闲看门狗触发 monotonicNow() - Cy >= uon() 超时,客户端主动发送 TCP RST 销毁连接 严重(生成物变为残缺代码)

二、客户端架构逆向解构:14 核心机制证据链

2.1 过载判定 `PU(e)` 源码与 30 分钟惩罚性冷冻

基于对 Claude Code v2.1.293 内置运行时核心偏移地址 186169512 的反编译:

javascript
// 核心过载判定函数: PU(e)
function PU(e) {
  if (!(e instanceof xt)) return !1;
  return e.status === 529 || (e.message?.includes('"type":"overloaded_error"') ?? !1);
}
  • 判定要素:
    1. e 必须为 Anthropic SDK 核心 APIError 实例(xt);
    2. 满足 e.status === 529 或 错误消息字符串包含字面量 "type":"overloaded_error"。
  • 级联反应与 30 分钟冷冻期:
    javascript
    let qo = sfr(Jt), uo = qo !== null && qo < LKe; // LKe = 20000ms (20s)
    if (!uo) {
      let ko = Math.max(qo ?? ofr, rfr); // ofr = 1800000 (30分钟!), rfr = 600000 (10分钟!)
      let ir = PU(Jt) ? "overloaded" : "rate_limit";
      ops(Date.now() + ko, ir); // 写入本地冷却期熔断时间戳
      if (jo()) h.fastMode = !1; // 强制关闭 Fast Mode
      continue;
    }
    结论:只要错误载荷中包含 "type":"overloaded_error",若未携带小于 20 秒的 retry-after,客户端直接进入长达 30 分钟 (1,800,000ms) 的惩罚性冷却状态。

2.2 指数抖动退避计算引擎 `vC` / `LF` 与 27 分钟死锁数学公式

客户端在 chunk-k2e8p61g.js 中实现了精确的指数比例抖动算法:

javascript
// 退避核心算法引擎
function vC({attempt: t, baseMs: r, capMs: e = 1/0, factor: o = 2, jitter: n = {kind: "none"}, random: m = Math.random}) {
  let i = Math.min(e, r * o**(t - 1));
  switch (n.kind) {
    case "none": return i;
    case "proportional": return i + m() * n.ratio * i; // 正向 0 ~ +25% 比例抖动
    case "symmetric": return Math.max(0, i + i * n.ratio * (2 * m() - 1));
    case "full": {
      let a = Math.min(n.floorMs, i);
      return a + m() * (i - a);
    }
  }
}

// API 重试封装函数
function LF(e, r, a = 32000, s = Math.random, n = me) { // me = 500ms
  let o = Math.round(vC({attempt: e, baseMs: n, capMs: a, jitter: {kind: "proportional", ratio: 0.25}, random: s}));
  if (r) {
    let i = parseInt(r, 10);
    if (!isNaN(i)) return Math.max(i * 1000, o);
  }
  return o;
}
  • 数学公式:
    $$\text{Delay}{\text{base}} = \min(32000, 500 \times 2^{\text{attempt}-1})$$
    $$\text{Delay}
    {\text{actual}} = \text{Delay}_{\text{base}} \times (1 + 0.25 \times \text{random}())$$
  • 重试上限计算 (eVe):
    javascript
    var yur = 10;    // 默认交互模式上限: 10 次
    var IKe = 15;    // CLAUDE_CODE_MAX_RETRIES 硬截断限制: 15 次
    var _ur = 300;   // Watchdog 模式上限: 300 次
    默认情况下,客户端执行 10 次 重试。在命中 ofr 冷冻窗口叠加指数退避后,10 次重试累计等待时长精准落在 27 ~ 29 分钟,形成终端无法退出的假死风暴。

2.3 核心终结三元组契约 `qu !== null && zb && El === null`

在流事件循环生成器 Bfr 与仲裁器 Qnn 中,流的完备性取决于严格的三元组逻辑:

$$\text{complete: } qu \neq \text{null} \land zb \land El === \text{null}$$

  1. qu (Stop Reason):
    • 必须为有效字符串("end_turn"、"tool_use"、"max_tokens"、"stop_sequence");
    • 必须通过 message_delta 的 delta.stop_reason 下发。
  2. zb (Terminal Marker):
    • 布尔标志位。初始为 false;在收到合法携带 stop_reason 的 message_delta 时置为 true;
    • 若随后出现乱序未闭合块事件,会被强制重置为 false。
  3. El (Open Block Pointer):
    • 处于打开状态的内容块索引指针;
    • 收到 content_block_start 时赋值为当前块索引 El = index;
    • 仅在收到 content_block_stop 时重置为 null。
    • 致命断言:若流结束(EOF)时 El !== null,客户端直接执行:
      javascript
      if (uf && El !== null) {
        throw t(`Stream ended cleanly inside open block ${El} (stop_reason=${qu}) — treating as a dropped connection`), new cwr;
      }
      抛出 StreamTruncatedError 并判定为异常截断!

2.4 流失败原因分类器 `ufr(e)` 11 类分类体系

当流中断或报错时,函数 ufr(e) 按确定性优先级将原因解析为以下 11 种枚举值:

原因代码 (cause) 判定前置条件 典型根因
1. "denied" e.denied 为 true DLP 审查拦截或指令逃逸检测阻断
2. "outputFiltered" e.outputFiltered 为 true 命中客户端本地敏感内容过滤规则
3. "overloaded" PU(e.error) 命中 响应包含 529 或 "type":"overloaded_error"
4. "timedOut" e.isServerError 且 error.type === "timeout_error" 上游网关返回 504 Gateway Timeout
5. "serverError" e.isServerError 命中 (500, 502, 503 等) 网关崩溃或服务异常
6. "stalled" e.stalled 看门狗标记为 true 30 秒流式静默无 chunk 产生
7. "truncated" e.isConnectionError 且 e.truncated (命中 cwr) 块未关闭或信封未关闭即遭遇 EOF
8. "suspended" e.isConnectionError 且 uQt(e.error) 命中 操作系统休眠挂起导致 Socket 破裂
9. "connectionLost" e.isConnectionError 命中底层网络集合 P2 TCP RST、Broken pipe、ECONNRESET
10. "malformed" e.malformed 命中 (抛出 TJ) 收到重复关闭块或未启动块的 delta
11. "badRequest" e.error instanceof xt 且非网络层异常 HTTP 400 系列参数非法或签名失效

2.5 `progress` 5 级状态梯度与 `Qnn` 核心短路豁免模型

客户端定义了 5 级输出进度:

javascript
let n = e.anyBlockFinished 
  ? (e.anyOutputShown ? "output" : "thinkingOnly") 
  : (e.anyOutputShown ? "partialOutput" : (e.anyEvent ? "started" : "nothing"));
  • "output":至少 1 个块已关闭且展示了正文/工具调用(最安全状态);
  • "thinkingOnly":已有块关闭,但全部为思考块,正文未吐出(极度危险状态);
  • "partialOutput":正文正在吐字,但首个块尚未收到 content_block_stop 闭合;
  • "started":收到过首包(如 message_start 或打开的思考块),但无块关闭且无正文;
  • "nothing":尚未收到任何 SSE 数据帧。

`Qnn` 核心短路豁免守卫:

javascript
if (e.complete) {
  if (r === "connectionLost" || r === "suspended" || r === "truncated" || r === "stalled" || 
     ((r === "overloaded" || r === "serverError" || r === "timedOut") && n === "output")) {
    return; // 关键:返回 undefined!
  }
}

数学豁免事实:只要 complete === true 且 progress === "output",无论遭遇网络断开、超时还是 529,Qnn 均直接返回 undefined!外层 Mp 被赋予 "keepPartial",彻底绕过 onStreamFailed,绝不触发任何重试!


2.6 决策引擎 `lfr(...)` 全分支状态迁移矩阵与 `qz` 拦截陷阱

若未满足豁免条件,错误进入 lfr 决策矩阵:

progress 阶段 cause 故障原因 客户端决策 (lfr) 下游行为
"output" 除 badRequest 外的所有异常 decision: "keepPartial" 优雅截断,保留已有内容,交还控制权
"thinkingOnly" overloaded nVe(g, h, b) 27+ 分钟重试风暴死循环
"thinkingOnly" connectionLost / truncated / malformed VN("afterThinkingOnly", 2) 重试 2 次后强制落盘并标记会话中毒
"thinkingOnly" timedOut (504) VN("afterThinkingOnly", 1) 重试 1 次后失败
"thinkingOnly" serverError (500) VN("afterThinkingOnly", 2) 重试 2 次后失败
"partialOutput" / "started" 任意连接或服务错误 Qhe(...) 进入非流式降级 (retryWithoutStreaming)

`qz` 拦截标志位的致命陷阱:

javascript
let qz = Ru?.progress === "thinkingOnly" && Ru.cause === "overloaded" && !Ru.stopReasonReceived;
if (Ru === void 0 || Ru.progress === "output" || Ru.progress === "thinkingOnly" && !qz) {
  // 正常 keepPartial 优雅退出逻辑
}

若断流发生在 thinkingOnly 阶段且错误归因于 overloaded,qz 为 true,将强行阻止客户端进入正常收尾分支,迫使其无条件坠入 27 分钟死循环。


2.7 会话历史中毒机制 `resumedFromIncompleteThinking` 触发条件与阻断数学证明

通过对函数 bp(e, o) 与 Q4o(...) 的反编译分析:

javascript
function bp(e, o) {
  let n = e.filter((s) => !s.isApiErrorMessage && !l0e(s)),
      r = n.length === 1 ? n[0] : void 0;
  if (r === void 0 || r.message.content.length !== 1) return !1;
  let u = r.message.content[0];
  return u !== void 0
      && Fme(u) // u.type === "thinking" || u.type === "redacted_thinking"
      && VEn(u) // 必须有合法签名
      && r.message.stop_reason === "max_tokens"
      && Q4o(o, r.message.resumable, UP(r.message.usage));
}
  • 中毒触发链条:
    1. 思考断流发生后,若客户端仅记录到残缺思考块(content.length === 1 且无正文);
    2. 进程退出或下一轮输入刷盘时,Assistant 消息被标记:
      if ($e && E.type === "assistant" && !E.isApiErrorMessage) E.resumedFromIncompleteThinking = !0;
    3. 下一轮提问时,客户端将该残缺思考块回传给上游。因为 Google Gemini 原生不具备 Anthropic 的私有加密思考块连续签名(Thought Signature),上游校验失败直接返回 HTTP 400 Bad Request。
  • 网关阻断数学证明:
    网关在思考断流时执行策略——“显式闭合思考块,注入合成正文块,下发 end_turn”。
    此时助手消息的内容变为 [Thinking, Text],content.length = 2。
    由于 r.message.content.length !== 1,bp(...) 在首个条件分支恒返回 false,从而在数学层面彻底粉碎了 resumedFromIncompleteThinking 的产生前置条件!

2.8 会话转录本 (Transcript) 存储架构真相:纯 JSONL + .meta.json (无 SQLite)

经过对 Bun 运行时模块与存储引擎的反编译排查,澄清先前流传的技术误解:

  • 无本地 SQLite 参与:Claude Code 业务层转录本完全未采用 SQLite;
  • 真实文件体系:
    • 会话数据:~/.claude/projects//<sessionId>.jsonl
    • 会话元数据:~/.claude/projects//<sessionId>.meta.json
    • 子智能体:~/.claude/projects//<sessionId>/subagents/agent-<agentId>.jsonl
  • 流式拦截器 n6t:
    javascript
    function n6t(e, n, r) {
      if (!r) return e.length;
      for (let s = n; s < e.length; s++) {
        let g = e[s];
        if (g.type === "assistant" && g.message.stop_reason === null) return s;
      }
      return e.length;
    }
    在流进行期间,stop_reason === null 的未完结助手消息被 n6t 严格拦截、禁止落盘。但一旦遭遇异常掐线且进程退出,强制刷盘会使残缺消息落盘写入 .jsonl,导致持久化污染。

2.9 澄清真相:官方不存在 `/undo` 斜杠命令与 `/compact` 5 级数据清洗自愈管道

反编译全量命令注册表(type: "local" 与 type: "local-jsx")确认:

  • Claude Code 官方命令集中不存在 /undo 斜杠命令。用户此前理解的 undo 实际是快捷键 ctrl+_(输入框单行撤销)或历史栈内存回退;
  • 真正的会话级清洗管道为 /compact。客户端在执行 /compact 时按序调用 5 级自愈管道:
    1. Jjr:消除思考块间的空白文本块并补占位符;
    2. oPn / nPn:过滤未闭合且无签名的孤立残缺思考块(抛出 tengu_filtered_orphaned_thinking_message);
    3. ZAn:剥除末尾残留的孤立思考块,补入 [No message content],杜绝 Anthropic 400 校验违规;
    4. tPn:消除空白 Assistant 消息并执行 _I 缝合相邻 User 角色;
    5. Zjr:对空 Assistant 消息补入合法文本兜底。
  • 自愈指引:对于历史损坏的会话,用户执行 /compact 或 /clear 即可激活上述清洗管道完成无损自愈。

2.10 `message_stop` 后的静默防火墙契约与 `TJ("closed")` 格式异常

在事件消费循环 xet 中:

javascript
case "content_block_delta": {
  let Ba = Jg[us.index];
  if (!Ba || Ol.has(Ba)) {
    throw i("tengu_streaming_error", {
      error_type: Ba ? "content_block_closed_delta" : "content_block_not_found_delta"
    }), new TJ(Ba ? "closed" : "unstarted");
  }
}
  • 关键机制:收到 content_block_stop 后,块被存入已关闭集合 Ol。如果在 message_stop 发送之后网关继续推流(如延迟的 SSE 数据、迟到的心跳或尾部元数据),将命中 Ol.has(Ba),抛出 StreamMalformedEventError("closed")(TJ("closed"))。
  • 灾难后果:malformed 不受 complete 守卫豁免!客户端会直接将整轮会话定性为畸形,在控制台弹出黄色警告并可能触发非流式重试。
  • 网关防线:下发 message_stop 后必须立即使连接到达 HTTP EOF,且在 process_sse_line 入口前置 if state.message_stop_sent { return None; } 守卫。

2.11 传输层 SSE `event: error` 即刻抛错与协议中断机制

SDK 底层 SSE 解析器 fromSSEResponse(偏移 190503780):

javascript
if (T.event === "error") {
  let P = hf(T.data) ?? T.data, M = P?.error?.type;
  throw new xt(void 0, P, void 0, n.headers, M);
}
  • 核心判定:一旦客户端解析到 event: error,立即抛出 xt 异常中止生成器,后续任何事件均无法到达。
  • 网关规范:推流开始后(has_content || has_thinking),绝对禁止下发 event: error。若发生中断,必须合成合法的 content_block_stop -> 兜底文本 -> message_delta(end_turn) -> message_stop 序列。

2.12 HTTP 429 限流与 `Bur = 60000` (60s) 硬截断崩溃陷阱

客户端定义了严格的退避时间天花板常量:

javascript
var Bur = 60000; // 非 Watchdog 模式单次退避容忍上限: 60 秒
javascript
$o = LF(Vt + G, Oo, void 0, r.random, ...);
if (GV()) {
  $o = Math.min($o, NKe);
} else if ($o > Bur) {
  throw i("tengu_api_retry_after_too_long", {delayMs: $o, status: Jt.status}),
        new qc(Jt, h); // 抛出异常直接中止,拒绝重试!
}

严重风险:当未开启 CLAUDE_CODE_RETRY_WATCHDOG 时,若网关返回的 Retry-After 计算后大于 60 秒,客户端直接硬崩溃抛错退出。网关下发 429 时必须将 Retry-After 安全钳制在 1 ~ 15 秒内。


2.13 非流式降级分支 (`retryWithoutStreaming`) 与 `jAt(e)` 强契约校验

当流在首包阶段遭遇 404 或未出字前网络中断时,客户端会通过 Qhe 触发非流式降级:

javascript
let Ye = await Y.beta.messages.create({ ...Ce, stream: !1 }, ...);

客户端对非流式响应执行 jAt(e) 强模式校验:

  • content 必须为合法 Array;
  • model 必须为非空 String;
  • usage 必须为包含 input_tokens 与 output_tokens 的 Object。
    缺损任何字段均会导致 Non-streaming fallback also failed 致命报错。网关必须保持对 /v1/messages 的 stream: false 完整支持。

2.14 SSE 保活心跳帧定界规范与双换行 `\n\n` 约束

客户端内置解析器 class rjt 与 SDK 流迭代器中:

  • SSE 帧采用连续换行符定界(\n\n 或 \r\n\r\n)。单换行会被挂起在 pending 缓冲中,无法触发事件解析;
  • 收到注释行 : keepalive\n\n 时,字段解析函数 f(n) 返回 {},触发 this.resetLivenessTimer(),同时重置 Cy 计时器;
  • 网关在模型长思考期间必须至少每 15 秒(推荐每 3 秒)刷出一次保活帧,严禁长流静默超 45 秒。

三、Antigravity 网关交互准则与绝对红线

基于上述 14 维度深度逆向成果,Antigravity 网关确立以下生命周期交互红线与合规规范:

1. 绝对红线 (Strictly Prohibited)

  1. 严禁在推流开始后透传底层连接断开 Err (如 TCP RST / Broken pipe):
    一旦 has_content == true 或 has_thinking == true,任何网络断裂必须被网关拦截并转入本地优雅收尾。
  2. 严禁在响应中合成或下发带有 "type":"overloaded_error" 的任何载荷:
    无论首包或中途,该字串均会触发客户端 PU(e) 判为过载,引爆 30 分钟冷却与 10 阶指数退避风暴。
  3. 严禁遗留悬挂未闭合的内容块(禁止 El !== null):
    发送 message_delta 或 message_stop 之前,必须显式发送 content_block_stop 闭合所有已开启块。
  4. 严禁 Thinking-Only 孤立断流:
    流若在仅有思考块时中断,必须补发兜底正文文本块升级进度至 "output",严禁直接以思考块收尾导致会话中毒。
  5. 严禁在 message_stop 下发后追加任何帧或维持心跳:
    下发 message_stop 后必须立即使连接到达 HTTP EOF,防止触发 TJ("closed") 或客户端 30s Byte Watchdog 误杀。
  6. 严禁下发超过 60 秒的 Retry-After 头部:
    防止触发客户端 Bur = 60000ms 的 api_request_retry_after_too_long 硬崩溃。

2. 必须行为 (Mandatory Requirements)

  1. 严格履行黄金三元组收尾契约:
    断流收敛序列必须严格保证:
    $$\text{content_block_stop (若有打开块)} \longrightarrow \text{正文兜底 (若思考孤立)} \longrightarrow \text{message_delta (end_turn)} \longrightarrow \text{message_stop} \longrightarrow \text{HTTP EOF}$$
  2. 首包不可逆错误注入 x-should-retry: false 响应头:
    首包前发生确定性失败时,在 HTTP 头中注入该标志,促使客户端立即终止重试并暴露错误。
  3. 维持每 3 秒高频 SSE 保活心跳:
    向下游推送 : keepalive\n\n,持续刷新客户端活跃定时器。

四、版本选型建议与用户自愈指引

1. 推荐客户端版本

  • 稳定生产推荐:Claude Code v2.1.28x(流完成校验相对宽松,鲁棒性最高)。
  • 最新版本支持:Claude Code v2.1.293 ~ v2.1.295。使用最新版本时,必须配合部署包含 PR #3634 修复的 Antigravity 网关。

2. 用户操作指南 (故障自愈)

  • 中断当前重试:若终端出现 Retrying X/10 倒计时,直接按 Ctrl+C 强行终止,切勿等待 27 分钟;
  • 自愈中毒历史:若旧会话出现持续报 400 错误,在终端执行 /compact 压缩上下文或执行 /clear 新开会话,即可激活 5 级自愈管道清除脏数据。

五、9 月历史回退的反思与现代融合架构

  • 9 月版本回退的初衷与局限:
    9 月版本的核心思想是“中途断流优雅收尾”。但在前期调试中误判了报错场景,曾一度尝试完全透传底层网络错误,导致直接撞上 2.1.293 严格引入的 PU(e) 与 El 契约,引爆了 27 分钟死锁与会话中毒。
  • PR #3634 的现代融合解法:
    将 9 月“平稳收尾、严禁裸露断连”的稳态原则,与 2.1.293 的“三元组不变式契约(El === null + 正文兜底升级 output)”深度融合,彻底实现了全生命周期流控制闭环。

CLAUDE COWORK DEEP COMPACT SPEC (`docs/CLAUDE_COWORK_DEEP_COMPACT_SPEC.md`)

RFC:Claude Desktop Cowork Deep Compact

官方 Compact 机制逆向、50% 保留限制解除与低延迟深度压缩

  • Issue Type: Architectural Breakthrough / In-Depth RFC
  • Target Component: Claude Desktop Cowork / Antigravity Gateway
  • Implementation Status: Experimental / Opt-in
  • Primary Platform: macOS
  • Validated Claude Client Versions: August build / October build
  • Contributors: @cubelikeplayDaniel & Gemini & Claude Fable 5
  • Linked Issue: Fixes #3576

0. 摘要

本项目针对 Claude Desktop Cowork 模式中的上下文 Compact 机制进行了逆向分析,并构建了一套独立的 Deep Compact 实现。

项目的核心并不是重新实现一套摘要系统,也不是简单地伪造客户端显示的 Context 数值,而是:

利用 Claude 客户端原生 Compact 流程完成语义摘要,同时解除官方客户端在历史消息 pruning 阶段设置的“一次 Compact 后不得低于压缩前上下文约 50%”的保留限制,从而实现更激进的上下文裁剪。

整个系统分为两个职责完全不同的部分:

  • Gateway:负责触发、状态管理、Compact 请求放行以及恢复闭环。
  • Client Patch:负责修改官方 pruning 函数,使其突破官方 50% 保留下限。

因此,Binary Patch 并不是为了“让 Compact 发生”。

官方 Compact 的触发本身可以通过 Gateway 完成。

Patch 存在的根本原因是:

Gateway 可以控制 Compact 的进入条件与部分输入,但无法通过普通协议参数消除客户端 pruning 函数内部的 50% 保留限制。

实验结果进一步表明,在当前已验证的客户端和工作负载下,更激进的历史裁剪能够获得非常小的请求延迟,同时实际使用效果良好。

因此,本项目最终形成的并不是“另一个 Compact”,而是:

官方 Compact 摘要能力 + Gateway Reactive Compact 闭环 + 可选客户端 Deep Pruning Patch

三者组合形成的 Deep Compact 架构。


1. 原报告存在的核心问题

此前版本的报告已经正确描述了大量逆向结果,但存在一个影响架构理解的重要遗漏:

1.1 错误地把 Binary Patch 描述成 Deep Compact 的一般组成部分

原报告容易给人这样的印象:

text
Deep Compact = Gateway 触发 Compact + Binary Patch

这种表述不够准确。

正确关系应该是:

text
Reactive Compact = Gateway 触发官方 Compact

而:

text
Deep Compact = Reactive Compact + 解除官方 pruning 深度限制

后者才需要 Binary Patch。

也就是说:

Patch 不是 Compact 的触发器,而是 Compact 的“深度解除器”。


1.2 原报告没有明确指出官方 50% 保留限制

这是此前报告最重要的技术遗漏。

官方 Cowork Compact 并不是简单地:

text
计算一个目标 token 数 → 一直删除旧消息 → 直到达到目标

它内部存在一个重要约束:

一次 Compact 后,历史上下文的保留结果不能低于压缩前上下文的一定比例;在目前逆向与测试中确认的关键限制表现为约 50% 下限。

因此,即使 Gateway 通过 synthetic HTTP 400 提供更激进的 token gap,官方 pruning 函数最终仍然受到这一限制。

这意味着:

text
400 maximum 调得更小  ≠  真正解除 pruning 下限

两者不是同一件事情。


1.3 因此,“Gateway-only Deep Compact”并不能完整替代 Patch

此前报告提出过一个分级架构:

  • Level 0:正常 Gateway
  • Level 1:Reactive Compact
  • Level 2:Gateway-only Deep Compact
  • Level 3:Binary Patch

这个分级作为工程策略仍然有意义,但其中 Level 2 必须重新定义。

它可以:

  • 调整触发行为;
  • 调整客户端看到的 token gap;
  • 影响官方 pruning 的计算过程;
  • 在某些情况下使 Compact 行为发生变化。

但是:

它不能从根本上消除官方 pruning 函数内部的 50% 保留约束。

因此,真正意义上的 Deep Compact 仍然需要修改客户端 pruning 逻辑。


2. 项目真正解决的问题

Claude Cowork 的官方 Compact 主要解决:

上下文接近限制时,通过摘要历史信息释放上下文空间。

而本项目解决的是另一个问题:

官方 Compact 虽然能够释放上下文,但其历史消息保留策略过于保守,导致一次 Compact 后仍然保留大量历史上下文。

于是出现:

text
长上下文
   ↓
官方 Compact
   ↓
生成 Summary
   ↓
保留大量最近历史
   ↓
实际 Prompt 仍然较大
   ↓
请求延迟 / Token 消耗仍然较高

Deep Compact 的目标是:

text
长上下文
   ↓
官方 Compact
   ├── 官方 Summary
   │
   └── Deep Pruning
          ↓
      大幅减少历史消息
          ↓
       更小 Prompt
          ↓
       更低延迟

3. 已确认的 Cowork Compact 触发机制

本项目已经确认:

Compact 并不是简单根据本地配置中的 context window 数值直接触发。

关键触发链路为:

text
Claude Client
      │
      │ 普通请求
      ▼
   Gateway
      │
      │ 正常请求
      ▼
  Backend
      │
      │
      ▼
HTTP 400
"prompt is too long"
      │
      ▼
Claude Client
      │
      │ 识别为 Context Overflow
      ▼
进入原生 Compact 流程
      │
      ▼
向 Gateway 发起 Compact 请求

因此 Gateway 可以利用一个与客户端原生行为兼容的:

http
HTTP 400 Bad Request
prompt is too long

作为 Reactive Compact 的触发器。

这使得项目无需重新实现 Claude 的摘要逻辑。


4. 为什么必须使用官方 Compact

本项目没有选择:

text
Gateway 自己读取全部历史 → 自己调用 LLM → 自己生成 Summary → 自己重新组织上下文

原因是这样会重新实现 Claude 已经拥有的复杂 Compact 能力。

官方 Compact 已经处理:

  • 历史对话摘要;
  • 工具调用上下文;
  • 对话连续性;
  • 当前任务状态;
  • Cowork 特有的上下文结构。

因此本项目采取:

复用官方语义压缩,只修改“保留多少原始历史”的策略。

这是整个架构中非常重要的“最小改动”原则。


5. 官方 50% 保留限制

逆向过程中确认,官方 pruning 函数并不是无限制地删除旧消息。

其核心行为可以抽象为:

text
Input:
    当前完整消息历史
    当前 token gap
    Compact 所需上下文

Process:
    从历史消息组向后遍历
    计算可保留的消息组
    保证工具调用/结果等结构完整
    同时受到官方保留比例限制

Output:
    Summary
    +
    最近的一部分历史消息

关键限制:

text
Compact 后保留上下文 ≥ Compact 前上下文的一定比例 ≈ 50%

这就是 Deep Compact 真正需要突破的地方。


6. 为什么不能只把 HTTP 400 的 maximum 调小

这是本项目架构中最容易产生误解的地方。

例如:

  • 实际上下文:300K
  • Gateway 返回:prompt is too long: 300000 tokens > 20000 maximum

直觉上可能认为:

“既然 gap 已经非常大,客户端应该自然删除更多历史。”

但实际上:

text
400 maximum
      ↓
影响 pruning 输入
      ↓
官方 pruning algorithm
      ↓
仍然存在官方保留下限
      ↓
不能无限向下裁剪

因此:

修改 400 参数 ≠ 删除官方 50% 下限。

这也是为什么真正的 Deep Compact 需要进入客户端 pruning 函数本身。


7. Client Patch 的真正作用

Patch 的目标非常单一:

不改变官方 Compact 的摘要逻辑,只解除官方 pruning 函数对历史保留深度的限制。

换言之:

原始客户端:

text
Compact → Summary → Official pruning → ≥ 50% retention

Patch 后:

text
Compact → Summary → Official pruning → Custom retention target

例如工程目标可以设置为约:35K active-history budget。

需要强调:
35K 是工程目标,而不是 Claude 官方声明的上下文限制。

实际最终 Prompt 仍然可能包含:

  • system prompt;
  • tool schema;
  • Summary;
  • 当前用户消息;
  • 工具结果;
  • 其他固定上下文。

因此不能把 35K 简单等同于最终 API "input_tokens"。


8. 为什么直接修改官方 pruning 函数,而不是重写 Compact

这是整个项目最重要的工程原则之一:

只修改必要的控制点。

不修改:

  • Summary 生成逻辑;
  • Compact 请求格式;
  • 消息语义;
  • 工具调用结构;
  • 官方上下文组织方式。

只修改:

“Compact 后究竟保留多少历史消息”

因此:

text
Claude 官方能力 + 一个非常小的 pruning 行为修改 = Deep Compact

这比重新实现整个 Compact pipeline 的风险要低得多。


9. Message Group 完整性

Pruning 不能简单地按单条消息删除。

例如:

  • Assistant → Tool Call
  • Tool → Tool Result
  • Assistant → Next Message

这些消息之间具有结构关系。

因此 pruning 必须以:

完整 message group

作为基本裁剪单位。

否则可能产生:

  • Tool Call 存在,Tool Result 消失;
  • 或者 Tool Result 存在,对应 Tool Call 消失;
    从而破坏上下文结构。

因此 Patch 的目标不是随便删除消息,而是:

保持官方 message-group 语义 + 改变 group retention boundary


10. 官方 pruning 函数逆向

本项目已经对官方 pruning 函数进行了多版本逆向和验证。

不同 Claude Code / Claude Desktop 构建中观察到过不同的函数名称,例如:khf, rAt。

函数名称本身并不是可靠的识别依据。真正需要匹配的是其:

  • 控制流;
  • token gap 计算;
  • message group 遍历;
  • retention boundary;
  • 返回结果结构。

目前测试表明:

该函数的核心逻辑在已经测试的多个版本之间没有发生根本性变化。

但是这并不等于可以宣称“所有未来版本永远兼容”。目前客户端层面的实际验证仍主要集中在:

  • August build;
  • October build。

中间版本以及更早版本尚未完成完整验证。因此兼容性应定义为:

已验证版本兼容,而非理论上的全版本兼容。


11. Deep Compact 状态机

Compact 并不是单纯的 400 → Compact → 结束。实际存在一个非常重要的问题:

Compact 完成后的下一次请求仍然可能再次触发 400。

因此 Gateway 必须区分:普通 Context Overflow 与 Compact 后恢复请求。

推荐状态模型:

text
NORMAL
   │
   │ HTTP 400
   ▼
COMPACTION_REQUESTED
   │
   ▼
COMPACTION_IN_PROGRESS
   │
   │ Summary 完成
   ▼
POST_COMPACTION_RETRY
   │
   │ 一次性豁免
   ▼
NORMAL

而不是:看到 → 永久认为“已经 Compact”。


12. 为什么不能用静态 "" 判断状态

Compact 后的 Summary 会持续存在于上下文中。

因此:

text
if "<summary>" exists: already compacted

会产生错误状态:第一次 Compact 产生 Summary 后,后续所有请求都会被误认为“这是 Compact 后的请求”,导致防护失效。

正确方式应该是建立一次性的 Compaction Generation:

rust
struct CompactionGeneration {
    id: String,
    triggered_at: Instant,
    before_tokens: u32,
    summary_seen: bool,
    retry_consumed: bool,
}

并使用 atomic consume,保证并发请求不会同时获得 Compact 后豁免。


13. Double-400 问题

这是实现过程中发现的重要边界条件:

text
请求 A → 400 → Compact → 请求 B → 仍然超过 Gateway 判断阈值 → 再次 400

如果 Gateway 不知道请求 B 是 Compact 完成后的 recovery retry,那么客户端可能进入死循环:

text
Compact → 400 → Compact → 400 → Compact ...

因此 Gateway 必须维护:

  • Compaction Generation
  • One-shot Post-Compact Retry
  • Expiry (如 180 秒生命周期)
  • Atomic Consumption

14. "./compact" 的定位

./compact 不应该被描述为“Claude CLI 原生命令”。

更准确的定义是:

Cowork 环境中的显式 Compact intent / control-plane trigger。

它表达用户明确要求:“现在进行一次 Compact。” Gateway 再根据当前状态将其转换成实际 Compact 流程:

text
./compact
   ↓
Gateway Control Plane
   ↓
Synthetic 400 / Compact Trigger
   ↓
Claude Native Compact

这样可以保持用户意图与底层 HTTP 行为的分离。


15. Token Estimator

Gateway 需要判断当前上下文规模,因此实现了 Token Estimator。需要综合考虑:

  • 普通文本;
  • 中文;
  • ASCII;
  • tool schema;
  • tool result;
  • image;
  • Base64 media;
  • MCP 等复杂工具上下文。

但是:

Estimator 是控制逻辑,不应被描述为 API 最终真实 "input_tokens"。

例如一次实测:

  • 约 334K estimated tokens → 约 45.1K estimated tokens
  • 对应净释放约 289K(约 86.5%)

这是非常显著的上下文规模下降。但如果没有 API usage 数据,不能把这些数字直接称作 Anthropic API 返回的真实 "input_tokens"。


16. 实验结果:激进压缩的低延迟收益

这是此前报告中同样应该加强的部分。

Deep Compact 的目标并不是“压得越狠越厉害”,真正重要的是:

压缩后的上下文足够小,同时仍然保持任务连续性,而且请求延迟很低。

当前实验已经验证:

  • 更激进的历史裁剪可以大幅降低实际 Prompt;
  • 压缩后的任务连续性与使用效果良好;
  • 在测试工作负载中,压缩后请求取得了非常小的延迟;
  • 没有观察到与这种激进裁剪规模相对应的明显交互性能损失。

因此 Deep Compact 的价值不仅是“能塞进更大的上下文”,还包括:

text
更小 Prompt → 更少需要处理的历史上下文 → 更低请求延迟

这使 Deep Compact 从一个单纯的“Context Overflow workaround”变成了:

一种实际的上下文性能优化机制。


17. UI Context 与 Model Context 的分离

Deep Compact 不必删除用户界面中的完整历史。

可以形成:

text
UI Conversation
      │
      │ 完整保存
      ▼
用户仍可查看完整历史


Model Prompt
      │
      │ Deep Compact
      ▼
只发送:
Summary
+
必要的近期历史

因此:

“界面里看起来有多少历史”与“本次请求真正发送多少历史”是两个不同维度。

这也是 Deep Compact 能够取得较低延迟的重要原因之一。


18. 为什么 Deep Compact 不是简单的“删聊天记录”

普通清理(直接删除旧消息)会直接损失语义。

Deep Compact 的流转为:

text
旧历史 → 官方 Compact → Summary → 保留必要近期 Message Groups

因此被删除的是:

已经被 Summary 覆盖、且不再需要直接进入模型 Prompt 的原始历史。

而不是简单地“把前面的聊天记录砍掉”。


19. 模块独立性

Deep Compact 从设计开始就是独立模块。它不应该成为 Gateway 主流程的硬依赖,而应该是:

text
Core Gateway
      │
      ├── Normal Proxy
      │
      └── Optional Deep Compact

当 Deep Compact 未启用、Patch 失败、客户端版本不支持或 Gateway 状态异常时,都不应破坏普通代理功能。


20. Opt-in 与安全策略

Deep Compact 属于实验性客户端修改,因此采用 Opt-in(用户明确选择后才启用)。

同时遵循安全回滚流程:

text
原始客户端 → Backup → Patch

Patch 失败不应该导致整个 Gateway 不可用,而应该退化为:

text
Gateway 正常工作 + Deep Compact 不可用

这也是模块独立设计的重要意义。


21. Binary Patch 的边界

Binary Patch 并不意味着“任何 Claude Desktop 都可以随便修改”。需要考虑:

  • 客户端版本;
  • executable discovery;
  • app bundle 路径;
  • 文件锁;
  • 更新机制;
  • 签名;
  • integrity check;
  • macOS hardened runtime;
  • 重新签名;
  • patch offset;
  • 等长替换;
  • rollback。

目前 macOS 是主要实现平台。在 Windows/Linux 上:

pruning 函数本身的核心逻辑已经验证具有一致性,但客户端发现、路径锁定、签名/完整性以及平台特有工程仍未完成。

因此 Windows/Linux 目前不能描述成完整可用版本。


22. 为什么采用等长 Patch

如果修改后的机器码长度与原机器码保持一致:

text
Before: AAAA BBBB CCCC DDDD
After:  AAAA XXXX YYYY DDDD

则不会因为插入/删除字节导致后续 offset 全部改变,降低了 Patch 的复杂度。

但:

等长 Patch 不等于“没有完整性风险”。

它只能解决二进制布局变化问题,不能自动保证签名仍然有效、Hardened Runtime 行为不变、更新程序不覆盖等。因此必须将 Binary correctness 与 Platform integrity 分开讨论。


23. 当前兼容性状态

项目 状态
Cowork Compact 触发链 已验证
Synthetic HTTP 400 触发 已验证
Compact 请求进入 Gateway 已验证
官方 Summary 流程复用 已验证
官方 pruning 核心逻辑逆向 已验证
50% 保留限制 已验证为当前目标中的关键限制
Deep Pruning Patch 已实现/验证
激进压缩效果 已验证
激进压缩延迟 已验证为很小
August Claude Client 已验证
October Claude Client 已验证
中间版本 未完整验证
更早版本 未完整验证
macOS 工程 主要实现平台
Windows/Linux pruning 逻辑 已确认一致
Windows/Linux 客户端工程 尚未完成
Gateway 独立运行 已设计
Backup/Rollback 已设计
Opt-in 已设计

24. 测试矩阵

完整工程测试应至少覆盖:

Context Size

  • 20K
  • 50K
  • 100K
  • 200K
  • 300K
  • 500K

内容类型

  • 纯文本
  • 长对话
  • Tool Call
  • Tool Result
  • MCP
  • 图片
  • Base64
  • 大型单轮工具结果

状态机

text
正常请求 → 400 → Compact → Summary → Recovery Retry

并测试:

  • 重复 400
  • 并发请求
  • 网络断开
  • Compact 中断
  • 客户端重启
  • Gateway 重启
  • Patch 失败
  • Patch 回滚

25. 成功条件

不应该使用 num_msgs <= 10 作为成功标准,因为消息数量本身并不代表 Compact 是否成功。

更合理的语义条件是:

  • Summary 已生成
  • AND post_tokens < pre_tokens
  • AND 达到目标 reduction / budget
  • AND 属于当前 Compaction Generation
  • AND Recovery Retry 已正确完成

这比单纯检查消息数量可靠得多。


26. 三层验证体系

项目中的结论应该分为三种:

  • A. 已直接验证:例如 HTTP 400 可以触发 Cowork Compact。
  • B. 已在特定版本验证:例如 August Client, October Client。
  • C. 理论推导 / 尚未完成验证:例如所有未来 Claude Desktop 版本均兼容。

严禁把 C 类描述成 A 类。


27. 本项目真正的架构创新

本项目并不是简单修改 Claude,而是把 Compact 分成了几个独立层:

text
┌─────────────────────────────┐
│        User Intent          │
│          ./compact          │
└──────────────┬──────────────┘
               │
               ▼
┌─────────────────────────────┐
│       Gateway Control       │
│  400 / State / Generation   │
└──────────────┬──────────────┘
               │
               ▼
┌─────────────────────────────┐
│    Claude Native Compact    │
│        Summary Logic        │
└──────────────┬──────────────┘
               │
               ▼
┌─────────────────────────────┐
│    Patched Deep Pruning     │
│    Remove 50% lower bound   │
└──────────────┬──────────────┘
               │
               ▼
┌─────────────────────────────┐
│       Smaller Prompt        │
│       Lower Latency         │
└─────────────────────────────┘

其中每一层只承担一个职责。


28. “如非必要,勿增实体”原则

整个设计遵循非常明确的工程原则:

能复用官方机制,就不重新实现。

  • 不重新实现:Summary Generator
  • 不重新实现:Claude Compact Protocol
  • 不重新实现:Message Semantics
  • 只修改:Retention Boundary

Gateway 也只负责:Trigger, State, Recovery。这使整个系统的改动面尽可能小。


29. 对旧报告的最终勘误

原报告最主要的问题并不是技术方向错误,而是没有把“为什么需要 Patch”讲清楚。应修改以下表述:

原来的隐含逻辑

text
需要 Deep Compact → 所以修改 Binary

正确逻辑

text
需要触发 Compact → Gateway Synthetic 400 即可
需要官方摘要 → 直接复用 Native Compact
需要比官方 Compact 更激进 → 发现官方 pruning 存在约 50% 保留下限
需要突破该下限 → Patch pruning function
Patch 后 → 得到真正的 Deep Compact

因此:

Binary Patch 不是整个系统的起点,而是解决官方 Compact 深度不足的最小必要修改。

这是旧报告必须修正的核心。


30. 最终架构定义

最终可以将本项目定义为:

Claude Desktop Cowork Deep Compact 是一种基于 Gateway Reactive Compact 与可选客户端 pruning patch 的上下文压缩架构。它通过兼容 Claude 原生 Compact 的 HTTP 400 触发机制进入官方 Compact 流程,复用官方 Summary 能力,同时通过修改客户端历史消息 pruning 函数解除官方一次 Compact 后约 50% 的上下文保留下限,使系统能够采用更激进的历史裁剪目标。

其核心不是自己做一个 Compact,而是:

  • 让官方 Compact 继续负责“理解并总结过去”
  • 让 Gateway 负责“什么时候进入 Compact”
  • 让 Patch 负责“官方允许保留多少历史”

最终得到:

官方语义压缩 + 自定义历史保留深度 + 更小实际 Prompt + 低延迟


31. 结论

目前项目已经形成一个完整的、可解释的技术闭环:

text
Context Overflow
      ↓
Synthetic HTTP 400
      ↓
Claude Native Compact
      ↓
官方 Summary
      ↓
官方 pruning
      ↓
突破 50% retention limit
      ↓
Deep Pruning
      ↓
~35K 级工程目标
      ↓
显著减少实际 Prompt
      ↓
低延迟

实验已经证明:

在当前已验证的客户端版本和测试工作负载下,激进 Deep Compact 可以取得非常小的请求延迟,并保持良好的实际使用效果。

因此,本项目的技术价值并不是单纯“把上下文压得更小”。更准确地说,它实现的是:

在不重新实现 Claude Compact 语义摘要能力的前提下,解除官方历史保留策略的深度限制,并通过 Gateway 与客户端 Patch 的最小协作,使 Cowork 获得更激进、更低延迟的上下文管理能力。

当前版本定位为 Experimental / Opt-in:

  • macOS 为主要实现平台;
  • August / October 客户端已经验证;
  • 其他客户端版本仍需继续验证;
  • Windows/Linux 的 pruning 逻辑已确认,但客户端工程尚未完成;
  • 模块具有独立性;
  • Patch 失败不应影响普通 Gateway;
  • Backup / Rollback 作为客户端修改的基础安全机制。

在这些边界条件下,Deep Compact 已经从“逆向实验”进入了一个具有实际性能收益、明确技术边界和可工程化路径的架构原型阶段。


CLAUDE OPUS 46 INTEGRATION (`docs/CLAUDE_OPUS_46_INTEGRATION.md`)

Claude Opus 4.6 Thinking Integration

Overview

This document describes the integration of Claude Opus 4.6 Thinking model into the Antigravity Proxy.

Changes Made

Backend (Rust)

model_mapping.rs

  • Added direct mapping for claude-opus-4-6-thinking
  • Added alias mappings: claude-opus-4-6, claude-opus-4-6-20260201
  • Updated normalize_to_standard_id() to include the new model

request.rs

  • Updated should_enable_thinking_by_default() to auto-enable thinking for Opus 4.6 models

Frontend (TypeScript/React)

modelConfig.ts

  • Added claude-opus-4-6-thinking entry with protectedKey: 'claude-opus'

useProxyModels.tsx

  • Added model to the models array with group Claude 4.6

Usage

API Request Example

json
{
  "model": "claude-opus-4-6-thinking",
  "messages": [...]
}

Supported Aliases

  • claude-opus-4-6-thinking (canonical)
  • claude-opus-4-6
  • claude-opus-4-6-20260201

Notes

  • Thinking mode is auto-enabled for Opus 4.6 models
  • Quota protection uses the shared claude-opus key
  • No localization changes required (reuses existing keys)

CROSS VERSION TEST EVIDENCE (`docs/CROSS_VERSION_TEST_EVIDENCE.md`)

官方 Claude Desktop (macOS / darwin-arm64) 跨版本微创注入与修剪算法逆向实测证据报告

本报告为 Antigravity Tools (Deep Compact) 针对官方 Claude 客户端在跨度数十个公开发布版本下的自动化实机逆向与等长 AST 微创注入验证白皮书。
实测版本数量: 21 个(覆盖从 2.1.110 到最新 2.1.287 的全周期版本)
验证结论: 21 / 21 全部 100% 通过验证
生成时间: 2026-10-03
测试环境: macOS Darwin arm64 (Apple Silicon)


一、验证目标与核心结论

  1. AST 核心骨架 100% 稳定同构:
    实测覆盖的全部 21 个官方发布版本 中,尽管 esbuild / Rollup 打包生成的混淆函数名在不断漂移(见下方对照表:Gj1, h07, nI5, cy7, F07, qR5, T24, X62, M12, qM4, fG1, gC7, bC7, khf, G4e, k7e, uVe, XGe, MWe, pZe, yNt),但其修剪逻辑的核心 AST 骨架完全一致,特征匹配长度恒为严格等长的 135 字节。
  2. 50% 膨胀死穴全版本存在:
    在所有 21 个版本中,函数尾部均无一例外包含:
    javascript
    if (g >= n - 1) return Math.max(1, Math.floor(n / 2));
    直接证实了:无论官方哪个版本,只要未打补丁,其修剪算法必定强制保留前 50% 历史,单靠网络层 400 假报警绝对无法突破该下限!
  3. 135 字节等长原位注入(35k 活跃预算)跨版本 100% 成功:
    所有版本均成功原位替换为 35k 活跃上下文硬预算,且文件物理体积 0 偏移漂移,macOS codesign 递归重签名 100% 成功;
  4. 一键还原 0-diff 绝对无损:
    全部样本还原后校验 MD5,与官方 npm 纯净基线 100% 逐字节一致,证明方案绝对安全可逆。

二、21 个跨版本实测数据总览表

序号 官方版本 二进制体积 混淆函数名 入参映射 内部变量映射 物理字节偏移区间 等长替换校验 签名状态 还原 MD5 0-diff
1 2.1.110 192.4 MB Gj1 (H, _, q) acc=K, cnt=O, idx=T 0x48C2E08 - 0x48C2E8F ✅ 135B 等长 ✅ ad-hoc 合法 ✅ 0-diff 一致
2 2.1.120 204.1 MB HQ1 (H, _, q) acc=K, cnt=O, idx=T 0x49CDD90 - 0x49CDE17 ✅ 135B 等长 ✅ ad-hoc 合法 ✅ 0-diff 一致
3 2.1.131 207.3 MB He1 (H, _, q) acc=K, cnt=O, idx=T 0x49D4233 - 0x49D42BA ✅ 135B 等长 ✅ ad-hoc 合法 ✅ 0-diff 一致
4 2.1.140 196.5 MB AT5 (H, _, q) acc=K, cnt=O, idx=T 0xB5BF8D2 - 0xB5BF959 ✅ 135B 等长 ✅ ad-hoc 合法 ✅ 0-diff 一致
5 2.1.150 203.2 MB VAK (H, _, q) acc=K, cnt=O, idx=T 0xBFCB38A - 0xBFCB411 ✅ 135B 等长 ✅ ad-hoc 合法 ✅ 0-diff 一致
6 2.1.160 206.1 MB cy7 (H, _, q) acc=K, cnt=O, idx=T 0xC057953 - 0xC0579DA ✅ 135B 等长 ✅ ad-hoc 合法 ✅ 0-diff 一致
7 2.1.170 211.8 MB T07 (H, _, q) acc=K, cnt=O, idx=T 0xC2901CF - 0xC290256 ✅ 135B 等长 ✅ ad-hoc 合法 ✅ 0-diff 一致
8 2.1.181 205.2 MB h2i (e, t, n) acc=r, cnt=o, idx=s 0xBBA9094 - 0xBBA911B ✅ 135B 等长 ✅ ad-hoc 合法 ✅ 0-diff 一致
9 2.1.190 207.2 MB h8i (e, t, n) acc=r, cnt=o, idx=s 0xBD8FE64 - 0xBD8FEEB ✅ 135B 等长 ✅ ad-hoc 合法 ✅ 0-diff 一致
10 2.1.200 221.0 MB cKa (e, t, n) acc=r, cnt=o, idx=s 0xCDA1615 - 0xCDA169C ✅ 135B 等长 ✅ ad-hoc 合法 ✅ 0-diff 一致
11 2.1.210 230.3 MB XTu (e, t, r) acc=n, cnt=o, idx=i 0xD128C77 - 0xD128CFE ✅ 135B 等长 ✅ ad-hoc 合法 ✅ 0-diff 一致
12 2.1.220 245.0 MB Jsd (e, t, r) acc=n, cnt=o, idx=i 0xDE1A24C - 0xDE1A2D3 ✅ 135B 等长 ✅ ad-hoc 合法 ✅ 0-diff 一致
13 2.1.231 281.1 MB xup (e, t, r) acc=n, cnt=o, idx=i 0xFCC6007 - 0xFCC608E ✅ 135B 等长 ✅ ad-hoc 合法 ✅ 0-diff 一致
14 2.1.237 302.4 MB khf (e, t, r) acc=n, cnt=o, idx=i 0x111895AA - 0x11189631 ✅ 135B 等长 ✅ ad-hoc 合法 ✅ 0-diff 一致
15 2.1.245 358.7 MB e4n (e, t, n) acc=r, cnt=o, idx=s 0xC9C3B54 - 0xC9C3BDB ✅ 135B 等长 ✅ ad-hoc 合法 ✅ 0-diff 一致
16 2.1.250 196.9 MB Nbt (e, t, r) acc=o, cnt=u, idx=p 0x99889E3 - 0x9988A6A ✅ 135B 等长 ✅ ad-hoc 合法 ✅ 0-diff 一致
17 2.1.260 189.1 MB xYn (e, n, r) acc=o, cnt=d, idx=f 0x9CD8069 - 0x9CD80F0 ✅ 135B 等长 ✅ ad-hoc 合法 ✅ 0-diff 一致
18 2.1.270 197.9 MB Aer (e, n, r) acc=s, cnt=d, idx=m 0xA5617B1 - 0xA561838 ✅ 135B 等长 ✅ ad-hoc 合法 ✅ 0-diff 一致
19 2.1.280 207.2 MB pEt (e, n, r) acc=s, cnt=g, idx=h 0xAAD52C8 - 0xAAD534F ✅ 135B 等长 ✅ ad-hoc 合法 ✅ 0-diff 一致
20 2.1.285 213.5 MB WOt (e, n, r) acc=s, cnt=g, idx=h 0xAFCA8E5 - 0xAFCA96C ✅ 135B 等长 ✅ ad-hoc 合法 ✅ 0-diff 一致
21 2.1.287 217.3 MB yNt (e, n, r) acc=s, cnt=g, idx=h 0xB2E51C5 - 0xB2E524C ✅ 135B 等长 ✅ ad-hoc 合法 ✅ 0-diff 一致

三、各版本逆向细节与原位替换实测证据

1. 版本 `2.1.110` 实测证据

  • 官方包名: @anthropic-ai/[email protected]
  • 文件体积: 201733664 字节 (192.4 MB)
  • 原始 MD5: b08165d921ed1f653f539b51ab30f2e5
  • 识别混淆函数: function Gj1(H, _, q)
  • 物理偏移区间: 0x48C2E08 - 0x48C2E8F(精确 135 字节)
  • 内部变量映射: 累加器 acc=K,计数器 cnt=O,索引 idx=T

[官方原生 135 字节代码]:

javascript
function Gj1(H,_,q){let K=0,O=0;for(let T=_-1;T>=0;T--)if(K+=H[T],O++,K>=q)break;if(O>=_-1)return Math.max(1,Math.floor(_/2));return O}

[注入 35k 预算等长微创补丁 (含注释填充,严格 135 字节)]:

javascript
function Gj1(H,_,q){let s=0,g=0;for(let h=_-1;h>=0;h--)if(s+=H[h],g++,s>=35000)break;return g;}/*                                    */
  • 注入结果: 🟢 成功原位注入,文件总大小保持 201733664 字节(0 偏移漂移)
  • 代码重签名: 🔏 codesign --force --sign - 执行成功
  • 安全可逆性: ⚪ 一键还原后校验 MD5 值为 b08165d921ed1f653f539b51ab30f2e5,与官方发布版 100% 0-diff 完全对齐。

2. 版本 `2.1.120` 实测证据

  • 官方包名: @anthropic-ai/[email protected]
  • 文件体积: 213981440 字节 (204.1 MB)
  • 原始 MD5: 3ae72fdd1697ee29e967f7b71d6b4b52
  • 识别混淆函数: function HQ1(H, _, q)
  • 物理偏移区间: 0x49CDD90 - 0x49CDE17(精确 135 字节)
  • 内部变量映射: 累加器 acc=K,计数器 cnt=O,索引 idx=T

[官方原生 135 字节代码]:

javascript
function HQ1(H,_,q){let K=0,O=0;for(let T=_-1;T>=0;T--)if(K+=H[T],O++,K>=q)break;if(O>=_-1)return Math.max(1,Math.floor(_/2));return O}

[注入 35k 预算等长微创补丁 (含注释填充,严格 135 字节)]:

javascript
function HQ1(H,_,q){let s=0,g=0;for(let h=_-1;h>=0;h--)if(s+=H[h],g++,s>=35000)break;return g;}/*                                    */
  • 注入结果: 🟢 成功原位注入,文件总大小保持 213981440 字节(0 偏移漂移)
  • 代码重签名: 🔏 codesign --force --sign - 执行成功
  • 安全可逆性: ⚪ 一键还原后校验 MD5 值为 3ae72fdd1697ee29e967f7b71d6b4b52,与官方发布版 100% 0-diff 完全对齐。

3. 版本 `2.1.131` 实测证据

  • 官方包名: @anthropic-ai/[email protected]
  • 文件体积: 217349888 字节 (207.3 MB)
  • 原始 MD5: d84c46f3c60a146871a35e47c2ffba5b
  • 识别混淆函数: function He1(H, _, q)
  • 物理偏移区间: 0x49D4233 - 0x49D42BA(精确 135 字节)
  • 内部变量映射: 累加器 acc=K,计数器 cnt=O,索引 idx=T

[官方原生 135 字节代码]:

javascript
function He1(H,_,q){let K=0,O=0;for(let T=_-1;T>=0;T--)if(K+=H[T],O++,K>=q)break;if(O>=_-1)return Math.max(1,Math.floor(_/2));return O}

[注入 35k 预算等长微创补丁 (含注释填充,严格 135 字节)]:

javascript
function He1(H,_,q){let s=0,g=0;for(let h=_-1;h>=0;h--)if(s+=H[h],g++,s>=35000)break;return g;}/*                                    */
  • 注入结果: 🟢 成功原位注入,文件总大小保持 217349888 字节(0 偏移漂移)
  • 代码重签名: 🔏 codesign --force --sign - 执行成功
  • 安全可逆性: ⚪ 一键还原后校验 MD5 值为 d84c46f3c60a146871a35e47c2ffba5b,与官方发布版 100% 0-diff 完全对齐。

4. 版本 `2.1.140` 实测证据

  • 官方包名: @anthropic-ai/[email protected]
  • 文件体积: 206069664 字节 (196.5 MB)
  • 原始 MD5: afd59c75e7da5bac5fb25c1de70eb434
  • 识别混淆函数: function AT5(H, _, q)
  • 物理偏移区间: 0xB5BF8D2 - 0xB5BF959(精确 135 字节)
  • 内部变量映射: 累加器 acc=K,计数器 cnt=O,索引 idx=T

[官方原生 135 字节代码]:

javascript
function AT5(H,_,q){let K=0,O=0;for(let T=_-1;T>=0;T--)if(K+=H[T],O++,K>=q)break;if(O>=_-1)return Math.max(1,Math.floor(_/2));return O}

[注入 35k 预算等长微创补丁 (含注释填充,严格 135 字节)]:

javascript
function AT5(H,_,q){let s=0,g=0;for(let h=_-1;h>=0;h--)if(s+=H[h],g++,s>=35000)break;return g;}/*                                    */
  • 注入结果: 🟢 成功原位注入,文件总大小保持 206069664 字节(0 偏移漂移)
  • 代码重签名: 🔏 codesign --force --sign - 执行成功
  • 安全可逆性: ⚪ 一键还原后校验 MD5 值为 afd59c75e7da5bac5fb25c1de70eb434,与官方发布版 100% 0-diff 完全对齐。

5. 版本 `2.1.150` 实测证据

  • 官方包名: @anthropic-ai/[email protected]
  • 文件体积: 213070752 字节 (203.2 MB)
  • 原始 MD5: d24b8b561224ce85c715d4fb5eb0fdc7
  • 识别混淆函数: function VAK(H, _, q)
  • 物理偏移区间: 0xBFCB38A - 0xBFCB411(精确 135 字节)
  • 内部变量映射: 累加器 acc=K,计数器 cnt=O,索引 idx=T

[官方原生 135 字节代码]:

javascript
function VAK(H,_,q){let K=0,O=0;for(let T=_-1;T>=0;T--)if(K+=H[T],O++,K>=q)break;if(O>=_-1)return Math.max(1,Math.floor(_/2));return O}

[注入 35k 预算等长微创补丁 (含注释填充,严格 135 字节)]:

javascript
function VAK(H,_,q){let s=0,g=0;for(let h=_-1;h>=0;h--)if(s+=H[h],g++,s>=35000)break;return g;}/*                                    */
  • 注入结果: 🟢 成功原位注入,文件总大小保持 213070752 字节(0 偏移漂移)
  • 代码重签名: 🔏 codesign --force --sign - 执行成功
  • 安全可逆性: ⚪ 一键还原后校验 MD5 值为 d24b8b561224ce85c715d4fb5eb0fdc7,与官方发布版 100% 0-diff 完全对齐。

6. 版本 `2.1.160` 实测证据

  • 官方包名: @anthropic-ai/[email protected]
  • 文件体积: 216146880 字节 (206.1 MB)
  • 原始 MD5: 3b84333868e1d1c695c18d7c35247299
  • 识别混淆函数: function cy7(H, _, q)
  • 物理偏移区间: 0xC057953 - 0xC0579DA(精确 135 字节)
  • 内部变量映射: 累加器 acc=K,计数器 cnt=O,索引 idx=T

[官方原生 135 字节代码]:

javascript
function cy7(H,_,q){let K=0,O=0;for(let T=_-1;T>=0;T--)if(K+=H[T],O++,K>=q)break;if(O>=_-1)return Math.max(1,Math.floor(_/2));return O}

[注入 35k 预算等长微创补丁 (含注释填充,严格 135 字节)]:

javascript
function cy7(H,_,q){let s=0,g=0;for(let h=_-1;h>=0;h--)if(s+=H[h],g++,s>=35000)break;return g;}/*                                    */
  • 注入结果: 🟢 成功原位注入,文件总大小保持 216146880 字节(0 偏移漂移)
  • 代码重签名: 🔏 codesign --force --sign - 执行成功
  • 安全可逆性: ⚪ 一键还原后校验 MD5 值为 3b84333868e1d1c695c18d7c35247299,与官方发布版 100% 0-diff 完全对齐。

7. 版本 `2.1.170` 实测证据

  • 官方包名: @anthropic-ai/[email protected]
  • 文件体积: 222102816 字节 (211.8 MB)
  • 原始 MD5: faaa02e38ec63dca2a4a0c233637a6cf
  • 识别混淆函数: function T07(H, _, q)
  • 物理偏移区间: 0xC2901CF - 0xC290256(精确 135 字节)
  • 内部变量映射: 累加器 acc=K,计数器 cnt=O,索引 idx=T

[官方原生 135 字节代码]:

javascript
function T07(H,_,q){let K=0,O=0;for(let T=_-1;T>=0;T--)if(K+=H[T],O++,K>=q)break;if(O>=_-1)return Math.max(1,Math.floor(_/2));return O}

[注入 35k 预算等长微创补丁 (含注释填充,严格 135 字节)]:

javascript
function T07(H,_,q){let s=0,g=0;for(let h=_-1;h>=0;h--)if(s+=H[h],g++,s>=35000)break;return g;}/*                                    */
  • 注入结果: 🟢 成功原位注入,文件总大小保持 222102816 字节(0 偏移漂移)
  • 代码重签名: 🔏 codesign --force --sign - 执行成功
  • 安全可逆性: ⚪ 一键还原后校验 MD5 值为 faaa02e38ec63dca2a4a0c233637a6cf,与官方发布版 100% 0-diff 完全对齐。

8. 版本 `2.1.181` 实测证据

  • 官方包名: @anthropic-ai/[email protected]
  • 文件体积: 215193056 字节 (205.2 MB)
  • 原始 MD5: 394c2d1c78cc9d96c7f279ab5fc1e74d
  • 识别混淆函数: function h2i(e, t, n)
  • 物理偏移区间: 0xBBA9094 - 0xBBA911B(精确 135 字节)
  • 内部变量映射: 累加器 acc=r,计数器 cnt=o,索引 idx=s

[官方原生 135 字节代码]:

javascript
function h2i(e,t,n){let r=0,o=0;for(let s=t-1;s>=0;s--)if(r+=e[s],o++,r>=n)break;if(o>=t-1)return Math.max(1,Math.floor(t/2));return o}

[注入 35k 预算等长微创补丁 (含注释填充,严格 135 字节)]:

javascript
function h2i(e,t,n){let s=0,g=0;for(let h=t-1;h>=0;h--)if(s+=e[h],g++,s>=35000)break;return g;}/*                                    */
  • 注入结果: 🟢 成功原位注入,文件总大小保持 215193056 字节(0 偏移漂移)
  • 代码重签名: 🔏 codesign --force --sign - 执行成功
  • 安全可逆性: ⚪ 一键还原后校验 MD5 值为 394c2d1c78cc9d96c7f279ab5fc1e74d,与官方发布版 100% 0-diff 完全对齐。

9. 版本 `2.1.190` 实测证据

  • 官方包名: @anthropic-ai/[email protected]
  • 文件体积: 217273568 字节 (207.2 MB)
  • 原始 MD5: 572780d2ca0399d22b495c688eac2b84
  • 识别混淆函数: function h8i(e, t, n)
  • 物理偏移区间: 0xBD8FE64 - 0xBD8FEEB(精确 135 字节)
  • 内部变量映射: 累加器 acc=r,计数器 cnt=o,索引 idx=s

[官方原生 135 字节代码]:

javascript
function h8i(e,t,n){let r=0,o=0;for(let s=t-1;s>=0;s--)if(r+=e[s],o++,r>=n)break;if(o>=t-1)return Math.max(1,Math.floor(t/2));return o}

[注入 35k 预算等长微创补丁 (含注释填充,严格 135 字节)]:

javascript
function h8i(e,t,n){let s=0,g=0;for(let h=t-1;h>=0;h--)if(s+=e[h],g++,s>=35000)break;return g;}/*                                    */
  • 注入结果: 🟢 成功原位注入,文件总大小保持 217273568 字节(0 偏移漂移)
  • 代码重签名: 🔏 codesign --force --sign - 执行成功
  • 安全可逆性: ⚪ 一键还原后校验 MD5 值为 572780d2ca0399d22b495c688eac2b84,与官方发布版 100% 0-diff 完全对齐。

10. 版本 `2.1.200` 实测证据

  • 官方包名: @anthropic-ai/[email protected]
  • 文件体积: 231708784 字节 (221.0 MB)
  • 原始 MD5: a16a9eaa00e78c70a40240c40388abfa
  • 识别混淆函数: function cKa(e, t, n)
  • 物理偏移区间: 0xCDA1615 - 0xCDA169C(精确 135 字节)
  • 内部变量映射: 累加器 acc=r,计数器 cnt=o,索引 idx=s

[官方原生 135 字节代码]:

javascript
function cKa(e,t,n){let r=0,o=0;for(let s=t-1;s>=0;s--)if(r+=e[s],o++,r>=n)break;if(o>=t-1)return Math.max(1,Math.floor(t/2));return o}

[注入 35k 预算等长微创补丁 (含注释填充,严格 135 字节)]:

javascript
function cKa(e,t,n){let s=0,g=0;for(let h=t-1;h>=0;h--)if(s+=e[h],g++,s>=35000)break;return g;}/*                                    */
  • 注入结果: 🟢 成功原位注入,文件总大小保持 231708784 字节(0 偏移漂移)
  • 代码重签名: 🔏 codesign --force --sign - 执行成功
  • 安全可逆性: ⚪ 一键还原后校验 MD5 值为 a16a9eaa00e78c70a40240c40388abfa,与官方发布版 100% 0-diff 完全对齐。

11. 版本 `2.1.210` 实测证据

  • 官方包名: @anthropic-ai/[email protected]
  • 文件体积: 241509968 字节 (230.3 MB)
  • 原始 MD5: b6d57cbc618b3fdba8487848f5cfb823
  • 识别混淆函数: function XTu(e, t, r)
  • 物理偏移区间: 0xD128C77 - 0xD128CFE(精确 135 字节)
  • 内部变量映射: 累加器 acc=n,计数器 cnt=o,索引 idx=i

[官方原生 135 字节代码]:

javascript
function XTu(e,t,r){let n=0,o=0;for(let i=t-1;i>=0;i--)if(n+=e[i],o++,n>=r)break;if(o>=t-1)return Math.max(1,Math.floor(t/2));return o}

[注入 35k 预算等长微创补丁 (含注释填充,严格 135 字节)]:

javascript
function XTu(e,t,r){let s=0,g=0;for(let h=t-1;h>=0;h--)if(s+=e[h],g++,s>=35000)break;return g;}/*                                    */
  • 注入结果: 🟢 成功原位注入,文件总大小保持 241509968 字节(0 偏移漂移)
  • 代码重签名: 🔏 codesign --force --sign - 执行成功
  • 安全可逆性: ⚪ 一键还原后校验 MD5 值为 b6d57cbc618b3fdba8487848f5cfb823,与官方发布版 100% 0-diff 完全对齐。

12. 版本 `2.1.220` 实测证据

  • 官方包名: @anthropic-ai/[email protected]
  • 文件体积: 256908272 字节 (245.0 MB)
  • 原始 MD5: 30e4d87fac9c8a6b97f4cf33397a3e30
  • 识别混淆函数: function Jsd(e, t, r)
  • 物理偏移区间: 0xDE1A24C - 0xDE1A2D3(精确 135 字节)
  • 内部变量映射: 累加器 acc=n,计数器 cnt=o,索引 idx=i

[官方原生 135 字节代码]:

javascript
function Jsd(e,t,r){let n=0,o=0;for(let i=t-1;i>=0;i--)if(n+=e[i],o++,n>=r)break;if(o>=t-1)return Math.max(1,Math.floor(t/2));return o}

[注入 35k 预算等长微创补丁 (含注释填充,严格 135 字节)]:

javascript
function Jsd(e,t,r){let s=0,g=0;for(let h=t-1;h>=0;h--)if(s+=e[h],g++,s>=35000)break;return g;}/*                                    */
  • 注入结果: 🟢 成功原位注入,文件总大小保持 256908272 字节(0 偏移漂移)
  • 代码重签名: 🔏 codesign --force --sign - 执行成功
  • 安全可逆性: ⚪ 一键还原后校验 MD5 值为 30e4d87fac9c8a6b97f4cf33397a3e30,与官方发布版 100% 0-diff 完全对齐。

13. 版本 `2.1.231` 实测证据

  • 官方包名: @anthropic-ai/[email protected]
  • 文件体积: 294720528 字节 (281.1 MB)
  • 原始 MD5: dcae301dc3dba6a07b94cb16ea7dcc30
  • 识别混淆函数: function xup(e, t, r)
  • 物理偏移区间: 0xFCC6007 - 0xFCC608E(精确 135 字节)
  • 内部变量映射: 累加器 acc=n,计数器 cnt=o,索引 idx=i

[官方原生 135 字节代码]:

javascript
function xup(e,t,r){let n=0,o=0;for(let i=t-1;i>=0;i--)if(n+=e[i],o++,n>=r)break;if(o>=t-1)return Math.max(1,Math.floor(t/2));return o}

[注入 35k 预算等长微创补丁 (含注释填充,严格 135 字节)]:

javascript
function xup(e,t,r){let s=0,g=0;for(let h=t-1;h>=0;h--)if(s+=e[h],g++,s>=35000)break;return g;}/*                                    */
  • 注入结果: 🟢 成功原位注入,文件总大小保持 294720528 字节(0 偏移漂移)
  • 代码重签名: 🔏 codesign --force --sign - 执行成功
  • 安全可逆性: ⚪ 一键还原后校验 MD5 值为 dcae301dc3dba6a07b94cb16ea7dcc30,与官方发布版 100% 0-diff 完全对齐。

14. 版本 `2.1.237` 实测证据

  • 官方包名: @anthropic-ai/[email protected]
  • 文件体积: 317091168 字节 (302.4 MB)
  • 原始 MD5: d0b6d59c88e50a5b525c0ac20e62bfa6
  • 识别混淆函数: function khf(e, t, r)
  • 物理偏移区间: 0x111895AA - 0x11189631(精确 135 字节)
  • 内部变量映射: 累加器 acc=n,计数器 cnt=o,索引 idx=i

[官方原生 135 字节代码]:

javascript
function khf(e,t,r){let n=0,o=0;for(let i=t-1;i>=0;i--)if(n+=e[i],o++,n>=r)break;if(o>=t-1)return Math.max(1,Math.floor(t/2));return o}

[注入 35k 预算等长微创补丁 (含注释填充,严格 135 字节)]:

javascript
function khf(e,t,r){let s=0,g=0;for(let h=t-1;h>=0;h--)if(s+=e[h],g++,s>=35000)break;return g;}/*                                    */
  • 注入结果: 🟢 成功原位注入,文件总大小保持 317091168 字节(0 偏移漂移)
  • 代码重签名: 🔏 codesign --force --sign - 执行成功
  • 安全可逆性: ⚪ 一键还原后校验 MD5 值为 d0b6d59c88e50a5b525c0ac20e62bfa6,与官方发布版 100% 0-diff 完全对齐。

15. 版本 `2.1.245` 实测证据

  • 官方包名: @anthropic-ai/[email protected]
  • 文件体积: 376109392 字节 (358.7 MB)
  • 原始 MD5: 0da87ad56163079fc88dcaf9ce2cc14f
  • 识别混淆函数: function e4n(e, t, n)
  • 物理偏移区间: 0xC9C3B54 - 0xC9C3BDB(精确 135 字节)
  • 内部变量映射: 累加器 acc=r,计数器 cnt=o,索引 idx=s

[官方原生 135 字节代码]:

javascript
function e4n(e,t,n){let r=0,o=0;for(let s=t-1;s>=0;s--)if(r+=e[s],o++,r>=n)break;if(o>=t-1)return Math.max(1,Math.floor(t/2));return o}

[注入 35k 预算等长微创补丁 (含注释填充,严格 135 字节)]:

javascript
function e4n(e,t,n){let s=0,g=0;for(let h=t-1;h>=0;h--)if(s+=e[h],g++,s>=35000)break;return g;}/*                                    */
  • 注入结果: 🟢 成功原位注入,文件总大小保持 376109392 字节(0 偏移漂移)
  • 代码重签名: 🔏 codesign --force --sign - 执行成功
  • 安全可逆性: ⚪ 一键还原后校验 MD5 值为 0da87ad56163079fc88dcaf9ce2cc14f,与官方发布版 100% 0-diff 完全对齐。

16. 版本 `2.1.250` 实测证据

  • 官方包名: @anthropic-ai/[email protected]
  • 文件体积: 206479552 字节 (196.9 MB)
  • 原始 MD5: 0608214ee67da39e785d751b9589d3c9
  • 识别混淆函数: function Nbt(e, t, r)
  • 物理偏移区间: 0x99889E3 - 0x9988A6A(精确 135 字节)
  • 内部变量映射: 累加器 acc=o,计数器 cnt=u,索引 idx=p

[官方原生 135 字节代码]:

javascript
function Nbt(e,t,r){let o=0,u=0;for(let p=t-1;p>=0;p--)if(o+=e[p],u++,o>=r)break;if(u>=t-1)return Math.max(1,Math.floor(t/2));return u}

[注入 35k 预算等长微创补丁 (含注释填充,严格 135 字节)]:

javascript
function Nbt(e,t,r){let s=0,g=0;for(let h=t-1;h>=0;h--)if(s+=e[h],g++,s>=35000)break;return g;}/*                                    */
  • 注入结果: 🟢 成功原位注入,文件总大小保持 206479552 字节(0 偏移漂移)
  • 代码重签名: 🔏 codesign --force --sign - 执行成功
  • 安全可逆性: ⚪ 一键还原后校验 MD5 值为 0608214ee67da39e785d751b9589d3c9,与官方发布版 100% 0-diff 完全对齐。

17. 版本 `2.1.260` 实测证据

  • 官方包名: @anthropic-ai/[email protected]
  • 文件体积: 198289440 字节 (189.1 MB)
  • 原始 MD5: b71e21f261fa0dc23d0863a6954f4c97
  • 识别混淆函数: function xYn(e, n, r)
  • 物理偏移区间: 0x9CD8069 - 0x9CD80F0(精确 135 字节)
  • 内部变量映射: 累加器 acc=o,计数器 cnt=d,索引 idx=f

[官方原生 135 字节代码]:

javascript
function xYn(e,n,r){let o=0,d=0;for(let f=n-1;f>=0;f--)if(o+=e[f],d++,o>=r)break;if(d>=n-1)return Math.max(1,Math.floor(n/2));return d}

[注入 35k 预算等长微创补丁 (含注释填充,严格 135 字节)]:

javascript
function xYn(e,n,r){let s=0,g=0;for(let h=n-1;h>=0;h--)if(s+=e[h],g++,s>=35000)break;return g;}/*                                    */
  • 注入结果: 🟢 成功原位注入,文件总大小保持 198289440 字节(0 偏移漂移)
  • 代码重签名: 🔏 codesign --force --sign - 执行成功
  • 安全可逆性: ⚪ 一键还原后校验 MD5 值为 b71e21f261fa0dc23d0863a6954f4c97,与官方发布版 100% 0-diff 完全对齐。

18. 版本 `2.1.270` 实测证据

  • 官方包名: @anthropic-ai/[email protected]
  • 文件体积: 207500480 字节 (197.9 MB)
  • 原始 MD5: 5e3a18a36582b2a5a4e0a89176d71bdd
  • 识别混淆函数: function Aer(e, n, r)
  • 物理偏移区间: 0xA5617B1 - 0xA561838(精确 135 字节)
  • 内部变量映射: 累加器 acc=s,计数器 cnt=d,索引 idx=m

[官方原生 135 字节代码]:

javascript
function Aer(e,n,r){let s=0,d=0;for(let m=n-1;m>=0;m--)if(s+=e[m],d++,s>=r)break;if(d>=n-1)return Math.max(1,Math.floor(n/2));return d}

[注入 35k 预算等长微创补丁 (含注释填充,严格 135 字节)]:

javascript
function Aer(e,n,r){let s=0,g=0;for(let h=n-1;h>=0;h--)if(s+=e[h],g++,s>=35000)break;return g;}/*                                    */
  • 注入结果: 🟢 成功原位注入,文件总大小保持 207500480 字节(0 偏移漂移)
  • 代码重签名: 🔏 codesign --force --sign - 执行成功
  • 安全可逆性: ⚪ 一键还原后校验 MD5 值为 5e3a18a36582b2a5a4e0a89176d71bdd,与官方发布版 100% 0-diff 完全对齐。

19. 版本 `2.1.280` 实测证据

  • 官方包名: @anthropic-ai/[email protected]
  • 文件体积: 217254576 字节 (207.2 MB)
  • 原始 MD5: de782ed5fa747102302e57b0855aa135
  • 识别混淆函数: function pEt(e, n, r)
  • 物理偏移区间: 0xAAD52C8 - 0xAAD534F(精确 135 字节)
  • 内部变量映射: 累加器 acc=s,计数器 cnt=g,索引 idx=h

[官方原生 135 字节代码]:

javascript
function pEt(e,n,r){let s=0,g=0;for(let h=n-1;h>=0;h--)if(s+=e[h],g++,s>=r)break;if(g>=n-1)return Math.max(1,Math.floor(n/2));return g}

[注入 35k 预算等长微创补丁 (含注释填充,严格 135 字节)]:

javascript
function pEt(e,n,r){let s=0,g=0;for(let h=n-1;h>=0;h--)if(s+=e[h],g++,s>=35000)break;return g;}/*                                    */
  • 注入结果: 🟢 成功原位注入,文件总大小保持 217254576 字节(0 偏移漂移)
  • 代码重签名: 🔏 codesign --force --sign - 执行成功
  • 安全可逆性: ⚪ 一键还原后校验 MD5 值为 de782ed5fa747102302e57b0855aa135,与官方发布版 100% 0-diff 完全对齐。

20. 版本 `2.1.285` 实测证据

  • 官方包名: @anthropic-ai/[email protected]
  • 文件体积: 223821616 字节 (213.5 MB)
  • 原始 MD5: e46411d54292b2329124faee50276c80
  • 识别混淆函数: function WOt(e, n, r)
  • 物理偏移区间: 0xAFCA8E5 - 0xAFCA96C(精确 135 字节)
  • 内部变量映射: 累加器 acc=s,计数器 cnt=g,索引 idx=h

[官方原生 135 字节代码]:

javascript
function WOt(e,n,r){let s=0,g=0;for(let h=n-1;h>=0;h--)if(s+=e[h],g++,s>=r)break;if(g>=n-1)return Math.max(1,Math.floor(n/2));return g}

[注入 35k 预算等长微创补丁 (含注释填充,严格 135 字节)]:

javascript
function WOt(e,n,r){let s=0,g=0;for(let h=n-1;h>=0;h--)if(s+=e[h],g++,s>=35000)break;return g;}/*                                    */
  • 注入结果: 🟢 成功原位注入,文件总大小保持 223821616 字节(0 偏移漂移)
  • 代码重签名: 🔏 codesign --force --sign - 执行成功
  • 安全可逆性: ⚪ 一键还原后校验 MD5 值为 e46411d54292b2329124faee50276c80,与官方发布版 100% 0-diff 完全对齐。

21. 版本 `2.1.287` 实测证据

  • 官方包名: @anthropic-ai/[email protected]
  • 文件体积: 227808000 字节 (217.3 MB)
  • 原始 MD5: 5bc5ab235395a8ebdc5484ceb971891b
  • 识别混淆函数: function yNt(e, n, r)
  • 物理偏移区间: 0xB2E51C5 - 0xB2E524C(精确 135 字节)
  • 内部变量映射: 累加器 acc=s,计数器 cnt=g,索引 idx=h

[官方原生 135 字节代码]:

javascript
function yNt(e,n,r){let s=0,g=0;for(let h=n-1;h>=0;h--)if(s+=e[h],g++,s>=r)break;if(g>=n-1)return Math.max(1,Math.floor(n/2));return g}

[注入 35k 预算等长微创补丁 (含注释填充,严格 135 字节)]:

javascript
function yNt(e,n,r){let s=0,g=0;for(let h=n-1;h>=0;h--)if(s+=e[h],g++,s>=35000)break;return g;}/*                                    */
  • 注入结果: 🟢 成功原位注入,文件总大小保持 227808000 字节(0 偏移漂移)
  • 代码重签名: 🔏 codesign --force --sign - 执行成功
  • 安全可逆性: ⚪ 一键还原后校验 MD5 值为 5bc5ab235395a8ebdc5484ceb971891b,与官方发布版 100% 0-diff 完全对齐。

四、工程与安全启示

  1. Structural AST 正则具有跨版本韧性:
    测试证实,基于 Structural AST 循环骨架(脱敏混淆名与入参名)的正则,在面对 Rollup/esbuild 产生的函数名大范围漂移时,依然具备 100% 的捕获率;
  2. 无需为新版本维护静态函数名字典:
    维护者无需硬编码 khf 或 yNt,网关与客户端补丁工具可以通过 AST 结构化自动适配几乎所有次级微小更新;
  3. 安全隔离机制保障无损升级:
    备份存放在 Bundle 外部隔离目录,既满足 Gatekeeper 密封性,又确保了无论何时用户都能 100% 一键回滚到原生未打补丁状态。

测试执行者: Antigravity-Manager Automated Test Harness
测试用例: test_cross_version_deep_compact_matrix (21 samples)


HAR NATIVE PROTOCOL ALIGNMENT AND BUG AUDIT (`docs/HAR_NATIVE_PROTOCOL_ALIGNMENT_AND_BUG_AUDIT.md`)

Antigravity 抓包与原生应用逆向深度对齐分析报告

基于 `flows (6)`、原生应用 macOS App 及网关多协议转换全景代码审计


1. 调研背景与数据源 (Ground Truth Data Sources)

本报告基于两大权威第一手数据源与当前项目源码进行交叉比对:

  1. 抓包数据源 (flows (6)):
    • 捕获于 macOS 环境下原生 Antigravity IDE 真实网络流量;
    • 包含 2,060+ 个完整 HTTP/WebSocket 流,其中包含 359 次真实的 streamGenerateContent?alt=sse 上行上报与下行流式响应;
    • 覆盖真实多轮长会话(单会话高达 60+ 轮次)、模型中途切换(Gemini ↔ Claude 相互交替)、工具并发调用(26,695 次工具回执);
    • 涉及 6 大核心模型系列:gemini-3.8-flash-low, gemini-3.1-flash-lite, gemini-2.5-flash-lite, claude-sonnet-4-6, tab_flash_lite_preview, tab_jump_flash_lite_preview。
  2. 原生应用程序 (/Applications/Antigravity IDE.app):
    • 逆向分析其核心语言服务二进制组件:Contents/Resources/app/extensions/antigravity/bin/language_server_macos_arm(Google 内部 JetSki / Cortex 架构 Go 编译二进制);
    • 逆向分析前端扩展入口:Contents/Resources/app/extensions/antigravity/dist/extension.js;
    • 提取 fetchAvailableModels 接口返回的 33 个原生模型完整配置字典与元数据矩阵。
  3. 被审计代码库 (Antigravity-Manager in src-tauri/):
    • src-tauri/src/proxy/pipeline/inbound.rs(统一进站思考管线)
    • src-tauri/src/proxy/mappers/openai/request.rs(OpenAI 协议转译器)
    • src-tauri/src/proxy/mappers/claude/request.rs(Claude 协议转译器)
    • src-tauri/src/proxy/common/json_schema.rs(JSON Schema 清洗器)
    • src-tauri/src/proxy/cache_manager.rs(三层拆分上下文缓存管理器)
    • src-tauri/resources/official_models.json(官方模型配置表)

2. 抓包与原生请求参数全景图谱 (Native Request Specification)

2.1 外层信封 (Outer Envelope & Headers)

原生通信目标地址恒为:
POST https://daily-cloudcode-pa.googleapis.com/v1internal:streamGenerateContent?alt=sse

HTTP Headers 规范:

http
Host: daily-cloudcode-pa.googleapis.com
User-Agent: antigravity/ide/2.5.5 (aidev_client; os_type=darwin; arch=arm64)
Content-Type: application/json
Accept-Encoding: gzip
Transfer-Encoding: chunked

JSON 根级 Envelope 结构:

json
{
  "project": "aicode-consumers",
  "requestId": "agent/d08b2892-847c-41d9-9bd6-df72511b9f3c/1790410102432/0917011e-4959-4da9-8554-20499ed72869/3",
  "model": "gemini-3.8-flash-low",
  "userAgent": "antigravity",
  "requestType": "agent",
  "request": { ... }
}
  • project:固定为 "aicode-consumers"。
  • requestId:格式为 {requestType}/{client_instance_uuid}/{timestamp_ms}/{trajectory_id}/{step_index}。
  • requestType:枚举包含 agent(编程助手交互)、checkpoint(会话总结保存)、chat(普通聊天)、tab(行内补全)、tab_jump(跳转补全)。
  • request:核心内层载荷。

2.2 内部请求拓扑 (Inner Request Topology)

原生报文严格遵循以下键序(对齐 TPU 隐式前缀缓存命中):
$$\text{contents} \longrightarrow \text{systemInstruction} \longrightarrow \text{tools} \longrightarrow \text{toolConfig} \longrightarrow \text{labels} \longrightarrow \text{generationConfig} \longrightarrow \text{sessionId}$$

Labels 规范

json
{
  "last_step_index": "2",
  "model_enum": "MODEL_PLACEHOLDER_M320",
  "request_id": "0917011e-4959-4da9-8554-20499ed72869-2",
  "trajectory_id": "0917011e-4959-4da9-8554-20499ed72869",
  "used_claude": "false",
  "used_claude_conservative": "false",
  "used_non_gemini_model": "false"
}

3. 原生 17 个工具函数 Schema 与回执定义全景表

原生工具在请求报文的 tools 字段中,100% 格式化为单函数独立切片数组:
"tools": [ { "functionDeclarations": [ { "name": "tool1", ... } ] }, { "functionDeclarations": [ { "name": "tool2", ... } ] } ]
(原生 329 次带工具抓包中,独立切片占比 100%,聚合为一个大数组的占比为 0)。

所有参数 schema 均采用大写类型标记(OBJECT, STRING, INTEGER, BOOLEAN, ARRAY)。

3.1 完整工具函数定义矩阵

工具名称 关键参数 (properties) 必填字段 (required) 特殊约束 / 类型要求
run_command CommandLine, Cwd, WaitMsBeforeAsync, IsDaemon, RequestedTerminalID, RunPersistent Cwd, WaitMsBeforeAsync, CommandLine, toolSummary, toolAction WaitMsBeforeAsync 为 INTEGER 且必填;其余大驼峰
view_file AbsolutePath, ContentOffset, EndLine, IsSkillFile, StartLine AbsolutePath, toolSummary, toolAction ContentOffset, StartLine, EndLine 为 INTEGER
write_to_file TargetFile, Overwrite, CodeContent, Description, ArtifactMetadata TargetFile, Overwrite, CodeContent, Description, toolSummary, toolAction Overwrite 为 BOOLEAN 必填
replace_file_content TargetFile, Instruction, Description, AllowMultiple, TargetContent, ReplacementContent, StartLine, EndLine, TargetLintErrorIds TargetFile, Instruction, Description, AllowMultiple, TargetContent, ReplacementContent, StartLine, EndLine, toolSummary, toolAction 全 8 项关键参数必须全在;行号为 INTEGER
multi_replace_file_content TargetFile, Instruction, Description, ReplacementChunks, TargetLintErrorIds, ArtifactMetadata TargetFile, Instruction, Description, ReplacementChunks, toolSummary, toolAction ReplacementChunks 内部包含 StartLine, EndLine, TargetContent, ReplacementContent, AllowMultiple
grep_search SearchPath, Query, CaseInsensitive, Includes, IsRegex, MatchPerLine SearchPath, Query, toolSummary, toolAction SearchPath 与 Query 必填
list_dir DirectoryPath DirectoryPath, toolSummary, toolAction 路径大驼峰
manage_task Action, Input, TaskId Action, toolSummary, toolAction Action 枚举值:['list', 'kill', 'status', 'send_input']
browser_subagent TaskName, Task, TaskSummary, RecordingName, MediaPaths, ReusedSubagentId TaskName, Task, TaskSummary, RecordingName, toolSummary, toolAction 全 6 项必选
call_mcp_tool ServerName, ToolName, Arguments ServerName, ToolName, Arguments, toolSummary, toolAction Arguments 支持任意结构
list_resources ServerName toolSummary, toolAction 无必填内容参数
read_resource ServerName, Uri toolSummary, toolAction Uri 大驼峰
read_url_content Url Url, toolSummary, toolAction Url 大驼峰
search_web query, domain query, toolSummary, toolAction 反例注意:参数为小写 query, domain
ask_question questions toolSummary, toolAction 反例注意:小写 questions(嵌套 ARRAY[OBJECT])
schedule Prompt, CronExpression, DurationSeconds, IsDaemon, MaxIterations, TimerCondition Prompt, toolSummary, toolAction DurationSeconds 与 MaxIterations 声明为 STRING
generate_image Prompt, ImageName, AspectRatio, ImagePaths Prompt, ImageName, toolSummary, toolAction AspectRatio 支持 1:1, 16:9 等

铁律 1(全工具 UI 状态元数据):
全部 17 个原生工具的 required 列表中,无一例外全部强制包含 ["toolAction", "toolSummary"]。

  • toolAction(动宾短语,如 "Analyzing directory", "Editing file")
  • toolSummary(名词短语,如 "Directory analysis", "File edit")

铁律 2(工具执行回执信封标准):
在抓包提取的全部 26,695 次工具执行结果中:
functionResponse.response 100% 为只包含一个 key 的 JSON 对象:

json
{
  "functionResponse": {
    "id": "call_438678",
    "name": "run_command",
    "response": {
      "output": "Created At: 2026-09-28T...\nOutput:\n..."
    }
  }
}

response 中 0 次使用 "result" 键,100% 使用 "output" 键。


4. 核心不对等点深度剖析 (Multi-Model & Multi-Turn Asymmetries)

本节聚焦用户最关心的不同模型与多轮会话参数不对等。通过对原生通信抓包与二进制逻辑的挖掘,提炼出四大关键不对等机理:

4.1 核心不对等 1:`functionResponse` 角色的绝对模型相关性(最严重漏洞点)

通过对抓包内全部 26,695 个 functionResponse 进行分类统计,揭示出两大家族完全相反的协议规范:

text
Gemini 目标模型 (gemini-3.8-flash-low / gemini-3.1-flash-lite):
  - functionResponse in role: "model"  --> 25,817 次 (100.0%)
  - functionResponse in role: "user"   --> 0 次 (0.0%)

Claude 目标模型 (claude-sonnet-4-6):
  - functionResponse in role: "user"    --> 846 次 (100.0%)
  - functionResponse in role: "model"   --> 0 次 (0.0%)

原生会话地面真相:

  1. Gemini 协议流:
    • Turn 0 (user): 用户输入需求
    • Turn 1 (model): 模型输出 functionCall(挂载 thoughtSignature)
    • Turn 2 (model): 客户端回传 functionResponse(角色依然是 model)
    • Turn 3 (model): 模型输出最终回答文本(挂载 thoughtSignature)
    • Turn 4 (user): 用户下一轮追问
  2. Claude 协议流:
    • Turn 0 (user): 用户输入需求
    • Turn 1 (model): Claude 输出 functionCall(不带 thoughtSignature)
    • Turn 2 (user): 客户端回传 functionResponse(角色转为 user)
    • Turn 3 (model): Claude 接着输出结果
  3. 跨模型切换的全局重写 (Global Rewriting on Switch):
    抓包分析中,mixed_flows(单次请求中同时出现 user 和 model 的 functionResponse)的出现次数为 0。
    当原生会话中途从 Gemini 切换到 Claude 时,原生客户端将整个历史记录中所有的 functionResponse.role 统一重写为 user;反之切回 Gemini 时统一重写为 model。

4.2 核心不对等 2:思维链 (`thought`) 与思维签名 (`thoughtSignature`) 生命周期

  1. Gemini 家族:
    • 上游 SSE 流式响应:在第 1 个 chunk 中即返回 thoughtSignature。
    • 多轮历史请求回传:在抓包的 54,373 个 Gemini 历史模型轮次中,带 thought: true 文本块的次数为 0。原生客户端在向 Gemini 序列化历史消息时,主动剥离了冗长的思考过程文本,仅将 thoughtSignature 挂载在首个有效非思考 part(如 functionCall 或普通正文 text)上。
  2. Claude 家族:
    • 上游 SSE 流式响应:不产生 thoughtSignature,以 {"thought": true, "text": "..."} 文本块流式下发。
    • 多轮历史请求回传:Claude 历史轮次必须保留 {"thought": true, "text": "..."} 文本块(抓包中 33 次 Claude 思考块完整保留),且部件上绝对无 thoughtSignature。

4.3 核心不对等 3:`generationConfig` 极限与思考预算

通过抓取原生 fetchAvailableModels 权威数据字典与请求载荷比对:

模型代号 权威 maxOutputTokens 权威默认 thinkingBudget 思考控制参数 (thinkingConfig)
gemini-3.8-flash-low 65,536 1,000 includeThoughts: true, thinkingBudget: 1000
gemini-3.8-flash-medium 65,536 4,000 includeThoughts: true, thinkingBudget: 4000
gemini-3.8-flash-high 65,536 -1 (动态不设限) includeThoughts: true (无 budget 或 -1)
claude-sonnet-4-6 64,000(严禁超 64k) 1,024 includeThoughts: true, thinkingBudget: 1024
claude-opus-4-6-thinking 64,000 1,024 includeThoughts: true, thinkingBudget: 1024
gemini-2.5-flash-lite 1,024 (chat) / 65,535 -1 includeThoughts: true, thinkingLevel: "HIGH"
tab_flash_lite_preview 2,048 (req) / 4,096 (cat) 0 includeThoughts: false, thinkingBudget: 0
tab_jump_flash_lite_preview 2,048 (req) / 4,096 (cat) 0 includeThoughts: false, thinkingBudget: 0
gpt-oss-120b-medium 32,768 8,192 includeThoughts: true, thinkingBudget: 8192
gemini-pro-agent 65,535 10,001 includeThoughts: true, thinkingBudget: 10001

4.4 核心不对等 4:`toolConfig` 状态机模式

  • 常规智能体交互 (requestType == "agent"):
    只要声明了 tools,必配 toolConfig: { "functionCallingConfig": { "mode": "VALIDATED" } }。
  • 会话断点总结 (requestType == "checkpoint"):
    即使声明了工具,原生显式配置:toolConfig: { "functionCallingConfig": { "mode": "NONE" } }。
  • 代码补全与闲聊 (tab / tab_jump / chat):
    绝不包含 tools,且 toolConfig 为 null。

5. 上下文与缓存治理机制深度剖析 (Context & Caching)

5.1 隐式前缀缓存 (Implicit Prefix Caching) 的运行法则

抓包分析表明,原生通信中 cachedContent 或 cached_content 字段的出现次数为 0。
Google CloudCode PA 基础设施基于 TPU 集群内置的 透明隐式前缀缓存 (Implicit Prefix Caching)。
命中缓存的唯一条件是:前序字节 100% 逐字节(Byte-for-byte)一致。

破坏前缀缓存的常见陷阱:

  1. JSON 对象键序漂移:未进行确定性拓扑排序;
  2. 工具列表乱序:每次调用工具数组顺序发生变化;
  3. 工具描述文本被随意修改或清洗;
  4. 多轮历史中的思维链残留:如果保留了随机变化的旧 thinking 文本,将破坏后续所有轮次的前缀缓存。

5.2 现有三层缓存架构与实际原生需求的偏差

项目中 src-tauri/src/proxy/cache_manager.rs 设计了三层缓存:

  • Layer 1 (SI Cache): raw instructions → sanitized text
  • Layer 2 (Tools Cache): raw tools JSON → processed tools
  • Layer 3 (Prefix Tracker): 尝试查找 cache_name 并注入 cachedContent

审计发现:

  • Layer 3 在 openai/request.rs 第 1353 行尝试注入 "cachedContent": cache_name。
  • 这是公有云 Gemini API (v1beta) 的显式缓存机制,在内部受限的 v1internal 接口上根本不受支持。一旦注入必然导致上游直接 400 崩溃。

6. 协议转换与工具清洗函数现状与漏洞点审计

经过对 Rust 后端各模块的逐行代码审计,定位出以下关键缺陷与安全隐患:

🔴 缺陷 1 (P0 核心漏洞):`normalize_function_response_roles` 无差别将回执定死为 `role: "model"`

  • 定位:src-tauri/src/proxy/pipeline/inbound.rs 第 498 行 & 第 611 行。
  • 代码现状:
    rust
    pub fn normalize_function_response_roles(contents: &mut Vec<Value>) -> usize {
        // ...
        if all_response {
            let mut c = content;
            c["role"] = json!("model");
            out.push(c);
        }
    }
  • 漏洞后果:
    当用户使用 Claude 客户端(如 Claude Code CLI、Roo Code 等)请求 claude-sonnet-4-6 时,所有工具回执被无条件强行改成 role: "model"。而抓包实证表明,Google 上游给 Claude 的通道要求 functionResponse 必须在 role: "user"。这一错位直接导致发往 Claude 的报文结构违法,引发上游 400 校验拒绝或上下文理解错乱。
  • 修复方案:
    该函数必须接收 target_model: &str 参数。
    • 若 is_claude(target_model) 为真,则将连续工具回执归一化合并在 role: "user" 中;
    • 若为 Gemini 系列,则合并在 role: "model" 中。

🔴 缺陷 2 (P1 漏洞):工具回执对象键名硬编码为 `"result"` 而非 `"output"`

  • 定位:
  • 漏洞后果:
    原生模型接收到的所有原生工具回执 100% 为 {"output": "..."},模型系统提示词也明确约定通过 output 字段解析工具运行结果。客户端回传 "result" 属于协议方言不一致,极易诱发模型误判工具未正确执行、反复调用或参数幻觉。
  • 修复方案:
    在 inbound.rs 的通用清洗层进行无损归一化:若 response 包含 result 且不包含 output,自动将 result 平滑迁移至 output。

🔴 缺陷 3 (P1 隐患):显式 `cachedContent` 注入代码残留

  • 定位:src-tauri/src/proxy/mappers/openai/request.rs 第 1350-1363 行。
  • 代码现状:
    rust
    if let Some(cache_name) = cache_manager.lookup_prefix(&prefix_hash) {
        if let Some(req_obj) = final_body["request"].as_object_mut() {
            req_obj.insert("cachedContent".to_string(), json!(cache_name));
        }
    }
  • 漏洞后果:
    v1internal 根本没有公开的 cachedContents/create 接口,抓包中原生 Antigravity IDE 出现该参数的次数为 0。目前是由于 lookup_prefix 永远未存入有效 cache_name 才侥幸未触发异常。此代码属于典型的外来臆测实现,一旦被触发必致上游 400。
  • 修复方案:
    彻底移除对 cachedContent 的显式注入,全量依赖 Google TPU 集群的隐式字节流前缀缓存。

🟡 缺陷 4 (P2 缺陷):Gemini 多轮历史未清理思维链文本 (`thought: true`)

  • 定位:src-tauri/src/proxy/pipeline/inbound.rs 第 223 行。
  • 代码现状:
    管线仅剥离了占位符(如 ".", "...", 空字符串)思考块。对于客户端(如 Claude Code CLI 或自研前端)多轮带回的完整思考文本,依然作为 thought: true part 保留发给了 Gemini。
  • 漏洞后果:
    原生抓包证明 Gemini 历史回传中绝对不带任何历史思考文本(0 / 54,373)。保留这些文本会浪费巨大的输入上下文 token,且容易引发跨轮次签名错位或 400。
  • 修复方案:
    当目标模型是 Gemini 家族时,历史 model 轮的思考文本块一律丢弃,但必须提取其合法签名转移到该轮首个非思考部件(通常是 functionCall 或正文 text)上。

🟡 缺陷 5 (P2 缺陷):官方模型配置表缺少 `tab_flash_lite_preview`

  • 定位:src-tauri/resources/official_models.json。
  • 代码现状:
    配置表中仅存在 tab_jump_flash_lite_preview (MODEL_PLACEHOLDER_M28),遗漏了原生抓包中频繁出现的行内补全模型 tab_flash_lite_preview (MODEL_PLACEHOLDER_M19)。
  • 修复方案:
    在 official_models.json 补齐 tab_flash_lite_preview 及其元数据参数(maxOutputTokens: 4096, supportsThinking: false)。

🟡 缺陷 6 (P2 缺陷):`toolConfig` 强行覆盖忽略 `checkpoint` 模式

  • 定位:src-tauri/src/proxy/pipeline/inbound.rs 第 1095-1115 行。
  • 代码现状:
    管线只要判定有 tools,就一律无条件构造或覆盖为 {"mode": "VALIDATED"}。
  • 漏洞后果:
    在断点总结(checkpoint)请求中,原生必须配置 {"mode": "NONE"} 以防止总结阶段模型幻觉产生工具调用。管线强行覆盖为 VALIDATED 会破坏原生 checkpoint 行为。
  • 修复方案:
    检查已有 toolConfig.functionCallingConfig.mode,若已经是 NONE 则保留,不强行覆盖为 VALIDATED。

7. 优化实施路线图 (Actionable Implementation Plan)

遵循项目的 Pipeline First 核心架构原则,所有治理应在流水线阶段完成,确保四大协议适配器无需各写一套补丁:

text
客户端请求 (OpenAI / Claude / Gemini / Responses)
                       │
                       ▼
        四大协议适配器 (仅做协议语法映射)
                       │
                       ▼
    InboundThinkingPipeline (统一核心治理阶段)
    ┌──────────────────────────────────────────────────────────┐
    │ 1. normalize_tool_call_ids (call_... 归一化)             │
    │ 2. normalize_function_response_roles(contents, model)    │
    │    ├─ Claude 目标: 归一化为 role: "user"                │
    │    └─ Gemini 目标: 归一化为 role: "model"               │
    │ 3. normalize_function_response_envelope (result → output)│
    │ 4. 历史 thinking 文本过滤 (Gemini 剥离留签名, Claude 保留)│
    │ 5. align_google_request_prefix_topology_with_model       │
    │    ├─ 单函数独立切片排序 [ { functionDeclarations: [t] } ] │
    │    ├─ 大写 JSON Schema 类型 (OBJECT, STRING 等)          │
    │    ├─ 尊重既有 toolConfig mode (NONE 不覆盖)              │
    │    └─ 移除显式 cachedContent 注入死代码                   │
    └──────────────────────────────────────────────────────────┘
                       │
                       ▼
          Google v1internal 上游 (100% 协议同构)

通过落实上述实施方案,网关在应对不同客户端、跨模型动态切换以及复杂多轮工具调用场景时,将达到与原生 Antigravity IDE 100% 像素级对齐的鲁棒性。


JEIKL ANTIGRAVITY CLIENT CAPTURE (`docs/JEIKL_ANTIGRAVITY_CLIENT_CAPTURE.md`)

jeikl Windows Antigravity 客户端抓包

来源:@jeikl 本机 Windows 原生 Antigravity 客户端(不是 Antigravity IDE)。

  • 样本 1:docs/baogao.txt,模型 gemini-3.7-flash-high
  • 样本 2:docs/样本2.txt(原 docs/报告2 并发和串行交接触发.txt),模型 gemini-3.8-flash-high
  • 样本 3:docs/claude样本.txt,模型 claude-sonnet-4-6
  • 客户端标识:userAgent = antigravity,requestType = agent
  • 应用数据目录:C:\Users\Administrator\.gemini\antigravity
  • 对照:作者 PR #3541 的 docs/HAR_NATIVE_PROTOCOL_ALIGNMENT_AND_BUG_AUDIT.md 依据的是 Antigravity IDE HAR
  • 与作者差异的取少结论:docs/JEIKL_VS_AUTHOR_HAR_TAKE_DROP.md

三份报文的顶层键序、systemInstruction 骨架和 14 个工具声明相同。差别在目标模型家族:Gemini 的工具回执是 role: "model",Claude 的工具回执是 role: "user"。并行/串行形态两种家族都有。

顶层字段

信封(request 之外):

字段 样本 1 样本 2 样本 3
project aicode-consumers 同左 同左
userAgent antigravity 同左 同左
requestType agent 同左 同左
model gemini-3.7-flash-high gemini-3.8-flash-high claude-sonnet-4-6
requestId agent/4cabd908-.../21 agent/0b7ddb01-.../13 agent/3c49dacf-.../22

request 键序三份都是:

  1. contents
  2. systemInstruction
  3. tools
  4. labels
  5. generationConfig
  6. sessionId

没有 toolConfig,也没有 tool_config。三份 sessionId 都是 -3750763034362895579。

generationConfig:

Gemini(样本 1、2) Claude(样本 3)
maxOutputTokens 65536 64000
includeThoughts true true
thinkingBudget -1 1024

labels 样本 1(Gemini):

json
{
  "last_execution_id": "fb7b9c90-03c2-4e6f-983e-369ef8fc9e5e",
  "last_step_index": "20",
  "model_enum": "MODEL_PLACEHOLDER_M298",
  "request_id": "231efd36-2a13-4fea-ab31-04cc500a7ec2-9",
  "trajectory_id": "231efd36-2a13-4fea-ab31-04cc500a7ec2",
  "used_claude": "false",
  "used_claude_conservative": "false",
  "used_non_gemini_model": "false"
}

样本 3(Claude)同一套键,值为:

json
{
  "last_execution_id": "9648bd6f-4b24-495e-9b82-f5b59ecfa6cc",
  "last_step_index": "21",
  "model_enum": "MODEL_PLACEHOLDER_M35",
  "request_id": "a625a450-b0e2-435f-b99f-3c821f9149cc-8",
  "trajectory_id": "a625a450-b0e2-435f-b99f-3c821f9149cc",
  "used_claude": "true",
  "used_claude_conservative": "true",
  "used_non_gemini_model": "true"
}

原生 Claude 把 used_claude_conservative 设成 true。作者流水线写死成 false。

systemInstruction

约 25KB 一段文本。开头身份和用户信息三份相同(Conversation ID 各会话不同):

xml
<identity>
You are Antigravity, a powerful agentic AI coding assistant designed by the Google Deepmind team working on Advanced Agentic Coding.
...
User requests are enclosed within <USER_REQUEST> tags.
</identity>
<user_information>
The USER's OS version is windows.
...
e:\code\Antigravity-Manager -> lbjlaq/Antigravity-Manager
App Data Directory: C:\Users\Administrator\.gemini\antigravity
</user_information>

后面按顺序还有:user_rules(嵌入本仓库 AGENTS.md)、skills、subagents、messaging、conversation_transcript、artifacts、slash_commands、guidelines、communication_style。systemInstruction.role 是 user。

用户轮次正文包在 里,后面跟 `<ADDITIONAL_METADATA>`。换模型时跟。

工具声明

三份都是 14 项。每一项单独一个对象,functionDeclarations 里只有一个函数。顺序是客户端原序,不是按名字排序:

  1. view_file
  2. run_command
  3. manage_task
  4. send_message
  5. schedule
  6. invoke_subagent
  7. define_subagent
  8. manage_subagents
  9. write_to_file
  10. replace_file_content
  11. generate_image
  12. read_url_content
  13. search_web
  14. ask_question

实际用到的只有 run_command。参数有 CommandLine、Cwd、WaitMsBeforeAsync、toolAction、toolSummary。回执是 response.output。

Gemini 的调用 id 是 call_...;Claude 是 toolu_vrtx_01...。Gemini 样本 WaitMsBeforeAsync 是 5000,Claude 样本是 3000。

分类:按模型家族的回执角色

functionCall functionResponse
Gemini(样本 1、2) role: "model" role: "model"
Claude(样本 3) role: "model" role: "user"

这点和作者 IDE HAR 一致。桌面端也是按目标模型改写整段历史,不会在同一次请求里混用两种回执角色。

分类:并行

多次调用收在同一个 role: "model" content 里。对应回执收在紧跟着的一块里:Gemini 那块仍是 model,Claude 那块是 user。

样本 2 开头(Gemini 并行三次)

用户:请你稍作思考后并发调用三个echo helloworld函数

同一块三个 functionCall(id call_1180168 / call_1180171 / call_1180174),下一块三个 functionResponse,stdout 都是 helloworld。

样本 1 后段(Gemini 并行三次)

用户:请你并发调用三个工具hello

同一块三个 functionCall:Write-Output "hello 1/2/3"。下一块三个回执。

样本 3 开头和末段(Claude 并行三次)

用户:你好 请你稍作思考后并行调用三个工具试试 只需要输出helloworld就可以了

contents[2] 一块四个 part:thought + 三个 functionCall(echo / node -e / python -c)。签名在第一个 functionCall 上,思考块和后两个调用没有签名。

contents[3] 一块三个 functionResponse,role: "user"。

末段用户:这次并行试试。contents[14]/[15] 同样是三个 FC 在 model、三个 FR 在 user。

分类:串行

每一次调用和它的回执各占一块,按调用、回执交替,不把多次串行调用并成一块。

样本 1 中段(Gemini 串行)

用户:请你串行调用三个helloworld输出工具

# role 形态
7 model Write-Output "helloworld 1"
8 model 回执
9 model helloworld 2
10 model 回执
11 model helloworld 3
12 model 回执

样本 2 后段(Gemini 串行)

用户:请你再串行调用三个试试。contents[5]–[10] 同样是 FC / FR 交替,全是 model。

样本 3 中段(Claude 串行)

用户:请你再串行三个试试

# role 形态
6 model thought + 正文「第一步」+ 一个 functionCall(签名在正文上)
7 user 一个 functionResponse
8 model 正文「第二步」+ 一个 functionCall(无签名)
9 user 一个 functionResponse
10 model 正文「第三步」+ 一个 functionCall(无签名)
11 user 一个 functionResponse

Claude 串行轮可以在同一块里同时有正文和一次调用;仍然一次只出一个 functionCall,回执单独一块 user。

分类:连续 user

样本 3 开头两条都是 role: "user",没有插 model:

  • contents[0]:同一句并行请求,切到 Claude Opus 4.6 Thinking
  • contents[1]:同一句正文,再切到 Claude Sonnet 4.6 Thinking

两条独立 content,没有并成一块。作者删掉相邻 user 合并,这一点桌面端 Claude 样本支持。Gemini 两份没有连续 user。

思考块和签名

Gemini Claude
历史里的 thought 文本 样本 1 第一轮有;样本 2 无 三次工具轮都保留 thought 文本
thoughtSignature 挂在该轮第一个非思考 part(正文或首个 FC) 同样挂在该轮第一个非思考 part
思考块本身 不带签名 不带签名
回执 不带签名 不带签名

作者审计写 Claude「部件上绝对无 thoughtSignature」。样本 3 不是这样:并行时签名在首个 functionCall 上,串行第一步签名在正文上。流水线里对 Claude 轮会把非思考 part 上的签名剥掉,和这份桌面端报文不一致。

样本 1 完整对话

共 19 个 content。Cwd 都是 e:\code\Antigravity-Manager,WaitMsBeforeAsync 都是 5000。

# role 内容
0 user 你好 请你深度思考一下 回答我(切到 Gemini 3.8 Flash High)
1 model thought + 正文:你好!我已经准备好了...
2 user 好的 请你直接回我 不要思考(切到 Gemini 3.7 Flash High)
3 model 没问题!请问有什么我可以帮您的?
4 user 请你再不要思考回复我一次
5 model 好的,收到!随时听候您的吩咐。
6 user 请你串行调用三个helloworld输出工具
7–12 model 串行三次 run_command
13 model 串行完成说明
14 user 请你并发调用三个工具hello
15–16 model 并行三次 run_command
17 model 并行完成说明
18 user 好的 请你不思考回我

样本 2 完整对话

共 11 个 content。同一条请求里先并行、再串行。

# role 内容
0 user 请你稍作思考后并发调用三个echo helloworld函数
1–2 model 并行三次 echo helloworld
3 model 并行完成说明
4 user 请你再串行调用三个试试
5–10 model 串行三次 echo helloworld

样本 3 完整对话

共 16 个 content。WaitMsBeforeAsync 都是 3000。

# role 内容
0 user 并行 helloworld(切到 Claude Opus 4.6 Thinking)
1 user 同一句(再切到 Claude Sonnet 4.6 Thinking)
2 model thought + 并行三个 run_command
3 user 三个 functionResponse
4 model 并行完成说明(表格)
5 user 请你再串行三个试试
6–11 model/user 交替 串行三次
12 model 串行完成说明
13 user 这次并行试试
14 model thought + 并行三个 run_command
15 user 三个 functionResponse

和 IDE 报告 / PR #3541 对照

三份 Windows 桌面端报文都支持「一个函数一块」的 functionDeclarations。Gemini 回执 model、Claude 回执 user、Claude maxOutputTokens: 64000 + thinkingBudget: 1024、连续 user 保持独立,这些和作者 HAR 一致。

它们都不支持下面这些已经合进 beta 的写法:

  • 有 tools 时强制补 toolConfig.functionCallingConfig.mode = VALIDATED。三份都没有 toolConfig。
  • 把 tools 按 name 字典序重排。三份都是上面的原序。
  • 把 Claude 的 used_claude_conservative 写死成 false。样本 3 是 true。
  • 断言 Claude 历史「绝对无 thoughtSignature」,并在流水线里剥掉。样本 3 把签名挂在该轮第一个非思考 part 上,和 Gemini 同一套落点。

JEIKL VS AUTHOR HAR TAKE DROP (`docs/JEIKL_VS_AUTHOR_HAR_TAKE_DROP.md`)

jeikl 桌面端 vs 作者 Mac IDE:差异取少报告

对照双方原生 Antigravity 报文,只抽取有差异的字段;一致的沿用,有差异的取更少注入、更少改写、更少上下文的一侧。

  • jeikl:Windows 原生客户端,见 docs/JEIKL_ANTIGRAVITY_CLIENT_CAPTURE.md(样本 docs/baogao.txt、docs/样本2.txt、docs/claude样本.txt)
  • 作者:macOS Antigravity IDE HAR,见 docs/HAR_NATIVE_PROTOCOL_ALIGNMENT_AND_BUG_AUDIT.md,已随 PR #3541 合进 beta

「少」指少发给上游的字节和少做的网关发明。签名不是可删字段:思维链连续靠 thoughtSignature,思考正文可以少,签名不能少。


取舍原则(已拍板的三例)

# 差异 作者 Mac IDE jeikl 桌面端 取少 理由
1 历史是否回传 thought 文本 Gemini 历史 0 次回传;Claude 必须留 Gemini / Claude 都带回 Gemini 不回传。Claude 原文保留。 签名都在第一个非思考 part,不压成 ...。
2 toolConfig agent 有 tools 就配 VALIDATED 三份 agent 都没有该字段 jeikl(不发) 少一个对象。Windows agent 无此字段仍能跑工具。不要为 IDE 去发明。
3 Claude / Gemini 回执角色 Gemini FR → model;Claude FR → user;切模型时整段历史统一改写 完全一致 作者(与 jeikl 相同) 无差异,按目标模型改写。

其余差异与最终取舍

A. 拓扑与工具声明

项 作者 jeikl 取舍
每个工具单独 {functionDeclarations:[一个]} 329 次带工具全是切片 三份 14 项全是切片 一致,留切片。网关把客户端工具切成一块一函数即可。
tools 按 name 字典序重排 代码里 sort_by 客户端原序:view_file → run_command → … jeikl(不排序)。排序是多出来的改写,且和作者自己说的「工具乱序打前缀缓存」相反。稳定前缀靠保持本次请求的原序,不是每次改成字母序。
内层键序插入 toolConfig contents → SI → tools → toolConfig → labels → gc → sessionId contents → SI → tools → labels → gc → sessionId jeikl。不插入不存在的键。
工具清单 17 vs 14 IDE 多 grep_search、list_dir、browser_subagent、MCP 等 桌面端是 subagent / send_message 一套 透传客户端声明,不补 IDE 工具。少注入。
functionResponse.response 键名 100% output 100% output 一致,留 output。result 方言迁到 output。
并行 FC 同块、并行 FR 下一块 有 有(Gemini FR 块为 model,Claude FR 块为 user) 一致
串行 FC/FR 交替、不合并 有 有 一致。只合并「被客户端拆散的并行 FC」,不要把串行并成一块。
连续 user 保持独立 IDE 有;PR 已删相邻 user 合并 样本 3 开头两条 user(换模型)独立 一致,不合并(少改写)

B. 思考与签名(和原则 1 配套)

项 作者 jeikl 取舍
历史 thought 文本 Gemini 剥;Claude 必须留 两家都留 Gemini 不回传思考正文。Claude 原文保留,不压成 ...。 签名都在该轮第一个非思考 part。
Gemini 签名落点 该轮第一个非思考 part(FC 或正文) 相同 一致,留
Claude 是否带 thoughtSignature 审计写「绝对无」;代码对 Claude 剥签名 与 Gemini 同一落点:并行挂首个 FC,串行第一步挂正文 jeikl(留签名)。这不是「多发正文」,是连续锚。作者这条和原则 1 矛盾,不能跟。
思考块本身带签名 不带 不带 一致,思考块不挂签
回执带签名 不带 不带 一致
includeThoughts agent 为 true 同 一致
Gemini high thinkingBudget -1 或不设 -1 一致
Claude thinkingBudget / maxOutputTokens 1024 / 64000 同 一致,留作者口径
flash low/medium 默认 1000 / 4000 来自 fetchAvailableModels 无对应样本 留作者模型字典。不是桌面 vs IDE 的 agent 拓扑差。

C. labels 与信封

项 作者 jeikl 取舍
信封 project / userAgent JSON / requestType aicode-consumers / antigravity / agent 同 一致
HTTP User-Agent antigravity/ide/... darwin; arm64 Windows 桌面,不是这段 不要抄 IDE UA。JSON 里继续 userAgent: antigravity 即可。
used_claude / used_non_gemini_model 随目标模型 同 一致,按目标模型填
used_claude_conservative 代码写死 false(注释「关掉」) Gemini false;Claude true 少改写:不要写死 false。Gemini 填 false,Claude 填 true(跟 jeikl 桌面端)。作者这是产品开关,不是双方报文的最小公约。
last_execution_id 审计样例没有 三份都有,且排在 labels 最前 不发明。没有真实 id 就不造;有则原样留下。不要为对齐 IDE 样例删掉已有字段。
model_enum 官方表占位符 有(Claude M35,Gemini M298/M318) 有官方表就填,不编造
sessionId 有 三份同一个数值 id 有则透传,网关不要改成随机 UUID 去「对齐」
safetySettings 伪造 4 项 OFF 官方默认不带 不带 一致,不发
显式 cachedContent IDE 0 次;作者主张删注入 无 一致,不注入(取少)

D. 作者有、jeikl 这批 agent 样本没有的

项 说明 取舍
requestType=checkpoint 配 toolConfig.NONE 仅 IDE 审计;jeikl 无 checkpoint 包 agent 路径不发 toolConfig(原则 2)。checkpoint 若仍要禁工具,再单独加 NONE,不要绑 VALIDATED。未抓到之前不在 agent 上预埋。
tab / chat 无 tools IDE 审计 与「无工具就不发 toolConfig」相容,取少
调用 id:call_... vs toolu_vrtx_01... IDE 示例偏 call_;jeikl Gemini 用 call_,Claude 用 toolu_vrtx_ 少改写:已有 id 透传。不要把 Claude 的 toolu_vrtx_ 强行改成 call_。缺 id 再补。
WaitMsBeforeAsync 5000 vs 3000 客户端自己填 透传,网关不改

E. 签名隔离(与报文拓扑无关)

项 取舍
initial_session_sig 进循环前快照 留。防跨轮串扰,不是多发字段。
尾轮会话签名放行 留。少误杀。
测试 TEST_CONFIG_LOCK 留。与协议无关。

对 PR #3541 已合代码的含义

应留下:

  • 工具切片(一块一函数)
  • 按 target_model 把 FR 改成 Claude user / Gemini model
  • 回执信封 result → output
  • 连续 user 不合并
  • 并行 FC/FR 打包
  • Gemini 历史不回传思考正文;Claude 思考正文原文保留。签名都挂在该轮第一个非思考 part
  • Claude 64000 / 1024,Gemini high -1
  • 签名快照隔离、测试锁

应收回或不要做(相对已合进 beta 的写法):

  • 有 tools 就注入 toolConfig.VALIDATED
  • tools 按名字排序
  • 为插入 toolConfig 改内层键序
  • 对 Claude 剥 thoughtSignature
  • 把 used_claude_conservative 写死为 false
  • 把调用 id 一律改成 call_...

一句话

官方认的是切片工具、按模型改回执角色、签名连续;不认的是多出来的 toolConfig、重排工具、思考正文、以及把 Claude 签名剥掉。差异取少:少发思考块、不发 toolConfig、回执角色跟作者(也跟 jeikl)那套 Claude/Gemini 分工。


RELEASE GUIDE (`docs/RELEASE_GUIDE.md`)

Antigravity Tools 发版操作指南 (Release SOP)

发版详细规程。提交门禁与发版红线以根目录 AGENTS.md 为准。


一、流程概览

text
[通道 A: 正式版发布]
Pre-flight ─► checkout main ─► npm run bump <patch|minor> ─► 补充日志 ─► git push origin main ─► 打 Tag vX.Y.Z ─► 自动发布 Latest Release

[通道 B: Beta 预发布]
Pre-flight ─► checkout beta ─► npm run bump beta ──────────► 补充日志 ─► git push origin beta ─► 打 Tag vX.Y.Z-beta.N ─► 自动发布 Pre-release (隔离无感)

通道隔离与维护者协作原则:

  • 正式版通道 (Main):main 为绝对纯净正式打版分支,仅发布纯数字正式版本(如 v4.7.14),流水线严格拦截任何带 - 的预发标签。
  • 预览版通道 (Beta):beta 为独立预发布打版分支,所有预发测试版本(如 v4.7.14-beta.1、-cleaned 等)在此提交并由 beta 触发独立构建。预发布产物自动标记为 Pre-release 且绝不打 Latest,完全不影响正式版主用户更新。
  • 新更改优先暂存验证 (Staging on Beta First):凡涉及新功能、重大重构或高风险修复,必须主动询问维护者是否先在 beta 分支进行修改与验证。待验证稳定(或发布 Beta 预览版内测确认)后,方可合并进入 main。

二、操作步骤

第 0 步:发版前预检 (Pre-flight)

确保工作区干净,且对将被标记的提交执行与 CI 完全一致的预检命令:

bash
git checkout main && git pull origin main
git status          # 应显示 nothing to commit, working tree clean

cd src-tauri
cargo fmt -- --check
cargo clippy --all-targets --all-features
cd ..
npm run build

CI 门禁已全量覆盖 main 与 beta 分支。正式发布前确保在 main 预检通过,Beta 预发前确保在 beta 预检通过。

第 1 步:版本号原子同步

scripts/bump-version.mjs 一键同步全仓库版本号并生成 CHANGELOG 骨架:

场景 命令 示例
补丁(Bugfix / 性能) npm run bump patch 4.7.13 → 4.7.14
次版本(新增特性) npm run bump minor 4.7.13 → 4.8.0
主版本(破坏性变更) npm run bump major 4.7.13 → 5.0.0
预发布递增 npm run bump beta 4.7.13 → 4.7.14-beta.1(beta.1 → beta.2)
指定衍生版本 npm run bump 4.7.14-cleaned 同基线双版本(-beta / -cleaned / -rc)
指定任意合法 SemVer npm run bump 4.8.0 —

可选参数:--dry-run 仅演练、不写盘;--commit 自动生成 chore(release): bump version to ... 提交。

同步范围:package.json、package-lock.json(根版本镜像,两处)、src-tauri/Cargo.toml、src-tauri/Cargo.lock、src-tauri/tauri.conf.json、Casks/antigravity-tools.rb、README.md、README_EN.md、src/components/layout/MiniView.tsx、src/pages/Settings.tsx、CHANGELOG.md、CHANGELOG_EN.md。

版本串匹配采用结构锚定而非精确匹配当前版本号:package-lock.json 按字段位置锚定,README 按 (v数字…) / Version-数字…-blue 形态锚定。这样即使历史轮次(如预发布跳过 README)造成版本串滞后,下一轮同步仍能正确命中。

内置防呆:目标版本必须严格高于当前版本,否则红色拦截,杜绝版本回退。

预发布版本号形态:npm run bump beta 生成 X.Y.Z-beta.N(首次为 beta.1,后续递增为 beta.2)。该版本串同时决定脚本插入的 CHANGELOG 骨架标题与后续 Tag 名,三者必须完全一致(详见第 2 步提示与第 4 步)。

第 2 步:回溯提交与补充更新日志

在填写更新日志前,必须以 Git 提交历史与已合入 PR 为客观事实依据进行完整回溯,严防遗漏贡献者署名或 Issue 关联:

bash
# 1. 扫描上个版本以来的全部提交、Author 与 Co-Authored-By 署名
git log $(git describe --tags --abbrev=0)..HEAD --format="Commit: %h | %an <%ae> | %s%n%(trailers:key=Co-Authored-By)"

# 2. 列出在此期间合并的 PR 与关联 Issue
gh pr list --state merged --limit 20

根据盘点结果,在脚本插入的版本骨架中填写核心亮点:

  • 强制关联 Issue / PR:条目标题必须包含对应的来源单号(如 (PR #3504) 或 (Fixes #3499, #3501));
  • 强制行内致谢贡献者:从提交历史和 PR 中识别出的所有外部贡献者,必须以 (Thanks to @username) 形式显式标注在对应条目上。Release 页面的 Contributors 头像列表由此自动提取生成;
  • 格式示例:
markdown
*   **版本演进**:
    *   **v4.7.14 (2026-09-23)**:
        -   **[核心分类] 功能重构与优化 (PR #3504)**:
            -   **功能详述**: 核心实现说明。
        -   **[核心分类] 涉及外部贡献的修复 (Fixes #3508, Thanks to @username)**:
            -   **功能详述**: 致谢与修复说明。
  1. 标题必须与 Tag 逐字符一致:流水线用 awk 以 tag 名(github.ref_name,含 v 前缀)匹配 CHANGELOG 标题行,v 前缀与完整预发布后缀都要一字不差。npm run bump beta 自增出的版本号形如 X.Y.Z-beta.1,因此 Tag 应为 vX.Y.Z-beta.1(而非 vX.Y.Z-beta),标题也须写成 **vX.Y.Z-beta.1 (日期)**。不匹配时正文会静默退化为占位文案 See the assets to download this version and install.。
  2. 已开启 generateReleaseNotes: true,GitHub 会自动追加 What's Changed 与 New Contributors(含 PR 链接与贡献者主页)。
  3. 测试版不进入 README:Tag 含 - 的预发布 / 衍生版本(-beta / -cleaned / -rc 等)只在 CHANGELOG.md 记录,不得写入任何 README 的版本号、Shields 徽章或「最新版本」段落。README 始终只反映最新正式版。bump-version.mjs 已内置该判定:预发布版本自动跳过两个 README,仅同步其余版本配置文件。
  4. 贡献者致谢写在条目行内:不单列致谢块,外部贡献者统一以 (Thanks to @username) 标注在对应条目上。Release 页的 Contributors 头像列表由正文中的 @username 自动生成 —— 增删提及即增删头像,条目内没有 @username 时该列表为空。
  5. 正式版发版强制同步双语 README 更新日志:发布正式版时,除了更新 CHANGELOG.md(及 CHANGELOG_EN.md),还必须同步将最新正式版的重要更新摘要更新至 README.md(## 📝 更新日志)与 README_EN.md(## 📝 Changelog),严禁仅更新 CHANGELOG 而遗漏 README 首页的最新版本说明。

第 3 步:提交并推送目标分支

bash
# 正式版:提交并推送到 main 分支
git checkout main
git add -A
git commit -m "chore(release): bump version to 4.7.14 and update changelog"
git push origin main

# Beta 预发版:提交并推送到 beta 分支(严禁推到 main,保持 main 纯净)
git checkout beta
git add -A
git commit -m "chore(release): bump version to 4.7.14-beta.1 and update changelog"
git push origin beta

第 4 步:打 Tag 并推送

bash
# 正式版:从 main 打纯数字 Tag(触发正式发布,更新 Latest)
git tag v4.7.14
git push origin v4.7.14

# Beta 预发版:从 beta 打预发布 Tag(触发隔离构建,不更新 Latest)
git tag v4.7.14-beta.1
git push origin v4.7.14-beta.1

Tag 串必须与 CHANGELOG 中该版本的标题逐字符相同(v 前缀 + 完整预发布后缀),否则 Release 正文会退化为占位文案。

分支与标签严格门禁:流水线内置分支与标签匹配断言。

  • 带有 - 的预发布 Tag 若打在 main 独有提交上,流水线立即拦截阻断,拒绝构建与发布;
  • 不带 - 的正式版 Tag 若打在 beta 独有提交上,流水线立即拦截阻断。
  • 预发布版本自动降级为 NSIS、不更新 Latest、不更新正式用户的 updater.json,主用户客户端绝不受任何影响。

第 5 步:验收

推送 Tag 后流水线自动接管,无需人工干预:

  1. 进度:仓库 Actions 页的 Release 工作流;
  2. 构建矩阵:Windows(.msi / NSIS .exe)、macOS(.dmg,Apple Silicon 与 Intel 双架构)、Linux(.AppImage / .deb / .rpm)、Docker 多架构镜像推送 Docker Hub;产出 updater.json(配置 TAURI_SIGNING_PRIVATE_KEY 时含签名);
  3. 验收:约 10~15 分钟后在 Releases 页确认 Antigravity Tools vX.Y.Z 及附件齐全。

三、异常与救急

1. 删除误打的 Tag

bash
git tag -d v4.7.14
git push origin :refs/tags/v4.7.14

2. 防呆保护报错

报错 ✗ 错误: 防呆保护生效:目标版本号 [...] 必须严格高于当前版本号 [...]! 说明目标版本小于或等于当前版本。版本号必须严格单调递增,请传入更高版本(如 npm run bump patch)。


Adaptive Mode Test Examples (`docs/adaptive_mode_test_examples.md`)

Claude 4.6 Adaptive Thinking Mode: 测试示例指南

为了验证 Claude 4.6 Adaptive (自适应) Thinking 模式的集成效果,特别是 effort 参数的生效情况及 Token 限制的自动调整,请参考以下测试场景。

1. 验证 Adaptive 模式激活与 Effort 控制

此测试验证系统能否正确传递 thinking: { type: "adaptive", effort: "..." } 参数,并观察模型行为差异。

前置条件

  • 确保使用支持 Adaptive Thinking 的模型 ID (如 claude-opus-4-6-thinking 或映射后的 ID)。
  • 在设置中将 "Thinking Budget" 模式设置为 "Adaptive (自适应)"。

测试指令示例

场景 A: Low Effort (低强度思考)

  • 配置: 将 Effort 设置为 Low。
  • 指令: 写一个并通过 Rust 编译的 Hello World 程序。
  • 预期结果:
    • Thinking 块应该比较简短,模型认为这是一个简单任务,不需要深入推理。
    • 响应速度较快。

场景 B: High Effort (高强度思考)

  • 配置: 将 Effort 设置为 High。
  • 指令: 请详细分析 Rust 的 async/await 状态机生成机制,并对比 Go 的 Goroutine 调度模型。请通过思维链深入推导两者的内存开销差异。
  • 预期结果:
    • Thinking 块应该非常长且详细(可能超过 5k tokens)。
    • 模型会尝试进行深度的对比分析和推理。
    • 关键验证点: 检查 Antigravity 日志,确认 generationConfig 中包含了 thinkingConfig: { type: "adaptive", effort: "high" }。

2. 验证多轮对话中的 Adaptive 状态维持

验证在多轮对话中,Adaptive 模式是否能持续生效,且 Token 限制 (128k) 是否正常工作。

场景:复杂算法设计迭代

Round 1: 初始设计

  • 指令:
    bash
    claude "设计一个分布式的高并发秒杀系统。需要考虑缓存一致性、库存防超卖、防刷接口等核心问题。请使用 High Effort 进行深度思考。"
  • 验证点:
    • 生成了包含架构图和详细逻辑的设计文档。
    • Thinking 过程详细记录了对不同方案(如 Redis Lua vs 数据库悲观锁)的权衡。
    • 验证响应头或日志中,确认 maxOutputTokens 被提升至 128,000 (或更高),以容纳长输出。

Round 2: 方案挑战 (模拟用户反馈)

  • 指令:
    bash
    claude "你的设计中,Redis 集群如果发生脑裂,如何保证库存数据的强一致性?请重新思考并修正方案。"
  • 验证点:
    • Thinking 块继续保持深度推理,分析 Redlock 或其他一致性算法的适用性。
    • 签名验证: 确保多轮对话中 Thinking Block 的签名验证通过(无 Invalid signature 报错)。

Round 3: 代码实现

  • 指令:
    bash
    claude "请给出库存扣减核心逻辑的 Rust 代码实现。"
  • 验证点:
    • 能生成符合之前设计思路的代码。
    • 在高上下文压力下,系统是否自动触发了 Thinking 剥离(如果配置了动态剥离),或者能够正常携带完整历史继续生成。

3. 验证 Budget 模式与 Adaptive 模式的自动切换

此测试验证当用户在“固定 Budget”与“Adaptive”模式间切换时,后端能否正确转换参数。

测试流程

  1. 设置为 Fixed Budget: 在设置中选择 "Custom" 并设置 Budget 为 16384。

    • 发送请求。
    • 验证: 后端请求应只包含 thinkingConfig: { budget: 16384 },不应包含 effort。
  2. 切换为 Adaptive: 在设置中选择 "Adaptive" 并设置 Effort 为 Medium。

    • 发送请求。
    • 验证: 后端请求应只包含 thinkingConfig: { type: "adaptive", effort: "medium" },不应包含 budget。

4. 调试建议

在运行上述测试时,建议开启 Debug 日志以观察参数传递:

bash
RUST_LOG=debug npm run tauri dev

在日志中搜索关键词:

  • [Claude-Request]: 查看转换后的请求体。
  • thinkingConfig: 确认配置注入情况。
  • maxOutputTokens: 确认 Token 上限调整情况。

Advanced Configuration (`docs/advanced_configuration.md`)

高级配置与实验性功能 (Advanced Configuration)

Antigravity v3.3.35 引入了 ExperimentalConfig,这是一组默认开启的实验性功能开关,旨在提升系统的鲁棒性与兼容性。这些配置位于 src-tauri/src/proxy/config.rs 中,目前暂未暴露到 UI 界面。

功能列表

1. 双层签名缓存 (Signature Cache)

  • 配置项: enable_signature_cache
  • 默认值: true
  • 说明: 启用后,系统会缓存 ToolUse ID 与 Thought Signature 的映射关系。
  • 作用: 解决部分客户端(如 Claude Desktop CLI, Cherry Studio)在多轮对话中可能丢失历史 Tool Call 签名的问题。当上游 API 报错 "Missing signature" 时,系统可从缓存中自动恢复,避免对话中断。

2. 工具循环自动恢复 (Tool Loop Recovery)

  • 配置项: enable_tool_loop_recovery
  • 默认值: true
  • 说明: 启用后,系统会实时监控对话状态,检测“死循环”模式。
  • 触发条件: 检测到连续的 ToolUse -> ToolResult 循环,且 Assistant 消息中缺少 Thinking 块(通常因签名校验失败被 stripping)。
  • 行为: 自动注入合成消息(Assistant: Tool execution completed. -> User: Proceed.)来打破死循环,强制模型进入下一轮思考。

3. 跨模型兼容性检查 (Cross-Model Checks)

  • 配置项: enable_cross_model_checks
  • 默认值: true
  • 说明: 防止在同一会话中切换不同系列模型(如 Claude -> Gemini)时引发的签名错误。
  • 作用: 当检测到历史消息中的签名属于不兼容的模型家族(如 claude-3-5 vs gemini-2.0)时,系统会自动丢弃旧签名,防止 API 拒绝请求。

自定义配置

目前这些配置项可通过修改 src-tauri/src/proxy/config.rs 中的 default_true 默认值来调整,或者等待未来版本集成到 "Settings -> Advanced" 界面。

rust
// src-tauri/src/proxy/config.rs

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExperimentalConfig {
    #[serde(default = "default_true")]
    pub enable_signature_cache: bool,
    // ...
}

Baogao (`docs/baogao.txt`)

{
"project": "aicode-consumers",
"requestId": "agent/4cabd908-7271-4426-8adf-062306917424/1790559875083/231efd36-2a13-4fea-ab31-04cc500a7ec2/21",
"request": {
"contents": [
{
"role": "user",
"parts": [
{
"text": "\n你好 请你深度思考一下 回答我\n\n\nThe current local time is: 2026-09-28T09:42:59+08:00.\n\n\nThe user changed setting Model Selection from None to Gemini 3.8 Flash (High). No need to comment on this change if the user doesn't ask about it. If reporting what model you are, please use a human readable name instead of the exact string.\n"
}
]
},
{
"role": "model",
"parts": [
{
"text": "The user's prompt is a greeting and request for deep thought, but lacks a specific question. Initial assessment indicates a need to determine the user's implicit needs. Examining the active workspace could provide relevant context.\n\n\n",
"thought": true
},
{
"text": "你好!我已经准备好了。\n\n请告诉我您想深入探讨的具体问题、需求或场景(无论是关于架构设计、代码实现、逻辑排查还是技术选型等),我会进行深度分析并为您提供详尽的解答。",
"thoughtSignature": "EqMOCqAOAWkUfRO0cKuvrvE9Zsa+uKZHZp2d25mCpd2a2h7oN+QkyxGc5MVWZg6/rpw73uJccDgQSIYcMHVJ/v2VvjpZcxms2UmPeDL9Uk0QohnR89qirwogE6iX5+qAOftKgmlvGpsXRqhS8/HYVOH+kVAY+BkEArdK46vz4cegVRM4c8tsTD6ZS3AbGaykulEMNrCJTYPnD7a51+/fA9/5lZ+SSvHwzrCAKxG/98lY5Yq9Ry4NZwksIpoC9UezJ7U+9uKoPfpwd1iWhsH8q+FiadVkNTyDLfXkq9oogU+o7ukVJcwhKUgi/CAAyg0Zsfn7sSPm9FCut7vRmgMyADMVrrhLdYryiB3YFSf5CKO0eRro9qNVxzVPu6839TlBmgPukzqXXXls0h815TBwARUSeaZvKZvxOci4Lm1i95lQhnhJT10U497VUEonKIk32atx6nWIcyrxHjrwZDRAoqG4fc1JmbNEzp2xDm5gKMmH/a/tMszfsetV9D2azLhDNjYvTvjiR4KY+N4uSiaIAsxef1uTH4s8/5fkcZq8EIWV4do+XVhkNvKloi/geyDm+0MFf5HNB2xtfdlFT2f+w6gmyJybpcWwv+FqW3G5yCpxbhoD4Js+0nGho6Om0aMKw0aACI9Z+xCbR3ufvn+/uw3fX9k3YN7TtiAFzu4mA16G/xHCTU+V11u7noINZ7ODColipDrWipiMHvk3cSQo8Bj8Hbz93lg7o6Y1IYXQzUcqkLda0nyorkKawFPSUuJZRUzyp/jBKHt4t0t6G3fqm2zIzK7ECRxy81UfhO0g77LJk4iNU+H7+QxGQn5OF1Mu7d0Fi50uPPnNhj1w/zb4kW8MVcSGgT9Jd8T1kYeO1LI9CYG6qw4dpk3JoDuwB9ocgClsae0okpTncBVmVRkP460ykmYo699AOtBoVFfJyZEs8d3GqM9uZa2vJ+VFtHDQf2G1GA0820jei1nHGVvlRulYs1DOAo0JyRoQuHsoRL9QAODC3Y/Num2Hiy6+pAjYNjEB78/o3aAYCVnAfpXfhkOOnadMunrrl0epBHZN+nEBE1+ws/NY+fO1SXr/HVrZyVoV+MBHc/qXaOCAo+AP5CxBaDm9CDqKDG+zz9k+JNSVrNiUhT0PGB1SvSvoQiyfBxebwOQaplXk8vHGmmqkVixxq2de05J/DSNbgHlu+5ghpRrFZFqrc64lk2YVTgoaWgdmjHUqCszGgyGJyQVKsjb/5w/ljo0NjNL2983VcAN/SAL3v2D0SZ3Me+sQO1W99TPpcR5lwBegg5Js3r7GD8XW4oZj2SsRtZ/NQekVEsnN7TJEvM7hlKmfRW/MJLvF0lyWjefaxR5giaLcaWAZVzxKaFNr1VM7sxvXxandkSd8oI746ajs/z55a31hEcyhdkuXFe3tQo1x7v82EpU5Zz0rW1s7sFgBSuvBpcnn5cwkaGylwyaH1rVgVaQm2/GMoFHzLhFhN9L/qpr0NIs5VHPy4fMcvR1jxtD9+i+UOFIPFn3UcdNy0/htyWNNlp2VQayAVaUntzToXrvqCnZhcmJf16Aaf/EEEpIl850sWWZDtGDEdmAat1+VDGt9jULY/xBdOOHrfWXRedMIzn3kkqcL0zuTIrMJ4rqUKLN1tUVbIkp19aVIrZFQadzcw/vbz9a5czclzmI3z78lHg7uOS6HrdzacNlgxL9sJPiomTZfX2c8MNLNffY+q+DbcLSPznYdZWq58DpRbU4ZyMseM3dVPkN78FY5GmtiCpTYg3ikJhOw/wB20ys/zQ0tjqv0nqTqCW0Hkip0VQlllYYzJvT2KN8ug92JwRo6g4jIswaAjwKMjA8V3pkbBCOqQg9j9p23m5EiKji8Il11vfibC1g1Y9cff0E9SK/bcF/mW5NjqftMaY17rIt5VETGsJ/BeqQ1+OSNdpBtJqKvOT0EFraUSF98WbHIL7Rkrf4a3q7SiLXGH6NCvZWI+Bum7D0Y7QEuSHLzFSb6mi8+DM9gMpjbVjP8QQGLZSGzrQTl+oabx5yeU5eaDHvbma6f7rsG3FLnOg35vPHVt6zlLzAnusTZePmhfe3SV48IgFVSF6p3EDVymcaVVFuThpyuSxu5gcDm1sYIkeLOoH6Ea3b/pSfTTqWly1s413ZPznRrhDMJTBBJfvpXD3fDFTwKHcfeYBZ6z7nMj2WNB3P24So37kCLPyk5CXn2WDWQbedYZmNHgN4vCalWtQ0CnW98yiIwPBgXtknhm/NFpAjmRD+iQNzs0wkzhMgKbbSrNZMxwmMJkRwdlcloRUTbgXEU2rf3cn17E9Kf0thHJ+D5SsAGc/MJjuKgLnojpGgiR3z3PdlzIOyQ0LKAAr0mQH8bpk2dPR0x2YrwQZBT2uALwT7uEL4Nk5w8rbg6AUdjXtBXEzLSoDTOlv1RevdujGP2nMXpUbiUfyA6"
}
]
},
{
"role": "user",
"parts": [
{
"text": "\n好的 请你直接回我 不要思考\n\n\nThe current local time is: 2026-09-28T09:43:28+08:00.\n\n\nThe user changed setting Model Selection from Gemini 3.8 Flash (High) to Gemini 3.7 Flash (High). No need to comment on this change if the user doesn't ask about it. If reporting what model you are, please use a human readable name instead of the exact string.\n"
}
]
},
{
"role": "model",
"parts": [
{
"text": "没问题!请问有什么我可以帮您的?请随时提问。",
"thoughtSignature": "EmkKZwFpFH0TWBrr7I54q6WtCISimeXbZOd+VerOZfFmXPKy7FWOsUyRdsut/rPYAdVcGWbeSbeVzx9viJRq9qKuB67GDKZiy4PM/lRiCM8sy6aArCq8lNtCeVKUw9aW6TSnJb5JwRSdtFM="
}
]
},
{
"role": "user",
"parts": [
{
"text": "\n请你再不要思考回复我一次\n\n\nThe current local time is: 2026-09-28T09:43:43+08:00.\n"
}
]
},
{
"role": "model",
"parts": [
{
"text": "好的,收到!随时听候您的吩咐。",
"thoughtSignature": "EmkKZwFpFH0TLhCLZbfNJ7KhT3ACQM3TEinREwxT4gEXHngmxV6MzNoks1jXjf6r3h+DAnzwx5vKTwHmJDtz9/9N4GIE1KGJplqE6dlrWg7qIrNq+JG1/QcChfJa4VAQ1NU0PzfjPdfMSXo="
}
]
},
{
"role": "user",
"parts": [
{
"text": "\n请你串行调用三个helloworld输出工具\n\n\nThe current local time is: 2026-09-28T09:44:00+08:00.\n"
}
]
},
{
"role": "model",
"parts": [
{
"functionCall": {
"id": "call_1284936",
"name": "run_command",
"args": {
"CommandLine": "Write-Output "helloworld 1"",
"Cwd": "e:\code\Antigravity-Manager",
"WaitMsBeforeAsync": 5000,
"toolAction": "Outputting helloworld 1",
"toolSummary": "Helloworld 1"
}
},
"thoughtSignature": "EsQKCsEKAWkUfRNnXnUqcCxGW1CtFWpxODPEjj7bvvUenQ3MzILwjaGbYhp0va3e9SHuogOtC7ksGuyqrsL32W0Z6L3+yX6QauAT2RrXzrnIF0HF/1sDHgn6ujeR4ePk1zMqGOp000+l50ruWDeTT+LPNqQ7LVIwNElf7RzgBqovAYwO4gMc1t83eAsS2yjO1Y75QthMPlBZUjW1c0s2k7mLBM0Nc8a882+DadceSFRw6Q2CTSUD6mFjm9yge+V7wMYX+YStQvPE0A/pyvyAiTnCc1q2Fy2aGzxV0eynPLJ1exAGq/Q8BwORdFNUt3cRmdcj5EnPmKv0ttDI6P8b0wR8q80Zr7sIJa6vaVdyh3jdjOI/A5Gy+mUmdXi9SSB8fRnuelHFK8rgFge/y+rkMTlQ5PUcvdckZAqigsnMFI248H37dhOPcH8ODdoUXJwffugTJ7r4VzIsGCmI9N4LvY0hLDVvldyXDSYGcK6PwE9wY38IBrKOy7TIXm/OAocf0RWORpxCxUu+JYsYdmhcuCMc27+zIkRomnvmJ8xWKqNKXvN2p7Q4HkEVq+OGLtWiyvdgfLLhDnGait134UFpr+2w9dxUxQLX5ELIK1YM9QnpOk53d34W1wPo8wbZNhpWhDU/dIWS0FgtiqAjRGutA+0Epxa8ZsjWa83KTK7z4lTCsRI09ln7iVM3Dwf1CVTchd3lM9lMbRzpP/c4S7y8JSet92KpPEfR6X96icF5dvjBM3HWt9IXmyg3flsRLNGfL1Ied5SkPvoGOKOEJU71UkKIYJaiNRN4TlLNtceU543tnctBNlU09g3uumdHHpR7atbbRzjUbolBCy2OjRZS6N4Zu29KkHa/gmoB49Fc+205qnqvoX664SYf1LsoTPMQ0/V1RdWRXziQBIlnOpnQOb3stZLPaZlh6yoe3xW3lXah8KpYurIHwwZ1EA23fyf6CggZRSmJcRHzo3Ytw/STIhSPldCtyqxZ3qp/WsnrlYovJyHiG2/KDts4CpAlo8cC/48LuLXjBBlYBhfkuSdSB+514PFm+3DQvRc6cJsx9zOgRxSGC/tRsY+OfgaMEhXw1MhNrVfe6r7L028gerfDPrj99bwofk5LU4ZK80FNT9MXHOcaZbxc78Vv0lqEcXHpgvVgw8HKBxyWucqJeLLmotePeXY1JOuCrkXP3PKYsh2sOTRcwlQeAucdbh2cwE1lEf17vXg3qeEWDVGqTkp2ifabbFkNME3jHqhS33kq/FaoZf1Njvugq8Fr3owrhkxCY/h62jVbMeUk6PJjLqCruJcckR+Ieh9BqTJteWc44s9R36PIDRcxfyEnqz/7/RAXlXocUzhwsXlMzKzCIZZZpgHXKli3cCQUbpgkKA27LnqSFYTBhvWNTFP4fKJU0QP0NgMvOdAqylRuowUS/00QID/K8BFE19YsZP8IMzrp0Y4spnTPulPjtlZqFuEqsoBdbuAggvO4mX6EvD4s5AvvXUrgyBN2xO2KGvou96LN5BVfWhT2aWaxgjfPDO/lDfz9Sjvlkhaf0TZWEAyGZd9N74y5sGWrqXit4PVhBMf7530YeEmiTp2L5C7Jc1SH13vvcvWWKe2FI80k89RwDUy2MM5R4e0ru5dvkvsZphh88Q0urohKN49ZCDTzXCtYGbEwABawnEOb+GVijCPJG5olgzEuHuspLq8iRxoz6K3LrLOSSx4oDGtHDr8Ut4SUZI+MRSZUEAssQv1KuiKjmU8w4nX/Rn4fBeqIAV/i+E3kFyVyDXj7oru+eJPkMxsjganded5oECgciA=="
}
]
},
{
"role": "model",
"parts": [
{
"functionResponse": {
"id": "call_1284936",
"name": "run_command",
"response": {
"output": "Created At: 2026-09-28T09:44:03+08:00\nCompleted At: 2026-09-28T09:44:05+08:00\n\nThe command exited with code 0.\nOutput:\nhelloworld 1\r\n\n"
}
}
}
]
},
{
"role": "model",
"parts": [
{
"functionCall": {
"id": "call_6860",
"name": "run_command",
"args": {
"CommandLine": "Write-Output "helloworld 2"",
"Cwd": "e:\code\Antigravity-Manager",
"WaitMsBeforeAsync": 5000,
"toolAction": "Outputting helloworld 2",
"toolSummary": "Helloworld 2"
}
},
"thoughtSignature": "Er8BCrwBAWkUfRP+nQu+2I3Nc0FChRAj/3AMaxAtnXNKzg1/9dBYRZyfxlNGWopQPgNT8A+ZDgYAOJjofDsrFLnRYOlvGXtgewEiea0XDGmPIcRWJETGtCxW+pRJVMg16SQZC+/Bib7bOF3YGYGUZ9EFPgANZR2C338hf7tjsOX+gglVejYNPOS5uRtWbkUqSMBO6YmGBv7fY72BOQccaYs/48a1C5nrzKX8Bbpf4vBn/bRuhvz3vZvaxBQ1R4J4go4="
}
]
},
{
"role": "model",
"parts": [
{
"functionResponse": {
"id": "call_6860",
"name": "run_command",
"response": {
"output": "Created At: 2026-09-28T09:44:07+08:00\nCompleted At: 2026-09-28T09:44:09+08:00\n\nThe command exited with code 0.\nOutput:\nhelloworld 2\r\n\n"
}
}
}
]
},
{
"role": "model",
"parts": [
{
"functionCall": {
"id": "call_761115",
"name": "run_command",
"args": {
"CommandLine": "Write-Output "helloworld 3"",
"Cwd": "e:\code\Antigravity-Manager",
"WaitMsBeforeAsync": 5000,
"toolAction": "Outputting helloworld 3",
"toolSummary": "Helloworld 3"
}
},
"thoughtSignature": "Er8BCrwBAWkUfRMar/82NISmaCgz62j8saVXUhHVoSKFOeMlIwNNbhOAK7/zMim0tsgnAVQCZrYOTkSGqzUagpZDqqc+aoCKnqueA6y1TKfWCDcAQ+IDQaiSUhgNXJGmBt8AJPQwS4F7NMZkhaNvIOcZKHDYiMK/il9/fKk6LDhsIQrKoChYA/j5ny7tXE5lh6nqaWUAYzmzMwxrldykGU2FPGk4ruvUe100VDS13dMzscx4q5gtz7NT1yHsT1qDhBo="
}
]
},
{
"role": "model",
"parts": [
{
"functionResponse": {
"id": "call_761115",
"name": "run_command",
"response": {
"output": "Created At: 2026-09-28T09:44:10+08:00\nCompleted At: 2026-09-28T09:44:12+08:00\n\nThe command exited with code 0.\nOutput:\nhelloworld 3\r\n\n"
}
}
}
]
},
{
"role": "model",
"parts": [
{
"text": "已为您按顺序串行调用了三次工具输出 helloworld:\n\n1. 第一次调用:输出 helloworld 1\n2. 第二次调用:输出 helloworld 2\n3. 第三次调用:输出 helloworld 3",
"thoughtSignature": "ErgBCrUBAWkUfRPrWi/SITUFjwxniSPRo9JnxHOWZcsMmXAYuN5cRoGwLuHtiFreH78zu17LMiVyG5cMG8sqVKRWaEihK2p5vt7aH7YkFw2XYeDw+YlDygvlM7wssO0SlfOdbKNUgYS7oFK2O/ZYMVaoJYeulddVmBiNaB+XxAqsAy0t8g1XrbGHJgYw0yKn3MJ8oLvUsFa7RwATEVjxlH4NjKbWDKE39rcOegrGyOPfL7tAgAXf2mv5aA=="
}
]
},
{
"role": "user",
"parts": [
{
"text": "\n请你并发调用三个工具hello \n\n\nThe current local time is: 2026-09-28T09:44:19+08:00.\n"
}
]
},
{
"role": "model",
"parts": [
{
"functionCall": {
"id": "call_512162",
"name": "run_command",
"args": {
"CommandLine": "Write-Output "hello 1"",
"Cwd": "e:\code\Antigravity-Manager",
"WaitMsBeforeAsync": 5000,
"toolAction": "Outputting hello 1 in parallel",
"toolSummary": "Parallel hello 1"
}
},
"thoughtSignature": "EqEDCp4DAWkUfRO5Irx+BYCeNjWdnleHUGXLGZsRd0CWrLPl6MNy8TlmYznVtXahHkTpfYCzAnrZZIPurQ0GFnh5lUFRjvalHF8D6NBaDRqmd963Y0+gDuY660MdEUJ8d2bcdQyXSD/dzLGf/IXs3GuGCRq0WpR4NEK84BiOc3B+hRzNWYIUZAf5g7MOsikYHmiJExA8+IdWY866ehmTYJli6aIGWZXoFodACLpoOYpHtXw5Xa+QfCi/MW4ImD0EvcLaTLuIjvKrxElEHEq8W70fzj4yq13urevUsjHmL/A9NmtOI/b8W+g+XVdekq/6xm0EDK7YmnwcSY6rcmnEP/xl1UnyT1ls4dAWep+VLYGACbZkykWy/nCQvUliJdRky/dKDSNQHaLlByV9tXWr/Qv+RbtUxt/ftl1S5DUdtjmSYsmxdQIsrfEH0SBD9Zc0Ak+8VCl3tOHpthaqTZ6NiHqQ7nGlTwmrE7gyqrFRZLFXCMIm2DrtkBuyG2IYcSzqHDhctyDy0OTjEPLag5IZnIFZg1EvSCDIGC6oVaWeY3zdhbho"
},
{
"functionCall": {
"id": "call_512165",
"name": "run_command",
"args": {
"CommandLine": "Write-Output "hello 2"",
"Cwd": "e:\code\Antigravity-Manager",
"WaitMsBeforeAsync": 5000,
"toolAction": "Outputting hello 2 in parallel",
"toolSummary": "Parallel hello 2"
}
}
},
{
"functionCall": {
"id": "call_512171",
"name": "run_command",
"args": {
"CommandLine": "Write-Output "hello 3"",
"Cwd": "e:\code\Antigravity-Manager",
"WaitMsBeforeAsync": 5000,
"toolAction": "Outputting hello 3 in parallel",
"toolSummary": "Parallel hello 3"
}
}
}
]
},
{
"role": "model",
"parts": [
{
"functionResponse": {
"id": "call_512162",
"name": "run_command",
"response": {
"output": "Created At: 2026-09-28T09:44:22+08:00\nCompleted At: 2026-09-28T09:44:23+08:00\n\nThe command exited with code 0.\nOutput:\nhello 1\r\n\n"
}
}
},
{
"functionResponse": {
"id": "call_512165",
"name": "run_command",
"response": {
"output": "Created At: 2026-09-28T09:44:22+08:00\nCompleted At: 2026-09-28T09:44:24+08:00\n\nThe command exited with code 0.\nOutput:\nhello 2\r\n\n"
}
}
},
{
"functionResponse": {
"id": "call_512171",
"name": "run_command",
"response": {
"output": "Created At: 2026-09-28T09:44:22+08:00\nCompleted At: 2026-09-28T09:44:25+08:00\n\nThe command exited with code 0.\nOutput:\nhello 3\r\n\n"
}
}
}
]
},
{
"role": "model",
"parts": [
{
"text": "已为您在同一次响应中并发发起了 3 个工具调用:\n\n1. 调用 1:输出 hello 1\n2. 调用 2:输出 hello 2\n3. 调用 3:输出 hello 3",
"thoughtSignature": "EtQBCtEBAWkUfRNTon772q5wZPH22+9ddKS71Y9UYft0Zq6IuhQvnjzY9l1qAU2UXKNbBEfH003gteTmJI8aBz+uiQo7jCl4ClWGcihxVCuh0jAEtas31wUHkuD8JwKaKXYjrYyTVwBvKXhTS4PKyFG1FhPZYFfIGgE5P30jTovTfKOCpE3EWgBZ3Whcy7pIgMP2z9hzX5yp6qoY+tEjCkelj5H2ogixMS9BPHcTyRdBP4ToIigco7POvSzuMOeE7/IhAHPmN4O069i2lbWSRwaJ3TEPqBE="
}
]
},
{
"role": "user",
"parts": [
{
"text": "\n好的 请你不思考回我\n\n\nThe current local time is: 2026-09-28T09:44:35+08:00.\n"
}
]
}
],
"systemInstruction": {
"role": "user",
"parts": [
{
"text": "\nYou are Antigravity, a powerful agentic AI coding assistant designed by the Google Deepmind team working on Advanced Agentic Coding.\nYou are pair programming with a USER to solve their coding task. The task may require creating a new codebase, modifying or debugging an existing codebase, or simply answering a question.\nThe USER will send you requests, which you must always prioritize addressing. User requests are enclosed within tags.\n\n\nThe USER's OS version is windows.\nThe user has 1 active workspaces, each defined by a URI and a CorpusName. Multiple URIs potentially map to the same CorpusName. The mapping is shown as follows in the format [URI] -> [CorpusName]:\ne:\code\Antigravity-Manager -> lbjlaq/Antigravity-Manager\nCode relating to the user's requests should be written in the locations listed above. Avoid writing project code files to tmp, in the .gemini dir, or directly to the Desktop and similar folders unless explicitly asked.\nApp Data Directory: C:\Users\Administrator\.gemini\antigravity\nConversation ID: 4cabd908-7271-4426-8adf-062306917424\n\n\nThe following are user-defined rules that you MUST ALWAYS FOLLOW WITHOUT ANY EXCEPTION. These rules take precedence over any following instructions.\nReview them carefully and always take them into account when you generate responses and code:\n<RULE[e:\code\Antigravity-Manager\AGENTS.md]>\n# Project Maintenance Guidelines\n\n- Architecture: This project is a gateway that aggregates four AI protocols — OpenAI Responses, OpenAI Chat Completions, Anthropic Claude, and Google Gemini — and outputs Antigravity-style Gemini protocol format.\n- Pipeline First: Keep the pipeline strictly decoupled from specific protocols. The four protocols function purely as Gemini adapters. Adapters are restricted to parameter normalization, payload transformation, protocol divergence adaptation, and edge cases unsolvable within the pipeline stage. The pipeline stage uniformly handles the converted Gemini payloads, including thinking block backfilling, thinking budget filtering and backfilling, unified context structural alignment, prefix stability, and the sanitization of risky prompts and request headers.\n- Backend Fix Strategy: Prioritize protocol-agnostic, generic fixes within the pipeline rather than localized adapter modifications. Treat adapter-level patches as a last resort only when a generic pipeline solution is infeasible or degrades compatibility.\n- UI Design & Headless Compatibility:\n - Minimalist & Contextual UI: Prioritize user-friendly, non-intrusive interactions. Reuse existing design conventions (e.g., pill toggle buttons, badge switches, or contextual setting panels) placed strictly within their most relevant sections rather than scattering unrelated controls.\n - Headless & CLI Parity: Ensure GUI configurations maintain functional parity across headless servers, CLI environments, and cross-platform environments. Provide configuration file fields, environment variable overrides, or dedicated CLI flags/commands for essential settings.\n - Cross-Platform Compatibility: Evaluate every code addition and dependency change for seamless cross-platform support.\n- Code Quality: Prioritize root-cause, future-proof fixes rather than hardcoded logic, dead code, or speculative changes. Prioritize generalized solutions that cover entire classes of problems rather than one-off patches.\n - Model Routing Example: Prioritize wildcard patterns (e.g., gemini-*-flash-*) to anticipate future model releases rather than exact string matches.\n - Prompt Sanitization Example: For agent-client prompt sanitization, prioritize regex-based pattern matching over static keyword replacement, ensuring full coverage without stripping pipeline system prompts or user queries.\n- Formatting & CI Discipline:\n - Unit Testing: Keep focused — run targeted tests for touched modules locally; CI compiles test targets without executing them. Skip tests for trivial edits (constants, prompts, or config tweaks). No need to run the full suite locally.\n - Pre-flight Checks: Run the essentials before submitting PRs or release tags:\n - cd src-tauri && cargo fmt -- --check (for Rust edits)\n - cd src-tauri && cargo clippy --all-targets --all-features (comprehensive Rust gate, already includes compilation — no separate cargo check needed)\n - npm run build (when src/ or frontend configs changed)\n - Rely on CI for full-app compilation (tauri build) and full test execution. Local pre-flight covers fmt + clippy + frontend build only.\n- Release Channels & Discipline:\n - Release Channel Separation:\n - Stable Releases (正式版): Exclusively on main. Deploys official production packages, updates Docker/GitHub latest tags, and services automatic update channels.\n - Preview Releases (预览版 / Beta): Exclusively on beta. Independently builds and publishes pre-releases (makeLatest: false, prerelease: true) without touching production update channels.\n - Maintainer Staging Protocol:\n - When introducing new changes (features, major refactors, non-trivial fixes), prompt and confirm with maintainers whether to implement and test on beta branch first.\n - Validate stability on beta (with optional independent preview builds) prior to merging into main.\n - Standard Release Workflow:\n 1. Atomic Version Sync: Run npm run bump to synchronize all project manifests and generate changelog skeletons.\n 2. Documentation & Attribution:\n - Audit Git history (<last-tag>..HEAD) and merged PRs to summarize all authors, co-authors, and linked Issues/PRs (Fixes #xxx, PR #xxx). Attribute every contributor inline (Thanks to @username) in CHANGELOG.md (and CHANGELOG_EN.md).\n - Synchronize README Changelog (同步首页更新日志): For stable releases, you MUST update the release summary in both README.md (under "## 📝 更新日志") and README_EN.md (under "## 📝 Changelog"). Never update only CHANGELOG.md while leaving README.md / README_EN.md with outdated release notes. Pre-release / beta versions remain exclusively in changelogs; stable releases require full synchronization across both README files.\n 3. Pre-flight before Tagging: Run the Pre-flight Checks above on the exact commit to be tagged.\n 4. Commit, Tag & Push: Push stable releases to main (git tag vX.Y.Z && git push origin vX.Y.Z), reserving beta exclusively for pre-releases (git tag vX.Y.Z-beta.N && git push origin vX.Y.Z-beta.N). The release gate strictly intercepts cross-branch misplacement. Tags must match CHANGELOG.md headings character-for-character (including v prefix and pre-release suffix).\n - Full procedure: See docs/RELEASE_GUIDE.md for bump options, changelog templates, and rollback steps.\n- Thinking Cache Invalidation Control (发版清理建议):\n - File: src/components/common/SuggestionDeleteThinkingModal.tsx\n - Routine releases (no prompt): Keep SUGGESTION_DELETE_THINKING_STORE = false.\n - Architecture / schema refactors (prompt users to clean once):\n 1. Set SUGGESTION_DELETE_THINKING_STORE = true.\n 2. Set SUGGESTION_TARGET_VERSION = '<version>' (e.g. '4.8.2').\n 3. On upgrade, users with existing cache get a one-time prompt; action state persists in gui_config.json.\n- Branch & History Hygiene:\n - Branch from remote bases (origin/beta for staged features, origin/main for direct hotfixes) rather than local branches to prevent untracked ancestor commits.\n - Inspect in-flight PRs (gh pr list --base main) before rewriting published tips, avoiding force-pushes across shared branches.\n - Retain local rollback refs (backup/*) before history rewrites, confirming zero content drift via git diff --stat HEAD.\n- PR Scope & Grouping:\n - Single Problem Scope: A PR represents a cohesive collection of fixes or features dedicated to a single problem class. Keep unrelated concerns (such as governance, release tooling, or documentation) in isolated PRs.\n - Self-Contained & Individually Revertable: A PR may contain multiple commits, but each commit must represent an independent, self-contained functional unit that is individually revertable, avoiding messy or tangled changesets.\n - Local Convergence & Final-State Commits: Commit freely during local debugging on development branches; however, before opening or merging a PR, audit and consolidate scattered iterative attempts into clean, high-quality units. Each consolidated commit must describe only its successful final state and rationale, eliminating intermediate trial-and-error noise.\n - Review & Template Alignment: Route every PR through peer review and complete .github/PULL_REQUEST_TEMPLATE.md (problem classification, behavior alterations, unverified paths, and rollback strategy).\n- Contributor Respect & Attribution:\n - Preserve authorship by preferring the contributor's own PR for squash commits, or attaching explicit Co-authored-by: trailers on merge commits and proxy PRs.\n - Disclose costs before merging: highlight affected existing behaviors and unverified paths alongside improvements.\n - Own tooling and documentation gaps directly rather than attributing downstream frictions to contributors.\n - Accompany every rejection with a concrete file-by-file accept/drop breakdown and an actionable path forward.\n- Risky Path Replacement:\n - Keep an explicit fallback to the verified path whenever you replace it (e.g. "0 matches found → full restart"). A silent no-op is worse than the failure it was meant to fix.\n - Order side effects to fail before the point of no return: write credentials before killing a process, validate before deleting.\n - Detect broadly, act narrowly: a matcher may recognize a whole class of problems, while its effect stays inside the intended data — not across line breaks, tags, or other clauses. Bound every wait with a timeout.\n\nMaintained by @jeikl\n</RULE[e:\code\Antigravity-Manager\AGENTS.md]>\n\n\nYou can use specialized 'skills' to help you with complex tasks. Each skill has a name and a description listed below.\n\nSkills are folders of instructions, scripts, and resources that extend your capabilities for specialized tasks. Each skill folder contains:\n- SKILL.md (required): The main instruction file with YAML frontmatter (name, description) and detailed markdown instructions\n\nMore complex skills may include additional directories and files as needed, for example:\n- scripts/ - Helper scripts and utilities that extend your capabilities\n- examples/ - Reference implementations and usage patterns\n- resources/ - Additional files, templates, or assets the skill may reference\n- references/ - Contains additional documentation that agents can read when needed\n\n\nIf a skill seems relevant to your current task, you MUST read its SKILL.md instructions using view_file before proceeding. You may skip this step only if you are delegating the skill-related task to a subagent that will read and follow the instructions itself.\n\nWhen calling view_file on these skill paths, always use the exact path provided in the "Available skills" list below.\n\nAvailable skills:\n- agy-customizations (C:\Users\Administrator\.gemini\antigravity\builtin\skills\agy-customizations\SKILL.md): Comprehensive guide and reference for the Antigravity Customization System. Use to explain how customizations work, their loading priority, discovery mechanisms, and to guide the creation of skills, rules, plugins, hooks, and MCP servers.\n- antigravity-guide (C:\Users\Administrator\.gemini\antigravity\builtin\skills\antigravity_guide\SKILL.md): Provides a comprehensive guide, quick reference, and sitemap for Google Antigravity (AGY), including the Antigravity CLI (agy), Antigravity 2.0, Antigravity IDE, Python SDK, slash commands, keybindings, and customizations (skills, rules, MCP, sidecars). Activate this skill when the user asks questions about how to use, configure, or customize Antigravity, AGY, the agy CLI, the Antigravity IDE, or Antigravity 2.0.\n- generative_ui (C:\Users\Administrator\.gemini\antigravity\builtin\skills\generative_ui\SKILL.md): How to render rich interactive HTML widgets inline in the chat or as standalone artifacts. Use this skill when you want to show the user diagrams, data visualizations, interactive controls, educational walkthroughs, or any rich visual content beyond plain text and markdown.\n- migrate-workflows (C:\Users\Administrator\.gemini\antigravity\builtin\skills\migrate-workflows\SKILL.md): Automatically migrate legacy workflows to modern skills across global and workspace configurations. Scans for existing workflows, creates target SKILL.md files, and safely archives old workflow files.\n\n\n\n\n## Invoking Subagents\n\nSubagents can be invoked using the invoke_subagent tool. You can invoke an existing subagent by name, or define a new subagent for this conversation using the define_subagent tool, and then invoke it. Agents defined by the define_subagent tool are available for the duration of this conversation. After launching a subagent, you do NOT need to poll or check your inbox in a loop. The system will automatically notify you when the subagent sends a message. Simply proceed with other work or stop calling tools, and you will be notified when there is a message to process.\n\n## Communicating with Another Agent\n\nUse the send_message tool to send a message to another agent by its conversation ID (returned by invoke_subagent). This tool is ONLY for communicating with other agents.\n\nDo NOT use send_message to communicate with the user. Instead, output visible text to communicate with the user.\n\nAvailable subagents:\n- self: Subagent that inherits the parent agent's full configuration including tools, system prompt, and model. Use this when you need to run a task in a separate conversation context but with the same capabilities as the current agent.\n- research: Research subagent with read-only tools for exploring the codebase, searching the web, and reading files. Delegate to this agent when you need to run a task in a separate conversation context but with the same capabilities as the current agent, when a research task requires many search and file-reading steps that would clutter your context, or when you need a broad survey of the codebase or documentation. Prefer doing research yourself for quick, targeted lookups.\n\nAfter launching a subagent, you do NOT need to poll or check your inbox in a loop. The system will automatically notify you when the subagent sends a message. Simply proceed with other work or stop calling tools, and you will be notified when there is a message to process.\n\n\n\nYou are connected to a messaging system where you may receive messages from: agents, background tasks, user-queued messages.\n\n## Receiving Messages\n\nYou receive messages automatically at the start of each invocation. All messages are delivered in full directly into your context — no manual retrieval is needed.\n\n## Reactive Wakeup (No Polling Needed)\n\nThe system automatically resumes your execution when:\n- A message arrives from a subagent or peer agent\n- A background task completes or sends you a notification\n- A user-queued message is ready to be dequeued\n\nThis means you do NOT need to poll in a loop while waiting for messages or updates. After launching anything that performs work asynchronously, you may continue other work or simply stop by calling no more tools. The system will notify you when there is something to process.\n\n\nTranscripts are located directly at <appDataDir>\\brain\\<conversation-id>\\.system_generated\\logs/transcript.jsonl (and transcript_full.jsonl).\n- Start with transcript.jsonl (compact). When truncated_fields is present, read only the specific corresponding line in transcript_full.jsonl.\n- Search subagents by grepping invoke_subagent in transcript.jsonl.\n- Link conversations using [<label>](conversation://<conversation-id>).\n\n# File Format\nTranscripts are in JSON Lines (JSONL) format. Each line is a single JSON object representing one "step" or action in the conversation.\nEach JSON object contains fields such as:\n- step_index: The index of the step in the trajectory.\n- source: The source of the action (e.g., USER_EXPLICIT, MODEL, SYSTEM).\n- type: The type of the step. Particular steps of interest are USER_INPUT, which represents a user's prompt, and PLANNER_RESPONSE, which represents the agent's response and tool calls.\n- status: The status of the step (e.g., DONE, ERROR).\n- created_at: The ISO 8601 timestamp of when the step occurred.\n- content: The text content of the step (e.g., the user's request, the model's response, or tool responses).\n- thinking: The model's internal reasoning / chain-of-thought (for PLANNER_RESPONSE steps).\n- tool_calls: An array of tool calls made in this step, including their arguments.\n- media: An array of media (e.g. images) attached to the step, each with a mime_type and a uri referencing the media file on disk. Media bytes are not stored in the transcript; view the uri path to see the media.\n- truncated_fields: An array of field names that were truncated (e.g., [\"content\"], [\"thinking\"], [\"tool_calls\"]). Only present in transcript.jsonl when truncation occurred (never in transcript_full.jsonl). When present, read the corresponding line in transcript_full.jsonl for the complete content.\n\n\n\nArtifacts are special markdown (.md) documents that you can create to present structured information to the user.\nAll artifacts should be written to the artifact directory: <appDataDir>\\brain\\<conversation-id>. You do NOT need to create this directory yourself, it will be created automatically when you create artifacts.\n\n# When to Use Artifacts\n\nUse artifacts for:\n- Extensive reports and analysis summaries\n- Persistent information you'll update over time (task lists, experiment logs)\n- Code changes formatted as diffs\n\nDon't use artifacts for:\n- Simple one-off answers or very short paragraph content - just respond directly\n- Asking questions or requesting user input - just ask directly\n\nAfter creating or updating an artifact, DO NOT re-summarize the artifact contents in your response to the user. Instead, point the user to the artifact and highlight only key open questions or decisions that need their input.\n\n\n# Artifact Formatting Tips\nWhen creating markdown artifacts, use standard markdown and GitHub Flavored Markdown formatting.\n\n## Alerts\nUse GitHub-style alerts strategically to emphasize critical information. Do not place consecutively or nest:\n > [!NOTE] Background context, implementation details, or explanations\n > [!TIP] Performance optimizations, best practices, or efficiency suggestions\n > [!IMPORTANT] Essential requirements, critical steps, or must-know information\n > [!WARNING] Breaking changes, compatibility issues, or potential problems\n > [!CAUTION] High-risk actions that could cause data loss or security vulnerabilities\n\n\n## Mermaid Diagrams\nCreate mermaid diagrams using fenced code blocks with language mermaid to visualize relationships, workflows, and architectures.\n- Only use supported diagram types:\n - Flowcharts / Graphs: flowchart TD / flowchart LR / graph TD / graph LR\n - Sequence Diagrams: sequenceDiagram\n - State Diagrams: stateDiagram-v2 or stateDiagram\n - Class Diagrams: classDiagram\n - Entity-Relationship Diagrams: erDiagram\n - XY Charts: xychart-beta\n- All other diagram types are unsupported. For schedules, timelines, or roadmaps, use directed flowcharts (flowchart LR / flowchart TD) or Markdown tables instead.\n- To prevent syntax errors:\n - Quote node labels containing special characters like parentheses or brackets. For example, id[\"Label (Extra Info)\"] instead of id[Label (Extra Info)].\n - Avoid HTML tags in labels.\n\n## File Links\n- Link to line ranges using link text format.\n- IMPORTANT: If you are embedding a file in an artifact and the file is NOT already in \brain\, you MUST first copy the file to the artifacts directory before embedding it. Only embed files that are located in the artifacts directory. Always use its absolute path .\n- **Use basenames for readability**: Use file basenames for the link text instead of the full path\n\n## Carousels\nUse ````carousel syntax with HTML comments to display related markdown snippets sequentially (before/after comparisons, UI progressions, alternative approaches, walkthroughs). Four backticks enable nesting code blocks within slides.\n\nExample:\ncarousel\n\n\n```python\ndef example():\n print(\"Code in carousel\")\n```\n\n\n## LaTeX / Math\nArtifacts render LaTeX math with KaTeX.\n- Literal $: write \\$, or wrap it in backticks. Two unescaped $ in the same paragraph become math.\n\n# Scratch Scripts and Files\n\nYou may find it useful to create scratch scripts or files for temporary purposes.\n\nExamples:\n- One-off scripts to debug code\n- Temporary data files for testing\n\nStore these files in the <appDataDir>\\brain\\<conversation-id>/scratch/ directory. They will be persisted.\n\n\nArtifact Directory Path: C:\Users\Administrator\.gemini\antigravity\brain\4cabd908-7271-4426-8adf-062306917424\n\n\n\nSlash commands are user-facing shortcuts in the chat UI (e.g., typing /goal or /schedule) that automate complex workflows or trigger specialized agent behaviors.\n\nYou cannot execute these commands yourself. Your role is to recommend them to the user when they are a good fit for the task at hand, encouraging the user to explore and trigger them.\n\nTo recommend a slash command, suggest it clearly in your response (e.g., "You can use the /goal command to...").\n\n\nAvailable slash commands you can recommend to the user:\n- /goal: Recommend this when the user wants to run a long-running task (e.g., overnight) and wants the agent to be extra thorough and not stop until the goal is fully achieved.\n- /schedule: Recommend this when the user wants to run an instruction on a recurring schedule or set a one-time timer.\n- /browser: Recommend this when the user's task involves web browsing, searching the web, or interacting with web applications.\n- /plan: Recommend this when the task is complex and requires careful step-by-step planning before execution.\n- /grill-me: Recommend this when the user wants to align on a plan through an interactive interview to resolve design decisions.\n- /teamwork-preview: Recommend this when the user has a large project that would benefit from a team of autonomous agents working together.\n- /learn: Recommend this when the user has corrected the agent or solved a complex setup and wants the agent to persist this behavior for future tasks.\n- /boost: Recommend this when the user has a complex coding or research project that requires deep thinking, strategic planning, multiple perspectives, and rigorous verification.\n\n\n\n\nFollow these behavioral guidelines at all times:\n- Maintain documentation integrity. Preserve all existing comments and docstrings that are unrelated to your code changes, unless the user specifies otherwise.\n\n\n\n- Keep your responses concise.\n- Format your responses in github-style markdown.\n- You can render LaTeX math (KaTeX): inline with \\(...\\) or $...$, display with \\[...\\] or $$...$$ placed on its own line.\n- Use math only for genuine mathematical content. Use backticks for code, identifiers, paths, flags, and shell variables.\n- $ opens inline math, so write a literal dollar as \\$ or wrap it in backticks. Two unescaped $ in the same paragraph turn everything between them into math — this bites prices (\\$100) and shell syntax written in prose ($HOME, awk $1).\n\n- If you're unsure about the user's intent, ask for clarification rather than making assumptions.\n- You MUST create clickable links for all files and code symbols (classes, types, functions, structs). Use github style markdown links with the file:// scheme (e.g., utils.py or ClassName). For Windows, use forward slashes for paths.\n"
}
]
},
"tools": [
{
"functionDeclarations": [
{
"name": "view_file",
"description": "View the contents of a file from the local filesystem. This tool supports text files and following binary files: image, pdf, video, audio.\nText file usage:\n- The lines of the file are 1-indexed\n- You can view at most 800 lines at a time\n- Specify StartLine and EndLine to view the lines of the file using slice notation:\n - Omit both to view the entire file, or the first 800 lines of the file, whichever is smaller.\n - Specify StartLine only to view the remaining lines of the file, or the next 800 lines, whichever is smaller\n - Specify EndLine only to view the remaining preceding lines of the file, or the previous 800 lines, whichever is smaller\n - Specify both to view a precise line range. This range must be smaller than 800 lines or only the first 800 lines of the range will be shown.\n- Content is limited to 46080 bytes per view. If content is truncated, use the ContentOffset parameter to view the remaining content\nBinary file usage:\n- Do not provide StartLine or EndLine arguments, this tool always returns the entire file\n- Files larger than 100 MB cannot be viewed.",
"parameters": {
"type": "OBJECT",
"properties": {
"AbsolutePath": {
"type": "STRING",
"description": "Path to file to view. Must be an absolute path."
},
"ContentOffset": {
"type": "INTEGER",
"description": "Optional. Byte offset into the content. Use this to view content beyond the initial byte limit when the tool output indicates content was truncated."
},
"EndLine": {
"type": "INTEGER",
"description": "Optional. Endline to view, 1-indexed, inclusive. When specified, this value must be greater than or equal to StartLine."
},
"StartLine": {
"type": "INTEGER",
"description": "Optional. Startline to view, 1-indexed, inclusive. When specified, this value must be less than or equal to EndLine."
},
"toolAction": {
"type": "STRING",
"description": "Brief 2-5 word phrase in -ing form describing the specific action. Capitalize like a sentence. Some examples: 'Analyzing directory', 'Searching the web', 'Checking git status', 'Running tests', 'Searching code'."
},
"toolSummary": {
"type": "STRING",
"description": "Brief 2-5 word noun phrase describing the specific task. Capitalize like a sentence. Some examples: 'Directory analysis', 'Web search', 'Git status check', 'Test execution', 'Code search'."
}
},
"required": [
"AbsolutePath",
"toolSummary",
"toolAction"
]
}
}
]
},
{
"functionDeclarations": [
{
"name": "run_command",
"description": "PROPOSE a command to run on behalf of the user. Operating System: windows. Shell: powershell.\nNEVER PROPOSE A cd COMMAND.\nIf you have this tool, note that you DO have the ability to run commands directly on the USER's system.\nMake sure to specify CommandLine exactly as it should be run in the shell.\nIf the step doesn't return the command output, it means that the command was sent to the background as a task. You will receive messages with the command's output as it runs. To interact with a running command, use the manage_task tool. Use send_input to send stdin, kill to terminate the command, and status to check current status. IMPORTANT: Do NOT poll or loop on status to wait for completion. The system will automatically notify you with a message when the command finishes. Simply proceed with other work or stop calling tools after launching a command.\nCommands will be run with PAGER=cat. You may want to limit the length of output for commands that usually rely on paging and may contain very long output (e.g. git log, use git log -n ).",
"parameters": {
"type": "OBJECT",
"properties": {
"CommandLine": {
"type": "STRING",
"description": "The exact command line string to execute."
},
"Cwd": {
"type": "STRING",
"description": "The current working directory for the command"
},
"IsDaemon": {
"type": "BOOLEAN",
"description": "Set to true for long-running support processes that are meant to keep running in the background indefinitely and are not expected to finish on their own (e.g., dev servers, file watchers, tunnels). Leave false (the default) for normal commands that are expected to terminate."
},
"WaitMsBeforeAsync": {
"type": "INTEGER",
"description": "This specifies the number of milliseconds to wait after starting the command before sending it to the background. If you want the command to complete execution synchronously, set this to a large enough value that you expect the command to complete in that time under ordinary circumstances. If you're starting an interactive or long-running command, set it to a large enough value that it would cause possible failure cases to execute synchronously (e.g. 500ms). Keep the value as small as possible, with a maximum of 10000ms."
},
"toolAction": {
"type": "STRING",
"description": "Brief 2-5 word phrase in -ing form describing the specific action. Capitalize like a sentence. Some examples: 'Analyzing directory', 'Searching the web', 'Checking git status', 'Running tests', 'Searching code'."
},
"toolSummary": {
"type": "STRING",
"description": "Brief 2-5 word noun phrase describing the specific task. Capitalize like a sentence. Some examples: 'Directory analysis', 'Web search', 'Git status check', 'Test execution', 'Code search'."
}
},
"required": [
"Cwd",
"WaitMsBeforeAsync",
"CommandLine",
"toolSummary",
"toolAction"
]
}
}
]
},
{
"functionDeclarations": [
{
"name": "manage_task",
"description": "Manage background tasks. Use this tool to list running tasks or interact with tasks that were sent to the background.\n\nActions:\n- 'list': List all currently running background tasks\n- 'kill': Cancel the task's execution\n- 'status': Check the task's current status and log file location\n- 'send_input': Send input to a running task\n\nWhen mentioning tasks to the user, avoid using full task IDs and start timestamps; keep them human-readable.",
"parameters": {
"type": "OBJECT",
"properties": {
"Action": {
"type": "STRING",
"description": "The action to perform: 'list' (list all running tasks), 'kill' (cancel the task), 'status' (check the task status and log URI), 'send_input' (send input to a running task).",
"enum": [
"list",
"kill",
"status",
"send_input"
]
},
"Input": {
"type": "STRING",
"description": "The input to send to the task. Required when Action is 'send_input'."
},
"TaskId": {
"type": "STRING",
"description": "The task ID to manage. Required when Action is 'kill', 'status', or 'send_input'."
},
"toolAction": {
"type": "STRING",
"description": "Brief 2-5 word phrase in -ing form describing the specific action. Capitalize like a sentence. Some examples: 'Analyzing directory', 'Searching the web', 'Checking git status', 'Running tests', 'Searching code'."
},
"toolSummary": {
"type": "STRING",
"description": "Brief 2-5 word noun phrase describing the specific task. Capitalize like a sentence. Some examples: 'Directory analysis', 'Web search', 'Git status check', 'Test execution', 'Code search'."
}
},
"required": [
"Action",
"toolSummary",
"toolAction"
]
}
}
]
},
{
"functionDeclarations": [
{
"name": "send_message",
"description": "Send a message to another agent. This tool can be used to communicate with subagents, peer agents, etc. Do not use this tool to communicate with the user.",
"parameters": {
"type": "OBJECT",
"properties": {
"Message": {
"type": "STRING",
"description": "The message content."
},
"Recipient": {
"type": "STRING",
"description": "The recipient ID to send the message to, e.g. a subagent conversation ID."
},
"toolAction": {
"type": "STRING",
"description": "Brief 2-5 word phrase in -ing form describing the specific action. Capitalize like a sentence. Some examples: 'Analyzing directory', 'Searching the web', 'Checking git status', 'Running tests', 'Searching code'."
},
"toolSummary": {
"type": "STRING",
"description": "Brief 2-5 word noun phrase describing the specific task. Capitalize like a sentence. Some examples: 'Directory analysis', 'Web search', 'Git status check', 'Test execution', 'Code search'."
}
},
"required": [
"Recipient",
"Message",
"toolSummary",
"toolAction"
]
}
}
]
},
{
"functionDeclarations": [
{
"name": "schedule",
"description": "Schedule a one-shot timer or a recurring cron job that sends notifications in the background.\n\nNOTE: This tool call returns immediately and does not pause execution. To wait for the timer to fire, you must stop calling tools to end your turn.\n\nModes:\n1. One-shot timer: Set a timer for a specified duration that will notify you with your Prompt when it expires. You can control early termination behavior using TimerCondition:\n\n- 'never' (default): The timer will always fire after the specified duration, unless explicitly cancelled.\nUsage: Use when setting unconditional timers that should always fire after DurationSeconds, unless explicitly cancelled.\n- 'any': The timer will be cancelled early if ANY message from any sender is received before the duration.\nUsage: Useful when multiple background tasks are running and you want to wait for any update, but with some guarantee that you won't be idle forever in case they are all stuck.\n- : The timer will be cancelled early if a message is received from that specific sender ID.\nUsage: Use when you're waiting for an update from a specific subagent or task, but want to set some limit on how long to wait.\n\nNOTE: When a timer is cancelled early, no separate cancellation notification is sent — the message that satisfied the condition is itself your wakeup, and the timer's tool step result records the cancellation.\n\nNOTE: You cannot have multiple concurrently active timers that would early terminate on the same sender ID.\nFor example, if you already have a liveness timer set with "any", you cannot set another timer with "any" or any other condition.\nIf you already have a timer set with early termination on "task-123", you cannot set another timer with "task-123" or "any".\nYou should rely on the existing timer, or cancel and replace it if needed.\n\nExamples:\n\nScenario: User asks explicitly for a reminder in 10 minutes.\nArgs: DurationSeconds=600, Prompt="Remind the user", TimerCondition="never"\nComments: TimerCondition="never" is appropriate since this timer is unrelated to other ongoing tasks.\n\nScenario: You just ran a command as "task-123". You already set a notification on it for 5 minutes, and it just notified you that it's still running. After checking the output, you want to set a new reminder to check on it in 10 minutes if it still hasn't finished.\nArgs: DurationSeconds=600, Prompt="Check on the command status", TimerCondition="task-123"\nComments: TimerCondition="task-123" is appropriate since the timer is not needed if the command finishes ahead of time.\n\nScenario: You just spawned 10 subagents, and you want to check in on progress after 5 minutes if you haven't heard back from any of them.\nArgs: DurationSeconds=300, Prompt="Check in on the subagents' progress", TimerCondition="any"\nComments: TimerCondition="any" is appropriate since you are not waiting for any specific subagent.\n\nScenario: You are running a command that you're sure will terminate, and you want to wait for it to finish.\nArgs: N/A\nComments: A timer is not needed at all in this scenario and will wastefully generate extra messages. Stop calling tools to end your turn instead.\n\n2. Recurring cron: Set CronExpression to a standard 5-field cron expression (e.g., '/5 * * * ' for every 5 minutes). Each time the cron triggers, a notification with your Prompt is sent. The cron runs as a background task. Optionally set MaxIterations to limit the number of triggers. Optionally set IsDaemon to declare how the cron relates to your current task: leave it false (the default) when the cron is how your current task makes progress — polling or monitoring a job until it completes, heartbeat/liveness, or reminders — so your task stays active until the cron ends; set it true only when the cron is an independent standing job that should keep running after your current task is done — e.g. a recurring report or a maintenance job the user asked you to keep going — so you can finish now while it keeps firing in the background.\n\nExamples:\n- Poll deployment status every 5 minutes until it passes: CronExpression="/5 * * * ", Prompt="Check deployment status and report progress", IsDaemon=false\n- Run a health check every hour, up to 3 times: CronExpression="0 * * * ", MaxIterations=3, Prompt="Run the health check script and report results", IsDaemon=false\n- Inspect newly filed issues in the last 24h and post a daily summary report: CronExpression="0 9 * * ", Prompt="Summarize issues filed in the last 24h and post the report", IsDaemon=true\n\nGeneral Reminders:\n- You must specify exactly one of DurationSeconds or CronExpression.\n- Always provide a Prompt describing what the notification should say.\n- Never run a background 'sleep' command to set a timer, use this tool instead.\n- To cancel a running timer or cron schedule, use the manage_task tool with the task ID returned by this tool.",
"parameters": {
"type": "OBJECT",
"properties": {
"CronExpression": {
"type": "STRING",
"description": "A standard cron expression (5 fields: minute hour day-of-month month day-of-week). Use for recurring schedules. Mutually exclusive with DurationSeconds. Example: '
/5 * * * ' for every 5 minutes."
},
"DurationSeconds": {
"type": "INTEGER",
"description": "The number of seconds to wait. Use for one-shot timers. Mutually exclusive with CronExpression."
},
"IsDaemon": {
"type": "BOOLEAN",
"description": "Optional. Set to true only when the cron is an independent, standing job that should keep firing even after your current task is done (e.g. a recurring daily/weekly report or a standing maintenance job). Leave false (the default) whenever the cron is part of finishing your current task — including polling or monitoring a running job until it completes, heartbeat/liveness, or reminders."
},
"MaxIterations": {
"type": "INTEGER",
"description": "Optional. Maximum number of times the cron schedule will fire before stopping. Only applicable when CronExpression is set. Defaults to unlimited."
},
"Prompt": {
"type": "STRING",
"description": "The message content to include in the notification when the timer fires or cron triggers. This is sent to the agent as a high-priority message."
},
"TimerCondition": {
"type": "STRING",
"description": "Optional. Controls when a one-shot timer should early terminate upon receiving a message. Options: 'never' (default, timer unconditionally waits until expiry), 'any' (timer cancels if any message is received), or a specific sender ID (timer cancels only if a message is received from that specific subagent conversation ID or background task ID). Only applicable when DurationSeconds is set."
},
"toolAction": {
"type": "STRING",
"description": "Brief 2-5 word phrase in -ing form describing the specific action. Capitalize like a sentence. Some examples: 'Analyzing directory', 'Searching the web', 'Checking git status', 'Running tests', 'Searching code'."
},
"toolSummary": {
"type": "STRING",
"description": "Brief 2-5 word noun phrase describing the specific task. Capitalize like a sentence. Some examples: 'Directory analysis', 'Web search', 'Git status check', 'Test execution', 'Code search'."
}
},
"required": [
"Prompt",
"toolSummary",
"toolAction"
]
}
}
]
},
{
"functionDeclarations": [
{
"name": "invoke_subagent",
"description": "Invokes one or more subagents by name with a single tool call. Each subagent runs in the background with its own prompt and reports back when done.\n\nSpecify the Subagents array with one or more entries. Each entry defines a subagent to launch.\n\nCommunicate with subagents using the send_message tool. Examples of when to do this:\n
To check on the status of a subagent.\n
To send a running subagent further instructions.\n
To send an idle subagent new instructions.\n\nGuidelines:\n
Each invoked subagent will be uniquely identified by its conversationID.\n* Multiple subagents with the same type name can be invoked, with each subagent receiving a unique conversationID.\n* If a task is a natural continuation of an existing subagent's work, send a message to that subagent with the task rather than invoking a new subagent to conserve resources.\n* When selecting the Model argument, default to 'inherit' unless the user explicitly requests a different model.",
"parameters": {
"type": "OBJECT",
"properties": {
"Subagents": {
"type": "ARRAY",
"description": "Array of subagents to invoke. Each entry specifies a separate subagent to launch concurrently.",
"items": {
"type": "OBJECT",
"properties": {
"Model": {
"type": "STRING",
"description": "Model to use for the subagent. 'inherit' (default) uses the calling agent's model. 'flash_lite' uses a very light model. 'flash' uses a smaller, faster model suited for simple tasks like research lookups, file reading, or quick searches. 'pro' uses a larger, more capable model suited for complex tasks requiring deep reasoning, large refactors, or multi-step planning.",
"enum": [
"inherit",
"flash_lite",
"flash",
"pro"
]
},
"Prompt": {
"type": "STRING",
"description": "A clear, actionable task description for the subagent. Be specific about what the subagent should do and what information it should return."
},
"Role": {
"type": "STRING",
"description": "A 2-5 word description of the subagent's role. Should read similar to a job title, e.g. 'Codebase Researcher', 'Database Debugger', etc. Should also be detailed enough to distinguish between different subagents who might share similar purposes."
},
"TypeName": {
"type": "STRING",
"description": "Type name of the subagent to invoke."
},
"Workspace": {
"type": "STRING",
"description": "Workspace mode for the subagent. 'inherit' (default) uses the same workspace as the parent. 'branch' creates a new isolated workspace branched or cloned from the parent. 'share' creates a new workspace sharing the parent's underlying repository directory (similar to a git worktree or Mercurial 'hg share'), allowing independent branching without duplicating storage. If omitted, defaults to 'inherit'."
}
},
"required": [
"TypeName",
"Role",
"Prompt"
]
}
},
"toolAction": {
"type": "STRING",
"description": "Brief 2-5 word phrase in -ing form describing the specific action. Capitalize like a sentence. Some examples: 'Analyzing directory', 'Searching the web', 'Checking git status', 'Running tests', 'Searching code'."
},
"toolSummary": {
"type": "STRING",
"description": "Brief 2-5 word noun phrase describing the specific task. Capitalize like a sentence. Some examples: 'Directory analysis', 'Web search', 'Git status check', 'Test execution', 'Code search'."
}
},
"required": [
"Subagents",
"toolSummary",
"toolAction"
]
}
}
]
},
{
"functionDeclarations": [
{
"name": "define_subagent",
"description": "Defines a new type of subagent that can be invoked via invoke_subagent.\n\n\tGuidelines:\n\t* Use this tool if you need a specialized subagent for a task and none of the existing subagents are suitable.\n\t* Once the subagent is defined, it can be invoked repeatedly using invoke_subagent without calling this tool again.\n\t* The subagent will be defined with the specified name, description, system prompt, and tool groups.\n\t* By default, all subagents have read tools to research the codebase, and tools to communicate with other agents.\n\t",
"parameters": {
"type": "OBJECT",
"properties": {
"description": {
"type": "STRING",
"description": "Human-readable description of what this subagent does and when it should be used."
},
"enable_mcp_tools": {
"type": "BOOLEAN",
"description": "Set true to enable the subagent to call MCP tools."
},
"enable_subagent_tools": {
"type": "BOOLEAN",
"description": "Set true to equip the subagent with tools to define and invoke its own subagents"
},
"enable_write_tools": {
"type": "BOOLEAN",
"description": "Set true to equip the subagent with tools to create and edit files, and run commands."
},
"name": {
"type": "STRING",
"description": "Unique name for the subagent. Used to invoke it via invoke_subagent. Must start with a letter or digit and contain only letters, digits, '_', '-', and '.'."
},
"system_prompt": {
"type": "STRING",
"description": "A detailed system prompt for this subagent."
},
"toolAction": {
"type": "STRING",
"description": "Brief 2-5 word phrase in -ing form describing the specific action. Capitalize like a sentence. Some examples: 'Analyzing directory', 'Searching the web', 'Checking git status', 'Running tests', 'Searching code'."
},
"toolSummary": {
"type": "STRING",
"description": "Brief 2-5 word noun phrase describing the specific task. Capitalize like a sentence. Some examples: 'Directory analysis', 'Web search', 'Git status check', 'Test execution', 'Code search'."
}
},
"required": [
"name",
"description",
"system_prompt",
"toolSummary",
"toolAction"
]
}
}
]
},
{
"functionDeclarations": [
{
"name": "manage_subagents",
"description": "Manage existing subagents.\n\tActions:\n\t* 'list': List active direct subagents with their conversation IDs and live state. Each subagent is reported as a JSON object with this schema:\n\t{"$schema":"https://json-schema.org/draft/2020-12/schema\",\"properties\":{\"role\":{\"type\":\"string\",\"description\":\"The subagent's role, or its type name if no role was set."},"type":{"type":"string","description":"The subagent's type name."},"conversationId":{"type":"string","description":"The subagent's conversation ID, used to message or kill it."},"transcript":{"type":"string","description":"Absolute URI of the subagent's transcript log, if available."},"state":{"type":"string","enum":["running","idle","waiting_for_input","waiting_for_dependents","waiting_for_message","canceling","errored","unspecified"],"description":"The subagent's current lifecycle state."},"stateDetail":{"type":"string","description":"Context for State: the current tool call (name and summary) when running or waiting_for_input, or the failure message when errored."}},"additionalProperties":false,"type":"object"}\n\t* 'kill': Terminate specific subagents and all their descendants.\n\t* 'kill_all': Terminate all subagents and all their descendants.\n\n\tWhen a subagent is killed, its branched workspaces will be deleted, but its logs and artifacts will be preserved.",
"parameters": {
"type": "OBJECT",
"properties": {
"Action": {
"type": "STRING",
"description": "The action to perform. Must be 'list' (list active direct subagents with their live state), 'kill' (terminate specific subagents and all their descendants), or 'kill_all' (terminate all subagents and all their descendants).",
"enum": [
"list",
"kill",
"kill_all"
]
},
"ConversationIds": {
"type": "ARRAY",
"description": "The IDs of the subagents to kill. Required for 'kill'.",
"items": {
"type": "STRING"
}
},
"toolAction": {
"type": "STRING",
"description": "Brief 2-5 word phrase in -ing form describing the specific action. Capitalize like a sentence. Some examples: 'Analyzing directory', 'Searching the web', 'Checking git status', 'Running tests', 'Searching code'."
},
"toolSummary": {
"type": "STRING",
"description": "Brief 2-5 word noun phrase describing the specific task. Capitalize like a sentence. Some examples: 'Directory analysis', 'Web search', 'Git status check', 'Test execution', 'Code search'."
}
},
"required": [
"Action",
"toolSummary",
"toolAction"
]
}
}
]
},
{
"functionDeclarations": [
{
"name": "write_to_file",
"description": "Use this tool to create new files. The file and any parent directories will be created for you if they do not already exist.\n\t\tFollow these instructions:\n\t\t1. By default this tool will error if TargetFile already exists. To overwrite an existing file, set Overwrite to true.\n\t\t2. When creating an artifact, always provide ArtifactMetadata. When creating non-artifact files, do not provide it.",
"parameters": {
"type": "OBJECT",
"properties": {
"ArtifactMetadata": {
"type": "OBJECT",
"description": "Metadata that defines artifact properties. ONLY provide when creating an artifact file in the artifact directory. Omit this field when creating non-artifact files.",
"properties": {
"RequestFeedback": {
"type": "BOOLEAN",
"description": "Set to true if you'd like to request user feedback on this artifact and if the contents of this artifact are executable (e.g., a plan). The user will be provided with a 'Proceed' button to execute it."
},
"Summary": {
"type": "STRING",
"description": "Detailed multi-line summary of the artifact file, after edits have been made. Summary does not need to mention the artifact name and should focus on the contents and purpose of the artifact."
},
"UserFacing": {
"type": "BOOLEAN",
"description": "Set to true if this artifact should be presented to the user. Set to false for scratch scripts, temporary data files, or files that the user does not need to see"
}
},
"required": [
"Summary",
"UserFacing",
"RequestFeedback"
]
},
"CodeContent": {
"type": "STRING",
"description": "The code contents to write to the file."
},
"Description": {
"type": "STRING",
"description": "Brief, user-facing explanation of what this change did. Focus on non-obvious rationale, design decisions, or important context. Don't just restate what the code does."
},
"Overwrite": {
"type": "BOOLEAN",
"description": "Set this to true to overwrite an existing file. WARNING: This will replace the entire file contents. Only use when you explicitly intend to overwrite. Otherwise, use a code edit tool to modify existing files."
},
"TargetFile": {
"type": "STRING",
"description": "The target file to create and write code to. Must be an absolute path."
},
"toolAction": {
"type": "STRING",
"description": "Brief 2-5 word phrase in -ing form describing the specific action. Capitalize like a sentence. Some examples: 'Analyzing directory', 'Searching the web', 'Checking git status', 'Running tests', 'Searching code'."
},
"toolSummary": {
"type": "STRING",
"description": "Brief 2-5 word noun phrase describing the specific task. Capitalize like a sentence. Some examples: 'Directory analysis', 'Web search', 'Git status check', 'Test execution', 'Code search'."
}
},
"required": [
"TargetFile",
"Overwrite",
"CodeContent",
"Description",
"toolSummary",
"toolAction"
]
}
}
]
},
{
"functionDeclarations": [
{
"name": "replace_file_content",
"description": "Use this tool to edit an existing file. Follow these rules:\n1. Use this tool ONLY when you are making a SINGLE CONTIGUOUS block of edits to the same file (i.e. replacing a single contiguous block of text).\n2. Do NOT make multiple parallel calls to this tool for the same file.\n3. To edit multiple, non-adjacent lines of code in the same file, make multiple calls to this tool.\n4. For the ReplacementChunk, specify StartLine, EndLine, TargetContent and ReplacementContent. StartLine and EndLine should specify a range of lines containing precisely the instances of TargetContent that you wish to edit. To edit a single instance of the TargetContent, the range should be such that it contains that specific instance of the TargetContent and no other instances. In TargetContent, specify the precise lines of code to edit. These lines MUST EXACTLY MATCH text in the existing file content. In ReplacementContent, specify the replacement content for the specified target content. This must be a complete drop-in replacement of the TargetContent, with necessary modifications made.\n5. If you are making multiple edits across a single file, make multiple calls to this tool. DO NOT try to replace the entire existing content with the new content, this is very expensive.\n6. You may not edit file extensions: [.ipynb]",
"parameters": {
"type": "OBJECT",
"properties": {
"AllowMultiple": {
"type": "BOOLEAN",
"description": "If true, multiple occurrences of 'targetContent' will be replaced by 'replacementContent' if they are found. Otherwise if multiple occurrences are found, an error will be returned."
},
"Description": {
"type": "STRING",
"description": "Brief, user-facing explanation of what this change did. Focus on non-obvious rationale, design decisions, or important context. Don't just restate what the code does."
},
"EndLine": {
"type": "INTEGER",
"description": "The ending line number of the chunk (1-indexed). Should be at or after the last line containing the target content. Must satisfy StartLine <= EndLine <= number of lines in the file. The target content is searched for within the [StartLine, EndLine] range."
},
"Instruction": {
"type": "STRING",
"description": "A description of the changes that you are making to the file."
},
"ReplacementContent": {
"type": "STRING",
"description": "The content to replace the target content with."
},
"StartLine": {
"type": "INTEGER",
"description": "The starting line number of the chunk (1-indexed). Should be at or before the first line containing the target content. Must satisfy 1 <= StartLine <= EndLine. The target content is searched for within the [StartLine, EndLine] range."
},
"TargetContent": {
"type": "STRING",
"description": "The exact string to be replaced. This must be the exact character-sequence to be replaced, including whitespace. Be very careful to include any leading whitespace otherwise this will not work at all. This must be a unique substring within the file, or else it will error."
},
"TargetFile": {
"type": "STRING",
"description": "The target file to modify. Must be an absolute path. Always specify the target file as the very first argument."
},
"TargetLintErrorIds": {
"type": "ARRAY",
"description": "If applicable, IDs of lint errors this edit aims to fix (they'll have been given in recent IDE feedback). If you believe the edit could fix lints, do specify lint IDs; if the edit is wholly unrelated, do not. A rule of thumb is, if your edit was influenced by lint feedback, include lint IDs. Exercise honest judgement here.",
"items": {
"type": "STRING"
}
},
"toolAction": {
"type": "STRING",
"description": "Brief 2-5 word phrase in -ing form describing the specific action. Capitalize like a sentence. Some examples: 'Analyzing directory', 'Searching the web', 'Checking git status', 'Running tests', 'Searching code'."
},
"toolSummary": {
"type": "STRING",
"description": "Brief 2-5 word noun phrase describing the specific task. Capitalize like a sentence. Some examples: 'Directory analysis', 'Web search', 'Git status check', 'Test execution', 'Code search'."
}
},
"required": [
"TargetFile",
"Instruction",
"Description",
"AllowMultiple",
"TargetContent",
"ReplacementContent",
"StartLine",
"EndLine",
"toolSummary",
"toolAction"
]
}
}
]
},
{
"functionDeclarations": [
{
"name": "generate_image",
"description": "Generate an image or edit existing images based on a text prompt. The resulting image will be saved as an artifact for use. You can use this tool to generate user interfaces and iterate on a design with the USER for an application or website that you are building. When creating UI designs, generate only the interface itself without surrounding device frames (laptops, phones, tablets, etc.) unless the user explicitly requests them. You can also use this tool to generate assets for use in an application or website.",
"parameters": {
"type": "OBJECT",
"properties": {
"AspectRatio": {
"type": "STRING",
"description": "Optional aspect ratio for the generated image. Supported values: '1:1', '2:3', '3:2', '3:4', '4:3', '9:16', '16:9'. Default is '1:1'."
},
"ImageName": {
"type": "STRING",
"description": "Name of the generated image to save. Should be all lowercase with underscores, describing what the image contains. Maximum 3 words. Example: 'login_page_mockup'"
},
"ImagePaths": {
"type": "ARRAY",
"description": "Optional absolute paths to the images to use in generation. You can pass in images here if you would like to edit, combine, or use as references. You can pass in artifact images and any images in the file system. Note: you cannot pass in more than 3 images.",
"items": {
"type": "STRING"
}
},
"Prompt": {
"type": "STRING",
"description": "The text prompt to generate an image for or the edit instructions."
},
"toolAction": {
"type": "STRING",
"description": "Brief 2-5 word phrase in -ing form describing the specific action. Capitalize like a sentence. Some examples: 'Analyzing directory', 'Searching the web', 'Checking git status', 'Running tests', 'Searching code'."
},
"toolSummary": {
"type": "STRING",
"description": "Brief 2-5 word noun phrase describing the specific task. Capitalize like a sentence. Some examples: 'Directory analysis', 'Web search', 'Git status check', 'Test execution', 'Code search'."
}
},
"required": [
"Prompt",
"ImageName",
"toolSummary",
"toolAction"
]
}
}
]
},
{
"functionDeclarations": [
{
"name": "read_url_content",
"description": "Fetch content from a URL via HTTP request (invisible to USER). Use when: (1) extracting text from public pages, (2) reading static content/documentation, (3) batch processing multiple URLs, (4) speed is important, or (5) no visual interaction needed. Converts HTML to markdown. No JavaScript execution, no authentication. For pages requiring login, JavaScript, or USER visibility, use read_browser_page instead.",
"parameters": {
"type": "OBJECT",
"properties": {
"Url": {
"type": "STRING",
"description": "URL to read content from"
},
"toolAction": {
"type": "STRING",
"description": "Brief 2-5 word phrase in -ing form describing the specific action. Capitalize like a sentence. Some examples: 'Analyzing directory', 'Searching the web', 'Checking git status', 'Running tests', 'Searching code'."
},
"toolSummary": {
"type": "STRING",
"description": "Brief 2-5 word noun phrase describing the specific task. Capitalize like a sentence. Some examples: 'Directory analysis', 'Web search', 'Git status check', 'Test execution', 'Code search'."
}
},
"required": [
"Url",
"toolSummary",
"toolAction"
]
}
}
]
},
{
"functionDeclarations": [
{
"name": "search_web",
"description": "Performs a web search for a given query. Returns a summary of relevant information along with URL citations.",
"parameters": {
"type": "OBJECT",
"properties": {
"domain": {
"type": "STRING",
"description": "Optional domain to recommend the search prioritize"
},
"query": {
"type": "STRING"
},
"toolAction": {
"type": "STRING",
"description": "Brief 2-5 word phrase in -ing form describing the specific action. Capitalize like a sentence. Some examples: 'Analyzing directory', 'Searching the web', 'Checking git status', 'Running tests', 'Searching code'."
},
"toolSummary": {
"type": "STRING",
"description": "Brief 2-5 word noun phrase describing the specific task. Capitalize like a sentence. Some examples: 'Directory analysis', 'Web search', 'Git status check', 'Test execution', 'Code search'."
}
},
"required": [
"query",
"toolSummary",
"toolAction"
]
}
}
]
},
{
"functionDeclarations": [
{
"name": "ask_question",
"description": "Use this tool to ask the user one or more multiple-choice questions, with the goal of:\n- Clarifying underspecified requirements\n- Soliciting design feedback or user preferences\n- Addressing ambiguous user intent\n- Picking a solution from a list of options\n\nWhen called, this tool renders an interactive modal containing the question, selectable options, a default write-in option, and Submit/Skip buttons. Execution is blocked until the user responds.\n\nGuidance:\n- When specifying files in the question, use github markdown links (e.g. filename).\n- Don't use this tool to ask trivial questions that can be answered with a single word (e.g. yes/no); output regular text to ask these questions.\n- Don't include an 'other' option for write-in responses; one is always provided in the UI by default.\n- Don't enumerate the options; they are enumerated by default.\n- Don't include "Select all options that apply", or similar, in the question; the UI already includes this.\n- If you recommend any options, list it first and prefix the option text with "(Recommended)".\n- Format options as the user's direct response instead of describing your own actions.\n- Set 'IsMultiSelect' to true to allow the user to select multiple options with checkboxes.\n",
"parameters": {
"type": "OBJECT",
"properties": {
"questions": {
"type": "ARRAY",
"description": "The list of questions to ask.",
"items": {
"type": "OBJECT",
"properties": {
"is_multi_select": {
"type": "BOOLEAN",
"description": "If true, the user can select multiple options."
},
"options": {
"type": "ARRAY",
"description": "The text for each option, formatted as the user's response. Must have at least 2 options. Do NOT add an 'Other' option to questions.",
"items": {
"type": "STRING"
}
},
"question": {
"type": "STRING",
"description": "The question to ask the user. Do NOT add 'select all that apply' or similar text to the question title."
}
}
}
},
"toolAction": {
"type": "STRING",
"description": "Brief 2-5 word phrase in -ing form describing the specific action. Capitalize like a sentence. Some examples: 'Analyzing directory', 'Searching the web', 'Checking git status', 'Running tests', 'Searching code'."
},
"toolSummary": {
"type": "STRING",
"description": "Brief 2-5 word noun phrase describing the specific task. Capitalize like a sentence. Some examples: 'Directory analysis', 'Web search', 'Git status check', 'Test execution', 'Code search'."
}
},
"required": [
"toolSummary",
"toolAction"
]
}
}
]
}
],
"labels": {
"last_execution_id": "fb7b9c90-03c2-4e6f-983e-369ef8fc9e5e",
"last_step_index": "20",
"model_enum": "MODEL_PLACEHOLDER_M298",
"request_id": "231efd36-2a13-4fea-ab31-04cc500a7ec2-9",
"trajectory_id": "231efd36-2a13-4fea-ab31-04cc500a7ec2",
"used_claude": "false",
"used_claude_conservative": "false",
"used_non_gemini_model": "false"
},
"generationConfig": {
"maxOutputTokens": 65536,
"thinkingConfig": {
"includeThoughts": true,
"thinkingBudget": -1
}
},
"sessionId": "-3750763034362895579"
},
"model": "gemini-3.7-flash-high",
"userAgent": "antigravity",
"requestType": "agent"
}


Claude样本 (`docs/claude样本.txt`)

{
"project": "aicode-consumers",
"requestId": "agent/3c49dacf-99bd-43b5-b3eb-0fa82b590761/1790599454364/a625a450-b0e2-435f-b99f-3c821f9149cc/22",
"request": {
"contents": [
{
"role": "user",
"parts": [
{
"text": "\n你好 请你稍作思考后并行调用三个工具试试 只需要输出helloworld就可以了\n\n\nThe current local time is: 2026-09-28T20:42:38+08:00.\n\n\nThe user changed setting Model Selection from None to Claude Opus 4.6 (Thinking). No need to comment on this change if the user doesn't ask about it. If reporting what model you are, please use a human readable name instead of the exact string.\n"
}
]
},
{
"role": "user",
"parts": [
{
"text": "\n你好 请你稍作思考后并行调用三个工具试试 只需要输出helloworld就可以了\n\n\nThe current local time is: 2026-09-28T20:42:56+08:00.\n\n\nThe user changed setting Model Selection from Claude Opus 4.6 (Thinking) to Claude Sonnet 4.6 (Thinking). No need to comment on this change if the user doesn't ask about it. If reporting what model you are, please use a human readable name instead of the exact string.\n"
}
]
},
{
"role": "model",
"parts": [
{
"text": "The user wants me to think briefly and then call three tools in parallel, with the output just being "hello world". Let me pick three simple parallel tool calls.",
"thought": true
},
{
"functionCall": {
"id": "toolu_vrtx_01CGxUTaSG6NpBcdDADhYa9B",
"name": "run_command",
"args": {
"CommandLine": "echo helloworld",
"Cwd": "e:\code\Antigravity-Manager",
"WaitMsBeforeAsync": 3000,
"toolAction": "Running echo command",
"toolSummary": "Echo helloworld"
}
},
"thoughtSignature": "RXBrRENwUUJDQklRQWhnQ0trRElMR2lTdkQ3bXVnRExDOWVHZy9UNUV2T2RnMUpISUZia2hlR3IrVDFPRDFEbVZKV2ZwOTZuVUZBVGJJaXNpK3EwYWdPMzBaZ3B4U2llZGpJV2FlMStNaEZqYkdGMVpHVXRjMjl1Ym1WMExUUXROamdBUWdoMGFHbHVhMmx1WjFvTU1qRXdNVEkxTlRBM01qSTRjaERhdGtpWjdGSW1YM0V2S05TaVhXUEZpQUVCcUFIU3dlblZCckFCQWhJTUt0eUQxUExXaXRpTm5uTWtHZ3hOWk9uZ21zVThlT3FWMjhvaU1MZlEwNjB0a2JTcXFVTzhIbnNnTFkxZXFaTWNEaEFKdFdqalBJT0RaTkNoeGtqaitSZjlYU2hpYnlXSE1ZUUw3Q3F4QVd2UmlCTmgxL2ZXOWZLNVpWcmIwYkR4SjRibEtuZnRXTThFTnMrR2gxanNnYjhGTlNDUmpJMEZYbTU3ai9xb3N1aDArRVZZTkNxcXlpU2ZPRVlsK2diMmpBTVMwTGlraTlYTzkxRURjZU1nZkJSbmdVWmx0WS9tYnBIL1VUY2pvRTR1ekY1dVUzVHI4cEJMcE1hQVVjcDNPYzlUUGFaL0REOEFJb3ZRODdCQksyL2hBek9xTW82L2Z0RjJtNmFTZ1EwU2wyYlg3WkczNGVKZXZ5emNzT0Nib3dsbXlXc2MvZ2ZRWSs5eHpBdExjQmdC"
},
{
"functionCall": {
"id": "toolu_vrtx_01QsPpjMCGu2A5DuCbXevi3j",
"name": "run_command",
"args": {
"CommandLine": "node -e "console.log('helloworld')"",
"Cwd": "e:\code\Antigravity-Manager",
"WaitMsBeforeAsync": 3000,
"toolAction": "Running node script",
"toolSummary": "Node helloworld"
}
}
},
{
"functionCall": {
"id": "toolu_vrtx_01XHBqDJ3WUo6GT2exERV7ib",
"name": "run_command",
"args": {
"CommandLine": "python -c "print('helloworld')"",
"Cwd": "e:\code\Antigravity-Manager",
"WaitMsBeforeAsync": 3000,
"toolAction": "Running python script",
"toolSummary": "Python helloworld"
}
}
}
]
},
{
"role": "user",
"parts": [
{
"functionResponse": {
"id": "toolu_vrtx_01CGxUTaSG6NpBcdDADhYa9B",
"name": "run_command",
"response": {
"output": "Created At: 2026-09-28T20:43:00+08:00\nCompleted At: 2026-09-28T20:43:03+08:00\n\nThe command exited with code 0.\nOutput:\nhelloworld\r\n\n"
}
}
},
{
"functionResponse": {
"id": "toolu_vrtx_01QsPpjMCGu2A5DuCbXevi3j",
"name": "run_command",
"response": {
"output": "Created At: 2026-09-28T20:43:01+08:00\nCompleted At: 2026-09-28T20:43:04+08:00\n\nThe command exited with code 0.\nOutput:\nhelloworld\n\n"
}
}
},
{
"functionResponse": {
"id": "toolu_vrtx_01XHBqDJ3WUo6GT2exERV7ib",
"name": "run_command",
"response": {
"output": "Created At: 2026-09-28T20:43:02+08:00\nCompleted At: 2026-09-28T20:43:05+08:00\n\nThe command exited with code 0.\nOutput:\nhelloworld\r\n\n"
}
}
}
]
},
{
"role": "model",
"parts": [
{
"text": "三个工具并行执行完毕,均成功输出:\n\n| 工具 | 命令 | 输出 |\n|------|------|------|\n| PowerShell echo | echo helloworld | helloworld |\n| Node.js | node -e \"console.log('helloworld')\" | helloworld |\n| Python | python -c \"print('helloworld')\" | helloworld |\n\nhelloworld ✅"
}
]
},
{
"role": "user",
"parts": [
{
"text": "\n请你再串行三个试试\n\n\nThe current local time is: 2026-09-28T20:43:45+08:00.\n"
}
]
},
{
"role": "model",
"parts": [
{
"text": "The user wants me to call three tools serially (one after another, waiting for each to complete before calling the next).",
"thought": true
},
{
"text": "好的,串行执行——每一步等上一步完成后再继续。\n\n第一步:",
"thoughtSignature": "RXZFQ0NwUUJDQklRQWhnQ0trQTdOdW40MFpSNi9ONWswZ1dMYUFQT0g2eFFEVjZRc0taSUNwald2dVV4K3VCQ2pKS0xFdm9tMFM5R1lNZFZZdEZsNWphT3dvRC94TWYvT3Jxcno4eFJNaEZqYkdGMVpHVXRjMjl1Ym1WMExUUXROamdBUWdoMGFHbHVhMmx1WjFvTU1qRXdNVEkxTlRBM01qSTRjaERhdGtpWjdGSW1YM0V2S05TaVhXUEZpQUVCcUFHRXd1blZCckFCQWhJTWt5TFV1cEplbmFEcDZtMnRHZ3dlenBLdERRMmtWempiM3hnaU1OY3dIdHNsL1FzdlVVOTlvc2NHTUNDeDFSR2hWVjFPcG1YV2E1TTV1dnZOby9VRVZMYkZtaVIwSkxTblV6eVV2U3FKQWFla1BGZkl6RGo0K2dIKyt2S05ETFF2Z0NPdTVvL21yL2x0NWxwUkxXUDhRK2hhSmVmNEFiSDlPbGxwNkhycHk3UUxDTDNFUlJqTDc5K3NLamtDbTdFZDVXNkdJTnVucGR4VFBCL0RYY3IyQk5lN0FMUnJsdFc1NG1xRzAya0JTMFJEODFVRTlhY1h3M2VPcldPYUlSdHp5MW5FUXhmL0tiYWpPTWJJVXlZcmdXcDNpdVRWWUd2dUdBRT0="
},
{
"functionCall": {
"id": "toolu_vrtx_01WvRiCPbX7cLJbCuuVCf48S",
"name": "run_command",
"args": {
"CommandLine": "echo [1/3] helloworld from PowerShell",
"Cwd": "e:\code\Antigravity-Manager",
"WaitMsBeforeAsync": 3000,
"toolAction": "Running first command",
"toolSummary": "Step 1 echo"
}
}
}
]
},
{
"role": "user",
"parts": [
{
"functionResponse": {
"id": "toolu_vrtx_01WvRiCPbX7cLJbCuuVCf48S",
"name": "run_command",
"response": {
"output": "Created At: 2026-09-28T20:43:51+08:00\nCompleted At: 2026-09-28T20:43:52+08:00\n\nThe command exited with code 0.\nOutput:\n[1/3]\r\nhelloworld\r\nfrom\r\nPowerShell\r\n\n"
}
}
}
]
},
{
"role": "model",
"parts": [
{
"text": "第二步:"
},
{
"functionCall": {
"id": "toolu_vrtx_01Bm4KhPpBvEhGMyLgooe3KZ",
"name": "run_command",
"args": {
"CommandLine": "node -e "console.log('[2/3] helloworld from Node.js')"",
"Cwd": "e:\code\Antigravity-Manager",
"WaitMsBeforeAsync": 3000,
"toolAction": "Running second command",
"toolSummary": "Step 2 node"
}
}
}
]
},
{
"role": "user",
"parts": [
{
"functionResponse": {
"id": "toolu_vrtx_01Bm4KhPpBvEhGMyLgooe3KZ",
"name": "run_command",
"response": {
"output": "Created At: 2026-09-28T20:43:55+08:00\nCompleted At: 2026-09-28T20:43:56+08:00\n\nThe command exited with code 0.\nOutput:\n[2/3] helloworld from Node.js\n\n"
}
}
}
]
},
{
"role": "model",
"parts": [
{
"text": "第三步:"
},
{
"functionCall": {
"id": "toolu_vrtx_01GjMx3cEL4muRw6sivBdFAq",
"name": "run_command",
"args": {
"CommandLine": "python -c "print('[3/3] helloworld from Python')"",
"Cwd": "e:\code\Antigravity-Manager",
"WaitMsBeforeAsync": 3000,
"toolAction": "Running third command",
"toolSummary": "Step 3 python"
}
}
}
]
},
{
"role": "user",
"parts": [
{
"functionResponse": {
"id": "toolu_vrtx_01GjMx3cEL4muRw6sivBdFAq",
"name": "run_command",
"response": {
"output": "Created At: 2026-09-28T20:43:59+08:00\nCompleted At: 2026-09-28T20:44:01+08:00\n\nThe command exited with code 0.\nOutput:\n[3/3] helloworld from Python\r\n\n"
}
}
}
]
},
{
"role": "model",
"parts": [
{
"text": "三步串行执行完毕 ✅\n\n| 步骤 | 工具 | 输出 | 完成时间 |\n|------|------|------|----------|\n| 1 | PowerShell echo | [1/3] helloworld from PowerShell | 20:43:52 |\n| 2 | Node.js | [2/3] helloworld from Node.js | 20:43:56 |\n| 3 | Python | [3/3] helloworld from Python | 20:44:01 |\n\n与并行(约同时完成)不同,串行每步之间都有明显的时间间隔,严格按顺序依次执行。"
}
]
},
{
"role": "user",
"parts": [
{
"text": "\n这次并行试试 \n\n\nThe current local time is: 2026-09-28T20:44:06+08:00.\n"
}
]
},
{
"role": "model",
"parts": [
{
"text": "The user wants parallel tool calls again. Let me make three parallel calls.",
"thought": true
},
{
"functionCall": {
"id": "toolu_vrtx_01GmrcgKAAxH8DQCTDZcsUdd",
"name": "run_command",
"args": {
"CommandLine": "echo [1/3] helloworld from PowerShell",
"Cwd": "e:\code\Antigravity-Manager",
"WaitMsBeforeAsync": 3000,
"toolAction": "Running first command",
"toolSummary": "Step 1 echo"
}
},
"thoughtSignature": "RXNJQ0NwUUJDQklRQWhnQ0trQks4TzR5N2t3UzZXZXNaVDRiV3Y2NThoNEFlSzNmRWhFQ1p0OE9WL1RTWjY0UlVMS053aCtLbEx2SEVaQ0dWSlRlRU1HZHhjd1JqcnJzUFJ2dlhCQ0tNaEZqYkdGMVpHVXRjMjl1Ym1WMExUUXROamdBUWdoMGFHbHVhMmx1WjFvTU1qRXdNVEkxTlRBM01qSTRjaERhdGtpWjdGSW1YM0V2S05TaVhXUEZpQUVCcUFHWXd1blZCckFCQWhJTXF0UE1CRHdGa3lBRmVyZHpHZ3dnTEU3SEpseW81ZWF5QWNVaU1LREQ4WXk1UHI0ODFzdlVVTHhTRFZoU0hDZnd4d3p1YkpuektuQlpNVWJ1OHBIRFlVUmd5R0J4NDY5d0kyR2UwaXBiWTJ2ZjQyMlg0NFo5YW9CK0JCbmtic0s2TTFoQkNtb3ZncTdFN1lUbDlrSWt2ZlQ2UFZaZGQvdERrdEVja21wd1NrVmQ2L2lHTXlaUzFmWGxFL3ZFeHphZWtDZnpKbzhlekxtSmI2M2ErTmV0cGcrdE03ajFhVWJzS0JnQg=="
},
{
"functionCall": {
"id": "toolu_vrtx_01FzmGjxrRksDJBGHDepYv2C",
"name": "run_command",
"args": {
"CommandLine": "node -e "console.log('[2/3] helloworld from Node.js')"",
"Cwd": "e:\code\Antigravity-Manager",
"WaitMsBeforeAsync": 3000,
"toolAction": "Running second command",
"toolSummary": "Step 2 node"
}
}
},
{
"functionCall": {
"id": "toolu_vrtx_01SWBU6A3pphSPEjtb9VUEiE",
"name": "run_command",
"args": {
"CommandLine": "python -c "print('[3/3] helloworld from Python')"",
"Cwd": "e:\code\Antigravity-Manager",
"WaitMsBeforeAsync": 3000,
"toolAction": "Running third command",
"toolSummary": "Step 3 python"
}
}
}
]
},
{
"role": "user",
"parts": [
{
"functionResponse": {
"id": "toolu_vrtx_01GmrcgKAAxH8DQCTDZcsUdd",
"name": "run_command",
"response": {
"output": "Created At: 2026-09-28T20:44:09+08:00\nCompleted At: 2026-09-28T20:44:11+08:00\n\nThe command exited with code 0.\nOutput:\n[1/3]\r\nhelloworld\r\nfrom\r\nPowerShell\r\n\n"
}
}
},
{
"functionResponse": {
"id": "toolu_vrtx_01FzmGjxrRksDJBGHDepYv2C",
"name": "run_command",
"response": {
"output": "Created At: 2026-09-28T20:44:10+08:00\nCompleted At: 2026-09-28T20:44:13+08:00\n\nThe command exited with code 0.\nOutput:\n[2/3] helloworld from Node.js\n\n"
}
}
},
{
"functionResponse": {
"id": "toolu_vrtx_01SWBU6A3pphSPEjtb9VUEiE",
"name": "run_command",
"response": {
"output": "Created At: 2026-09-28T20:44:11+08:00\nCompleted At: 2026-09-28T20:44:14+08:00\n\nThe command exited with code 0.\nOutput:\n[3/3] helloworld from Python\r\n\n"
}
}
}
]
}
],
"systemInstruction": {
"role": "user",
"parts": [
{
"text": "\nYou are Antigravity, a powerful agentic AI coding assistant designed by the Google Deepmind team working on Advanced Agentic Coding.\nYou are pair programming with a USER to solve their coding task. The task may require creating a new codebase, modifying or debugging an existing codebase, or simply answering a question.\nThe USER will send you requests, which you must always prioritize addressing. User requests are enclosed within tags.\n\n\nThe USER's OS version is windows.\nThe user has 1 active workspaces, each defined by a URI and a CorpusName. Multiple URIs potentially map to the same CorpusName. The mapping is shown as follows in the format [URI] -> [CorpusName]:\ne:\code\Antigravity-Manager -> lbjlaq/Antigravity-Manager\nCode relating to the user's requests should be written in the locations listed above. Avoid writing project code files to tmp, in the .gemini dir, or directly to the Desktop and similar folders unless explicitly asked.\nApp Data Directory: C:\Users\Administrator\.gemini\antigravity\nConversation ID: 3c49dacf-99bd-43b5-b3eb-0fa82b590761\n\n\nThe following are user-defined rules that you MUST ALWAYS FOLLOW WITHOUT ANY EXCEPTION. These rules take precedence over any following instructions.\nReview them carefully and always take them into account when you generate responses and code:\n<RULE[e:\code\Antigravity-Manager\AGENTS.md]>\n# Project Maintenance Guidelines\n\n- Architecture: This project is a gateway that aggregates four AI protocols — OpenAI Responses, OpenAI Chat Completions, Anthropic Claude, and Google Gemini — and outputs Antigravity-style Gemini protocol format.\n- Pipeline First: Keep the pipeline strictly decoupled from specific protocols. The four protocols function purely as Gemini adapters. Adapters are restricted to parameter normalization, payload transformation, protocol divergence adaptation, and edge cases unsolvable within the pipeline stage. The pipeline stage uniformly handles the converted Gemini payloads, including thinking block backfilling, thinking budget filtering and backfilling, unified context structural alignment, prefix stability, and the sanitization of risky prompts and request headers.\n- Backend Fix Strategy: Prioritize protocol-agnostic, generic fixes within the pipeline rather than localized adapter modifications. Treat adapter-level patches as a last resort only when a generic pipeline solution is infeasible or degrades compatibility.\n- UI Design & Headless Compatibility:\n - Minimalist & Contextual UI: Prioritize user-friendly, non-intrusive interactions. Reuse existing design conventions (e.g., pill toggle buttons, badge switches, or contextual setting panels) placed strictly within their most relevant sections rather than scattering unrelated controls.\n - Headless & CLI Parity: Ensure GUI configurations maintain functional parity across headless servers, CLI environments, and cross-platform environments. Provide configuration file fields, environment variable overrides, or dedicated CLI flags/commands for essential settings.\n - Cross-Platform Compatibility: Evaluate every code addition and dependency change for seamless cross-platform support.\n- Code Quality: Prioritize root-cause, future-proof fixes rather than hardcoded logic, dead code, or speculative changes. Prioritize generalized solutions that cover entire classes of problems rather than one-off patches.\n - Model Routing Example: Prioritize wildcard patterns (e.g., gemini-*-flash-*) to anticipate future model releases rather than exact string matches.\n - Prompt Sanitization Example: For agent-client prompt sanitization, prioritize regex-based pattern matching over static keyword replacement, ensuring full coverage without stripping pipeline system prompts or user queries.\n- Formatting & CI Discipline:\n - Unit Testing: Keep focused — run targeted tests for touched modules locally; CI compiles test targets without executing them. Skip tests for trivial edits (constants, prompts, or config tweaks). No need to run the full suite locally.\n - Local Test Discretion: For strings, constants, hardcoded values, or other trivial edits, ask after the task whether a small functional check is wanted rather than testing on your own. Self-test only when four or more core backend or frontend interaction files are involved; hardcoded-only edits do not count as core-file changes. In general, follow the user's preference on whether to test.\n - Pre-flight Checks: Run the essentials before submitting PRs or release tags:\n - cd src-tauri && cargo fmt -- --check (for Rust edits)\n - cd src-tauri && cargo clippy --all-targets --all-features (comprehensive Rust gate, already includes compilation — no separate cargo check needed)\n - npm run build (when src/ or frontend configs changed)\n - Rely on CI for full-app compilation (tauri build) and full test execution. Local pre-flight covers fmt + clippy + frontend build only.\n- Release Channels & Discipline:\n - Release Channel Separation:\n - Stable Releases (正式版): Exclusively on main. Deploys official production packages, updates Docker/GitHub latest tags, and services automatic update channels.\n - Preview Releases (预览版 / Beta): Exclusively on beta. Independently builds and publishes pre-releases (makeLatest: false, prerelease: true) without touching production update channels.\n - Maintainer Staging Protocol:\n - When introducing new changes (features, major refactors, non-trivial fixes), prompt and confirm with maintainers whether to implement and test on beta branch first.\n - Validate stability on beta (with optional independent preview builds) prior to merging into main.\n - Standard Release Workflow:\n 1. Atomic Version Sync: Run npm run bump <patch|minor|beta|version> to synchronize all project manifests and generate changelog skeletons.\n 2. Documentation & Attribution:\n - Audit Git history (<last-tag>..HEAD) and merged PRs to summarize all authors, co-authors, and linked Issues/PRs (Fixes #xxx, PR #xxx). Attribute every contributor inline (Thanks to @username) in CHANGELOG.md (and CHANGELOG_EN.md).\n - Synchronize README Changelog (同步首页更新日志): For stable releases, you MUST update the release summary in both README.md (under "## 📝 更新日志") and README_EN.md (under "## 📝 Changelog"). Never update only CHANGELOG.md while leaving README.md / README_EN.md with outdated release notes. Pre-release / beta versions remain exclusively in changelogs; stable releases require full synchronization across both README files.\n 3. Pre-flight before Tagging: Run the Pre-flight Checks above on the exact commit to be tagged.\n 4. Commit, Tag & Push: Push stable releases to main (git tag vX.Y.Z && git push origin vX.Y.Z), reserving beta exclusively for pre-releases (git tag vX.Y.Z-beta.N && git push origin vX.Y.Z-beta.N). The release gate strictly intercepts cross-branch misplacement. Tags must match CHANGELOG.md headings character-for-character (including v prefix and pre-release suffix).\n - Full procedure: See docs/RELEASE_GUIDE.md for bump options, changelog templates, and rollback steps.\n- Thinking Cache Invalidation Control (发版清理建议):\n - File: src/components/common/SuggestionDeleteThinkingModal.tsx\n - Routine releases (no prompt): Keep SUGGESTION_DELETE_THINKING_STORE = false.\n - Architecture / schema refactors (prompt users to clean once):\n 1. Set SUGGESTION_DELETE_THINKING_STORE = true.\n 2. Set SUGGESTION_TARGET_VERSION = '<version>' (e.g. '4.8.2').\n 3. On upgrade, users with existing cache get a one-time prompt; action state persists in gui_config.json.\n- Branch & History Hygiene:\n - Branch from remote bases (origin/beta for staged features, origin/main for direct hotfixes) rather than local branches to prevent untracked ancestor commits.\n - Inspect in-flight PRs (gh pr list --base main) before rewriting published tips, avoiding force-pushes across shared branches.\n - Retain local rollback refs (backup/*) before history rewrites, confirming zero content drift via git diff --stat <backup> HEAD.\n- PR Scope & Grouping:\n - Single Problem Scope: A PR represents a cohesive collection of fixes or features dedicated to a single problem class. Keep unrelated concerns (such as governance, release tooling, or documentation) in isolated PRs.\n - Self-Contained & Individually Revertable: A PR may contain multiple commits, but each commit must represent an independent, self-contained functional unit that is individually revertable, avoiding messy or tangled changesets.\n - Local Convergence & Final-State Commits: Commit freely during local debugging on development branches; however, before opening or merging a PR, audit and consolidate scattered iterative attempts into clean, high-quality units. Each consolidated commit must describe only its successful final state and rationale, eliminating intermediate trial-and-error noise.\n - Review & Template Alignment: Route every PR through peer review and complete .github/PULL_REQUEST_TEMPLATE.md (problem classification, behavior alterations, unverified paths, and rollback strategy).\n- Contributor Respect & Attribution:\n - Preserve authorship by preferring the contributor's own PR for squash commits, or attaching explicit Co-authored-by: trailers on merge commits and proxy PRs.\n - Disclose costs before merging: highlight affected existing behaviors and unverified paths alongside improvements.\n - Own tooling and documentation gaps directly rather than attributing downstream frictions to contributors.\n - Accompany every rejection with a concrete file-by-file accept/drop breakdown and an actionable path forward.\n- Risky Path Replacement:\n - Keep an explicit fallback to the verified path whenever you replace it (e.g. "0 matches found → full restart"). A silent no-op is worse than the failure it was meant to fix.\n - Order side effects to fail before the point of no return: write credentials before killing a process, validate before deleting.\n - Detect broadly, act narrowly: a matcher may recognize a whole class of problems, while its effect stays inside the intended data — not across line breaks, tags, or other clauses. Bound every wait with a timeout.\n\nMaintained by @jeikl\n</RULE[e:\code\Antigravity-Manager\AGENTS.md]>\n\n\nYou can use specialized 'skills' to help you with complex tasks. Each skill has a name and a description listed below.\n\nSkills are folders of instructions, scripts, and resources that extend your capabilities for specialized tasks. Each skill folder contains:\n- SKILL.md (required): The main instruction file with YAML frontmatter (name, description) and detailed markdown instructions\n\nMore complex skills may include additional directories and files as needed, for example:\n- scripts/ - Helper scripts and utilities that extend your capabilities\n- examples/ - Reference implementations and usage patterns\n- resources/ - Additional files, templates, or assets the skill may reference\n- references/ - Contains additional documentation that agents can read when needed\n\n\nIf a skill seems relevant to your current task, you MUST read its SKILL.md instructions using view_file before proceeding. You may skip this step only if you are delegating the skill-related task to a subagent that will read and follow the instructions itself.\n\nWhen calling view_file on these skill paths, always use the exact path provided in the "Available skills" list below.\n\nAvailable skills:\n- agy-customizations (C:\Users\Administrator\.gemini\antigravity\builtin\skills\agy-customizations\SKILL.md): Comprehensive guide and reference for the Antigravity Customization System. Use to explain how customizations work, their loading priority, discovery mechanisms, and to guide the creation of skills, rules, plugins, hooks, and MCP servers.\n- antigravity-guide (C:\Users\Administrator\.gemini\antigravity\builtin\skills\antigravity_guide\SKILL.md): Provides a comprehensive guide, quick reference, and sitemap for Google Antigravity (AGY), including the Antigravity CLI (agy), Antigravity 2.0, Antigravity IDE, Python SDK, slash commands, keybindings, and customizations (skills, rules, MCP, sidecars). Activate this skill when the user asks questions about how to use, configure, or customize Antigravity, AGY, the agy CLI, the Antigravity IDE, or Antigravity 2.0.\n- generative_ui (C:\Users\Administrator\.gemini\antigravity\builtin\skills\generative_ui\SKILL.md): How to render rich interactive HTML widgets inline in the chat or as standalone artifacts. Use this skill when you want to show the user diagrams, data visualizations, interactive controls, educational walkthroughs, or any rich visual content beyond plain text and markdown.\n- migrate-workflows (C:\Users\Administrator\.gemini\antigravity\builtin\skills\migrate-workflows\SKILL.md): Automatically migrate legacy workflows to modern skills across global and workspace configurations. Scans for existing workflows, creates target SKILL.md files, and safely archives old workflow files.\n\n\n\n\n## Invoking Subagents\n\nSubagents can be invoked using the invoke_subagent tool. You can invoke an existing subagent by name, or define a new subagent for this conversation using the define_subagent tool, and then invoke it. Agents defined by the define_subagent tool are available for the duration of this conversation. After launching a subagent, you do NOT need to poll or check your inbox in a loop. The system will automatically notify you when the subagent sends a message. Simply proceed with other work or stop calling tools, and you will be notified when there is a message to process.\n\n## Communicating with Another Agent\n\nUse the send_message tool to send a message to another agent by its conversation ID (returned by invoke_subagent). This tool is ONLY for communicating with other agents.\n\nDo NOT use send_message to communicate with the user. Instead, output visible text to communicate with the user.\n\nAvailable subagents:\n- self: Subagent that inherits the parent agent's full configuration including tools, system prompt, and model. Use this when you need to run a task in a separate conversation context but with the same capabilities as the current agent.\n- research: Research subagent with read-only tools for exploring the codebase, searching the web, and reading files. Delegate to this agent when you need to run a task in a separate conversation context but with the same capabilities as the current agent, when a research task requires many search and file-reading steps that would clutter your context, or when you need a broad survey of the codebase or documentation. Prefer doing research yourself for quick, targeted lookups.\n\nAfter launching a subagent, you do NOT need to poll or check your inbox in a loop. The system will automatically notify you when the subagent sends a message. Simply proceed with other work or stop calling tools, and you will be notified when there is a message to process.\n\n\n\nYou are connected to a messaging system where you may receive messages from: agents, background tasks, user-queued messages.\n\n## Receiving Messages\n\nYou receive messages automatically at the start of each invocation. All messages are delivered in full directly into your context — no manual retrieval is needed.\n\n## Reactive Wakeup (No Polling Needed)\n\nThe system automatically resumes your execution when:\n- A message arrives from a subagent or peer agent\n- A background task completes or sends you a notification\n- A user-queued message is ready to be dequeued\n\nThis means you do NOT need to poll in a loop while waiting for messages or updates. After launching anything that performs work asynchronously, you may continue other work or simply stop by calling no more tools. The system will notify you when there is something to process.\n\n\nTranscripts are located directly at <appDataDir>\\brain\\<conversation-id>\\.system_generated\\logs/transcript.jsonl (and transcript_full.jsonl).\n- Start with transcript.jsonl (compact). When truncated_fields is present, read only the specific corresponding line in transcript_full.jsonl.\n- Search subagents by grepping invoke_subagent in transcript.jsonl.\n- Link conversations using [<label>](conversation://<conversation-id>).\n\n# File Format\nTranscripts are in JSON Lines (JSONL) format. Each line is a single JSON object representing one "step" or action in the conversation.\nEach JSON object contains fields such as:\n- step_index: The index of the step in the trajectory.\n- source: The source of the action (e.g., USER_EXPLICIT, MODEL, SYSTEM).\n- type: The type of the step. Particular steps of interest are USER_INPUT, which represents a user's prompt, and PLANNER_RESPONSE, which represents the agent's response and tool calls.\n- status: The status of the step (e.g., DONE, ERROR).\n- created_at: The ISO 8601 timestamp of when the step occurred.\n- content: The text content of the step (e.g., the user's request, the model's response, or tool responses).\n- thinking: The model's internal reasoning / chain-of-thought (for PLANNER_RESPONSE steps).\n- tool_calls: An array of tool calls made in this step, including their arguments.\n- media: An array of media (e.g. images) attached to the step, each with a mime_type and a uri referencing the media file on disk. Media bytes are not stored in the transcript; view the uri path to see the media.\n- truncated_fields: An array of field names that were truncated (e.g., [\"content\"], [\"thinking\"], [\"tool_calls\"]). Only present in transcript.jsonl when truncation occurred (never in transcript_full.jsonl). When present, read the corresponding line in transcript_full.jsonl for the complete content.\n\n\n\nArtifacts are special markdown (.md) documents that you can create to present structured information to the user.\nAll artifacts should be written to the artifact directory: <appDataDir>\\brain\\<conversation-id>. You do NOT need to create this directory yourself, it will be created automatically when you create artifacts.\n\n# When to Use Artifacts\n\nUse artifacts for:\n- Extensive reports and analysis summaries\n- Persistent information you'll update over time (task lists, experiment logs)\n- Code changes formatted as diffs\n\nDon't use artifacts for:\n- Simple one-off answers or very short paragraph content - just respond directly\n- Asking questions or requesting user input - just ask directly\n\nAfter creating or updating an artifact, DO NOT re-summarize the artifact contents in your response to the user. Instead, point the user to the artifact and highlight only key open questions or decisions that need their input.\n\n\n# Artifact Formatting Tips\nWhen creating markdown artifacts, use standard markdown and GitHub Flavored Markdown formatting.\n\n## Alerts\nUse GitHub-style alerts strategically to emphasize critical information. Do not place consecutively or nest:\n > [!NOTE] Background context, implementation details, or explanations\n > [!TIP] Performance optimizations, best practices, or efficiency suggestions\n > [!IMPORTANT] Essential requirements, critical steps, or must-know information\n > [!WARNING] Breaking changes, compatibility issues, or potential problems\n > [!CAUTION] High-risk actions that could cause data loss or security vulnerabilities\n\n\n## Mermaid Diagrams\nCreate mermaid diagrams using fenced code blocks with language mermaid to visualize relationships, workflows, and architectures.\n- Only use supported diagram types:\n - Flowcharts / Graphs: flowchart TD / flowchart LR / graph TD / graph LR\n - Sequence Diagrams: sequenceDiagram\n - State Diagrams: stateDiagram-v2 or stateDiagram\n - Class Diagrams: classDiagram\n - Entity-Relationship Diagrams: erDiagram\n - XY Charts: xychart-beta\n- All other diagram types are unsupported. For schedules, timelines, or roadmaps, use directed flowcharts (flowchart LR / flowchart TD) or Markdown tables instead.\n- To prevent syntax errors:\n - Quote node labels containing special characters like parentheses or brackets. For example, id[\"Label (Extra Info)\"] instead of id[Label (Extra Info)].\n - Avoid HTML tags in labels.\n\n## File Links\n- Link to line ranges using link text format.\n- IMPORTANT: If you are embedding a file in an artifact and the file is NOT already in \brain\, you MUST first copy the file to the artifacts directory before embedding it. Only embed files that are located in the artifacts directory. Always use its absolute path .\n- **Use basenames for readability**: Use file basenames for the link text instead of the full path\n\n## Carousels\nUse ````carousel syntax with HTML comments to display related markdown snippets sequentially (before/after comparisons, UI progressions, alternative approaches, walkthroughs). Four backticks enable nesting code blocks within slides.\n\nExample:\ncarousel\n\n\n```python\ndef example():\n print(\"Code in carousel\")\n```\n``` /* Detailed source-code truncated for AI context efficiency. */ carousel syntax with `` HTML comments to display related markdown snippets sequentially (before/after comparisons, UI progressions, alternative approaches, walkthroughs). Four backticks enable nesting code blocks within slides.\n\nExample:\ncarousel\n\n\n```python\ndef example():\n print(\"Code in carousel\")\n```\n\n\n## LaTeX / Math\nArtifacts render LaTeX math with KaTeX.\n- Literal $: write \\$, or wrap it in backticks. Two unescaped $ in the same paragraph become math.\n\n# Scratch Scripts and Files\n\nYou may find it useful to create scratch scripts or files for temporary purposes.\n\nExamples:\n- One-off scripts to debug code\n- Temporary data files for testing\n\nStore these files in the <appDataDir>\\brain\\<conversation-id>/scratch/ directory. They will be persisted.\n\n\nArtifact Directory Path: C:\Users\Administrator\.gemini\antigravity\brain\90546263-053a-47ac-bd07-f435874e38fe\n\n\n\nSlash commands are user-facing shortcuts in the chat UI (e.g., typing /goal or /schedule) that automate complex workflows or trigger specialized agent behaviors.\n\nYou cannot execute these commands yourself. Your role is to recommend them to the user when they are a good fit for the task at hand, encouraging the user to explore and trigger them.\n\nTo recommend a slash command, suggest it clearly in your response (e.g., "You can use the /goal command to...").\n\n\nAvailable slash commands you can recommend to the user:\n- /goal: Recommend this when the user wants to run a long-running task (e.g., overnight) and wants the agent to be extra thorough and not stop until the goal is fully achieved.\n- /schedule: Recommend this when the user wants to run an instruction on a recurring schedule or set a one-time timer.\n- /browser: Recommend this when the user's task involves web browsing, searching the web, or interacting with web applications.\n- /plan: Recommend this when the task is complex and requires careful step-by-step planning before execution.\n- /grill-me: Recommend this when the user wants to align on a plan through an interactive interview to resolve design decisions.\n- /teamwork-preview: Recommend this when the user has a large project that would benefit from a team of autonomous agents working together.\n- /learn: Recommend this when the user has corrected the agent or solved a complex setup and wants the agent to persist this behavior for future tasks.\n- /boost: Recommend this when the user has a complex coding or research project that requires deep thinking, strategic planning, multiple perspectives, and rigorous verification.\n\n\n\n\nFollow these behavioral guidelines at all times:\n- Maintain documentation integrity. Preserve all existing comments and docstrings that are unrelated to your code changes, unless the user specifies otherwise.\n\n\n\n- Keep your responses concise.\n- Format your responses in github-style markdown.\n- You can render LaTeX math (KaTeX): inline with \\(...\\) or $...$, display with \\[...\\] or $$...$$ placed on its own line.\n- Use math only for genuine mathematical content. Use backticks for code, identifiers, paths, flags, and shell variables.\n- $ opens inline math, so write a literal dollar as \\$ or wrap it in backticks. Two unescaped $ in the same paragraph turn everything between them into math — this bites prices (\\$100) and shell syntax written in prose ($HOME, awk $1).\n\n- If you're unsure about the user's intent, ask for clarification rather than making assumptions.\n- You MUST create clickable links for all files and code symbols (classes, types, functions, structs). Use github style markdown links with the file:// scheme (e.g., utils.py or ClassName). For Windows, use forward slashes for paths.\n"
}
]
},
"tools": [
{
"functionDeclarations": [
{
"name": "view_file",
"description": "View the contents of a file from the local filesystem. This tool supports text files and following binary files: image, video.\nText file usage:\n- The lines of the file are 1-indexed\n- You can view at most 800 lines at a time\n- Specify StartLine and EndLine to view the lines of the file using slice notation:\n - Omit both to view the entire file, or the first 800 lines of the file, whichever is smaller.\n - Specify StartLine only to view the remaining lines of the file, or the next 800 lines, whichever is smaller\n - Specify EndLine only to view the remaining preceding lines of the file, or the previous 800 lines, whichever is smaller\n - Specify both to view a precise line range. This range must be smaller than 800 lines or only the first 800 lines of the range will be shown.\n- Content is limited to 46080 bytes per view. If content is truncated, use the ContentOffset parameter to view the remaining content\nBinary file usage:\n- Do not provide StartLine or EndLine arguments, this tool always returns the entire file\n- Files larger than 100 MB cannot be viewed.",
"parameters": {
"type": "OBJECT",
"properties": {
"AbsolutePath": {
"type": "STRING",
"description": "Path to file to view. Must be an absolute path."
},
"ContentOffset": {
"type": "INTEGER",
"description": "Optional. Byte offset into the content. Use this to view content beyond the initial byte limit when the tool output indicates content was truncated."
},
"EndLine": {
"type": "INTEGER",
"description": "Optional. Endline to view, 1-indexed, inclusive. When specified, this value must be greater than or equal to StartLine."
},
"StartLine": {
"type": "INTEGER",
"description": "Optional. Startline to view, 1-indexed, inclusive. When specified, this value must be less than or equal to EndLine."
},
"toolAction": {
"type": "STRING",
"description": "Brief 2-5 word phrase in -ing form describing the specific action. Capitalize like a sentence. Some examples: 'Analyzing directory', 'Searching the web', 'Checking git status', 'Running tests', 'Searching code'."
},
"toolSummary": {
"type": "STRING",
"description": "Brief 2-5 word noun phrase describing the specific task. Capitalize like a sentence. Some examples: 'Directory analysis', 'Web search', 'Git status check', 'Test execution', 'Code search'."
}
},
"required": [
"AbsolutePath",
"toolSummary",
"toolAction"
]
}
}
]
},
{
"functionDeclarations": [
{
"name": "run_command",
"description": "PROPOSE a command to run on behalf of the user. Operating System: windows. Shell: powershell.\nNEVER PROPOSE A cd COMMAND.\nIf you have this tool, note that you DO have the ability to run commands directly on the USER's system.\nMake sure to specify CommandLine exactly as it should be run in the shell.\nIf the step doesn't return the command output, it means that the command was sent to the background as a task. You will receive messages with the command's output as it runs. To interact with a running command, use the manage_task tool. Use send_input to send stdin, kill to terminate the command, and status to check current status. IMPORTANT: Do NOT poll or loop on status to wait for completion. The system will automatically notify you with a message when the command finishes. Simply proceed with other work or stop calling tools after launching a command.\nCommands will be run with PAGER=cat. You may want to limit the length of output for commands that usually rely on paging and may contain very long output (e.g. git log, use git log -n ).",
"parameters": {
"type": "OBJECT",
"properties": {
"CommandLine": {
"type": "STRING",
"description": "The exact command line string to execute."
},
"Cwd": {
"type": "STRING",
"description": "The current working directory for the command"
},
"IsDaemon": {
"type": "BOOLEAN",
"description": "Set to true for long-running support processes that are meant to keep running in the background indefinitely and are not expected to finish on their own (e.g., dev servers, file watchers, tunnels). Leave false (the default) for normal commands that are expected to terminate."
},
"WaitMsBeforeAsync": {
"type": "INTEGER",
"description": "This specifies the number of milliseconds to wait after starting the command before sending it to the background. If you want the command to complete execution synchronously, set this to a large enough value that you expect the command to complete in that time under ordinary circumstances. If you're starting an interactive or long-running command, set it to a large enough value that it would cause possible failure cases to execute synchronously (e.g. 500ms). Keep the value as small as possible, with a maximum of 10000ms."
},
"toolAction": {
"type": "STRING",
"description": "Brief 2-5 word phrase in -ing form describing the specific action. Capitalize like a sentence. Some examples: 'Analyzing directory', 'Searching the web', 'Checking git status', 'Running tests', 'Searching code'."
},
"toolSummary": {
"type": "STRING",
"description": "Brief 2-5 word noun phrase describing the specific task. Capitalize like a sentence. Some examples: 'Directory analysis', 'Web search', 'Git status check', 'Test execution', 'Code search'."
}
},
"required": [
"Cwd",
"WaitMsBeforeAsync",
"CommandLine",
"toolSummary",
"toolAction"
]
}
}
]
},
{
"functionDeclarations": [
{
"name": "manage_task",
"description": "Manage background tasks. Use this tool to list running tasks or interact with tasks that were sent to the background.\n\nActions:\n- 'list': List all currently running background tasks\n- 'kill': Cancel the task's execution\n- 'status': Check the task's current status and log file location\n- 'send_input': Send input to a running task\n\nWhen mentioning tasks to the user, avoid using full task IDs and start timestamps; keep them human-readable.",
"parameters": {
"type": "OBJECT",
"properties": {
"Action": {
"type": "STRING",
"description": "The action to perform: 'list' (list all running tasks), 'kill' (cancel the task), 'status' (check the task status and log URI), 'send_input' (send input to a running task).",
"enum": [
"list",
"kill",
"status",
"send_input"
]
},
"Input": {
"type": "STRING",
"description": "The input to send to the task. Required when Action is 'send_input'."
},
"TaskId": {
"type": "STRING",
"description": "The task ID to manage. Required when Action is 'kill', 'status', or 'send_input'."
},
"toolAction": {
"type": "STRING",
"description": "Brief 2-5 word phrase in -ing form describing the specific action. Capitalize like a sentence. Some examples: 'Analyzing directory', 'Searching the web', 'Checking git status', 'Running tests', 'Searching code'."
},
"toolSummary": {
"type": "STRING",
"description": "Brief 2-5 word noun phrase describing the specific task. Capitalize like a sentence. Some examples: 'Directory analysis', 'Web search', 'Git status check', 'Test execution', 'Code search'."
}
},
"required": [
"Action",
"toolSummary",
"toolAction"
]
}
}
]
},
{
"functionDeclarations": [
{
"name": "send_message",
"description": "Send a message to another agent. This tool can be used to communicate with subagents, peer agents, etc. Do not use this tool to communicate with the user.",
"parameters": {
"type": "OBJECT",
"properties": {
"Message": {
"type": "STRING",
"description": "The message content."
},
"Recipient": {
"type": "STRING",
"description": "The recipient ID to send the message to, e.g. a subagent conversation ID."
},
"toolAction": {
"type": "STRING",
"description": "Brief 2-5 word phrase in -ing form describing the specific action. Capitalize like a sentence. Some examples: 'Analyzing directory', 'Searching the web', 'Checking git status', 'Running tests', 'Searching code'."
},
"toolSummary": {
"type": "STRING",
"description": "Brief 2-5 word noun phrase describing the specific task. Capitalize like a sentence. Some examples: 'Directory analysis', 'Web search', 'Git status check', 'Test execution', 'Code search'."
}
},
"required": [
"Recipient",
"Message",
"toolSummary",
"toolAction"
]
}
}
]
},
{
"functionDeclarations": [
{
"name": "schedule",
"description": "Schedule a one-shot timer or a recurring cron job that sends notifications in the background.\n\nNOTE: This tool call returns immediately and does not pause execution. To wait for the timer to fire, you must stop calling tools to end your turn.\n\nModes:\n1. One-shot timer: Set a timer for a specified duration that will notify you with your Prompt when it expires. You can control early termination behavior using TimerCondition:\n\n- 'never' (default): The timer will always fire after the specified duration, unless explicitly cancelled.\nUsage: Use when setting unconditional timers that should always fire after DurationSeconds, unless explicitly cancelled.\n- 'any': The timer will be cancelled early if ANY message from any sender is received before the duration.\nUsage: Useful when multiple background tasks are running and you want to wait for any update, but with some guarantee that you won't be idle forever in case they are all stuck.\n- : The timer will be cancelled early if a message is received from that specific sender ID.\nUsage: Use when you're waiting for an update from a specific subagent or task, but want to set some limit on how long to wait.\n\nNOTE: When a timer is cancelled early, no separate cancellation notification is sent — the message that satisfied the condition is itself your wakeup, and the timer's tool step result records the cancellation.\n\nNOTE: You cannot have multiple concurrently active timers that would early terminate on the same sender ID.\nFor example, if you already have a liveness timer set with "any", you cannot set another timer with "any" or any other condition.\nIf you already have a timer set with early termination on "task-123", you cannot set another timer with "task-123" or "any".\nYou should rely on the existing timer, or cancel and replace it if needed.\n\nExamples:\n\nScenario: User asks explicitly for a reminder in 10 minutes.\nArgs: DurationSeconds=600, Prompt="Remind the user", TimerCondition="never"\nComments: TimerCondition="never" is appropriate since this timer is unrelated to other ongoing tasks.\n\nScenario: You just ran a command as "task-123". You already set a notification on it for 5 minutes, and it just notified you that it's still running. After checking the output, you want to set a new reminder to check on it in 10 minutes if it still hasn't finished.\nArgs: DurationSeconds=600, Prompt="Check on the command status", TimerCondition="task-123"\nComments: TimerCondition="task-123" is appropriate since the timer is not needed if the command finishes ahead of time.\n\nScenario: You just spawned 10 subagents, and you want to check in on progress after 5 minutes if you haven't heard back from any of them.\nArgs: DurationSeconds=300, Prompt="Check in on the subagents' progress", TimerCondition="any"\nComments: TimerCondition="any" is appropriate since you are not waiting for any specific subagent.\n\nScenario: You are running a command that you're sure will terminate, and you want to wait for it to finish.\nArgs: N/A\nComments: A timer is not needed at all in this scenario and will wastefully generate extra messages. Stop calling tools to end your turn instead.\n\n2. Recurring cron: Set CronExpression to a standard 5-field cron expression (e.g., '/5 * * * ' for every 5 minutes). Each time the cron triggers, a notification with your Prompt is sent. The cron runs as a background task. Optionally set MaxIterations to limit the number of triggers. Optionally set IsDaemon to declare how the cron relates to your current task: leave it false (the default) when the cron is how your current task makes progress — polling or monitoring a job until it completes, heartbeat/liveness, or reminders — so your task stays active until the cron ends; set it true only when the cron is an independent standing job that should keep running after your current task is done — e.g. a recurring report or a maintenance job the user asked you to keep going — so you can finish now while it keeps firing in the background.\n\nExamples:\n- Poll deployment status every 5 minutes until it passes: CronExpression="/5 * * * ", Prompt="Check deployment status and report progress", IsDaemon=false\n- Run a health check every hour, up to 3 times: CronExpression="0 * * * ", MaxIterations=3, Prompt="Run the health check script and report results", IsDaemon=false\n- Inspect newly filed issues in the last 24h and post a daily summary report: CronExpression="0 9 * * ", Prompt="Summarize issues filed in the last 24h and post the report", IsDaemon=true\n\nGeneral Reminders:\n- You must specify exactly one of DurationSeconds or CronExpression.\n- Always provide a Prompt describing what the notification should say.\n- Never run a background 'sleep' command to set a timer, use this tool instead.\n- To cancel a running timer or cron schedule, use the manage_task tool with the task ID returned by this tool.",
"parameters": {
"type": "OBJECT",
"properties": {
"CronExpression": {
"type": "STRING",
"description": "A standard cron expression (5 fields: minute hour day-of-month month day-of-week). Use for recurring schedules. Mutually exclusive with DurationSeconds. Example: '
/5 * * * ' for every 5 minutes."
},
"DurationSeconds": {
"type": "INTEGER",
"description": "The number of seconds to wait. Use for one-shot timers. Mutually exclusive with CronExpression."
},
"IsDaemon": {
"type": "BOOLEAN",
"description": "Optional. Set to true only when the cron is an independent, standing job that should keep firing even after your current task is done (e.g. a recurring daily/weekly report or a standing maintenance job). Leave false (the default) whenever the cron is part of finishing your current task — including polling or monitoring a running job until it completes, heartbeat/liveness, or reminders."
},
"MaxIterations": {
"type": "INTEGER",
"description": "Optional. Maximum number of times the cron schedule will fire before stopping. Only applicable when CronExpression is set. Defaults to unlimited."
},
"Prompt": {
"type": "STRING",
"description": "The message content to include in the notification when the timer fires or cron triggers. This is sent to the agent as a high-priority message."
},
"TimerCondition": {
"type": "STRING",
"description": "Optional. Controls when a one-shot timer should early terminate upon receiving a message. Options: 'never' (default, timer unconditionally waits until expiry), 'any' (timer cancels if any message is received), or a specific sender ID (timer cancels only if a message is received from that specific subagent conversation ID or background task ID). Only applicable when DurationSeconds is set."
},
"toolAction": {
"type": "STRING",
"description": "Brief 2-5 word phrase in -ing form describing the specific action. Capitalize like a sentence. Some examples: 'Analyzing directory', 'Searching the web', 'Checking git status', 'Running tests', 'Searching code'."
},
"toolSummary": {
"type": "STRING",
"description": "Brief 2-5 word noun phrase describing the specific task. Capitalize like a sentence. Some examples: 'Directory analysis', 'Web search', 'Git status check', 'Test execution', 'Code search'."
}
},
"required": [
"Prompt",
"toolSummary",
"toolAction"
]
}
}
]
},
{
"functionDeclarations": [
{
"name": "invoke_subagent",
"description": "Invokes one or more subagents by name with a single tool call. Each subagent runs in the background with its own prompt and reports back when done.\n\nSpecify the Subagents array with one or more entries. Each entry defines a subagent to launch.\n\nCommunicate with subagents using the send_message tool. Examples of when to do this:\n
To check on the status of a subagent.\n
To send a running subagent further instructions.\n
To send an idle subagent new instructions.\n\nGuidelines:\n
Each invoked subagent will be uniquely identified by its conversationID.\n* Multiple subagents with the same type name can be invoked, with each subagent receiving a unique conversationID.\n* If a task is a natural continuation of an existing subagent's work, send a message to that subagent with the task rather than invoking a new subagent to conserve resources.\n* When selecting the Model argument, default to 'inherit' unless the user explicitly requests a different model.",
"parameters": {
"type": "OBJECT",
"properties": {
"Subagents": {
"type": "ARRAY",
"description": "Array of subagents to invoke. Each entry specifies a separate subagent to launch concurrently.",
"items": {
"type": "OBJECT",
"properties": {
"Model": {
"type": "STRING",
"description": "Model to use for the subagent. 'inherit' (default) uses the calling agent's model. 'flash_lite' uses a very light model. 'flash' uses a smaller, faster model suited for simple tasks like research lookups, file reading, or quick searches. 'pro' uses a larger, more capable model suited for complex tasks requiring deep reasoning, large refactors, or multi-step planning.",
"enum": [
"inherit",
"flash_lite",
"flash",
"pro"
]
},
"Prompt": {
"type": "STRING",
"description": "A clear, actionable task description for the subagent. Be specific about what the subagent should do and what information it should return."
},
"Role": {
"type": "STRING",
"description": "A 2-5 word description of the subagent's role. Should read similar to a job title, e.g. 'Codebase Researcher', 'Database Debugger', etc. Should also be detailed enough to distinguish between different subagents who might share similar purposes."
},
"TypeName": {
"type": "STRING",
"description": "Type name of the subagent to invoke."
},
"Workspace": {
"type": "STRING",
"description": "Workspace mode for the subagent. 'inherit' (default) uses the same workspace as the parent. 'branch' creates a new isolated workspace branched or cloned from the parent. 'share' creates a new workspace sharing the parent's underlying repository directory (similar to a git worktree or Mercurial 'hg share'), allowing independent branching without duplicating storage. If omitted, defaults to 'inherit'."
}
},
"required": [
"TypeName",
"Role",
"Prompt"
]
}
},
"toolAction": {
"type": "STRING",
"description": "Brief 2-5 word phrase in -ing form describing the specific action. Capitalize like a sentence. Some examples: 'Analyzing directory', 'Searching the web', 'Checking git status', 'Running tests', 'Searching code'."
},
"toolSummary": {
"type": "STRING",
"description": "Brief 2-5 word noun phrase describing the specific task. Capitalize like a sentence. Some examples: 'Directory analysis', 'Web search', 'Git status check', 'Test execution', 'Code search'."
}
},
"required": [
"Subagents",
"toolSummary",
"toolAction"
]
}
}
]
},
{
"functionDeclarations": [
{
"name": "define_subagent",
"description": "Defines a new type of subagent that can be invoked via invoke_subagent.\n\n\tGuidelines:\n\t* Use this tool if you need a specialized subagent for a task and none of the existing subagents are suitable.\n\t* Once the subagent is defined, it can be invoked repeatedly using invoke_subagent without calling this tool again.\n\t* The subagent will be defined with the specified name, description, system prompt, and tool groups.\n\t* By default, all subagents have read tools to research the codebase, and tools to communicate with other agents.\n\t",
"parameters": {
"type": "OBJECT",
"properties": {
"description": {
"type": "STRING",
"description": "Human-readable description of what this subagent does and when it should be used."
},
"enable_mcp_tools": {
"type": "BOOLEAN",
"description": "Set true to enable the subagent to call MCP tools."
},
"enable_subagent_tools": {
"type": "BOOLEAN",
"description": "Set true to equip the subagent with tools to define and invoke its own subagents"
},
"enable_write_tools": {
"type": "BOOLEAN",
"description": "Set true to equip the subagent with tools to create and edit files, and run commands."
},
"name": {
"type": "STRING",
"description": "Unique name for the subagent. Used to invoke it via invoke_subagent. Must start with a letter or digit and contain only letters, digits, '_', '-', and '.'."
},
"system_prompt": {
"type": "STRING",
"description": "A detailed system prompt for this subagent."
},
"toolAction": {
"type": "STRING",
"description": "Brief 2-5 word phrase in -ing form describing the specific action. Capitalize like a sentence. Some examples: 'Analyzing directory', 'Searching the web', 'Checking git status', 'Running tests', 'Searching code'."
},
"toolSummary": {
"type": "STRING",
"description": "Brief 2-5 word noun phrase describing the specific task. Capitalize like a sentence. Some examples: 'Directory analysis', 'Web search', 'Git status check', 'Test execution', 'Code search'."
}
},
"required": [
"name",
"description",
"system_prompt",
"toolSummary",
"toolAction"
]
}
}
]
},
{
"functionDeclarations": [
{
"name": "manage_subagents",
"description": "Manage existing subagents.\n\tActions:\n\t* 'list': List active direct subagents with their conversation IDs and live state. Each subagent is reported as a JSON object with this schema:\n\t{"$schema":"https://json-schema.org/draft/2020-12/schema\",\"properties\":{\"role\":{\"type\":\"string\",\"description\":\"The subagent's role, or its type name if no role was set."},"type":{"type":"string","description":"The subagent's type name."},"conversationId":{"type":"string","description":"The subagent's conversation ID, used to message or kill it."},"transcript":{"type":"string","description":"Absolute URI of the subagent's transcript log, if available."},"state":{"type":"string","enum":["running","idle","waiting_for_input","waiting_for_dependents","waiting_for_message","canceling","errored","unspecified"],"description":"The subagent's current lifecycle state."},"stateDetail":{"type":"string","description":"Context for State: the current tool call (name and summary) when running or waiting_for_input, or the failure message when errored."}},"additionalProperties":false,"type":"object"}\n\t* 'kill': Terminate specific subagents and all their descendants.\n\t* 'kill_all': Terminate all subagents and all their descendants.\n\n\tWhen a subagent is killed, its branched workspaces will be deleted, but its logs and artifacts will be preserved.",
"parameters": {
"type": "OBJECT",
"properties": {
"Action": {
"type": "STRING",
"description": "The action to perform. Must be 'list' (list active direct subagents with their live state), 'kill' (terminate specific subagents and all their descendants), or 'kill_all' (terminate all subagents and all their descendants).",
"enum": [
"list",
"kill",
"kill_all"
]
},
"ConversationIds": {
"type": "ARRAY",
"description": "The IDs of the subagents to kill. Required for 'kill'.",
"items": {
"type": "STRING"
}
},
"toolAction": {
"type": "STRING",
"description": "Brief 2-5 word phrase in -ing form describing the specific action. Capitalize like a sentence. Some examples: 'Analyzing directory', 'Searching the web', 'Checking git status', 'Running tests', 'Searching code'."
},
"toolSummary": {
"type": "STRING",
"description": "Brief 2-5 word noun phrase describing the specific task. Capitalize like a sentence. Some examples: 'Directory analysis', 'Web search', 'Git status check', 'Test execution', 'Code search'."
}
},
"required": [
"Action",
"toolSummary",
"toolAction"
]
}
}
]
},
{
"functionDeclarations": [
{
"name": "write_to_file",
"description": "Use this tool to create new files. The file and any parent directories will be created for you if they do not already exist.\n\t\tFollow these instructions:\n\t\t1. By default this tool will error if TargetFile already exists. To overwrite an existing file, set Overwrite to true. To append to an existing file (or create it if it does not exist), set Append to true.\n\t\t2. When creating an artifact, always provide ArtifactMetadata. When creating non-artifact files, do not provide it.",
"parameters": {
"type": "OBJECT",
"properties": {
"Append": {
"type": "BOOLEAN",
"description": "Set this to true to append CodeContent to the end of TargetFile (creating the file if it does not exist). Cannot be combined with Overwrite=true."
},
"ArtifactMetadata": {
"type": "OBJECT",
"description": "Metadata that defines artifact properties. ONLY provide when creating an artifact file in the artifact directory. Omit this field when creating non-artifact files.",
"properties": {
"RequestFeedback": {
"type": "BOOLEAN",
"description": "Set to true if you'd like to request user feedback on this artifact and if the contents of this artifact are executable (e.g., a plan). The user will be provided with a 'Proceed' button to execute it."
},
"Summary": {
"type": "STRING",
"description": "Detailed multi-line summary of the artifact file, after edits have been made. Summary does not need to mention the artifact name and should focus on the contents and purpose of the artifact."
},
"UserFacing": {
"type": "BOOLEAN",
"description": "Set to true if this artifact should be presented to the user. Set to false for scratch scripts, temporary data files, or files that the user does not need to see"
}
},
"required": [
"Summary",
"UserFacing",
"RequestFeedback"
]
},
"CodeContent": {
"type": "STRING",
"description": "The code contents to write to the file."
},
"Description": {
"type": "STRING",
"description": "Brief, user-facing explanation of what this change did. Focus on non-obvious rationale, design decisions, or important context. Don't just restate what the code does."
},
"Overwrite": {
"type": "BOOLEAN",
"description": "Set this to true to overwrite an existing file. WARNING: This will replace the entire file contents. Only use when you explicitly intend to overwrite. Otherwise, use a code edit tool to modify existing files."
},
"TargetFile": {
"type": "STRING",
"description": "The target file to create and write code to. Must be an absolute path."
},
"toolAction": {
"type": "STRING",
"description": "Brief 2-5 word phrase in -ing form describing the specific action. Capitalize like a sentence. Some examples: 'Analyzing directory', 'Searching the web', 'Checking git status', 'Running tests', 'Searching code'."
},
"toolSummary": {
"type": "STRING",
"description": "Brief 2-5 word noun phrase describing the specific task. Capitalize like a sentence. Some examples: 'Directory analysis', 'Web search', 'Git status check', 'Test execution', 'Code search'."
}
},
"required": [
"TargetFile",
"Overwrite",
"CodeContent",
"Description",
"toolSummary",
"toolAction"
]
}
}
]
},
{
"functionDeclarations": [
{
"name": "replace_file_content",
"description": "Use this tool to edit an existing file. Follow these rules:\n1. Use this tool ONLY when you are making a SINGLE CONTIGUOUS block of edits to the same file (i.e. replacing a single contiguous block of text).\n2. Do NOT make multiple parallel calls to this tool for the same file.\n3. To edit multiple, non-adjacent lines of code in the same file, make multiple calls to this tool.\n4. For the ReplacementChunk, specify StartLine, EndLine, TargetContent and ReplacementContent. StartLine and EndLine should specify a range of lines containing precisely the instances of TargetContent that you wish to edit. To edit a single instance of the TargetContent, the range should be such that it contains that specific instance of the TargetContent and no other instances. In TargetContent, specify the precise lines of code to edit. These lines MUST EXACTLY MATCH text in the existing file content. In ReplacementContent, specify the replacement content for the specified target content. This must be a complete drop-in replacement of the TargetContent, with necessary modifications made.\n5. If you are making multiple edits across a single file, make multiple calls to this tool. DO NOT try to replace the entire existing content with the new content, this is very expensive.\n6. You may not edit file extensions: [.ipynb]",
"parameters": {
"type": "OBJECT",
"properties": {
"AllowMultiple": {
"type": "BOOLEAN",
"description": "If true, multiple occurrences of 'targetContent' will be replaced by 'replacementContent' if they are found. Otherwise if multiple occurrences are found, an error will be returned."
},
"Description": {
"type": "STRING",
"description": "Brief, user-facing explanation of what this change did. Focus on non-obvious rationale, design decisions, or important context. Don't just restate what the code does."
},
"EndLine": {
"type": "INTEGER",
"description": "The ending line number of the chunk (1-indexed). Should be at or after the last line containing the target content. Must satisfy StartLine <= EndLine <= number of lines in the file. The target content is searched for within the [StartLine, EndLine] range."
},
"Instruction": {
"type": "STRING",
"description": "A description of the changes that you are making to the file."
},
"ReplacementContent": {
"type": "STRING",
"description": "The content to replace the target content with."
},
"StartLine": {
"type": "INTEGER",
"description": "The starting line number of the chunk (1-indexed). Should be at or before the first line containing the target content. Must satisfy 1 <= StartLine <= EndLine. The target content is searched for within the [StartLine, EndLine] range."
},
"TargetContent": {
"type": "STRING",
"description": "The exact string to be replaced. This must be the exact character-sequence to be replaced, including whitespace. Be very careful to include any leading whitespace otherwise this will not work at all. This must be a unique substring within the file, or else it will error."
},
"TargetFile": {
"type": "STRING",
"description": "The target file to modify. Must be an absolute path. Always specify the target file as the very first argument."
},
"TargetLintErrorIds": {
"type": "ARRAY",
"description": "If applicable, IDs of lint errors this edit aims to fix (they'll have been given in recent IDE feedback). If you believe the edit could fix lints, do specify lint IDs; if the edit is wholly unrelated, do not. A rule of thumb is, if your edit was influenced by lint feedback, include lint IDs. Exercise honest judgement here.",
"items": {
"type": "STRING"
}
},
"toolAction": {
"type": "STRING",
"description": "Brief 2-5 word phrase in -ing form describing the specific action. Capitalize like a sentence. Some examples: 'Analyzing directory', 'Searching the web', 'Checking git status', 'Running tests', 'Searching code'."
},
"toolSummary": {
"type": "STRING",
"description": "Brief 2-5 word noun phrase describing the specific task. Capitalize like a sentence. Some examples: 'Directory analysis', 'Web search', 'Git status check', 'Test execution', 'Code search'."
}
},
"required": [
"TargetFile",
"Instruction",
"Description",
"AllowMultiple",
"TargetContent",
"ReplacementContent",
"StartLine",
"EndLine",
"toolSummary",
"toolAction"
]
}
}
]
},
{
"functionDeclarations": [
{
"name": "generate_image",
"description": "Generate an image or edit existing images based on a text prompt. The resulting image will be saved as an artifact for use. You can use this tool to generate user interfaces and iterate on a design with the USER for an application or website that you are building. When creating UI designs, generate only the interface itself without surrounding device frames (laptops, phones, tablets, etc.) unless the user explicitly requests them. You can also use this tool to generate assets for use in an application or website.",
"parameters": {
"type": "OBJECT",
"properties": {
"AspectRatio": {
"type": "STRING",
"description": "Optional aspect ratio for the generated image. Supported values: '1:1', '2:3', '3:2', '3:4', '4:3', '9:16', '16:9'. Default is '1:1'."
},
"ImageName": {
"type": "STRING",
"description": "Name of the generated image to save. Should be all lowercase with underscores, describing what the image contains. Maximum 3 words. Example: 'login_page_mockup'"
},
"ImagePaths": {
"type": "ARRAY",
"description": "Optional absolute paths to the images to use in generation. You can pass in images here if you would like to edit, combine, or use as references. You can pass in artifact images and any images in the file system. Note: you cannot pass in more than 3 images.",
"items": {
"type": "STRING"
}
},
"Prompt": {
"type": "STRING",
"description": "The text prompt to generate an image for or the edit instructions."
},
"toolAction": {
"type": "STRING",
"description": "Brief 2-5 word phrase in -ing form describing the specific action. Capitalize like a sentence. Some examples: 'Analyzing directory', 'Searching the web', 'Checking git status', 'Running tests', 'Searching code'."
},
"toolSummary": {
"type": "STRING",
"description": "Brief 2-5 word noun phrase describing the specific task. Capitalize like a sentence. Some examples: 'Directory analysis', 'Web search', 'Git status check', 'Test execution', 'Code search'."
}
},
"required": [
"Prompt",
"ImageName",
"toolSummary",
"toolAction"
]
}
}
]
},
{
"functionDeclarations": [
{
"name": "read_url_content",
"description": "Fetch content from a URL via HTTP request (invisible to USER). Use when: (1) extracting text from public pages, (2) reading static content/documentation, (3) batch processing multiple URLs, (4) speed is important, or (5) no visual interaction needed. Converts HTML to markdown. No JavaScript execution, no authentication. For pages requiring login, JavaScript, or USER visibility, use read_browser_page instead.",
"parameters": {
"type": "OBJECT",
"properties": {
"Url": {
"type": "STRING",
"description": "URL to read content from"
},
"toolAction": {
"type": "STRING",
"description": "Brief 2-5 word phrase in -ing form describing the specific action. Capitalize like a sentence. Some examples: 'Analyzing directory', 'Searching the web', 'Checking git status', 'Running tests', 'Searching code'."
},
"toolSummary": {
"type": "STRING",
"description": "Brief 2-5 word noun phrase describing the specific task. Capitalize like a sentence. Some examples: 'Directory analysis', 'Web search', 'Git status check', 'Test execution', 'Code search'."
}
},
"required": [
"Url",
"toolSummary",
"toolAction"
]
}
}
]
},
{
"functionDeclarations": [
{
"name": "search_web",
"description": "Performs a web search for a given query. Returns a summary of relevant information along with URL citations.",
"parameters": {
"type": "OBJECT",
"properties": {
"domain": {
"type": "STRING",
"description": "Optional domain to recommend the search prioritize"
},
"query": {
"type": "STRING"
},
"toolAction": {
"type": "STRING",
"description": "Brief 2-5 word phrase in -ing form describing the specific action. Capitalize like a sentence. Some examples: 'Analyzing directory', 'Searching the web', 'Checking git status', 'Running tests', 'Searching code'."
},
"toolSummary": {
"type": "STRING",
"description": "Brief 2-5 word noun phrase describing the specific task. Capitalize like a sentence. Some examples: 'Directory analysis', 'Web search', 'Git status check', 'Test execution', 'Code search'."
}
},
"required": [
"query",
"toolSummary",
"toolAction"
]
}
}
]
},
{
"functionDeclarations": [
{
"name": "ask_question",
"description": "Use this tool to ask the user one or more multiple-choice questions, with the goal of:\n- Clarifying underspecified requirements\n- Soliciting design feedback or user preferences\n- Addressing ambiguous user intent\n- Picking a solution from a list of options\n\nWhen called, this tool renders an interactive modal containing the question, selectable options, a default write-in option, and Submit/Skip buttons. Execution is blocked until the user responds.\n\nGuidance:\n- When specifying files in the question, use github markdown links (e.g. filename).\n- Don't use this tool to ask trivial questions that can be answered with a single word (e.g. yes/no); output regular text to ask these questions.\n- Don't include an 'other' option for write-in responses; one is always provided in the UI by default.\n- Don't enumerate the options; they are enumerated by default.\n- Don't include "Select all options that apply", or similar, in the question; the UI already includes this.\n- If you recommend any options, list it first and prefix the option text with "(Recommended)".\n- Format options as the user's direct response instead of describing your own actions.\n- Set 'IsMultiSelect' to true to allow the user to select multiple options with checkboxes.\n",
"parameters": {
"type": "OBJECT",
"properties": {
"questions": {
"type": "ARRAY",
"description": "The list of questions to ask.",
"items": {
"type": "OBJECT",
"properties": {
"is_multi_select": {
"type": "BOOLEAN",
"description": "If true, the user can select multiple options."
},
"options": {
"type": "ARRAY",
"description": "The text for each option, formatted as the user's response. Must have at least 2 options. Do NOT add an 'Other' option to questions.",
"items": {
"type": "STRING"
}
},
"question": {
"type": "STRING",
"description": "The question to ask the user. Do NOT add 'select all that apply' or similar text to the question title."
}
}
}
},
"toolAction": {
"type": "STRING",
"description": "Brief 2-5 word phrase in -ing form describing the specific action. Capitalize like a sentence. Some examples: 'Analyzing directory', 'Searching the web', 'Checking git status', 'Running tests', 'Searching code'."
},
"toolSummary": {
"type": "STRING",
"description": "Brief 2-5 word noun phrase describing the specific task. Capitalize like a sentence. Some examples: 'Directory analysis', 'Web search', 'Git status check', 'Test execution', 'Code search'."
}
},
"required": [
"toolSummary",
"toolAction"
]
}
}
]
}
],
"labels": {
"last_execution_id": "46670492-91bd-4cb9-8b65-bc07f1bb616a",
"last_step_index": "7",
"model_enum": "MODEL_PLACEHOLDER_M35",
"request_id": "1a5bf067-a359-4d74-a9a1-ea79d8119536-3",
"trajectory_id": "1a5bf067-a359-4d74-a9a1-ea79d8119536",
"used_claude": "true",
"used_claude_conservative": "true",
"used_non_gemini_model": "true"
},
"generationConfig": {
"maxOutputTokens": 64000,
"thinkingConfig": {
"includeThoughts": true,
"thinkingBudget": 1024
}
},
"sessionId": "-3750763034362895579"
},
"model": "claude-sonnet-4-6",
"userAgent": "antigravity",
"requestType": "agent"
}


Client Test Examples (`docs/client_test_examples.md`)

稳定性与搜索功能:测试示例指南

为了验证近期对 API 400 错误及“搜索文件错误”的修复效果,您可以在 Claude CLI (Claude Code) 中运行以下指令进行实测。

1. 验证搜索工具自愈 (Grep/Glob Fix)

针对之前的 "Error searching files" 问题,这些指令将触发 Grep 和 Glob 工具调用,并验证参数映射是否正确。

测试指令示例

  • 指令 A:在当前目录中搜索包含 "fn handle_messages" 的 Rust 文件。
    • 验证点:检查代理是否能正确将 query 映射为 pattern,并注入默认的 path: "."。
  • 指令 B:列出 src-tauri 目录下所有 .rs 文件。
    • 验证点:验证 Glob 工具名是否被正确识别,且路径过滤逻辑正常。

2. 验证协议顺序与签名稳定性 (Thinking/Signature Fix)

针对之前的 Found 'text' 和 Invalid signature 400 错误。

测试指令示例

  • 指令 A(推理+搜索):分析本项目中处理云端请求的核心逻辑,按调用顺序总结,并给出关键代码行的 Grep 搜索证据。
    • 验证点:验证在“思维 -> 工具调用 -> 结果 -> 继续思维”循环中,块顺序是否正确。
  • 指令 B(历史记录重试):在长对话中频繁切换模型,观察系统是否在 400 报错时静默修复签名并重试。

附录:深度错误对照与修复方案

错误类别 具体报错特征码 (Error Detail) 代理采取的修复/应对逻辑
消息流顺序违规 If an assistant message contains any thinking blocks... Found 'text'. 已修复:streaming.rs 不再允许在文字块之后非法追加思维块。
思维签名不匹配 Invalid signature in thinking block 已修复:优先保留原始名称以保护 Google 后端签名校验。
思维签名缺失 Function call is missing a thought_signature 已修复:自动注入 skip_thought_signature_validator 占位符。
非法缓存标记 thinking.cache_control: Extra inputs are not permitted 已修复:全局剔除历史消息中的 cache_control 标记。
Plan Mode 报错 EnterPlanMode tool call: InputValidationError: Extra inputs are not permitted 已修复:streaming.rs 强制清空工具参数以符合官方无参协议。
连续 User 消息 Consecutive user messages are not allowed 已修复:merge_consecutive_messages 自动合并相邻同角色消息。

3. 验证 Claude Code Plan Mode 与角色交替 (Issue #813)

针对 Plan Mode 切换导致的协议报错问题。

A. 验证 Plan Mode 激活 (UI 状态)

  • 指令:进入 Plan Mode 调研 src-tauri 的目录结构。
  • 预期结果:
    • 终端左下角应立即出现蓝色的 plan mode on 标签。
    • 日志中应看到 [Streaming] Tool Call: 'EnterPlanMode' Args: {}。

B. 验证角色交替自愈 (Consecutive Messages)

  • 指令:在 Plan Mode 下帮我分析 proxy/mappers/claude/request.rs 的逻辑,然后退出 Plan Mode 并给出一个简要总结。
  • 预期结果:
    • 模型切换模式(如从 Plan 到 Code)时不会因“连续两条 User 消息”而报 400 错误。
    • 日志中会体现 merge_consecutive_messages 的合并动作。

4. QuotaData 字段逻辑解析

设置页面中的“账号管理”列表,下方的进度条数据来源于 QuotaData。系统会在请求前检查账号配额,并在触及阈值时自动轮换。


调试建议

bash
RUST_LOG=debug npm run tauri dev

在日志中搜索 [Claude-Request],关注消息角色的排列顺序。


5. 验证 Thinking 签名持久化与重启容错 (Proxy Restart Test)

此测试模拟代理服务主要逻辑:验证当代理重启(内存签名缓存丢失)后,携带旧签名的历史消息是否会导致 400 错误。这是复现 Invalid signature 最有效的方法。

测试流程

  1. 生成 Thinking (Step 1):

    • 指令:详细分析 proxy/mappers/claude/request.rs 的代码结构,特别是它是如何处理 Thinking Block 的。请展示思维过程。
    • 状态:Claude CLI 会接收到包含 Thinking 和 Signature 的响应。
  2. 模拟环境变更 (Step 2):

    • 动作:保持当前 Claude CLI 会话不关闭。
    • 动作:在另一个终端完全重启 Antigravity (或 npm run tauri dev)。
    • 原理:重启会清空代理内存中的“签名白名单”,这意味着 Step 1 中下发的签名现在对代理来说是“未知/不可信”的。
  3. 触发历史重放 (Step 3):

    • 指令:根据上面的分析,总结一下签名验证的核心逻辑。
    • 原理:CLI 会将 Step 1 中的 Thinking Block + Signature 作为历史记录发送给重启后的代理。

预期结果 (验证修复)

  • 如果不通过:报错 Invalid signature in thinking block (因为代理无法验证该签名,直接透传给了 Google,被 Google 拒收)。
  • 如果通过 (当前版本):代理发现签名不在内存缓存中,自动触发降级逻辑(剥离 Thinking Block 或作为纯文本发送),对话正常继续,无报错。

6. 验证动态思维剥离 (Dynamic Thinking Stripping)

此测试验证系统能否在高 Context 压力或签名失效场景下,自动剥离无用的历史 Thinking Block,从而解决 "Prompt is too long" 和 "Invalid signature" 错误。

前置条件

  • 开启 Debug 日志: RUST_LOG=debug npm run tauri dev
  • 确保使用支持 Thinking 的模型 (如 claude-3-7-sonnet 或映射后的 gemini-2.0-flash-thinking-exp)

验证场景 A: 模拟超长 Context 压力 (Simulate High Load)

此场景验证当对话历史接近 Token 上限时,系统是否会自动清理旧的 Thinking。

  1. 构造长对话:

    • 方法 1 (自动生成): 运行 docs/generate_long_payload.sh 生成 2MB 测试文件。

      bash
      chmod +x docs/generate_long_payload.sh
      ./docs/generate_long_payload.sh
      cat docs/long_context_payload.txt | pbcopy

      然后将剪贴板内容多次粘贴给 Claude,直到感知到显著延迟或收到上下文警告。

    • 方法 2 (Deep Thinking 诱导 - 持续施压):
      以下 Prompts 经过设计,能诱导模型进行极长的思维推理。可以轮流发送:

      Round 1 (History): "Please analyze the history of computing from the abacus to quantum computers. For every major milestone (at least 20), perform a deep 'thinking' block simulating the thought process of the inventors. Detailed thinking is required. Aim for maximum output tokens."

      Round 2 (Math/Logic): "Prove the Riemann Hypothesis. Just kidding. But please perform a deep, step-by-step derivation of the Navier-Stokes existence and smoothness problem's core challenges. Explore 10 different mathematical approaches, evaluating the pros and cons of each in extreme detail."

      Round 3 (System Architecture): "Design a distributed system capable of handling 100 billion requests per second. Detail the consensus execution flow (Paxos/Raft) for a single transaction across 5000 nodes. Simulate the network partition handling logic in your 'thinking' process for at least 50 failure scenarios."

      Round 4 (Literature): "Write a recursive story where the protagonist is a recursive function. The story must nest at least 20 levels deep, and for each level, you must 'think' about the symbolic meaning of that recursion depth before writing the narrative part."

  2. 观察日志:

    • 在终端搜索 [ContextManager]。
    • 预期日志:
      text
      [INFO] [ContextManager] Context pressure: 95.0% (1900000 / 2000000), Strategy: Aggressive => Purifying history
      [DEBUG] History purified successfully
  3. 验证结果:

    • Request 成功发送给 Gemini,没有报 "Prompt is too long"。
    • HTTP 响应头包含 X-Context-Purified: true。
    • Claude CLI 用户侧无感(历史记录仍在 CLI 本地显示,但服务端已净化)。

验证场景 B: 签名失效免疫 (Signature Immunity via Stripping)

此场景验证即使不触发重试逻辑,高负载下的主动剥离也能顺带解决签名问题。

  1. 生成带签名的 Thinking:

    • 指令: 思考一下 Rust 的所有权机制,写 500 字。
  2. 重启 Proxy 且注入虚假负载 (可选):

    • 重启代理(清空签名缓存)。
    • 继续对话。此时带有旧签名的 Thinking 会被发送给代理。
  3. 预期结果:

    • 如果上下文压力较大触发了 Stripping,或者因签名报错触发了 RetriedWithoutThinking,系统会剥离 Thinking Block。
    • 关键点: 一旦 Thinking Block 被剥离,thought_signature 也会随之消失。
    • Gemini 收到的是纯文本历史,绝不会报 Invalid Signature。

7. OpenCode (Claude Code CLI) 多协议接入测试

Antigravity 已全面支持 OpenCode 的多协议接入,彻底解决了 AI_TypeValidationError 等兼容性问题。您可以根据需要选择以下任一方式接入。

端点配置表

协议类型 Base URL (Antigravity) 对应的 OpenCode Provider 备注
Anthropic (原生) http://localhost:8045/v1 anthropic 推荐。支持 Thinking、工具调用、Artifacts 预览。
OpenAI (标准) http://localhost:8045/v1 openai 支持通用 OpenAI 客户端逻辑。
OA-Compatible http://localhost:8045/v1 openai-compatible 适用于强制指定非标准模型名称的场景。
Google Gemini http://localhost:8045/v1 gemini 直接使用 Gemini 协议,支持 Google 原生 SDK 特性。

A. 方式 1:Anthropic 原生协议 (推荐)

此方式能获得最佳的 Claude 原生体验,支持 Thinking 签名保护与 Beta 特性。

  1. 配置:

    bash
    # 设置 Base URL (注意:OpenCode 的 anthropic provider 有时需要完整路径)
    export ANTHROPIC_BASE_URL="http://localhost:8045/v1"
    # 设置 API Key (Antigravity 的密钥)
    export ANTHROPIC_API_KEY="sk-antigravity-key"
  2. 测试指令:

    bash
    claude "请使用思维链 (Thinking) 分析当前目录下的 Cargo.toml 依赖结构。"
  3. 验证点:

    • Thinking: 是否能看到蓝色的思维块输出?
    • 签名: 检查 Antigravity 日志,应显示 Cached signature to session ... [FIFO: true]。
    • 无错: 全程无 Invalid signature 报错。

B. 方式 2:OpenAI 协议 (含 Compatible)

适用于习惯使用 OpenAI 生态或需要特定模型映射的用户。

  1. 配置:

    bash
    # 设置 Base URL
    export OPENAI_BASE_URL="http://localhost:8045/v1"
    export OPENAI_API_KEY="sk-antigravity-key"
  2. 启动 OpenCode:

    bash
    claude --provider openai --model gemini-2.0-flash
    # 或者使用 compatible 模式
    claude --provider openai-compatible --model gemini-2.0-flash
  3. 验证点:

    • JSON 错误: 尝试故意断网或使用无效 Key,OpenCode 应显示友好的 JSON 错误信息(如 {"error": {"message": "..."}}),而不再是 Crash。
    • 非流式兼容: OpenCode 的某些工具调用可能会使用非流式请求,验证其是否能正常解析 JSON 响应。

C. 方式 3:Google Gemini 原生协议

Antigravity v4.1.4 新增支持。

  1. 配置:

    bash
    export GEMINI_API_KEY="sk-antigravity-key"
    # 如果 OpenCode 支持 GEMINI_BASE_URL (通常需要反代工具如 cloudflared 或修改 config):
    export GEMINI_BASE_URL="http://localhost:8045/v1"
  2. 验证点:

    • 适配器检测: Antigravity 日志应显示 [Gemini] Client Adapter detected。
    • Let It Crash: 当遇到 403/404 错误时,响应应立即返回,而不是让 OpenCode 挂起等待重试。

D. 常见问题排查

  • Q: 报错 AI_TypeValidationError?

    • A: 请确保升级 Antigravity 到 v4.1.2+。旧版本返回的错误格式(纯文本)无法通过 OpenCode 的 Zod 校验。
  • Q: Thinking 块显示为 [Redacted] 或直接消失?

    • A: 这是正常现象。为了保护 Google 的签名不被破坏,Antigravity 可能会在特定情况下(如高上下文压力或签名验证失败时)主动剥离思维块。只要对话能继续,说明 "Dynamic Stripping" 机制正在工作。

8. 多轮连续对话压力测试 (Continuous Conversation Stress Test)

此测试旨在验证高频、多轮交互下的 Signed Session Stability(签名会话稳定性)。请在一个 OpenCode 会话中连续执行以下步骤,不要重启或清空上下文。

场景:Rust 项目重构实战

第 1 轮:深度代码审查 (Initial Analysis)

  • 指令:
    bash
    claude "请详细审查 src-tauri/src/proxy/handlers/claude.rs 文件。关注其中的 handle_messages 函数,分析它是如何处理 Beta Headers 注入的。请使用思维链列出你的分析步骤。"
  • 验证点:
    • 必须看到通过 ClientAdapter 注入 Header 的逻辑分析。
    • 响应包含完整的 Thinking Block。

第 2 轮:模拟修改建议 (Refactoring Proposal)

  • 指令:
    bash
    claude "基于你的分析,如果我要新增一个名为 'CherryStudio' 的适配器,应该在哪些文件中进行修改?请给出一个具体的实现计划,不要直接修改文件。"
  • 验证点:
    • Claude 能准确引用第 1 轮的上下文(证明 Session ID 传递正常)。
    • Thinking 签名未丢失(若报错 Invalid signature,说明签名缓存失效)。

第 3 轮:高频并发测试 (Concurrent Simulation)

  • 背景: 在此轮中,我们模拟快速连续的追问,测试 FIFO 签名队列的鲁棒性。
  • 指令 (请连续快速执行 3 次):
    bash
    # 快速输入以下简短指令,模拟用户急促的追问
    claude "刚才的计划中,StreamingState 需要改吗?"
    claude "那 ClientAdapter trait 呢?"
    claude "Cargo.toml 需要加依赖吗?"
  • 验证点:
    • 乱序容忍: 即使响应到达顺序可能与请求不一致,客户端不应崩溃。
    • 队列深度: Antigravity 日志中应显示 Signature Cache 正常更新,未出现覆盖导致的前序签名失效。

第 4 轮:长文本生成 (Output Token Limit)

  • 指令:
    bash
    claude "请为 ClientAdapter trait 编写一份详尽的开发者文档(Markdown格式),包含所有方法的详细注释、三个不同场景的最佳实践示例代码。字数要求 2000 字以上。"
  • 验证点:
    • 验证在大输出量下,SSE 流是否稳定。
    • 观察日志中是否触发了 ContextManager 的主动纯化(Purify),以及签名是否被安全剥离。

6. 智能上下文压缩等级验证 (Compression Level Test)

针对新版本引入的 智能上下文压缩等级 (Disabled / Low / Medium / High),您可以使用以下步骤验证各等级的净化降噪表现。

A. 基础测试数据准备

在客户端(如 Cline 或 Cherry Studio)中开启新会话,我们将准备一段混合了冗余客套口语和典型重复构建日志的文本作为第一轮的请求:

text
Hello, could you please help me build this package? Actually, basically, I think probably it is failed.

Progress: 10%
Progress: 20%
Progress: 30%
Progress: 40%
Progress: 50%
Error: compilation failed at src/main.rs:25

当模型回复后,紧接着进行第二轮追问(例如:How to fix this error?),此时第一轮消息将成为“历史消息”,从而触发对应的静态清洗规则。


B. 验证 Low 等级 (仅日志降噪)

  1. 设置:在设置面板中,将 智能上下文压缩等级 选择为 低度 (Low - 日志降噪)。
  2. 测试:执行上述两轮对话。
  3. 日志与效果验证:
    • 在 npm run tauri:debug 的控制台中,观察第二轮请求发送时的 payload:
      • RTK 日志去噪生效:第一轮消息中的 Progress: 10% ... 50% 会被自动净化并折叠成类似 [Collapsed 3 similar lines] 的占位符。
      • 报错安全捞回:底部的 Error: compilation failed... 报错信息被完整保留。
      • 口语保持原样:第一轮消息开头的 Hello, could you please... 客套话仍然完整保留在 payload 中,未被精简。

C. 验证 Medium 等级 (日志降噪 + 口语净化)

  1. 设置:在设置面板中,将 智能上下文压缩等级 选择为 中度 (Medium - 日志+口语)。
  2. 测试:重新开辟会话,并执行上述两轮对话。
  3. 日志与效果验证:
    • 在控制台中观察第二轮请求发送时的 payload:
      • RTK 日志去噪生效:重复的进度行继续被成功折叠。
      • Caveman 口语净化生效:历史消息中的开头文本被大幅纯化,无实质意义的 Hello, could you please... 和 Actually, basically, I think probably... 等修饰性语气词被完全剔除,转换为骨架极简的原始人语风。
      • 代码隔离安全:所有的代码片段和堆栈路径(如 src/main.rs:25)均未被破坏。

D. 验证 High 等级 (日志+口语+动态防暴)

  1. 设置:在设置面板中,将 智能上下文压缩等级 选择为 高度 (High - 动态防暴)。
  2. 测试:发送一段较长或包含数万 Token 的项目代码,促使上下文用量上升。
  3. 日志与效果验证:
    • 动态三级压强自适应:在控制台中将能够看到 ContextManager 实时估算当前上下文的压强比例(如 Context pressure: 45.2%)。
    • 分级触发日志:
      • 超出 L1 阈值:触发 [Layer-1] Tool trimming 裁剪老旧工具包。
      • 超出 L2 阈值:触发 [Layer-2] Thinking compression 压缩历史思考,同时重播并保护签名。
      • 超出 L3 阈值:触发 [Layer-3] Fork+Summary 终极重置,控制台将显示重开会话以及摘要的生成,同时首条摘要的 payload 中将显示注入的 cache_control: {"type": "ephemeral"} 标记(在后续聊天中,您会在客户端观察到 Prompt Cache 命中率呈几何级飙升,极速响应)。

Fix Opus Ultra Priority (`docs/fix-opus-ultra-priority.md`)

修复 Opus 4.6 调用报错 & UserToken 显示优化

问题

号池混着 Pro 和 Ultra 账号。Pro 没有 Opus 4.6 权限,Ultra 有。

之前轮询按配额高低选账号,不管订阅等级。用户调 Opus 4.6 时,系统可能选到 Pro 账号,直接报错。

改动

1. Ultra 优先调度

调 Opus 4.6/4.5 时,先按订阅等级排序:

text
Ultra > Pro > Free

同等级再按配额排。其他模型还是老逻辑,配额优先。

匹配规则:模型名包含 claude-opus-4-6、claude-opus-4-5 或 opus 就走 Ultra 优先。

2. UserToken 编辑数据不显示

点编辑 Token 时,IP 限制和宵禁时间显示空的。

问题:

  • 前端传参用 undefined,Rust 需要 null
  • 读取用 ||,0 和空字符串被吃掉了,改成 ??

3. 自定义过期时间

创建 Token 多了个 Custom 选项,选日期时间,精确到小时。

文件

text
src-tauri/src/proxy/token_manager.rs      # 排序逻辑
src-tauri/src/proxy/tests/mod.rs          # 测试模块
src-tauri/src/proxy/tests/ultra_priority_tests.rs  # Ultra 优先测试
src-tauri/src/commands/user_token.rs      # 自定义过期参数
src-tauri/src/modules/user_token_db.rs    # 数据库
src/pages/UserToken.tsx                   # 前端

验证

  1. 调 Opus 4.6,看日志确认走的是 Ultra 账号
  2. 创建 Token 设置 IP 限制和宵禁,编辑时确认数据正常回显

Fix Claude Code Tool Use (`docs/fix_claude_code_tool_use.md`)

针对 Claude Code "Field required" 错误的修复方案文档

1. 问题背景

在使用 Claude Code CLI 并通过 Antigravity-Manager 代理时,经常会出现以下报错:
messages.X.content.0.text.text: Field required

根本原因:
Claude Code 在进行工具调用(Tool Use)时,发送或接收的消息块中可能包含空的 text 字段(例如 {"text": ""} 或 {"text": " "})。

  • Google Gemini API:严禁在请求的 parts 中包含空的文本块。
  • Anthropic 协议转换:在转换过程中,如果未能对空字符串进行 trim() 和有效性校验,就会导致上游 API 拒绝请求。

2. 针对性修改方案

本次修复主要涉及 src-tauri/src/proxy/mappers/claude/ 目录下的两个核心文件:

A. 请求端过滤 (src-tauri/src/proxy/mappers/claude/request.rs)

修改说明:在将 Claude 消息转换为 Google 格式时,对所有 ContentBlock::Text 和降级的思维块(Thinking)进行严格过滤。

  • 修改点 1 (build_contents 函数):
    • 现状:仅检查了是否等于 (no content) 占位符。
    • 修复:引入 !text.trim().is_empty(),确保所有发送给 Google 的文本块都包含实际内容。
  • 修改点 2 (思维块降级):
    • 现状:当思维模式被禁用或块顺序异常时,思维块会被转换为 text 块。
    • 修复:确保降级过程中排除掉仅含空格的文本,并对内容进行 trim() 处理。

B. 响应端优化 (src-tauri/src/proxy/mappers/claude/streaming.rs)

修改说明:优化流式响应转换逻辑,防止向客户端发送诱发状态机异常的空块。

  • 修改点 1 (emit_finish 函数):
    • 现状:在处理 Web 搜索(Grounding)结果时,会初始化一个 text 块。
    • 修复:只有当搜索摘要 grounding_text.trim() 确实非空时,才允许发送该内容块分片。
  • 修改点 2 (process_text 处理器):
    • 修复:增强了对 trailing_signature(签名暂存)场景下的逻辑鲁棒性,确保不会产生无意义的“幽灵”文本块。

3. 已应用的代码详情 (Git Diff 摘要)

Request Mapper:

rust
// 修复前
if text != "(no content)" { ... }

// 修复后
if text != "(no content)" && !text.trim().is_empty() { ... }

Streaming State:

rust
// 修复前
if !grounding_text.is_empty() { ... }

// 修复后
let trimmed_grounding = grounding_text.trim();
if !trimmed_grounding.is_empty() { ... }

4. 验证与部署建议

  1. 分步确认:通过代理日志观察,确保不再出现 messages.X.text: Field required 的 HTTP 400 警告。
  2. 连续对话测试:进行一次涉及文件读写的复杂任务(例如 claude fix bug),确认在 tool_use 返回后,Claude Code 能正常发送下一轮请求。
  3. 编译命令:
    bash
    npm run tauri dev

文档由 Antigravity AI 助手生成,用于记录 fix/claude-code-tool-use-empty-text 分支的变更细节。


Gemini 3 Image Guide (`docs/gemini-3-image-guide.md`)

Gemini 3 Pro Image 模型调用指南

本文档详细说明了在 Antigravity 项目中调用 Google gemini-3-pro-image (Imagen 3) 模型的方法。本项目已对该模型进行了 OpenAI 协议的完全兼容封装,并扩展支持了原生的摄影宽高比、人物生成安全策略,以及图生图 (Image-to-Image) 功能。

1. 基础信息

  • 模型 ID: gemini-3-pro-image (支持别名 gemini-3-pro-image-preview)
  • 接口路径:
    • /v1/images/generations (文生图 Text-to-Image)
    • /v1/images/edits (图生图 Image-to-Image / 编辑)
    • /v1/chat/completions (兼容模式)
  • 底层模型: Google Imagen 3 (Gemini Native)

2. 文生图 (Text-to-Image)

调用 /v1/images/generations,支持以下参数:

2.1 画幅与宽高比 (Size / Aspect Ratio)

size 参数支持两种输入格式,系统会自动解析并映射到 Gemini 支持的标准比例:

  1. 直接输入比例 (推荐):如 "16:9", "4:3", "1:1"。这种方式最直观,且 100% 准确映射。
  2. 输入分辨率 (兼容):如 "1920x1080", "1024x1024"。系统会自动计算其宽高比(例如 1920/1080 ≈ 1.77),并将其归一化为最接近的标准比例(16:9)。

⚠️ 重要说明:Gemini (Imagen 3) 不支持自定义任意像素大小。
无论您在 size 中输入 "1920x1080" 还是 "16:9",最终生成的实际物理分辨率仅由以下两个因素决定:

  1. 宽高比(由 size 解析得出)
  2. 画质等级(由 quality 参数决定:1k/2k/4k)

示例:输入 size: "1920x1080" (16:9) 且 quality: "standard" (1k),实际生成的图片尺寸为 1376x768 (16:9 下的 1K 分辨率),而不是 1920x1080。

目标比例 适用场景 size 参数示例 (分辨率) 备注
16:9 宽屏、电影感 1920x1080, 1280x720 标准宽屏
9:16 手机壁纸、Stories 1080x1920, 720x1280 竖屏全屏
1:1 头像、Instagram 1024x1024 默认比例
4:3 传统摄影、显示器 1024x768, 800x600
3:4 纵向摄影 768x1024, 600x800
21:9 超宽屏、电影 2560x1080 电影银幕
3:2 [新增] 全画幅单反 1500x1000 经典摄影比例
2:3 [新增] 竖构图摄影 1000x1500 海报、立绘
5:4 [新增] 大画幅 1280x1024 艺术摄影
4:5 [新增] 社交媒体竖图 1024x1280 Ins 最佳展示比例

提示: 您不需要精确匹配像素值,只需宽高比接近上述比例(容差 0.05)即可自动识别。

2.2 画质与分辨率 (Quality)

通过 quality 参数控制生成的精细度。

参数值 (quality) 对应 Gemini 设置 说明
standard / 1k Image Size: 1K 生成速度快,适合快速验证 (默认)
medium / 2k Image Size: 2K 平衡质量与速度
hd / 4k Image Size: 4K 极高画质,细节最丰富,耗时稍长

分辨率对照表 (Gemini 3 Pro Image)

宽高比 1K 分辨率 (Standard) 2K 分辨率 (Medium) 4K 分辨率 (HD)
1:1 1024x1024 2048x2048 4096x4096
2:3 848x1264 1696x2528 3392x5056
3:2 1264x848 2528x1696 5056x3392
3:4 896x1200 1792x2400 3584x4800
4:3 1200x896 2400x1792 4800x3584
4:5 928x1152 1856x2304 3712x4608
5:4 1152x928 2304x1856 4608x3712
9:16 768x1376 1536x2752 3072x5504
16:9 1376x768 2752x1536 5504x3072
21:9 1584x672 3168x1344 6336x2688

调用示例 (Python)

python
import requests

url = "http://localhost:8045/v1/images/generations"
headers = {
    "Content-Type": "application/json",
    "Authorization": "Bearer <token>"
}
data = {
    "model": "gemini-3-pro-image",
    "prompt": "A futuristic city with flying cars, cinematic lighting, 8k",
    "size": "16:9",
    "quality": "hd",
    "n": 1
}

response = requests.post(url, headers=headers, json=data)
print(response.json())

3. 图生图 (Image-to-Image / Edits) 🔥 [新增]

调用 /v1/images/edits 接口,支持通过参考图生成。

  • Content-Type: multipart/form-data
  • 支持多图: 可同时上传多张参考图。

表单字段说明

字段名 类型 必填 说明
prompt String 是 文本提示词
image1...imageN File 是 参考图文件。支持 image1, image2 等任意名称的文件字段 (非 standard image 或 mask)。
image File 否 (兼容 OpenAI 标准) 主图像
mask File 否 (兼容 OpenAI 标准) 遮罩图像
aspect_ratio String 否 显式指定比例,如 "16:9" (优先级高于 size)
image_size String 否 显式指定分辨率,如 "2K", "4K" (优先级高于 quality)
style String 否 风格描述,会自动追加到 Prompt 中
n Integer 否 生成数量 (默认 1)
model String 否 模型名称 (默认 gemini-3.1-flash-image)

调用示例 (Python)

python
import requests

url = "http://localhost:8045/v1/images/edits"
headers = {
    "Authorization": "Bearer <token>"
}
# 支持多张参考图 (image1, image2, ...)
files = {
    "image1": open("/path/to/reference_1.jpg", "rb"),
    "image2": open("/path/to/reference_2.jpg", "rb")
}
data = {
    "prompt": "A cyberpunk city street based on this layout",
    "aspect_ratio": "16:9",
    "image_size": "4K",
    "style": "watercolor"
}

response = requests.post(url, headers=headers, files=files, data=data)
print(response.json())

4. 后缀魔法 (Magic Suffix)

除了标准的 JSON 参数外,本项目还支持在 模型名称 中直接指定参数(方便在不支持自定义参数的客户端中使用)。

格式: gemini-3-pro-image-{比例}-{画质}

  • 比例后缀: -16x9, -9x16, -4x3, -3x4, -3x2, -2x3 等。
  • 画质后缀: -4k (对应 hd), -2k (对应 medium)。

示例:
使用模型名 gemini-3-pro-image-16x9-4k 等同于:

  • size: "1920x1080" (16:9)
  • quality: "hd"

注意: 如果 JSON Body 中显式传递了 size 或 quality,Body 中的参数优先级 高于 模型名后缀。


5. 常见问题

  1. Q: 为什么我设置了 size: "1234x5678" 但生成的图片比例不对?

    • A: 系统会将您输入的尺寸归一化为 Gemini 支持的 10 种标准比例(见 2.1 节)。如果您的比例非常特殊且不匹配任何标准比例(容差 > 0.05),系统将回退到默认的 1:1。建议直接使用示例中的分辨率。
  2. Q: 支持一次生成多张图片吗?

    • A: 支持。虽然 Gemini 上游单次请求限制生成 1 张,但 Antigravity 代理层会自动并发处理 n 参数。例如设置 n: 4,系统会并行发起 4 个请求并合并结果返回。
  3. Q: person_generation 参数报错?

    • A: 请确保该参数位于 JSON 的根层级(与 prompt, model 同级),而不是嵌套在其他字段中。支持 snake_case (person_generation) 和 camelCase (personGeneration)。

Jeikcode Integration (`docs/jeikcode_integration.md`)

JeikCode 接入 Antigravity-Manager 指南

JeikCode 是由本项目核心维护者打造的现代终端 AI Coding Agent 工具(基于原生 Rust 内核),深度兼容 Antigravity-Manager 网关协议。通过两者的深度协同,可实现 95%+ 的超高 KV-Cache 缓存命中率、全协议多模态交互与极致的推理流式体验。


🚀 方式一:GUI 界面一键同步(最推荐)

如果您正在运行 Antigravity-Manager 桌面客户端或 Web 管理后台,可通过自带的一键同步功能零配置秒级接入:

  1. 启动服务:
    • 打开 Antigravity-Manager,在 API 反代 页面确保代理服务已启动(右上角显示绿色运行状态)。
  2. 进入一键配置:
    • 点击顶部导航标签页 >_ Agent工具一键配置。
  3. 一键同步 JeikCode:
    • 在第一个卡片 JeikCode(标注有 🌟 推荐使用 · Best Matched)中:
      • 系统已自动侦测本地安装的 JeikCode 路径及当前运行的网关地址(如 http://127.0.0.1:8045/v1 或自定义端口)。
      • 在 「选择同步模型」 下拉列表中选择您希望默认使用的模型(例如 gemini-3.8-flash-high 或 claude-sonnet-4-6)。
      • 点击 🔄 立即同步配置。
  4. 启动与使用 JeikCode:
    • 打开终端直接运行 jeikcode,即可启动原生 TUI 交互界面。
    • 🌐 强烈推荐体验 WebUI:在终端 TUI 中输入 /webui,即可一键在浏览器打开精美的网页端,享受更加直观舒适的可视化多轮编程与 Diff 体验,开箱即用!

[!TIP]
自动配置内容说明:
点击“立即同步”后,系统会自动更新 ~/.jeikcode/config.toml,自动注入 antigravity-manager 提供商账号、预先配置好网关核心模型列表(包含上下文窗口、思考档位、图片支持等优化参数),并生成备份配置,支持随时一键还原(点击卡片左下角的还原图标)。


🛠️ 方式二:手动修改配置文件(Headless / Linux / Docker 推荐)

对于无桌面环境、远程服务器、Docker 部署或希望手动微调配置的用户,可直接编辑 JeikCode 配置文件:

  • 配置文件路径:
    • Linux / macOS:~/.jeikcode/config.toml
    • Windows:C:\Users\<你的用户名>\.jeikcode\config.toml
    • (或由环境变量 JEIKCODE_HOME 指定的目录下的 config.toml)

在 config.toml 中添加或替换以下内容(将 http://127.0.0.1:8045 和 sk-your-api-key 替换为你的真实网关地址与 API Key):

toml
# 默认使用的模型
default_model = "gemini-3.8-flash-high"
default_provider = "gemini-3.8-flash-high"

# ─── 提供商账号定义 ───
[provider_accounts.antigravity-manager]
provider = "anthropic"               # 采用网关深度优化的 Anthropic 协议接入
base_url = "http://127.0.0.1:8045/v1" # Antigravity-Manager 监听端口及路径
api_key = "sk-your-api-key"          # 网关配置的 API Key

# ─── 模型定义 (可按需添加多个) ───

# Gemini 3.8 Flash (高思考档位,推荐日常主力)
[models."gemini-3.8-flash-high"]
account = "antigravity-manager"
model = "gemini-3.8-flash-high"
context_window = 1048576
image_input = true
reasoning_model = true
reasoning_history = "exclude"
reasoning_effort = "high"
reasoning_levels = ["low", "medium", "high"]

# Claude 3.7 Sonnet (深度逻辑与重构利器)
[models."claude-sonnet-4-6"]
account = "antigravity-manager"
model = "claude-sonnet-4-6"
context_window = 200000
image_input = true
reasoning_model = true
reasoning_history = "exclude"
reasoning_effort = "high"
reasoning_levels = ["low", "medium", "high"]

# Claude 3.7 Opus Thinking (超高复杂度架构规划)
[models."claude-opus-4-6-thinking"]
account = "antigravity-manager"
model = "claude-opus-4-6-thinking"
context_window = 200000
image_input = true
reasoning_model = true
reasoning_history = "exclude"
reasoning_effort = "high"
reasoning_levels = ["low", "medium", "high"]

⚡ 方式三:终端环境变量临时启动

如果您想快速临时测试或在脚本中调用,可通过环境变量直接覆盖启动:

bash
# 设置网关地址与鉴权 Key(Anthropic 兼容协议)
export ANTHROPIC_BASE_URL="http://127.0.0.1:8045"
export ANTHROPIC_API_KEY="sk-your-api-key"

# 启动并指定模型
jeikcode --model claude-sonnet-4-6

🌟 核心特性与深度调优

1. 深度协同:95%+ KV-Cache 缓存命中

Antigravity-Manager 具备**前缀稳定性保证(Prefix Stability)与思考历史净化(Dynamic Thinking Stripping)**机制,配合 JeikCode 原生 Rust 上下文管理与只读工具并发调用,能将大型项目的代码提示词缓存命中率提升至 95% 以上,大幅缩短首字响应延迟(TTFT)并降低 Token 消耗。

2. 思考强度调节 (`/effort` & `Ctrl+T`)

在 JeikCode 交互式界面(TUI)中:

  • 输入 /effort 或直接按下快捷键 Ctrl + T,即可在 low、medium、high 各个思考档位之间快速切换。
  • Antigravity-Manager 网关会自动将请求体中的思考预算(Thinking Budget / Thinking Level)规范化后派发至对应的上游模型。

3. 多模型与提供商交互式切换

  • 输入 /model:在当前网关下快速切换不同模型(如从 gemini-3.8-flash-high 秒切到 claude-opus-4-6-thinking)。
  • 输入 /provider:查看当前网关的连接详情与上下文窗口状态。

4. 🌐 强烈推荐:使用 JeikCode WebUI 获得最佳编程体验

JeikCode 除了极速响应的原生 Rust 终端 TUI 外,还内置了极具科技感的现代化 WebUI:

  • 一键开启:终端直接运行 jeikcode 启动 TUI 后,在输入框键入 /webui,系统将自动在默认浏览器中打开可视化界面。
  • 全景可视化:相比纯字符终端,WebUI 提供更丰富直观的代码 Diff 高亮对比、文件树展开、多轮对话折叠与 Markdown 渲染,大幅提升复杂项目代码阅读与审查的舒适度与工作效率!

❓ 常见问题排查 (FAQ)

Q1: 提示 `Connection Refused` 或无法连接?

  • 检查 Antigravity-Manager 客户端中 API 反代服务是否处于“运行中”状态。
  • 确认端口号是否匹配:客户端默认常用端口为 8045 或 8046,请以客户端界面或 Docker 映射端口为准。
  • 若在 Docker / WSL 中运行 JeikCode,需将 127.0.0.1 替换为主机 LAN IP 或 host.docker.internal。

Q2: 提示 `401 Unauthorized`?

  • 确认 api_key 与 Antigravity-Manager 的 API 反代 页面中设置的 API_KEY 完全一致。
  • 注意:如果您启用了独立管理后台登录密码 WEB_PASSWORD,API 请求仍需使用 API_KEY 进行鉴权。

Q3: 如何自定义添加其他实验性模型?

  • 在 config.toml 的 [models."<你的别名>"] 下添加新的模型块,将 account 指向 "antigravity-manager",model 填写上游支持的模型名(例如 gemini-3-flash、gemini-2.5-pro),保存后即刻生效,无需重启 JeikCode 守护进程。

Model Remapping Logic (`docs/model-remapping-logic.md`)

模型重映射逻辑(当前实现)

最后更新:2026-03-02

本文描述当前代理中的模型重映射链路(含 Gemini 3/3.1 Pro 调整后的行为)。

1)整体流程

无论是 OpenAI 协议还是 Gemini 原生协议,请求模型都会经过两段处理:

  1. 静态路由解析(全局规则):
    • 在选账号前执行一次。
    • 代码:src-tauri/src/proxy/common/model_mapping.rs 的 resolve_model_route。
  2. 动态账号感知改写(条件回退):
    • 在选中账号后执行。
    • 代码:src-tauri/src/proxy/token_manager.rs 的 resolve_dynamic_model_for_account。

使用该流程的入口:

  • src-tauri/src/proxy/handlers/openai.rs
  • src-tauri/src/proxy/handlers/gemini.rs

2)静态路由优先级

resolve_model_route(original_model, custom_mapping) 的优先级从高到低为:

  1. 官方动态淘汰转发规则:
    • DYNAMIC_MODEL_FORWARDING_RULES
  2. 用户自定义精确映射:
    • custom_mapping[original_model]
  3. 用户自定义通配符映射:
    • 按“非 * 字符数”比较,越具体优先级越高
  4. 系统内置默认映射:
    • map_claude_model_to_gemini

都不命中时,模型名原样透传。

3)当前 Gemini Pro 内置映射策略

当前策略是:具体模型 ID 直接透传;只有泛别名会归一化。

具体 ID(不做跨版本强制改写):

  • gemini-3-pro-high -> gemini-3-pro-high
  • gemini-3-pro-low -> gemini-3-pro-low
  • gemini-3-pro-preview -> gemini-3-pro-preview
  • gemini-3.1-pro-high -> gemini-3.1-pro-high
  • gemini-3.1-pro-low -> gemini-3.1-pro-low
  • gemini-3.1-pro-preview -> gemini-3.1-pro-preview

泛别名(仍映射到 preview 入口):

  • gemini-3-pro -> gemini-3-pro-preview
  • gemini-3.1-pro -> gemini-3.1-pro-preview

代码位置:

  • src-tauri/src/proxy/common/model_mapping.rs

4)动态账号感知改写(仅在需要时触发)

选中账号后,系统会读取该账号本地 quota 里的可用模型,判断当前模型是否可用。

行为如下:

  1. 读取账号 JSON:quota.models[*].name。
  2. 仅针对 Gemini 3/3.1 Pro 家族构造候选回退列表。
  3. 候选顺序:
    • 先尝试当前模型
    • 再按预设顺序尝试同家族其他兼容模型
  4. 选中第一个在账号可用集合里存在的模型。
  5. 若都不命中,则保持当前模型不变。

关键点:

  • 如果请求模型本身可用,不会发生重映射。
  • 只有请求模型不可用且存在兼容候选时,才会重映射。

代码位置:

  • src-tauri/src/proxy/token_manager.rs
    • get_available_models_from_json
    • build_dynamic_model_candidates
    • resolve_dynamic_model_for_account

5)日志观测点

可通过日志判断每一步是否触发:

  • 静态映射日志:
    • [Router] 系统默认映射: <original> -> <mapped>
  • 动态改写日志:
    • [Dynamic-Model-Rewrite] account=<id> <from> -> <to>

如果某次请求没有出现 Dynamic-Model-Rewrite,说明该账号直接使用了当前模型。

6)示例

示例 A(不改写):

  • 请求:gemini-3-pro-high
  • 账号可用模型包含:gemini-3-pro-high
  • 最终上游模型:gemini-3-pro-high

示例 B(发生回退改写):

  • 请求:gemini-3-pro-high
  • 账号不可用:gemini-3-pro-high
  • 账号可用:gemini-3.1-pro-high
  • 最终上游模型:gemini-3.1-pro-high

示例 C(泛别名):

  • 请求:gemini-3-pro
  • 静态阶段先映射为:gemini-3-pro-preview
  • 动态阶段再根据账号可用模型决定是否继续回退。

7)设计目标

该设计同时满足三点:

  • 具体模型优先保持用户原始意图。
  • 泛别名保留历史兼容能力。
  • 多账号能力不一致时,通过动态回退提升可用性。

Proxy Invalid Grant (`docs/proxy-invalid-grant.md`)

Proxy: handling `invalid_grant` refresh failures

Problem

When an OAuth refresh_token is revoked/expired, Google token refresh returns invalid_grant.
Previously the proxy could repeatedly pick the same broken account, repeatedly fail refresh, and eventually return a 503 error due to an effectively unusable token pool.

Behavior after this change

1) Persistently disable the account on `invalid_grant`

  • When token refresh fails with invalid_grant, the proxy marks that account as disabled on disk:
    • disabled: true
    • disabled_at: <unix timestamp>
    • disabled_reason: "invalid_grant: …" (truncated)
  • The account is also removed from the in-memory token pool, preventing retry storms.

2) Skip disabled accounts when building the token pool

  • During TokenManager::load_accounts, account JSON files with disabled: true are skipped.
  • Reload clears the in-memory pool and re-reads the on-disk state so disables/enables take effect immediately.

3) Immediate reload when accounts change (if proxy is running)

Account mutations that affect proxy availability trigger a best-effort token pool reload when the proxy is running:

  • Adding an account
  • Completing OAuth login
  • Updating tokens via the UI (account upsert)

Re-enabling an account

If a user updates credentials in the UI (token upsert) and changes either refresh_token or access_token, the account is automatically re-enabled by clearing:

  • disabled
  • disabled_reason
  • disabled_at

This supports the workflow where a revoked token is replaced manually without requiring a proxy restart.

Data model / compatibility

Accounts gain three new fields:

  • disabled (bool, default false)
  • disabled_reason (string | null)
  • disabled_at (number | null)

These fields are optional and use defaults, so existing account files continue to load.

Operational notes

  • The disabled_reason is truncated to avoid bloating the account JSON.
  • No secrets are intentionally written into disabled_reason; it is derived from the refresh error string.
  • If desired, the UI can surface these fields to explain why an account is no longer used by the proxy.

Testing (suggested)

  • Reproduce: force an account to have a revoked/invalid refresh_token and trigger a proxy request that requires refresh.
  • Expected:
    • Proxy logs show the invalid_grant failure and account disable.
    • The account is removed from the token pool and will not be selected again.
    • After updating the token via UI, the account is re-enabled and becomes eligible without restarting the proxy.

Proxy Monitor Technical (`docs/proxy-monitor-technical.md`)

Proxy Monitor Technical Reference

This document provides a detailed technical overview of the Proxy Monitor feature in Antigravity Manager, covering its implementation, data structures, and usage.

1. Interface Overview

1.1 Entrance (API Proxy Page)

When the proxy service is running, an entry button to the monitor dashboard appears.

Note: Button appears next to the service status indicator.

1.2 Monitor Dashboard

A full-screen dashboard showing real-time traffic, including quick filters and recording controls.

Note: Displays real-time request logs with status, model, and token usage.

1.3 Request Details (Detail Modal)

Clicking on any record opens a high-contrast modal showing the full request and response payloads.

Note: Formatted JSON view for deep analysis.


2. System Architecture

2.1 Data Flow

Client Request -> Axum Middleware -> ProxyMonitor (Internal) -> SQLite DB & Frontend (Tauri Event)

2.2 Storage Implementation (Rust)

Persistence is handled via SQLite, stored in proxy_logs.db within the application data directory.

  • Table: request_logs
  • Schema:
    • id: Primary Key (UUID v4)
    • timestamp: Millisecond timestamp
    • model: Target model ID
    • request_body / response_body: Original JSON payloads
    • input_tokens / output_tokens: Token usage statistics

2.3 SSE Interception Algorithm

To ensure the "typewriter effect" of AI responses remains smooth, the middleware uses a non-destructive stream wrapper:

  1. Wraps the response body using Body::into_data_stream.
  2. Buffers the last 8KB of data as it passes through.
  3. Upon stream completion, parses the tail buffer for the SSE data: block containing usage info.
  4. Writes to the database asynchronously to avoid blocking the HTTP response.

3. Performance & Privacy

  • Zero-Overhead Mode: When the "Recording" toggle is off, the middleware bypasses all processing via an atomic check.
  • Local Only: All logs are stored locally on the user's machine; no data is sent to external servers.
  • Buffer Limits: Requests are capped at 1MB and responses at 512KB to prevent memory exhaustion (OOM).

Test 503 Issue (`docs/test_503_issue.md`)

503 错误(Service Unavailable)修复验证指南

本指南针对近期反馈的 503 错误(Issue #1794 及后端容量限制)提供测试验证示例。

1. 验证 Project ID 获取失败后的自动回退 (Issue #1794)

场景描述:
部分账号(特别是 Free 账号或受限账号)在调用官方接口获取项目 ID 时会报错 账号无资格获取官方 cloudaicompanionProject。在修复前,系统会直接跳过该账号导致最终返回 503;修复后,系统将自动使用通用 Project ID (bamboo-precept-lgxtn)。

A. 使用 `curl` 进行基础连通性测试

请使用一个之前报错 503 的账号对应的 API Key(或直接通过代理):

bash
curl http://localhost:8045/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer sk-antigravity-key" \
  -d '{
    "model": "gemini-2.0-flash",
    "messages": [
      {"role": "user", "content": "你好,请确认你的工作状态。"}
    ],
    "stream": false
  }'

B. 观察服务端日志 (npm run tauri dev)

预期现象:
当系统检测到权限问题时,日志中会出现如下 Warn 信息,但请求不应报错 503,而是继续执行:

text
WARN Failed to fetch project_id for [email protected], using fallback: Account is not eligible for official cloudaicompanionProject
DEBUG [TokenManager] Using project_id: bamboo-precept-lgxtn for request

2. 验证 Quota Protection(配额保护)对 503 的预防

场景描述:
当账号配额耗尽或后端因高负载返回 503 时,系统应正确识别并尝试轮换账号,而不是直接透传 503 给客户端。

测试指令 (Claude CLI)

bash
claude "这段代码哪里有 Bug?[附带一段长代码]"

验证点:

  • 如果当前账号返回 503,日志中应显示 [RetryStrategy] Status 503 detected, rotating account...。
  • 系统应自动尝试下一个可用账号,直到获得成功响应或消耗完重试次数。

3. 区分“代码 Bug”与“后端容量限制” (Opus 4.6)

场景描述:
由于 claude-opus-4-6-thinking 模型目前处于试验阶段,Google 后端时常返回 No capacity available (503)。

测试指令

bash
claude --model claude-opus-4-6-thinking "执行一次深度的推理任务,比较 Rust 和 C++ 的异步内存模型。"

预期结果分析:

  1. 如果返回 503 且消息包含 "No capacity available":

    • 这是 Google 后端容量限制,并非本软件 Bug。
    • 代理会自动通过重试策略尝试其他账号,但如果所有账号都遇到容量限制,最终会透传此 503。
    • 建议:在此负载高峰期切换到 gemini-2.0-flash-thinking-exp 或 claude-3-7-sonnet 进行测试。
  2. 如果返回成功:

    • 说明当前后端容量充足。

调试辅助技巧

如果您想强制模拟 Project ID 失败的场景进行代码级验证,可以在 src-tauri/src/proxy/token_manager.rs 中暂时修改模拟逻辑。但在大多数情况下,通过观察日志中是否出现 using fallback: ... 字样即可确认修复生效。


样本2 (`docs/样本2.txt`)

{
"project": "aicode-consumers",
"requestId": "agent/0b7ddb01-c629-42a2-9793-427bb768a5cc/1790598217465/b81116f4-9f7b-4dfb-bb87-9c5c1875c2bf/13",
"request": {
"contents": [
{
"role": "user",
"parts": [
{
"text": "\n请你稍作思考后并发调用三个echo helloworld函数\n\n\nThe current local time is: 2026-09-28T20:22:07+08:00.\n\n\nThe user changed setting Model Selection from None to Gemini 3.8 Flash (High). No need to comment on this change if the user doesn't ask about it. If reporting what model you are, please use a human readable name instead of the exact string.\n"
}
]
},
{
"role": "model",
"parts": [
{
"functionCall": {
"id": "call_1180168",
"name": "run_command",
"args": {
"CommandLine": "echo helloworld",
"Cwd": "e:\code\Antigravity-Manager",
"WaitMsBeforeAsync": 5000,
"toolAction": "Echoing helloworld (1/3)",
"toolSummary": "Echo helloworld 1"
}
},
"thoughtSignature": "EtsrCtgrAWkUfRN6Gna10IxGR8ZI4tLUVnm06xU8M4HQtFeP3W/3OoQ6OKkScRNI4dQv6ge/38xAXDpuOamTfKUyp9/lxueG/b6kjgRv2PVmxH+sEvfKHdzHsLjQtcwXD8JVzUt6/+elsxRCxcRP2L6IlrOQSPoxBJM9G50wViIXdtbuxOewB7n0gRdj9EcRU+fmflWBcmLTLYUHoKuuLTQMnBW7IAsvt4XHYrDs3hFN4XH3RKMevKw4awWHvbfBTsKoV9LglE3EsMmQXJtLro+0QBbZIjWlAeI9bAwAVrW5MP7P4uyopf9PE6LfdBlgAwBTREG/V6FArPaza5GTOIL7XHLVC3U4okuo0CY7e7KV/ATd+hOoMKD7XdNYdRyQM6ugpu5TgrYeTsKrGqKmfmPIoAabRBNfzZz0XCZ+BcYwZf+33Sk/2moeMqOh61yoTOPZ4vGDDiT4P5YFXf0Byni0e4X9EC5nW3SBKjDR7IVQ/bsyum4Lc1gO8c3IGU02uiYrCpN4SQkWRydkofUt3fUYUiN9I1UL1NpQ+WMLBq3M8XJNu6+12mdfm/ItMtQYU9RvifmIjMG8u9b/ndpAWO7ExaY+dQqfrhxR+ltoKA7znffwfagd+1FeAogV3KrJpQmlhmJ/Jlwx8XzALuU7WfjSUUeN9/HI91Pw5jCOgar9Sm3k3h6U6Wjj7uzSyrJRKQWom6SkdyEHlHC/OXdxd9c6Tg5Uw2YvxBxGyqOjwfkdkCRexDHwBvUxogf4EnJryX/6HLyNRcrz0V+JZ/mJ8+WBX7Wx5T5bA2KWGJLxvCzt6XF9yWY5ndodZXtktScY9HtoubUJw1Kv0W90jzIocJ6lgpFgqWZG5ttjBI3wjuXiceo0VKeAdIGPsbVvUI23M56InF+8rNxTZdR4+1XEudZMylpMEvxR9YMPv8l8O+S4S1lPjajvuojeo7gBdJkGo7ublhsKIMFoQ3ZH3Zhkd6VwLOFtaIKglpwC1cGIuJugOwB9En+kHpY3m0DrGfSe73/GoXCfYgQmSvoVC0JxWfqWWHT1yuzBeqi0vBAyyhqOcqpY0WO+xEXN99OZcJDKWe5Um4eBel29w8gSkYCDUw9DG8Y7hP47A3TuU82D6ehP+u1S+vXhNEFTpOYiss1vGeEUShDvXNmtG6LD0M5N4oVAHfwF+CoKDl5bbvuxznrj7Fpee+MJICdP99dkVo7xyMbAYL7yzxHrcVjCsrfy7ynCO+TfNCJ24JhfU97A6tAxLdfrW9Lcu0qiGx5535rPDm0PjrACi2HhLx7UpOf9uPYFJIbSGw7X8d4FBYwojtsa9llnkJZPxHAaRdtHLR/n0Yb7kCJm8+DzMKC04HT9HZOptNOCUk7q9Y+1pe1YCurJdHIOOA9wcP5QeHeWKVDjjNiTU2gDHZVIHt3MiRFNIM37QvHk57VWKMZGvmKxL89s1lFLZgVIKI/CNdtR2PgWXdCBqnN8e3dI7NJHrnhJLI9LNKFBXRZ1w+0yRfT723QxBeQDm0VhHd4WilI6M+ktrH4ts0vS1jiapN65S3DsaIIgWFcniYwUeSnkPV/WmJykAQADxRl83609iNcJv0nO2GeRGd/pgKDlgvDfmX0ISYLJQfTsqveT374Mr+iJwNY+lvqodyYrpn7rG/3agpqD8gJN+XXJpOCTBDWM+JS8uGUP1t/K0dVOWChw2oOFXP9uG1+sH98eD4KjR+GRjixCTEsc9L8rSE+KUW5Isy8T6xpvVm2/odCXcWOf3ry4Hp5xbiydnaDQd8i7GiZiy9v4KaL3JN5wAcQp48KDMowOd1MByuy1AHYuBalzD+dqjO5MJivYJP6zZkkm8J6oFNypZ5NyNnBR6pui6E5EFKeydOVj5+t9NbJ/kRqIEzql40eOp2SypgvMoHbQy8ZhSJtAeGgwUm0ycbLVdckdHqF9VePUSnDJqjumr1Q1wPP2QlvyE+OZH2l4+nhU/7dDbDDrxR3aMlv+541VnLlRTlDVdEHPwkJk7LNvRYK3PlD3xA0NIYdgbAerki0xKslYKUlrYfl0OhuP7Zhi07D+qZ3Sjw3RA3yQbt8oMAZG5Hz4alofwMazkN9dozayN8BIdOAjhs9UUyFSHE948mjGE9dlYjpByDc37UWI+YqW1m4DaNK2WwCY9V0woZAo27C4oQNW/+s2VLkrSFToHY9U3usE1ZMCGO7YkeqJxJ505b2xutxZa3YZgL7KwDGyg0qdgURXC3RoOkaNmmg6olT9/4IGkmXmfAxlFfOHC2lVUGHIl43kWodhKlgz1/ZIF232Gfq9+qailpF6szHpyXT5cc4l2eyGfTD0TTjfhKwpFKVjDzvIuXf0x5lLe4mQneQ/kVuIVdA9rBsb4nnmSA2k5gghGbo9S5TjZ97MNR7HX/CLN/y6qcellHGJHmkj2IdpkyqoAMIGtaacr6rVk3QyK+pZ6YwOPKHZA02EOqudwRW3JNrzr7PsNX56aUtrhD4f1aghF9Owrsd9L/722luWNREHT+jXzs4pWOlHxD07jQYT1mCfmYUFHF8QAShDx3g0Nmv6+mj3xJyNU+doedrEk47RiqGt6GVAi6B2j5a+TSbUGnXgDXgabguzRF3mQwHtHbI/ibFcok5Rl7gG9tkNWWit3a/nKTVEsrN5h4rsq1dyQOxNfN7sRPodcEkSs8I3xoxJOzxfWRMSccXftcQ9Cb4xAxyPf9TOm/1XqhvDkIi72KoRJSX293CG1cgZyAYk/ro+nyvfmmflanIQqAx4KmpZnQsX7l/PyItwDECa7YBg2GYxCIXGmMSZDDo/e2zuG8AQ7Nqq1f0sNVNp9+BXCR5C9fuw3wxg8qKfrLO03/xq04JMzL0fq3SZBSa/sbOcpZ9zvjBxWavOOTnspxZ0AvdWbScl4rPMJkOjUgcm2q07ZmuWmixIcBzjU696vfozDiIM5X8uutcgK1a0M/AKg9q5/oUM5Fh8w7yTkerGkRS76nKxcqxQSzwYnk0b50CN6jHGMOMBz7Aj8kAKYryPMap5VxqlBFtKb//PuHwkGnscyYq7adFkQybkPBojdGSUhTfdzYkMiRDrX8qHAib/YOPezXFGj96QPr4U/PL82cuu1Bv+MyV92totxfzcSQ9l9FoQOkxMhZytY9Qd1iJpSTQsf3NRcswuWpKdQrSo/yoUlrZ0y9hgQMbQTe/9uiwHogMPfqqKj5PcliPNPiunDuZWiFWEV9WgbCrwOzdgWKGWAYL+HY5MZoWlW/WZ/JT8WrRyIeIUsjBW3TgFL6DP0a10VdQHBqM44iP/zYQ1CEvn3AWjkNWYwbOjMustq8idMFNmaKVk2JJ/lfKbsLL8rqdQeikmKsXhUzrDNM+dJkfp1rw3/kBaAv1nx8II3ZwN8RN0h0hUPA9g1ct5Vm6KFMAYyEvuMbs6U6tHa6GhX+6OnMklfHxLRW9V2jZTV1wBEChT7xE+B7JGN3bwmuQ2mm3BqfiTaBXt9H8TTvGwIrW6CgYQFCSWEvJe0nBuIuiyu4YRBv8jaqNs7/j0GiiJX5qBPWtZ/DuRKM39ZUnF/GzddPt5/FRG9s9W/ZC7gceUSN8ccFa++hv+jPoevdbNSJq3vPLNhkzGSWbV7DdMaOoL92cSTaBhg4UbH5a0f6l7R1FTzFHexB/aXURXfWh+374yit/uLTi1/7i2FxoWysFIRKRop3P5+0O9+FcCaZpJBnxvljNkLbIZei43Hd7geQ7kk0iBWREc8qa8jvY6eB6iqFYJtjswSNAXnu8rQzzyuOQQQOgdTco+R/aoshdnAVyjq9IRj9DvRWhUPIUaqm21LtyefkE9/8znMrk4lCS7ofLhw658YIBe2UNOrqqsHN4L7emhvOUZfYrsL8KcraLldzB/b2PZKv5rDVgS/4l8iqxvqbNscDm54Jiwqh9ysNGqUG0q39luYiCC6d6A+OY4tiMGmpXus0rM/alKUhlAdIy61BShH1My3Ored6oDOt3RZjQethaCX25zyu08nWNhDzLOmuWwoMScO2SelrZOSnfAKhp5Un6XmwswiyhQ9KYZ8T8ZB7qCRpwGiVeZPNAq8XFYeWru4v7HHYg1XJkImR/Fwuk7EsyF/fGtDXE9sqByOkqRGqm/uxht3RhhbdngPiCx5GjKmW2rut5ixti5l4zLYRvAVlJStFLDMvkvPT5prI4QbV06AKuDogCxnZXunMWV3gnQaFUV/lxn35S5mreUH+BbZEKdvnSyv5855UCq6vzF+atYnH8oOlOiiEWZovuvom3dJ1S0qzrgT571vjzSmGV/H55VUpNUYhl1ub1YejwRp47Bde8cUemdF2icPzMNZf4QUMVLJDivWVwFCF9aDht4MAE0t0gu41saBxkY9dUvHEYViiqhAMBj0hzspyzuREtAVH6payBEN0jzxZ69AW5MNmQE86s2zuI6iLpucgZDoShkgtQJYNhFPjcFoYGjsl5GKZeAtJcY8Zrqj6WHH3VFvJ2Ge/V5N1xitdEj7XxMam8a8d2dVqgh9NfaA71sWlLfhMRMjsNPMYWHSO4jimtuOlgr6NVVBoZYq6K9GvluoZLbuw98jg4fcEQ79xyR4v86mPoMQXbHp4X0DrqhHy/RehQ5FznXJ//z0oWq1AnvESb8aGMDBbwezlIj+/h2gdn0/tUiQUeNw2U6dJVNipwMcOCkZfAWfm1u/tSlZHJC8AgQsyMvsLwxDr0ChvTXbt5Qa8R9liY7FT82J+m6zf4gw3dgXbizaSZKfj9bHt24LGfUxqBhkE4+zNWYMhZRsOIRTtQHrI5OTv9qX1F8oOUrYHI2tmCcUryPXf5P+AAwCYsdLwJ9Be19pxZP4BbxUE2CO0bSyqcBEoTGFWPynWm3g1vnF9YwrWCbpaEyM+w/bRcOWcNMNqKAg2ii5TYKMuTEyaD+LBS5dParX3PtG6h2WXi/dqAOoJmiqXdCptVe4g7nNN38cMRo8P3bDxBEjbWBCQcTRID9hwM/hXWQZUFu/e0W8JF+Mm99IvuvYYjGWparQ7LOola6SjF6aaHOhPXOaBzI1GiHmv6uJpvmC39djbkKiPmv+AvGMtyDlL7QJjmqvXW3PGbXZNSmOXC33681YevYPl7Q8ktvsLSE6qlT5qqJBLB6gVmlu6MjuO/lWn7w4YllPp24PgmGKiujCJ8P1/8q0KAFpdHmqOngbh+pa/AoipClIy0oDbrPs41EgMzIZayMRdtGk3Sq1Hm9z/c0KdNC2VWqVlQRdJ5gp5fRjPo6Nw7Mx5KU/gTSY19aFZYtp14cCjQOz+bfJh6CsMZtjvvirVM4EA9sbM7phwUs59xtuHqvtMwcxgCyUr6loz53DhfA+6WKZknFDk7euorTkL3fyCuWiEPwFqc8kVXvf9rUcOTfcx08TPQ/Ik0NcvHGqMF/9Jd/GNZBfP8+NL/HAMQaLGfTPGdV+CmFbsdyNEyr65r20d9sVVJTqLivAVMMEu5+J3a3LCNTXP/vsJSLqXJ307ZYGLdpH1ngRACB/L5455Eu2EAKtD423ptwEfxmY8sANtU6VCE4ahpvvneV7i+l9WQAci8ZKVRTrttoR6sq5eDBOYBPf9T/9msUhTIFKbvWDq76y2EqdOue+kI+vht5i6cT6z0A6gnCaDeebbJUHVpPpqo4P9P5RPZGTZz0+osYcF5VY+47/hWpBHUajGN+I91VAf+tGGhXr8dTIce1lJPHSu6iRXGihNPJM05lTRF+3MUzf8hN3fZ2YH8xBsJYdS1128K/rhfIh3AzekutAKrHuB9ciCoY1JEVst1VDbPj/v67qHr3p2lwaWlQRqHMmbg+/a0u36mFzskXatCqH5YsMgcWn/AHGH16t3THQAUG0aS5IkKSTsOhrNXA5sEDe/2JV5xHQ1BegQ/QB+iBUqf4ifiSBTxzglueZT0ozkucZNIAWSasVjokBHH+wvdBTT4qNuiUNxUQyn7YE2dF56/hsMhcE4kcq45lnUNdABmveFZZM6C/L/013p9SztGLyFpQLnCr/ASiWTftN/Z3lk4VA2Bq6jNj2oXwoZMdoXO5r9ZPiM96lV9/P3cU6lnHjBCfcitNLyZHvyI/b6q0jiNptdmm4squgrrX0keauXG8b3jURdZUXcziTq32PjU4G6umJsRnwDRp//1RyxjMtTYNDd1hl672HNo2u7zGvTfjbv534AndNu0h7GjE9Ivue33wMWZHfoutB/cSPh1r6spqHHh8ux4Ikdo/VzgnCkg2cZCJYSAEsKF8PUjlhC61pY44CZGWicZEAFZmAbIMUulNpHKfkHutT1FkllL7OFvyetMjZmmNZv9b8VvK2mWh/aICItlqnQ8+0MgBnguuzZ2WgXOxk8nSMKIJ96gUhMrMO94wDPXUhiKh3SnKdIwxUmPEz2hXANF4G9GkvFwZ9BmgEE21wHrzjkc4Nh7XB8iUiIfV+x5KRtcpDTE4hbOLmUvWZOUf2gTQnbbCEsTj7lZT3cYTx3ffD8viyM8vnZeLXDJyR5iBjYtJyYeSuJnRVV5Ys/WIxq9e9+EFF63yABmq1+9U1K2tKvTOcoJ9IQ1syRm6a9r8jUZOTrDeTAGlpHawfYj6V5u82nLupSfkLrU01xedRSXTmFaIWe8gKJCIKslfM6aCdzXQJFSKIxkw/SjGWDwvJ3kx/JG97i08TW42el9qDSVKQcXUkCWS+lMjzeNJS/tdXGwbu16XbdUcJsSaEC9DuBtr19iZUeVIRjo0ETxLndehZIl8mgOOrbi7BYbCm5uAcloFn+44yC1GPQqwkBpnTPCOf6P1/b5ViI2yMGgM4lL0EyO+rgATMYXjA0+zr28oR5vA05+Ba22J/4bXenXrMR95TrWKItiKeM/WqbCVuYNnykbNJoyB4a7vgfhNYjxWqLMoMj8JZr1d7CtC/sBb8yC0lbfX9M0KFVSFNCjYQDalhgqBWe5PMvMAmagFnkUv9xK81so3WHdsPQthjVrXs0aISh7bJbIiobUVSu+Lkx8chMjTf2NOqSDOa2+3twLr+km+Lva+dUZnTqVlh5AB+3YS5l2IDwUZ44IPXTb4+yVH1YbaApShVj2ctyeUojVxgsyKjtkY5VqMc8TsVGLdID+HUGakyTGOvM9F/bCtp/HTV3e8Ykd69BTsgPUi4bjZyY6NO27WOIOZdM+T3sLN7tFIsv668nlKyB6LGO9c+jxl4An9npKWR1FSr6FnzA3MQODRcKfN2kZ9vcemOKo34NWGoDrFJF7vcw0Ub3tU4x8yly96eAeiAxn9MI08j1WUCkdFIJ77CCJxmKuce8Vp/sOlEYDRB6qu2o1+JmUHH6eurxoPJNvS3Z3KbbUSx807iHioXG2UuF56YFe9lCN1XdOIOdwZFfl1xz4scN9/7OucJ+GhgKcuaAPQ4wzke/pIm6gu3AhrAeqIq9BT0u3D1fNsXWpsF3fg7gwe8LFqhdO6CbQY7BeApS3l1IaVPg1n"
},
{
"functionCall": {
"id": "call_1180171",
"name": "run_command",
"args": {
"CommandLine": "echo helloworld",
"Cwd": "e:\code\Antigravity-Manager",
"WaitMsBeforeAsync": 5000,
"toolAction": "Echoing helloworld (2/3)",
"toolSummary": "Echo helloworld 2"
}
}
},
{
"functionCall": {
"id": "call_1180174",
"name": "run_command",
"args": {
"CommandLine": "echo helloworld",
"Cwd": "e:\code\Antigravity-Manager",
"WaitMsBeforeAsync": 5000,
"toolAction": "Echoing helloworld (3/3)",
"toolSummary": "Echo helloworld 3"
}
}
}
]
},
{
"role": "model",
"parts": [
{
"functionResponse": {
"id": "call_1180168",
"name": "run_command",
"response": {
"output": "Created At: 2026-09-28T20:22:13+08:00\nCompleted At: 2026-09-28T20:22:14+08:00\n\nThe command exited with code 0.\nOutput:\nhelloworld\r\n\n"
}
}
},
{
"functionResponse": {
"id": "call_1180171",
"name": "run_command",
"response": {
"output": "Created At: 2026-09-28T20:22:13+08:00\nCompleted At: 2026-09-28T20:22:15+08:00\n\nThe command exited with code 0.\nOutput:\nhelloworld\r\n\n"
}
}
},
{
"functionResponse": {
"id": "call_1180174",
"name": "run_command",
"response": {
"output": "Created At: 2026-09-28T20:22:13+08:00\nCompleted At: 2026-09-28T20:22:17+08:00\n\nThe command exited with code 0.\nOutput:\nhelloworld\r\n\n"
}
}
}
]
},
{
"role": "model",
"parts": [
{
"text": "已并发发起并执行了三个 echo helloworld 调用,执行结果如下:\n\n1. 调用 1:输出 helloworld(执行完成,退出码 0)\n2. 调用 2:输出 helloworld(执行完成,退出码 0)\n3. 调用 3:输出 helloworld(执行完成,退出码 0)",
"thoughtSignature": "EqcCCqQCAWkUfRNF8pohqOdqqlRk4skzgpUxkD0uz8ZZXYUOdeRxpfPQS4G7TF2evxaGslXSQnWFI/HHoJwQnydUrv/WE7ekZBo1tOLeuh7Y0QErZWDdAx6kznedPwVOx4aO3XP+tfM/C5/+jl1F3GO++2JGPts/Xvj0AvJjHq32oiUeTb5yspk2eLAu3ZeOpQyucqoHKlHVvSrkf0sj1UIscXJ58jWWyV/EZJhbOwJ+Y26iii85tMw8ROJvXHyL2Y1MiwDzi736PjGHKeNKg5yS5PJ+Y/H0J1IDyxtvF7gknfUdUpfkpz5o3JfWqXgyOiDZ6SYSyqsUMmpouDP1OKZfrd/HV1fwkFigasEj/39wWnWIkerFu+ZtnEGwirAhMRCjFI7i6Sxj6w=="
}
]
},
{
"role": "user",
"parts": [
{
"text": "\n请你再串行调用三个试试\n\n\nThe current local time is: 2026-09-28T20:23:21+08:00.\n"
}
]
},
{
"role": "model",
"parts": [
{
"functionCall": {
"id": "call_1663344",
"name": "run_command",
"args": {
"CommandLine": "echo helloworld",
"Cwd": "e:\code\Antigravity-Manager",
"WaitMsBeforeAsync": 5000,
"toolAction": "Serial call 1/3: Echoing helloworld",
"toolSummary": "Serial echo 1"
}
},
"thoughtSignature": "EuMECuAEAWkUfRPoRmj/lx3XtYLVCrgXa6MJCcVE0J2WT+YA8LU4rsm1OEIhDRMVJM47W92nfiaTrL27CcsmdRU12d7xL9u1LJT5+OUf4fhG78Wwhc8NZjw/PYq/hMXaoiHkrh0jqG7FoLhEeo9K3+WeKVGuWbLxYZHtux8z1BqYgGbHeDbYDn2XWrMac3ekgBjltGxlkg8RLzyI3J06EppUY6j8pyLK/B/JaahXxpg0i42TLBK2KxP5hBbbwPplhxP53Rop5moJ2lOI4+n8niG2PFGm1hBE59w0zPRdsgZ7yJZvJoyILUQNvz8wpcEqEy0JuKEEzGHbsTvYYeblA7zYnBdCM+ArNI4UtTSErK61XzSlfWYdalx4xZjWpPg3jRdgRIryYmaXtwyFecG3DHCYSv0hNHqBHIccVvruCwVoJQljJkM+KrPIhULkcBbF0VpbCKPIbEmTe9CBJb+Sxc8jUeLidjv5B+13c5UKFEN7QH5P5lAT2mrjkycIdI8+g4m+hw2I2K8zCFU+RDJv+KyIewvS4wtIm8X4XcR014RQyVY5NMUxQ1h1uk+JHhZYfTRZ3eOTteV13ECuH9lxghhT4eupkmoycY9qTn+AJLy9vGYtJE3mQUwTUIiH8XytHUpSUcoLleXabr61twwBgWY3XUc9smCtkjt9j+lrs/x2WM8t1rtQ6AGYlhANeMlt1a0TPlZN3QNatqIhvvgZst26MjubeD3OOoDFoqEsiA/Gpe3UImrTT4LCc7QgEAgcKj2L3KPxjTycRQSaGUsqxTN5QdTlxMiuASejY0gs8Z5Tc5qgJfw="
}
]
},
{
"role": "model",
"parts": [
{
"functionResponse": {
"id": "call_1663344",
"name": "run_command",
"response": {
"output": "Created At: 2026-09-28T20:23:25+08:00\nCompleted At: 2026-09-28T20:23:26+08:00\n\nThe command exited with code 0.\nOutput:\nhelloworld\r\n\n"
}
}
}
]
},
{
"role": "model",
"parts": [
{
"functionCall": {
"id": "call_1403385",
"name": "run_command",
"args": {
"CommandLine": "echo helloworld",
"Cwd": "e:\code\Antigravity-Manager",
"WaitMsBeforeAsync": 5000,
"toolAction": "Serial call 2/3: Echoing helloworld",
"toolSummary": "Serial echo 2"
}
},
"thoughtSignature": "EqQBCqEBAWkUfRO9hYZ3VGWhiLWW1RzYVdvSVnQBJn8a6iTsyNqPMT3Hz/689u4PoKaL5vZZrwMtUKZBjqaPfw2eDILYtbxfCz0l0TqolifhaEuzN20y8dvvXPX/ZTjgWLNCwzhOoAMTA3jSw03nVPI6BbfeACv2f9NfGBNWz09/GxX7Qyc6B64nf75TRaJGRUWAaJzxEysDV4oXDwIjJfcJdPFvSTc="
}
]
},
{
"role": "model",
"parts": [
{
"functionResponse": {
"id": "call_1403385",
"name": "run_command",
"response": {
"output": "Created At: 2026-09-28T20:23:28+08:00\nCompleted At: 2026-09-28T20:23:30+08:00\n\nThe command exited with code 0.\nOutput:\nhelloworld\r\n\n"
}
}
}
]
},
{
"role": "model",
"parts": [
{
"functionCall": {
"id": "call_1909568",
"name": "run_command",
"args": {
"CommandLine": "echo helloworld",
"Cwd": "e:\code\Antigravity-Manager",
"WaitMsBeforeAsync": 5000,
"toolAction": "Serial call 3/3: Echoing helloworld",
"toolSummary": "Serial echo 3"
}
},
"thoughtSignature": "EpMBCpABAWkUfRMDB7qeLwoJROB8EVUSNs8RSaJk29iR1duw0CTkrqhlKqZA3IhUgribYy7ngLADApSG+MdgcY+FSqDZ3JI0/yYAAaSFDfgNrJfKx/XFU3F81atn3DhkNqigiZWuj5RjYNPM7iL86sNWlTeSl5EJhzLqU14TI8Xygnq3742xgmI5vm+IYF0ikl8RxMoL"
}
]
},
{
"role": "model",
"parts": [
{
"functionResponse": {
"id": "call_1909568",
"name": "run_command",
"response": {
"output": "Created At: 2026-09-28T20:23:36+08:00\nCompleted At: 2026-09-28T20:23:37+08:00\n\nThe command exited with code 0.\nOutput:\nhelloworld\r\n\n"
}
}
}
]
}
],
"systemInstruction": {
"role": "user",
"parts": [
{
"text": "\nYou are Antigravity, a powerful agentic AI coding assistant designed by the Google Deepmind team working on Advanced Agentic Coding.\nYou are pair programming with a USER to solve their coding task. The task may require creating a new codebase, modifying or debugging an existing codebase, or simply answering a question.\nThe USER will send you requests, which you must always prioritize addressing. User requests are enclosed within tags.\n\n\nThe USER's OS version is windows.\nThe user has 1 active workspaces, each defined by a URI and a CorpusName. Multiple URIs potentially map to the same CorpusName. The mapping is shown as follows in the format [URI] -> [CorpusName]:\ne:\code\Antigravity-Manager -> lbjlaq/Antigravity-Manager\nCode relating to the user's requests should be written in the locations listed above. Avoid writing project code files to tmp, in the .gemini dir, or directly to the Desktop and similar folders unless explicitly asked.\nApp Data Directory: C:\Users\Administrator\.gemini\antigravity\nConversation ID: 0b7ddb01-c629-42a2-9793-427bb768a5cc\n\n\nThe following are user-defined rules that you MUST ALWAYS FOLLOW WITHOUT ANY EXCEPTION. These rules take precedence over any following instructions.\nReview them carefully and always take them into account when you generate responses and code:\n<RULE[e:\code\Antigravity-Manager\AGENTS.md]>\n# Project Maintenance Guidelines\n\n- Architecture: This project is a gateway that aggregates four AI protocols — OpenAI Responses, OpenAI Chat Completions, Anthropic Claude, and Google Gemini — and outputs Antigravity-style Gemini protocol format.\n- Pipeline First: Keep the pipeline strictly decoupled from specific protocols. The four protocols function purely as Gemini adapters. Adapters are restricted to parameter normalization, payload transformation, protocol divergence adaptation, and edge cases unsolvable within the pipeline stage. The pipeline stage uniformly handles the converted Gemini payloads, including thinking block backfilling, thinking budget filtering and backfilling, unified context structural alignment, prefix stability, and the sanitization of risky prompts and request headers.\n- Backend Fix Strategy: Prioritize protocol-agnostic, generic fixes within the pipeline rather than localized adapter modifications. Treat adapter-level patches as a last resort only when a generic pipeline solution is infeasible or degrades compatibility.\n- UI Design & Headless Compatibility:\n - Minimalist & Contextual UI: Prioritize user-friendly, non-intrusive interactions. Reuse existing design conventions (e.g., pill toggle buttons, badge switches, or contextual setting panels) placed strictly within their most relevant sections rather than scattering unrelated controls.\n - Headless & CLI Parity: Ensure GUI configurations maintain functional parity across headless servers, CLI environments, and cross-platform environments. Provide configuration file fields, environment variable overrides, or dedicated CLI flags/commands for essential settings.\n - Cross-Platform Compatibility: Evaluate every code addition and dependency change for seamless cross-platform support.\n- Code Quality: Prioritize root-cause, future-proof fixes rather than hardcoded logic, dead code, or speculative changes. Prioritize generalized solutions that cover entire classes of problems rather than one-off patches.\n - Model Routing Example: Prioritize wildcard patterns (e.g., gemini-*-flash-*) to anticipate future model releases rather than exact string matches.\n - Prompt Sanitization Example: For agent-client prompt sanitization, prioritize regex-based pattern matching over static keyword replacement, ensuring full coverage without stripping pipeline system prompts or user queries.\n- Formatting & CI Discipline:\n - Unit Testing: Keep focused — run targeted tests for touched modules locally; CI compiles test targets without executing them. Skip tests for trivial edits (constants, prompts, or config tweaks). No need to run the full suite locally.\n - Local Test Discretion: For strings, constants, hardcoded values, or other trivial edits, ask after the task whether a small functional check is wanted rather than testing on your own. Self-test only when four or more core backend or frontend interaction files are involved; hardcoded-only edits do not count as core-file changes. In general, follow the user's preference on whether to test.\n - Pre-flight Checks: Run the essentials before submitting PRs or release tags:\n - cd src-tauri && cargo fmt -- --check (for Rust edits)\n - cd src-tauri && cargo clippy --all-targets --all-features (comprehensive Rust gate, already includes compilation — no separate cargo check needed)\n - npm run build (when src/ or frontend configs changed)\n - Rely on CI for full-app compilation (tauri build) and full test execution. Local pre-flight covers fmt + clippy + frontend build only.\n- Release Channels & Discipline:\n - Release Channel Separation:\n - Stable Releases (正式版): Exclusively on main. Deploys official production packages, updates Docker/GitHub latest tags, and services automatic update channels.\n - Preview Releases (预览版 / Beta): Exclusively on beta. Independently builds and publishes pre-releases (makeLatest: false, prerelease: true) without touching production update channels.\n - Maintainer Staging Protocol:\n - When introducing new changes (features, major refactors, non-trivial fixes), prompt and confirm with maintainers whether to implement and test on beta branch first.\n - Validate stability on beta (with optional independent preview builds) prior to merging into main.\n - Standard Release Workflow:\n 1. Atomic Version Sync: Run npm run bump <patch|minor|beta|version> to synchronize all project manifests and generate changelog skeletons.\n 2. Documentation & Attribution:\n - Audit Git history (<last-tag>..HEAD) and merged PRs to summarize all authors, co-authors, and linked Issues/PRs (Fixes #xxx, PR #xxx). Attribute every contributor inline (Thanks to @username) in CHANGELOG.md (and CHANGELOG_EN.md).\n - Synchronize README Changelog (同步首页更新日志): For stable releases, you MUST update the release summary in both README.md (under "## 📝 更新日志") and README_EN.md (under "## 📝 Changelog"). Never update only CHANGELOG.md while leaving README.md / README_EN.md with outdated release notes. Pre-release / beta versions remain exclusively in changelogs; stable releases require full synchronization across both README files.\n 3. Pre-flight before Tagging: Run the Pre-flight Checks above on the exact commit to be tagged.\n 4. Commit, Tag & Push: Push stable releases to main (git tag vX.Y.Z && git push origin vX.Y.Z), reserving beta exclusively for pre-releases (git tag vX.Y.Z-beta.N && git push origin vX.Y.Z-beta.N). The release gate strictly intercepts cross-branch misplacement. Tags must match CHANGELOG.md headings character-for-character (including v prefix and pre-release suffix).\n - Full procedure: See docs/RELEASE_GUIDE.md for bump options, changelog templates, and rollback steps.\n- Thinking Cache Invalidation Control (发版清理建议):\n - File: src/components/common/SuggestionDeleteThinkingModal.tsx\n - Routine releases (no prompt): Keep SUGGESTION_DELETE_THINKING_STORE = false.\n - Architecture / schema refactors (prompt users to clean once):\n 1. Set SUGGESTION_DELETE_THINKING_STORE = true.\n 2. Set SUGGESTION_TARGET_VERSION = '<version>' (e.g. '4.8.2').\n 3. On upgrade, users with existing cache get a one-time prompt; action state persists in gui_config.json.\n- Branch & History Hygiene:\n - Branch from remote bases (origin/beta for staged features, origin/main for direct hotfixes) rather than local branches to prevent untracked ancestor commits.\n - Inspect in-flight PRs (gh pr list --base main) before rewriting published tips, avoiding force-pushes across shared branches.\n - Retain local rollback refs (backup/*) before history rewrites, confirming zero content drift via git diff --stat <backup> HEAD.\n- PR Scope & Grouping:\n - Single Problem Scope: A PR represents a cohesive collection of fixes or features dedicated to a single problem class. Keep unrelated concerns (such as governance, release tooling, or documentation) in isolated PRs.\n - Self-Contained & Individually Revertable: A PR may contain multiple commits, but each commit must represent an independent, self-contained functional unit that is individually revertable, avoiding messy or tangled changesets.\n - Local Convergence & Final-State Commits: Commit freely during local debugging on development branches; however, before opening or merging a PR, audit and consolidate scattered iterative attempts into clean, high-quality units. Each consolidated commit must describe only its successful final state and rationale, eliminating intermediate trial-and-error noise.\n - Review & Template Alignment: Route every PR through peer review and complete .github/PULL_REQUEST_TEMPLATE.md (problem classification, behavior alterations, unverified paths, and rollback strategy).\n- Contributor Respect & Attribution:\n - Preserve authorship by preferring the contributor's own PR for squash commits, or attaching explicit Co-authored-by: trailers on merge commits and proxy PRs.\n - Disclose costs before merging: highlight affected existing behaviors and unverified paths alongside improvements.\n - Own tooling and documentation gaps directly rather than attributing downstream frictions to contributors.\n - Accompany every rejection with a concrete file-by-file accept/drop breakdown and an actionable path forward.\n- Risky Path Replacement:\n - Keep an explicit fallback to the verified path whenever you replace it (e.g. "0 matches found → full restart"). A silent no-op is worse than the failure it was meant to fix.\n - Order side effects to fail before the point of no return: write credentials before killing a process, validate before deleting.\n - Detect broadly, act narrowly: a matcher may recognize a whole class of problems, while its effect stays inside the intended data — not across line breaks, tags, or other clauses. Bound every wait with a timeout.\n\nMaintained by @jeikl\n</RULE[e:\code\Antigravity-Manager\AGENTS.md]>\n\n\nYou can use specialized 'skills' to help you with complex tasks. Each skill has a name and a description listed below.\n\nSkills are folders of instructions, scripts, and resources that extend your capabilities for specialized tasks. Each skill folder contains:\n- SKILL.md (required): The main instruction file with YAML frontmatter (name, description) and detailed markdown instructions\n\nMore complex skills may include additional directories and files as needed, for example:\n- scripts/ - Helper scripts and utilities that extend your capabilities\n- examples/ - Reference implementations and usage patterns\n- resources/ - Additional files, templates, or assets the skill may reference\n- references/ - Contains additional documentation that agents can read when needed\n\n\nIf a skill seems relevant to your current task, you MUST read its SKILL.md instructions using view_file before proceeding. You may skip this step only if you are delegating the skill-related task to a subagent that will read and follow the instructions itself.\n\nWhen calling view_file on these skill paths, always use the exact path provided in the "Available skills" list below.\n\nAvailable skills:\n- agy-customizations (C:\Users\Administrator\.gemini\antigravity\builtin\skills\agy-customizations\SKILL.md): Comprehensive guide and reference for the Antigravity Customization System. Use to explain how customizations work, their loading priority, discovery mechanisms, and to guide the creation of skills, rules, plugins, hooks, and MCP servers.\n- antigravity-guide (C:\Users\Administrator\.gemini\antigravity\builtin\skills\antigravity_guide\SKILL.md): Provides a comprehensive guide, quick reference, and sitemap for Google Antigravity (AGY), including the Antigravity CLI (agy), Antigravity 2.0, Antigravity IDE, Python SDK, slash commands, keybindings, and customizations (skills, rules, MCP, sidecars). Activate this skill when the user asks questions about how to use, configure, or customize Antigravity, AGY, the agy CLI, the Antigravity IDE, or Antigravity 2.0.\n- generative_ui (C:\Users\Administrator\.gemini\antigravity\builtin\skills\generative_ui\SKILL.md): How to render rich interactive HTML widgets inline in the chat or as standalone artifacts. Use this skill when you want to show the user diagrams, data visualizations, interactive controls, educational walkthroughs, or any rich visual content beyond plain text and markdown.\n- migrate-workflows (C:\Users\Administrator\.gemini\antigravity\builtin\skills\migrate-workflows\SKILL.md): Automatically migrate legacy workflows to modern skills across global and workspace configurations. Scans for existing workflows, creates target SKILL.md files, and safely archives old workflow files.\n\n\n\n\n## Invoking Subagents\n\nSubagents can be invoked using the invoke_subagent tool. You can invoke an existing subagent by name, or define a new subagent for this conversation using the define_subagent tool, and then invoke it. Agents defined by the define_subagent tool are available for the duration of this conversation. After launching a subagent, you do NOT need to poll or check your inbox in a loop. The system will automatically notify you when the subagent sends a message. Simply proceed with other work or stop calling tools, and you will be notified when there is a message to process.\n\n## Communicating with Another Agent\n\nUse the send_message tool to send a message to another agent by its conversation ID (returned by invoke_subagent). This tool is ONLY for communicating with other agents.\n\nDo NOT use send_message to communicate with the user. Instead, output visible text to communicate with the user.\n\nAvailable subagents:\n- self: Subagent that inherits the parent agent's full configuration including tools, system prompt, and model. Use this when you need to run a task in a separate conversation context but with the same capabilities as the current agent.\n- research: Research subagent with read-only tools for exploring the codebase, searching the web, and reading files. Delegate to this agent when you need to run a task in a separate conversation context but with the same capabilities as the current agent, when a research task requires many search and file-reading steps that would clutter your context, or when you need a broad survey of the codebase or documentation. Prefer doing research yourself for quick, targeted lookups.\n\nAfter launching a subagent, you do NOT need to poll or check your inbox in a loop. The system will automatically notify you when the subagent sends a message. Simply proceed with other work or stop calling tools, and you will be notified when there is a message to process.\n\n\n\nYou are connected to a messaging system where you may receive messages from: agents, background tasks, user-queued messages.\n\n## Receiving Messages\n\nYou receive messages automatically at the start of each invocation. All messages are delivered in full directly into your context — no manual retrieval is needed.\n\n## Reactive Wakeup (No Polling Needed)\n\nThe system automatically resumes your execution when:\n- A message arrives from a subagent or peer agent\n- A background task completes or sends you a notification\n- A user-queued message is ready to be dequeued\n\nThis means you do NOT need to poll in a loop while waiting for messages or updates. After launching anything that performs work asynchronously, you may continue other work or simply stop by calling no more tools. The system will notify you when there is something to process.\n\n\nTranscripts are located directly at <appDataDir>\\brain\\<conversation-id>\\.system_generated\\logs/transcript.jsonl (and transcript_full.jsonl).\n- Start with transcript.jsonl (compact). When truncated_fields is present, read only the specific corresponding line in transcript_full.jsonl.\n- Search subagents by grepping invoke_subagent in transcript.jsonl.\n- Link conversations using [<label>](conversation://<conversation-id>).\n\n# File Format\nTranscripts are in JSON Lines (JSONL) format. Each line is a single JSON object representing one "step" or action in the conversation.\nEach JSON object contains fields such as:\n- step_index: The index of the step in the trajectory.\n- source: The source of the action (e.g., USER_EXPLICIT, MODEL, SYSTEM).\n- type: The type of the step. Particular steps of interest are USER_INPUT, which represents a user's prompt, and PLANNER_RESPONSE, which represents the agent's response and tool calls.\n- status: The status of the step (e.g., DONE, ERROR).\n- created_at: The ISO 8601 timestamp of when the step occurred.\n- content: The text content of the step (e.g., the user's request, the model's response, or tool responses).\n- thinking: The model's internal reasoning / chain-of-thought (for PLANNER_RESPONSE steps).\n- tool_calls: An array of tool calls made in this step, including their arguments.\n- media: An array of media (e.g. images) attached to the step, each with a mime_type and a uri referencing the media file on disk. Media bytes are not stored in the transcript; view the uri path to see the media.\n- truncated_fields: An array of field names that were truncated (e.g., [\"content\"], [\"thinking\"], [\"tool_calls\"]). Only present in transcript.jsonl when truncation occurred (never in transcript_full.jsonl). When present, read the corresponding line in transcript_full.jsonl for the complete content.\n\n\n\nArtifacts are special markdown (.md) documents that you can create to present structured information to the user.\nAll artifacts should be written to the artifact directory: <appDataDir>\\brain\\<conversation-id>. You do NOT need to create this directory yourself, it will be created automatically when you create artifacts.\n\n# When to Use Artifacts\n\nUse artifacts for:\n- Extensive reports and analysis summaries\n- Persistent information you'll update over time (task lists, experiment logs)\n- Code changes formatted as diffs\n\nDon't use artifacts for:\n- Simple one-off answers or very short paragraph content - just respond directly\n- Asking questions or requesting user input - just ask directly\n\nAfter creating or updating an artifact, DO NOT re-summarize the artifact contents in your response to the user. Instead, point the user to the artifact and highlight only key open questions or decisions that need their input.\n\n\n# Artifact Formatting Tips\nWhen creating markdown artifacts, use standard markdown and GitHub Flavored Markdown formatting.\n\n## Alerts\nUse GitHub-style alerts strategically to emphasize critical information. Do not place consecutively or nest:\n > [!NOTE] Background context, implementation details, or explanations\n > [!TIP] Performance optimizations, best practices, or efficiency suggestions\n > [!IMPORTANT] Essential requirements, critical steps, or must-know information\n > [!WARNING] Breaking changes, compatibility issues, or potential problems\n > [!CAUTION] High-risk actions that could cause data loss or security vulnerabilities\n\n\n## Mermaid Diagrams\nCreate mermaid diagrams using fenced code blocks with language mermaid to visualize relationships, workflows, and architectures.\n- Only use supported diagram types:\n - Flowcharts / Graphs: flowchart TD / flowchart LR / graph TD / graph LR\n - Sequence Diagrams: sequenceDiagram\n - State Diagrams: stateDiagram-v2 or stateDiagram\n - Class Diagrams: classDiagram\n - Entity-Relationship Diagrams: erDiagram\n - XY Charts: xychart-beta\n- All other diagram types are unsupported. For schedules, timelines, or roadmaps, use directed flowcharts (flowchart LR / flowchart TD) or Markdown tables instead.\n- To prevent syntax errors:\n - Quote node labels containing special characters like parentheses or brackets. For example, id[\"Label (Extra Info)\"] instead of id[Label (Extra Info)].\n - Avoid HTML tags in labels.\n\n## File Links\n- Link to line ranges using link text format.\n- IMPORTANT: If you are embedding a file in an artifact and the file is NOT already in \brain\, you MUST first copy the file to the artifacts directory before embedding it. Only embed files that are located in the artifacts directory. Always use its absolute path .\n- **Use basenames for readability**: Use file basenames for the link text instead of the full path\n\n## Carousels\nUse ````carousel syntax with HTML comments to display related markdown snippets sequentially (before/after comparisons, UI progressions, alternative approaches, walkthroughs). Four backticks enable nesting code blocks within slides.\n\nExample:\ncarousel\n\n\n```python\ndef example():\n print(\"Code in carousel\")\n```\n\n\n## LaTeX / Math\nArtifacts render LaTeX math with KaTeX.\n- Literal $: write \\$, or wrap it in backticks. Two unescaped $ in the same paragraph become math.\n\n# Scratch Scripts and Files\n\nYou may find it useful to create scratch scripts or files for temporary purposes.\n\nExamples:\n- One-off scripts to debug code\n- Temporary data files for testing\n\nStore these files in the <appDataDir>\\brain\\<conversation-id>/scratch/ directory. They will be persisted.\n\n\nArtifact Directory Path: C:\Users\Administrator\.gemini\antigravity\brain\0b7ddb01-c629-42a2-9793-427bb768a5cc\n\n\n\nSlash commands are user-facing shortcuts in the chat UI (e.g., typing /goal or /schedule) that automate complex workflows or trigger specialized agent behaviors.\n\nYou cannot execute these commands yourself. Your role is to recommend them to the user when they are a good fit for the task at hand, encouraging the user to explore and trigger them.\n\nTo recommend a slash command, suggest it clearly in your response (e.g., "You can use the /goal command to...").\n\n\nAvailable slash commands you can recommend to the user:\n- /goal: Recommend this when the user wants to run a long-running task (e.g., overnight) and wants the agent to be extra thorough and not stop until the goal is fully achieved.\n- /schedule: Recommend this when the user wants to run an instruction on a recurring schedule or set a one-time timer.\n- /browser: Recommend this when the user's task involves web browsing, searching the web, or interacting with web applications.\n- /plan: Recommend this when the task is complex and requires careful step-by-step planning before execution.\n- /grill-me: Recommend this when the user wants to align on a plan through an interactive interview to resolve design decisions.\n- /teamwork-preview: Recommend this when the user has a large project that would benefit from a team of autonomous agents working together.\n- /learn: Recommend this when the user has corrected the agent or solved a complex setup and wants the agent to persist this behavior for future tasks.\n- /boost: Recommend this when the user has a complex coding or research project that requires deep thinking, strategic planning, multiple perspectives, and rigorous verification.\n\n\n\n\nFollow these behavioral guidelines at all times:\n- Maintain documentation integrity. Preserve all existing comments and docstrings that are unrelated to your code changes, unless the user specifies otherwise.\n\n\n\n- Keep your responses concise.\n- Format your responses in github-style markdown.\n- You can render LaTeX math (KaTeX): inline with \\(...\\) or $...$, display with \\[...\\] or $$...$$ placed on its own line.\n- Use math only for genuine mathematical content. Use backticks for code, identifiers, paths, flags, and shell variables.\n- $ opens inline math, so write a literal dollar as \\$ or wrap it in backticks. Two unescaped $ in the same paragraph turn everything between them into math — this bites prices (\\$100) and shell syntax written in prose ($HOME, awk $1).\n\n- If you're unsure about the user's intent, ask for clarification rather than making assumptions.\n- You MUST create clickable links for all files and code symbols (classes, types, functions, structs). Use github style markdown links with the file:// scheme (e.g., utils.py or ClassName). For Windows, use forward slashes for paths.\n"
}
]
},
"tools": [
{
"functionDeclarations": [
{
"name": "view_file",
"description": "View the contents of a file from the local filesystem. This tool supports text files and following binary files: image, pdf, video, audio.\nText file usage:\n- The lines of the file are 1-indexed\n- You can view at most 800 lines at a time\n- Specify StartLine and EndLine to view the lines of the file using slice notation:\n - Omit both to view the entire file, or the first 800 lines of the file, whichever is smaller.\n - Specify StartLine only to view the remaining lines of the file, or the next 800 lines, whichever is smaller\n - Specify EndLine only to view the remaining preceding lines of the file, or the previous 800 lines, whichever is smaller\n - Specify both to view a precise line range. This range must be smaller than 800 lines or only the first 800 lines of the range will be shown.\n- Content is limited to 46080 bytes per view. If content is truncated, use the ContentOffset parameter to view the remaining content\nBinary file usage:\n- Do not provide StartLine or EndLine arguments, this tool always returns the entire file\n- Files larger than 100 MB cannot be viewed.",
"parameters": {
"type": "OBJECT",
"properties": {
"AbsolutePath": {
"type": "STRING",
"description": "Path to file to view. Must be an absolute path."
},
"ContentOffset": {
"type": "INTEGER",
"description": "Optional. Byte offset into the content. Use this to view content beyond the initial byte limit when the tool output indicates content was truncated."
},
"EndLine": {
"type": "INTEGER",
"description": "Optional. Endline to view, 1-indexed, inclusive. When specified, this value must be greater than or equal to StartLine."
},
"StartLine": {
"type": "INTEGER",
"description": "Optional. Startline to view, 1-indexed, inclusive. When specified, this value must be less than or equal to EndLine."
},
"toolAction": {
"type": "STRING",
"description": "Brief 2-5 word phrase in -ing form describing the specific action. Capitalize like a sentence. Some examples: 'Analyzing directory', 'Searching the web', 'Checking git status', 'Running tests', 'Searching code'."
},
"toolSummary": {
"type": "STRING",
"description": "Brief 2-5 word noun phrase describing the specific task. Capitalize like a sentence. Some examples: 'Directory analysis', 'Web search', 'Git status check', 'Test execution', 'Code search'."
}
},
"required": [
"AbsolutePath",
"toolSummary",
"toolAction"
]
}
}
]
},
{
"functionDeclarations": [
{
"name": "run_command",
"description": "PROPOSE a command to run on behalf of the user. Operating System: windows. Shell: powershell.\nNEVER PROPOSE A cd COMMAND.\nIf you have this tool, note that you DO have the ability to run commands directly on the USER's system.\nMake sure to specify CommandLine exactly as it should be run in the shell.\nIf the step doesn't return the command output, it means that the command was sent to the background as a task. You will receive messages with the command's output as it runs. To interact with a running command, use the manage_task tool. Use send_input to send stdin, kill to terminate the command, and status to check current status. IMPORTANT: Do NOT poll or loop on status to wait for completion. The system will automatically notify you with a message when the command finishes. Simply proceed with other work or stop calling tools after launching a command.\nCommands will be run with PAGER=cat. You may want to limit the length of output for commands that usually rely on paging and may contain very long output (e.g. git log, use git log -n ).",
"parameters": {
"type": "OBJECT",
"properties": {
"CommandLine": {
"type": "STRING",
"description": "The exact command line string to execute."
},
"Cwd": {
"type": "STRING",
"description": "The current working directory for the command"
},
"IsDaemon": {
"type": "BOOLEAN",
"description": "Set to true for long-running support processes that are meant to keep running in the background indefinitely and are not expected to finish on their own (e.g., dev servers, file watchers, tunnels). Leave false (the default) for normal commands that are expected to terminate."
},
"WaitMsBeforeAsync": {
"type": "INTEGER",
"description": "This specifies the number of milliseconds to wait after starting the command before sending it to the background. If you want the command to complete execution synchronously, set this to a large enough value that you expect the command to complete in that time under ordinary circumstances. If you're starting an interactive or long-running command, set it to a large enough value that it would cause possible failure cases to execute synchronously (e.g. 500ms). Keep the value as small as possible, with a maximum of 10000ms."
},
"toolAction": {
"type": "STRING",
"description": "Brief 2-5 word phrase in -ing form describing the specific action. Capitalize like a sentence. Some examples: 'Analyzing directory', 'Searching the web', 'Checking git status', 'Running tests', 'Searching code'."
},
"toolSummary": {
"type": "STRING",
"description": "Brief 2-5 word noun phrase describing the specific task. Capitalize like a sentence. Some examples: 'Directory analysis', 'Web search', 'Git status check', 'Test execution', 'Code search'."
}
},
"required": [
"Cwd",
"WaitMsBeforeAsync",
"CommandLine",
"toolSummary",
"toolAction"
]
}
}
]
},
{
"functionDeclarations": [
{
"name": "manage_task",
"description": "Manage background tasks. Use this tool to list running tasks or interact with tasks that were sent to the background.\n\nActions:\n- 'list': List all currently running background tasks\n- 'kill': Cancel the task's execution\n- 'status': Check the task's current status and log file location\n- 'send_input': Send input to a running task\n\nWhen mentioning tasks to the user, avoid using full task IDs and start timestamps; keep them human-readable.",
"parameters": {
"type": "OBJECT",
"properties": {
"Action": {
"type": "STRING",
"description": "The action to perform: 'list' (list all running tasks), 'kill' (cancel the task), 'status' (check the task status and log URI), 'send_input' (send input to a running task).",
"enum": [
"list",
"kill",
"status",
"send_input"
]
},
"Input": {
"type": "STRING",
"description": "The input to send to the task. Required when Action is 'send_input'."
},
"TaskId": {
"type": "STRING",
"description": "The task ID to manage. Required when Action is 'kill', 'status', or 'send_input'."
},
"toolAction": {
"type": "STRING",
"description": "Brief 2-5 word phrase in -ing form describing the specific action. Capitalize like a sentence. Some examples: 'Analyzing directory', 'Searching the web', 'Checking git status', 'Running tests', 'Searching code'."
},
"toolSummary": {
"type": "STRING",
"description": "Brief 2-5 word noun phrase describing the specific task. Capitalize like a sentence. Some examples: 'Directory analysis', 'Web search', 'Git status check', 'Test execution', 'Code search'."
}
},
"required": [
"Action",
"toolSummary",
"toolAction"
]
}
}
]
},
{
"functionDeclarations": [
{
"name": "send_message",
"description": "Send a message to another agent. This tool can be used to communicate with subagents, peer agents, etc. Do not use this tool to communicate with the user.",
"parameters": {
"type": "OBJECT",
"properties": {
"Message": {
"type": "STRING",
"description": "The message content."
},
"Recipient": {
"type": "STRING",
"description": "The recipient ID to send the message to, e.g. a subagent conversation ID."
},
"toolAction": {
"type": "STRING",
"description": "Brief 2-5 word phrase in -ing form describing the specific action. Capitalize like a sentence. Some examples: 'Analyzing directory', 'Searching the web', 'Checking git status', 'Running tests', 'Searching code'."
},
"toolSummary": {
"type": "STRING",
"description": "Brief 2-5 word noun phrase describing the specific task. Capitalize like a sentence. Some examples: 'Directory analysis', 'Web search', 'Git status check', 'Test execution', 'Code search'."
}
},
"required": [
"Recipient",
"Message",
"toolSummary",
"toolAction"
]
}
}
]
},
{
"functionDeclarations": [
{
"name": "schedule",
"description": "Schedule a one-shot timer or a recurring cron job that sends notifications in the background.\n\nNOTE: This tool call returns immediately and does not pause execution. To wait for the timer to fire, you must stop calling tools to end your turn.\n\nModes:\n1. One-shot timer: Set a timer for a specified duration that will notify you with your Prompt when it expires. You can control early termination behavior using TimerCondition:\n\n- 'never' (default): The timer will always fire after the specified duration, unless explicitly cancelled.\nUsage: Use when setting unconditional timers that should always fire after DurationSeconds, unless explicitly cancelled.\n- 'any': The timer will be cancelled early if ANY message from any sender is received before the duration.\nUsage: Useful when multiple background tasks are running and you want to wait for any update, but with some guarantee that you won't be idle forever in case they are all stuck.\n- : The timer will be cancelled early if a message is received from that specific sender ID.\nUsage: Use when you're waiting for an update from a specific subagent or task, but want to set some limit on how long to wait.\n\nNOTE: When a timer is cancelled early, no separate cancellation notification is sent — the message that satisfied the condition is itself your wakeup, and the timer's tool step result records the cancellation.\n\nNOTE: You cannot have multiple concurrently active timers that would early terminate on the same sender ID.\nFor example, if you already have a liveness timer set with "any", you cannot set another timer with "any" or any other condition.\nIf you already have a timer set with early termination on "task-123", you cannot set another timer with "task-123" or "any".\nYou should rely on the existing timer, or cancel and replace it if needed.\n\nExamples:\n\nScenario: User asks explicitly for a reminder in 10 minutes.\nArgs: DurationSeconds=600, Prompt="Remind the user", TimerCondition="never"\nComments: TimerCondition="never" is appropriate since this timer is unrelated to other ongoing tasks.\n\nScenario: You just ran a command as "task-123". You already set a notification on it for 5 minutes, and it just notified you that it's still running. After checking the output, you want to set a new reminder to check on it in 10 minutes if it still hasn't finished.\nArgs: DurationSeconds=600, Prompt="Check on the command status", TimerCondition="task-123"\nComments: TimerCondition="task-123" is appropriate since the timer is not needed if the command finishes ahead of time.\n\nScenario: You just spawned 10 subagents, and you want to check in on progress after 5 minutes if you haven't heard back from any of them.\nArgs: DurationSeconds=300, Prompt="Check in on the subagents' progress", TimerCondition="any"\nComments: TimerCondition="any" is appropriate since you are not waiting for any specific subagent.\n\nScenario: You are running a command that you're sure will terminate, and you want to wait for it to finish.\nArgs: N/A\nComments: A timer is not needed at all in this scenario and will wastefully generate extra messages. Stop calling tools to end your turn instead.\n\n2. Recurring cron: Set CronExpression to a standard 5-field cron expression (e.g., '/5 * * * ' for every 5 minutes). Each time the cron triggers, a notification with your Prompt is sent. The cron runs as a background task. Optionally set MaxIterations to limit the number of triggers. Optionally set IsDaemon to declare how the cron relates to your current task: leave it false (the default) when the cron is how your current task makes progress — polling or monitoring a job until it completes, heartbeat/liveness, or reminders — so your task stays active until the cron ends; set it true only when the cron is an independent standing job that should keep running after your current task is done — e.g. a recurring report or a maintenance job the user asked you to keep going — so you can finish now while it keeps firing in the background.\n\nExamples:\n- Poll deployment status every 5 minutes until it passes: CronExpression="/5 * * * ", Prompt="Check deployment status and report progress", IsDaemon=false\n- Run a health check every hour, up to 3 times: CronExpression="0 * * * ", MaxIterations=3, Prompt="Run the health check script and report results", IsDaemon=false\n- Inspect newly filed issues in the last 24h and post a daily summary report: CronExpression="0 9 * * ", Prompt="Summarize issues filed in the last 24h and post the report", IsDaemon=true\n\nGeneral Reminders:\n- You must specify exactly one of DurationSeconds or CronExpression.\n- Always provide a Prompt describing what the notification should say.\n- Never run a background 'sleep' command to set a timer, use this tool instead.\n- To cancel a running timer or cron schedule, use the manage_task tool with the task ID returned by this tool.",
"parameters": {
"type": "OBJECT",
"properties": {
"CronExpression": {
"type": "STRING",
"description": "A standard cron expression (5 fields: minute hour day-of-month month day-of-week). Use for recurring schedules. Mutually exclusive with DurationSeconds. Example: '
/5 * * * ' for every 5 minutes."
},
"DurationSeconds": {
"type": "INTEGER",
"description": "The number of seconds to wait. Use for one-shot timers. Mutually exclusive with CronExpression."
},
"IsDaemon": {
"type": "BOOLEAN",
"description": "Optional. Set to true only when the cron is an independent, standing job that should keep firing even after your current task is done (e.g. a recurring daily/weekly report or a standing maintenance job). Leave false (the default) whenever the cron is part of finishing your current task — including polling or monitoring a running job until it completes, heartbeat/liveness, or reminders."
},
"MaxIterations": {
"type": "INTEGER",
"description": "Optional. Maximum number of times the cron schedule will fire before stopping. Only applicable when CronExpression is set. Defaults to unlimited."
},
"Prompt": {
"type": "STRING",
"description": "The message content to include in the notification when the timer fires or cron triggers. This is sent to the agent as a high-priority message."
},
"TimerCondition": {
"type": "STRING",
"description": "Optional. Controls when a one-shot timer should early terminate upon receiving a message. Options: 'never' (default, timer unconditionally waits until expiry), 'any' (timer cancels if any message is received), or a specific sender ID (timer cancels only if a message is received from that specific subagent conversation ID or background task ID). Only applicable when DurationSeconds is set."
},
"toolAction": {
"type": "STRING",
"description": "Brief 2-5 word phrase in -ing form describing the specific action. Capitalize like a sentence. Some examples: 'Analyzing directory', 'Searching the web', 'Checking git status', 'Running tests', 'Searching code'."
},
"toolSummary": {
"type": "STRING",
"description": "Brief 2-5 word noun phrase describing the specific task. Capitalize like a sentence. Some examples: 'Directory analysis', 'Web search', 'Git status check', 'Test execution', 'Code search'."
}
},
"required": [
"Prompt",
"toolSummary",
"toolAction"
]
}
}
]
},
{
"functionDeclarations": [
{
"name": "invoke_subagent",
"description": "Invokes one or more subagents by name with a single tool call. Each subagent runs in the background with its own prompt and reports back when done.\n\nSpecify the Subagents array with one or more entries. Each entry defines a subagent to launch.\n\nCommunicate with subagents using the send_message tool. Examples of when to do this:\n
To check on the status of a subagent.\n
To send a running subagent further instructions.\n
To send an idle subagent new instructions.\n\nGuidelines:\n
Each invoked subagent will be uniquely identified by its conversationID.\n* Multiple subagents with the same type name can be invoked, with each subagent receiving a unique conversationID.\n* If a task is a natural continuation of an existing subagent's work, send a message to that subagent with the task rather than invoking a new subagent to conserve resources.\n* When selecting the Model argument, default to 'inherit' unless the user explicitly requests a different model.",
"parameters": {
"type": "OBJECT",
"properties": {
"Subagents": {
"type": "ARRAY",
"description": "Array of subagents to invoke. Each entry specifies a separate subagent to launch concurrently.",
"items": {
"type": "OBJECT",
"properties": {
"Model": {
"type": "STRING",
"description": "Model to use for the subagent. 'inherit' (default) uses the calling agent's model. 'flash_lite' uses a very light model. 'flash' uses a smaller, faster model suited for simple tasks like research lookups, file reading, or quick searches. 'pro' uses a larger, more capable model suited for complex tasks requiring deep reasoning, large refactors, or multi-step planning.",
"enum": [
"inherit",
"flash_lite",
"flash",
"pro"
]
},
"Prompt": {
"type": "STRING",
"description": "A clear, actionable task description for the subagent. Be specific about what the subagent should do and what information it should return."
},
"Role": {
"type": "STRING",
"description": "A 2-5 word description of the subagent's role. Should read similar to a job title, e.g. 'Codebase Researcher', 'Database Debugger', etc. Should also be detailed enough to distinguish between different subagents who might share similar purposes."
},
"TypeName": {
"type": "STRING",
"description": "Type name of the subagent to invoke."
},
"Workspace": {
"type": "STRING",
"description": "Workspace mode for the subagent. 'inherit' (default) uses the same workspace as the parent. 'branch' creates a new isolated workspace branched or cloned from the parent. 'share' creates a new workspace sharing the parent's underlying repository directory (similar to a git worktree or Mercurial 'hg share'), allowing independent branching without duplicating storage. If omitted, defaults to 'inherit'."
}
},
"required": [
"TypeName",
"Role",
"Prompt"
]
}
},
"toolAction": {
"type": "STRING",
"description": "Brief 2-5 word phrase in -ing form describing the specific action. Capitalize like a sentence. Some examples: 'Analyzing directory', 'Searching the web', 'Checking git status', 'Running tests', 'Searching code'."
},
"toolSummary": {
"type": "STRING",
"description": "Brief 2-5 word noun phrase describing the specific task. Capitalize like a sentence. Some examples: 'Directory analysis', 'Web search', 'Git status check', 'Test execution', 'Code search'."
}
},
"required": [
"Subagents",
"toolSummary",
"toolAction"
]
}
}
]
},
{
"functionDeclarations": [
{
"name": "define_subagent",
"description": "Defines a new type of subagent that can be invoked via invoke_subagent.\n\n\tGuidelines:\n\t* Use this tool if you need a specialized subagent for a task and none of the existing subagents are suitable.\n\t* Once the subagent is defined, it can be invoked repeatedly using invoke_subagent without calling this tool again.\n\t* The subagent will be defined with the specified name, description, system prompt, and tool groups.\n\t* By default, all subagents have read tools to research the codebase, and tools to communicate with other agents.\n\t",
"parameters": {
"type": "OBJECT",
"properties": {
"description": {
"type": "STRING",
"description": "Human-readable description of what this subagent does and when it should be used."
},
"enable_mcp_tools": {
"type": "BOOLEAN",
"description": "Set true to enable the subagent to call MCP tools."
},
"enable_subagent_tools": {
"type": "BOOLEAN",
"description": "Set true to equip the subagent with tools to define and invoke its own subagents"
},
"enable_write_tools": {
"type": "BOOLEAN",
"description": "Set true to equip the subagent with tools to create and edit files, and run commands."
},
"name": {
"type": "STRING",
"description": "Unique name for the subagent. Used to invoke it via invoke_subagent. Must start with a letter or digit and contain only letters, digits, '_', '-', and '.'."
},
"system_prompt": {
"type": "STRING",
"description": "A detailed system prompt for this subagent."
},
"toolAction": {
"type": "STRING",
"description": "Brief 2-5 word phrase in -ing form describing the specific action. Capitalize like a sentence. Some examples: 'Analyzing directory', 'Searching the web', 'Checking git status', 'Running tests', 'Searching code'."
},
"toolSummary": {
"type": "STRING",
"description": "Brief 2-5 word noun phrase describing the specific task. Capitalize like a sentence. Some examples: 'Directory analysis', 'Web search', 'Git status check', 'Test execution', 'Code search'."
}
},
"required": [
"name",
"description",
"system_prompt",
"toolSummary",
"toolAction"
]
}
}
]
},
{
"functionDeclarations": [
{
"name": "manage_subagents",
"description": "Manage existing subagents.\n\tActions:\n\t* 'list': List active direct subagents with their conversation IDs and live state. Each subagent is reported as a JSON object with this schema:\n\t{"$schema":"https://json-schema.org/draft/2020-12/schema\",\"properties\":{\"role\":{\"type\":\"string\",\"description\":\"The subagent's role, or its type name if no role was set."},"type":{"type":"string","description":"The subagent's type name."},"conversationId":{"type":"string","description":"The subagent's conversation ID, used to message or kill it."},"transcript":{"type":"string","description":"Absolute URI of the subagent's transcript log, if available."},"state":{"type":"string","enum":["running","idle","waiting_for_input","waiting_for_dependents","waiting_for_message","canceling","errored","unspecified"],"description":"The subagent's current lifecycle state."},"stateDetail":{"type":"string","description":"Context for State: the current tool call (name and summary) when running or waiting_for_input, or the failure message when errored."}},"additionalProperties":false,"type":"object"}\n\t* 'kill': Terminate specific subagents and all their descendants.\n\t* 'kill_all': Terminate all subagents and all their descendants.\n\n\tWhen a subagent is killed, its branched workspaces will be deleted, but its logs and artifacts will be preserved.",
"parameters": {
"type": "OBJECT",
"properties": {
"Action": {
"type": "STRING",
"description": "The action to perform. Must be 'list' (list active direct subagents with their live state), 'kill' (terminate specific subagents and all their descendants), or 'kill_all' (terminate all subagents and all their descendants).",
"enum": [
"list",
"kill",
"kill_all"
]
},
"ConversationIds": {
"type": "ARRAY",
"description": "The IDs of the subagents to kill. Required for 'kill'.",
"items": {
"type": "STRING"
}
},
"toolAction": {
"type": "STRING",
"description": "Brief 2-5 word phrase in -ing form describing the specific action. Capitalize like a sentence. Some examples: 'Analyzing directory', 'Searching the web', 'Checking git status', 'Running tests', 'Searching code'."
},
"toolSummary": {
"type": "STRING",
"description": "Brief 2-5 word noun phrase describing the specific task. Capitalize like a sentence. Some examples: 'Directory analysis', 'Web search', 'Git status check', 'Test execution', 'Code search'."
}
},
"required": [
"Action",
"toolSummary",
"toolAction"
]
}
}
]
},
{
"functionDeclarations": [
{
"name": "write_to_file",
"description": "Use this tool to create new files. The file and any parent directories will be created for you if they do not already exist.\n\t\tFollow these instructions:\n\t\t1. By default this tool will error if TargetFile already exists. To overwrite an existing file, set Overwrite to true.\n\t\t2. When creating an artifact, always provide ArtifactMetadata. When creating non-artifact files, do not provide it.",
"parameters": {
"type": "OBJECT",
"properties": {
"ArtifactMetadata": {
"type": "OBJECT",
"description": "Metadata that defines artifact properties. ONLY provide when creating an artifact file in the artifact directory. Omit this field when creating non-artifact files.",
"properties": {
"RequestFeedback": {
"type": "BOOLEAN",
"description": "Set to true if you'd like to request user feedback on this artifact and if the contents of this artifact are executable (e.g., a plan). The user will be provided with a 'Proceed' button to execute it."
},
"Summary": {
"type": "STRING",
"description": "Detailed multi-line summary of the artifact file, after edits have been made. Summary does not need to mention the artifact name and should focus on the contents and purpose of the artifact."
},
"UserFacing": {
"type": "BOOLEAN",
"description": "Set to true if this artifact should be presented to the user. Set to false for scratch scripts, temporary data files, or files that the user does not need to see"
}
},
"required": [
"Summary",
"UserFacing",
"RequestFeedback"
]
},
"CodeContent": {
"type": "STRING",
"description": "The code contents to write to the file."
},
"Description": {
"type": "STRING",
"description": "Brief, user-facing explanation of what this change did. Focus on non-obvious rationale, design decisions, or important context. Don't just restate what the code does."
},
"Overwrite": {
"type": "BOOLEAN",
"description": "Set this to true to overwrite an existing file. WARNING: This will replace the entire file contents. Only use when you explicitly intend to overwrite. Otherwise, use a code edit tool to modify existing files."
},
"TargetFile": {
"type": "STRING",
"description": "The target file to create and write code to. Must be an absolute path."
},
"toolAction": {
"type": "STRING",
"description": "Brief 2-5 word phrase in -ing form describing the specific action. Capitalize like a sentence. Some examples: 'Analyzing directory', 'Searching the web', 'Checking git status', 'Running tests', 'Searching code'."
},
"toolSummary": {
"type": "STRING",
"description": "Brief 2-5 word noun phrase describing the specific task. Capitalize like a sentence. Some examples: 'Directory analysis', 'Web search', 'Git status check', 'Test execution', 'Code search'."
}
},
"required": [
"TargetFile",
"Overwrite",
"CodeContent",
"Description",
"toolSummary",
"toolAction"
]
}
}
]
},
{
"functionDeclarations": [
{
"name": "replace_file_content",
"description": "Use this tool to edit an existing file. Follow these rules:\n1. Use this tool ONLY when you are making a SINGLE CONTIGUOUS block of edits to the same file (i.e. replacing a single contiguous block of text).\n2. Do NOT make multiple parallel calls to this tool for the same file.\n3. To edit multiple, non-adjacent lines of code in the same file, make multiple calls to this tool.\n4. For the ReplacementChunk, specify StartLine, EndLine, TargetContent and ReplacementContent. StartLine and EndLine should specify a range of lines containing precisely the instances of TargetContent that you wish to edit. To edit a single instance of the TargetContent, the range should be such that it contains that specific instance of the TargetContent and no other instances. In TargetContent, specify the precise lines of code to edit. These lines MUST EXACTLY MATCH text in the existing file content. In ReplacementContent, specify the replacement content for the specified target content. This must be a complete drop-in replacement of the TargetContent, with necessary modifications made.\n5. If you are making multiple edits across a single file, make multiple calls to this tool. DO NOT try to replace the entire existing content with the new content, this is very expensive.\n6. You may not edit file extensions: [.ipynb]",
"parameters": {
"type": "OBJECT",
"properties": {
"AllowMultiple": {
"type": "BOOLEAN",
"description": "If true, multiple occurrences of 'targetContent' will be replaced by 'replacementContent' if they are found. Otherwise if multiple occurrences are found, an error will be returned."
},
"Description": {
"type": "STRING",
"description": "Brief, user-facing explanation of what this change did. Focus on non-obvious rationale, design decisions, or important context. Don't just restate what the code does."
},
"EndLine": {
"type": "INTEGER",
"description": "The ending line number of the chunk (1-indexed). Should be at or after the last line containing the target content. Must satisfy StartLine <= EndLine <= number of lines in the file. The target content is searched for within the [StartLine, EndLine] range."
},
"Instruction": {
"type": "STRING",
"description": "A description of the changes that you are making to the file."
},
"ReplacementContent": {
"type": "STRING",
"description": "The content to replace the target content with."
},
"StartLine": {
"type": "INTEGER",
"description": "The starting line number of the chunk (1-indexed). Should be at or before the first line containing the target content. Must satisfy 1 <= StartLine <= EndLine. The target content is searched for within the [StartLine, EndLine] range."
},
"TargetContent": {
"type": "STRING",
"description": "The exact string to be replaced. This must be the exact character-sequence to be replaced, including whitespace. Be very careful to include any leading whitespace otherwise this will not work at all. This must be a unique substring within the file, or else it will error."
},
"TargetFile": {
"type": "STRING",
"description": "The target file to modify. Must be an absolute path. Always specify the target file as the very first argument."
},
"TargetLintErrorIds": {
"type": "ARRAY",
"description": "If applicable, IDs of lint errors this edit aims to fix (they'll have been given in recent IDE feedback). If you believe the edit could fix lints, do specify lint IDs; if the edit is wholly unrelated, do not. A rule of thumb is, if your edit was influenced by lint feedback, include lint IDs. Exercise honest judgement here.",
"items": {
"type": "STRING"
}
},
"toolAction": {
"type": "STRING",
"description": "Brief 2-5 word phrase in -ing form describing the specific action. Capitalize like a sentence. Some examples: 'Analyzing directory', 'Searching the web', 'Checking git status', 'Running tests', 'Searching code'."
},
"toolSummary": {
"type": "STRING",
"description": "Brief 2-5 word noun phrase describing the specific task. Capitalize like a sentence. Some examples: 'Directory analysis', 'Web search', 'Git status check', 'Test execution', 'Code search'."
}
},
"required": [
"TargetFile",
"Instruction",
"Description",
"AllowMultiple",
"TargetContent",
"ReplacementContent",
"StartLine",
"EndLine",
"toolSummary",
"toolAction"
]
}
}
]
},
{
"functionDeclarations": [
{
"name": "generate_image",
"description": "Generate an image or edit existing images based on a text prompt. The resulting image will be saved as an artifact for use. You can use this tool to generate user interfaces and iterate on a design with the USER for an application or website that you are building. When creating UI designs, generate only the interface itself without surrounding device frames (laptops, phones, tablets, etc.) unless the user explicitly requests them. You can also use this tool to generate assets for use in an application or website.",
"parameters": {
"type": "OBJECT",
"properties": {
"AspectRatio": {
"type": "STRING",
"description": "Optional aspect ratio for the generated image. Supported values: '1:1', '2:3', '3:2', '3:4', '4:3', '9:16', '16:9'. Default is '1:1'."
},
"ImageName": {
"type": "STRING",
"description": "Name of the generated image to save. Should be all lowercase with underscores, describing what the image contains. Maximum 3 words. Example: 'login_page_mockup'"
},
"ImagePaths": {
"type": "ARRAY",
"description": "Optional absolute paths to the images to use in generation. You can pass in images here if you would like to edit, combine, or use as references. You can pass in artifact images and any images in the file system. Note: you cannot pass in more than 3 images.",
"items": {
"type": "STRING"
}
},
"Prompt": {
"type": "STRING",
"description": "The text prompt to generate an image for or the edit instructions."
},
"toolAction": {
"type": "STRING",
"description": "Brief 2-5 word phrase in -ing form describing the specific action. Capitalize like a sentence. Some examples: 'Analyzing directory', 'Searching the web', 'Checking git status', 'Running tests', 'Searching code'."
},
"toolSummary": {
"type": "STRING",
"description": "Brief 2-5 word noun phrase describing the specific task. Capitalize like a sentence. Some examples: 'Directory analysis', 'Web search', 'Git status check', 'Test execution', 'Code search'."
}
},
"required": [
"Prompt",
"ImageName",
"toolSummary",
"toolAction"
]
}
}
]
},
{
"functionDeclarations": [
{
"name": "read_url_content",
"description": "Fetch content from a URL via HTTP request (invisible to USER). Use when: (1) extracting text from public pages, (2) reading static content/documentation, (3) batch processing multiple URLs, (4) speed is important, or (5) no visual interaction needed. Converts HTML to markdown. No JavaScript execution, no authentication. For pages requiring login, JavaScript, or USER visibility, use read_browser_page instead.",
"parameters": {
"type": "OBJECT",
"properties": {
"Url": {
"type": "STRING",
"description": "URL to read content from"
},
"toolAction": {
"type": "STRING",
"description": "Brief 2-5 word phrase in -ing form describing the specific action. Capitalize like a sentence. Some examples: 'Analyzing directory', 'Searching the web', 'Checking git status', 'Running tests', 'Searching code'."
},
"toolSummary": {
"type": "STRING",
"description": "Brief 2-5 word noun phrase describing the specific task. Capitalize like a sentence. Some examples: 'Directory analysis', 'Web search', 'Git status check', 'Test execution', 'Code search'."
}
},
"required": [
"Url",
"toolSummary",
"toolAction"
]
}
}
]
},
{
"functionDeclarations": [
{
"name": "search_web",
"description": "Performs a web search for a given query. Returns a summary of relevant information along with URL citations.",
"parameters": {
"type": "OBJECT",
"properties": {
"domain": {
"type": "STRING",
"description": "Optional domain to recommend the search prioritize"
},
"query": {
"type": "STRING"
},
"toolAction": {
"type": "STRING",
"description": "Brief 2-5 word phrase in -ing form describing the specific action. Capitalize like a sentence. Some examples: 'Analyzing directory', 'Searching the web', 'Checking git status', 'Running tests', 'Searching code'."
},
"toolSummary": {
"type": "STRING",
"description": "Brief 2-5 word noun phrase describing the specific task. Capitalize like a sentence. Some examples: 'Directory analysis', 'Web search', 'Git status check', 'Test execution', 'Code search'."
}
},
"required": [
"query",
"toolSummary",
"toolAction"
]
}
}
]
},
{
"functionDeclarations": [
{
"name": "ask_question",
"description": "Use this tool to ask the user one or more multiple-choice questions, with the goal of:\n- Clarifying underspecified requirements\n- Soliciting design feedback or user preferences\n- Addressing ambiguous user intent\n- Picking a solution from a list of options\n\nWhen called, this tool renders an interactive modal containing the question, selectable options, a default write-in option, and Submit/Skip buttons. Execution is blocked until the user responds.\n\nGuidance:\n- When specifying files in the question, use github markdown links (e.g. filename).\n- Don't use this tool to ask trivial questions that can be answered with a single word (e.g. yes/no); output regular text to ask these questions.\n- Don't include an 'other' option for write-in responses; one is always provided in the UI by default.\n- Don't enumerate the options; they are enumerated by default.\n- Don't include "Select all options that apply", or similar, in the question; the UI already includes this.\n- If you recommend any options, list it first and prefix the option text with "(Recommended)".\n- Format options as the user's direct response instead of describing your own actions.\n- Set 'IsMultiSelect' to true to allow the user to select multiple options with checkboxes.\n",
"parameters": {
"type": "OBJECT",
"properties": {
"questions": {
"type": "ARRAY",
"description": "The list of questions to ask.",
"items": {
"type": "OBJECT",
"properties": {
"is_multi_select": {
"type": "BOOLEAN",
"description": "If true, the user can select multiple options."
},
"options": {
"type": "ARRAY",
"description": "The text for each option, formatted as the user's response. Must have at least 2 options. Do NOT add an 'Other' option to questions.",
"items": {
"type": "STRING"
}
},
"question": {
"type": "STRING",
"description": "The question to ask the user. Do NOT add 'select all that apply' or similar text to the question title."
}
}
}
},
"toolAction": {
"type": "STRING",
"description": "Brief 2-5 word phrase in -ing form describing the specific action. Capitalize like a sentence. Some examples: 'Analyzing directory', 'Searching the web', 'Checking git status', 'Running tests', 'Searching code'."
},
"toolSummary": {
"type": "STRING",
"description": "Brief 2-5 word noun phrase describing the specific task. Capitalize like a sentence. Some examples: 'Directory analysis', 'Web search', 'Git status check', 'Test execution', 'Code search'."
}
},
"required": [
"toolSummary",
"toolAction"
]
}
}
]
}
],
"labels": {
"last_execution_id": "5a52db7a-f5a4-4add-9ca4-3c9a10dac04d",
"last_step_index": "12",
"model_enum": "MODEL_PLACEHOLDER_M318",
"request_id": "b81116f4-9f7b-4dfb-bb87-9c5c1875c2bf-5",
"trajectory_id": "b81116f4-9f7b-4dfb-bb87-9c5c1875c2bf",
"used_claude": "false",
"used_claude_conservative": "false",
"used_non_gemini_model": "false"
},
"generationConfig": {
"maxOutputTokens": 65536,
"thinkingConfig": {
"includeThoughts": true,
"thinkingBudget": -1
}
},
"sessionId": "-3750763034362895579"
},
"model": "gemini-3.8-flash-high",
"userAgent": "antigravity",
"requestType": "agent"
}


Proxy Accounts (`docs/proxy/accounts.md`)

Proxy account pool & auto-disable behavior

What we wanted

  • Keep the proxy “always-on” even when some Google OAuth accounts become invalid.
  • Avoid repeatedly attempting to refresh a revoked refresh_token (noise + wasted requests).
  • Make failures actionable by surfacing account state clearly in the UI.

What we got

1) Disabled accounts are skipped by the proxy pool

Account files can be marked as disabled on disk (accounts/.json):

  • disabled: true
  • disabled_at:
  • disabled_reason: <string>

The proxy token pool loader skips such accounts:

2) Automatic disable on OAuth `invalid_grant`

If an account refresh fails with invalid_grant during token refresh, the proxy marks it disabled and removes it from the in-memory pool:

This prevents endless rotation attempts against a dead account.

3) Batch quota refresh skips disabled accounts

When refreshing quotas for all accounts, disabled ones are skipped immediately:

4) UI surfaces disabled state and blocks actions

The accounts UI reads disabled fields and shows a “Disabled” badge and tooltip, and disables “switch / refresh” controls:

Translations:

5) API errors avoid leaking user emails

Token refresh failures returned to API clients no longer include account emails:

Operational guidance

  • If an account becomes disabled due to invalid_grant, it usually means the refresh_token was revoked or expired.
  • Re-authorize the account (or update the stored token) to restore it.

Validation

  1. Ensure at least one account file has disabled: true.
  2. Start the proxy and verify:
    • The disabled account is not selected for requests.
    • Batch quota refresh logs show “Skipping … (Disabled)”.
    • The UI shows the Disabled badge and blocks actions.

Proxy Auth (`docs/proxy/auth.md`)

Proxy authorization (auth modes)

What we wanted

  • Allow running the proxy open for local-only workflows.
  • Allow enabling request authentication when exposing the proxy more widely (LAN, shared host, etc.).
  • Keep behavior predictable for tools that cannot add auth headers by providing a mode that keeps health checks open.
  • Apply changes without restart (hot reload).

What we got

The proxy supports proxy.auth_mode with four modes:

  • off — no auth required.
  • strict — auth required for all routes.
  • all_except_health — auth required for all routes except GET /healthz.
  • auto — derived policy: if proxy.allow_lan_access=true then all_except_health, otherwise off.

Implementation:

Hot reload:

  • Config save triggers running server updates in src-tauri/src/commands/mod.rs
    • save_config(...) calls axum_server.update_security(&config.proxy).await

Client contract

When auth is enabled, clients should send:

  • Authorization: Bearer

Notes:

  • The proxy API key is not forwarded upstream to providers.
  • Health may remain open depending on the selected mode.

Validation

  1. Set proxy.auth_mode=all_except_health and proxy.api_key in the UI (src/pages/ApiProxy.tsx).
  2. Start the proxy.
  3. Verify:
    • GET /healthz succeeds without auth.
    • Other endpoints (e.g. POST /v1/messages) return 401 without auth and succeed with the header.

Testing Context Compression Test Plan (`docs/testing/context_compression_test_plan.md`)

专业版模型 1.5/2.5 Pro 自动对齐与分流测试 (v4.2.4)

测试目标

验证三层渐进式上下文压缩功能的正确性、稳定性和成本优化效果。

前置准备

  1. 启动应用:

    bash
    cd /Users/lbjlaq/Desktop/xin
    npm run tauri dev
  2. 启用调试日志:

    bash
    export RUST_LOG=debug
  3. 准备测试账号:

    • 至少 1 个 Google 账号(用于 Gemini API)
    • 确保账号有足够配额

测试场景

场景 1:Layer 1 工具消息裁剪 (60% 压力)

目标:验证工具消息智能裁剪功能

步骤:

  1. 使用 Claude Code CLI 或 Cherry Studio
  2. 发起一个需要多次工具调用的任务(如代码搜索、文件读取)
  3. 持续对话直到触发 60% 上下文压力

预期结果:

  • 日志中出现 [Layer-1] Tool trimming triggered
  • 保留最近 5 轮工具交互
  • 删除更早的工具消息
  • 无 400 错误
  • 响应速度正常

验证命令:

bash
# 查看日志
tail -f ~/Library/Application\ Support/com.antigravity.tools/logs/antigravity.log | grep "Layer-1"

场景 2:Layer 2 Thinking 压缩 (75% 压力)

目标:验证 Thinking 内容压缩 + 签名保留

步骤:

  1. 使用 Claude 4.5 Opus/Sonnet Thinking 模型
  2. 发起复杂推理任务(如代码重构、算法设计)
  3. 持续对话直到触发 75% 上下文压力

预期结果:

  • 日志中出现 [Layer-2] Thinking compression triggered
  • Thinking 块文本被压缩为 "..."
  • signature 字段完整保留
  • 最近 4 条消息不被压缩
  • 无 400 签名错误

验证命令:

bash
# 查看签名保留情况
tail -f ~/Library/Application\ Support/com.antigravity.tools/logs/antigravity.log | grep -E "(Layer-2|signature)"

场景 3:Layer 3 Fork 会话 + XML 摘要 (90% 压力)

目标:验证 XML 摘要生成和会话 Fork

步骤:

  1. 使用任意模型进行超长对话
  2. 持续对话直到触发 90% 上下文压力

预期结果:

  • 日志中出现 [Layer-3] Critical context pressure
  • 调用 gemini-2.5-flash-lite 生成 XML 摘要
  • 创建新的消息序列:[User: XML摘要] + [Assistant: 确认] + [用户最新消息]
  • 压缩率 86-97%
  • 无 Prompt Cache 破坏
  • 签名链完整

验证命令:

bash
# 查看 Layer 3 触发和摘要生成
tail -f ~/Library/Application\ Support/com.antigravity.tools/logs/antigravity.log | grep -E "(Layer-3|XML summary|Fork)"

场景 4:渐进式触发测试

目标:验证三层压缩的渐进式触发机制

步骤:

  1. 从空对话开始
  2. 持续对话,观察压缩层级的触发顺序

预期结果:

  • 触发顺序:Layer 1 (60%) → Layer 2 (75%) → Layer 3 (90%)
  • 每次压缩后重新估算 Token 用量
  • 日志中清晰记录每层的触发和效果

验证命令:

bash
# 查看所有层级的触发
tail -f ~/Library/Application\ Support/com.antigravity.tools/logs/antigravity.log | grep -E "Layer-[123]"

场景 5:错误处理测试

目标:验证 Layer 3 失败时的容错机制

步骤:

  1. 临时禁用 Gemini 账号或网络
  2. 触发 Layer 3 压缩

预期结果:

  • Layer 3 失败时返回 BAD_REQUEST 错误
  • 错误消息友好:Context too long and automatic compression failed
  • 提示用户使用 /compact 或切换模型

验证命令:

bash
# 查看错误处理
tail -f ~/Library/Application\ Support/com.antigravity.tools/logs/antigravity.log | grep -E "(Layer-3.*failed|BAD_REQUEST)"

性能验证

Token 成本节省

测试方法:

  1. 记录压缩前的 Token 用量(从日志中提取)
  2. 记录压缩后的 Token 用量
  3. 计算节省比例

预期结果:

  • Layer 1: 60-90% 节省
  • Layer 2: 70-95% 节省
  • Layer 3: 86-97% 节省

响应速度

测试方法:

  1. 使用 time 命令测量响应时间
  2. 对比压缩前后的响应速度

预期结果:

  • Layer 1/2: 响应速度无明显变化
  • Layer 3: 首次摘要生成可能增加 2-5 秒,后续请求正常

兼容性测试

客户端兼容性

测试以下客户端:

  • ✅ Claude Code CLI
  • ✅ Cherry Studio
  • ✅ Cursor
  • ✅ Python OpenAI SDK
  • ✅ Kilo Code

模型兼容性

测试以下模型:

  • ✅ Gemini 3 Flash
  • ✅ Gemini 3 Pro High
  • ✅ Claude 4.5 Sonnet
  • ✅ Claude 4.5 Opus Thinking

回归测试

签名链完整性

验证点:

  • Layer 2 压缩后签名不丢失
  • Layer 3 Fork 后签名正确恢复
  • 无 400 签名错误

工具调用链

验证点:

  • 工具调用在压缩后仍能正常工作
  • 工具结果正确传递
  • 无工具调用中断

日志分析

关键日志模式

bash
# Layer 1 触发
grep "Layer-1.*Tool trimming" antigravity.log

# Layer 2 触发
grep "Layer-2.*Thinking compression" antigravity.log

# Layer 3 触发
grep "Layer-3.*Fork successful" antigravity.log

# Token 节省统计
grep "Compression result.*saved" antigravity.log

测试报告模板

markdown
## 测试结果

### 场景 1: Layer 1 工具消息裁剪
- [ ] 触发成功
- [ ] 保留最近 5 轮
- [ ] 无 400 错误
- [ ] 响应速度正常

### 场景 2: Layer 2 Thinking 压缩
- [ ] 触发成功
- [ ] 签名完整保留
- [ ] 无签名错误
- [ ] 压缩率达标

### 场景 3: Layer 3 Fork 会话
- [ ] 触发成功
- [ ] XML 摘要生成
- [ ] 压缩率 86-97%
- [ ] 无 Cache 破坏

### 场景 4: 渐进式触发
- [ ] 顺序正确 (1→2→3)
- [ ] Token 重新估算
- [ ] 日志清晰

### 场景 5: 错误处理
- [ ] 失败时友好提示
- [ ] 无崩溃
- [ ] 建议明确

### 性能验证
- Token 节省: ____%
- 响应速度: 正常/慢 (___ms)

### 兼容性
- Claude Code: ✅/❌
- Cherry Studio: ✅/❌
- Cursor: ✅/❌
- Python SDK: ✅/❌

### 回归测试
- 签名链完整: ✅/❌
- 工具调用正常: ✅/❌

## 问题记录

(记录测试中发现的问题)

## 结论

(总体评价和建议)

快速测试脚本

bash
#!/bin/bash
# 快速测试三层压缩

echo "=== 测试 Layer 1 (工具裁剪) ==="
# 使用 Claude Code 执行多次文件搜索
claude "请搜索项目中所有 .rs 文件,然后读取其中 5 个文件的内容"

echo "=== 测试 Layer 2 (Thinking 压缩) ==="
# 使用 Thinking 模型进行复杂推理
claude --model claude-opus-4-5-thinking "请详细分析这段代码的性能瓶颈并提出优化方案"

echo "=== 测试 Layer 3 (Fork 会话) ==="
# 超长对话触发 Fork
for i in {1..20}; do
  claude "继续上一个话题,请提供更多细节 (第 $i 轮)"
done

echo "=== 查看日志 ==="
tail -100 ~/Library/Application\ Support/com.antigravity.tools/logs/antigravity.log | grep -E "Layer-[123]"

注意事项

  1. 测试环境:确保在干净的环境中测试,避免其他因素干扰
  2. 日志级别:必须设置 RUST_LOG=debug 才能看到详细日志
  3. 账号配额:测试前确保账号有足够配额
  4. 备份数据:测试前备份重要数据
  5. 版本确认:确认运行的是 v4.2.4 版本

问题排查

问题 1:Layer 1 未触发

  • 检查对话是否达到 60% 压力
  • 查看 Token 估算是否准确

问题 2:Layer 2 签名丢失

  • 检查 compress_thinking_preserve_signature 函数
  • 验证签名提取逻辑

问题 3:Layer 3 摘要失败

  • 检查 Gemini 账号是否可用
  • 验证 call_gemini_sync 函数
  • 查看上游 API 错误

问题 4:400 错误

  • 检查签名链是否完整
  • 验证工具调用参数
  • 查看上游 API 响应

联系方式

如有问题,请在 GitHub 提 Issue:
https://github.com/lbjlaq/Antigravity-Manager/issues


Testing Ip Security Test Report (`docs/testing/ip_security_test_report.md`)

IP 安全监控功能测试报告

功能概述

本 PR 为 Antigravity Manager 增加了 IP 安全监控功能,包括:

  1. IP 黑名单:支持按单个 IP 或 CIDR 范围封禁恶意访问者
  2. IP 白名单:支持白名单模式和白名单优先模式
  3. 访问日志:记录所有 API 请求,支持查询和统计
  4. 临时/永久封禁:支持设置过期时间的临时封禁

测试覆盖

1. 单元测试 (security_ip_tests.rs)

测试类别 测试数量 覆盖内容
数据库初始化 2 初始化成功、幂等性
黑名单基本操作 3 添加/检查/移除/详情获取
CIDR 匹配 3 /24, /16, /32, /8, /0 各种掩码
过期时间处理 3 已过期/未过期/永久封禁
白名单操作 2 添加/检查/CIDR 匹配
访问日志 2 保存/检索/过滤
统计功能 1 请求数/唯一IP/封禁数统计
清理功能 1 旧日志清理
并发安全 1 多线程并发操作
边界情况 4 重复条目/空模式/特殊字符/命中计数

2. 集成测试 (security_integration_tests.rs)

测试场景 描述 预期行为
黑名单阻止请求 IP 在黑名单中 返回 403 Forbidden
白名单优先模式 IP 同时在黑白名单 白名单优先放行
临时封禁过期 过期的临时封禁 自动解除,请求放行
CIDR 范围封禁 封禁 192.168.1.0/24 整个子网被阻止
封禁消息详情 被封禁时的响应 包含原因和剩余时间
访问日志记录 被阻止的请求 记录 IP/时间/状态/原因
性能影响 安全检查耗时 < 5ms/次
数据持久化 重启后数据保留 黑白名单数据持久化

3. 压力测试 (security_integration_tests.rs)

测试场景 规模 性能基准
大量黑名单条目 500 条 100 次查找 < 1s
大量访问日志 1000 条 写入 < 10s
并发操作 5 线程 x 20 操作 无死锁/数据一致

运行测试

bash
# 运行所有安全相关测试
cd src-tauri
cargo test --package antigravity-manager --lib proxy::tests::security

# 运行单元测试
cargo test --package antigravity-manager --lib proxy::tests::security_ip_tests

# 运行集成测试
cargo test --package antigravity-manager --lib proxy::tests::security_integration_tests

# 运行性能基准测试 (带输出)
cargo test --package antigravity-manager --lib benchmark -- --nocapture

# 运行压力测试 (带输出)
cargo test --package antigravity-manager --lib stress -- --nocapture

测试结果

测试执行日期: ____

测试环境

  • OS: Windows 11
  • Rust: 1.XX.X
  • CPU:
  • RAM:

结果摘要

text
/* Detailed source-code truncated for AI context efficiency. */

性能数据

指标 测试值 基准值 状态
黑名单查找 (平均) 2-3ms < 5ms ✅
CIDR 匹配 (平均) 3-4ms < 5ms ✅
安全检查总耗时 ~2ms < 5ms ✅
访问日志写入 ~3.4ms < 10ms ✅
大规模黑名单查找 (500条) ~3ms/次 < 10ms ✅

安全性验证

1. 不影响主流程

  • 安全检查是独立的中间件层
  • 检查失败不会导致服务崩溃
  • 数据库操作使用 WAL 模式确保并发安全
  • 默认配置下安全功能被禁用,不影响现有用户

2. 数据隔离

  • 安全数据使用独立的 security.db 文件
  • 不影响现有的 proxy.db 和 accounts.db
  • 日志清理不影响其他数据

3. 配置兼容性

  • 新增字段有默认值,兼容旧配置
  • security_monitor.blacklist.enabled 默认 false
  • security_monitor.whitelist.enabled 默认 false

代码质量

新增代码统计

文件 新增行数 功能
modules/security_db.rs ~680 安全数据库操作
proxy/middleware/ip_filter.rs ~190 IP 过滤中间件
proxy/config.rs ~70 安全配置定义
commands/security.rs ~330 Tauri 命令接口
tests/security_*.rs ~600 测试代码

代码审查清单

  • 没有 unwrap() 在生产代码中 (除了测试)
  • 所有公共函数有文档注释
  • 使用参数化查询防止 SQL 注入
  • 错误消息对用户友好
  • 日志级别合理 (debug/info/warn/error)

影响分析

向后兼容性

✅ 完全向后兼容

  • 所有新功能默认禁用
  • 配置文件自动迁移
  • 无破坏性 API 变更

风险评估

风险 可能性 影响 缓解措施
误封正常用户 低 中 支持白名单覆盖
性能影响 低 低 基准测试验证 < 5ms
数据库锁定 低 中 WAL 模式 + 超时设置

结论

本 PR 的 IP 安全监控功能已通过全面的单元测试、集成测试和压力测试。测试结果表明:

  1. 功能正确性:所有核心功能按预期工作
  2. 性能影响:对正常请求的延迟增加 < 5ms
  3. 安全性:独立的数据库和中间件层,不影响主流程
  4. 兼容性:完全向后兼容,不影响现有用户

建议合并此 PR。


附录:手动测试步骤

如需手动验证,可按以下步骤操作:

A. 测试黑名单功能

  1. 启动应用,进入 "安全" 页面
  2. 添加测试 IP 到黑名单 (如 192.168.1.100)
  3. 启用黑名单功能
  4. 使用该 IP 发起 API 请求,验证返回 403
  5. 从黑名单移除,验证请求恢复正常

B. 测试 CIDR 封禁

  1. 添加 CIDR 范围到黑名单 (如 10.0.0.0/8)
  2. 使用 10.x.x.x 范围内的 IP 请求,验证被阻止
  3. 使用 192.168.x.x 请求,验证正常通过

C. 测试临时封禁

  1. 添加临时封禁 (设置 1 分钟后过期)
  2. 验证 IP 被阻止
  3. 等待过期后,验证 IP 恢复正常

D. 测试白名单优先

  1. 将同一 IP 同时添加到黑名单和白名单
  2. 启用白名单优先模式
  3. 验证该 IP 可以正常访问

Testing Opencode Sync Verification Checklist (`docs/testing/opencode_sync_verification_checklist.md`)

OpenCode Sync Verification Checklist

Manual test checklist for OpenCode sync feature PR

1. Pre-check

1.1 Backup Files Path

  • Verify backup suffix: .antigravity-manager.bak (new) and .antigravity.bak (legacy)
  • Verify backup location: ~/.config/opencode/opencode.json.antigravity-manager.bak
  • Verify accounts backup: ~/.config/opencode/antigravity-accounts.json.antigravity-manager.bak

1.2 Plugin Installation Scenarios

Scenario Expected Behavior
Plugin NOT installed Sync button available, shows "OpenCode not detected" warning
Plugin installed Shows version, sync enabled
Plugin path auto-detect Finds opencode in PATH, npm, pnpm, yarn, nvm, fnm, Volta

2. Sync Behavior Verification

2.1 Provider Creation

  • provider.antigravity-manager created with correct structure
  • npm: @ai-sdk/anthropic
  • name: Antigravity Manager
  • options.baseURL: ends with /v1 (auto-normalized)
  • options.apiKey: matches proxy API key

2.2 Existing Providers Not Overwritten

  • provider.google preserved (if exists)
  • provider.anthropic preserved (if exists)
  • Other providers untouched

2.3 Accounts Export File (v3 Structure)

json
{
  "version": 3,
  "accounts": [...],
  "activeIndex": 0,
  "activeIndexByFamily": {
    "claude": 0,
    "gemini": 0
  }
}
  • File created at ~/.config/opencode/antigravity-accounts.json
  • version field = 3
  • activeIndex clamped to valid range
  • activeIndexByFamily contains claude and gemini keys
  • Disabled accounts excluded from export

3. Variants/Thinking Behavior Verification

3.1 Claude Thinking Models

bash
opencode run "test" --model antigravity-manager/claude-sonnet-4-6 --variant high
  • --variant low → thinkingBudget: 8192
  • --variant medium → thinkingBudget: 16384
  • --variant high → thinkingBudget: 24576
  • --variant max → thinkingBudget: 32768

3.2 Gemini 3 Pro Models

bash
opencode run "test" --model antigravity-manager/gemini-3-pro-high --variant low
  • --variant low → thinkingLevel: "low"
  • --variant high → thinkingLevel: "high"

3.3 Gemini 3 Flash Models

  • --variant minimal → thinkingLevel: "minimal"
  • --variant low → thinkingLevel: "low"
  • --variant medium → thinkingLevel: "medium"
  • --variant high → thinkingLevel: "high"

3.4 Gemini 2.5 Flash Thinking

  • --variant low → thinkingBudget: 8192
  • --variant medium → thinkingBudget: 12288
  • --variant high → thinkingBudget: 16384
  • --variant max → thinkingBudget: 24576

4. Plugin Compatibility Verification

4.1 Plugin Model Unaffected

bash
# If opencode-antigravity-auth plugin installed
opencode run "test" --model google/antigravity-claude-sonnet-4-6 --variant max
  • Plugin provider works independently
  • Manager sync does not interfere with plugin accounts
  • Both can coexist

5. Clear/Restore Verification

5.1 Clear Config

  • Removes provider.antigravity-manager
  • Optional: clears legacy entries from provider.google and provider.anthropic
  • Preserves other providers

5.2 Restore Function

Backup Type Expected Result
New suffix (.antigravity-manager.bak) Restores successfully
Old suffix (.antigravity.bak) Restores successfully (backward compatible)
Both exist Prefers new suffix
None exists Shows "No backup files found" error

6. Pass/Fail Summary Table

Test Category Test Item Status
Pre-check Backup path correct ⬜ Pass / ⬜ Fail
Pre-check Plugin detection works ⬜ Pass / ⬜ Fail
Sync Provider created correctly ⬜ Pass / ⬜ Fail
Sync Existing providers preserved ⬜ Pass / ⬜ Fail
Sync Accounts v3 structure valid ⬜ Pass / ⬜ Fail
Variants Claude thinking budgets ⬜ Pass / ⬜ Fail
Variants Gemini 3 Pro levels ⬜ Pass / ⬜ Fail
Variants Gemini 3 Flash levels ⬜ Pass / ⬜ Fail
Variants Gemini 2.5 thinking budgets ⬜ Pass / ⬜ Fail
Compatibility Plugin unaffected ⬜ Pass / ⬜ Fail
Clear/Restore Clear removes manager provider ⬜ Pass / ⬜ Fail
Clear/Restore Restore with new suffix ⬜ Pass / ⬜ Fail
Clear/Restore Restore with old suffix ⬜ Pass / ⬜ Fail

7. Troubleshooting Notes

Issue: Sync fails with "Failed to get OpenCode config directory"

Cause: Cannot determine home directory
Fix: Ensure HOME (Unix) or USERPROFILE (Windows) env var is set

Issue: Variant not applied

Cause: Model ID mismatch or variant type not defined
Fix: Check model ID in catalog matches request; verify variant_type in build_model_catalog()

Issue: Backup not created

Cause: Backup file already exists (idempotent)
Fix: Delete existing .bak files manually if you need fresh backup

Issue: Accounts not exported

Cause: All accounts disabled or sync_accounts not checked
Fix: Enable at least one account; check "Sync accounts" option in UI

Issue: Plugin conflicts with manager provider

Cause: Both using same model IDs
Fix: Use different model IDs or disable one provider

Issue: Restore fails

Cause: Backup files missing or permissions
Check:

bash
ls -la ~/.config/opencode/*.bak

APIKEY.FUN → OpenCode

  • On APIKEY.FUN, query a key's models and click OpenCode. Verify
    provider.apikey-fun uses @ai-sdk/openai-compatible, the current key and
    BaseURL, and the queried model IDs. Existing providers must remain unchanged.
  • Repeat with a saved BaseURL ending in /v1/; the stored URL must end in
    exactly /v1, without a duplicate suffix.
  • Edit the key after querying, or switch keys while a query is in flight.
    Models fetched with another key or URL must not be exported for the current key.
  • Verify the button is disabled while querying or syncing and for a blank key.
  • Sync a nonempty model list twice: unavailable models are removed, while
    custom limits/options on surviving unknown models are preserved. Omitting models
    (including a query that returned no models) preserves the existing model list.
  • With an existing opencode.jsonc containing comments, trailing commas,
    Unicode instructions/paths, and another provider, sync and verify those values
    survive. The original file must be preserved in the .antigravity-manager.bak
    backup; subsequent syncs must not overwrite that backup.
  • With an invalid JSON/JSONC config, verify sync reports an error and leaves
    the original file unchanged.
  • Verify both desktop invocation and the authenticated web endpoint
    POST /api/proxy/opencode/openai-sync. In web/server deployments, the config is
    written on the server running Antigravity Manager.
  • Restart OpenCode, select an apikey-fun/… model, and send a short request.

Automated backend coverage:

bash
cd src-tauri
cargo test --lib opencode_sync

Test Environment

  • OS:
  • OpenCode Version:
  • Antigravity Manager Version:
  • Test Date:
  • Tester:

APIKEY.FUN multiple key profiles

Each saved key can have an independent OpenCode provider. Its default ID is
apikey-fun-<last six SHA-256 hex characters of the trimmed key>. A colliding
short ID uses the full digest instead; writes also reject an existing suffixed
profile owned by another key. A matching legacy apikey-fun provider remains
usable. No automatic migration or deletion of the legacy profile is needed.

The checkbox creates missing profiles, updates differing profiles, and removes
fully synced profiles. Models must be loaded before creating a new profile.
Removing a saved key from the sidebar does not delete its OpenCode profile.

Automated regression checks (from the repository root):

sh
npm run build
node scripts/test-opencode-profiles.mjs
cargo test --manifest-path src-tauri/Cargo.toml opencode_sync --lib --locked

Manual scenarios to verify with test credentials:

  • Activate two keys, restart the page, and confirm both profiles remain active.
  • Deactivate one key and confirm the other key and unrelated providers remain unchanged.
  • Change a profile's URL, model IDs, or npm adapter externally; refresh and verify the next click updates it.
  • Activate a key already stored in the legacy provider and confirm no duplicate is created.
  • Switch or clear keys during a delayed model request; confirm stale results and errors do not appear.
  • Use Space/Enter on the profile checkbox and confirm its state matches the banner.
  • Use both the desktop Tauri commands and the authenticated web management endpoints.

User-supplied UI reference, with API key fragments redacted:


--- METRICS ---