@cortexkit/opencode-antigravity-auth 1.0.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +225 -553
- package/dist/cli.js +3790 -0
- package/dist/cli.js.map +7 -0
- package/dist/index.d.ts +1 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18951 -13145
- package/dist/index.js.map +4 -4
- package/dist/src/antigravity/oauth.d.ts +1 -1
- package/dist/src/antigravity/oauth.js +1 -1
- package/dist/src/cli.d.ts +21 -0
- package/dist/src/cli.d.ts.map +1 -0
- package/dist/src/cli.js +237 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/constants.d.ts +1 -1
- package/dist/src/constants.js +1 -1
- package/dist/src/hooks/auto-update-checker/cache.d.ts.map +1 -1
- package/dist/src/hooks/auto-update-checker/cache.js +13 -13
- package/dist/src/hooks/auto-update-checker/cache.js.map +1 -1
- package/dist/src/hooks/auto-update-checker/checker.d.ts +1 -1
- package/dist/src/hooks/auto-update-checker/checker.d.ts.map +1 -1
- package/dist/src/hooks/auto-update-checker/checker.js +75 -51
- package/dist/src/hooks/auto-update-checker/checker.js.map +1 -1
- package/dist/src/hooks/auto-update-checker/constants.d.ts.map +1 -1
- package/dist/src/hooks/auto-update-checker/constants.js +12 -12
- package/dist/src/hooks/auto-update-checker/constants.js.map +1 -1
- package/dist/src/hooks/auto-update-checker/index.d.ts +6 -6
- package/dist/src/hooks/auto-update-checker/index.d.ts.map +1 -1
- package/dist/src/hooks/auto-update-checker/index.js +17 -17
- package/dist/src/hooks/auto-update-checker/index.js.map +1 -1
- package/dist/src/hooks/auto-update-checker/logging.js +2 -2
- package/dist/src/hooks/auto-update-checker/logging.js.map +1 -1
- package/dist/src/hooks/auto-update-checker/types.d.ts.map +1 -1
- package/dist/src/plugin/account-access.d.ts +99 -0
- package/dist/src/plugin/account-access.d.ts.map +1 -0
- package/dist/src/plugin/account-access.js +480 -0
- package/dist/src/plugin/account-access.js.map +1 -0
- package/dist/src/plugin/accounts.d.ts +7 -236
- package/dist/src/plugin/accounts.d.ts.map +1 -1
- package/dist/src/plugin/accounts.js +31 -1166
- package/dist/src/plugin/accounts.js.map +1 -1
- package/dist/src/plugin/agy-request-metadata.d.ts +2 -0
- package/dist/src/plugin/agy-request-metadata.d.ts.map +1 -0
- package/dist/src/plugin/agy-request-metadata.js +2 -0
- package/dist/src/plugin/agy-request-metadata.js.map +1 -0
- package/dist/src/plugin/agy-transport.d.ts +1 -1
- package/dist/src/plugin/agy-transport.js +1 -1
- package/dist/src/plugin/auth-doctor.d.ts +6 -6
- package/dist/src/plugin/auth-doctor.d.ts.map +1 -1
- package/dist/src/plugin/auth-doctor.js +79 -62
- package/dist/src/plugin/auth-doctor.js.map +1 -1
- package/dist/src/plugin/auth-drift.d.ts +4 -4
- package/dist/src/plugin/auth-drift.d.ts.map +1 -1
- package/dist/src/plugin/auth-drift.js +15 -15
- package/dist/src/plugin/auth-drift.js.map +1 -1
- package/dist/src/plugin/auth-loader.d.ts +35 -0
- package/dist/src/plugin/auth-loader.d.ts.map +1 -0
- package/dist/src/plugin/auth-loader.js +144 -0
- package/dist/src/plugin/auth-loader.js.map +1 -0
- package/dist/src/plugin/auth.d.ts +1 -1
- package/dist/src/plugin/auth.js +1 -1
- package/dist/src/plugin/cache/index.d.ts +1 -1
- package/dist/src/plugin/cache/index.d.ts.map +1 -1
- package/dist/src/plugin/cache/index.js +1 -1
- package/dist/src/plugin/cache/index.js.map +1 -1
- package/dist/src/plugin/cache/signature-cache.d.ts +1 -1
- package/dist/src/plugin/cache/signature-cache.d.ts.map +1 -1
- package/dist/src/plugin/cache/signature-cache.js +23 -24
- package/dist/src/plugin/cache/signature-cache.js.map +1 -1
- package/dist/src/plugin/cache.d.ts +6 -5
- package/dist/src/plugin/cache.d.ts.map +1 -1
- package/dist/src/plugin/cache.js +21 -7
- package/dist/src/plugin/cache.js.map +1 -1
- package/dist/src/plugin/catalog.d.ts +23 -0
- package/dist/src/plugin/catalog.d.ts.map +1 -0
- package/dist/src/plugin/catalog.js +60 -0
- package/dist/src/plugin/catalog.js.map +1 -0
- package/dist/src/plugin/cli.d.ts +7 -7
- package/dist/src/plugin/cli.d.ts.map +1 -1
- package/dist/src/plugin/cli.js +62 -55
- package/dist/src/plugin/cli.js.map +1 -1
- package/dist/src/plugin/command-data.d.ts +276 -0
- package/dist/src/plugin/command-data.d.ts.map +1 -0
- package/dist/src/plugin/command-data.js +325 -0
- package/dist/src/plugin/command-data.js.map +1 -0
- package/dist/src/plugin/commands.d.ts +151 -0
- package/dist/src/plugin/commands.d.ts.map +1 -0
- package/dist/src/plugin/commands.js +639 -0
- package/dist/src/plugin/commands.js.map +1 -0
- package/dist/src/plugin/config/index.d.ts +2 -2
- package/dist/src/plugin/config/index.d.ts.map +1 -1
- package/dist/src/plugin/config/index.js +2 -2
- package/dist/src/plugin/config/index.js.map +1 -1
- package/dist/src/plugin/config/loader.d.ts +1 -1
- package/dist/src/plugin/config/loader.d.ts.map +1 -1
- package/dist/src/plugin/config/loader.js +18 -16
- package/dist/src/plugin/config/loader.js.map +1 -1
- package/dist/src/plugin/config/models.d.ts +2 -2
- package/dist/src/plugin/config/models.d.ts.map +1 -1
- package/dist/src/plugin/config/models.js +1 -1
- package/dist/src/plugin/config/models.js.map +1 -1
- package/dist/src/plugin/config/operator-settings-schema.d.ts +29 -0
- package/dist/src/plugin/config/operator-settings-schema.d.ts.map +1 -0
- package/dist/src/plugin/config/operator-settings-schema.js +28 -0
- package/dist/src/plugin/config/operator-settings-schema.js.map +1 -0
- package/dist/src/plugin/config/schema.d.ts +21 -2
- package/dist/src/plugin/config/schema.d.ts.map +1 -1
- package/dist/src/plugin/config/schema.js +85 -25
- package/dist/src/plugin/config/schema.js.map +1 -1
- package/dist/src/plugin/config/updater.d.ts.map +1 -1
- package/dist/src/plugin/config/updater.js +15 -15
- package/dist/src/plugin/config/updater.js.map +1 -1
- package/dist/src/plugin/config/writer.d.ts +34 -0
- package/dist/src/plugin/config/writer.d.ts.map +1 -0
- package/dist/src/plugin/config/writer.js +81 -0
- package/dist/src/plugin/config/writer.js.map +1 -0
- package/dist/src/plugin/core/streaming/index.d.ts +1 -1
- package/dist/src/plugin/core/streaming/index.d.ts.map +1 -1
- package/dist/src/plugin/core/streaming/index.js +1 -1
- package/dist/src/plugin/core/streaming/index.js.map +1 -1
- package/dist/src/plugin/core/streaming/transformer.d.ts.map +1 -1
- package/dist/src/plugin/core/streaming/transformer.js +85 -44
- package/dist/src/plugin/core/streaming/transformer.js.map +1 -1
- package/dist/src/plugin/core/streaming/types.d.ts.map +1 -1
- package/dist/src/plugin/debug.d.ts +3 -3
- package/dist/src/plugin/debug.d.ts.map +1 -1
- package/dist/src/plugin/debug.js +85 -47
- package/dist/src/plugin/debug.js.map +1 -1
- package/dist/src/plugin/dependencies.d.ts +129 -0
- package/dist/src/plugin/dependencies.d.ts.map +1 -0
- package/dist/src/plugin/dependencies.js +107 -0
- package/dist/src/plugin/dependencies.js.map +1 -0
- package/dist/src/plugin/errors.d.ts +27 -0
- package/dist/src/plugin/errors.d.ts.map +1 -1
- package/dist/src/plugin/errors.js +29 -3
- package/dist/src/plugin/errors.js.map +1 -1
- package/dist/src/plugin/event-handler.d.ts +53 -0
- package/dist/src/plugin/event-handler.d.ts.map +1 -0
- package/dist/src/plugin/event-handler.js +101 -0
- package/dist/src/plugin/event-handler.js.map +1 -0
- package/dist/src/plugin/fetch/retry-state.d.ts +61 -0
- package/dist/src/plugin/fetch/retry-state.d.ts.map +1 -0
- package/dist/src/plugin/fetch/retry-state.js +248 -0
- package/dist/src/plugin/fetch/retry-state.js.map +1 -0
- package/dist/src/plugin/fetch/warmup.d.ts +30 -0
- package/dist/src/plugin/fetch/warmup.d.ts.map +1 -0
- package/dist/src/plugin/fetch/warmup.js +76 -0
- package/dist/src/plugin/fetch/warmup.js.map +1 -0
- package/dist/src/plugin/fetch-interceptor.d.ts +59 -0
- package/dist/src/plugin/fetch-interceptor.d.ts.map +1 -0
- package/dist/src/plugin/fetch-interceptor.js +1387 -0
- package/dist/src/plugin/fetch-interceptor.js.map +1 -0
- package/dist/src/plugin/fetch-routing.d.ts +26 -0
- package/dist/src/plugin/fetch-routing.d.ts.map +1 -0
- package/dist/src/plugin/fetch-routing.js +99 -0
- package/dist/src/plugin/fetch-routing.js.map +1 -0
- package/dist/src/plugin/fingerprint.d.ts +1 -1
- package/dist/src/plugin/fingerprint.js +1 -1
- package/dist/src/plugin/gemini-dump.d.ts +5 -5
- package/dist/src/plugin/gemini-dump.d.ts.map +1 -1
- package/dist/src/plugin/gemini-dump.js +109 -55
- package/dist/src/plugin/gemini-dump.js.map +1 -1
- package/dist/src/plugin/google-search-tool.d.ts +9 -0
- package/dist/src/plugin/google-search-tool.d.ts.map +1 -0
- package/dist/src/plugin/google-search-tool.js +56 -0
- package/dist/src/plugin/google-search-tool.js.map +1 -0
- package/dist/src/plugin/image-saver.d.ts +2 -21
- package/dist/src/plugin/image-saver.d.ts.map +1 -1
- package/dist/src/plugin/image-saver.js +22 -56
- package/dist/src/plugin/image-saver.js.map +1 -1
- package/dist/src/plugin/index.d.ts +23 -0
- package/dist/src/plugin/index.d.ts.map +1 -0
- package/dist/src/plugin/index.js +308 -0
- package/dist/src/plugin/index.js.map +1 -0
- package/dist/src/plugin/killswitch.d.ts +94 -0
- package/dist/src/plugin/killswitch.d.ts.map +1 -0
- package/dist/src/plugin/killswitch.js +194 -0
- package/dist/src/plugin/killswitch.js.map +1 -0
- package/dist/src/plugin/lifecycle.d.ts +40 -0
- package/dist/src/plugin/lifecycle.d.ts.map +1 -0
- package/dist/src/plugin/lifecycle.js +73 -0
- package/dist/src/plugin/lifecycle.js.map +1 -0
- package/dist/src/plugin/logger.d.ts +10 -1
- package/dist/src/plugin/logger.d.ts.map +1 -1
- package/dist/src/plugin/logger.js +38 -12
- package/dist/src/plugin/logger.js.map +1 -1
- package/dist/src/plugin/logging-utils.d.ts +35 -1
- package/dist/src/plugin/logging-utils.d.ts.map +1 -1
- package/dist/src/plugin/logging-utils.js +98 -15
- package/dist/src/plugin/logging-utils.js.map +1 -1
- package/dist/src/plugin/model-registry.d.ts +1 -1
- package/dist/src/plugin/model-registry.js +1 -1
- package/dist/src/plugin/oauth-login.d.ts +22 -0
- package/dist/src/plugin/oauth-login.d.ts.map +1 -0
- package/dist/src/plugin/oauth-login.js +37 -0
- package/dist/src/plugin/oauth-login.js.map +1 -0
- package/dist/src/plugin/oauth-methods.d.ts +42 -0
- package/dist/src/plugin/oauth-methods.d.ts.map +1 -0
- package/dist/src/plugin/oauth-methods.js +1284 -0
- package/dist/src/plugin/oauth-methods.js.map +1 -0
- package/dist/src/plugin/operator-settings.d.ts +40 -0
- package/dist/src/plugin/operator-settings.d.ts.map +1 -0
- package/dist/src/plugin/operator-settings.js +148 -0
- package/dist/src/plugin/operator-settings.js.map +1 -0
- package/dist/src/plugin/persist-account-pool.d.ts +37 -0
- package/dist/src/plugin/persist-account-pool.d.ts.map +1 -0
- package/dist/src/plugin/persist-account-pool.js +142 -0
- package/dist/src/plugin/persist-account-pool.js.map +1 -0
- package/dist/src/plugin/project.d.ts +1 -1
- package/dist/src/plugin/project.js +1 -1
- package/dist/src/plugin/prompt-context.d.ts.map +1 -1
- package/dist/src/plugin/prompt-context.js +11 -11
- package/dist/src/plugin/prompt-context.js.map +1 -1
- package/dist/src/plugin/quota.d.ts +97 -41
- package/dist/src/plugin/quota.d.ts.map +1 -1
- package/dist/src/plugin/quota.js +273 -285
- package/dist/src/plugin/quota.js.map +1 -1
- package/dist/src/plugin/recovery/constants.d.ts.map +1 -1
- package/dist/src/plugin/recovery/constants.js +24 -15
- package/dist/src/plugin/recovery/constants.js.map +1 -1
- package/dist/src/plugin/recovery/index.d.ts +3 -3
- package/dist/src/plugin/recovery/index.d.ts.map +1 -1
- package/dist/src/plugin/recovery/index.js +3 -3
- package/dist/src/plugin/recovery/index.js.map +1 -1
- package/dist/src/plugin/recovery/storage.d.ts +1 -1
- package/dist/src/plugin/recovery/storage.d.ts.map +1 -1
- package/dist/src/plugin/recovery/storage.js +31 -39
- package/dist/src/plugin/recovery/storage.js.map +1 -1
- package/dist/src/plugin/recovery/types.d.ts +12 -12
- package/dist/src/plugin/recovery/types.d.ts.map +1 -1
- package/dist/src/plugin/recovery.d.ts +3 -3
- package/dist/src/plugin/recovery.d.ts.map +1 -1
- package/dist/src/plugin/recovery.js +62 -58
- package/dist/src/plugin/recovery.js.map +1 -1
- package/dist/src/plugin/refresh-queue.d.ts +5 -2
- package/dist/src/plugin/refresh-queue.d.ts.map +1 -1
- package/dist/src/plugin/refresh-queue.js +37 -15
- package/dist/src/plugin/refresh-queue.js.map +1 -1
- package/dist/src/plugin/request-helpers.d.ts +2 -1
- package/dist/src/plugin/request-helpers.d.ts.map +1 -1
- package/dist/src/plugin/request-helpers.js +458 -354
- package/dist/src/plugin/request-helpers.js.map +1 -1
- package/dist/src/plugin/request.d.ts +12 -6
- package/dist/src/plugin/request.d.ts.map +1 -1
- package/dist/src/plugin/request.js +801 -390
- package/dist/src/plugin/request.js.map +1 -1
- package/dist/src/plugin/rotation.d.ts +1 -168
- package/dist/src/plugin/rotation.d.ts.map +1 -1
- package/dist/src/plugin/rotation.js +1 -327
- package/dist/src/plugin/rotation.js.map +1 -1
- package/dist/src/plugin/search.d.ts.map +1 -1
- package/dist/src/plugin/search.js +39 -35
- package/dist/src/plugin/search.js.map +1 -1
- package/dist/src/plugin/server.d.ts +1 -1
- package/dist/src/plugin/server.d.ts.map +1 -1
- package/dist/src/plugin/server.js +42 -37
- package/dist/src/plugin/server.js.map +1 -1
- package/dist/src/plugin/session-context.d.ts +22 -0
- package/dist/src/plugin/session-context.d.ts.map +1 -0
- package/dist/src/plugin/session-context.js +67 -0
- package/dist/src/plugin/session-context.js.map +1 -0
- package/dist/src/plugin/storage.d.ts +59 -114
- package/dist/src/plugin/storage.d.ts.map +1 -1
- package/dist/src/plugin/storage.js +140 -474
- package/dist/src/plugin/storage.js.map +1 -1
- package/dist/src/plugin/stores/signature-store.d.ts.map +1 -1
- package/dist/src/plugin/stores/signature-store.js.map +1 -1
- package/dist/src/plugin/thinking-recovery.d.ts.map +1 -1
- package/dist/src/plugin/thinking-recovery.js +35 -33
- package/dist/src/plugin/thinking-recovery.js.map +1 -1
- package/dist/src/plugin/token.d.ts +2 -2
- package/dist/src/plugin/token.d.ts.map +1 -1
- package/dist/src/plugin/token.js +30 -21
- package/dist/src/plugin/token.js.map +1 -1
- package/dist/src/plugin/transform/cross-model-sanitizer.d.ts +1 -1
- package/dist/src/plugin/transform/cross-model-sanitizer.js +1 -1
- package/dist/src/plugin/transform/index.d.ts +1 -1
- package/dist/src/plugin/transform/index.js +1 -1
- package/dist/src/plugin/transform/model-resolver.d.ts +1 -1
- package/dist/src/plugin/transform/model-resolver.js +1 -1
- package/dist/src/plugin/transform/types.d.ts +1 -1
- package/dist/src/plugin/transform/types.js +1 -1
- package/dist/src/plugin/types.d.ts +25 -69
- package/dist/src/plugin/types.d.ts.map +1 -1
- package/dist/src/plugin/ui/ansi.d.ts.map +1 -1
- package/dist/src/plugin/ui/ansi.js.map +1 -1
- package/dist/src/plugin/ui/auth-menu.d.ts +2 -2
- package/dist/src/plugin/ui/auth-menu.d.ts.map +1 -1
- package/dist/src/plugin/ui/auth-menu.js +84 -31
- package/dist/src/plugin/ui/auth-menu.js.map +1 -1
- package/dist/src/plugin/ui/confirm.d.ts.map +1 -1
- package/dist/src/plugin/ui/confirm.js.map +1 -1
- package/dist/src/plugin/ui/model-status.d.ts +3 -3
- package/dist/src/plugin/ui/model-status.d.ts.map +1 -1
- package/dist/src/plugin/ui/model-status.js +3 -3
- package/dist/src/plugin/ui/model-status.js.map +1 -1
- package/dist/src/plugin/ui/quota-status.d.ts +4 -4
- package/dist/src/plugin/ui/quota-status.d.ts.map +1 -1
- package/dist/src/plugin/ui/quota-status.js +51 -48
- package/dist/src/plugin/ui/quota-status.js.map +1 -1
- package/dist/src/plugin/ui/select.d.ts.map +1 -1
- package/dist/src/plugin/ui/select.js +22 -12
- package/dist/src/plugin/ui/select.js.map +1 -1
- package/dist/src/plugin/version.d.ts +1 -1
- package/dist/src/plugin/version.js +1 -1
- package/dist/src/rpc/notifications.d.ts +6 -0
- package/dist/src/rpc/notifications.d.ts.map +1 -0
- package/dist/src/rpc/notifications.js +46 -0
- package/dist/src/rpc/notifications.js.map +1 -0
- package/dist/src/rpc/port-file.d.ts +13 -0
- package/dist/src/rpc/port-file.d.ts.map +1 -0
- package/dist/src/rpc/port-file.js +109 -0
- package/dist/src/rpc/port-file.js.map +1 -0
- package/dist/src/rpc/protocol.d.ts +22 -0
- package/dist/src/rpc/protocol.d.ts.map +1 -0
- package/dist/src/rpc/protocol.js +1 -0
- package/dist/src/rpc/protocol.js.map +1 -0
- package/dist/src/rpc/rpc-client.d.ts +10 -0
- package/dist/src/rpc/rpc-client.d.ts.map +1 -0
- package/dist/src/rpc/rpc-client.js +58 -0
- package/dist/src/rpc/rpc-client.js.map +1 -0
- package/dist/src/rpc/rpc-dir.d.ts +4 -0
- package/dist/src/rpc/rpc-dir.d.ts.map +1 -0
- package/dist/src/rpc/rpc-dir.js +19 -0
- package/dist/src/rpc/rpc-dir.js.map +1 -0
- package/dist/src/rpc/rpc-server.d.ts +13 -0
- package/dist/src/rpc/rpc-server.d.ts.map +1 -0
- package/dist/src/rpc/rpc-server.js +231 -0
- package/dist/src/rpc/rpc-server.js.map +1 -0
- package/dist/src/sidebar-state.d.ts +240 -0
- package/dist/src/sidebar-state.d.ts.map +1 -0
- package/dist/src/sidebar-state.js +520 -0
- package/dist/src/sidebar-state.js.map +1 -0
- package/dist/src/tui/command-dialogs.d.ts +61 -0
- package/dist/src/tui/command-dialogs.d.ts.map +1 -0
- package/dist/src/tui/command-dialogs.js +497 -0
- package/dist/src/tui/command-dialogs.js.map +1 -0
- package/dist/src/tui/file-logger.d.ts +41 -0
- package/dist/src/tui/file-logger.d.ts.map +1 -0
- package/dist/src/tui/file-logger.js +114 -0
- package/dist/src/tui/file-logger.js.map +1 -0
- package/dist/src/tui-preferences.d.ts +40 -0
- package/dist/src/tui-preferences.d.ts.map +1 -0
- package/dist/src/tui-preferences.js +328 -0
- package/dist/src/tui-preferences.js.map +1 -0
- package/dist/src/tui.d.ts +68 -0
- package/dist/src/tui.d.ts.map +1 -0
- package/dist/src/tui.js +662 -0
- package/dist/src/tui.js.map +1 -0
- package/package.json +46 -22
- package/src/plugin/command-data.ts +651 -0
- package/src/rpc/port-file.ts +143 -0
- package/src/rpc/protocol.ts +31 -0
- package/src/rpc/rpc-client.ts +102 -0
- package/src/rpc/rpc-dir.ts +24 -0
- package/src/sidebar-state.ts +717 -0
- package/src/tui/command-dialogs.tsx +699 -0
- package/src/tui/entry.mjs +74 -0
- package/src/tui/file-logger.ts +145 -0
- package/src/tui-compiled/plugin/command-data.ts +651 -0
- package/src/tui-compiled/rpc/port-file.ts +143 -0
- package/src/tui-compiled/rpc/protocol.ts +31 -0
- package/src/tui-compiled/rpc/rpc-client.ts +102 -0
- package/src/tui-compiled/rpc/rpc-dir.ts +24 -0
- package/src/tui-compiled/sidebar-state.ts +717 -0
- package/src/tui-compiled/tui/command-dialogs.tsx +659 -0
- package/src/tui-compiled/tui/file-logger.ts +145 -0
- package/src/tui-compiled/tui-preferences.ts +416 -0
- package/src/tui-compiled/tui.tsx +1150 -0
- package/src/tui-preferences.ts +416 -0
- package/src/tui.tsx +1117 -0
- package/dist/src/plugin.d.ts +0 -32
- package/dist/src/plugin.d.ts.map +0 -1
- package/dist/src/plugin.js +0 -3151
- package/dist/src/plugin.js.map +0 -1
|
@@ -1,54 +1,118 @@
|
|
|
1
|
-
import crypto from
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { DEBUG_MESSAGE_PREFIX,
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
|
|
18
|
-
const log = createLogger("request");
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
import { ANTIGRAVITY_DEFAULT_PROJECT_ID, ANTIGRAVITY_ENDPOINT, CLAUDE_DESCRIPTION_PROMPT, CLAUDE_TOOL_SYSTEM_INSTRUCTION, EMPTY_SCHEMA_PLACEHOLDER_DESCRIPTION, EMPTY_SCHEMA_PLACEHOLDER_NAME, GEMINI_CLI_ENDPOINT, getRandomizedHeaders, SKIP_THOUGHT_SIGNATURE, } from '../constants';
|
|
3
|
+
import { buildAgyAgentRequestMetadata, createAgyRequestSessionContext, orderAgyRequestPayloadInPlace, } from './agy-request-metadata';
|
|
4
|
+
import { cacheSignature, getCachedSignature } from './cache';
|
|
5
|
+
import { getKeepThinking } from './config';
|
|
6
|
+
import { createStreamingTransformer, transformSseLine, transformStreamingPayload, } from './core/streaming';
|
|
7
|
+
import { DEBUG_MESSAGE_PREFIX, isDebugTuiEnabled, logAntigravityDebugResponse, logCacheStats, } from './debug';
|
|
8
|
+
import { buildFingerprintHeaders, getSessionFingerprint, } from './fingerprint';
|
|
9
|
+
import { appendGeminiDumpResponseText, createGeminiDumpResponseTransform, noteGeminiDumpResponse, } from './gemini-dump';
|
|
10
|
+
import { createLogger } from './logger';
|
|
11
|
+
import { detectErrorType } from './recovery';
|
|
12
|
+
import { applyToolPairingFixes, cleanJSONSchemaForAntigravity, deepFilterThinkingBlocks, extractThinkingConfig, extractUsageFromSsePayload, extractUsageMetadata, extractVariantThinkingConfig, fixToolResponseGrouping, injectParameterSignatures, injectToolHardeningInstruction, isThinkingCapableModel, normalizeThinkingConfig, parseAntigravityApiBody, resolveThinkingConfig, rewriteAntigravityPreviewAccessError, transformThinkingParts, validateAndFixClaudeToolPairing, } from './request-helpers';
|
|
13
|
+
import { defaultSignatureStore } from './stores/signature-store';
|
|
14
|
+
import { analyzeConversationState, closeToolLoopForThinking, needsThinkingRecovery, } from './thinking-recovery';
|
|
15
|
+
import { appendClaudeThinkingHint, applyGeminiTransforms, buildImageGenerationConfig, computeClaudeMaxOutputTokens, isClaudeModel, isClaudeThinkingModel, isGemini3Model, isImageGenerationModel, resolveModelForHeaderStyle, } from './transform';
|
|
16
|
+
import { sanitizeCrossModelPayloadInPlace } from './transform/cross-model-sanitizer';
|
|
17
|
+
const log = createLogger('request');
|
|
19
18
|
const PLUGIN_SESSION_ID = `-${crypto.randomUUID()}`;
|
|
19
|
+
const DEFAULT_AGY_REQUEST_SESSION = createAgyRequestSessionContext('');
|
|
20
20
|
const sessionDisplayedThinkingHashes = new Set();
|
|
21
21
|
const MIN_SIGNATURE_LENGTH = 50;
|
|
22
|
+
const AGY_CLAUDE_THINKING_BUDGET = 1024;
|
|
22
23
|
const ANTIGRAVITY_ENVELOPE_FIELD_ORDER = [
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
"enabledCreditTypes",
|
|
24
|
+
'project',
|
|
25
|
+
'requestId',
|
|
26
|
+
'request',
|
|
27
|
+
'model',
|
|
28
|
+
'userAgent',
|
|
29
|
+
'requestType',
|
|
30
30
|
];
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
const OPENCODE_TITLE_PROMPT_PREFIX = 'Generate a title for this conversation:';
|
|
32
|
+
function getOpenCodeTitleSourceText(payload) {
|
|
33
|
+
if (!Array.isArray(payload.contents)) {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
const texts = payload.contents.flatMap((content) => {
|
|
37
|
+
if (!content || typeof content !== 'object') {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
const parts = content.parts;
|
|
41
|
+
if (!Array.isArray(parts)) {
|
|
42
|
+
return [];
|
|
43
|
+
}
|
|
44
|
+
return parts.flatMap((part) => part &&
|
|
45
|
+
typeof part === 'object' &&
|
|
46
|
+
typeof part.text === 'string'
|
|
47
|
+
? [part.text]
|
|
48
|
+
: []);
|
|
49
|
+
});
|
|
50
|
+
const promptIndex = texts.findIndex((text) => text.startsWith(OPENCODE_TITLE_PROMPT_PREFIX));
|
|
51
|
+
return promptIndex >= 0
|
|
52
|
+
? texts.slice(promptIndex + 1).find((text) => text.trim().length > 0)
|
|
53
|
+
: undefined;
|
|
54
|
+
}
|
|
55
|
+
function isOpenCodeTitleGenerationRequest(payload) {
|
|
56
|
+
return getOpenCodeTitleSourceText(payload) !== undefined;
|
|
57
|
+
}
|
|
58
|
+
function formatLocalImageTitle(sourceText) {
|
|
59
|
+
const normalized = sourceText
|
|
60
|
+
.trim()
|
|
61
|
+
.replace(/^(["“])(.*)(["”])$/s, '$2')
|
|
62
|
+
.replace(/\s+/g, ' ');
|
|
63
|
+
const characters = Array.from(normalized);
|
|
64
|
+
if (characters.length <= 50) {
|
|
65
|
+
return normalized || 'Image generation';
|
|
66
|
+
}
|
|
67
|
+
return `${characters.slice(0, 47).join('').trimEnd()}...`;
|
|
68
|
+
}
|
|
69
|
+
export function getImageModelLocalTitle(input, init) {
|
|
70
|
+
const url = fetchInputToUrl(input);
|
|
71
|
+
if (!/\/models\/[^/:]*(?:image|imagen)[^/:]*:streamGenerateContent/i.test(url)) {
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
const body = init?.body;
|
|
75
|
+
const bodyText = typeof body === 'string'
|
|
76
|
+
? body
|
|
77
|
+
: body instanceof Uint8Array
|
|
78
|
+
? new TextDecoder().decode(body)
|
|
79
|
+
: '';
|
|
80
|
+
if (!bodyText) {
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
const sourceText = getOpenCodeTitleSourceText(JSON.parse(bodyText));
|
|
85
|
+
return sourceText === undefined
|
|
86
|
+
? undefined
|
|
87
|
+
: formatLocalImageTitle(sourceText);
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return undefined;
|
|
34
91
|
}
|
|
35
|
-
return `agent/${crypto.randomUUID()}/${Date.now()}/${crypto.randomUUID()}/2`;
|
|
36
92
|
}
|
|
37
93
|
function getAgyMaxOutputTokens(model) {
|
|
38
94
|
const lower = model.toLowerCase();
|
|
39
|
-
if (lower ===
|
|
95
|
+
if (lower === 'gemini-3.5-flash-low' ||
|
|
96
|
+
lower === 'gemini-3.5-flash-extra-low' ||
|
|
97
|
+
lower === 'gemini-3-flash-agent' ||
|
|
98
|
+
lower === 'gemini-3.6-flash-low' ||
|
|
99
|
+
lower === 'gemini-3.6-flash-medium' ||
|
|
100
|
+
lower === 'gemini-3.6-flash-high') {
|
|
40
101
|
return 65536;
|
|
41
102
|
}
|
|
42
|
-
if (lower ===
|
|
103
|
+
if (lower === 'gemini-3.1-pro-low' || lower === 'gemini-pro-agent') {
|
|
43
104
|
return 65535;
|
|
44
105
|
}
|
|
45
|
-
if (lower ===
|
|
106
|
+
if (lower === 'claude-sonnet-4-6' || lower === 'claude-opus-4-6-thinking') {
|
|
46
107
|
return 64000;
|
|
47
108
|
}
|
|
109
|
+
if (lower === 'gpt-oss-120b-medium') {
|
|
110
|
+
return 32768;
|
|
111
|
+
}
|
|
48
112
|
return undefined;
|
|
49
113
|
}
|
|
50
114
|
function applyAgyGenerationDefaults(model, generationConfig, headerStyle) {
|
|
51
|
-
if (headerStyle !==
|
|
115
|
+
if (headerStyle !== 'antigravity') {
|
|
52
116
|
return;
|
|
53
117
|
}
|
|
54
118
|
const maxOutputTokens = getAgyMaxOutputTokens(model);
|
|
@@ -72,13 +136,13 @@ function orderAntigravityEnvelope(body) {
|
|
|
72
136
|
return ordered;
|
|
73
137
|
}
|
|
74
138
|
function buildSignatureSessionKey(sessionId, model, conversationKey, projectKey) {
|
|
75
|
-
const modelKey = typeof model ===
|
|
76
|
-
const projectPart = typeof projectKey ===
|
|
139
|
+
const modelKey = typeof model === 'string' && model.trim() ? model.toLowerCase() : 'unknown';
|
|
140
|
+
const projectPart = typeof projectKey === 'string' && projectKey.trim()
|
|
77
141
|
? projectKey.trim()
|
|
78
|
-
:
|
|
79
|
-
const conversationPart = typeof conversationKey ===
|
|
142
|
+
: 'default';
|
|
143
|
+
const conversationPart = typeof conversationKey === 'string' && conversationKey.trim()
|
|
80
144
|
? conversationKey.trim()
|
|
81
|
-
:
|
|
145
|
+
: 'default';
|
|
82
146
|
return `${sessionId}:${modelKey}:${projectPart}:${conversationPart}`;
|
|
83
147
|
}
|
|
84
148
|
/**
|
|
@@ -90,22 +154,30 @@ function buildSignatureSessionKey(sessionId, model, conversationKey, projectKey)
|
|
|
90
154
|
* by checking for JSON Schema primitive types. Everything else that's
|
|
91
155
|
* a non-string `thinking` value gets flattened to "".
|
|
92
156
|
*/
|
|
93
|
-
const JSON_SCHEMA_TYPES = new Set([
|
|
157
|
+
const JSON_SCHEMA_TYPES = new Set([
|
|
158
|
+
'boolean',
|
|
159
|
+
'string',
|
|
160
|
+
'number',
|
|
161
|
+
'integer',
|
|
162
|
+
'array',
|
|
163
|
+
'object',
|
|
164
|
+
]);
|
|
94
165
|
function thinkingSafeReplacer(key, value) {
|
|
95
|
-
if (key ===
|
|
96
|
-
// Preserve
|
|
166
|
+
if (key === 'thinking' && typeof value === 'object' && value !== null) {
|
|
167
|
+
// Preserve tool schemas before and after Gemini uppercases their type.
|
|
97
168
|
const rec = value;
|
|
98
|
-
if (typeof rec.type ===
|
|
169
|
+
if (typeof rec.type === 'string' &&
|
|
170
|
+
JSON_SCHEMA_TYPES.has(rec.type.toLowerCase())) {
|
|
99
171
|
return value;
|
|
100
172
|
}
|
|
101
173
|
// Flatten any non-string, non-Schema thinking to empty string
|
|
102
|
-
return
|
|
174
|
+
return '';
|
|
103
175
|
}
|
|
104
176
|
return value;
|
|
105
177
|
}
|
|
106
178
|
/** Stringify with built-in thinking sanitization. Impossible to bypass. */
|
|
107
179
|
function ensureThinkingFields(obj) {
|
|
108
|
-
if (!obj || typeof obj !==
|
|
180
|
+
if (!obj || typeof obj !== 'object')
|
|
109
181
|
return;
|
|
110
182
|
if (Array.isArray(obj)) {
|
|
111
183
|
for (const item of obj)
|
|
@@ -116,11 +188,11 @@ function ensureThinkingFields(obj) {
|
|
|
116
188
|
// Fix: check for missing OR undefined OR non-string thinking field.
|
|
117
189
|
// JSON.stringify silently drops undefined values, so key-exists-but-undefined
|
|
118
190
|
// produces { type: "thinking", signature: "..." } with NO thinking field.
|
|
119
|
-
if (rec.type ===
|
|
120
|
-
rec.thinking =
|
|
191
|
+
if (rec.type === 'thinking' && typeof rec.thinking !== 'string') {
|
|
192
|
+
rec.thinking = '';
|
|
121
193
|
}
|
|
122
|
-
if (rec.thought === true && typeof rec.text !==
|
|
123
|
-
rec.text =
|
|
194
|
+
if (rec.thought === true && typeof rec.text !== 'string') {
|
|
195
|
+
rec.text = '';
|
|
124
196
|
}
|
|
125
197
|
for (const val of Object.values(rec)) {
|
|
126
198
|
ensureThinkingFields(val);
|
|
@@ -131,59 +203,67 @@ function safeStringify(obj) {
|
|
|
131
203
|
return JSON.stringify(obj, thinkingSafeReplacer);
|
|
132
204
|
}
|
|
133
205
|
function shouldCacheThinkingSignatures(model) {
|
|
134
|
-
if (typeof model !==
|
|
206
|
+
if (typeof model !== 'string')
|
|
135
207
|
return false;
|
|
136
208
|
const lower = model.toLowerCase();
|
|
137
209
|
// Both Claude and Gemini 3 models require thought signature caching
|
|
138
210
|
// for multi-turn conversations with function calling
|
|
139
|
-
return lower.includes(
|
|
211
|
+
return lower.includes('claude') || lower.includes('gemini-3');
|
|
140
212
|
}
|
|
141
213
|
function hashConversationSeed(seed) {
|
|
142
|
-
return crypto
|
|
214
|
+
return crypto
|
|
215
|
+
.createHash('sha256')
|
|
216
|
+
.update(seed, 'utf8')
|
|
217
|
+
.digest('hex')
|
|
218
|
+
.slice(0, 16);
|
|
143
219
|
}
|
|
144
220
|
function extractTextFromContent(content) {
|
|
145
|
-
if (typeof content ===
|
|
221
|
+
if (typeof content === 'string') {
|
|
146
222
|
return content;
|
|
147
223
|
}
|
|
148
224
|
if (!Array.isArray(content)) {
|
|
149
|
-
return
|
|
225
|
+
return '';
|
|
150
226
|
}
|
|
151
227
|
for (const block of content) {
|
|
152
|
-
if (!block || typeof block !==
|
|
228
|
+
if (!block || typeof block !== 'object') {
|
|
153
229
|
continue;
|
|
154
230
|
}
|
|
155
231
|
const anyBlock = block;
|
|
156
|
-
if (typeof anyBlock.text ===
|
|
232
|
+
if (typeof anyBlock.text === 'string') {
|
|
157
233
|
return anyBlock.text;
|
|
158
234
|
}
|
|
159
|
-
if (anyBlock.text &&
|
|
235
|
+
if (anyBlock.text &&
|
|
236
|
+
typeof anyBlock.text === 'object' &&
|
|
237
|
+
typeof anyBlock.text.text === 'string') {
|
|
160
238
|
return anyBlock.text.text;
|
|
161
239
|
}
|
|
162
240
|
}
|
|
163
|
-
return
|
|
241
|
+
return '';
|
|
164
242
|
}
|
|
165
243
|
function extractConversationSeedFromMessages(messages) {
|
|
166
|
-
const system = messages.find((message) => message?.role ===
|
|
167
|
-
const users = messages.filter((message) => message?.role ===
|
|
244
|
+
const system = messages.find((message) => message?.role === 'system');
|
|
245
|
+
const users = messages.filter((message) => message?.role === 'user');
|
|
168
246
|
const firstUser = users[0];
|
|
169
247
|
const lastUser = users.length > 0 ? users[users.length - 1] : undefined;
|
|
170
|
-
const systemText = system ? extractTextFromContent(system.content) :
|
|
171
|
-
const userText = firstUser ? extractTextFromContent(firstUser.content) :
|
|
172
|
-
const fallbackUserText = !userText && lastUser ? extractTextFromContent(lastUser.content) :
|
|
173
|
-
return [systemText, userText || fallbackUserText].filter(Boolean).join(
|
|
248
|
+
const systemText = system ? extractTextFromContent(system.content) : '';
|
|
249
|
+
const userText = firstUser ? extractTextFromContent(firstUser.content) : '';
|
|
250
|
+
const fallbackUserText = !userText && lastUser ? extractTextFromContent(lastUser.content) : '';
|
|
251
|
+
return [systemText, userText || fallbackUserText].filter(Boolean).join('|');
|
|
174
252
|
}
|
|
175
253
|
function extractConversationSeedFromContents(contents) {
|
|
176
|
-
const users = contents.filter((content) => content?.role ===
|
|
254
|
+
const users = contents.filter((content) => content?.role === 'user');
|
|
177
255
|
const firstUser = users[0];
|
|
178
256
|
const lastUser = users.length > 0 ? users[users.length - 1] : undefined;
|
|
179
|
-
const primaryUser = firstUser && Array.isArray(firstUser.parts)
|
|
257
|
+
const primaryUser = firstUser && Array.isArray(firstUser.parts)
|
|
258
|
+
? extractTextFromContent(firstUser.parts)
|
|
259
|
+
: '';
|
|
180
260
|
if (primaryUser) {
|
|
181
261
|
return primaryUser;
|
|
182
262
|
}
|
|
183
263
|
if (lastUser && Array.isArray(lastUser.parts)) {
|
|
184
264
|
return extractTextFromContent(lastUser.parts);
|
|
185
265
|
}
|
|
186
|
-
return
|
|
266
|
+
return '';
|
|
187
267
|
}
|
|
188
268
|
function resolveConversationKey(requestPayload) {
|
|
189
269
|
const anyPayload = requestPayload;
|
|
@@ -202,20 +282,20 @@ function resolveConversationKey(requestPayload) {
|
|
|
202
282
|
anyPayload.metadata?.threadId,
|
|
203
283
|
];
|
|
204
284
|
for (const candidate of candidates) {
|
|
205
|
-
if (typeof candidate ===
|
|
285
|
+
if (typeof candidate === 'string' && candidate.trim()) {
|
|
206
286
|
return candidate.trim();
|
|
207
287
|
}
|
|
208
288
|
}
|
|
209
|
-
const systemSeed = extractTextFromContent(anyPayload.systemInstruction?.parts
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
289
|
+
const systemSeed = extractTextFromContent(anyPayload.systemInstruction?.parts ??
|
|
290
|
+
anyPayload.systemInstruction ??
|
|
291
|
+
anyPayload.system ??
|
|
292
|
+
anyPayload.system_instruction);
|
|
213
293
|
const messageSeed = Array.isArray(anyPayload.messages)
|
|
214
294
|
? extractConversationSeedFromMessages(anyPayload.messages)
|
|
215
295
|
: Array.isArray(anyPayload.contents)
|
|
216
296
|
? extractConversationSeedFromContents(anyPayload.contents)
|
|
217
|
-
:
|
|
218
|
-
const seed = [systemSeed, messageSeed].filter(Boolean).join(
|
|
297
|
+
: '';
|
|
298
|
+
const seed = [systemSeed, messageSeed].filter(Boolean).join('|');
|
|
219
299
|
if (!seed) {
|
|
220
300
|
return undefined;
|
|
221
301
|
}
|
|
@@ -231,10 +311,10 @@ function resolveConversationKeyFromRequests(requestObjects) {
|
|
|
231
311
|
return undefined;
|
|
232
312
|
}
|
|
233
313
|
function resolveProjectKey(candidate, fallback) {
|
|
234
|
-
if (typeof candidate ===
|
|
314
|
+
if (typeof candidate === 'string' && candidate.trim()) {
|
|
235
315
|
return candidate.trim();
|
|
236
316
|
}
|
|
237
|
-
if (typeof fallback ===
|
|
317
|
+
if (typeof fallback === 'string' && fallback.trim()) {
|
|
238
318
|
return fallback.trim();
|
|
239
319
|
}
|
|
240
320
|
return undefined;
|
|
@@ -245,10 +325,10 @@ function formatDebugLinesForThinking(lines) {
|
|
|
245
325
|
.filter((line) => line.length > 0)
|
|
246
326
|
.slice(-50);
|
|
247
327
|
const prelude = `[ThinkingResolution] source=debug_tui lines=${cleaned.length}`;
|
|
248
|
-
return `${DEBUG_MESSAGE_PREFIX}\n- ${prelude}\n${cleaned.map((line) => `- ${line}`).join(
|
|
328
|
+
return `${DEBUG_MESSAGE_PREFIX}\n- ${prelude}\n${cleaned.map((line) => `- ${line}`).join('\n')}`;
|
|
249
329
|
}
|
|
250
330
|
function injectDebugThinking(response, debugText) {
|
|
251
|
-
if (!response || typeof response !==
|
|
331
|
+
if (!response || typeof response !== 'object') {
|
|
252
332
|
return response;
|
|
253
333
|
}
|
|
254
334
|
const resp = response;
|
|
@@ -256,9 +336,9 @@ function injectDebugThinking(response, debugText) {
|
|
|
256
336
|
const candidates = resp.candidates.slice();
|
|
257
337
|
const first = candidates[0];
|
|
258
338
|
if (first &&
|
|
259
|
-
typeof first ===
|
|
339
|
+
typeof first === 'object' &&
|
|
260
340
|
first.content &&
|
|
261
|
-
typeof first.content ===
|
|
341
|
+
typeof first.content === 'object' &&
|
|
262
342
|
Array.isArray(first.content.parts)) {
|
|
263
343
|
const parts = [{ thought: true, text: debugText }, ...first.content.parts];
|
|
264
344
|
candidates[0] = { ...first, content: { ...first.content, parts } };
|
|
@@ -267,7 +347,7 @@ function injectDebugThinking(response, debugText) {
|
|
|
267
347
|
return resp;
|
|
268
348
|
}
|
|
269
349
|
if (Array.isArray(resp.content)) {
|
|
270
|
-
const content = [{ type:
|
|
350
|
+
const content = [{ type: 'thinking', thinking: debugText }, ...resp.content];
|
|
271
351
|
return { ...resp, content };
|
|
272
352
|
}
|
|
273
353
|
if (!resp.reasoning_content) {
|
|
@@ -280,7 +360,7 @@ function injectDebugThinking(response, debugText) {
|
|
|
280
360
|
* Injected via the same path as debug text (injectDebugThinking) to ensure consistent
|
|
281
361
|
* signature caching and multi-turn handling.
|
|
282
362
|
*/
|
|
283
|
-
const SYNTHETIC_THINKING_PLACEHOLDER =
|
|
363
|
+
const SYNTHETIC_THINKING_PLACEHOLDER = '[Thinking preserved]\n';
|
|
284
364
|
function stripInjectedDebugFromParts(parts) {
|
|
285
365
|
if (!Array.isArray(parts)) {
|
|
286
366
|
return parts;
|
|
@@ -288,18 +368,20 @@ function stripInjectedDebugFromParts(parts) {
|
|
|
288
368
|
// Use .map() with empty text sentinels instead of .filter() to preserve
|
|
289
369
|
// array indices and prevent prompt cache invalidation.
|
|
290
370
|
return parts.map((part) => {
|
|
291
|
-
if (!part || typeof part !==
|
|
371
|
+
if (!part || typeof part !== 'object') {
|
|
292
372
|
return part;
|
|
293
373
|
}
|
|
294
374
|
const record = part;
|
|
295
|
-
const text = typeof record.text ===
|
|
375
|
+
const text = typeof record.text === 'string'
|
|
296
376
|
? record.text
|
|
297
|
-
: typeof record.thinking ===
|
|
377
|
+
: typeof record.thinking === 'string'
|
|
298
378
|
? record.thinking
|
|
299
379
|
: undefined;
|
|
300
380
|
// Replace debug blocks and synthetic thinking placeholders with empty text sentinel
|
|
301
|
-
if (text &&
|
|
302
|
-
|
|
381
|
+
if (text &&
|
|
382
|
+
(text.startsWith(DEBUG_MESSAGE_PREFIX) ||
|
|
383
|
+
text.startsWith(SYNTHETIC_THINKING_PLACEHOLDER.trim()))) {
|
|
384
|
+
const sentinel = { text: '.' };
|
|
303
385
|
if (record.cache_control !== undefined)
|
|
304
386
|
sentinel.cache_control = record.cache_control;
|
|
305
387
|
return sentinel;
|
|
@@ -311,65 +393,150 @@ function stripInjectedDebugFromRequestPayload(payload) {
|
|
|
311
393
|
const anyPayload = payload;
|
|
312
394
|
if (Array.isArray(anyPayload.contents)) {
|
|
313
395
|
anyPayload.contents = anyPayload.contents.map((content) => {
|
|
314
|
-
if (!content || typeof content !==
|
|
396
|
+
if (!content || typeof content !== 'object') {
|
|
315
397
|
return content;
|
|
316
398
|
}
|
|
317
399
|
if (Array.isArray(content.parts)) {
|
|
318
400
|
return { ...content, parts: stripInjectedDebugFromParts(content.parts) };
|
|
319
401
|
}
|
|
320
402
|
if (Array.isArray(content.content)) {
|
|
321
|
-
return {
|
|
403
|
+
return {
|
|
404
|
+
...content,
|
|
405
|
+
content: stripInjectedDebugFromParts(content.content),
|
|
406
|
+
};
|
|
322
407
|
}
|
|
323
408
|
return content;
|
|
324
409
|
});
|
|
325
410
|
}
|
|
326
411
|
if (Array.isArray(anyPayload.messages)) {
|
|
327
412
|
anyPayload.messages = anyPayload.messages.map((message) => {
|
|
328
|
-
if (!message || typeof message !==
|
|
413
|
+
if (!message || typeof message !== 'object') {
|
|
329
414
|
return message;
|
|
330
415
|
}
|
|
331
416
|
if (Array.isArray(message.content)) {
|
|
332
|
-
return {
|
|
417
|
+
return {
|
|
418
|
+
...message,
|
|
419
|
+
content: stripInjectedDebugFromParts(message.content),
|
|
420
|
+
};
|
|
333
421
|
}
|
|
334
422
|
return message;
|
|
335
423
|
});
|
|
336
424
|
}
|
|
337
425
|
}
|
|
338
426
|
function isValidRequestPart(part) {
|
|
339
|
-
if (!part || typeof part !==
|
|
427
|
+
if (!part || typeof part !== 'object') {
|
|
340
428
|
return false;
|
|
341
429
|
}
|
|
342
430
|
const record = part;
|
|
343
|
-
return (Object.
|
|
344
|
-
Object.
|
|
345
|
-
Object.
|
|
346
|
-
Object.
|
|
347
|
-
Object.
|
|
348
|
-
Object.
|
|
349
|
-
Object.
|
|
350
|
-
Object.
|
|
431
|
+
return (Object.hasOwn(record, 'text') ||
|
|
432
|
+
Object.hasOwn(record, 'functionCall') ||
|
|
433
|
+
Object.hasOwn(record, 'functionResponse') ||
|
|
434
|
+
Object.hasOwn(record, 'inlineData') ||
|
|
435
|
+
Object.hasOwn(record, 'fileData') ||
|
|
436
|
+
Object.hasOwn(record, 'executableCode') ||
|
|
437
|
+
Object.hasOwn(record, 'codeExecutionResult') ||
|
|
438
|
+
Object.hasOwn(record, 'thought'));
|
|
439
|
+
}
|
|
440
|
+
function stripCacheControlFromParts(parts) {
|
|
441
|
+
if (!Array.isArray(parts)) {
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
for (const part of parts) {
|
|
445
|
+
if (!part || typeof part !== 'object' || Array.isArray(part)) {
|
|
446
|
+
continue;
|
|
447
|
+
}
|
|
448
|
+
const record = part;
|
|
449
|
+
delete record.cache_control;
|
|
450
|
+
delete record.cacheControl;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
function stripUnsupportedAntigravityFields(payload) {
|
|
454
|
+
delete payload.providerOptions;
|
|
455
|
+
delete payload.cached_content;
|
|
456
|
+
delete payload.cachedContent;
|
|
457
|
+
delete payload.cache_control;
|
|
458
|
+
delete payload.cacheControl;
|
|
459
|
+
const extraBody = payload.extra_body;
|
|
460
|
+
if (extraBody && typeof extraBody === 'object' && !Array.isArray(extraBody)) {
|
|
461
|
+
const extraBodyRecord = extraBody;
|
|
462
|
+
delete extraBodyRecord.cached_content;
|
|
463
|
+
delete extraBodyRecord.cachedContent;
|
|
464
|
+
delete extraBodyRecord.cache_control;
|
|
465
|
+
delete extraBodyRecord.cacheControl;
|
|
466
|
+
if (Object.keys(extraBodyRecord).length === 0) {
|
|
467
|
+
delete payload.extra_body;
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
const stripContentParts = (items) => {
|
|
471
|
+
if (!Array.isArray(items)) {
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
for (const item of items) {
|
|
475
|
+
if (!item || typeof item !== 'object' || Array.isArray(item)) {
|
|
476
|
+
continue;
|
|
477
|
+
}
|
|
478
|
+
const record = item;
|
|
479
|
+
stripCacheControlFromParts(record.parts);
|
|
480
|
+
stripCacheControlFromParts(record.content);
|
|
481
|
+
}
|
|
482
|
+
};
|
|
483
|
+
stripContentParts(payload.contents);
|
|
484
|
+
stripContentParts(payload.messages);
|
|
485
|
+
for (const instructionKey of [
|
|
486
|
+
'systemInstruction',
|
|
487
|
+
'system_instruction',
|
|
488
|
+
]) {
|
|
489
|
+
const instruction = payload[instructionKey];
|
|
490
|
+
if (instruction &&
|
|
491
|
+
typeof instruction === 'object' &&
|
|
492
|
+
!Array.isArray(instruction)) {
|
|
493
|
+
stripCacheControlFromParts(instruction.parts);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
function configureAntigravityToolCalling(payload) {
|
|
498
|
+
if (!Array.isArray(payload.tools) || payload.tools.length === 0) {
|
|
499
|
+
delete payload.toolConfig;
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
const toolConfig = payload.toolConfig &&
|
|
503
|
+
typeof payload.toolConfig === 'object' &&
|
|
504
|
+
!Array.isArray(payload.toolConfig)
|
|
505
|
+
? payload.toolConfig
|
|
506
|
+
: {};
|
|
507
|
+
const functionCallingConfig = toolConfig.functionCallingConfig &&
|
|
508
|
+
typeof toolConfig.functionCallingConfig === 'object' &&
|
|
509
|
+
!Array.isArray(toolConfig.functionCallingConfig)
|
|
510
|
+
? toolConfig.functionCallingConfig
|
|
511
|
+
: {};
|
|
512
|
+
functionCallingConfig.mode = 'VALIDATED';
|
|
513
|
+
toolConfig.functionCallingConfig = functionCallingConfig;
|
|
514
|
+
payload.toolConfig = toolConfig;
|
|
351
515
|
}
|
|
352
516
|
function sanitizeRequestPayloadForAntigravity(payload) {
|
|
353
517
|
const anyPayload = payload;
|
|
354
518
|
if (Array.isArray(anyPayload.contents)) {
|
|
355
519
|
// Use .map() instead of .map().filter() to preserve array indices for prompt cache stability
|
|
356
|
-
anyPayload.contents = anyPayload.contents
|
|
357
|
-
|
|
358
|
-
if (!content || typeof content !== "object") {
|
|
520
|
+
anyPayload.contents = anyPayload.contents.map((content) => {
|
|
521
|
+
if (!content || typeof content !== 'object') {
|
|
359
522
|
// Preserve non-object entries as empty sentinel instead of filtering
|
|
360
|
-
return { role:
|
|
523
|
+
return { role: 'user', parts: [{ text: '.' }] };
|
|
361
524
|
}
|
|
362
525
|
const contentRecord = content;
|
|
363
|
-
const rawParts = Array.isArray(contentRecord.parts)
|
|
526
|
+
const rawParts = Array.isArray(contentRecord.parts)
|
|
527
|
+
? contentRecord.parts
|
|
528
|
+
: [];
|
|
364
529
|
let foundFirstFunctionCall = false;
|
|
365
|
-
const sanitizedParts = rawParts
|
|
530
|
+
const sanitizedParts = rawParts
|
|
531
|
+
.map((part) => {
|
|
366
532
|
// Replace invalid parts with sentinel to preserve array indices for cache stability
|
|
367
533
|
if (!isValidRequestPart(part)) {
|
|
368
|
-
return { text:
|
|
534
|
+
return { text: '.' };
|
|
369
535
|
}
|
|
370
536
|
return part;
|
|
371
|
-
})
|
|
372
|
-
|
|
537
|
+
})
|
|
538
|
+
.map((part) => {
|
|
539
|
+
if (part && typeof part === 'object' && part.functionCall) {
|
|
373
540
|
let sig = part.thoughtSignature || part.thought_signature;
|
|
374
541
|
// Only the first functionCall part in a block should have the signature.
|
|
375
542
|
// If it's the first one and missing a valid signature, inject the sentinel
|
|
@@ -397,7 +564,7 @@ function sanitizeRequestPayloadForAntigravity(payload) {
|
|
|
397
564
|
});
|
|
398
565
|
if (sanitizedParts.length === 0) {
|
|
399
566
|
// Preserve as empty text sentinel instead of filtering out
|
|
400
|
-
return { ...contentRecord, parts: [{ text:
|
|
567
|
+
return { ...contentRecord, parts: [{ text: '.' }] };
|
|
401
568
|
}
|
|
402
569
|
return {
|
|
403
570
|
...contentRecord,
|
|
@@ -407,23 +574,28 @@ function sanitizeRequestPayloadForAntigravity(payload) {
|
|
|
407
574
|
}
|
|
408
575
|
if (Array.isArray(anyPayload.messages)) {
|
|
409
576
|
anyPayload.messages = anyPayload.messages.map((message) => {
|
|
410
|
-
if (!message || typeof message !==
|
|
411
|
-
return { role:
|
|
577
|
+
if (!message || typeof message !== 'object') {
|
|
578
|
+
return { role: 'user', content: [{ type: 'text', text: '.' }] };
|
|
412
579
|
}
|
|
413
580
|
const messageRecord = message;
|
|
414
|
-
const rawContent = Array.isArray(messageRecord.content)
|
|
581
|
+
const rawContent = Array.isArray(messageRecord.content)
|
|
582
|
+
? messageRecord.content
|
|
583
|
+
: messageRecord.content;
|
|
415
584
|
if (!Array.isArray(rawContent)) {
|
|
416
585
|
return messageRecord;
|
|
417
586
|
}
|
|
418
587
|
const sanitizedContent = rawContent.map((block) => {
|
|
419
|
-
if (!block || typeof block !==
|
|
420
|
-
return { type:
|
|
588
|
+
if (!block || typeof block !== 'object') {
|
|
589
|
+
return { type: 'text', text: '.' };
|
|
421
590
|
}
|
|
422
591
|
const blockRecord = block;
|
|
423
|
-
if (blockRecord.type ===
|
|
592
|
+
if (blockRecord.type === 'text') {
|
|
424
593
|
const text = blockRecord.text;
|
|
425
|
-
if (typeof text !==
|
|
426
|
-
const sentinel = {
|
|
594
|
+
if (typeof text !== 'string' || text.trim().length === 0) {
|
|
595
|
+
const sentinel = {
|
|
596
|
+
type: 'text',
|
|
597
|
+
text: '.',
|
|
598
|
+
};
|
|
427
599
|
if (blockRecord.cache_control !== undefined)
|
|
428
600
|
sentinel.cache_control = blockRecord.cache_control;
|
|
429
601
|
return sentinel;
|
|
@@ -432,7 +604,7 @@ function sanitizeRequestPayloadForAntigravity(payload) {
|
|
|
432
604
|
return block;
|
|
433
605
|
});
|
|
434
606
|
if (sanitizedContent.length === 0) {
|
|
435
|
-
return { ...messageRecord, content: [{ type:
|
|
607
|
+
return { ...messageRecord, content: [{ type: 'text', text: '.' }] };
|
|
436
608
|
}
|
|
437
609
|
return {
|
|
438
610
|
...messageRecord,
|
|
@@ -441,7 +613,9 @@ function sanitizeRequestPayloadForAntigravity(payload) {
|
|
|
441
613
|
});
|
|
442
614
|
}
|
|
443
615
|
const systemInstruction = anyPayload.systemInstruction;
|
|
444
|
-
if (systemInstruction &&
|
|
616
|
+
if (systemInstruction &&
|
|
617
|
+
typeof systemInstruction === 'object' &&
|
|
618
|
+
!Array.isArray(systemInstruction)) {
|
|
445
619
|
const sys = systemInstruction;
|
|
446
620
|
if (Array.isArray(sys.parts)) {
|
|
447
621
|
// Use .map() with sentinels instead of .filter() to preserve array indices
|
|
@@ -450,13 +624,16 @@ function sanitizeRequestPayloadForAntigravity(payload) {
|
|
|
450
624
|
if (isValidRequestPart(part))
|
|
451
625
|
return part;
|
|
452
626
|
const record = part;
|
|
453
|
-
const sentinel = { text:
|
|
627
|
+
const sentinel = { text: '.' };
|
|
454
628
|
if (record?.cache_control !== undefined)
|
|
455
629
|
sentinel.cache_control = record.cache_control;
|
|
456
630
|
return sentinel;
|
|
457
631
|
});
|
|
458
632
|
// Only delete systemInstruction if ALL parts were invalid (all sentinels, no real content)
|
|
459
|
-
const hasRealContent = sanitizedSystemParts.some((p) => p &&
|
|
633
|
+
const hasRealContent = sanitizedSystemParts.some((p) => p &&
|
|
634
|
+
typeof p === 'object' &&
|
|
635
|
+
typeof p.text === 'string' &&
|
|
636
|
+
p.text !== '.');
|
|
460
637
|
if (hasRealContent) {
|
|
461
638
|
sys.parts = sanitizedSystemParts;
|
|
462
639
|
}
|
|
@@ -467,31 +644,35 @@ function sanitizeRequestPayloadForAntigravity(payload) {
|
|
|
467
644
|
}
|
|
468
645
|
}
|
|
469
646
|
function isGeminiToolUsePart(part) {
|
|
470
|
-
return !!(part &&
|
|
647
|
+
return !!(part &&
|
|
648
|
+
typeof part === 'object' &&
|
|
649
|
+
(part.functionCall || part.tool_use || part.toolUse));
|
|
471
650
|
}
|
|
472
651
|
function isGeminiThinkingPart(part) {
|
|
473
652
|
return !!(part &&
|
|
474
|
-
typeof part ===
|
|
475
|
-
(part.thought === true ||
|
|
653
|
+
typeof part === 'object' &&
|
|
654
|
+
(part.thought === true ||
|
|
655
|
+
part.type === 'thinking' ||
|
|
656
|
+
part.type === 'reasoning'));
|
|
476
657
|
}
|
|
477
658
|
// Sentinel value used when signature recovery fails - allows Claude to handle gracefully
|
|
478
659
|
// by redacting the thinking block instead of rejecting the request entirely.
|
|
479
660
|
// Reference: LLM-API-Key-Proxy uses this pattern for Gemini 3 tool calls.
|
|
480
|
-
const SENTINEL_SIGNATURE =
|
|
661
|
+
const SENTINEL_SIGNATURE = 'skip_thought_signature_validator';
|
|
481
662
|
function getThinkingPartText(part) {
|
|
482
|
-
if (!part || typeof part !==
|
|
483
|
-
return
|
|
663
|
+
if (!part || typeof part !== 'object') {
|
|
664
|
+
return '';
|
|
484
665
|
}
|
|
485
|
-
if (typeof part.text ===
|
|
666
|
+
if (typeof part.text === 'string') {
|
|
486
667
|
return part.text;
|
|
487
668
|
}
|
|
488
|
-
if (typeof part.thinking ===
|
|
669
|
+
if (typeof part.thinking === 'string') {
|
|
489
670
|
return part.thinking;
|
|
490
671
|
}
|
|
491
|
-
return
|
|
672
|
+
return '';
|
|
492
673
|
}
|
|
493
674
|
function hasCachedMatchingSignature(part, sessionId) {
|
|
494
|
-
if (!part || typeof part !==
|
|
675
|
+
if (!part || typeof part !== 'object') {
|
|
495
676
|
return false;
|
|
496
677
|
}
|
|
497
678
|
const text = getThinkingPartText(part);
|
|
@@ -508,7 +689,7 @@ function hasCachedMatchingSignature(part, sessionId) {
|
|
|
508
689
|
return part.signature === expectedSignature;
|
|
509
690
|
}
|
|
510
691
|
function ensureThoughtSignature(part, sessionId) {
|
|
511
|
-
if (!part || typeof part !==
|
|
692
|
+
if (!part || typeof part !== 'object') {
|
|
512
693
|
return part;
|
|
513
694
|
}
|
|
514
695
|
if (!sessionId) {
|
|
@@ -521,20 +702,24 @@ function ensureThoughtSignature(part, sessionId) {
|
|
|
521
702
|
if (part.thought === true) {
|
|
522
703
|
return { ...part, thoughtSignature: SENTINEL_SIGNATURE };
|
|
523
704
|
}
|
|
524
|
-
if (part.type ===
|
|
705
|
+
if (part.type === 'thinking' ||
|
|
706
|
+
part.type === 'reasoning' ||
|
|
707
|
+
part.type === 'redacted_thinking') {
|
|
525
708
|
return { ...part, signature: SENTINEL_SIGNATURE };
|
|
526
709
|
}
|
|
527
710
|
return part;
|
|
528
711
|
}
|
|
529
712
|
function hasSignedThinkingPart(part, sessionId) {
|
|
530
|
-
if (!part || typeof part !==
|
|
713
|
+
if (!part || typeof part !== 'object') {
|
|
531
714
|
return false;
|
|
532
715
|
}
|
|
533
716
|
if (part.thought === true) {
|
|
534
|
-
if (part.thoughtSignature === SENTINEL_SIGNATURE ||
|
|
717
|
+
if (part.thoughtSignature === SENTINEL_SIGNATURE ||
|
|
718
|
+
part.thoughtSignature === SKIP_THOUGHT_SIGNATURE) {
|
|
535
719
|
return true;
|
|
536
720
|
}
|
|
537
|
-
if (typeof part.thoughtSignature !==
|
|
721
|
+
if (typeof part.thoughtSignature !== 'string' ||
|
|
722
|
+
part.thoughtSignature.length < MIN_SIGNATURE_LENGTH) {
|
|
538
723
|
return false;
|
|
539
724
|
}
|
|
540
725
|
if (!sessionId) {
|
|
@@ -542,11 +727,15 @@ function hasSignedThinkingPart(part, sessionId) {
|
|
|
542
727
|
}
|
|
543
728
|
return hasCachedMatchingSignature(part, sessionId);
|
|
544
729
|
}
|
|
545
|
-
if (part.type ===
|
|
546
|
-
|
|
730
|
+
if (part.type === 'thinking' ||
|
|
731
|
+
part.type === 'reasoning' ||
|
|
732
|
+
part.type === 'redacted_thinking') {
|
|
733
|
+
if (part.signature === SENTINEL_SIGNATURE ||
|
|
734
|
+
part.signature === SKIP_THOUGHT_SIGNATURE) {
|
|
547
735
|
return true;
|
|
548
736
|
}
|
|
549
|
-
if (typeof part.signature !==
|
|
737
|
+
if (typeof part.signature !== 'string' ||
|
|
738
|
+
part.signature.length < MIN_SIGNATURE_LENGTH) {
|
|
550
739
|
return false;
|
|
551
740
|
}
|
|
552
741
|
if (!sessionId) {
|
|
@@ -558,11 +747,13 @@ function hasSignedThinkingPart(part, sessionId) {
|
|
|
558
747
|
}
|
|
559
748
|
function ensureThinkingBeforeToolUseInContents(contents, signatureSessionKey) {
|
|
560
749
|
return contents.map((content) => {
|
|
561
|
-
if (!content ||
|
|
750
|
+
if (!content ||
|
|
751
|
+
typeof content !== 'object' ||
|
|
752
|
+
!Array.isArray(content.parts)) {
|
|
562
753
|
return content;
|
|
563
754
|
}
|
|
564
755
|
const role = content.role;
|
|
565
|
-
if (role !==
|
|
756
|
+
if (role !== 'model' && role !== 'assistant') {
|
|
566
757
|
return content;
|
|
567
758
|
}
|
|
568
759
|
const parts = content.parts;
|
|
@@ -571,22 +762,31 @@ function ensureThinkingBeforeToolUseInContents(contents, signatureSessionKey) {
|
|
|
571
762
|
return content;
|
|
572
763
|
}
|
|
573
764
|
// Check if any thinking part has a valid signed signature
|
|
574
|
-
const hasSignedThinking = parts.some(p => isGeminiThinkingPart(p) &&
|
|
765
|
+
const hasSignedThinking = parts.some((p) => isGeminiThinkingPart(p) &&
|
|
766
|
+
hasSignedThinkingPart(ensureThoughtSignature(p, signatureSessionKey), signatureSessionKey));
|
|
575
767
|
if (hasSignedThinking) {
|
|
576
768
|
// Ensure signatures on thinking parts in-place — NO reordering to preserve array indices (cache-friendly)
|
|
577
|
-
return {
|
|
769
|
+
return {
|
|
770
|
+
...content,
|
|
771
|
+
parts: parts.map((p) => isGeminiThinkingPart(p)
|
|
772
|
+
? ensureThoughtSignature(p, signatureSessionKey)
|
|
773
|
+
: p),
|
|
774
|
+
};
|
|
578
775
|
}
|
|
579
776
|
// Replace thinking parts with sentinels in-place to preserve array indices (cache-friendly).
|
|
580
777
|
// Deleting parts via .filter() shifts array indices → changes hash → busts prompt cache.
|
|
581
778
|
const lastThinking = defaultSignatureStore.get(signatureSessionKey);
|
|
582
|
-
log.debug(
|
|
583
|
-
|
|
779
|
+
log.debug('Replacing thinking with sentinels in-place', {
|
|
780
|
+
signatureSessionKey,
|
|
781
|
+
hasCachedSig: !!lastThinking,
|
|
782
|
+
});
|
|
783
|
+
const newParts = parts.map((p) => {
|
|
584
784
|
if (!isGeminiThinkingPart(p))
|
|
585
785
|
return p;
|
|
586
786
|
const cc = p.cache_control;
|
|
587
787
|
// Use plain empty text part — thinking-format sentinels get converted by the proxy
|
|
588
788
|
// into Claude thinking blocks missing the required `thinking` field.
|
|
589
|
-
const sentinel = { text:
|
|
789
|
+
const sentinel = { text: '.' };
|
|
590
790
|
if (cc)
|
|
591
791
|
sentinel.cache_control = cc;
|
|
592
792
|
return sentinel;
|
|
@@ -595,10 +795,10 @@ function ensureThinkingBeforeToolUseInContents(contents, signatureSessionKey) {
|
|
|
595
795
|
});
|
|
596
796
|
}
|
|
597
797
|
function ensureMessageThinkingSignature(block, sessionId) {
|
|
598
|
-
if (!block || typeof block !==
|
|
798
|
+
if (!block || typeof block !== 'object') {
|
|
599
799
|
return block;
|
|
600
800
|
}
|
|
601
|
-
if (block.type !==
|
|
801
|
+
if (block.type !== 'thinking' && block.type !== 'redacted_thinking') {
|
|
602
802
|
return block;
|
|
603
803
|
}
|
|
604
804
|
const text = getThinkingPartText(block);
|
|
@@ -612,7 +812,9 @@ function ensureMessageThinkingSignature(block, sessionId) {
|
|
|
612
812
|
}
|
|
613
813
|
function hasToolUseInContents(contents) {
|
|
614
814
|
return contents.some((content) => {
|
|
615
|
-
if (!content ||
|
|
815
|
+
if (!content ||
|
|
816
|
+
typeof content !== 'object' ||
|
|
817
|
+
!Array.isArray(content.parts)) {
|
|
616
818
|
return false;
|
|
617
819
|
}
|
|
618
820
|
return content.parts.some(isGeminiToolUsePart);
|
|
@@ -620,7 +822,9 @@ function hasToolUseInContents(contents) {
|
|
|
620
822
|
}
|
|
621
823
|
function hasSignedThinkingInContents(contents, sessionId) {
|
|
622
824
|
return contents.some((content) => {
|
|
623
|
-
if (!content ||
|
|
825
|
+
if (!content ||
|
|
826
|
+
typeof content !== 'object' ||
|
|
827
|
+
!Array.isArray(content.parts)) {
|
|
624
828
|
return false;
|
|
625
829
|
}
|
|
626
830
|
return content.parts.some((part) => hasSignedThinkingPart(part, sessionId));
|
|
@@ -628,15 +832,21 @@ function hasSignedThinkingInContents(contents, sessionId) {
|
|
|
628
832
|
}
|
|
629
833
|
function hasToolUseInMessages(messages) {
|
|
630
834
|
return messages.some((message) => {
|
|
631
|
-
if (!message ||
|
|
835
|
+
if (!message ||
|
|
836
|
+
typeof message !== 'object' ||
|
|
837
|
+
!Array.isArray(message.content)) {
|
|
632
838
|
return false;
|
|
633
839
|
}
|
|
634
|
-
return message.content.some((block) => block &&
|
|
840
|
+
return message.content.some((block) => block &&
|
|
841
|
+
typeof block === 'object' &&
|
|
842
|
+
(block.type === 'tool_use' || block.type === 'tool_result'));
|
|
635
843
|
});
|
|
636
844
|
}
|
|
637
845
|
function hasSignedThinkingInMessages(messages, sessionId) {
|
|
638
846
|
return messages.some((message) => {
|
|
639
|
-
if (!message ||
|
|
847
|
+
if (!message ||
|
|
848
|
+
typeof message !== 'object' ||
|
|
849
|
+
!Array.isArray(message.content)) {
|
|
640
850
|
return false;
|
|
641
851
|
}
|
|
642
852
|
return message.content.some((block) => hasSignedThinkingPart(block, sessionId));
|
|
@@ -644,40 +854,64 @@ function hasSignedThinkingInMessages(messages, sessionId) {
|
|
|
644
854
|
}
|
|
645
855
|
function ensureThinkingBeforeToolUseInMessages(messages, signatureSessionKey) {
|
|
646
856
|
return messages.map((message) => {
|
|
647
|
-
if (!message ||
|
|
857
|
+
if (!message ||
|
|
858
|
+
typeof message !== 'object' ||
|
|
859
|
+
!Array.isArray(message.content)) {
|
|
648
860
|
return message;
|
|
649
861
|
}
|
|
650
|
-
if (message.role !==
|
|
862
|
+
if (message.role !== 'assistant') {
|
|
651
863
|
return message;
|
|
652
864
|
}
|
|
653
865
|
const blocks = message.content;
|
|
654
|
-
const hasToolUse = blocks.some((b) => b &&
|
|
866
|
+
const hasToolUse = blocks.some((b) => b &&
|
|
867
|
+
typeof b === 'object' &&
|
|
868
|
+
(b.type === 'tool_use' || b.type === 'tool_result'));
|
|
655
869
|
if (!hasToolUse) {
|
|
656
870
|
return message;
|
|
657
871
|
}
|
|
658
|
-
const isThinkingBlock = (b) => b &&
|
|
872
|
+
const isThinkingBlock = (b) => b &&
|
|
873
|
+
typeof b === 'object' &&
|
|
874
|
+
(b.type === 'thinking' || b.type === 'redacted_thinking');
|
|
659
875
|
// Check if any thinking block has a valid signed signature
|
|
660
|
-
const hasSignedThinking = blocks.some((b) => isThinkingBlock(b) &&
|
|
876
|
+
const hasSignedThinking = blocks.some((b) => isThinkingBlock(b) &&
|
|
877
|
+
hasSignedThinkingPart(ensureMessageThinkingSignature(b, signatureSessionKey), signatureSessionKey));
|
|
661
878
|
if (hasSignedThinking) {
|
|
662
879
|
// Ensure signatures on thinking blocks in-place — NO reordering to preserve array indices (cache-friendly)
|
|
663
|
-
return {
|
|
880
|
+
return {
|
|
881
|
+
...message,
|
|
882
|
+
content: blocks.map((b) => isThinkingBlock(b)
|
|
883
|
+
? ensureMessageThinkingSignature(b, signatureSessionKey)
|
|
884
|
+
: b),
|
|
885
|
+
};
|
|
664
886
|
}
|
|
665
887
|
// Replace thinking blocks with sentinels in-place to preserve array indices (cache-friendly).
|
|
666
888
|
// Deleting/reordering via .filter() shifts indices → changes hash → busts prompt cache.
|
|
667
889
|
const lastThinking = defaultSignatureStore.get(signatureSessionKey);
|
|
668
|
-
log.debug(
|
|
669
|
-
|
|
890
|
+
log.debug('Replacing thinking with sentinels in-place (Messages format)', {
|
|
891
|
+
signatureSessionKey,
|
|
892
|
+
hasCachedSig: !!lastThinking,
|
|
893
|
+
});
|
|
894
|
+
return {
|
|
895
|
+
...message,
|
|
896
|
+
content: blocks.map((b) => {
|
|
670
897
|
if (!isThinkingBlock(b))
|
|
671
898
|
return b;
|
|
672
|
-
const
|
|
899
|
+
const _thinkingText = lastThinking
|
|
900
|
+
? lastThinking.text
|
|
901
|
+
: typeof b.thinking === 'string'
|
|
902
|
+
? b.thinking
|
|
903
|
+
: typeof b.text === 'string'
|
|
904
|
+
? b.text
|
|
905
|
+
: '';
|
|
673
906
|
const cc = b.cache_control;
|
|
674
907
|
// Use plain empty text part — thinking-format sentinels get converted by the proxy
|
|
675
908
|
// into Claude thinking blocks missing the required `thinking` field.
|
|
676
|
-
const sentinel = { text:
|
|
909
|
+
const sentinel = { text: '.' };
|
|
677
910
|
if (cc)
|
|
678
911
|
sentinel.cache_control = cc;
|
|
679
912
|
return sentinel;
|
|
680
|
-
})
|
|
913
|
+
}),
|
|
914
|
+
};
|
|
681
915
|
});
|
|
682
916
|
}
|
|
683
917
|
/**
|
|
@@ -690,18 +924,18 @@ let _lastCacheStats = null;
|
|
|
690
924
|
export function getLastCacheStats() {
|
|
691
925
|
return _lastCacheStats;
|
|
692
926
|
}
|
|
693
|
-
const STREAM_ACTION =
|
|
927
|
+
const STREAM_ACTION = 'streamGenerateContent';
|
|
694
928
|
/**
|
|
695
929
|
* Extract a URL string from any fetch() input shape (string, URL, or Request).
|
|
696
930
|
*/
|
|
697
931
|
export function fetchInputToUrl(input) {
|
|
698
|
-
if (typeof input ===
|
|
932
|
+
if (typeof input === 'string')
|
|
699
933
|
return input;
|
|
700
934
|
if (input instanceof URL)
|
|
701
935
|
return input.href;
|
|
702
936
|
// Request-like object
|
|
703
937
|
const url = input.url;
|
|
704
|
-
return typeof url ===
|
|
938
|
+
return typeof url === 'string' ? url : String(input);
|
|
705
939
|
}
|
|
706
940
|
/**
|
|
707
941
|
* Detects requests headed to the Google Generative Language API so we can
|
|
@@ -710,12 +944,12 @@ export function fetchInputToUrl(input) {
|
|
|
710
944
|
* interceptor entirely.
|
|
711
945
|
*/
|
|
712
946
|
export function isGenerativeLanguageRequest(input) {
|
|
713
|
-
return fetchInputToUrl(input).includes(
|
|
947
|
+
return fetchInputToUrl(input).includes('generativelanguage.googleapis.com');
|
|
714
948
|
}
|
|
715
|
-
export function prepareAntigravityRequest(input, init, accessToken, projectId, endpointOverride, headerStyle =
|
|
949
|
+
export function prepareAntigravityRequest(input, init, accessToken, projectId, endpointOverride, headerStyle = 'antigravity', forceThinkingRecovery = false, options) {
|
|
716
950
|
const baseInit = { ...init };
|
|
717
951
|
const headers = new Headers(init?.headers ?? {});
|
|
718
|
-
let resolvedProjectId = projectId?.trim() ||
|
|
952
|
+
let resolvedProjectId = projectId?.trim() || '';
|
|
719
953
|
let toolDebugMissing = 0;
|
|
720
954
|
const toolDebugSummaries = [];
|
|
721
955
|
let toolDebugPayload;
|
|
@@ -730,14 +964,16 @@ export function prepareAntigravityRequest(input, init, accessToken, projectId, e
|
|
|
730
964
|
headerStyle,
|
|
731
965
|
};
|
|
732
966
|
}
|
|
733
|
-
headers.set(
|
|
734
|
-
headers.delete(
|
|
735
|
-
headers.delete(
|
|
736
|
-
headers.delete(
|
|
967
|
+
headers.set('Authorization', `Bearer ${accessToken}`);
|
|
968
|
+
headers.delete('x-api-key');
|
|
969
|
+
headers.delete('x-goog-api-key');
|
|
970
|
+
headers.delete('x-session-affinity');
|
|
971
|
+
headers.delete('x-session-id');
|
|
972
|
+
headers.delete('x-parent-session-id');
|
|
737
973
|
// Strip x-goog-user-project header to prevent 403 auth/license conflicts.
|
|
738
974
|
// This header is added by OpenCode/AI SDK and can force project-level checks
|
|
739
975
|
// that are not required for Antigravity/Gemini CLI OAuth requests.
|
|
740
|
-
headers.delete(
|
|
976
|
+
headers.delete('x-goog-user-project');
|
|
741
977
|
const urlString = fetchInputToUrl(input);
|
|
742
978
|
const match = urlString.match(/\/models\/([^:]+):(\w+)/);
|
|
743
979
|
if (!match) {
|
|
@@ -748,14 +984,14 @@ export function prepareAntigravityRequest(input, init, accessToken, projectId, e
|
|
|
748
984
|
headerStyle,
|
|
749
985
|
};
|
|
750
986
|
}
|
|
751
|
-
const [, rawModel =
|
|
987
|
+
const [, rawModel = '', rawAction = ''] = match;
|
|
752
988
|
const requestedModel = rawModel;
|
|
753
989
|
const resolved = resolveModelForHeaderStyle(rawModel, headerStyle);
|
|
754
990
|
let effectiveModel = resolved.actualModel;
|
|
755
991
|
const streaming = rawAction === STREAM_ACTION;
|
|
756
|
-
const defaultEndpoint = headerStyle ===
|
|
992
|
+
const defaultEndpoint = headerStyle === 'gemini-cli' ? GEMINI_CLI_ENDPOINT : ANTIGRAVITY_ENDPOINT;
|
|
757
993
|
const baseEndpoint = endpointOverride ?? defaultEndpoint;
|
|
758
|
-
const transformedUrl = `${baseEndpoint}/v1internal:${rawAction}${streaming ?
|
|
994
|
+
const transformedUrl = `${baseEndpoint}/v1internal:${rawAction}${streaming ? '?alt=sse' : ''}`;
|
|
759
995
|
const isClaude = isClaudeModel(resolved.actualModel);
|
|
760
996
|
const isClaudeThinking = isClaudeThinkingModel(resolved.actualModel);
|
|
761
997
|
const keepThinkingEnabled = getKeepThinking();
|
|
@@ -764,24 +1000,23 @@ export function prepareAntigravityRequest(input, init, accessToken, projectId, e
|
|
|
764
1000
|
let tierThinkingLevel = resolved.thinkingLevel;
|
|
765
1001
|
let signatureSessionKey = buildSignatureSessionKey(PLUGIN_SESSION_ID, effectiveModel, undefined, resolveProjectKey(projectId));
|
|
766
1002
|
let body = baseInit.body;
|
|
767
|
-
if (typeof baseInit.body ===
|
|
1003
|
+
if (typeof baseInit.body === 'string' && baseInit.body) {
|
|
768
1004
|
try {
|
|
769
1005
|
const parsedBody = JSON.parse(baseInit.body);
|
|
770
|
-
const isWrapped = typeof parsedBody.project ===
|
|
1006
|
+
const isWrapped = typeof parsedBody.project === 'string' && 'request' in parsedBody;
|
|
771
1007
|
if (isWrapped) {
|
|
772
1008
|
const wrappedBody = {
|
|
773
1009
|
...parsedBody,
|
|
774
1010
|
model: effectiveModel,
|
|
775
1011
|
};
|
|
776
|
-
if (headerStyle ===
|
|
777
|
-
if (typeof wrappedBody.
|
|
778
|
-
wrappedBody.
|
|
1012
|
+
if (headerStyle === 'antigravity') {
|
|
1013
|
+
if (typeof wrappedBody.userAgent !== 'string' ||
|
|
1014
|
+
!wrappedBody.userAgent) {
|
|
1015
|
+
wrappedBody.userAgent = 'antigravity';
|
|
779
1016
|
}
|
|
780
|
-
if (typeof wrappedBody.
|
|
781
|
-
wrappedBody.
|
|
782
|
-
|
|
783
|
-
if (typeof wrappedBody.requestType !== "string" || !wrappedBody.requestType) {
|
|
784
|
-
wrappedBody.requestType = "agent";
|
|
1017
|
+
if (typeof wrappedBody.requestType !== 'string' ||
|
|
1018
|
+
!wrappedBody.requestType) {
|
|
1019
|
+
wrappedBody.requestType = 'agent';
|
|
785
1020
|
}
|
|
786
1021
|
}
|
|
787
1022
|
// Some callers may already send an Antigravity-wrapped body.
|
|
@@ -789,96 +1024,151 @@ export function prepareAntigravityRequest(input, init, accessToken, projectId, e
|
|
|
789
1024
|
// and attach a stable sessionId so multi-turn signature caching works.
|
|
790
1025
|
const requestRoot = wrappedBody.request;
|
|
791
1026
|
const requestObjects = [];
|
|
792
|
-
if (requestRoot && typeof requestRoot ===
|
|
1027
|
+
if (requestRoot && typeof requestRoot === 'object') {
|
|
793
1028
|
requestObjects.push(requestRoot);
|
|
794
1029
|
const nested = requestRoot.request;
|
|
795
|
-
if (nested && typeof nested ===
|
|
1030
|
+
if (nested && typeof nested === 'object') {
|
|
796
1031
|
requestObjects.push(nested);
|
|
797
1032
|
}
|
|
798
1033
|
}
|
|
799
1034
|
const conversationKey = resolveConversationKeyFromRequests(requestObjects);
|
|
800
1035
|
// Strip tier suffix from model for cache key to prevent cache misses on tier change
|
|
801
1036
|
// e.g., "claude-opus-4-6-thinking-high" -> "claude-opus-4-6-thinking"
|
|
802
|
-
const modelForCacheKey = effectiveModel.replace(/-(minimal|low|medium|high)$/i,
|
|
1037
|
+
const modelForCacheKey = effectiveModel.replace(/-(minimal|low|medium|high)$/i, '');
|
|
803
1038
|
signatureSessionKey = buildSignatureSessionKey(PLUGIN_SESSION_ID, modelForCacheKey, conversationKey, resolveProjectKey(parsedBody.project));
|
|
804
1039
|
if (requestObjects.length > 0) {
|
|
805
1040
|
sessionId = signatureSessionKey;
|
|
806
1041
|
}
|
|
807
1042
|
for (const req of requestObjects) {
|
|
808
|
-
// Use stable session ID for signature caching across multi-turn conversations
|
|
809
|
-
req.sessionId = signatureSessionKey;
|
|
810
1043
|
stripInjectedDebugFromRequestPayload(req);
|
|
811
1044
|
if (isClaude) {
|
|
812
1045
|
// Step 0: Sanitize cross-model metadata (strips Gemini signatures when sending to Claude)
|
|
813
|
-
sanitizeCrossModelPayloadInPlace(req, {
|
|
1046
|
+
sanitizeCrossModelPayloadInPlace(req, {
|
|
1047
|
+
targetModel: effectiveModel,
|
|
1048
|
+
});
|
|
814
1049
|
// Step 1: Strip corrupted/unsigned thinking blocks FIRST
|
|
815
1050
|
deepFilterThinkingBlocks(req, signatureSessionKey, getCachedSignature, true);
|
|
816
1051
|
// Step 2: THEN inject signed thinking from cache (after stripping)
|
|
817
|
-
if (isClaudeThinking &&
|
|
1052
|
+
if (isClaudeThinking &&
|
|
1053
|
+
keepThinkingEnabled &&
|
|
1054
|
+
Array.isArray(req.contents)) {
|
|
1055
|
+
;
|
|
818
1056
|
req.contents = ensureThinkingBeforeToolUseInContents(req.contents, signatureSessionKey);
|
|
819
1057
|
}
|
|
820
|
-
if (isClaudeThinking &&
|
|
1058
|
+
if (isClaudeThinking &&
|
|
1059
|
+
keepThinkingEnabled &&
|
|
1060
|
+
Array.isArray(req.messages)) {
|
|
1061
|
+
;
|
|
821
1062
|
req.messages = ensureThinkingBeforeToolUseInMessages(req.messages, signatureSessionKey);
|
|
822
1063
|
}
|
|
823
1064
|
// Step 3: Apply tool pairing fixes (ID assignment, response matching, orphan recovery)
|
|
824
1065
|
applyToolPairingFixes(req, true);
|
|
825
1066
|
}
|
|
1067
|
+
if (headerStyle === 'antigravity') {
|
|
1068
|
+
sanitizeRequestPayloadForAntigravity(req);
|
|
1069
|
+
stripUnsupportedAntigravityFields(req);
|
|
1070
|
+
configureAntigravityToolCalling(req);
|
|
1071
|
+
}
|
|
826
1072
|
}
|
|
827
|
-
//
|
|
828
|
-
//
|
|
829
|
-
//
|
|
830
|
-
|
|
1073
|
+
// AGY rejects every request that ends with a model turn. Enforce this at
|
|
1074
|
+
// the final wire boundary because host races, recovery, or sanitization can
|
|
1075
|
+
// otherwise leave a model/assistant entry last. Claude fallback transports
|
|
1076
|
+
// have the same assistant-prefill restriction.
|
|
1077
|
+
if (headerStyle === 'antigravity' || isClaude) {
|
|
831
1078
|
for (const req of requestObjects) {
|
|
832
1079
|
if (Array.isArray(req.contents)) {
|
|
833
1080
|
const contents = req.contents;
|
|
834
1081
|
const lastContent = contents[contents.length - 1];
|
|
835
|
-
if (lastContent?.role ===
|
|
836
|
-
|
|
1082
|
+
if (lastContent?.role === 'model' ||
|
|
1083
|
+
lastContent?.role === 'assistant') {
|
|
1084
|
+
contents.push({ role: 'user', parts: [{ text: '[Continue]' }] });
|
|
837
1085
|
}
|
|
838
1086
|
}
|
|
839
1087
|
if (Array.isArray(req.messages)) {
|
|
840
1088
|
const messages = req.messages;
|
|
841
1089
|
const lastMessage = messages[messages.length - 1];
|
|
842
|
-
if (lastMessage?.role ===
|
|
843
|
-
|
|
1090
|
+
if (lastMessage?.role === 'model' ||
|
|
1091
|
+
lastMessage?.role === 'assistant') {
|
|
1092
|
+
messages.push({ role: 'user', parts: [{ text: '[Continue]' }] });
|
|
844
1093
|
}
|
|
845
1094
|
}
|
|
846
1095
|
}
|
|
847
1096
|
}
|
|
848
1097
|
if (isClaudeThinking && keepThinkingEnabled && sessionId) {
|
|
849
|
-
const hasToolUse = requestObjects.some((req) => (Array.isArray(req.contents) &&
|
|
850
|
-
(
|
|
851
|
-
|
|
852
|
-
|
|
1098
|
+
const hasToolUse = requestObjects.some((req) => (Array.isArray(req.contents) &&
|
|
1099
|
+
hasToolUseInContents(req.contents)) ||
|
|
1100
|
+
(Array.isArray(req.messages) &&
|
|
1101
|
+
hasToolUseInMessages(req.messages)));
|
|
1102
|
+
const hasSignedThinking = requestObjects.some((req) => (Array.isArray(req.contents) &&
|
|
1103
|
+
hasSignedThinkingInContents(req.contents, signatureSessionKey)) ||
|
|
1104
|
+
(Array.isArray(req.messages) &&
|
|
1105
|
+
hasSignedThinkingInMessages(req.messages, signatureSessionKey)));
|
|
853
1106
|
const hasCachedThinking = defaultSignatureStore.has(signatureSessionKey);
|
|
854
|
-
needsSignedThinkingWarmup =
|
|
1107
|
+
needsSignedThinkingWarmup =
|
|
1108
|
+
hasToolUse && !hasSignedThinking && !hasCachedThinking;
|
|
1109
|
+
}
|
|
1110
|
+
const wireRequest = requestObjects.at(-1);
|
|
1111
|
+
if (wireRequest) {
|
|
1112
|
+
if (headerStyle === 'antigravity') {
|
|
1113
|
+
const metadata = buildAgyAgentRequestMetadata(options?.agySession ?? DEFAULT_AGY_REQUEST_SESSION, wireRequest, effectiveModel, options?.agyRequestTimestamp);
|
|
1114
|
+
wrappedBody.requestId = metadata.requestId;
|
|
1115
|
+
wireRequest.sessionId = metadata.sessionId;
|
|
1116
|
+
wireRequest.labels = metadata.labels;
|
|
1117
|
+
orderAgyRequestPayloadInPlace(wireRequest);
|
|
1118
|
+
}
|
|
1119
|
+
else {
|
|
1120
|
+
wireRequest.sessionId = signatureSessionKey;
|
|
1121
|
+
}
|
|
855
1122
|
}
|
|
856
|
-
body = safeStringify(headerStyle ===
|
|
1123
|
+
body = safeStringify(headerStyle === 'antigravity'
|
|
1124
|
+
? orderAntigravityEnvelope(wrappedBody)
|
|
1125
|
+
: wrappedBody);
|
|
857
1126
|
}
|
|
858
1127
|
else {
|
|
859
1128
|
const requestPayload = { ...parsedBody };
|
|
1129
|
+
if (headerStyle === 'antigravity' &&
|
|
1130
|
+
isImageGenerationModel(effectiveModel) &&
|
|
1131
|
+
isOpenCodeTitleGenerationRequest(requestPayload)) {
|
|
1132
|
+
// OpenCode runs title generation through the active model. Route that
|
|
1133
|
+
// text-only helper call away from image generation to avoid consuming
|
|
1134
|
+
// image quota and writing unrelated image files.
|
|
1135
|
+
effectiveModel = 'gemini-3.5-flash-low';
|
|
1136
|
+
tierThinkingBudget = 4000;
|
|
1137
|
+
tierThinkingLevel = undefined;
|
|
1138
|
+
}
|
|
860
1139
|
const rawGenerationConfig = requestPayload.generationConfig;
|
|
861
1140
|
const extraBody = requestPayload.extra_body;
|
|
862
1141
|
const variantConfig = extractVariantThinkingConfig(requestPayload.providerOptions, rawGenerationConfig);
|
|
863
|
-
const isGemini3 = effectiveModel.toLowerCase().includes(
|
|
864
|
-
log.debug(`[ThinkingResolution] rawModel=${rawModel} resolvedModel=${effectiveModel} resolvedTier=${tierThinkingLevel ??
|
|
1142
|
+
const isGemini3 = effectiveModel.toLowerCase().includes('gemini-3');
|
|
1143
|
+
log.debug(`[ThinkingResolution] rawModel=${rawModel} resolvedModel=${effectiveModel} resolvedTier=${tierThinkingLevel ?? 'none'} variantLevel=${variantConfig?.thinkingLevel ?? 'none'} variantBudget=${variantConfig?.thinkingBudget ?? 'none'} providerOptions.google=${JSON.stringify(requestPayload.providerOptions?.google ?? null)} generationConfig.thinkingConfig=${JSON.stringify(rawGenerationConfig?.thinkingConfig ?? null)}`);
|
|
1144
|
+
// providerOptions belongs to the host AI SDK and is only used above to
|
|
1145
|
+
// resolve the requested variant. It is never part of the Google wire schema.
|
|
1146
|
+
delete requestPayload.providerOptions;
|
|
865
1147
|
if (variantConfig?.thinkingLevel && isGemini3) {
|
|
866
1148
|
// Gemini 3 native format - use thinkingLevel directly
|
|
867
1149
|
const variantModelBase = rawModel
|
|
868
|
-
.replace(/-preview-customtools$/i,
|
|
869
|
-
.replace(/-preview$/i,
|
|
870
|
-
.replace(/-(minimal|low|medium|high)$/i,
|
|
1150
|
+
.replace(/-preview-customtools$/i, '')
|
|
1151
|
+
.replace(/-preview$/i, '')
|
|
1152
|
+
.replace(/-(minimal|low|medium|high)$/i, '');
|
|
871
1153
|
const variantResolved = resolveModelForHeaderStyle(`${variantModelBase}-${variantConfig.thinkingLevel}`, headerStyle);
|
|
872
1154
|
effectiveModel = variantResolved.actualModel;
|
|
873
1155
|
tierThinkingBudget = variantResolved.thinkingBudget;
|
|
874
|
-
tierThinkingLevel =
|
|
1156
|
+
tierThinkingLevel =
|
|
1157
|
+
variantResolved.thinkingLevel ??
|
|
1158
|
+
(variantResolved.thinkingBudget
|
|
1159
|
+
? undefined
|
|
1160
|
+
: variantConfig.thinkingLevel);
|
|
875
1161
|
}
|
|
876
1162
|
else if (variantConfig?.thinkingBudget) {
|
|
877
1163
|
if (isGemini3) {
|
|
878
1164
|
// Legacy format for Gemini 3 - convert with deprecation warning
|
|
879
|
-
log.warn(
|
|
880
|
-
tierThinkingLevel =
|
|
881
|
-
|
|
1165
|
+
log.warn('[Deprecated] Using thinkingBudget for Gemini 3 model. Use thinkingLevel instead.');
|
|
1166
|
+
tierThinkingLevel =
|
|
1167
|
+
variantConfig.thinkingBudget <= 8192
|
|
1168
|
+
? 'low'
|
|
1169
|
+
: variantConfig.thinkingBudget <= 16384
|
|
1170
|
+
? 'medium'
|
|
1171
|
+
: 'high';
|
|
882
1172
|
tierThinkingBudget = undefined;
|
|
883
1173
|
}
|
|
884
1174
|
else {
|
|
@@ -887,27 +1177,17 @@ export function prepareAntigravityRequest(input, init, accessToken, projectId, e
|
|
|
887
1177
|
tierThinkingLevel = undefined;
|
|
888
1178
|
}
|
|
889
1179
|
}
|
|
890
|
-
if (isClaude) {
|
|
891
|
-
if (!requestPayload.toolConfig) {
|
|
892
|
-
requestPayload.toolConfig = {};
|
|
893
|
-
}
|
|
894
|
-
if (typeof requestPayload.toolConfig === "object" && requestPayload.toolConfig !== null) {
|
|
895
|
-
const toolConfig = requestPayload.toolConfig;
|
|
896
|
-
if (!toolConfig.functionCallingConfig) {
|
|
897
|
-
toolConfig.functionCallingConfig = {};
|
|
898
|
-
}
|
|
899
|
-
if (typeof toolConfig.functionCallingConfig === "object" && toolConfig.functionCallingConfig !== null) {
|
|
900
|
-
toolConfig.functionCallingConfig.mode = "VALIDATED";
|
|
901
|
-
}
|
|
902
|
-
}
|
|
903
|
-
}
|
|
904
1180
|
// Resolve thinking configuration based on user settings and model capabilities
|
|
905
1181
|
// Image generation models don't support thinking - skip thinking config entirely
|
|
906
1182
|
const isImageModel = isImageGenerationModel(effectiveModel);
|
|
907
|
-
const userThinkingConfig = isImageModel
|
|
1183
|
+
const userThinkingConfig = isImageModel
|
|
1184
|
+
? undefined
|
|
1185
|
+
: extractThinkingConfig(requestPayload, rawGenerationConfig, extraBody);
|
|
908
1186
|
const hasAssistantHistory = Array.isArray(requestPayload.contents) &&
|
|
909
|
-
requestPayload.contents.some((c) => c?.role ===
|
|
910
|
-
const effectiveUserThinkingConfig = isImageModel
|
|
1187
|
+
requestPayload.contents.some((c) => c?.role === 'model' || c?.role === 'assistant');
|
|
1188
|
+
const effectiveUserThinkingConfig = isImageModel
|
|
1189
|
+
? undefined
|
|
1190
|
+
: userThinkingConfig;
|
|
911
1191
|
// For image models, add imageConfig instead of thinkingConfig
|
|
912
1192
|
if (isImageModel) {
|
|
913
1193
|
const imageConfig = buildImageGenerationConfig();
|
|
@@ -923,11 +1203,26 @@ export function prepareAntigravityRequest(input, init, accessToken, projectId, e
|
|
|
923
1203
|
// Add safety settings for image generation (permissive to allow creative content)
|
|
924
1204
|
if (!requestPayload.safetySettings) {
|
|
925
1205
|
requestPayload.safetySettings = [
|
|
926
|
-
{
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
{
|
|
1206
|
+
{
|
|
1207
|
+
category: 'HARM_CATEGORY_HARASSMENT',
|
|
1208
|
+
threshold: 'BLOCK_ONLY_HIGH',
|
|
1209
|
+
},
|
|
1210
|
+
{
|
|
1211
|
+
category: 'HARM_CATEGORY_HATE_SPEECH',
|
|
1212
|
+
threshold: 'BLOCK_ONLY_HIGH',
|
|
1213
|
+
},
|
|
1214
|
+
{
|
|
1215
|
+
category: 'HARM_CATEGORY_SEXUALLY_EXPLICIT',
|
|
1216
|
+
threshold: 'BLOCK_ONLY_HIGH',
|
|
1217
|
+
},
|
|
1218
|
+
{
|
|
1219
|
+
category: 'HARM_CATEGORY_DANGEROUS_CONTENT',
|
|
1220
|
+
threshold: 'BLOCK_ONLY_HIGH',
|
|
1221
|
+
},
|
|
1222
|
+
{
|
|
1223
|
+
category: 'HARM_CATEGORY_CIVIC_INTEGRITY',
|
|
1224
|
+
threshold: 'BLOCK_ONLY_HIGH',
|
|
1225
|
+
},
|
|
931
1226
|
];
|
|
932
1227
|
}
|
|
933
1228
|
// Image models don't support tools - remove them entirely
|
|
@@ -936,7 +1231,11 @@ export function prepareAntigravityRequest(input, init, accessToken, projectId, e
|
|
|
936
1231
|
// Replace system instruction with a simple image generation prompt
|
|
937
1232
|
// Image models should not receive agentic coding assistant instructions
|
|
938
1233
|
requestPayload.systemInstruction = {
|
|
939
|
-
parts: [
|
|
1234
|
+
parts: [
|
|
1235
|
+
{
|
|
1236
|
+
text: "You are an AI image generator. Generate images based on user descriptions. Focus on creating high-quality, visually appealing images that match the user's request.",
|
|
1237
|
+
},
|
|
1238
|
+
],
|
|
940
1239
|
};
|
|
941
1240
|
}
|
|
942
1241
|
else {
|
|
@@ -947,11 +1246,11 @@ export function prepareAntigravityRequest(input, init, accessToken, projectId, e
|
|
|
947
1246
|
const thinkingBudget = tierThinkingBudget ?? normalizedThinking.thinkingBudget;
|
|
948
1247
|
// Build thinking config based on model type
|
|
949
1248
|
let thinkingConfig;
|
|
950
|
-
if (isClaudeThinking && headerStyle !==
|
|
1249
|
+
if (isClaudeThinking && headerStyle !== 'antigravity') {
|
|
951
1250
|
// Claude-on-Gemini fallback uses snake_case keys.
|
|
952
1251
|
thinkingConfig = {
|
|
953
1252
|
include_thoughts: normalizedThinking.includeThoughts ?? true,
|
|
954
|
-
...(typeof thinkingBudget ===
|
|
1253
|
+
...(typeof thinkingBudget === 'number' && thinkingBudget > 0
|
|
955
1254
|
? { thinking_budget: thinkingBudget }
|
|
956
1255
|
: {}),
|
|
957
1256
|
};
|
|
@@ -967,19 +1266,25 @@ export function prepareAntigravityRequest(input, init, accessToken, projectId, e
|
|
|
967
1266
|
// Gemini 2.5 and others use numeric budget
|
|
968
1267
|
thinkingConfig = {
|
|
969
1268
|
includeThoughts: normalizedThinking.includeThoughts,
|
|
970
|
-
...(typeof thinkingBudget ===
|
|
1269
|
+
...(typeof thinkingBudget === 'number' && thinkingBudget > 0
|
|
1270
|
+
? { thinkingBudget }
|
|
1271
|
+
: {}),
|
|
971
1272
|
};
|
|
972
1273
|
}
|
|
973
1274
|
if (rawGenerationConfig) {
|
|
974
1275
|
rawGenerationConfig.thinkingConfig = thinkingConfig;
|
|
975
1276
|
applyAgyGenerationDefaults(effectiveModel, rawGenerationConfig, headerStyle);
|
|
976
|
-
if (isClaudeThinking &&
|
|
977
|
-
|
|
978
|
-
|
|
1277
|
+
if (isClaudeThinking &&
|
|
1278
|
+
typeof thinkingBudget === 'number' &&
|
|
1279
|
+
thinkingBudget > 0) {
|
|
1280
|
+
const currentMax = (rawGenerationConfig.maxOutputTokens ??
|
|
1281
|
+
rawGenerationConfig.max_output_tokens);
|
|
1282
|
+
if (headerStyle === 'antigravity' && !currentMax) {
|
|
979
1283
|
rawGenerationConfig.maxOutputTokens = 64000;
|
|
980
1284
|
}
|
|
981
1285
|
else if (!currentMax || currentMax <= thinkingBudget) {
|
|
982
|
-
rawGenerationConfig.maxOutputTokens =
|
|
1286
|
+
rawGenerationConfig.maxOutputTokens =
|
|
1287
|
+
computeClaudeMaxOutputTokens(thinkingBudget);
|
|
983
1288
|
}
|
|
984
1289
|
if (rawGenerationConfig.max_output_tokens !== undefined) {
|
|
985
1290
|
delete rawGenerationConfig.max_output_tokens;
|
|
@@ -988,11 +1293,16 @@ export function prepareAntigravityRequest(input, init, accessToken, projectId, e
|
|
|
988
1293
|
requestPayload.generationConfig = rawGenerationConfig;
|
|
989
1294
|
}
|
|
990
1295
|
else {
|
|
991
|
-
const generationConfig = {
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
1296
|
+
const generationConfig = {
|
|
1297
|
+
thinkingConfig,
|
|
1298
|
+
};
|
|
1299
|
+
if (isClaudeThinking &&
|
|
1300
|
+
typeof thinkingBudget === 'number' &&
|
|
1301
|
+
thinkingBudget > 0) {
|
|
1302
|
+
generationConfig.maxOutputTokens =
|
|
1303
|
+
headerStyle === 'antigravity'
|
|
1304
|
+
? 64000
|
|
1305
|
+
: computeClaudeMaxOutputTokens(thinkingBudget);
|
|
996
1306
|
}
|
|
997
1307
|
applyAgyGenerationDefaults(effectiveModel, generationConfig, headerStyle);
|
|
998
1308
|
requestPayload.generationConfig = generationConfig;
|
|
@@ -1015,29 +1325,38 @@ export function prepareAntigravityRequest(input, init, accessToken, projectId, e
|
|
|
1015
1325
|
}
|
|
1016
1326
|
delete requestPayload.thinkingConfig;
|
|
1017
1327
|
delete requestPayload.thinking;
|
|
1018
|
-
if (
|
|
1328
|
+
if ('system_instruction' in requestPayload) {
|
|
1019
1329
|
requestPayload.systemInstruction = requestPayload.system_instruction;
|
|
1020
1330
|
delete requestPayload.system_instruction;
|
|
1021
1331
|
}
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1332
|
+
if (headerStyle !== 'antigravity') {
|
|
1333
|
+
// Gemini CLI accepts explicit cachedContent references. Normalize its
|
|
1334
|
+
// snake_case aliases only on that transport; agy relies on implicit
|
|
1335
|
+
// prefix caching and does not send any of these fields.
|
|
1336
|
+
const cachedContentFromExtra = typeof requestPayload.extra_body === 'object' &&
|
|
1337
|
+
requestPayload.extra_body
|
|
1338
|
+
? (requestPayload.extra_body
|
|
1339
|
+
.cached_content ??
|
|
1340
|
+
requestPayload.extra_body
|
|
1341
|
+
.cachedContent)
|
|
1342
|
+
: undefined;
|
|
1343
|
+
const cachedContent = requestPayload.cached_content ??
|
|
1344
|
+
requestPayload.cachedContent ??
|
|
1345
|
+
cachedContentFromExtra;
|
|
1346
|
+
if (cachedContent) {
|
|
1347
|
+
requestPayload.cachedContent = cachedContent;
|
|
1348
|
+
}
|
|
1349
|
+
delete requestPayload.cached_content;
|
|
1350
|
+
if (requestPayload.extra_body &&
|
|
1351
|
+
typeof requestPayload.extra_body === 'object') {
|
|
1352
|
+
delete requestPayload.extra_body
|
|
1353
|
+
.cached_content;
|
|
1354
|
+
delete requestPayload.extra_body
|
|
1355
|
+
.cachedContent;
|
|
1356
|
+
if (Object.keys(requestPayload.extra_body)
|
|
1357
|
+
.length === 0) {
|
|
1358
|
+
delete requestPayload.extra_body;
|
|
1359
|
+
}
|
|
1041
1360
|
}
|
|
1042
1361
|
}
|
|
1043
1362
|
// Normalize tools. For Claude models, keep full function declarations (names + schemas).
|
|
@@ -1049,39 +1368,46 @@ export function prepareAntigravityRequest(input, init, accessToken, projectId, e
|
|
|
1049
1368
|
const normalizeSchema = (schema) => {
|
|
1050
1369
|
const createPlaceholderSchema = (base = {}) => ({
|
|
1051
1370
|
...base,
|
|
1052
|
-
type:
|
|
1371
|
+
type: 'object',
|
|
1053
1372
|
properties: {
|
|
1054
1373
|
[EMPTY_SCHEMA_PLACEHOLDER_NAME]: {
|
|
1055
|
-
type:
|
|
1374
|
+
type: 'boolean',
|
|
1056
1375
|
description: EMPTY_SCHEMA_PLACEHOLDER_DESCRIPTION,
|
|
1057
1376
|
},
|
|
1058
1377
|
},
|
|
1059
1378
|
required: [EMPTY_SCHEMA_PLACEHOLDER_NAME],
|
|
1060
1379
|
});
|
|
1061
|
-
if (!schema ||
|
|
1380
|
+
if (!schema ||
|
|
1381
|
+
typeof schema !== 'object' ||
|
|
1382
|
+
Array.isArray(schema)) {
|
|
1062
1383
|
toolDebugMissing += 1;
|
|
1063
1384
|
return createPlaceholderSchema();
|
|
1064
1385
|
}
|
|
1065
1386
|
const cleaned = cleanJSONSchemaForAntigravity(schema);
|
|
1066
|
-
if (!cleaned ||
|
|
1387
|
+
if (!cleaned ||
|
|
1388
|
+
typeof cleaned !== 'object' ||
|
|
1389
|
+
Array.isArray(cleaned)) {
|
|
1067
1390
|
toolDebugMissing += 1;
|
|
1068
1391
|
return createPlaceholderSchema();
|
|
1069
1392
|
}
|
|
1070
1393
|
// Claude VALIDATED mode requires tool parameters to be an object schema
|
|
1071
1394
|
// with at least one property.
|
|
1072
1395
|
const hasProperties = cleaned.properties &&
|
|
1073
|
-
typeof cleaned.properties ===
|
|
1396
|
+
typeof cleaned.properties === 'object' &&
|
|
1074
1397
|
Object.keys(cleaned.properties).length > 0;
|
|
1075
|
-
cleaned.type =
|
|
1398
|
+
cleaned.type = 'object';
|
|
1076
1399
|
if (!hasProperties) {
|
|
1077
1400
|
cleaned.properties = {
|
|
1078
1401
|
[EMPTY_SCHEMA_PLACEHOLDER_NAME]: {
|
|
1079
|
-
type:
|
|
1402
|
+
type: 'boolean',
|
|
1080
1403
|
description: EMPTY_SCHEMA_PLACEHOLDER_DESCRIPTION,
|
|
1081
1404
|
},
|
|
1082
1405
|
};
|
|
1083
1406
|
cleaned.required = Array.isArray(cleaned.required)
|
|
1084
|
-
? Array.from(new Set([
|
|
1407
|
+
? Array.from(new Set([
|
|
1408
|
+
...cleaned.required,
|
|
1409
|
+
EMPTY_SCHEMA_PLACEHOLDER_NAME,
|
|
1410
|
+
]))
|
|
1085
1411
|
: [EMPTY_SCHEMA_PLACEHOLDER_NAME];
|
|
1086
1412
|
}
|
|
1087
1413
|
return cleaned;
|
|
@@ -1109,21 +1435,26 @@ export function prepareAntigravityRequest(input, init, accessToken, projectId, e
|
|
|
1109
1435
|
tool.custom?.name ||
|
|
1110
1436
|
`tool-${functionDeclarations.length}`;
|
|
1111
1437
|
// Sanitize tool name: must be alphanumeric with underscores, no special chars
|
|
1112
|
-
name = String(name)
|
|
1438
|
+
name = String(name)
|
|
1439
|
+
.replace(/[^a-zA-Z0-9_-]/g, '_')
|
|
1440
|
+
.slice(0, 64);
|
|
1113
1441
|
const description = decl?.description ||
|
|
1114
1442
|
tool.description ||
|
|
1115
1443
|
tool.function?.description ||
|
|
1116
1444
|
tool.custom?.description ||
|
|
1117
|
-
|
|
1445
|
+
'';
|
|
1118
1446
|
functionDeclarations.push({
|
|
1119
1447
|
name,
|
|
1120
|
-
description: String(description ||
|
|
1448
|
+
description: String(description || ''),
|
|
1121
1449
|
parameters: normalizeSchema(schema),
|
|
1122
1450
|
});
|
|
1123
|
-
toolDebugSummaries.push(`decl=${name},src=${source},hasSchema=${schema ?
|
|
1451
|
+
toolDebugSummaries.push(`decl=${name},src=${source},hasSchema=${schema ? 'y' : 'n'}`);
|
|
1124
1452
|
};
|
|
1125
|
-
if (Array.isArray(tool.functionDeclarations) &&
|
|
1126
|
-
tool.functionDeclarations.
|
|
1453
|
+
if (Array.isArray(tool.functionDeclarations) &&
|
|
1454
|
+
tool.functionDeclarations.length > 0) {
|
|
1455
|
+
tool.functionDeclarations.forEach((decl) => {
|
|
1456
|
+
pushDeclaration(decl, 'functionDeclarations');
|
|
1457
|
+
});
|
|
1127
1458
|
return;
|
|
1128
1459
|
}
|
|
1129
1460
|
// Fall back to function/custom style definitions.
|
|
@@ -1132,7 +1463,7 @@ export function prepareAntigravityRequest(input, init, accessToken, projectId, e
|
|
|
1132
1463
|
tool.parameters ||
|
|
1133
1464
|
tool.input_schema ||
|
|
1134
1465
|
tool.inputSchema) {
|
|
1135
|
-
pushDeclaration(tool.function ?? tool.custom ?? tool,
|
|
1466
|
+
pushDeclaration(tool.function ?? tool.custom ?? tool, 'function/custom');
|
|
1136
1467
|
return;
|
|
1137
1468
|
}
|
|
1138
1469
|
// Preserve any non-function tool entries (e.g., codeExecution) untouched.
|
|
@@ -1165,7 +1496,10 @@ export function prepareAntigravityRequest(input, init, accessToken, projectId, e
|
|
|
1165
1496
|
// Injects parameter signatures into descriptions and adds system instruction
|
|
1166
1497
|
// Can be disabled via config.claude_tool_hardening = false to reduce context size
|
|
1167
1498
|
const enableToolHardening = options?.claudeToolHardening ?? true;
|
|
1168
|
-
if (enableToolHardening &&
|
|
1499
|
+
if (enableToolHardening &&
|
|
1500
|
+
isClaude &&
|
|
1501
|
+
Array.isArray(requestPayload.tools) &&
|
|
1502
|
+
requestPayload.tools.length > 0) {
|
|
1169
1503
|
// Inject parameter signatures into tool descriptions
|
|
1170
1504
|
requestPayload.tools = injectParameterSignatures(requestPayload.tools, CLAUDE_DESCRIPTION_PROMPT);
|
|
1171
1505
|
// Inject tool hardening system instruction
|
|
@@ -1174,7 +1508,9 @@ export function prepareAntigravityRequest(input, init, accessToken, projectId, e
|
|
|
1174
1508
|
// Append interleaved thinking hint for Claude thinking models with tools.
|
|
1175
1509
|
// Must come AFTER tool hardening so it is the last system instruction part,
|
|
1176
1510
|
// preserving the stable prefix for prompt cache matching.
|
|
1177
|
-
if (isClaudeThinking &&
|
|
1511
|
+
if (isClaudeThinking &&
|
|
1512
|
+
Array.isArray(requestPayload.tools) &&
|
|
1513
|
+
requestPayload.tools.length > 0) {
|
|
1178
1514
|
appendClaudeThinkingHint(requestPayload);
|
|
1179
1515
|
}
|
|
1180
1516
|
}
|
|
@@ -1185,24 +1521,35 @@ export function prepareAntigravityRequest(input, init, accessToken, projectId, e
|
|
|
1185
1521
|
// Handle both Gemini-style contents[] and Anthropic-style messages[] payloads.
|
|
1186
1522
|
if (isClaude) {
|
|
1187
1523
|
// Step 0: Sanitize cross-model metadata (strips Gemini signatures when sending to Claude)
|
|
1188
|
-
sanitizeCrossModelPayloadInPlace(requestPayload, {
|
|
1524
|
+
sanitizeCrossModelPayloadInPlace(requestPayload, {
|
|
1525
|
+
targetModel: effectiveModel,
|
|
1526
|
+
});
|
|
1189
1527
|
// Step 1: Strip corrupted/unsigned thinking blocks FIRST
|
|
1190
1528
|
deepFilterThinkingBlocks(requestPayload, signatureSessionKey, getCachedSignature, true);
|
|
1191
1529
|
// Step 2: THEN inject signed thinking from cache (after stripping)
|
|
1192
|
-
if (isClaudeThinking &&
|
|
1530
|
+
if (isClaudeThinking &&
|
|
1531
|
+
keepThinkingEnabled &&
|
|
1532
|
+
Array.isArray(requestPayload.contents)) {
|
|
1193
1533
|
requestPayload.contents = ensureThinkingBeforeToolUseInContents(requestPayload.contents, signatureSessionKey);
|
|
1194
1534
|
}
|
|
1195
|
-
if (isClaudeThinking &&
|
|
1535
|
+
if (isClaudeThinking &&
|
|
1536
|
+
keepThinkingEnabled &&
|
|
1537
|
+
Array.isArray(requestPayload.messages)) {
|
|
1196
1538
|
requestPayload.messages = ensureThinkingBeforeToolUseInMessages(requestPayload.messages, signatureSessionKey);
|
|
1197
1539
|
}
|
|
1198
1540
|
// Step 3: Check if warmup needed (AFTER injection attempt)
|
|
1199
1541
|
if (isClaudeThinking && keepThinkingEnabled) {
|
|
1200
|
-
const hasToolUse = (Array.isArray(requestPayload.contents) &&
|
|
1201
|
-
(
|
|
1202
|
-
|
|
1203
|
-
|
|
1542
|
+
const hasToolUse = (Array.isArray(requestPayload.contents) &&
|
|
1543
|
+
hasToolUseInContents(requestPayload.contents)) ||
|
|
1544
|
+
(Array.isArray(requestPayload.messages) &&
|
|
1545
|
+
hasToolUseInMessages(requestPayload.messages));
|
|
1546
|
+
const hasSignedThinking = (Array.isArray(requestPayload.contents) &&
|
|
1547
|
+
hasSignedThinkingInContents(requestPayload.contents, signatureSessionKey)) ||
|
|
1548
|
+
(Array.isArray(requestPayload.messages) &&
|
|
1549
|
+
hasSignedThinkingInMessages(requestPayload.messages, signatureSessionKey));
|
|
1204
1550
|
const hasCachedThinking = defaultSignatureStore.has(signatureSessionKey);
|
|
1205
|
-
needsSignedThinkingWarmup =
|
|
1551
|
+
needsSignedThinkingWarmup =
|
|
1552
|
+
hasToolUse && !hasSignedThinking && !hasCachedThinking;
|
|
1206
1553
|
}
|
|
1207
1554
|
}
|
|
1208
1555
|
// For Claude models, ensure functionCall/tool use parts carry IDs (required by Anthropic).
|
|
@@ -1218,12 +1565,14 @@ export function prepareAntigravityRequest(input, init, accessToken, projectId, e
|
|
|
1218
1565
|
return content;
|
|
1219
1566
|
}
|
|
1220
1567
|
const newParts = content.parts.map((part) => {
|
|
1221
|
-
if (part && typeof part ===
|
|
1568
|
+
if (part && typeof part === 'object' && part.functionCall) {
|
|
1222
1569
|
const call = { ...part.functionCall };
|
|
1223
1570
|
if (!call.id) {
|
|
1224
1571
|
call.id = `tool-call-${++toolCallCounter}`;
|
|
1225
1572
|
}
|
|
1226
|
-
const nameKey = typeof call.name ===
|
|
1573
|
+
const nameKey = typeof call.name === 'string'
|
|
1574
|
+
? call.name
|
|
1575
|
+
: `tool-${toolCallCounter}`;
|
|
1227
1576
|
// Push to the queue for this function name
|
|
1228
1577
|
const queue = pendingCallIdsByName.get(nameKey) || [];
|
|
1229
1578
|
queue.push(call.id);
|
|
@@ -1240,9 +1589,9 @@ export function prepareAntigravityRequest(input, init, accessToken, projectId, e
|
|
|
1240
1589
|
return content;
|
|
1241
1590
|
}
|
|
1242
1591
|
const newParts = content.parts.map((part) => {
|
|
1243
|
-
if (part && typeof part ===
|
|
1592
|
+
if (part && typeof part === 'object' && part.functionResponse) {
|
|
1244
1593
|
const resp = { ...part.functionResponse };
|
|
1245
|
-
if (!resp.id && typeof resp.name ===
|
|
1594
|
+
if (!resp.id && typeof resp.name === 'string') {
|
|
1246
1595
|
const queue = pendingCallIdsByName.get(resp.name);
|
|
1247
1596
|
if (queue && queue.length > 0) {
|
|
1248
1597
|
// Consume the first pending ID (FIFO order)
|
|
@@ -1286,68 +1635,90 @@ export function prepareAntigravityRequest(input, init, accessToken, projectId, e
|
|
|
1286
1635
|
const conversationState = analyzeConversationState(requestPayload.contents);
|
|
1287
1636
|
// Force recovery if API returned thinking_block_order error (retry case)
|
|
1288
1637
|
// or if proactive check detects we need recovery
|
|
1289
|
-
if (forceThinkingRecovery ||
|
|
1638
|
+
if (forceThinkingRecovery ||
|
|
1639
|
+
needsThinkingRecovery(conversationState)) {
|
|
1290
1640
|
// Set message for toast notification (shown in plugin.ts, respects quiet mode)
|
|
1291
1641
|
thinkingRecoveryMessage = forceThinkingRecovery
|
|
1292
|
-
?
|
|
1293
|
-
:
|
|
1642
|
+
? 'Thinking recovery: retrying with fresh turn (API error)'
|
|
1643
|
+
: 'Thinking recovery: restarting turn (corrupted context)';
|
|
1294
1644
|
requestPayload.contents = closeToolLoopForThinking(requestPayload.contents);
|
|
1295
1645
|
defaultSignatureStore.delete(signatureSessionKey);
|
|
1296
1646
|
}
|
|
1297
1647
|
}
|
|
1298
|
-
//
|
|
1299
|
-
//
|
|
1300
|
-
//
|
|
1301
|
-
|
|
1648
|
+
// AGY rejects every request that ends with a model turn. Enforce this at
|
|
1649
|
+
// the final wire boundary because host races, recovery, or sanitization can
|
|
1650
|
+
// otherwise leave a model/assistant entry last. Claude fallback transports
|
|
1651
|
+
// have the same assistant-prefill restriction.
|
|
1652
|
+
if (headerStyle === 'antigravity' || isClaude) {
|
|
1302
1653
|
if (Array.isArray(requestPayload.contents)) {
|
|
1303
1654
|
const lastContent = requestPayload.contents[requestPayload.contents.length - 1];
|
|
1304
|
-
if (lastContent?.role ===
|
|
1305
|
-
|
|
1655
|
+
if (lastContent?.role === 'model' ||
|
|
1656
|
+
lastContent?.role === 'assistant') {
|
|
1657
|
+
requestPayload.contents.push({
|
|
1658
|
+
role: 'user',
|
|
1659
|
+
parts: [{ text: '[Continue]' }],
|
|
1660
|
+
});
|
|
1306
1661
|
}
|
|
1307
1662
|
}
|
|
1308
1663
|
if (Array.isArray(requestPayload.messages)) {
|
|
1309
1664
|
const lastMessage = requestPayload.messages[requestPayload.messages.length - 1];
|
|
1310
|
-
if (lastMessage?.role ===
|
|
1311
|
-
|
|
1665
|
+
if (lastMessage?.role === 'model' ||
|
|
1666
|
+
lastMessage?.role === 'assistant') {
|
|
1667
|
+
;
|
|
1668
|
+
requestPayload.messages.push({
|
|
1669
|
+
role: 'user',
|
|
1670
|
+
parts: [{ text: '[Continue]' }],
|
|
1671
|
+
});
|
|
1312
1672
|
}
|
|
1313
1673
|
}
|
|
1314
1674
|
}
|
|
1315
|
-
if (
|
|
1675
|
+
if ('model' in requestPayload) {
|
|
1316
1676
|
delete requestPayload.model;
|
|
1317
1677
|
}
|
|
1318
1678
|
stripInjectedDebugFromRequestPayload(requestPayload);
|
|
1319
1679
|
sanitizeRequestPayloadForAntigravity(requestPayload);
|
|
1680
|
+
if (headerStyle === 'antigravity') {
|
|
1681
|
+
stripUnsupportedAntigravityFields(requestPayload);
|
|
1682
|
+
configureAntigravityToolCalling(requestPayload);
|
|
1683
|
+
}
|
|
1320
1684
|
// Use the stable default project ID (never a per-request random one):
|
|
1321
1685
|
// a fresh random project each request busts the prompt cache and
|
|
1322
1686
|
// fragments server-side quota/session state. ensureProjectContext
|
|
1323
1687
|
// already floors empty cases to this default; this is defense in depth.
|
|
1324
|
-
const effectiveProjectId = projectId?.trim() ||
|
|
1688
|
+
const effectiveProjectId = projectId?.trim() ||
|
|
1689
|
+
(headerStyle === 'antigravity' ? ANTIGRAVITY_DEFAULT_PROJECT_ID : '');
|
|
1325
1690
|
resolvedProjectId = effectiveProjectId;
|
|
1326
|
-
//
|
|
1327
|
-
|
|
1691
|
+
// Keep internal signature-cache identity separate from AGY wire session metadata.
|
|
1692
|
+
sessionId = signatureSessionKey;
|
|
1693
|
+
const agyMetadata = headerStyle === 'antigravity'
|
|
1694
|
+
? buildAgyAgentRequestMetadata(options?.agySession ?? DEFAULT_AGY_REQUEST_SESSION, requestPayload, effectiveModel, options?.agyRequestTimestamp)
|
|
1695
|
+
: null;
|
|
1696
|
+
requestPayload.sessionId = agyMetadata?.sessionId ?? signatureSessionKey;
|
|
1697
|
+
if (agyMetadata) {
|
|
1698
|
+
requestPayload.labels = agyMetadata.labels;
|
|
1699
|
+
orderAgyRequestPayloadInPlace(requestPayload);
|
|
1700
|
+
}
|
|
1701
|
+
const wrappedBody = headerStyle === 'antigravity'
|
|
1328
1702
|
? {
|
|
1329
1703
|
project: effectiveProjectId,
|
|
1330
|
-
requestId:
|
|
1704
|
+
requestId: agyMetadata?.requestId,
|
|
1331
1705
|
request: requestPayload,
|
|
1332
1706
|
model: effectiveModel,
|
|
1333
|
-
userAgent:
|
|
1334
|
-
requestType:
|
|
1707
|
+
userAgent: 'antigravity',
|
|
1708
|
+
requestType: 'agent',
|
|
1335
1709
|
}
|
|
1336
1710
|
: {
|
|
1337
1711
|
project: effectiveProjectId,
|
|
1338
1712
|
model: effectiveModel,
|
|
1339
1713
|
request: requestPayload,
|
|
1340
1714
|
};
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
wrappedBody.request.sessionId = signatureSessionKey;
|
|
1345
|
-
}
|
|
1346
|
-
body = safeStringify(headerStyle === "antigravity" ? orderAntigravityEnvelope(wrappedBody) : wrappedBody);
|
|
1715
|
+
body = safeStringify(headerStyle === 'antigravity'
|
|
1716
|
+
? orderAntigravityEnvelope(wrappedBody)
|
|
1717
|
+
: wrappedBody);
|
|
1347
1718
|
}
|
|
1348
1719
|
}
|
|
1349
1720
|
catch {
|
|
1350
|
-
throw new Error(
|
|
1721
|
+
throw new Error('Failed to build Antigravity request body');
|
|
1351
1722
|
}
|
|
1352
1723
|
}
|
|
1353
1724
|
// agy CLI does not send an Accept header on streamGenerateContent requests.
|
|
@@ -1355,36 +1726,36 @@ export function prepareAntigravityRequest(input, init, accessToken, projectId, e
|
|
|
1355
1726
|
// Add interleaved thinking header for Claude thinking models
|
|
1356
1727
|
// This enables real-time streaming of thinking tokens
|
|
1357
1728
|
if (isClaudeThinking) {
|
|
1358
|
-
const existing = headers.get(
|
|
1359
|
-
const interleavedHeader =
|
|
1729
|
+
const existing = headers.get('anthropic-beta');
|
|
1730
|
+
const interleavedHeader = 'interleaved-thinking-2025-05-14';
|
|
1360
1731
|
if (existing) {
|
|
1361
1732
|
if (!existing.includes(interleavedHeader)) {
|
|
1362
|
-
headers.set(
|
|
1733
|
+
headers.set('anthropic-beta', `${existing},${interleavedHeader}`);
|
|
1363
1734
|
}
|
|
1364
1735
|
}
|
|
1365
1736
|
else {
|
|
1366
|
-
headers.set(
|
|
1737
|
+
headers.set('anthropic-beta', interleavedHeader);
|
|
1367
1738
|
}
|
|
1368
1739
|
}
|
|
1369
|
-
if (headerStyle ===
|
|
1740
|
+
if (headerStyle === 'antigravity') {
|
|
1370
1741
|
// Use randomized headers as the fallback pool for Antigravity mode
|
|
1371
|
-
const selectedHeaders = getRandomizedHeaders(
|
|
1742
|
+
const selectedHeaders = getRandomizedHeaders('antigravity', requestedModel);
|
|
1372
1743
|
// Antigravity mode: Match Antigravity Manager behavior
|
|
1373
1744
|
// AM only sends User-Agent on content requests — no X-Goog-Api-Client, no Client-Metadata header
|
|
1374
1745
|
// (ideType=ANTIGRAVITY goes in request body metadata via project.ts, not as a header)
|
|
1375
1746
|
const fingerprint = options?.fingerprint ?? getSessionFingerprint();
|
|
1376
1747
|
const fingerprintHeaders = buildFingerprintHeaders(fingerprint);
|
|
1377
|
-
headers.set(
|
|
1378
|
-
headers.set(
|
|
1748
|
+
headers.set('User-Agent', fingerprintHeaders['User-Agent'] || selectedHeaders['User-Agent']);
|
|
1749
|
+
headers.set('Accept-Encoding', 'gzip');
|
|
1379
1750
|
}
|
|
1380
1751
|
else {
|
|
1381
1752
|
// Gemini CLI mode: match official google-gemini/gemini-cli User-Agent format
|
|
1382
|
-
const geminiCliHeaders = getRandomizedHeaders(
|
|
1383
|
-
headers.set(
|
|
1384
|
-
if (geminiCliHeaders[
|
|
1385
|
-
headers.set(
|
|
1386
|
-
if (geminiCliHeaders[
|
|
1387
|
-
headers.set(
|
|
1753
|
+
const geminiCliHeaders = getRandomizedHeaders('gemini-cli', requestedModel);
|
|
1754
|
+
headers.set('User-Agent', geminiCliHeaders['User-Agent']);
|
|
1755
|
+
if (geminiCliHeaders['X-Goog-Api-Client'])
|
|
1756
|
+
headers.set('X-Goog-Api-Client', geminiCliHeaders['X-Goog-Api-Client']);
|
|
1757
|
+
if (geminiCliHeaders['Client-Metadata'])
|
|
1758
|
+
headers.set('Client-Metadata', geminiCliHeaders['Client-Metadata']);
|
|
1388
1759
|
}
|
|
1389
1760
|
return {
|
|
1390
1761
|
request: transformedUrl,
|
|
@@ -1400,7 +1771,7 @@ export function prepareAntigravityRequest(input, init, accessToken, projectId, e
|
|
|
1400
1771
|
endpoint: transformedUrl,
|
|
1401
1772
|
sessionId,
|
|
1402
1773
|
toolDebugMissing,
|
|
1403
|
-
toolDebugSummary: toolDebugSummaries.slice(0, 20).join(
|
|
1774
|
+
toolDebugSummary: toolDebugSummaries.slice(0, 20).join(' | '),
|
|
1404
1775
|
toolDebugPayload,
|
|
1405
1776
|
needsSignedThinkingWarmup,
|
|
1406
1777
|
headerStyle,
|
|
@@ -1418,29 +1789,50 @@ export function buildThinkingWarmupBody(bodyText, isClaudeThinking) {
|
|
|
1418
1789
|
catch {
|
|
1419
1790
|
return null;
|
|
1420
1791
|
}
|
|
1421
|
-
const warmupPrompt =
|
|
1792
|
+
const warmupPrompt = 'Warmup request for thinking signature.';
|
|
1793
|
+
const wireModel = typeof parsed.model === 'string' ? parsed.model : 'claude-sonnet-4-6';
|
|
1794
|
+
const requestObjects = [];
|
|
1422
1795
|
const updateRequest = (req) => {
|
|
1423
|
-
req.contents = [{ role:
|
|
1796
|
+
req.contents = [{ role: 'user', parts: [{ text: warmupPrompt }] }];
|
|
1424
1797
|
delete req.tools;
|
|
1425
1798
|
delete req.toolConfig;
|
|
1426
1799
|
const generationConfig = (req.generationConfig ?? {});
|
|
1427
1800
|
generationConfig.thinkingConfig = {
|
|
1428
|
-
|
|
1429
|
-
|
|
1801
|
+
includeThoughts: true,
|
|
1802
|
+
thinkingBudget: AGY_CLAUDE_THINKING_BUDGET,
|
|
1430
1803
|
};
|
|
1431
|
-
generationConfig.maxOutputTokens =
|
|
1804
|
+
generationConfig.maxOutputTokens =
|
|
1805
|
+
getAgyMaxOutputTokens(wireModel) ??
|
|
1806
|
+
computeClaudeMaxOutputTokens(AGY_CLAUDE_THINKING_BUDGET);
|
|
1432
1807
|
req.generationConfig = generationConfig;
|
|
1808
|
+
requestObjects.push(req);
|
|
1433
1809
|
};
|
|
1434
|
-
if (parsed.request && typeof parsed.request ===
|
|
1810
|
+
if (parsed.request && typeof parsed.request === 'object') {
|
|
1435
1811
|
updateRequest(parsed.request);
|
|
1436
1812
|
const nested = parsed.request.request;
|
|
1437
|
-
if (nested && typeof nested ===
|
|
1813
|
+
if (nested && typeof nested === 'object') {
|
|
1438
1814
|
updateRequest(nested);
|
|
1439
1815
|
}
|
|
1440
1816
|
}
|
|
1441
1817
|
else {
|
|
1442
1818
|
updateRequest(parsed);
|
|
1443
1819
|
}
|
|
1820
|
+
const wireRequest = requestObjects.at(-1);
|
|
1821
|
+
if (wireRequest) {
|
|
1822
|
+
const numericSessionId = typeof wireRequest.sessionId === 'string'
|
|
1823
|
+
? wireRequest.sessionId
|
|
1824
|
+
: DEFAULT_AGY_REQUEST_SESSION.numericSessionId;
|
|
1825
|
+
const warmupSession = {
|
|
1826
|
+
conversationId: crypto.randomUUID(),
|
|
1827
|
+
trajectoryId: crypto.randomUUID(),
|
|
1828
|
+
numericSessionId,
|
|
1829
|
+
};
|
|
1830
|
+
const metadata = buildAgyAgentRequestMetadata(warmupSession, wireRequest, wireModel);
|
|
1831
|
+
parsed.requestId = metadata.requestId;
|
|
1832
|
+
wireRequest.sessionId = metadata.sessionId;
|
|
1833
|
+
wireRequest.labels = metadata.labels;
|
|
1834
|
+
orderAgyRequestPayloadInPlace(wireRequest);
|
|
1835
|
+
}
|
|
1444
1836
|
return safeStringify(parsed);
|
|
1445
1837
|
}
|
|
1446
1838
|
/**
|
|
@@ -1451,9 +1843,9 @@ export function buildThinkingWarmupBody(bodyText, isClaudeThinking) {
|
|
|
1451
1843
|
* Thinking/reasoning tokens are transformed and forwarded immediately as they arrive.
|
|
1452
1844
|
*/
|
|
1453
1845
|
export async function transformAntigravityResponse(response, streaming, debugContext, requestedModel, projectId, endpoint, effectiveModel, sessionId, toolDebugMissing, toolDebugSummary, toolDebugPayload, debugLines, dumpContext) {
|
|
1454
|
-
const contentType = response.headers.get(
|
|
1455
|
-
const isJsonResponse = contentType.includes(
|
|
1456
|
-
const isEventStreamResponse = contentType.includes(
|
|
1846
|
+
const contentType = response.headers.get('content-type') ?? '';
|
|
1847
|
+
const isJsonResponse = contentType.includes('application/json');
|
|
1848
|
+
const isEventStreamResponse = contentType.includes('text/event-stream');
|
|
1457
1849
|
// Generate text for thinking injection:
|
|
1458
1850
|
// - If debug=true: inject full debug logs
|
|
1459
1851
|
// - If keep_thinking=true (but no debug): inject placeholder to trigger signature caching
|
|
@@ -1466,7 +1858,7 @@ export async function transformAntigravityResponse(response, streaming, debugCon
|
|
|
1466
1858
|
const cacheSignatures = shouldCacheThinkingSignatures(effectiveModel);
|
|
1467
1859
|
if (!isJsonResponse && !isEventStreamResponse) {
|
|
1468
1860
|
logAntigravityDebugResponse(debugContext, response, {
|
|
1469
|
-
note:
|
|
1861
|
+
note: 'Non-JSON response (body omitted)',
|
|
1470
1862
|
});
|
|
1471
1863
|
return response;
|
|
1472
1864
|
}
|
|
@@ -1476,11 +1868,13 @@ export async function transformAntigravityResponse(response, streaming, debugCon
|
|
|
1476
1868
|
if (streaming && response.ok && isEventStreamResponse && response.body) {
|
|
1477
1869
|
const headers = new Headers(response.headers);
|
|
1478
1870
|
logAntigravityDebugResponse(debugContext, response, {
|
|
1479
|
-
note:
|
|
1871
|
+
note: 'Streaming SSE response (real-time transform)',
|
|
1480
1872
|
});
|
|
1481
1873
|
noteGeminiDumpResponse(dumpContext, response);
|
|
1482
1874
|
const rawDumpTransformer = createGeminiDumpResponseTransform(dumpContext);
|
|
1483
|
-
const sourceBody = rawDumpTransformer
|
|
1875
|
+
const sourceBody = rawDumpTransformer
|
|
1876
|
+
? response.body.pipeThrough(rawDumpTransformer)
|
|
1877
|
+
: response.body;
|
|
1484
1878
|
const streamingTransformer = createStreamingTransformer(defaultSignatureStore, {
|
|
1485
1879
|
onCacheSignature: cacheSignature,
|
|
1486
1880
|
onInjectDebug: injectDebugThinking,
|
|
@@ -1489,10 +1883,15 @@ export async function transformAntigravityResponse(response, streaming, debugCon
|
|
|
1489
1883
|
const cacheRead = usage.cachedContentTokenCount;
|
|
1490
1884
|
const totalInput = usage.promptTokenCount ?? usage.totalTokenCount;
|
|
1491
1885
|
const hitRate = totalInput > 0 ? Math.round((cacheRead / totalInput) * 100) : 0;
|
|
1492
|
-
const status = cacheRead > 0 ?
|
|
1886
|
+
const status = cacheRead > 0 ? 'HIT' : 'MISS';
|
|
1493
1887
|
logCacheStats(effectiveModel, cacheRead, 0, totalInput);
|
|
1494
1888
|
log.debug(`[Cache] ${status} model=${effectiveModel} read=${cacheRead} total=${totalInput} hitRate=${hitRate}%`);
|
|
1495
|
-
_lastCacheStats = {
|
|
1889
|
+
_lastCacheStats = {
|
|
1890
|
+
model: effectiveModel,
|
|
1891
|
+
read: cacheRead,
|
|
1892
|
+
total: totalInput,
|
|
1893
|
+
hitRate,
|
|
1894
|
+
};
|
|
1496
1895
|
}
|
|
1497
1896
|
},
|
|
1498
1897
|
transformThinkingParts,
|
|
@@ -1500,7 +1899,9 @@ export async function transformAntigravityResponse(response, streaming, debugCon
|
|
|
1500
1899
|
signatureSessionKey: sessionId,
|
|
1501
1900
|
debugText,
|
|
1502
1901
|
cacheSignatures,
|
|
1503
|
-
displayedThinkingHashes: effectiveModel && isGemini3Model(effectiveModel)
|
|
1902
|
+
displayedThinkingHashes: effectiveModel && isGemini3Model(effectiveModel)
|
|
1903
|
+
? sessionDisplayedThinkingHashes
|
|
1904
|
+
: undefined,
|
|
1504
1905
|
// injectSyntheticThinking removed - keep_thinking now unified with debug via debugText
|
|
1505
1906
|
});
|
|
1506
1907
|
return new Response(sourceBody.pipeThrough(streamingTransformer), {
|
|
@@ -1525,48 +1926,50 @@ export async function transformAntigravityResponse(response, streaming, debugCon
|
|
|
1525
1926
|
}
|
|
1526
1927
|
// Inject Debug Info
|
|
1527
1928
|
if (errorBody?.error) {
|
|
1528
|
-
const rawErrorMessage = typeof errorBody.error.message ===
|
|
1929
|
+
const rawErrorMessage = typeof errorBody.error.message === 'string' &&
|
|
1930
|
+
errorBody.error.message.length > 0
|
|
1529
1931
|
? errorBody.error.message
|
|
1530
|
-
:
|
|
1932
|
+
: 'Unknown error';
|
|
1531
1933
|
const errorType = detectErrorType(rawErrorMessage);
|
|
1532
|
-
const debugInfo = `\n\n[Debug Info]\nRequested Model: ${requestedModel ||
|
|
1533
|
-
const injectedDebug = debugText ? `\n\n${debugText}` :
|
|
1934
|
+
const debugInfo = `\n\n[Debug Info]\nRequested Model: ${requestedModel || 'Unknown'}\nEffective Model: ${effectiveModel || 'Unknown'}\nProject: ${projectId || 'Unknown'}\nEndpoint: ${endpoint || 'Unknown'}\nStatus: ${response.status}\nRequest ID: ${headers.get('x-request-id') || 'N/A'}${toolDebugMissing !== undefined ? `\nTool Debug Missing: ${toolDebugMissing}` : ''}${toolDebugSummary ? `\nTool Debug Summary: ${toolDebugSummary}` : ''}${toolDebugPayload ? `\nTool Debug Payload: ${toolDebugPayload}` : ''}`;
|
|
1935
|
+
const injectedDebug = debugText ? `\n\n${debugText}` : '';
|
|
1534
1936
|
errorBody.error.message = rawErrorMessage + debugInfo + injectedDebug;
|
|
1535
1937
|
// Check if this is a recoverable thinking error - throw to trigger retry
|
|
1536
|
-
if (errorType ===
|
|
1537
|
-
const recoveryError = new Error(
|
|
1938
|
+
if (errorType === 'thinking_block_order') {
|
|
1939
|
+
const recoveryError = new Error('THINKING_RECOVERY_NEEDED');
|
|
1538
1940
|
recoveryError.recoveryType = errorType;
|
|
1539
1941
|
recoveryError.originalError = errorBody;
|
|
1540
1942
|
recoveryError.debugInfo = debugInfo;
|
|
1541
1943
|
throw recoveryError;
|
|
1542
1944
|
}
|
|
1543
1945
|
// Detect context length / prompt too long errors - signal to caller for toast
|
|
1544
|
-
const errorMessage = errorBody.error.message?.toLowerCase() ||
|
|
1545
|
-
if (errorMessage.includes(
|
|
1546
|
-
errorMessage.includes(
|
|
1547
|
-
errorMessage.includes(
|
|
1548
|
-
errorMessage.includes(
|
|
1549
|
-
headers.set(
|
|
1946
|
+
const errorMessage = errorBody.error.message?.toLowerCase() || '';
|
|
1947
|
+
if (errorMessage.includes('prompt is too long') ||
|
|
1948
|
+
errorMessage.includes('context length exceeded') ||
|
|
1949
|
+
errorMessage.includes('context_length_exceeded') ||
|
|
1950
|
+
errorMessage.includes('maximum context length')) {
|
|
1951
|
+
headers.set('x-antigravity-context-error', 'prompt_too_long');
|
|
1550
1952
|
}
|
|
1551
1953
|
// Detect tool pairing errors - signal to caller for toast
|
|
1552
|
-
if (errorMessage.includes(
|
|
1553
|
-
errorMessage.includes(
|
|
1554
|
-
(errorMessage.includes(
|
|
1555
|
-
|
|
1954
|
+
if (errorMessage.includes('tool_use') &&
|
|
1955
|
+
errorMessage.includes('tool_result') &&
|
|
1956
|
+
(errorMessage.includes('without') ||
|
|
1957
|
+
errorMessage.includes('immediately after'))) {
|
|
1958
|
+
headers.set('x-antigravity-context-error', 'tool_pairing');
|
|
1556
1959
|
}
|
|
1557
1960
|
return new Response(JSON.stringify(errorBody), {
|
|
1558
1961
|
status: response.status,
|
|
1559
1962
|
statusText: response.statusText,
|
|
1560
|
-
headers
|
|
1963
|
+
headers,
|
|
1561
1964
|
});
|
|
1562
1965
|
}
|
|
1563
1966
|
if (errorBody?.error?.details && Array.isArray(errorBody.error.details)) {
|
|
1564
1967
|
const retryInfo = errorBody.error.details.find((detail) => detail['@type'] === 'type.googleapis.com/google.rpc.RetryInfo');
|
|
1565
1968
|
if (retryInfo?.retryDelay) {
|
|
1566
1969
|
const match = retryInfo.retryDelay.match(/^([\d.]+)s$/);
|
|
1567
|
-
if (match
|
|
1970
|
+
if (match?.[1]) {
|
|
1568
1971
|
const retrySeconds = parseFloat(match[1]);
|
|
1569
|
-
if (!isNaN(retrySeconds) && retrySeconds > 0) {
|
|
1972
|
+
if (!Number.isNaN(retrySeconds) && retrySeconds > 0) {
|
|
1570
1973
|
const retryAfterSec = Math.ceil(retrySeconds).toString();
|
|
1571
1974
|
const retryAfterMs = Math.ceil(retrySeconds * 1000).toString();
|
|
1572
1975
|
headers.set('Retry-After', retryAfterSec);
|
|
@@ -1581,35 +1984,42 @@ export async function transformAntigravityResponse(response, streaming, debugCon
|
|
|
1581
1984
|
statusText: response.statusText,
|
|
1582
1985
|
headers,
|
|
1583
1986
|
};
|
|
1584
|
-
const usageFromSse = streaming && isEventStreamResponse
|
|
1585
|
-
|
|
1586
|
-
|
|
1987
|
+
const usageFromSse = streaming && isEventStreamResponse
|
|
1988
|
+
? extractUsageFromSsePayload(text)
|
|
1989
|
+
: null;
|
|
1990
|
+
const parsed = !streaming || !isEventStreamResponse
|
|
1991
|
+
? parseAntigravityApiBody(text)
|
|
1992
|
+
: null;
|
|
1993
|
+
const patched = parsed
|
|
1994
|
+
? rewriteAntigravityPreviewAccessError(parsed, response.status, requestedModel)
|
|
1995
|
+
: null;
|
|
1587
1996
|
const effectiveBody = patched ?? parsed ?? undefined;
|
|
1588
|
-
const usage = usageFromSse ??
|
|
1997
|
+
const usage = usageFromSse ??
|
|
1998
|
+
(effectiveBody ? extractUsageMetadata(effectiveBody) : null);
|
|
1589
1999
|
// Log cache stats when available
|
|
1590
2000
|
if (usage && effectiveModel) {
|
|
1591
2001
|
const cacheRead = usage.cachedContentTokenCount ?? 0;
|
|
1592
2002
|
const totalInput = usage.promptTokenCount ?? usage.totalTokenCount ?? 0;
|
|
1593
2003
|
const hitRate = totalInput > 0 ? Math.round((cacheRead / totalInput) * 100) : 0;
|
|
1594
|
-
const status = cacheRead > 0 ?
|
|
2004
|
+
const status = cacheRead > 0 ? 'HIT' : 'MISS';
|
|
1595
2005
|
logCacheStats(effectiveModel, cacheRead, 0, totalInput);
|
|
1596
2006
|
log.debug(`[Cache] ${status} model=${effectiveModel} read=${cacheRead} total=${totalInput} hitRate=${hitRate}%`);
|
|
1597
2007
|
}
|
|
1598
2008
|
if (usage?.cachedContentTokenCount !== undefined) {
|
|
1599
|
-
headers.set(
|
|
2009
|
+
headers.set('x-antigravity-cached-content-token-count', String(usage.cachedContentTokenCount));
|
|
1600
2010
|
if (usage.totalTokenCount !== undefined) {
|
|
1601
|
-
headers.set(
|
|
2011
|
+
headers.set('x-antigravity-total-token-count', String(usage.totalTokenCount));
|
|
1602
2012
|
}
|
|
1603
2013
|
if (usage.promptTokenCount !== undefined) {
|
|
1604
|
-
headers.set(
|
|
2014
|
+
headers.set('x-antigravity-prompt-token-count', String(usage.promptTokenCount));
|
|
1605
2015
|
}
|
|
1606
2016
|
if (usage.candidatesTokenCount !== undefined) {
|
|
1607
|
-
headers.set(
|
|
2017
|
+
headers.set('x-antigravity-candidates-token-count', String(usage.candidatesTokenCount));
|
|
1608
2018
|
}
|
|
1609
2019
|
}
|
|
1610
2020
|
logAntigravityDebugResponse(debugContext, response, {
|
|
1611
2021
|
body: text,
|
|
1612
|
-
note: streaming ?
|
|
2022
|
+
note: streaming ? 'Streaming SSE payload (buffered fallback)' : undefined,
|
|
1613
2023
|
headersOverride: headers,
|
|
1614
2024
|
});
|
|
1615
2025
|
// Note: successful streaming responses are handled above via TransformStream.
|
|
@@ -1633,12 +2043,13 @@ export async function transformAntigravityResponse(response, streaming, debugCon
|
|
|
1633
2043
|
return new Response(text, init);
|
|
1634
2044
|
}
|
|
1635
2045
|
catch (error) {
|
|
1636
|
-
if (error instanceof Error &&
|
|
2046
|
+
if (error instanceof Error &&
|
|
2047
|
+
error.message === 'THINKING_RECOVERY_NEEDED') {
|
|
1637
2048
|
throw error;
|
|
1638
2049
|
}
|
|
1639
2050
|
logAntigravityDebugResponse(debugContext, response, {
|
|
1640
2051
|
error,
|
|
1641
|
-
note:
|
|
2052
|
+
note: 'Failed to transform Antigravity response',
|
|
1642
2053
|
});
|
|
1643
2054
|
return responseFallback;
|
|
1644
2055
|
}
|