@remodex/rmx 1.0.2
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/AGENTS_INSTALL.md +91 -0
- package/LICENSE +21 -0
- package/README.md +242 -0
- package/assets/architecture.png +0 -0
- package/assets/banner.png +0 -0
- package/assets/claude-code-models.gif +0 -0
- package/assets/codex-app-picker.png +0 -0
- package/bin/ocx.mjs +584 -0
- package/bin/package-main.mjs +9 -0
- package/gui/dist/assets/index-CZqebSPQ.css +1 -0
- package/gui/dist/assets/index-CkETtt7P.js +71 -0
- package/gui/dist/favicon.png +0 -0
- package/gui/dist/fonts/google-sans-cyrillic.woff2 +0 -0
- package/gui/dist/fonts/google-sans-latin.woff2 +0 -0
- package/gui/dist/icons.svg +24 -0
- package/gui/dist/index.html +25 -0
- package/gui/dist/logo.png +0 -0
- package/gui/dist/provider-icons/alibaba-color.svg +1 -0
- package/gui/dist/provider-icons/antigravity-color.svg +1 -0
- package/gui/dist/provider-icons/claude-color.svg +1 -0
- package/gui/dist/provider-icons/cline-color.svg +16 -0
- package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -0
- package/gui/dist/provider-icons/commandcode-color.svg +1 -0
- package/gui/dist/provider-icons/copilot-color.svg +1 -0
- package/gui/dist/provider-icons/cursor-color.svg +2 -0
- package/gui/dist/provider-icons/deepseek-color.svg +1 -0
- package/gui/dist/provider-icons/discord.svg +1 -0
- package/gui/dist/provider-icons/firepass-color.svg +1 -0
- package/gui/dist/provider-icons/fireworks-color.svg +1 -0
- package/gui/dist/provider-icons/gemini-color.svg +1 -0
- package/gui/dist/provider-icons/github-copilot-color.svg +1 -0
- package/gui/dist/provider-icons/gitlab-duo-color.svg +1 -0
- package/gui/dist/provider-icons/grok.svg +1 -0
- package/gui/dist/provider-icons/groq-color.svg +1 -0
- package/gui/dist/provider-icons/huggingface-color.svg +1 -0
- package/gui/dist/provider-icons/kimi-color.svg +1 -0
- package/gui/dist/provider-icons/kiro-color.svg +15 -0
- package/gui/dist/provider-icons/lm-studio-color.svg +1 -0
- package/gui/dist/provider-icons/mistral-color.svg +1 -0
- package/gui/dist/provider-icons/moonshot-color.svg +1 -0
- package/gui/dist/provider-icons/nvidia-color.svg +1 -0
- package/gui/dist/provider-icons/ollama-color.svg +1 -0
- package/gui/dist/provider-icons/openai.svg +1 -0
- package/gui/dist/provider-icons/opencode.svg +2 -0
- package/gui/dist/provider-icons/openrouter-color.svg +1 -0
- package/gui/dist/provider-icons/pi.svg +21 -0
- package/gui/dist/provider-icons/qianfan-color.svg +1 -0
- package/gui/dist/provider-icons/qwen-portal-color.svg +1 -0
- package/gui/dist/provider-icons/telegram.svg +1 -0
- package/gui/dist/provider-icons/vercel-ai-gateway-color.svg +1 -0
- package/gui/dist/provider-icons/vllm-color.svg +1 -0
- package/gui/dist/provider-icons/xiaomi-color.svg +1 -0
- package/package.json +118 -0
- package/src/AGENTS.md +28 -0
- package/src/adapters/anthropic-image-guard.ts +251 -0
- package/src/adapters/anthropic-image-normalize.ts +518 -0
- package/src/adapters/anthropic.ts +1205 -0
- package/src/adapters/azure.ts +36 -0
- package/src/adapters/base.ts +83 -0
- package/src/adapters/client-fingerprint.ts +59 -0
- package/src/adapters/command-code.ts +453 -0
- package/src/adapters/cursor/arg-codec.ts +38 -0
- package/src/adapters/cursor/arg-normalize.ts +104 -0
- package/src/adapters/cursor/cursor-errors.ts +165 -0
- package/src/adapters/cursor/discovery.ts +276 -0
- package/src/adapters/cursor/effort-map.ts +139 -0
- package/src/adapters/cursor/exec-policy.ts +88 -0
- package/src/adapters/cursor/framing.ts +250 -0
- package/src/adapters/cursor/gen/agent_pb.ts +15274 -0
- package/src/adapters/cursor/kv-store.ts +52 -0
- package/src/adapters/cursor/live-models.ts +153 -0
- package/src/adapters/cursor/live-smoke-gate.ts +41 -0
- package/src/adapters/cursor/live-transport.ts +1235 -0
- package/src/adapters/cursor/mcp-config.ts +42 -0
- package/src/adapters/cursor/mcp-manager.ts +333 -0
- package/src/adapters/cursor/message-mapper.ts +49 -0
- package/src/adapters/cursor/native-exec-common.ts +59 -0
- package/src/adapters/cursor/native-exec-desktop.ts +184 -0
- package/src/adapters/cursor/native-exec-fs.ts +332 -0
- package/src/adapters/cursor/native-exec-mcp.ts +153 -0
- package/src/adapters/cursor/native-exec-network.ts +43 -0
- package/src/adapters/cursor/native-exec-shell.ts +548 -0
- package/src/adapters/cursor/native-exec-tools.ts +118 -0
- package/src/adapters/cursor/native-exec.ts +604 -0
- package/src/adapters/cursor/protobuf-events.ts +735 -0
- package/src/adapters/cursor/protobuf-request.ts +719 -0
- package/src/adapters/cursor/request-builder.ts +280 -0
- package/src/adapters/cursor/thread-continuity.ts +67 -0
- package/src/adapters/cursor/tool-definitions.ts +621 -0
- package/src/adapters/cursor/transport-retry.ts +132 -0
- package/src/adapters/cursor/transport.ts +57 -0
- package/src/adapters/cursor/types.ts +59 -0
- package/src/adapters/cursor.ts +196 -0
- package/src/adapters/google-antigravity-replay.ts +520 -0
- package/src/adapters/google-antigravity-wire.ts +140 -0
- package/src/adapters/google-errors.ts +85 -0
- package/src/adapters/google-http.ts +100 -0
- package/src/adapters/google-tool-schema.ts +173 -0
- package/src/adapters/google-truncation.ts +24 -0
- package/src/adapters/google-wire-compiler.ts +232 -0
- package/src/adapters/google.ts +859 -0
- package/src/adapters/identity.ts +77 -0
- package/src/adapters/image.ts +23 -0
- package/src/adapters/kiro-constants.ts +16 -0
- package/src/adapters/kiro-errors.ts +208 -0
- package/src/adapters/kiro-events.ts +197 -0
- package/src/adapters/kiro-images.ts +129 -0
- package/src/adapters/kiro-retry.ts +312 -0
- package/src/adapters/kiro-thinking.ts +104 -0
- package/src/adapters/kiro-tool-fallback.ts +36 -0
- package/src/adapters/kiro-tools.ts +224 -0
- package/src/adapters/kiro-truncation.ts +33 -0
- package/src/adapters/kiro-wire.ts +129 -0
- package/src/adapters/kiro.ts +1924 -0
- package/src/adapters/mimo-free.ts +263 -0
- package/src/adapters/openai-chat.ts +1265 -0
- package/src/adapters/openai-responses.ts +1309 -0
- package/src/adapters/run-turn-queue.ts +114 -0
- package/src/adapters/tool-catalog-nudge.ts +71 -0
- package/src/adapters/upstream-http-error.ts +48 -0
- package/src/android-remote/assets.ts +218 -0
- package/src/android-remote/attachments.ts +168 -0
- package/src/android-remote/auth.ts +237 -0
- package/src/android-remote/cloudflare-provisioning.ts +409 -0
- package/src/android-remote/cloudflare-secret.ts +106 -0
- package/src/android-remote/cloudflare-tunnel.ts +488 -0
- package/src/android-remote/cloudflared.ts +286 -0
- package/src/android-remote/codex-app-server.ts +565 -0
- package/src/android-remote/desktop-history-page.ts +936 -0
- package/src/android-remote/desktop-ipc.ts +3643 -0
- package/src/android-remote/desktop-ownership-store.ts +98 -0
- package/src/android-remote/desktop-project-registration.ts +129 -0
- package/src/android-remote/desktop-session-stream.ts +1110 -0
- package/src/android-remote/desktop-workspace-state.ts +443 -0
- package/src/android-remote/file-change-parser.ts +112 -0
- package/src/android-remote/gateway.ts +9108 -0
- package/src/android-remote/mutation-store.ts +299 -0
- package/src/android-remote/projection.ts +1780 -0
- package/src/android-remote/queued-turn-store.ts +248 -0
- package/src/android-remote/session-command-recovery.ts +1384 -0
- package/src/android-remote/store.ts +466 -0
- package/src/android-remote/thread-reconciliation.ts +133 -0
- package/src/android-remote/thread-source-paths.ts +307 -0
- package/src/android-remote/thread-stream.ts +546 -0
- package/src/android-remote/turn-activity.ts +235 -0
- package/src/android-remote/user-message-identity.ts +94 -0
- package/src/bridge.ts +1793 -0
- package/src/chat/inbound.ts +295 -0
- package/src/chat/outbound.ts +821 -0
- package/src/claude/agents-inject.ts +267 -0
- package/src/claude/alias.ts +149 -0
- package/src/claude/auth-detect.ts +229 -0
- package/src/claude/auth-mode-migration.ts +32 -0
- package/src/claude/auth-mode.ts +62 -0
- package/src/claude/context-windows.ts +189 -0
- package/src/claude/desktop-3p-guard.ts +35 -0
- package/src/claude/desktop-3p-paths.ts +84 -0
- package/src/claude/desktop-3p.ts +601 -0
- package/src/claude/desktop-health.ts +26 -0
- package/src/claude/desktop-profile.ts +263 -0
- package/src/claude/gateway-cache.ts +70 -0
- package/src/claude/inbound-debug.ts +163 -0
- package/src/claude/inbound.ts +519 -0
- package/src/claude/model-info.ts +154 -0
- package/src/claude/outbound.ts +898 -0
- package/src/cli/access.ts +108 -0
- package/src/cli/account-api.ts +296 -0
- package/src/cli/account-auth.ts +250 -0
- package/src/cli/account-catalog-refresh.ts +14 -0
- package/src/cli/account-extended.ts +476 -0
- package/src/cli/account-main.ts +317 -0
- package/src/cli/account.ts +297 -0
- package/src/cli/agent-driven.ts +70 -0
- package/src/cli/agent.ts +184 -0
- package/src/cli/catalog-prewarm.ts +27 -0
- package/src/cli/claude-desktop.ts +211 -0
- package/src/cli/claude.ts +302 -0
- package/src/cli/codex-shim-autorestore.ts +45 -0
- package/src/cli/codex-shim-readiness.ts +69 -0
- package/src/cli/combo.ts +124 -0
- package/src/cli/config-command.ts +183 -0
- package/src/cli/debug.ts +228 -0
- package/src/cli/desktop-first-run.ts +25 -0
- package/src/cli/doctor.ts +1022 -0
- package/src/cli/export-command.ts +201 -0
- package/src/cli/help.ts +370 -0
- package/src/cli/index.ts +1565 -0
- package/src/cli/init.ts +224 -0
- package/src/cli/integrations.ts +225 -0
- package/src/cli/interactive-confirm.ts +133 -0
- package/src/cli/internal-dispatch.ts +35 -0
- package/src/cli/launcher-context.ts +77 -0
- package/src/cli/models-runtime.ts +224 -0
- package/src/cli/models.ts +340 -0
- package/src/cli/observe.ts +170 -0
- package/src/cli/opencode.ts +587 -0
- package/src/cli/provider-runtime.ts +179 -0
- package/src/cli/provider.ts +476 -0
- package/src/cli/ready.ts +301 -0
- package/src/cli/route-policy.ts +92 -0
- package/src/cli/runtime-api.ts +328 -0
- package/src/cli/star-prompt.ts +211 -0
- package/src/cli/status-oauth.ts +78 -0
- package/src/cli/status.ts +321 -0
- package/src/cli/system-command.ts +196 -0
- package/src/cli/system-restart-client.ts +146 -0
- package/src/cli/tray-proxy.ts +205 -0
- package/src/cli/v2.ts +200 -0
- package/src/cli.ts +10 -0
- package/src/clients/config-export.ts +1109 -0
- package/src/codex/account-id.ts +34 -0
- package/src/codex/account-label.ts +34 -0
- package/src/codex/account-lifecycle.ts +172 -0
- package/src/codex/account-namespace-match.ts +63 -0
- package/src/codex/account-namespaces.ts +195 -0
- package/src/codex/account-pause.ts +20 -0
- package/src/codex/account-priority.ts +83 -0
- package/src/codex/account-runtime-state.ts +31 -0
- package/src/codex/account-store.ts +517 -0
- package/src/codex/account-usability.ts +40 -0
- package/src/codex/admission.ts +263 -0
- package/src/codex/app-server-processes.ts +799 -0
- package/src/codex/auth-api.ts +2098 -0
- package/src/codex/auth-collision.ts +107 -0
- package/src/codex/auth-context.ts +480 -0
- package/src/codex/autostart-health.ts +156 -0
- package/src/codex/catalog/account-models.ts +67 -0
- package/src/codex/catalog/aggregation.ts +471 -0
- package/src/codex/catalog/bundled.ts +533 -0
- package/src/codex/catalog/effort.ts +432 -0
- package/src/codex/catalog/filesystem-evidence.ts +302 -0
- package/src/codex/catalog/kinds.ts +2 -0
- package/src/codex/catalog/metadata.ts +287 -0
- package/src/codex/catalog/native-models.ts +7 -0
- package/src/codex/catalog/parsing.ts +503 -0
- package/src/codex/catalog/provider-fetch.ts +2267 -0
- package/src/codex/catalog/sync.ts +1606 -0
- package/src/codex/catalog-admission.ts +197 -0
- package/src/codex/catalog-refresh-status.ts +87 -0
- package/src/codex/catalog-write-serialization.ts +242 -0
- package/src/codex/catalog.ts +15 -0
- package/src/codex/codex-write-lock.ts +384 -0
- package/src/codex/convergence-types.ts +593 -0
- package/src/codex/convergence.ts +580 -0
- package/src/codex/custom-model-catalog-migration.ts +176 -0
- package/src/codex/data/upstream-models.json +830 -0
- package/src/codex/desired-state.ts +230 -0
- package/src/codex/desktop-client-processes.ts +521 -0
- package/src/codex/exec-invocation.ts +22 -0
- package/src/codex/features.ts +1091 -0
- package/src/codex/generation.ts +202 -0
- package/src/codex/history-job.ts +347 -0
- package/src/codex/history-lock.ts +242 -0
- package/src/codex/history-migration-guardian.ts +115 -0
- package/src/codex/history-provider.ts +1075 -0
- package/src/codex/history-transition.ts +105 -0
- package/src/codex/history-worker.ts +204 -0
- package/src/codex/home.ts +206 -0
- package/src/codex/inject-coordination.ts +257 -0
- package/src/codex/inject.ts +1857 -0
- package/src/codex/injected-marker.ts +79 -0
- package/src/codex/integration-record.ts +266 -0
- package/src/codex/internal/catalog-writer.ts +203 -0
- package/src/codex/internal/history-writer.ts +105 -0
- package/src/codex/journal.ts +172 -0
- package/src/codex/main-account-cache.ts +56 -0
- package/src/codex/main-account.ts +40 -0
- package/src/codex/management-convergence.ts +114 -0
- package/src/codex/model-cache.ts +267 -0
- package/src/codex/native-main-admission.ts +47 -0
- package/src/codex/native-main-auth-temp.ts +187 -0
- package/src/codex/native-main-claim.ts +167 -0
- package/src/codex/native-main-lock-file.ts +162 -0
- package/src/codex/native-main-owner.ts +329 -0
- package/src/codex/native-profile-api.ts +247 -0
- package/src/codex/native-profile-manager.ts +1531 -0
- package/src/codex/native-profile-processes.ts +121 -0
- package/src/codex/native-profile-recovery.ts +99 -0
- package/src/codex/native-profile-stage-store.ts +387 -0
- package/src/codex/native-profile-startup.ts +348 -0
- package/src/codex/native-profile-store.ts +855 -0
- package/src/codex/native-profile-types.ts +120 -0
- package/src/codex/native-residue.ts +691 -0
- package/src/codex/paths.ts +78 -0
- package/src/codex/plugins-doctor.ts +242 -0
- package/src/codex/pool-rotation.ts +295 -0
- package/src/codex/project-config-warnings.ts +426 -0
- package/src/codex/prompt-journal.ts +311 -0
- package/src/codex/prompt-layers.ts +967 -0
- package/src/codex/prompt-lock.ts +143 -0
- package/src/codex/provider-adoption.ts +242 -0
- package/src/codex/quota-rejection.ts +224 -0
- package/src/codex/quota.ts +494 -0
- package/src/codex/refresh.ts +60 -0
- package/src/codex/routing.ts +1855 -0
- package/src/codex/runtime.ts +659 -0
- package/src/codex/shim.ts +1215 -0
- package/src/codex/subagent-defaults.ts +557 -0
- package/src/codex/subagent-model-fallback.ts +560 -0
- package/src/codex/sync.ts +238 -0
- package/src/codex/transition-state.ts +612 -0
- package/src/codex/upstream-host-health.ts +368 -0
- package/src/codex/user-identity.ts +374 -0
- package/src/codex/warmup.ts +192 -0
- package/src/codex/websocket-registry.ts +100 -0
- package/src/codex/write-coordination.ts +114 -0
- package/src/combos/failover.ts +140 -0
- package/src/combos/index.ts +44 -0
- package/src/combos/request.ts +64 -0
- package/src/combos/resolve.ts +232 -0
- package/src/combos/types.ts +392 -0
- package/src/config.ts +3270 -0
- package/src/generated/model-metadata.ts +144 -0
- package/src/github/star-state.ts +203 -0
- package/src/grok/inject.ts +540 -0
- package/src/grok/inspect.ts +45 -0
- package/src/grok/status.ts +127 -0
- package/src/grok/sync.ts +66 -0
- package/src/images/artifacts.ts +516 -0
- package/src/images/fulfill-video.ts +163 -0
- package/src/images/fulfill.ts +149 -0
- package/src/images/index.ts +4 -0
- package/src/images/loop.ts +922 -0
- package/src/images/plan.ts +133 -0
- package/src/images/synthetic-tool.ts +133 -0
- package/src/images/types.ts +41 -0
- package/src/images/xai-client.ts +141 -0
- package/src/images/xai-video-client.ts +163 -0
- package/src/index.ts +22 -0
- package/src/integrations/config-io.ts +151 -0
- package/src/integrations/journal.ts +315 -0
- package/src/integrations/merge.ts +135 -0
- package/src/integrations/native/ownership-preflight.ts +202 -0
- package/src/integrations/ownership.ts +111 -0
- package/src/integrations/registry.ts +108 -0
- package/src/integrations/serialize.ts +235 -0
- package/src/integrations/state.ts +290 -0
- package/src/integrations/store.ts +103 -0
- package/src/integrations/writer.ts +492 -0
- package/src/lib/abort.ts +146 -0
- package/src/lib/admin-secrets.ts +25 -0
- package/src/lib/admission.ts +83 -0
- package/src/lib/app-owned-memory-stores.ts +173 -0
- package/src/lib/app-owned-memory.ts +265 -0
- package/src/lib/bounded-body.ts +242 -0
- package/src/lib/bun-binary-validator.d.mts +3 -0
- package/src/lib/bun-binary-validator.mjs +18 -0
- package/src/lib/bun-runtime.ts +184 -0
- package/src/lib/bun-stream-caps.ts +127 -0
- package/src/lib/config-ownership.ts +438 -0
- package/src/lib/crash-guard.ts +344 -0
- package/src/lib/debug-log-buffer.ts +83 -0
- package/src/lib/debug-settings.ts +108 -0
- package/src/lib/debug.ts +31 -0
- package/src/lib/destination-policy.ts +316 -0
- package/src/lib/errors.ts +364 -0
- package/src/lib/eventstream-decoder.ts +253 -0
- package/src/lib/gcp-adc.ts +341 -0
- package/src/lib/injection-debug-log.ts +58 -0
- package/src/lib/local-management-attestation.ts +51 -0
- package/src/lib/open-url.ts +25 -0
- package/src/lib/pinned-http.ts +182 -0
- package/src/lib/privacy.ts +20 -0
- package/src/lib/process-control.ts +168 -0
- package/src/lib/provider-environment.ts +470 -0
- package/src/lib/provider-outbound.ts +203 -0
- package/src/lib/provider-url.ts +14 -0
- package/src/lib/proxy-env.ts +18 -0
- package/src/lib/redact.ts +510 -0
- package/src/lib/remodex-home.ts +616 -0
- package/src/lib/retry-after.ts +55 -0
- package/src/lib/service-secrets.ts +178 -0
- package/src/lib/shadow-call.ts +54 -0
- package/src/lib/sidecar-tracker.ts +52 -0
- package/src/lib/sse-decoder.ts +364 -0
- package/src/lib/state-store-registrations.ts +109 -0
- package/src/lib/state-store-sweeper.ts +184 -0
- package/src/lib/system-restart-contract.ts +73 -0
- package/src/lib/test-home-guard.ts +98 -0
- package/src/lib/token-estimate.ts +69 -0
- package/src/lib/translator-budget.ts +366 -0
- package/src/lib/upstream-reachability.ts +91 -0
- package/src/lib/upstream-retry.ts +508 -0
- package/src/lib/win-exec.ts +115 -0
- package/src/lib/win-paths.ts +68 -0
- package/src/lib/windows-elevation.ts +705 -0
- package/src/lib/windows-secret-acl.ts +817 -0
- package/src/lib/windows-user-principal.ts +283 -0
- package/src/lib/winsw.ts +402 -0
- package/src/model-sources.ts +73 -0
- package/src/oauth/anthropic-routing.ts +594 -0
- package/src/oauth/anthropic.ts +177 -0
- package/src/oauth/callback-server.ts +294 -0
- package/src/oauth/chatgpt.ts +150 -0
- package/src/oauth/command-code.ts +239 -0
- package/src/oauth/cursor.ts +231 -0
- package/src/oauth/github-copilot.ts +428 -0
- package/src/oauth/google-antigravity.ts +230 -0
- package/src/oauth/health.ts +443 -0
- package/src/oauth/index.ts +1280 -0
- package/src/oauth/key-providers.ts +128 -0
- package/src/oauth/kimi.ts +213 -0
- package/src/oauth/kiro-credentials.ts +726 -0
- package/src/oauth/kiro.ts +621 -0
- package/src/oauth/local-token-detect.ts +121 -0
- package/src/oauth/log.ts +48 -0
- package/src/oauth/login-cli.ts +163 -0
- package/src/oauth/pkce.ts +15 -0
- package/src/oauth/store.ts +655 -0
- package/src/oauth/token-guardian.ts +309 -0
- package/src/oauth/types.ts +62 -0
- package/src/oauth/xai.ts +241 -0
- package/src/providers/alibaba-region-backup.ts +75 -0
- package/src/providers/alibaba-region-migration.ts +156 -0
- package/src/providers/alibaba-region-startup.ts +36 -0
- package/src/providers/antigravity-models.ts +317 -0
- package/src/providers/api-keys.ts +140 -0
- package/src/providers/base-url-choices.ts +64 -0
- package/src/providers/codex-capacity.ts +288 -0
- package/src/providers/command-code-efforts.ts +85 -0
- package/src/providers/context-cap.ts +73 -0
- package/src/providers/derive.ts +451 -0
- package/src/providers/free-directory.ts +187 -0
- package/src/providers/github-copilot-transport.ts +56 -0
- package/src/providers/google-vertex-location.ts +14 -0
- package/src/providers/key-failover.ts +271 -0
- package/src/providers/kiro-models.ts +67 -0
- package/src/providers/label.ts +19 -0
- package/src/providers/model-discovery-limits.ts +16 -0
- package/src/providers/model-discovery.ts +361 -0
- package/src/providers/openai-sidecar.ts +235 -0
- package/src/providers/openai-tier-startup.ts +27 -0
- package/src/providers/openai-tiers.ts +301 -0
- package/src/providers/openai-virtual-models.ts +83 -0
- package/src/providers/openrouter-routing.ts +102 -0
- package/src/providers/provider-id-rewrite.ts +179 -0
- package/src/providers/quota.ts +1942 -0
- package/src/providers/reasoning-capabilities.ts +336 -0
- package/src/providers/registry.ts +2375 -0
- package/src/providers/slug-codec.ts +74 -0
- package/src/providers/xai-transport.ts +149 -0
- package/src/reasoning-effort.ts +243 -0
- package/src/responses/compaction.ts +124 -0
- package/src/responses/hosted-tool-policy.ts +9 -0
- package/src/responses/parser.ts +714 -0
- package/src/responses/reasoning-envelope.ts +60 -0
- package/src/responses/reasoning-replay-cache.ts +106 -0
- package/src/responses/schema.ts +159 -0
- package/src/responses/spill-store.ts +431 -0
- package/src/responses/state.ts +1039 -0
- package/src/responses/tool-groups.ts +19 -0
- package/src/router.ts +802 -0
- package/src/routing/analytics.ts +377 -0
- package/src/routing/capability.ts +205 -0
- package/src/routing/cost.ts +77 -0
- package/src/routing/evaluator.ts +444 -0
- package/src/routing/health.ts +401 -0
- package/src/routing/history/cursor.ts +43 -0
- package/src/routing/history/indexer.ts +605 -0
- package/src/routing/history/schema.ts +72 -0
- package/src/routing/profile-namespace.ts +15 -0
- package/src/routing/profile.ts +424 -0
- package/src/routing/quota.ts +145 -0
- package/src/routing/request-evidence.ts +45 -0
- package/src/routing/trace.ts +686 -0
- package/src/server/adapter-resolve.ts +83 -0
- package/src/server/auth-cors.ts +606 -0
- package/src/server/chat-completions.ts +379 -0
- package/src/server/claude-messages.ts +980 -0
- package/src/server/effort-policy.ts +251 -0
- package/src/server/github-copilot-responses-repair.ts +338 -0
- package/src/server/gui-static.ts +152 -0
- package/src/server/image-retry.ts +42 -0
- package/src/server/images.ts +485 -0
- package/src/server/index.ts +1633 -0
- package/src/server/lifecycle.ts +482 -0
- package/src/server/live.ts +609 -0
- package/src/server/management/agent-settings-routes.ts +1180 -0
- package/src/server/management/android-remote-routes.ts +390 -0
- package/src/server/management/api-access.ts +141 -0
- package/src/server/management/api-key-usage.ts +167 -0
- package/src/server/management/body.ts +35 -0
- package/src/server/management/combo-routes.ts +244 -0
- package/src/server/management/config-routes.ts +602 -0
- package/src/server/management/context.ts +88 -0
- package/src/server/management/integration-routes.ts +538 -0
- package/src/server/management/logs-usage-routes.ts +516 -0
- package/src/server/management/model-routes.ts +519 -0
- package/src/server/management/model-rows.ts +143 -0
- package/src/server/management/native-integration-routes.ts +781 -0
- package/src/server/management/oauth-account-routes.ts +573 -0
- package/src/server/management/provider-routes.ts +781 -0
- package/src/server/management/request-history-routes.ts +191 -0
- package/src/server/management/routing-analytics-routes.ts +74 -0
- package/src/server/management/routing-profile-routes.ts +384 -0
- package/src/server/management/shared.ts +277 -0
- package/src/server/management/sidebar-routes.ts +106 -0
- package/src/server/management/sync-response.ts +69 -0
- package/src/server/management/system-restart.ts +433 -0
- package/src/server/management/system-routes.ts +141 -0
- package/src/server/management/usage-summary-cache.ts +86 -0
- package/src/server/management-api.ts +269 -0
- package/src/server/management-auth.ts +353 -0
- package/src/server/memory-watchdog.ts +156 -0
- package/src/server/port-reclaim.ts +307 -0
- package/src/server/ports.ts +156 -0
- package/src/server/proxy-liveness.ts +326 -0
- package/src/server/proxy-stop.ts +92 -0
- package/src/server/readiness.ts +99 -0
- package/src/server/relay-eager.ts +353 -0
- package/src/server/relay.ts +1179 -0
- package/src/server/request-decompress.ts +132 -0
- package/src/server/request-log-conversation.ts +168 -0
- package/src/server/request-log.ts +1072 -0
- package/src/server/responses/collaboration.ts +409 -0
- package/src/server/responses/compact.ts +710 -0
- package/src/server/responses/core.ts +3561 -0
- package/src/server/responses/encrypted-payload.ts +308 -0
- package/src/server/responses/fetch-helpers.ts +171 -0
- package/src/server/responses/passthrough-error.ts +78 -0
- package/src/server/responses/policy-fallback.ts +152 -0
- package/src/server/responses/terminal-guard.ts +230 -0
- package/src/server/responses/upstream-error.ts +48 -0
- package/src/server/responses-image-gen-repair.ts +132 -0
- package/src/server/responses-item-id-repair.ts +272 -0
- package/src/server/responses-json-events.ts +52 -0
- package/src/server/responses-model-rewrite.ts +29 -0
- package/src/server/responses-snapshot-repair.ts +621 -0
- package/src/server/responses.ts +10 -0
- package/src/server/search.ts +181 -0
- package/src/server/sse-frame-buffer.ts +292 -0
- package/src/server/sse-payload-rewrite.ts +263 -0
- package/src/server/startup-action-control.ts +308 -0
- package/src/server/startup-health-cache.ts +119 -0
- package/src/server/system-env.ts +418 -0
- package/src/server/windows-tcp-drop.ts +184 -0
- package/src/server/windows-tray-control.ts +41 -0
- package/src/server/ws-bridge.ts +470 -0
- package/src/service-manager-probe.ts +824 -0
- package/src/service.ts +3011 -0
- package/src/stall-timeout.ts +20 -0
- package/src/storage/cleanup-job.ts +57 -0
- package/src/storage/cleanup.ts +3085 -0
- package/src/storage/policy-job.ts +457 -0
- package/src/storage/policy-scheduler.ts +40 -0
- package/src/storage/policy-worker.ts +59 -0
- package/src/storage/policy.ts +527 -0
- package/src/storage/restore-job.ts +299 -0
- package/src/storage/restore-worker.ts +58 -0
- package/src/storage/scanner.ts +238 -0
- package/src/storage/storage-mutation-coordinator.ts +139 -0
- package/src/storage/worker-lifecycle.ts +215 -0
- package/src/tray/assets/opencodex-tray-offline.ico +0 -0
- package/src/tray/assets/opencodex-tray-online.ico +0 -0
- package/src/tray/assets/opencodex-tray-warning.ico +0 -0
- package/src/tray/assets/opencodex-tray.png +0 -0
- package/src/tray/windows-tray.ps1 +364 -0
- package/src/tray/windows.ts +738 -0
- package/src/types.ts +1531 -0
- package/src/update/badge.ts +72 -0
- package/src/update/desktop-release.ts +1620 -0
- package/src/update/index.ts +402 -0
- package/src/update/job.ts +1906 -0
- package/src/update/notify.ts +261 -0
- package/src/update/npm-cache-preflight.d.mts +47 -0
- package/src/update/npm-cache-preflight.mjs +201 -0
- package/src/update/npm-invocation.d.mts +23 -0
- package/src/update/npm-invocation.mjs +94 -0
- package/src/update/tray-update-plan.d.mts +18 -0
- package/src/update/tray-update-plan.mjs +38 -0
- package/src/usage/cost.ts +0 -0
- package/src/usage/debug.ts +97 -0
- package/src/usage/expected-prices.ts +283 -0
- package/src/usage/log.ts +695 -0
- package/src/usage/summary.ts +585 -0
- package/src/usage/totals.ts +14 -0
- package/src/vision/anthropic-describe.ts +185 -0
- package/src/vision/describe.ts +127 -0
- package/src/vision/index.ts +558 -0
- package/src/vision/reasoning.ts +55 -0
- package/src/web-search/anthropic-executor.ts +189 -0
- package/src/web-search/executor.ts +105 -0
- package/src/web-search/format-result.ts +89 -0
- package/src/web-search/index.ts +196 -0
- package/src/web-search/loop.ts +791 -0
- package/src/web-search/parse.ts +235 -0
- package/src/web-search/progress-stream.ts +342 -0
- package/src/web-search/synthetic-tool.ts +47 -0
|
@@ -0,0 +1,520 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { enforceAppOwnedMemoryBudget } from "../lib/app-owned-memory";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Google-family thoughtSignature reasoning-replay cache.
|
|
6
|
+
*
|
|
7
|
+
* Gemini-3 interleaved thinking is stateless upstream: each model content part carries a
|
|
8
|
+
* `thoughtSignature` that MUST be echoed back on the matching part in the next request, or the
|
|
9
|
+
* upstream rejects the turn (HTTP 400). We observe signatures on the response stream, cache them
|
|
10
|
+
* per `model + session`, and re-inject them into the outgoing `request.contents` on the next turn.
|
|
11
|
+
*
|
|
12
|
+
* Mirrors CLIProxyAPI `internal/runtime/executor/antigravity_reasoning_replay.go` and is also used
|
|
13
|
+
* by Vertex with a transport/project/location-prefixed model identity. Gemini-only;
|
|
14
|
+
* Claude-on-Antigravity uses inline signature sanitization instead (see google-antigravity-wire).
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
interface ReplayCall {
|
|
18
|
+
signature: string;
|
|
19
|
+
sizeBytes: number;
|
|
20
|
+
touchedAtMs: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface ReplayEntry {
|
|
24
|
+
/** thoughtSignature keyed by functionCall identity (name + canonical args). */
|
|
25
|
+
byCall: Map<string, ReplayCall>;
|
|
26
|
+
bytes: number;
|
|
27
|
+
expiresAtMs: number;
|
|
28
|
+
oldestAtMs: number | null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const MIN_SIGNATURE_LEN = 16;
|
|
32
|
+
const REPLAY_TTL_MS = 60 * 60 * 1000; // 1h
|
|
33
|
+
export const ANTIGRAVITY_REPLAY_MAX_ENTRIES = 10_240;
|
|
34
|
+
const REPLAY_EVICT_BATCH = 128;
|
|
35
|
+
const REPLAY_MAX_CALLS_PER_SESSION = 256;
|
|
36
|
+
export const ANTIGRAVITY_REPLAY_MAX_BYTES_PER_SESSION = 2 * 1024 * 1024;
|
|
37
|
+
export const ANTIGRAVITY_REPLAY_MAX_TOTAL_BYTES = 64 * 1024 * 1024;
|
|
38
|
+
const REPLAY_MAX_SIGNATURE_BYTES = 64 * 1024;
|
|
39
|
+
/** Fixed 64-hex outer key length, counted once per session entry. */
|
|
40
|
+
const REPLAY_SESSION_KEY_BYTES = 64;
|
|
41
|
+
|
|
42
|
+
interface ReplayLimits {
|
|
43
|
+
maxCallsPerSession: number;
|
|
44
|
+
maxBytesPerSession: number;
|
|
45
|
+
maxSignatureBytes: number;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const DEFAULT_REPLAY_LIMITS: ReplayLimits = {
|
|
49
|
+
maxCallsPerSession: REPLAY_MAX_CALLS_PER_SESSION,
|
|
50
|
+
maxBytesPerSession: ANTIGRAVITY_REPLAY_MAX_BYTES_PER_SESSION,
|
|
51
|
+
maxSignatureBytes: REPLAY_MAX_SIGNATURE_BYTES,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const replayCache = new Map<string, ReplayEntry>();
|
|
55
|
+
const utf8 = new TextEncoder();
|
|
56
|
+
let replayLimits = { ...DEFAULT_REPLAY_LIMITS };
|
|
57
|
+
let replayBytes = 0;
|
|
58
|
+
let replayOldestSessionKey: string | undefined;
|
|
59
|
+
let replayOldestAt: number | null = null;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Fixed-size identity for a (model, sessionId) pair: SHA-256 over
|
|
63
|
+
* length-prefixed UTF-16 code units fed incrementally (no separator ambiguity
|
|
64
|
+
* — `("a\0b","c")` and `("a","b\0c")` derive different keys — and no raw
|
|
65
|
+
* model/session strings retained as Map keys, which the byte caps never
|
|
66
|
+
* counted).
|
|
67
|
+
*/
|
|
68
|
+
/**
|
|
69
|
+
* Injective string feed for key derivation: length-prefixed in CODE UNITS,
|
|
70
|
+
* then each code unit as two little-endian bytes. TextEncoder/UTF-8 would
|
|
71
|
+
* fold lone surrogates into U+FFFD, colliding distinct strings (e.g.
|
|
72
|
+
* "�" and "�") into the same key.
|
|
73
|
+
*/
|
|
74
|
+
function updateHashWithString(hash: ReturnType<typeof createHash>, value: string): void {
|
|
75
|
+
hash.update(String(value.length));
|
|
76
|
+
hash.update("\0");
|
|
77
|
+
const buf = Buffer.allocUnsafe(8192);
|
|
78
|
+
let offset = 0;
|
|
79
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
80
|
+
buf.writeUInt16LE(value.charCodeAt(index), offset);
|
|
81
|
+
offset += 2;
|
|
82
|
+
if (offset === buf.length) {
|
|
83
|
+
hash.update(buf);
|
|
84
|
+
offset = 0;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (offset > 0) hash.update(buf.subarray(0, offset));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function replayKey(model: string, sessionId: string): string {
|
|
91
|
+
const hash = createHash("sha256");
|
|
92
|
+
updateHashWithString(hash, model);
|
|
93
|
+
updateHashWithString(hash, sessionId);
|
|
94
|
+
return hash.digest("hex");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Canonical output exceeding this budget is rejected DURING the walk — the
|
|
98
|
+
* pre-fix path materialized an unbounded canonical string before admission. */
|
|
99
|
+
const REPLAY_MAX_CANONICAL_ARGS_BYTES = 64 * 1024;
|
|
100
|
+
const CANONICAL_OVERFLOW = Symbol("canonical-overflow");
|
|
101
|
+
|
|
102
|
+
/** Byte-identical output to the old recursive canonicalJson, written incrementally. */
|
|
103
|
+
/**
|
|
104
|
+
* Key-count pre-check: every object key costs at least 4 canonical bytes
|
|
105
|
+
* (two quotes, colon, separator), so a wider object ALWAYS overflows the
|
|
106
|
+
* canonical budget — skip the sort and the walk. Object.keys allocation
|
|
107
|
+
* itself is linear and irreducible in JS (there is no streaming key API),
|
|
108
|
+
* but it is transient and never sorted or walked past this bound.
|
|
109
|
+
*/
|
|
110
|
+
const CANONICAL_MAX_KEYS_PER_OBJECT = REPLAY_MAX_CANONICAL_ARGS_BYTES / 4;
|
|
111
|
+
|
|
112
|
+
/** Test-only scan instrumentation: proves overflow aborts the walk near the
|
|
113
|
+
* cap instead of scanning/materializing the whole input. */
|
|
114
|
+
let canonicalScanUnitsForTests = 0;
|
|
115
|
+
export function canonicalScanUnitsForTestsValue(): number {
|
|
116
|
+
return canonicalScanUnitsForTests;
|
|
117
|
+
}
|
|
118
|
+
export function resetCanonicalScanUnitsForTests(): void {
|
|
119
|
+
canonicalScanUnitsForTests = 0;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const MAX_CANONICAL_DEPTH = 128;
|
|
123
|
+
|
|
124
|
+
function writeCanonicalJson(value: unknown, sink: (chunk: string) => void, depth = 0): void {
|
|
125
|
+
canonicalScanUnitsForTests += 1;
|
|
126
|
+
// Depth overflow is the same class as byte overflow: skip replay for this
|
|
127
|
+
// call instead of exhausting the stack on a pathological argument shape.
|
|
128
|
+
if (depth > MAX_CANONICAL_DEPTH) throw CANONICAL_OVERFLOW;
|
|
129
|
+
if (typeof value === "string") {
|
|
130
|
+
writeJsonStringEscaped(value, sink);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (value === null || typeof value !== "object") {
|
|
134
|
+
sink(JSON.stringify(value) ?? "null");
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (Array.isArray(value)) {
|
|
138
|
+
sink("[");
|
|
139
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
140
|
+
if (index > 0) sink(",");
|
|
141
|
+
// Array.prototype.map parity: holes produce NOTHING between the commas
|
|
142
|
+
// (old output `[1,,3]`), while an explicit undefined element is "null".
|
|
143
|
+
if (index in value) writeCanonicalJson(value[index], sink, depth + 1);
|
|
144
|
+
}
|
|
145
|
+
sink("]");
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
const keys = Object.keys(value as Record<string, unknown>);
|
|
149
|
+
if (keys.length > CANONICAL_MAX_KEYS_PER_OBJECT) throw CANONICAL_OVERFLOW;
|
|
150
|
+
keys.sort();
|
|
151
|
+
sink("{");
|
|
152
|
+
keys.forEach((k, index) => {
|
|
153
|
+
if (index > 0) sink(",");
|
|
154
|
+
writeJsonStringEscaped(k, sink);
|
|
155
|
+
sink(":");
|
|
156
|
+
writeCanonicalJson((value as Record<string, unknown>)[k], sink, depth + 1);
|
|
157
|
+
});
|
|
158
|
+
sink("}");
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* JSON.stringify string escaping, streamed in small chunks so the budget can
|
|
163
|
+
* reject mid-string — calling JSON.stringify on a multi-MiB primitive would
|
|
164
|
+
* materialize its full escaped form before the sink could refuse it.
|
|
165
|
+
* Semantics mirror JSON.stringify for strings exactly (ES2019 JSON
|
|
166
|
+
* superset): quotes/backslash and control characters are escaped, LONE
|
|
167
|
+
* surrogates become \uXXXX, and valid surrogate pairs pass through raw.
|
|
168
|
+
*/
|
|
169
|
+
function writeJsonStringEscaped(value: string, sink: (chunk: string) => void): void {
|
|
170
|
+
sink('"');
|
|
171
|
+
let buffer = "";
|
|
172
|
+
for (const cp of value) {
|
|
173
|
+
canonicalScanUnitsForTests += 1;
|
|
174
|
+
const code = cp.codePointAt(0)!;
|
|
175
|
+
let escaped: string;
|
|
176
|
+
if (cp === '"') escaped = '\\"';
|
|
177
|
+
else if (cp === "\\") escaped = "\\\\";
|
|
178
|
+
else if (cp === "\b") escaped = "\\b";
|
|
179
|
+
else if (cp === "\f") escaped = "\\f";
|
|
180
|
+
else if (cp === "\n") escaped = "\\n";
|
|
181
|
+
else if (cp === "\r") escaped = "\\r";
|
|
182
|
+
else if (cp === "\t") escaped = "\\t";
|
|
183
|
+
else if (code < 0x20) escaped = `\\u${code.toString(16).padStart(4, "0")}`;
|
|
184
|
+
// Lone surrogates: JSON.stringify emits \uXXXX (a raw one would decode
|
|
185
|
+
// back as U+FFFD and collide with real U+FFFD content).
|
|
186
|
+
else if (code >= 0xd800 && code <= 0xdfff) escaped = `\\u${code.toString(16).padStart(4, "0")}`;
|
|
187
|
+
else escaped = cp;
|
|
188
|
+
buffer += escaped;
|
|
189
|
+
if (buffer.length >= 4096) {
|
|
190
|
+
sink(buffer);
|
|
191
|
+
buffer = "";
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if (buffer.length > 0) sink(buffer);
|
|
195
|
+
sink('"');
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/** Bounded canonicalization: null on overflow (skip replay for that call). */
|
|
199
|
+
function canonicalJsonBounded(value: unknown, maxBytes: number): string | null {
|
|
200
|
+
let written = 0;
|
|
201
|
+
const parts: string[] = [];
|
|
202
|
+
const sink = (chunk: string) => {
|
|
203
|
+
written += utf8.encode(chunk).byteLength;
|
|
204
|
+
if (written > maxBytes) throw CANONICAL_OVERFLOW;
|
|
205
|
+
parts.push(chunk);
|
|
206
|
+
};
|
|
207
|
+
try {
|
|
208
|
+
writeCanonicalJson(value, sink);
|
|
209
|
+
} catch (error) {
|
|
210
|
+
if (error === CANONICAL_OVERFLOW) return null;
|
|
211
|
+
throw error;
|
|
212
|
+
}
|
|
213
|
+
return parts.join("");
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Stable identity for a functionCall part: fixed-size SHA-256 over
|
|
218
|
+
* length-prefixed name + canonical args. Overflow during canonicalization
|
|
219
|
+
* skips replay for that call (never materializes an unbounded string); other
|
|
220
|
+
* canonicalization failures keep the old name-only fallback semantics.
|
|
221
|
+
*/
|
|
222
|
+
function functionCallKey(name: unknown, args: unknown): string | undefined {
|
|
223
|
+
if (typeof name !== "string" || name.length === 0) return undefined;
|
|
224
|
+
let canonical: string | null;
|
|
225
|
+
try {
|
|
226
|
+
canonical = canonicalJsonBounded(args ?? {}, REPLAY_MAX_CANONICAL_ARGS_BYTES);
|
|
227
|
+
} catch {
|
|
228
|
+
canonical = "";
|
|
229
|
+
}
|
|
230
|
+
if (canonical === null) return undefined;
|
|
231
|
+
const hash = createHash("sha256");
|
|
232
|
+
updateHashWithString(hash, name);
|
|
233
|
+
updateHashWithString(hash, canonical);
|
|
234
|
+
return hash.digest("hex");
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/** Test-only key-derivation seam: the fixed-key regression cannot go red
|
|
238
|
+
* through snapshot.bytes (that metric never counted raw outer keys). */
|
|
239
|
+
export function antigravityReplayKeyForTests(model: string, sessionId: string): string {
|
|
240
|
+
return replayKey(model, sessionId);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export function antigravityFunctionCallKeyForTests(name: unknown, args: unknown): string | undefined {
|
|
244
|
+
return functionCallKey(name, args);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/** Test-only bounded-canonicalization seam: proves mid-walk rejection without
|
|
248
|
+
* materializing the escaped form (allocation guard). */
|
|
249
|
+
export function antigravityCanonicalJsonBoundedForTests(value: unknown, maxBytes: number): string | null {
|
|
250
|
+
return canonicalJsonBounded(value, maxBytes);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/** Test-only: the ACTUAL internal session keys, so tests can prove raw
|
|
254
|
+
* model/session strings are never retained as Map keys. */
|
|
255
|
+
export function antigravityReplaySessionKeysForTests(): string[] {
|
|
256
|
+
return [...replayCache.keys()];
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function extractSignature(part: Record<string, unknown>): string | undefined {
|
|
260
|
+
const direct = part.thoughtSignature ?? part.thought_signature;
|
|
261
|
+
if (typeof direct === "string" && direct.length >= MIN_SIGNATURE_LEN) return direct;
|
|
262
|
+
const extra = part.extra_content as { google?: { thought_signature?: unknown } } | undefined;
|
|
263
|
+
const nested = extra?.google?.thought_signature;
|
|
264
|
+
if (typeof nested === "string" && nested.length >= MIN_SIGNATURE_LEN) return nested;
|
|
265
|
+
return undefined;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function deleteReplaySession(key: string): number {
|
|
269
|
+
const entry = replayCache.get(key);
|
|
270
|
+
if (!entry) return 0;
|
|
271
|
+
replayCache.delete(key);
|
|
272
|
+
replayBytes -= entry.bytes;
|
|
273
|
+
if (replayOldestSessionKey === key) recomputeReplayOldestCandidate();
|
|
274
|
+
return entry.bytes;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function recomputeReplayOldestCandidate(): void {
|
|
278
|
+
replayOldestSessionKey = undefined;
|
|
279
|
+
replayOldestAt = null;
|
|
280
|
+
for (const [key, entry] of replayCache) {
|
|
281
|
+
if (entry.oldestAtMs === null || (replayOldestAt !== null && entry.oldestAtMs >= replayOldestAt)) continue;
|
|
282
|
+
replayOldestSessionKey = key;
|
|
283
|
+
replayOldestAt = entry.oldestAtMs;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function refreshReplaySessionCandidate(key: string, entry: ReplayEntry): void {
|
|
288
|
+
entry.oldestAtMs = entry.byCall.values().next().value?.touchedAtMs ?? null;
|
|
289
|
+
if (replayOldestSessionKey === key) {
|
|
290
|
+
recomputeReplayOldestCandidate();
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
if (entry.oldestAtMs !== null && (replayOldestAt === null || entry.oldestAtMs < replayOldestAt)) {
|
|
294
|
+
replayOldestSessionKey = key;
|
|
295
|
+
replayOldestAt = entry.oldestAtMs;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function deleteExpiredReplaySessions(now: number): void {
|
|
300
|
+
for (const [key, entry] of replayCache) if (entry.expiresAtMs <= now) deleteReplaySession(key);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* The lazy per-call expiry scan is O(sessions); at the 10,240-session cap
|
|
305
|
+
* every observe/apply would rescan the whole map — O(n²) under load. The 60s
|
|
306
|
+
* state-store sweeper is already the periodic expiry authority, so lazy scans
|
|
307
|
+
* are throttled to at most one per interval (expired entries may linger a few
|
|
308
|
+
* extra seconds; TTL is fuzzy at that scale by design).
|
|
309
|
+
*/
|
|
310
|
+
const LAZY_SWEEP_INTERVAL_MS = 30_000;
|
|
311
|
+
let lastLazySweepAt = Number.NEGATIVE_INFINITY;
|
|
312
|
+
|
|
313
|
+
function deleteExpiredReplaySessionsThrottled(now: number): void {
|
|
314
|
+
if (now - lastLazySweepAt < LAZY_SWEEP_INTERVAL_MS) return;
|
|
315
|
+
lastLazySweepAt = now;
|
|
316
|
+
deleteExpiredReplaySessions(now);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
export function sweepExpiredAntigravityReplay(now = Date.now()): number {
|
|
320
|
+
const before = replayCache.size;
|
|
321
|
+
deleteExpiredReplaySessions(now);
|
|
322
|
+
return before - replayCache.size;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function deleteReplayCall(entry: ReplayEntry, callKey: string): number {
|
|
326
|
+
const call = entry.byCall.get(callKey);
|
|
327
|
+
if (!call) return 0;
|
|
328
|
+
entry.byCall.delete(callKey);
|
|
329
|
+
entry.bytes -= call.sizeBytes;
|
|
330
|
+
replayBytes -= call.sizeBytes;
|
|
331
|
+
return call.sizeBytes;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function evictInnerCalls(entry: ReplayEntry): void {
|
|
335
|
+
while (
|
|
336
|
+
entry.byCall.size > replayLimits.maxCallsPerSession
|
|
337
|
+
|| entry.bytes > replayLimits.maxBytesPerSession
|
|
338
|
+
) {
|
|
339
|
+
const oldest = entry.byCall.keys().next().value;
|
|
340
|
+
if (oldest === undefined) break;
|
|
341
|
+
deleteReplayCall(entry, oldest);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
function evictIfNeeded(): void {
|
|
346
|
+
if (replayCache.size > ANTIGRAVITY_REPLAY_MAX_ENTRIES) {
|
|
347
|
+
const oldest = [...replayCache.entries()]
|
|
348
|
+
.sort((a, b) => a[1].expiresAtMs - b[1].expiresAtMs)
|
|
349
|
+
.slice(0, REPLAY_EVICT_BATCH);
|
|
350
|
+
for (const [key] of oldest) deleteReplaySession(key);
|
|
351
|
+
}
|
|
352
|
+
while (replayBytes > ANTIGRAVITY_REPLAY_MAX_TOTAL_BYTES) {
|
|
353
|
+
const oldestKey = [...replayCache.entries()]
|
|
354
|
+
.sort((a, b) => a[1].expiresAtMs - b[1].expiresAtMs)[0]?.[0];
|
|
355
|
+
if (oldestKey === undefined) return;
|
|
356
|
+
deleteReplaySession(oldestKey);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/** Gemini/Flash/Agent use the replay cache; Claude does not (inline sanitization instead). */
|
|
361
|
+
export function antigravityUsesReplayCache(model: string): boolean {
|
|
362
|
+
return !/claude/i.test(model);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Observe a parsed CCA chunk's `candidates[0].content.parts` and record thought signatures keyed by
|
|
367
|
+
* the functionCall identity (name + args). Accumulates across the whole session so a sequential
|
|
368
|
+
* multi-step tool loop keeps EVERY prior call's signature, not just the latest part-index slot.
|
|
369
|
+
* A signature on a standalone thought part is paired with the next functionCall in the same
|
|
370
|
+
* array (#897); a call's own signature takes precedence and an unpaired one is dropped.
|
|
371
|
+
* `parts` is the already-unwrapped `response.candidates[0].content.parts`.
|
|
372
|
+
*/
|
|
373
|
+
export function observeAntigravityReplay(model: string, sessionId: string, parts: unknown[]): void {
|
|
374
|
+
if (!antigravityUsesReplayCache(model) || !Array.isArray(parts) || parts.length === 0) return;
|
|
375
|
+
const now = Date.now();
|
|
376
|
+
deleteExpiredReplaySessionsThrottled(now);
|
|
377
|
+
const key = replayKey(model, sessionId);
|
|
378
|
+
const existing = replayCache.get(key);
|
|
379
|
+
const entry = existing ?? {
|
|
380
|
+
byCall: new Map<string, ReplayCall>(),
|
|
381
|
+
bytes: REPLAY_SESSION_KEY_BYTES,
|
|
382
|
+
expiresAtMs: 0,
|
|
383
|
+
oldestAtMs: null,
|
|
384
|
+
};
|
|
385
|
+
let inserted = false;
|
|
386
|
+
let pendingThoughtSig: string | undefined;
|
|
387
|
+
for (const raw of parts) {
|
|
388
|
+
if (!raw || typeof raw !== "object") continue;
|
|
389
|
+
const part = raw as Record<string, unknown>;
|
|
390
|
+
const sig = extractSignature(part);
|
|
391
|
+
const fc = part.functionCall as { name?: unknown; args?: unknown } | undefined;
|
|
392
|
+
if (!fc) {
|
|
393
|
+
// A signature on a standalone thought part pairs with the NEXT functionCall in this
|
|
394
|
+
// array: thought parts are stripped from replayed history, so the call part is the only
|
|
395
|
+
// carrier that survives (#897). Non-thought parts keep their own signature in history.
|
|
396
|
+
if (sig && part.thought === true) pendingThoughtSig = sig;
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
399
|
+
const callSig = sig ?? pendingThoughtSig; // a signature on the call part itself wins
|
|
400
|
+
pendingThoughtSig = undefined;
|
|
401
|
+
if (!callSig) continue;
|
|
402
|
+
const ck = functionCallKey(fc.name, fc.args);
|
|
403
|
+
if (!ck) continue; // only function-call signatures are replayable by identity
|
|
404
|
+
const signatureBytes = utf8.encode(callSig).byteLength;
|
|
405
|
+
const sizeBytes = utf8.encode(ck).byteLength + signatureBytes;
|
|
406
|
+
if (signatureBytes > replayLimits.maxSignatureBytes || sizeBytes > replayLimits.maxBytesPerSession) continue;
|
|
407
|
+
deleteReplayCall(entry, ck);
|
|
408
|
+
entry.byCall.set(ck, { signature: callSig, sizeBytes, touchedAtMs: now });
|
|
409
|
+
entry.bytes += sizeBytes;
|
|
410
|
+
replayBytes += sizeBytes;
|
|
411
|
+
inserted = true;
|
|
412
|
+
}
|
|
413
|
+
if (!inserted) return;
|
|
414
|
+
// Charge the fixed outer key only when the session is actually stored.
|
|
415
|
+
if (!existing) replayBytes += REPLAY_SESSION_KEY_BYTES;
|
|
416
|
+
evictInnerCalls(entry);
|
|
417
|
+
if (entry.byCall.size === 0) {
|
|
418
|
+
// The fixed session overhead can exceed the per-session cap on its own
|
|
419
|
+
// (test-sized limits): an entry holding zero calls is unusable — drop it
|
|
420
|
+
// instead of retaining an unevictable shell.
|
|
421
|
+
if (existing) deleteReplaySession(key);
|
|
422
|
+
else replayBytes -= REPLAY_SESSION_KEY_BYTES;
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
entry.expiresAtMs = now + REPLAY_TTL_MS;
|
|
426
|
+
replayCache.set(key, entry);
|
|
427
|
+
refreshReplaySessionCandidate(key, entry);
|
|
428
|
+
evictIfNeeded();
|
|
429
|
+
enforceAppOwnedMemoryBudget();
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Re-inject cached thought signatures into the outgoing `request.contents`, matched by functionCall
|
|
434
|
+
* identity across ALL model turns (not just the last one). Only fills a functionCall part that
|
|
435
|
+
* lacks a real signature. Returns the same array reference (mutated in place).
|
|
436
|
+
*/
|
|
437
|
+
export function applyAntigravityReplay(model: string, sessionId: string, contents: unknown[]): unknown[] {
|
|
438
|
+
if (!antigravityUsesReplayCache(model) || !Array.isArray(contents)) return contents;
|
|
439
|
+
const now = Date.now();
|
|
440
|
+
deleteExpiredReplaySessionsThrottled(now);
|
|
441
|
+
const entry = replayCache.get(replayKey(model, sessionId));
|
|
442
|
+
if (!entry) {
|
|
443
|
+
return contents;
|
|
444
|
+
}
|
|
445
|
+
let touched = false;
|
|
446
|
+
for (const c of contents as { role?: string; parts?: unknown[] }[]) {
|
|
447
|
+
if (!c || typeof c !== "object" || c.role !== "model" || !Array.isArray(c.parts)) continue;
|
|
448
|
+
for (const raw of c.parts) {
|
|
449
|
+
if (!raw || typeof raw !== "object") continue;
|
|
450
|
+
const part = raw as Record<string, unknown>;
|
|
451
|
+
const fc = part.functionCall as { name?: unknown; args?: unknown } | undefined;
|
|
452
|
+
if (!fc) continue;
|
|
453
|
+
if (part.thoughtSignature !== undefined || part.thought_signature !== undefined) continue;
|
|
454
|
+
const ck = functionCallKey(fc.name, fc.args);
|
|
455
|
+
const call = ck ? entry.byCall.get(ck) : undefined;
|
|
456
|
+
if (call && ck) {
|
|
457
|
+
part.thoughtSignature = call.signature;
|
|
458
|
+
entry.byCall.delete(ck);
|
|
459
|
+
entry.byCall.set(ck, { ...call, touchedAtMs: now });
|
|
460
|
+
touched = true;
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
if (touched) refreshReplaySessionCandidate(replayKey(model, sessionId), entry);
|
|
465
|
+
return contents;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/** Drop the cache entry when upstream rejects a signature (clear-on-invalid). */
|
|
469
|
+
export function clearAntigravityReplay(model: string, sessionId: string): void {
|
|
470
|
+
deleteReplaySession(replayKey(model, sessionId));
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
export function antigravityReplayMetrics(): {
|
|
474
|
+
sessions: number;
|
|
475
|
+
calls: number;
|
|
476
|
+
totalBytes: number;
|
|
477
|
+
largestSessionBytes: number;
|
|
478
|
+
} {
|
|
479
|
+
let calls = 0;
|
|
480
|
+
let largestSessionBytes = 0;
|
|
481
|
+
for (const entry of replayCache.values()) {
|
|
482
|
+
calls += entry.byCall.size;
|
|
483
|
+
largestSessionBytes = Math.max(largestSessionBytes, entry.bytes);
|
|
484
|
+
}
|
|
485
|
+
return { sessions: replayCache.size, calls, totalBytes: replayBytes, largestSessionBytes };
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
export function antigravityReplayRetainedStoreSnapshot(): {
|
|
489
|
+
count: number;
|
|
490
|
+
bytes: number;
|
|
491
|
+
evictableBytes: number;
|
|
492
|
+
pinnedBytes: number;
|
|
493
|
+
oldestAt: number | null;
|
|
494
|
+
} {
|
|
495
|
+
return {
|
|
496
|
+
count: replayCache.size,
|
|
497
|
+
bytes: replayBytes,
|
|
498
|
+
evictableBytes: replayBytes,
|
|
499
|
+
pinnedBytes: 0,
|
|
500
|
+
oldestAt: replayOldestAt,
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
export function evictOldestAntigravityReplayForBudget(): number {
|
|
505
|
+
return replayOldestSessionKey === undefined ? 0 : deleteReplaySession(replayOldestSessionKey);
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
export function setAntigravityReplayLimitsForTests(limits?: Partial<ReplayLimits>): void {
|
|
509
|
+
__resetAntigravityReplayCache();
|
|
510
|
+
replayLimits = limits ? { ...DEFAULT_REPLAY_LIMITS, ...limits } : { ...DEFAULT_REPLAY_LIMITS };
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
/** Test seam. */
|
|
514
|
+
export function __resetAntigravityReplayCache(): void {
|
|
515
|
+
lastLazySweepAt = Number.NEGATIVE_INFINITY;
|
|
516
|
+
replayCache.clear();
|
|
517
|
+
replayBytes = 0;
|
|
518
|
+
replayOldestSessionKey = undefined;
|
|
519
|
+
replayOldestAt = null;
|
|
520
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import type { OcxContentPart, OcxParsedRequest } from "../types";
|
|
3
|
+
import { antigravityUserAgent } from "./client-fingerprint";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Antigravity request User-Agent. Mirrors the real Antigravity CLI UA
|
|
7
|
+
* (`antigravity/cli/{ver} (aidev_client; os_type=darwin; arch=arm64)`) so the request fingerprint
|
|
8
|
+
* matches the OAuth credential — the prior literal `"antigravity"` was a giveaway no real client
|
|
9
|
+
* sends. A `GOOGLE_ANTIGRAVITY_USER_AGENT` override still wins.
|
|
10
|
+
*/
|
|
11
|
+
export const ANTIGRAVITY_REQUEST_UA = process.env.GOOGLE_ANTIGRAVITY_USER_AGENT || antigravityUserAgent();
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Whether a stored `OcxToolCall.thoughtSignature` is a REAL upstream Gemini signature versus a
|
|
15
|
+
* foreign id that must not be forwarded to Gemini/Antigravity.
|
|
16
|
+
*
|
|
17
|
+
* Foreign ids that have 400'd Antigravity (`TYPE_BYTES` / Base64 decoding failed) include:
|
|
18
|
+
* - Responses/bridge item ids: `fc_...`, `ctc_...` (custom_tool_call), `tsc_...` (tool_search_call),
|
|
19
|
+
* `call_...`, `rs_...`, …
|
|
20
|
+
* - Anthropic tool-use ids: `toolu_...`
|
|
21
|
+
*
|
|
22
|
+
* Only real signatures may be forwarded — sending a foreign id as `thoughtSignature` breaks
|
|
23
|
+
* multi-turn reasoning continuity. Real signatures are opaque base64-ish blobs with no
|
|
24
|
+
* Responses/Anthropic id prefix.
|
|
25
|
+
*
|
|
26
|
+
* Note: do NOT reject a bare `sig_` / `sig-` prefix — existing Gemini replay fixtures and some
|
|
27
|
+
* upstream blobs use that shape; a deny-list entry for `sig` would drop valid continuity tokens.
|
|
28
|
+
*/
|
|
29
|
+
export function isLikelyRealThoughtSignature(sig: string | undefined): boolean {
|
|
30
|
+
if (typeof sig !== "string" || sig.length < 16) return false;
|
|
31
|
+
// Reject synthetic Responses/tool-call ids and Anthropic tool-use ids (`_` or `-` separators).
|
|
32
|
+
if (/^(fc|ctc|tsc|call|msg|rs|resp|reasoning|item|ws|toolu|tool|func|function)[-_]/i.test(sig)) return false;
|
|
33
|
+
// Real Gemini thought signatures are opaque base64/base64url blobs: only [A-Za-z0-9+/_=-].
|
|
34
|
+
// Anything containing other characters (or whitespace) is not a real signature.
|
|
35
|
+
return /^[A-Za-z0-9+/_=-]+$/.test(sig);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function firstUserText(parsed: OcxParsedRequest): string | undefined {
|
|
39
|
+
for (const msg of parsed.context.messages) {
|
|
40
|
+
if (msg.role !== "user") continue;
|
|
41
|
+
if (typeof msg.content === "string") return msg.content;
|
|
42
|
+
const first = (msg.content as OcxContentPart[]).find(p => p.type === "text" && typeof p.text === "string");
|
|
43
|
+
if (first && first.type === "text") return first.text;
|
|
44
|
+
}
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Deterministic Cloud Code Assist session id from the first user message text. Mirrors
|
|
50
|
+
* CLIProxyAPI `generateStableSessionID`: sha256(firstUserText) → BigEndian uint64 masked with
|
|
51
|
+
* 0x7FFFFFFFFFFFFFFF, prefixed with "-". Falls back to a random "-<digits>" id when there is no text.
|
|
52
|
+
*/
|
|
53
|
+
export function antigravitySessionId(parsed: OcxParsedRequest): string {
|
|
54
|
+
// The id must be IDENTICAL on turn N and turn N+1: the replay cache observes thought signatures
|
|
55
|
+
// on turn N's response and re-injects them on turn N+1's request, so a changing id loses the
|
|
56
|
+
// association entirely. (A *shared* id does not — signatures are keyed on functionCall identity,
|
|
57
|
+
// name+args, so cross-conversation collisions stay harmless. Instability is the failure mode.)
|
|
58
|
+
//
|
|
59
|
+
// First-user text does not guarantee that. It is stable only while the first user message
|
|
60
|
+
// survives verbatim in what this adapter sees, and Codex compacts, summarises, and trims long
|
|
61
|
+
// histories — at which point the anchor changes mid-conversation (#1295's sibling, #1297).
|
|
62
|
+
//
|
|
63
|
+
// `_clientThreadId` is Codex's own thread identity from `x-codex-parent-thread-id`. It survives
|
|
64
|
+
// compaction because it does not depend on message content, which is the property first-user
|
|
65
|
+
// text fails to provide. Deliberately NOT `promptCacheKey`: that is arbitrary Responses input
|
|
66
|
+
// and is explicitly shared across conversations for some clients
|
|
67
|
+
// (`adapters/cursor/request-builder.ts`), so it identifies a cache cohort, not a conversation.
|
|
68
|
+
//
|
|
69
|
+
// What is NOT claimed: that Google treats this id as anything but opaque, or that upstream
|
|
70
|
+
// signatures are not session-bound. Only the local association is verified here.
|
|
71
|
+
//
|
|
72
|
+
// Clients that send no thread header keep the text anchor and its instability; this is a scoped
|
|
73
|
+
// repair, not a universal one.
|
|
74
|
+
const text = clientThreadAnchor(parsed) ?? firstUserText(parsed);
|
|
75
|
+
if (!text) return `-${Math.floor(Math.random() * 9e18).toString()}`;
|
|
76
|
+
const digest = createHash("sha256").update(text, "utf8").digest();
|
|
77
|
+
const masked = digest.readBigUInt64BE(0) & 0x7fffffffffffffffn;
|
|
78
|
+
return `-${masked.toString()}`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Codex's stable client thread id, prefixed before hashing so it does not collide with a first
|
|
83
|
+
* user message equal to the bare thread id.
|
|
84
|
+
*
|
|
85
|
+
* This is a prefix, not full domain separation: a textless-anchor request whose first message is
|
|
86
|
+
* literally `codex-thread:<id>` still hashes the same preimage. Tagging BOTH anchor classes would
|
|
87
|
+
* separate them completely, but it would also change every existing text-derived id — a
|
|
88
|
+
* Google-visible wire value for live conversations — which is a larger blast radius than the
|
|
89
|
+
* collision it removes. A collision is harmless here anyway: the replay cache keys signatures on
|
|
90
|
+
* functionCall identity (name+args), so a shared id does not misattribute them. Instability, not
|
|
91
|
+
* collision, is the failure mode this function exists to prevent.
|
|
92
|
+
*/
|
|
93
|
+
function clientThreadAnchor(parsed: OcxParsedRequest): string | undefined {
|
|
94
|
+
const threadId = parsed._clientThreadId?.trim();
|
|
95
|
+
return threadId ? `codex-thread:${threadId}` : undefined;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** A Gemini content part as it appears in an Antigravity request body. */
|
|
99
|
+
interface GeminiPart {
|
|
100
|
+
thought?: boolean;
|
|
101
|
+
thoughtSignature?: string;
|
|
102
|
+
thought_signature?: string;
|
|
103
|
+
text?: string;
|
|
104
|
+
[key: string]: unknown;
|
|
105
|
+
}
|
|
106
|
+
interface GeminiContent {
|
|
107
|
+
role?: string;
|
|
108
|
+
parts?: GeminiPart[];
|
|
109
|
+
[key: string]: unknown;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function hasSignature(part: GeminiPart): boolean {
|
|
113
|
+
return typeof part.thoughtSignature === "string" && part.thoughtSignature.length > 0
|
|
114
|
+
|| typeof part.thought_signature === "string" && part.thought_signature.length > 0;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Claude-on-Antigravity signature sanitization (the no-cache path). Mirrors CLIProxyAPI's
|
|
119
|
+
* `StripEmptySignatureThinkingBlocks` + non-model signature stripping: drop thinking parts that
|
|
120
|
+
* carry no valid signature (they would 400 upstream), and strip signature fields from non-model
|
|
121
|
+
* (user) content. Mutates and returns `contents`.
|
|
122
|
+
*/
|
|
123
|
+
export function sanitizeAntigravityClaudeSignatures(contents: unknown[]): unknown[] {
|
|
124
|
+
if (!Array.isArray(contents)) return contents;
|
|
125
|
+
for (const raw of contents as GeminiContent[]) {
|
|
126
|
+
if (!raw || typeof raw !== "object" || !Array.isArray(raw.parts)) continue;
|
|
127
|
+
const isModel = raw.role === "model";
|
|
128
|
+
if (!isModel) {
|
|
129
|
+
// Non-model parts must not carry thought signatures.
|
|
130
|
+
for (const part of raw.parts) {
|
|
131
|
+
delete part.thoughtSignature;
|
|
132
|
+
delete part.thought_signature;
|
|
133
|
+
}
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
// Model turn: drop thinking blocks lacking a valid signature.
|
|
137
|
+
raw.parts = raw.parts.filter(part => !(part.thought === true && !hasSignature(part)));
|
|
138
|
+
}
|
|
139
|
+
return contents;
|
|
140
|
+
}
|