fix: v1.3.1 bugfixes - Zod v4 upgrade, keep_thinking, tool params, auth headers, quiet_mode

Changes:
- Upgraded to Zod v4 and adjusted schema generation for compatibility
- Fixed keep_thinking=true failing without debug mode (signature validation)
- Fixed tool calls failing for tools with no parameters (default args to {})
- Aligned auth headers with official Gemini CLI to reduce account issues
- Fixed quiet_mode not suppressing all toast notifications
This commit is contained in:
tctinh
2026-01-19 11:13:02 +07:00
parent cd6147b57c
commit 814709c8e6
13 changed files with 1197 additions and 206 deletions

34
CHANGELOG.md Normal file
View File

@@ -0,0 +1,34 @@
# Changelog
## [1.3.1] - 2026-01-19
### Changed
- Upgraded to Zod v4 and adjusted schema generation for compatibility
### Fixed
- **`keep_thinking=true` now works without debug mode** - Fixed Claude multi-turn conversations failing with "Failed to process error response" when `keep_thinking=true` after tool calls, unless debug mode was enabled
- Root cause: `filterContentArray` trusted any signature >= 50 chars for last assistant messages, but Claude returns its own signatures that Antigravity doesn't recognize
- Fix: Now verifies signatures against our cache via `isOurCachedSignature()` before passing through. Foreign/missing signatures get replaced with `SKIP_THOUGHT_SIGNATURE` sentinel
- Why debug worked: Debug mode injects synthetic thinking with no signature, triggering sentinel injection correctly
- **Fixed tool calls failing for tools with no parameters** - Tools like `hive_plan_read`, `hive_status`, and `hive_feature_list` that have no required parameters would fail with Zod validation error `state.input: expected record, received undefined`
- Root cause: When Claude calls a tool with no parameters, it returns `functionCall` without an `args` field. The response transformation only processed parts where `functionCall.args` was defined, leaving `args` as `undefined`
- Fix: Changed condition to handle all `functionCall` parts, defaulting `args` to `{}` when missing, ensuring opencode's `state.input` always receives a valid record
- **Auth headers aligned with official Gemini CLI** - Updated authentication headers to match the official Antigravity/Gemini CLI behavior, reducing "account ineligible" errors and potential bans ([#178](https://github.com/NoeFabris/opencode-antigravity-auth/issues/178))
- `GEMINI_CLI_HEADERS["User-Agent"]`: `9.15.1``10.3.0`
- `GEMINI_CLI_HEADERS["X-Goog-Api-Client"]`: `gl-node/22.17.0``gl-node/22.18.0`
- `ANTIGRAVITY_HEADERS["User-Agent"]`: Updated to full Chrome/Electron user agent string
- Token exchange now includes `Accept`, `Accept-Encoding`, `User-Agent`, `X-Goog-Api-Client` headers
- Userinfo fetch now includes `User-Agent`, `X-Goog-Api-Client` headers
- `fetchProjectID` now uses centralized constants instead of hardcoded strings
- **`quiet_mode` now properly suppresses all toast notifications** - Fixed `quiet_mode: true` in `antigravity.json` not suppressing "Status dialog dismissed" and other toast notifications ([#207](https://github.com/NoeFabris/opencode-antigravity-auth/issues/207))
- Root cause: The `showToast` helper function didn't check `quietMode`, and only some call sites had manual `!quietMode &&` guards
- Fix: Moved `quietMode` check inside `showToast` helper so all toasts are automatically suppressed when `quiet_mode: true`
## [1.3.0] - Previous Release
See [releases](https://github.com/NoeFabris/opencode-antigravity-auth/releases) for previous versions.

View File

@@ -1,156 +1,280 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://raw.githubusercontent.com/NoeFabris/opencode-antigravity-auth/main/assets/antigravity.schema.json",
"title": "Antigravity Auth Configuration",
"description": "Configuration schema for opencode-antigravity-auth plugin",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"$schema": {
"type": "string",
"description": "JSON Schema reference for IDE support"
"type": "string"
},
"quiet_mode": {
"type": "boolean",
"default": false,
"description": "Suppress most toast notifications (rate limit, account switching, etc.). Recovery toasts are always shown."
"type": "boolean",
"description": "Suppress most toast notifications (rate limit, account switching). Recovery toasts always shown. Env: OPENCODE_ANTIGRAVITY_QUIET=1"
},
"debug": {
"type": "boolean",
"default": false,
"description": "Enable debug logging to file. Env override: OPENCODE_ANTIGRAVITY_DEBUG=1"
"type": "boolean",
"description": "Enable debug logging to file. Env: OPENCODE_ANTIGRAVITY_DEBUG=1 (or =2 for verbose)"
},
"log_dir": {
"type": "string",
"description": "Custom directory for debug logs. Env override: OPENCODE_ANTIGRAVITY_LOG_DIR=/path/to/logs"
"description": "Custom directory for debug logs. Env: OPENCODE_ANTIGRAVITY_LOG_DIR=/path/to/logs"
},
"keep_thinking": {
"type": "boolean",
"default": false,
"description": "[EXPERIMENTAL] Preserve thinking blocks for Claude models using signature caching. Env override: OPENCODE_ANTIGRAVITY_KEEP_THINKING=1"
"type": "boolean",
"description": "Preserve thinking blocks for Claude models using signature caching. May cause signature errors. Env: OPENCODE_ANTIGRAVITY_KEEP_THINKING=1"
},
"session_recovery": {
"type": "boolean",
"default": true,
"description": "Enable automatic session recovery from tool_result_missing and thinking errors."
"type": "boolean",
"description": "Enable automatic session recovery from tool_result_missing errors. Env: OPENCODE_ANTIGRAVITY_SESSION_RECOVERY=1"
},
"auto_resume": {
"default": false,
"type": "boolean",
"default": true,
"description": "Automatically send 'continue' prompt after successful recovery. Only applies when session_recovery is enabled."
"description": "Automatically send resume prompt after successful recovery. Env: OPENCODE_ANTIGRAVITY_AUTO_RESUME=1"
},
"resume_text": {
"type": "string",
"default": "continue",
"description": "Custom text to send when auto-resuming after recovery."
},
"auto_update": {
"type": "boolean",
"default": true,
"description": "Enable automatic plugin updates."
},
"max_rate_limit_wait_seconds": {
"type": "number",
"minimum": 0,
"maximum": 3600,
"default": 300,
"description": "Maximum time in seconds to wait when all accounts are rate-limited. If the minimum wait time exceeds this, fails fast with an error. Set to 0 to disable (wait indefinitely)."
},
"quota_fallback": {
"type": "boolean",
"default": false,
"description": "Gemini only. When rate-limited on primary quota pool (Antigravity or Gemini CLI), automatically try the alternate pool before switching accounts. Effectively doubles retry attempts per account."
},
"account_selection_strategy": {
"type": "string",
"enum": ["sticky", "round-robin", "hybrid"],
"default": "sticky",
"description": "Strategy for selecting accounts: sticky (same until rate-limited), round-robin (rotate each request), hybrid (fresh accounts first, then sticky). Env override: OPENCODE_ANTIGRAVITY_ACCOUNT_SELECTION_STRATEGY"
"description": "Custom text to send when auto-resuming after recovery. Env: OPENCODE_ANTIGRAVITY_RESUME_TEXT=continue"
},
"signature_cache": {
"type": "object",
"properties": {
"enabled": {
"type": "boolean",
"default": true,
"type": "boolean",
"description": "Enable disk caching of thinking block signatures."
},
"memory_ttl_seconds": {
"default": 3600,
"type": "number",
"minimum": 60,
"maximum": 86400,
"default": 3600,
"description": "In-memory TTL in seconds."
},
"disk_ttl_seconds": {
"default": 172800,
"type": "number",
"minimum": 3600,
"maximum": 604800,
"default": 172800,
"description": "Disk TTL in seconds."
},
"write_interval_seconds": {
"default": 60,
"type": "number",
"minimum": 10,
"maximum": 600,
"default": 60,
"description": "Background write interval in seconds."
}
},
"required": [
"enabled",
"memory_ttl_seconds",
"disk_ttl_seconds",
"write_interval_seconds"
],
"additionalProperties": false,
"description": "Signature cache configuration for persisting thinking block signatures. Only used when keep_thinking is enabled."
},
"empty_response_max_attempts": {
"default": 4,
"type": "number",
"minimum": 1,
"maximum": 10,
"default": 4,
"description": "Maximum retry attempts when Antigravity returns an empty response (no candidates)."
},
"empty_response_retry_delay_ms": {
"default": 2000,
"type": "number",
"minimum": 500,
"maximum": 10000,
"default": 2000,
"description": "Delay in milliseconds between empty response retries."
},
"tool_id_recovery": {
"type": "boolean",
"default": true,
"type": "boolean",
"description": "Enable tool ID orphan recovery. Matches mismatched tool responses by function name or creates placeholders."
},
"claude_tool_hardening": {
"type": "boolean",
"default": true,
"type": "boolean",
"description": "Enable tool hallucination prevention for Claude models. Injects parameter signatures and strict usage rules."
},
"proactive_token_refresh": {
"type": "boolean",
"default": true,
"type": "boolean",
"description": "Enable proactive background token refresh before expiry, ensuring requests never block."
},
"proactive_refresh_buffer_seconds": {
"default": 1800,
"type": "number",
"minimum": 60,
"maximum": 7200,
"default": 1800,
"description": "Seconds before token expiry to trigger proactive refresh."
},
"proactive_refresh_check_interval_seconds": {
"default": 300,
"type": "number",
"minimum": 30,
"maximum": 1800,
"default": 300,
"description": "Interval between proactive refresh checks in seconds."
},
"switch_on_first_rate_limit": {
"type": "boolean",
"default": true,
"description": "Switch to next account immediately on first 429 rate limit (after 1s delay). When disabled, waits for full retry-after duration before switching."
"max_rate_limit_wait_seconds": {
"default": 300,
"type": "number",
"minimum": 0,
"maximum": 3600
},
"quota_fallback": {
"default": false,
"type": "boolean"
},
"account_selection_strategy": {
"default": "hybrid",
"type": "string",
"enum": [
"sticky",
"round-robin",
"hybrid"
]
},
"pid_offset_enabled": {
"type": "boolean",
"default": false,
"description": "Use process ID to offset initial account selection. Helps distribute load when running multiple OpenCode sessions in parallel (e.g., with subagents)."
"type": "boolean"
},
"switch_on_first_rate_limit": {
"default": true,
"type": "boolean"
},
"default_retry_after_seconds": {
"default": 60,
"type": "number",
"minimum": 1,
"maximum": 300
},
"max_backoff_seconds": {
"default": 60,
"type": "number",
"minimum": 5,
"maximum": 300
},
"health_score": {
"type": "object",
"properties": {
"initial": {
"default": 70,
"type": "number",
"minimum": 0,
"maximum": 100
},
"success_reward": {
"default": 1,
"type": "number",
"minimum": 0,
"maximum": 10
},
"rate_limit_penalty": {
"default": -10,
"type": "number",
"minimum": -50,
"maximum": 0
},
"failure_penalty": {
"default": -20,
"type": "number",
"minimum": -100,
"maximum": 0
},
"recovery_rate_per_hour": {
"default": 2,
"type": "number",
"minimum": 0,
"maximum": 20
},
"min_usable": {
"default": 50,
"type": "number",
"minimum": 0,
"maximum": 100
},
"max_score": {
"default": 100,
"type": "number",
"minimum": 50,
"maximum": 100
}
},
"required": [
"initial",
"success_reward",
"rate_limit_penalty",
"failure_penalty",
"recovery_rate_per_hour",
"min_usable",
"max_score"
],
"additionalProperties": false
},
"token_bucket": {
"type": "object",
"properties": {
"max_tokens": {
"default": 50,
"type": "number",
"minimum": 1,
"maximum": 1000
},
"regeneration_rate_per_minute": {
"default": 6,
"type": "number",
"minimum": 0.1,
"maximum": 60
},
"initial_tokens": {
"default": 50,
"type": "number",
"minimum": 1,
"maximum": 1000
}
},
"required": [
"max_tokens",
"regeneration_rate_per_minute",
"initial_tokens"
],
"additionalProperties": false
},
"auto_update": {
"default": true,
"type": "boolean",
"description": "Enable automatic plugin updates. Env: OPENCODE_ANTIGRAVITY_AUTO_UPDATE=1"
},
"web_search": {
"type": "object",
"properties": {
"default_mode": {
"default": "off",
"type": "string",
"enum": [
"auto",
"off"
]
},
"grounding_threshold": {
"default": 0.3,
"type": "number",
"minimum": 0,
"maximum": 1
}
},
"required": [
"default_mode",
"grounding_threshold"
],
"additionalProperties": false
}
}
},
"additionalProperties": false
}

754
package-lock.json generated
View File

@@ -12,7 +12,7 @@
"@openauthjs/openauth": "^0.4.3",
"proper-lockfile": "^4.1.2",
"xdg-basedir": "^5.1.0",
"zod": "^3.24.0"
"zod": "^4.0.0"
},
"devDependencies": {
"@opencode-ai/plugin": "^0.15.30",
@@ -22,7 +22,7 @@
"@vitest/ui": "^3.0.0",
"typescript": "^5.0.0",
"vitest": "^3.0.0",
"zod-to-json-schema": "^3.25.0"
"zod-to-json-schema": "^3.25.1"
},
"engines": {
"node": ">=20.0.0"
@@ -105,6 +105,278 @@
"node": ">=18"
}
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
"integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"aix"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
"integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
"integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
"integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
"integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
"integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
"integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
"integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
"integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
"integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
"integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
"integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
"cpu": [
"loong64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
"integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
"cpu": [
"mips64el"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
"integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
"integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
"integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
@@ -122,6 +394,159 @@
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-arm64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
"integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
"integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-arm64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
"integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
"integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openharmony-arm64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
"integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openharmony"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
"integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
"integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
"integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.25.12",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
"integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@isaacs/cliui": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
@@ -234,7 +659,6 @@
"resolved": "https://registry.npmjs.org/@oslojs/asn1/-/asn1-1.0.0.tgz",
"integrity": "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA==",
"license": "MIT",
"peer": true,
"dependencies": {
"@oslojs/binary": "1.0.0"
}
@@ -243,15 +667,13 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@oslojs/binary/-/binary-1.0.0.tgz",
"integrity": "sha512-9RCU6OwXU6p67H4NODbuxv2S3eenuQ4/WFLrsq+K/k682xrznH5EVWA7N4VFk9VYVcbFtKqur5YQQZc0ySGhsQ==",
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/@oslojs/crypto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@oslojs/crypto/-/crypto-1.0.1.tgz",
"integrity": "sha512-7n08G8nWjAr/Yu3vu9zzrd0L9XnrJfpMioQcvCMxBIiF5orECHe5/3J0jmXRVvgfqMm/+4oxlQ+Sq39COYLcNQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"@oslojs/asn1": "1.0.0",
"@oslojs/binary": "1.0.0"
@@ -261,15 +683,13 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-1.1.0.tgz",
"integrity": "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==",
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/@oslojs/jwt": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/@oslojs/jwt/-/jwt-0.2.0.tgz",
"integrity": "sha512-bLE7BtHrURedCn4Mco3ma9L4Y1GR2SMBuIvjWr7rmQ4/W/4Jy70TIAgZ+0nIlk0xHz1vNP8x8DCns45Sb2XRbg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@oslojs/encoding": "0.4.1"
}
@@ -278,8 +698,7 @@
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-0.4.1.tgz",
"integrity": "sha512-hkjo6MuIK/kQR5CrGNdAPZhS01ZCXuWDRJ187zh6qqF2+yMHZpD9fAYpX8q2bOO6Ryhl3XpCT6kUX76N8hhm4Q==",
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/@pkgjs/parseargs": {
"version": "0.11.0",
@@ -299,6 +718,216 @@
"dev": true,
"license": "MIT"
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.53.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz",
"integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
]
},
"node_modules/@rollup/rollup-android-arm64": {
"version": "4.53.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz",
"integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.53.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz",
"integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@rollup/rollup-darwin-x64": {
"version": "4.53.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz",
"integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
"version": "4.53.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz",
"integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
"version": "4.53.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz",
"integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.53.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz",
"integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.53.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz",
"integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.53.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz",
"integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.53.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz",
"integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-loong64-gnu": {
"version": "4.53.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz",
"integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==",
"cpu": [
"loong64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
"version": "4.53.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz",
"integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.53.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz",
"integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
"version": "4.53.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz",
"integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.53.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz",
"integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==",
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.53.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz",
@@ -327,6 +956,76 @@
"linux"
]
},
"node_modules/@rollup/rollup-openharmony-arm64": {
"version": "4.53.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz",
"integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openharmony"
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.53.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz",
"integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.53.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz",
"integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@rollup/rollup-win32-x64-gnu": {
"version": "4.53.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz",
"integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.53.3",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz",
"integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@standard-schema/spec": {
"version": "1.0.0-beta.3",
"resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.0.0-beta.3.tgz",
@@ -364,6 +1063,7 @@
"integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"undici-types": "~7.16.0"
}
@@ -525,6 +1225,7 @@
"integrity": "sha512-hGISOaP18plkzbWEcP/QvtRW1xDXF2+96HbEX6byqQhAUbiS5oH6/9JwW+QsQCIYON2bI6QZBF+2PvOmrRZ9wA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@vitest/utils": "3.2.4",
"fflate": "^0.8.2",
@@ -871,6 +1572,21 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/glob": {
"version": "10.5.0",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
@@ -1206,6 +1922,7 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@@ -1648,6 +2365,7 @@
"integrity": "sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "^0.25.0",
"fdir": "^6.5.0",
@@ -1746,6 +2464,7 @@
"integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@types/chai": "^5.2.2",
"@vitest/expect": "3.2.4",
@@ -1957,18 +2676,19 @@
}
},
"node_modules/zod": {
"version": "3.25.76",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
"version": "4.3.5",
"resolved": "https://registry.npmjs.org/zod/-/zod-4.3.5.tgz",
"integrity": "sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g==",
"license": "MIT",
"peer": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
},
"node_modules/zod-to-json-schema": {
"version": "3.25.0",
"resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.0.tgz",
"integrity": "sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ==",
"version": "3.25.1",
"resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz",
"integrity": "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==",
"dev": true,
"license": "ISC",
"peerDependencies": {

View File

@@ -56,12 +56,12 @@
"@vitest/ui": "^3.0.0",
"typescript": "^5.0.0",
"vitest": "^3.0.0",
"zod-to-json-schema": "^3.25.0"
"zod-to-json-schema": "^3.25.1"
},
"dependencies": {
"@openauthjs/openauth": "^0.4.3",
"proper-lockfile": "^4.1.2",
"xdg-basedir": "^5.1.0",
"zod": "^3.24.0"
"zod": "^4.0.0"
}
}

View File

@@ -1,17 +1,21 @@
import { writeFileSync, mkdirSync } from "node:fs";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import { zodToJsonSchema } from "zod-to-json-schema";
import { AntigravityConfigSchema } from "../src/plugin/config/schema.js";
const __dirname = dirname(fileURLToPath(import.meta.url));
const outputPath = join(__dirname, "../assets/antigravity.schema.json");
const jsonSchema = zodToJsonSchema(AntigravityConfigSchema, {
name: "AntigravityConfig",
$refStrategy: "none",
// Use zod v4's built-in toJSONSchema method
const rawSchema = AntigravityConfigSchema.toJSONSchema({
unrepresentable: "any",
override: (_ctx) => undefined // Use default handling
}) as Record<string, unknown>;
// Remove the "required" array since all fields have defaults and are optional
// This preserves backwards compatibility with the draft-07 schema behavior
delete rawSchema.required;
const envVarDescriptions: Record<string, string> = {
quiet_mode:
"Suppress most toast notifications (rate limit, account switching). Recovery toasts always shown. Env: OPENCODE_ANTIGRAVITY_QUIET=1",
@@ -72,14 +76,14 @@ function addDescriptions(schema: Record<string, unknown>): void {
}
}
const definitions = jsonSchema.definitions as Record<string, Record<string, unknown>> | undefined;
const definitions = rawSchema.definitions as Record<string, Record<string, unknown>> | undefined;
if (definitions?.AntigravityConfig) {
addDescriptions(definitions.AntigravityConfig);
} else {
addDescriptions(jsonSchema);
addDescriptions(rawSchema);
}
mkdirSync(dirname(outputPath), { recursive: true });
writeFileSync(outputPath, JSON.stringify(jsonSchema, null, 2) + "\n");
writeFileSync(outputPath, JSON.stringify(rawSchema, null, 2) + "\n");
console.log(`Schema written to ${outputPath}`);

View File

@@ -8,6 +8,7 @@ import {
ANTIGRAVITY_ENDPOINT_FALLBACKS,
ANTIGRAVITY_LOAD_ENDPOINTS,
ANTIGRAVITY_HEADERS,
GEMINI_CLI_HEADERS,
} from "../constants";
import { createLogger } from "../plugin/logger";
import { calculateTokenExpiry } from "../plugin/auth";
@@ -133,8 +134,8 @@ async function fetchProjectID(accessToken: string): Promise<string> {
const loadHeaders: Record<string, string> = {
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
"User-Agent": "google-api-nodejs-client/9.15.1",
"X-Goog-Api-Client": "google-cloud-sdk vscode_cloudshelleditor/0.1",
"User-Agent": GEMINI_CLI_HEADERS["User-Agent"],
"X-Goog-Api-Client": GEMINI_CLI_HEADERS["X-Goog-Api-Client"],
"Client-Metadata": ANTIGRAVITY_HEADERS["Client-Metadata"],
};
@@ -209,7 +210,11 @@ export async function exchangeAntigravity(
const tokenResponse = await fetch("https://oauth2.googleapis.com/token", {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8",
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate, br",
"User-Agent": GEMINI_CLI_HEADERS["User-Agent"],
"X-Goog-Api-Client": GEMINI_CLI_HEADERS["X-Goog-Api-Client"],
},
body: new URLSearchParams({
client_id: ANTIGRAVITY_CLIENT_ID,
@@ -233,6 +238,8 @@ export async function exchangeAntigravity(
{
headers: {
Authorization: `Bearer ${tokenPayload.access_token}`,
"User-Agent": GEMINI_CLI_HEADERS["User-Agent"],
"X-Goog-Api-Client": GEMINI_CLI_HEADERS["X-Goog-Api-Client"],
},
},
);

View File

@@ -71,14 +71,14 @@ export const GEMINI_CLI_ENDPOINT = ANTIGRAVITY_ENDPOINT_PROD;
export const ANTIGRAVITY_DEFAULT_PROJECT_ID = "rising-fact-p41fc";
export const ANTIGRAVITY_HEADERS = {
"User-Agent": "antigravity/1.11.5 windows/amd64",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Antigravity/1.104.0 Chrome/138.0.7204.235 Electron/37.3.1 Safari/537.36",
"X-Goog-Api-Client": "google-cloud-sdk vscode_cloudshelleditor/0.1",
"Client-Metadata": '{"ideType":"IDE_UNSPECIFIED","platform":"PLATFORM_UNSPECIFIED","pluginType":"GEMINI"}',
} as const;
export const GEMINI_CLI_HEADERS = {
"User-Agent": "google-api-nodejs-client/9.15.1",
"X-Goog-Api-Client": "gl-node/22.17.0",
"User-Agent": "google-api-nodejs-client/10.3.0",
"X-Goog-Api-Client": "gl-node/22.18.0",
"Client-Metadata": "ideType=IDE_UNSPECIFIED,platform=PLATFORM_UNSPECIFIED,pluginType=GEMINI",
} as const;

View File

@@ -862,8 +862,13 @@ export const createAntigravityPlugin = (providerId: string) => async (
}
};
// Helper to show toast without blocking on abort
// Use while(true) loop to handle rate limits with backoff
// This ensures we wait and retry when all accounts are rate-limited
const quietMode = config.quiet_mode;
// Helper to show toast without blocking on abort (respects quiet_mode)
const showToast = async (message: string, variant: "info" | "warning" | "success" | "error") => {
if (quietMode) return;
if (abortSignal?.aborted) return;
try {
await client.tui.showToast({
@@ -873,10 +878,6 @@ export const createAntigravityPlugin = (providerId: string) => async (
// TUI may not be available
}
};
// Use while(true) loop to handle rate limits with backoff
// This ensures we wait and retry when all accounts are rate-limited
const quietMode = config.quiet_mode;
const hasOtherAccountWithAntigravity = (currentAccount: any): boolean => {
if (family !== "gemini") return false;
@@ -964,8 +965,8 @@ export const createAntigravityPlugin = (providerId: string) => async (
});
}
// Show toast when switching to a different account (debounced, respects quiet mode)
if (!quietMode && accountCount > 1 && accountManager.shouldShowAccountToast(account.index)) {
// Show toast when switching to a different account (debounced, quiet_mode handled by showToast)
if (accountCount > 1 && accountManager.shouldShowAccountToast(account.index)) {
const accountLabel = account.email || `Account ${account.index + 1}`;
await showToast(
`Using ${accountLabel} (${account.index + 1}/${accountCount})`,
@@ -1162,12 +1163,10 @@ export const createAntigravityPlugin = (providerId: string) => async (
if (alternateStyle && alternateStyle !== headerStyle) {
const quotaName = headerStyle === "gemini-cli" ? "Gemini CLI" : "Antigravity";
const altQuotaName = alternateStyle === "gemini-cli" ? "Gemini CLI" : "Antigravity";
if (!quietMode) {
await showToast(
`${quotaName} quota exhausted, using ${altQuotaName} quota`,
"warning"
);
}
await showToast(
`${quotaName} quota exhausted, using ${altQuotaName} quota`,
"warning"
);
headerStyle = alternateStyle;
pushDebug(`quota fallback: ${headerStyle}`);
} else {
@@ -1454,12 +1453,10 @@ export const createAntigravityPlugin = (providerId: string) => async (
const cloned = response.clone();
const bodyText = await cloned.text();
if (bodyText.includes("Prompt is too long") || bodyText.includes("prompt_too_long")) {
if (!quietMode) {
await showToast(
"Context too long - use /compact to reduce size",
"warning"
);
}
await showToast(
"Context too long - use /compact to reduce size",
"warning"
);
const errorMessage = `[Antigravity Error] Context is too long for this model.\n\nPlease use /compact to reduce context size, then retry your request.\n\nAlternatively, you can:\n- Use /clear to start fresh\n- Use /undo to remove recent messages\n- Switch to a model with larger context window`;
return createSyntheticErrorResponse(errorMessage, prepared.requestedModel);
}
@@ -1525,7 +1522,7 @@ export const createAntigravityPlugin = (providerId: string) => async (
// Check for context errors and show appropriate toast
const contextError = transformedResponse.headers.get("x-antigravity-context-error");
if (contextError && !quietMode) {
if (contextError) {
if (contextError === "prompt_too_long") {
await showToast(
"Context too long - use /compact to reduce size, or trim your request",

View File

@@ -212,6 +212,13 @@ export function transformSseLine(
response = callbacks.onInjectDebug(response, options.debugText);
debugState.injected = true;
}
// Inject synthetic thinking placeholder when keep_thinking=true but no debug injection
// This ensures Claude multi-turn works by triggering SKIP_THOUGHT_SIGNATURE sentinel
if (!debugState.injected && options.injectSyntheticThinking && callbacks.onInjectSyntheticThinking) {
response = callbacks.onInjectSyntheticThinking(response);
debugState.injected = true; // Prevent duplicate injections
}
const transformed = callbacks.transformThinkingParts
? callbacks.transformThinkingParts(response)
@@ -263,16 +270,25 @@ export function cacheThinkingSignaturesFromResponse(
}
if (Array.isArray(resp.content)) {
let thinkingText = '';
// Use thoughtBuffer to accumulate thinking text across SSE events
// Claude streams thinking content and signature in separate events
const CLAUDE_BUFFER_KEY = 0; // Use index 0 for Claude's single-stream content
resp.content.forEach((block: unknown) => {
const b = block as Record<string, unknown> | null;
if (b?.type === 'thinking') {
thinkingText += (b.thinking || b.text || '') as string;
const text = (b.thinking || b.text || '') as string;
if (text) {
const current = thoughtBuffer.get(CLAUDE_BUFFER_KEY) ?? '';
thoughtBuffer.set(CLAUDE_BUFFER_KEY, current + text);
}
}
if (b?.signature && thinkingText) {
const signature = b.signature as string;
onCacheSignature?.(signatureSessionKey, thinkingText, signature);
signatureStore.set(signatureSessionKey, { text: thinkingText, signature });
if (b?.signature) {
const fullText = thoughtBuffer.get(CLAUDE_BUFFER_KEY) ?? '';
if (fullText) {
const signature = b.signature as string;
onCacheSignature?.(signatureSessionKey, fullText, signature);
signatureStore.set(signatureSessionKey, { text: fullText, signature });
}
}
});
}

View File

@@ -13,6 +13,7 @@ export interface SignatureStore {
export interface StreamingCallbacks {
onCacheSignature?: (sessionKey: string, text: string, signature: string) => void;
onInjectDebug?: (response: unknown, debugText: string) => unknown;
onInjectSyntheticThinking?: (response: unknown) => unknown;
transformThinkingParts?: (parts: unknown) => unknown;
}
@@ -21,6 +22,8 @@ export interface StreamingOptions {
debugText?: string;
cacheSignatures?: boolean;
displayedThinkingHashes?: Set<string>;
/** When true, injects synthetic thinking placeholder for keep_thinking multi-turn support */
injectSyntheticThinking?: boolean;
}
export interface ThoughtBuffer {

View File

@@ -1129,25 +1129,29 @@ function filterContentArray(
}
// For the LAST assistant message with thinking blocks:
// - If signature is valid (length >= 50), pass through unchanged
// - If signature is invalid/missing, inject sentinel to bypass validation
// - If signature is OUR cached signature, pass through unchanged
// - Otherwise inject sentinel to bypass Antigravity validation
// NOTE: We can't trust signatures just because they're >= 50 chars - Claude returns
// its own signatures which are long but invalid for Antigravity.
if (isLastAssistantMessage && (isThinking || hasSignature)) {
const existingSignature = item.signature || item.thoughtSignature;
const hasValidSignature = typeof existingSignature === "string" && existingSignature.length >= 50;
if (hasValidSignature) {
filtered.push(item);
} else {
// Invalid or missing signature - inject sentinel
const thinkingText = getThinkingText(item) || "";
log.debug("Injecting sentinel signature for invalid last-message thinking block");
const sentinelPart = {
type: item.type || "thinking",
thinking: thinkingText,
signature: SKIP_THOUGHT_SIGNATURE,
};
filtered.push(sentinelPart);
// First check if it's our cached signature
if (isOurCachedSignature(item, sessionId, getCachedSignatureFn)) {
const sanitized = sanitizeThinkingPart(item);
if (sanitized) filtered.push(sanitized);
continue;
}
// Not our signature (or no signature) - inject sentinel
const thinkingText = getThinkingText(item) || "";
const existingSignature = item.signature || item.thoughtSignature;
const signatureInfo = existingSignature ? `foreign signature (${String(existingSignature).length} chars)` : "no signature";
log.debug(`Injecting sentinel for last-message thinking block with ${signatureInfo}`);
const sentinelPart = {
type: item.type || "thinking",
thinking: thinkingText,
signature: SKIP_THOUGHT_SIGNATURE,
};
filtered.push(sentinelPart);
continue;
}
@@ -1375,10 +1379,14 @@ function transformGeminiCandidate(candidate: any): any {
return transformed;
}
// Handle functionCall: parse JSON strings in args
// Handle functionCall: parse JSON strings in args and ensure args is always defined
// (Ported from LLM-API-Key-Proxy's _extract_tool_call)
if (part.functionCall && part.functionCall.args) {
const parsedArgs = recursivelyParseJsonStrings(part.functionCall.args);
// Fix: When Claude calls a tool with no parameters, args may be undefined.
// opencode expects state.input to be a record, so we must ensure args: {} as fallback.
if (part.functionCall) {
const parsedArgs = part.functionCall.args
? recursivelyParseJsonStrings(part.functionCall.args)
: {};
return {
...part,
functionCall: {

View File

@@ -10,6 +10,7 @@ import {
type HeaderStyle,
} from "../constants";
import { cacheSignature, getCachedSignature } from "./cache";
import { getKeepThinking } from "./config";
import {
createStreamingTransformer,
transformSseLine,
@@ -182,9 +183,9 @@ function resolveConversationKey(requestPayload: Record<string, unknown>): string
const systemSeed = extractTextFromContent(
(anyPayload.systemInstruction as any)?.parts
?? anyPayload.systemInstruction
?? anyPayload.system
?? anyPayload.system_instruction,
?? anyPayload.systemInstruction
?? anyPayload.system
?? anyPayload.system_instruction,
);
const messageSeed = Array.isArray(anyPayload.messages)
? extractConversationSeedFromMessages(anyPayload.messages)
@@ -264,6 +265,70 @@ function injectDebugThinking(response: unknown, debugText: string): unknown {
return resp;
}
/**
* Synthetic thinking placeholder text used when keep_thinking=true but debug mode is off.
* This ensures Claude multi-turn conversations work correctly by triggering the
* SKIP_THOUGHT_SIGNATURE sentinel injection in filterContentArray on the next turn.
*/
const SYNTHETIC_THINKING_PLACEHOLDER = "[Thinking preserved]\n";
/**
* Injects a minimal synthetic thinking block when keep_thinking=true.
* This is needed for Claude multi-turn because:
* 1. When the response comes back on next turn, filterContentArray sees the thinking block
* 2. If it has no valid signature, it injects SKIP_THOUGHT_SIGNATURE sentinel
* 3. This allows Claude to accept the conversation history without signature validation errors
*/
function injectSyntheticThinkingForKeepThinking(response: unknown): unknown {
if (!response || typeof response !== "object") {
return response;
}
const resp = response as any;
// For Claude responses (resp.content array)
if (Array.isArray(resp.content)) {
// Check if there's already a thinking block - don't inject duplicate
const hasThinking = resp.content.some((block: any) =>
block && typeof block === "object" && block.type === "thinking"
);
if (hasThinking) {
return resp;
}
const content = [{ type: "thinking", thinking: SYNTHETIC_THINKING_PLACEHOLDER }, ...resp.content];
return { ...resp, content };
}
// For Gemini responses (resp.candidates array)
if (Array.isArray(resp.candidates) && resp.candidates.length > 0) {
const candidates = resp.candidates.slice();
const first = candidates[0];
if (
first &&
typeof first === "object" &&
first.content &&
typeof first.content === "object" &&
Array.isArray(first.content.parts)
) {
// Check if there's already a thinking part
const hasThinking = first.content.parts.some((part: any) =>
part && typeof part === "object" && (part.thought === true || part.type === "thinking")
);
if (hasThinking) {
return resp;
}
const parts = [{ thought: true, text: SYNTHETIC_THINKING_PLACEHOLDER }, ...first.content.parts];
candidates[0] = { ...first, content: { ...first.content, parts } };
return { ...resp, candidates };
}
}
return resp;
}
function stripInjectedDebugFromParts(parts: unknown): unknown {
if (!Array.isArray(parts)) {
return parts;
@@ -649,10 +714,10 @@ export function prepareAntigravityRequest(
const defaultEndpoint = headerStyle === "gemini-cli" ? GEMINI_CLI_ENDPOINT : ANTIGRAVITY_ENDPOINT;
const baseEndpoint = endpointOverride ?? defaultEndpoint;
const transformedUrl = `${baseEndpoint}/v1internal:${rawAction}${streaming ? "?alt=sse" : ""}`;
const isClaude = isClaudeModel(resolved.actualModel);
const isClaudeThinking = isClaudeThinkingModel(resolved.actualModel);
// Tier-based thinking configuration from model resolver (can be overridden by variant config)
let tierThinkingBudget = resolved.thinkingBudget;
let tierThinkingLevel = resolved.thinkingLevel;
@@ -748,7 +813,7 @@ export function prepareAntigravityRequest(
requestPayload.providerOptions as Record<string, unknown> | undefined
);
const isGemini3 = effectiveModel.toLowerCase().includes("gemini-3");
if (variantConfig?.thinkingLevel && isGemini3) {
// Gemini 3 native format - use thinkingLevel directly
tierThinkingLevel = variantConfig.thinkingLevel;
@@ -757,7 +822,7 @@ export function prepareAntigravityRequest(
if (isGemini3) {
// Legacy format for Gemini 3 - convert with deprecation warning
log.warn("[Deprecated] Using thinkingBudget for Gemini 3 model. Use thinkingLevel instead.");
tierThinkingLevel = variantConfig.thinkingBudget <= 8192 ? "low"
tierThinkingLevel = variantConfig.thinkingBudget <= 8192 ? "low"
: variantConfig.thinkingBudget <= 16384 ? "medium" : "high";
tierThinkingBudget = undefined;
} else {
@@ -806,7 +871,7 @@ export function prepareAntigravityRequest(
generationConfig.candidateCount = 1;
}
requestPayload.generationConfig = generationConfig;
// Add safety settings for image generation (permissive to allow creative content)
if (!requestPayload.safetySettings) {
requestPayload.safetySettings = [
@@ -817,11 +882,11 @@ export function prepareAntigravityRequest(
{ category: "HARM_CATEGORY_CIVIC_INTEGRITY", threshold: "BLOCK_ONLY_HIGH" },
];
}
// Image models don't support tools - remove them entirely
delete requestPayload.tools;
delete requestPayload.toolConfig;
// Replace system instruction with a simple image generation prompt
// Image models should not receive agentic coding assistant instructions
requestPayload.systemInstruction = {
@@ -829,69 +894,69 @@ export function prepareAntigravityRequest(
};
} else {
const finalThinkingConfig = resolveThinkingConfig(
effectiveUserThinkingConfig,
isClaudeSonnetNonThinking ? false : (resolved.isThinkingModel ?? isThinkingCapableModel(effectiveModel)),
isClaude,
hasAssistantHistory,
);
effectiveUserThinkingConfig,
isClaudeSonnetNonThinking ? false : (resolved.isThinkingModel ?? isThinkingCapableModel(effectiveModel)),
isClaude,
hasAssistantHistory,
);
const normalizedThinking = normalizeThinkingConfig(finalThinkingConfig);
if (normalizedThinking) {
// Use tier-based thinking budget if specified via model suffix, otherwise fall back to user config
const thinkingBudget = tierThinkingBudget ?? normalizedThinking.thinkingBudget;
// Build thinking config based on model type
let thinkingConfig: Record<string, unknown>;
if (isClaudeThinking) {
// Claude uses snake_case keys
thinkingConfig = {
include_thoughts: normalizedThinking.includeThoughts ?? true,
...(typeof thinkingBudget === "number" && thinkingBudget > 0
? { thinking_budget: thinkingBudget }
: {}),
};
} else if (tierThinkingLevel) {
// Gemini 3 uses thinkingLevel string (low/medium/high)
thinkingConfig = {
includeThoughts: normalizedThinking.includeThoughts,
thinkingLevel: tierThinkingLevel,
};
} else {
// Gemini 2.5 and others use numeric budget
thinkingConfig = {
includeThoughts: normalizedThinking.includeThoughts,
...(typeof thinkingBudget === "number" && thinkingBudget > 0 ? { thinkingBudget } : {}),
};
}
const normalizedThinking = normalizeThinkingConfig(finalThinkingConfig);
if (normalizedThinking) {
// Use tier-based thinking budget if specified via model suffix, otherwise fall back to user config
const thinkingBudget = tierThinkingBudget ?? normalizedThinking.thinkingBudget;
if (rawGenerationConfig) {
rawGenerationConfig.thinkingConfig = thinkingConfig;
// Build thinking config based on model type
let thinkingConfig: Record<string, unknown>;
if (isClaudeThinking && typeof thinkingBudget === "number" && thinkingBudget > 0) {
const currentMax = (rawGenerationConfig.maxOutputTokens ?? rawGenerationConfig.max_output_tokens) as number | undefined;
if (!currentMax || currentMax <= thinkingBudget) {
rawGenerationConfig.maxOutputTokens = CLAUDE_THINKING_MAX_OUTPUT_TOKENS;
if (rawGenerationConfig.max_output_tokens !== undefined) {
delete rawGenerationConfig.max_output_tokens;
if (isClaudeThinking) {
// Claude uses snake_case keys
thinkingConfig = {
include_thoughts: normalizedThinking.includeThoughts ?? true,
...(typeof thinkingBudget === "number" && thinkingBudget > 0
? { thinking_budget: thinkingBudget }
: {}),
};
} else if (tierThinkingLevel) {
// Gemini 3 uses thinkingLevel string (low/medium/high)
thinkingConfig = {
includeThoughts: normalizedThinking.includeThoughts,
thinkingLevel: tierThinkingLevel,
};
} else {
// Gemini 2.5 and others use numeric budget
thinkingConfig = {
includeThoughts: normalizedThinking.includeThoughts,
...(typeof thinkingBudget === "number" && thinkingBudget > 0 ? { thinkingBudget } : {}),
};
}
if (rawGenerationConfig) {
rawGenerationConfig.thinkingConfig = thinkingConfig;
if (isClaudeThinking && typeof thinkingBudget === "number" && thinkingBudget > 0) {
const currentMax = (rawGenerationConfig.maxOutputTokens ?? rawGenerationConfig.max_output_tokens) as number | undefined;
if (!currentMax || currentMax <= thinkingBudget) {
rawGenerationConfig.maxOutputTokens = CLAUDE_THINKING_MAX_OUTPUT_TOKENS;
if (rawGenerationConfig.max_output_tokens !== undefined) {
delete rawGenerationConfig.max_output_tokens;
}
}
}
}
requestPayload.generationConfig = rawGenerationConfig;
} else {
const generationConfig: Record<string, unknown> = { thinkingConfig };
if (isClaudeThinking && typeof thinkingBudget === "number" && thinkingBudget > 0) {
generationConfig.maxOutputTokens = CLAUDE_THINKING_MAX_OUTPUT_TOKENS;
}
requestPayload.generationConfig = generationConfig;
}
} else if (rawGenerationConfig?.thinkingConfig) {
delete rawGenerationConfig.thinkingConfig;
requestPayload.generationConfig = rawGenerationConfig;
} else {
const generationConfig: Record<string, unknown> = { thinkingConfig };
if (isClaudeThinking && typeof thinkingBudget === "number" && thinkingBudget > 0) {
generationConfig.maxOutputTokens = CLAUDE_THINKING_MAX_OUTPUT_TOKENS;
}
requestPayload.generationConfig = generationConfig;
}
} else if (rawGenerationConfig?.thinkingConfig) {
delete rawGenerationConfig.thinkingConfig;
requestPayload.generationConfig = rawGenerationConfig;
}
} // End of else block for non-image models
// Clean up thinking fields from extra_body
@@ -1101,9 +1166,9 @@ export function prepareAntigravityRequest(
} else {
// Gemini-specific tool normalization and feature injection
// Resolve Google Search config: Variant takes precedence over global default
const effectiveSearchConfig: GoogleSearchConfig | undefined =
const effectiveSearchConfig: GoogleSearchConfig | undefined =
variantConfig?.googleSearch ?? options?.googleSearch;
const geminiResult = applyGeminiTransforms(requestPayload, {
model: effectiveModel,
normalizedThinking: undefined, // Thinking config already applied above (lines 816-880)
@@ -1111,7 +1176,7 @@ export function prepareAntigravityRequest(
tierThinkingLevel: tierThinkingLevel as ThinkingTier | undefined,
googleSearch: effectiveSearchConfig,
});
toolDebugMissing = geminiResult.toolDebugMissing;
toolDebugSummaries.push(...geminiResult.toolDebugSummaries);
}
@@ -1488,6 +1553,7 @@ export async function transformAntigravityResponse(
{
onCacheSignature: cacheSignature,
onInjectDebug: injectDebugThinking,
onInjectSyntheticThinking: injectSyntheticThinkingForKeepThinking,
transformThinkingParts,
},
{
@@ -1495,6 +1561,9 @@ export async function transformAntigravityResponse(
debugText,
cacheSignatures,
displayedThinkingHashes: effectiveModel && isGemini3Model(effectiveModel) ? sessionDisplayedThinkingHashes : undefined,
// Inject synthetic thinking when keep_thinking=true but debug is off
// This ensures Claude multi-turn works without needing OPENCODE_ANTIGRAVITY_DEBUG=2
injectSyntheticThinking: getKeepThinking() && !debugText,
},
);
return new Response(response.body.pipeThrough(streamingTransformer), {
@@ -1618,7 +1687,15 @@ export async function transformAntigravityResponse(
}
if (effectiveBody?.response !== undefined) {
const responseBody = debugText ? injectDebugThinking(effectiveBody.response, debugText) : effectiveBody.response;
let responseBody: unknown = effectiveBody.response;
// Inject debug thinking if debug mode is on
if (debugText) {
responseBody = injectDebugThinking(responseBody, debugText);
}
// Inject synthetic thinking if keep_thinking=true but no debug injection
else if (getKeepThinking()) {
responseBody = injectSyntheticThinkingForKeepThinking(responseBody);
}
const transformed = transformThinkingParts(responseBody);
return new Response(JSON.stringify(transformed), init);
}

View File

@@ -1,4 +1,5 @@
{
"exclude": ["opencode-better-antigravity-auth", "CLIProxyAPI"],
"compilerOptions": {
// Environment setup & latest features
"lib": ["ESNext", "DOM"],