@iislee/opencodex 2.11.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/LICENSE +21 -0
- package/README.md +225 -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 +451 -0
- package/bin/package-main.mjs +9 -0
- package/gui/dist/assets/index-DTpMHS4F.js +67 -0
- package/gui/dist/assets/index-ZNVDE3C7.css +1 -0
- package/gui/dist/favicon.png +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/antigravity.svg +1 -0
- package/gui/dist/provider-icons/claude-color.svg +1 -0
- package/gui/dist/provider-icons/claude.svg +1 -0
- package/gui/dist/provider-icons/cloudflare-ai-gateway-color.svg +1 -0
- package/gui/dist/provider-icons/copilot-color.svg +1 -0
- package/gui/dist/provider-icons/copilot.svg +1 -0
- package/gui/dist/provider-icons/cursor-color.svg +2 -0
- package/gui/dist/provider-icons/cursor.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/gemini.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-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/kiro.svg +14 -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 +1 -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 +102 -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 +1003 -0
- package/src/adapters/azure.ts +36 -0
- package/src/adapters/base.ts +72 -0
- package/src/adapters/client-fingerprint.ts +59 -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 +127 -0
- package/src/adapters/cursor/exec-policy.ts +88 -0
- package/src/adapters/cursor/framing.ts +211 -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 +1214 -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 +55 -0
- package/src/adapters/cursor/native-exec-desktop.ts +184 -0
- package/src/adapters/cursor/native-exec-fs.ts +329 -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 +576 -0
- package/src/adapters/cursor/protobuf-events.ts +563 -0
- package/src/adapters/cursor/protobuf-request.ts +714 -0
- package/src/adapters/cursor/request-builder.ts +255 -0
- package/src/adapters/cursor/thread-continuity.ts +67 -0
- package/src/adapters/cursor/tool-definitions.ts +505 -0
- package/src/adapters/cursor/transport-retry.ts +132 -0
- package/src/adapters/cursor/transport.ts +57 -0
- package/src/adapters/cursor/types.ts +52 -0
- package/src/adapters/cursor.ts +196 -0
- package/src/adapters/google-antigravity-replay.ts +303 -0
- package/src/adapters/google-antigravity-wire.ts +108 -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 +13 -0
- package/src/adapters/google-wire-compiler.ts +232 -0
- package/src/adapters/google.ts +758 -0
- package/src/adapters/identity.ts +44 -0
- package/src/adapters/image.ts +23 -0
- package/src/adapters/kiro-constants.ts +16 -0
- package/src/adapters/kiro-errors.ts +197 -0
- package/src/adapters/kiro-events.ts +179 -0
- package/src/adapters/kiro-images.ts +129 -0
- package/src/adapters/kiro-retry.ts +312 -0
- package/src/adapters/kiro-thinking.ts +96 -0
- package/src/adapters/kiro-tool-fallback.ts +36 -0
- package/src/adapters/kiro-tools.ts +215 -0
- package/src/adapters/kiro-truncation.ts +33 -0
- package/src/adapters/kiro-wire.ts +129 -0
- package/src/adapters/kiro.ts +1898 -0
- package/src/adapters/mimo-free.ts +263 -0
- package/src/adapters/openai-chat.ts +1005 -0
- package/src/adapters/openai-responses.ts +1137 -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/bridge.ts +1619 -0
- package/src/chat/inbound.ts +295 -0
- package/src/chat/outbound.ts +765 -0
- package/src/claude/agents-inject.ts +243 -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 +381 -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 +509 -0
- package/src/claude/model-info.ts +151 -0
- package/src/claude/outbound.ts +872 -0
- package/src/cli/access.ts +108 -0
- package/src/cli/account-api.ts +268 -0
- package/src/cli/account-auth.ts +223 -0
- package/src/cli/account-extended.ts +350 -0
- package/src/cli/account.ts +275 -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 +188 -0
- package/src/cli/claude.ts +286 -0
- package/src/cli/codex-shim-autorestore.ts +45 -0
- package/src/cli/combo.ts +119 -0
- package/src/cli/config-command.ts +145 -0
- package/src/cli/debug.ts +228 -0
- package/src/cli/doctor.ts +930 -0
- package/src/cli/export-command.ts +187 -0
- package/src/cli/help.ts +354 -0
- package/src/cli/index.ts +1113 -0
- package/src/cli/init.ts +224 -0
- package/src/cli/integrations.ts +142 -0
- package/src/cli/interactive-confirm.ts +133 -0
- package/src/cli/internal-dispatch.ts +20 -0
- package/src/cli/models-runtime.ts +212 -0
- package/src/cli/models.ts +336 -0
- package/src/cli/observe.ts +117 -0
- package/src/cli/opencode.ts +586 -0
- package/src/cli/pi.ts +188 -0
- package/src/cli/provider-runtime.ts +162 -0
- package/src/cli/provider.ts +463 -0
- package/src/cli/runtime-api.ts +325 -0
- package/src/cli/star-prompt.ts +155 -0
- package/src/cli/status-oauth.ts +78 -0
- package/src/cli/status.ts +321 -0
- package/src/cli/sync-cloud.ts +283 -0
- package/src/cli/system-command.ts +112 -0
- package/src/cli/tray-proxy.ts +52 -0
- package/src/cli/v2.ts +173 -0
- package/src/cli.ts +10 -0
- package/src/clients/config-export.ts +377 -0
- package/src/clients/effective-status.ts +385 -0
- package/src/clients/probes/agy.ts +55 -0
- package/src/clients/probes/cc-switch.ts +110 -0
- package/src/clients/probes/claude.ts +90 -0
- package/src/clients/probes/codex.ts +125 -0
- package/src/clients/probes/grok.ts +29 -0
- package/src/clients/probes/opencode.ts +109 -0
- package/src/clients/probes/paseo.ts +55 -0
- package/src/clients/probes/pi.ts +55 -0
- package/src/cloud/onedrive-auth.ts +666 -0
- package/src/cloud/onedrive-graph.ts +108 -0
- package/src/cloud/settings.ts +75 -0
- package/src/cloud/sync.ts +212 -0
- package/src/cloud/types.ts +56 -0
- package/src/cloud/vault.ts +89 -0
- package/src/codex/account-id.ts +34 -0
- package/src/codex/account-label.ts +34 -0
- package/src/codex/account-lifecycle.ts +55 -0
- package/src/codex/account-namespace-match.ts +63 -0
- package/src/codex/account-namespaces.ts +149 -0
- package/src/codex/account-pause.ts +20 -0
- package/src/codex/account-runtime-state.ts +31 -0
- package/src/codex/account-store.ts +517 -0
- package/src/codex/account-usability.ts +20 -0
- package/src/codex/app-server-processes.ts +756 -0
- package/src/codex/auth-api.ts +1540 -0
- package/src/codex/auth-collision.ts +107 -0
- package/src/codex/auth-context.ts +352 -0
- package/src/codex/autostart-health.ts +149 -0
- package/src/codex/catalog/aggregation.ts +378 -0
- package/src/codex/catalog/bundled.ts +251 -0
- package/src/codex/catalog/effort.ts +355 -0
- package/src/codex/catalog/metadata.ts +180 -0
- package/src/codex/catalog/parsing.ts +456 -0
- package/src/codex/catalog/provider-fetch.ts +902 -0
- package/src/codex/catalog/sync.ts +620 -0
- package/src/codex/catalog.ts +12 -0
- package/src/codex/data/upstream-models.json +830 -0
- package/src/codex/exec-invocation.ts +22 -0
- package/src/codex/features.ts +969 -0
- package/src/codex/history-migration-guardian.ts +102 -0
- package/src/codex/history-provider.ts +776 -0
- package/src/codex/home.ts +206 -0
- package/src/codex/inject.ts +799 -0
- package/src/codex/injected-marker.ts +72 -0
- package/src/codex/journal.ts +163 -0
- package/src/codex/main-account-cache.ts +32 -0
- package/src/codex/main-account.ts +40 -0
- package/src/codex/model-cache.ts +227 -0
- package/src/codex/paths.ts +65 -0
- package/src/codex/plugins-doctor.ts +242 -0
- package/src/codex/pool-rotation.ts +225 -0
- package/src/codex/project-config-warnings.ts +411 -0
- package/src/codex/quota.ts +411 -0
- package/src/codex/refresh.ts +53 -0
- package/src/codex/routing.ts +1477 -0
- package/src/codex/runtime.ts +538 -0
- package/src/codex/shim.ts +1189 -0
- package/src/codex/subagent-defaults.ts +550 -0
- package/src/codex/subagent-model-fallback.ts +469 -0
- package/src/codex/sync.ts +130 -0
- package/src/codex/warmup.ts +192 -0
- package/src/codex/websocket-registry.ts +100 -0
- package/src/combos/failover.ts +140 -0
- package/src/combos/index.ts +41 -0
- package/src/combos/request.ts +62 -0
- package/src/combos/resolve.ts +232 -0
- package/src/combos/types.ts +326 -0
- package/src/config.ts +2356 -0
- package/src/generated/jawcode-model-metadata.ts +104 -0
- package/src/github/star-state.ts +203 -0
- package/src/grok/inject.ts +545 -0
- package/src/grok/status.ts +121 -0
- package/src/grok/sync.ts +103 -0
- package/src/grok/usage-hook/report.mjs +348 -0
- package/src/grok/usage-hook.ts +278 -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 +829 -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/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 +202 -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 +71 -0
- package/src/lib/bun-stream-caps.ts +126 -0
- package/src/lib/config-ownership.ts +360 -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/open-url.ts +25 -0
- package/src/lib/pinned-http.ts +151 -0
- package/src/lib/privacy.ts +20 -0
- package/src/lib/process-control.ts +165 -0
- package/src/lib/provider-outbound.ts +170 -0
- package/src/lib/provider-url.ts +14 -0
- package/src/lib/proxy-env.ts +18 -0
- package/src/lib/redact.ts +105 -0
- package/src/lib/retry-after.ts +55 -0
- package/src/lib/service-secrets.ts +25 -0
- package/src/lib/shadow-call.ts +30 -0
- package/src/lib/sidecar-tracker.ts +52 -0
- package/src/lib/sse-decoder.ts +323 -0
- package/src/lib/state-store-registrations.ts +109 -0
- package/src/lib/state-store-sweeper.ts +184 -0
- package/src/lib/test-home-guard.ts +90 -0
- package/src/lib/token-estimate.ts +69 -0
- package/src/lib/translator-budget.ts +356 -0
- package/src/lib/upstream-retry.ts +239 -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 +514 -0
- package/src/lib/winsw.ts +375 -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/cursor.ts +211 -0
- package/src/oauth/github-copilot.ts +428 -0
- package/src/oauth/google-antigravity.ts +230 -0
- package/src/oauth/health.ts +399 -0
- package/src/oauth/index.ts +1174 -0
- package/src/oauth/key-providers.ts +108 -0
- package/src/oauth/kimi.ts +213 -0
- package/src/oauth/kiro-credentials.ts +726 -0
- package/src/oauth/kiro.ts +577 -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 +630 -0
- package/src/oauth/token-guardian.ts +303 -0
- package/src/oauth/types.ts +62 -0
- package/src/oauth/xai.ts +241 -0
- package/src/pi/extensions.ts +72 -0
- package/src/pi/home.ts +42 -0
- package/src/pi/index.ts +40 -0
- package/src/pi/models.ts +278 -0
- package/src/pi/packages.ts +219 -0
- package/src/pi/settings.ts +365 -0
- package/src/pi/status.ts +68 -0
- package/src/pi/sync.ts +75 -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 +205 -0
- package/src/providers/api-keys.ts +140 -0
- package/src/providers/base-url-choices.ts +64 -0
- package/src/providers/context-cap.ts +65 -0
- package/src/providers/derive.ts +339 -0
- package/src/providers/free-directory.ts +184 -0
- package/src/providers/github-copilot-transport.ts +56 -0
- package/src/providers/key-failover.ts +203 -0
- package/src/providers/kiro-models.ts +67 -0
- package/src/providers/label.ts +19 -0
- package/src/providers/model-discovery.ts +356 -0
- package/src/providers/openai-sidecar.ts +175 -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 +82 -0
- package/src/providers/openrouter-routing.ts +102 -0
- package/src/providers/provider-id-rewrite.ts +150 -0
- package/src/providers/quota.ts +1260 -0
- package/src/providers/registry.ts +1600 -0
- package/src/providers/slug-codec.ts +67 -0
- package/src/providers/xai-transport.ts +141 -0
- package/src/reasoning-effort.ts +135 -0
- package/src/responses/compaction.ts +117 -0
- package/src/responses/parser.ts +656 -0
- package/src/responses/reasoning-envelope.ts +52 -0
- package/src/responses/schema.ts +159 -0
- package/src/responses/spill-store.ts +394 -0
- package/src/responses/state.ts +895 -0
- package/src/responses/tool-groups.ts +19 -0
- package/src/router.ts +425 -0
- package/src/server/adapter-resolve.ts +80 -0
- package/src/server/auth-cors.ts +530 -0
- package/src/server/chat-completions.ts +368 -0
- package/src/server/claude-messages.ts +914 -0
- package/src/server/effort-policy.ts +172 -0
- package/src/server/gui-static.ts +123 -0
- package/src/server/image-retry.ts +42 -0
- package/src/server/images.ts +476 -0
- package/src/server/index.ts +1126 -0
- package/src/server/lifecycle.ts +227 -0
- package/src/server/live.ts +598 -0
- package/src/server/management/agent-settings-routes.ts +1169 -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/clients-routes.ts +63 -0
- package/src/server/management/cloud-sync-routes.ts +266 -0
- package/src/server/management/combo-routes.ts +220 -0
- package/src/server/management/config-routes.ts +422 -0
- package/src/server/management/context.ts +31 -0
- package/src/server/management/logs-usage-routes.ts +707 -0
- package/src/server/management/model-routes.ts +525 -0
- package/src/server/management/oauth-account-routes.ts +563 -0
- package/src/server/management/provider-routes.ts +556 -0
- package/src/server/management/shared.ts +277 -0
- package/src/server/management/sidebar-routes.ts +90 -0
- package/src/server/management/system-restart.ts +179 -0
- package/src/server/management/system-routes.ts +117 -0
- package/src/server/management/usage-summary-cache.ts +86 -0
- package/src/server/management-api.ts +215 -0
- package/src/server/management-auth.ts +267 -0
- package/src/server/memory-watchdog.ts +156 -0
- package/src/server/port-reclaim.ts +307 -0
- package/src/server/ports.ts +116 -0
- package/src/server/proxy-liveness.ts +201 -0
- package/src/server/relay-eager.ts +313 -0
- package/src/server/relay.ts +1049 -0
- package/src/server/request-decompress.ts +132 -0
- package/src/server/request-log-conversation.ts +168 -0
- package/src/server/request-log.ts +1046 -0
- package/src/server/responses/collaboration.ts +354 -0
- package/src/server/responses/compact.ts +384 -0
- package/src/server/responses/core.ts +2758 -0
- package/src/server/responses/encrypted-payload.ts +308 -0
- package/src/server/responses/fetch-helpers.ts +157 -0
- package/src/server/responses/passthrough-error.ts +78 -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 +224 -0
- package/src/server/responses.ts +9 -0
- package/src/server/search.ts +136 -0
- package/src/server/sse-payload-rewrite.ts +175 -0
- package/src/server/startup-action-control.ts +308 -0
- package/src/server/startup-health-cache.ts +113 -0
- package/src/server/system-env.ts +413 -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 +471 -0
- package/src/service.ts +2554 -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 +290 -0
- package/src/tray/windows.ts +730 -0
- package/src/types.ts +1237 -0
- package/src/update/badge.ts +72 -0
- package/src/update/index.ts +407 -0
- package/src/update/job.ts +1520 -0
- package/src/update/notify.ts +257 -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 +164 -0
- package/src/usage/log.ts +658 -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 +125 -0
- package/src/vision/index.ts +467 -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 +664 -0
- package/src/web-search/parse.ts +220 -0
- package/src/web-search/progress-stream.ts +342 -0
- package/src/web-search/synthetic-tool.ts +47 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { OcxProviderConfig } from "../../types";
|
|
2
|
+
import type { CursorClientMessage, CursorRunRequest, CursorServerMessage } from "./types";
|
|
3
|
+
import type { TranslatorBudget } from "../../lib/translator-budget";
|
|
4
|
+
|
|
5
|
+
export interface CursorTransport {
|
|
6
|
+
run(request: CursorRunRequest, signal?: AbortSignal): AsyncIterable<CursorServerMessage>;
|
|
7
|
+
writeClient(message: CursorClientMessage): void | Promise<void>;
|
|
8
|
+
close?(): void | Promise<void>;
|
|
9
|
+
/**
|
|
10
|
+
* Whether the run request has been committed to the wire. The retry orchestrator only re-dials
|
|
11
|
+
* failures that happened BEFORE this becomes true, so a turn the Cursor server may already have
|
|
12
|
+
* accepted is never replayed. Absent (undefined) is treated as "committed" — safe by default.
|
|
13
|
+
*/
|
|
14
|
+
requestCommitted?(): boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface CursorTransportFactoryInput {
|
|
18
|
+
provider: OcxProviderConfig;
|
|
19
|
+
translatorBudget: TranslatorBudget;
|
|
20
|
+
headers?: Headers;
|
|
21
|
+
/** Pre-first-frame deadline (dial + first server frame). Defaults to 30s when omitted. */
|
|
22
|
+
firstFrameTimeoutMs?: number;
|
|
23
|
+
/** Grace (ms) between close() and the force-destroy fallback after a first-frame timeout. Defaults to 1s. */
|
|
24
|
+
timeoutDestroyGraceMs?: number;
|
|
25
|
+
/**
|
|
26
|
+
* Grace window (ms) before a drained client-tool turn is finalized, so a sibling tool call
|
|
27
|
+
* announced in a later receive chunk can revoke a premature finalize. Defaults to 50ms.
|
|
28
|
+
*/
|
|
29
|
+
clientToolFinalizeGraceMs?: number;
|
|
30
|
+
/**
|
|
31
|
+
* True when inbound request text carries the legacy Codex full-access sandbox marker.
|
|
32
|
+
* This is retained as diagnostic/context only; exec-policy.ts does not trust it as
|
|
33
|
+
* native local exec authorization because the text is caller-controlled.
|
|
34
|
+
*/
|
|
35
|
+
requestDeclaresFullAccess?: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type CursorTransportFactory = (input: CursorTransportFactoryInput) => CursorTransport;
|
|
39
|
+
|
|
40
|
+
export class CursorTransportDisabledError extends Error {
|
|
41
|
+
readonly code = "cursor_transport_disabled";
|
|
42
|
+
|
|
43
|
+
constructor(message = "live Cursor transport is disabled") {
|
|
44
|
+
super(message);
|
|
45
|
+
this.name = "CursorTransportDisabledError";
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function createDisabledCursorTransport(): CursorTransport {
|
|
50
|
+
return {
|
|
51
|
+
async *run() {
|
|
52
|
+
throw new CursorTransportDisabledError();
|
|
53
|
+
},
|
|
54
|
+
writeClient() {},
|
|
55
|
+
close() {},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { OcxUsage } from "../../types";
|
|
2
|
+
import type { OcxMessage, OcxRequestOptions, OcxTool } from "../../types";
|
|
3
|
+
import type { CursorRoutingLevel } from "./discovery";
|
|
4
|
+
|
|
5
|
+
export interface CursorRunRequest {
|
|
6
|
+
modelId: string;
|
|
7
|
+
/** Cursor Router optimization parameter; valid only while modelId is the `default` wire model. */
|
|
8
|
+
routingLevel?: CursorRoutingLevel;
|
|
9
|
+
conversationId: string;
|
|
10
|
+
system: string[];
|
|
11
|
+
messages: CursorRequestMessage[];
|
|
12
|
+
rawMessages?: OcxMessage[];
|
|
13
|
+
tools?: OcxTool[];
|
|
14
|
+
toolChoice?: OcxRequestOptions["toolChoice"];
|
|
15
|
+
parallelToolCalls?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Clear provider-private context-usage carry-forward before this run. Used when Codex starts a
|
|
18
|
+
* newly observed compacted context epoch, so pre-compaction totals are not over-reported while
|
|
19
|
+
* historical previous_response_id replay remains idempotent.
|
|
20
|
+
*/
|
|
21
|
+
contextUsageReset?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Defaults to true. Set false for compaction summarizer turns: their checkpoints describe the
|
|
24
|
+
* pre-compaction history being summarized and must not become the next turn's carry-forward total.
|
|
25
|
+
*/
|
|
26
|
+
contextUsageStoreCheckpoints?: boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface CursorRequestMessage {
|
|
30
|
+
role: "user" | "assistant" | "developer" | "tool";
|
|
31
|
+
content: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export type CursorServerMessage =
|
|
35
|
+
| { type: "text"; text: string }
|
|
36
|
+
| { type: "thinking"; thinking: string }
|
|
37
|
+
| { type: "tool_call_start"; id: string; name: string }
|
|
38
|
+
| { type: "tool_call_delta"; arguments: string }
|
|
39
|
+
| { type: "tool_call_end"; id?: string }
|
|
40
|
+
| { type: "done"; usage?: OcxUsage }
|
|
41
|
+
| { type: "error"; message: string; usage?: OcxUsage }
|
|
42
|
+
| { type: "heartbeat" }
|
|
43
|
+
| { type: "kv_get"; key: string }
|
|
44
|
+
| { type: "kv_set"; key: string; value: Uint8Array }
|
|
45
|
+
| { type: "exec"; execCase: string; requestId: string }
|
|
46
|
+
/** A native exec/MCP action ran locally; retrying this turn could duplicate its side effects. */
|
|
47
|
+
| { type: "local_side_effect" };
|
|
48
|
+
|
|
49
|
+
export type CursorClientMessage =
|
|
50
|
+
| { type: "kv_value"; key: string; value?: Uint8Array }
|
|
51
|
+
| { type: "kv_stored"; key: string }
|
|
52
|
+
| { type: "exec_result"; requestId: string; ok: boolean; message: string };
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import type { AdapterEvent, OcxProviderConfig } from "../types";
|
|
3
|
+
import type { ProviderAdapter } from "./base";
|
|
4
|
+
import { isTranslatorBudgetExceededError } from "../lib/translator-budget";
|
|
5
|
+
import { cursorExecDeniedMessage, cursorRequestDeclaresFullAccess } from "./cursor/exec-policy";
|
|
6
|
+
import { isCursorBenignCancelError, isCursorInvalidArgumentError, safeCursorErrorMessage } from "./cursor/cursor-errors";
|
|
7
|
+
import { isCursorExternalWireModel } from "./cursor/discovery";
|
|
8
|
+
import { createCursorKvStore, type CursorKvStore } from "./cursor/kv-store";
|
|
9
|
+
import { mapCursorServerMessage } from "./cursor/message-mapper";
|
|
10
|
+
import { createCursorRequest } from "./cursor/request-builder";
|
|
11
|
+
import {
|
|
12
|
+
createLiveCursorTransport,
|
|
13
|
+
CursorMissingCredentialError,
|
|
14
|
+
rekeyCursorContextUsage,
|
|
15
|
+
resolveCursorToken,
|
|
16
|
+
} from "./cursor/live-transport";
|
|
17
|
+
import { rememberCursorThreadConversation } from "./cursor/thread-continuity";
|
|
18
|
+
import { runCursorTurnWithRetry } from "./cursor/transport-retry";
|
|
19
|
+
import {
|
|
20
|
+
createDisabledCursorTransport,
|
|
21
|
+
CursorTransportDisabledError,
|
|
22
|
+
type CursorTransportFactory,
|
|
23
|
+
} from "./cursor/transport";
|
|
24
|
+
|
|
25
|
+
export const CURSOR_API_URL = "https://api2.cursor.sh";
|
|
26
|
+
|
|
27
|
+
export {
|
|
28
|
+
CURSOR_EXEC_CASES_DENIED,
|
|
29
|
+
cursorExecDeniedMessage,
|
|
30
|
+
type CursorDeniedExecCase,
|
|
31
|
+
} from "./cursor/exec-policy";
|
|
32
|
+
|
|
33
|
+
const CURSOR_TRANSPORT_DISABLED_MESSAGE = [
|
|
34
|
+
"An explicit disabled Cursor transport was injected.",
|
|
35
|
+
"Production Cursor requests use live transport when a Cursor access token is configured.",
|
|
36
|
+
].join(" ");
|
|
37
|
+
|
|
38
|
+
export interface CursorAdapterDeps {
|
|
39
|
+
createTransport?: CursorTransportFactory;
|
|
40
|
+
kv?: CursorKvStore;
|
|
41
|
+
/** Test seam: observe/replace context-usage rekeying on conversation-id rotation. */
|
|
42
|
+
rekeyContextUsage?: (fromConversationId: string, toConversationId: string) => void;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function safeCursorTransportError(err: unknown): string {
|
|
46
|
+
if (err instanceof CursorTransportDisabledError) return CURSOR_TRANSPORT_DISABLED_MESSAGE;
|
|
47
|
+
if (err instanceof CursorMissingCredentialError) {
|
|
48
|
+
return "Cursor live transport is enabled, but no Cursor access token is configured. Set provider.apiKey or OPENCODEX_CURSOR_TEST_TOKEN.";
|
|
49
|
+
}
|
|
50
|
+
const message = err instanceof Error ? err.message : typeof err === "string" ? err : undefined;
|
|
51
|
+
if (message) return safeCursorErrorMessage(message);
|
|
52
|
+
return "Cursor upstream error: transport failed before completion.";
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function createCursorAdapter(provider: OcxProviderConfig, deps: CursorAdapterDeps = {}): ProviderAdapter {
|
|
56
|
+
return {
|
|
57
|
+
name: "cursor",
|
|
58
|
+
|
|
59
|
+
buildRequest() {
|
|
60
|
+
return {
|
|
61
|
+
url: provider.baseUrl || CURSOR_API_URL,
|
|
62
|
+
method: "POST",
|
|
63
|
+
headers: {},
|
|
64
|
+
body: "",
|
|
65
|
+
};
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
async *parseStream(): AsyncGenerator<AdapterEvent> {
|
|
69
|
+
yield {
|
|
70
|
+
type: "error",
|
|
71
|
+
message: "Cursor adapter uses runTurn; the fetch/parseStream path is disabled.",
|
|
72
|
+
};
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
async runTurn(_parsed, incoming, emit) {
|
|
76
|
+
if (incoming.abortSignal?.aborted) {
|
|
77
|
+
emit({ type: "error", message: "Cursor turn was aborted before start." });
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
const makeTransport = deps.createTransport ?? createLiveCursorTransport;
|
|
82
|
+
const kv = deps.kv ?? createCursorKvStore({}, incoming.translatorBudget);
|
|
83
|
+
const rekeyContextUsage = deps.rekeyContextUsage ?? rekeyCursorContextUsage;
|
|
84
|
+
// Namespace thread→conversation derivation by the authenticated Cursor credential so
|
|
85
|
+
// shared-proxy tenants with different Cursor accounts cannot collide on a parent thread id.
|
|
86
|
+
// Prefer an already-set auth scope (e.g. Codex pool account) when present.
|
|
87
|
+
if (!_parsed._cursorIdentityScope) {
|
|
88
|
+
try {
|
|
89
|
+
const token = resolveCursorToken(provider, incoming.headers);
|
|
90
|
+
_parsed._cursorIdentityScope = createHash("sha256")
|
|
91
|
+
.update("ocx:cursor:acct:")
|
|
92
|
+
.update(token)
|
|
93
|
+
.digest("hex")
|
|
94
|
+
.slice(0, 16);
|
|
95
|
+
} catch {
|
|
96
|
+
/* Missing credential is handled by the live transport path below. */
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const previousConversationId = _parsed._cursorConversationId;
|
|
100
|
+
let request = createCursorRequest(_parsed);
|
|
101
|
+
// The builder may derive a stable provider id from the client thread when Responses state
|
|
102
|
+
// is unavailable. Rekey only existing state; there is nothing to migrate on a fresh turn,
|
|
103
|
+
// and isolated helper/compaction turns must never inherit or donate the parent's usage state.
|
|
104
|
+
if (
|
|
105
|
+
previousConversationId
|
|
106
|
+
&& request.conversationId !== previousConversationId
|
|
107
|
+
&& _parsed._cursorIsolateConversation !== true
|
|
108
|
+
) {
|
|
109
|
+
rekeyContextUsage(previousConversationId, request.conversationId);
|
|
110
|
+
}
|
|
111
|
+
_parsed._cursorConversationId = request.conversationId;
|
|
112
|
+
let emittedOutput = false;
|
|
113
|
+
let replayUnsafe = false;
|
|
114
|
+
const lastRawIsToolResult = _parsed.context.messages.at(-1)?.role === "toolResult";
|
|
115
|
+
|
|
116
|
+
const runOnce = async (activeRequest: ReturnType<typeof createCursorRequest>) => {
|
|
117
|
+
await runCursorTurnWithRetry(
|
|
118
|
+
makeTransport,
|
|
119
|
+
{
|
|
120
|
+
provider,
|
|
121
|
+
headers: incoming.headers,
|
|
122
|
+
translatorBudget: incoming.translatorBudget,
|
|
123
|
+
requestDeclaresFullAccess: cursorRequestDeclaresFullAccess(activeRequest),
|
|
124
|
+
},
|
|
125
|
+
activeRequest,
|
|
126
|
+
incoming.abortSignal,
|
|
127
|
+
(message, activeTransport) => {
|
|
128
|
+
if (incoming.abortSignal?.aborted) {
|
|
129
|
+
emit({ type: "error", message: "Cursor turn was aborted." });
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
if (message.type === "local_side_effect") replayUnsafe = true;
|
|
133
|
+
const events = mapCursorServerMessage(message, {
|
|
134
|
+
kv,
|
|
135
|
+
writeClient: clientMessage => {
|
|
136
|
+
void activeTransport.writeClient(clientMessage);
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
for (const event of events) {
|
|
140
|
+
if (event.type !== "heartbeat") emittedOutput = true;
|
|
141
|
+
emit(event);
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
);
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
try {
|
|
148
|
+
await runOnce(request);
|
|
149
|
+
} catch (err) {
|
|
150
|
+
// One-shot fallback for external-model Connect invalid_argument before any
|
|
151
|
+
// non-heartbeat output. Retries apply only to safe plain-user turns; tool-result
|
|
152
|
+
// resumes, local exec/MCP side effects, and already-emitted output fail closed.
|
|
153
|
+
if (
|
|
154
|
+
!isCursorInvalidArgumentError(err)
|
|
155
|
+
|| !isCursorExternalWireModel(request.modelId)
|
|
156
|
+
|| lastRawIsToolResult
|
|
157
|
+
|| emittedOutput
|
|
158
|
+
|| replayUnsafe
|
|
159
|
+
|| incoming.abortSignal?.aborted
|
|
160
|
+
) {
|
|
161
|
+
throw err;
|
|
162
|
+
}
|
|
163
|
+
const failedConversationId = request.conversationId;
|
|
164
|
+
_parsed._cursorConversationId = undefined;
|
|
165
|
+
request = createCursorRequest(_parsed, { forceFreshConversation: true });
|
|
166
|
+
rekeyContextUsage(failedConversationId, request.conversationId);
|
|
167
|
+
_parsed._cursorConversationId = request.conversationId;
|
|
168
|
+
// Persist recovery for store:false clients that only send a parent thread id, so the
|
|
169
|
+
// next turn does not recompute the stale deterministic thread hash. Isolated helper /
|
|
170
|
+
// compaction turns must not park their throwaway id under the parent thread key.
|
|
171
|
+
if (_parsed._clientThreadId && _parsed._cursorIsolateConversation !== true) {
|
|
172
|
+
rememberCursorThreadConversation(
|
|
173
|
+
_parsed._clientThreadId,
|
|
174
|
+
request.conversationId,
|
|
175
|
+
_parsed._cursorIdentityScope,
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
await runOnce(request);
|
|
179
|
+
}
|
|
180
|
+
} catch (err) {
|
|
181
|
+
if (isCursorBenignCancelError(err)) return;
|
|
182
|
+
const partialUsage = (err as { partialUsage?: import("../types").OcxUsage }).partialUsage;
|
|
183
|
+
emit({
|
|
184
|
+
type: "error",
|
|
185
|
+
message: isTranslatorBudgetExceededError(err)
|
|
186
|
+
? "upstream translation buffer exceeded the safe limit"
|
|
187
|
+
: safeCursorTransportError(err),
|
|
188
|
+
...(isTranslatorBudgetExceededError(err)
|
|
189
|
+
? { status: 502, errorType: "upstream_error", code: "translation_buffer_limit" }
|
|
190
|
+
: {}),
|
|
191
|
+
...(partialUsage ? { usage: partialUsage } : {}),
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
}
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import { enforceAppOwnedMemoryBudget } from "../lib/app-owned-memory";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Antigravity (Cloud Code Assist) thoughtSignature reasoning-replay cache.
|
|
5
|
+
*
|
|
6
|
+
* Gemini-3 interleaved thinking is stateless upstream: each model content part carries a
|
|
7
|
+
* `thoughtSignature` that MUST be echoed back on the matching part in the next request, or the
|
|
8
|
+
* upstream rejects the turn (HTTP 400). We observe signatures on the response stream, cache them
|
|
9
|
+
* per `model + session`, and re-inject them into the outgoing `request.contents` on the next turn.
|
|
10
|
+
*
|
|
11
|
+
* Mirrors CLIProxyAPI `internal/runtime/executor/antigravity_reasoning_replay.go`. Gemini-only;
|
|
12
|
+
* Claude-on-Antigravity uses inline signature sanitization instead (see google-antigravity-wire).
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
interface ReplayCall {
|
|
16
|
+
signature: string;
|
|
17
|
+
sizeBytes: number;
|
|
18
|
+
touchedAtMs: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface ReplayEntry {
|
|
22
|
+
/** thoughtSignature keyed by functionCall identity (name + canonical args). */
|
|
23
|
+
byCall: Map<string, ReplayCall>;
|
|
24
|
+
bytes: number;
|
|
25
|
+
expiresAtMs: number;
|
|
26
|
+
oldestAtMs: number | null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const MIN_SIGNATURE_LEN = 16;
|
|
30
|
+
const REPLAY_TTL_MS = 60 * 60 * 1000; // 1h
|
|
31
|
+
export const ANTIGRAVITY_REPLAY_MAX_ENTRIES = 10_240;
|
|
32
|
+
const REPLAY_EVICT_BATCH = 128;
|
|
33
|
+
const REPLAY_MAX_CALLS_PER_SESSION = 256;
|
|
34
|
+
export const ANTIGRAVITY_REPLAY_MAX_BYTES_PER_SESSION = 2 * 1024 * 1024;
|
|
35
|
+
export const ANTIGRAVITY_REPLAY_MAX_TOTAL_BYTES = 64 * 1024 * 1024;
|
|
36
|
+
const REPLAY_MAX_SIGNATURE_BYTES = 64 * 1024;
|
|
37
|
+
|
|
38
|
+
interface ReplayLimits {
|
|
39
|
+
maxCallsPerSession: number;
|
|
40
|
+
maxBytesPerSession: number;
|
|
41
|
+
maxSignatureBytes: number;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const DEFAULT_REPLAY_LIMITS: ReplayLimits = {
|
|
45
|
+
maxCallsPerSession: REPLAY_MAX_CALLS_PER_SESSION,
|
|
46
|
+
maxBytesPerSession: ANTIGRAVITY_REPLAY_MAX_BYTES_PER_SESSION,
|
|
47
|
+
maxSignatureBytes: REPLAY_MAX_SIGNATURE_BYTES,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const replayCache = new Map<string, ReplayEntry>();
|
|
51
|
+
const utf8 = new TextEncoder();
|
|
52
|
+
let replayLimits = { ...DEFAULT_REPLAY_LIMITS };
|
|
53
|
+
let replayBytes = 0;
|
|
54
|
+
let replayOldestSessionKey: string | undefined;
|
|
55
|
+
let replayOldestAt: number | null = null;
|
|
56
|
+
|
|
57
|
+
function replayKey(model: string, sessionId: string): string {
|
|
58
|
+
return `${model}::session:${sessionId}`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Recursively canonicalize a JSON value: object keys sorted, arrays preserved. */
|
|
62
|
+
function canonicalJson(value: unknown): string {
|
|
63
|
+
if (value === null || typeof value !== "object") return JSON.stringify(value) ?? "null";
|
|
64
|
+
if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
|
|
65
|
+
const entries = Object.keys(value as Record<string, unknown>).sort()
|
|
66
|
+
.map(k => `${JSON.stringify(k)}:${canonicalJson((value as Record<string, unknown>)[k])}`);
|
|
67
|
+
return `{${entries.join(",")}}`;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Stable identity for a functionCall part: name + recursively canonicalized args. */
|
|
71
|
+
function functionCallKey(name: unknown, args: unknown): string | undefined {
|
|
72
|
+
if (typeof name !== "string" || name.length === 0) return undefined;
|
|
73
|
+
let argsKey = "";
|
|
74
|
+
try {
|
|
75
|
+
argsKey = canonicalJson(args ?? {});
|
|
76
|
+
} catch {
|
|
77
|
+
argsKey = "";
|
|
78
|
+
}
|
|
79
|
+
return `${name}::${argsKey}`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function extractSignature(part: Record<string, unknown>): string | undefined {
|
|
83
|
+
const direct = part.thoughtSignature ?? part.thought_signature;
|
|
84
|
+
if (typeof direct === "string" && direct.length >= MIN_SIGNATURE_LEN) return direct;
|
|
85
|
+
const extra = part.extra_content as { google?: { thought_signature?: unknown } } | undefined;
|
|
86
|
+
const nested = extra?.google?.thought_signature;
|
|
87
|
+
if (typeof nested === "string" && nested.length >= MIN_SIGNATURE_LEN) return nested;
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function deleteReplaySession(key: string): number {
|
|
92
|
+
const entry = replayCache.get(key);
|
|
93
|
+
if (!entry) return 0;
|
|
94
|
+
replayCache.delete(key);
|
|
95
|
+
replayBytes -= entry.bytes;
|
|
96
|
+
if (replayOldestSessionKey === key) recomputeReplayOldestCandidate();
|
|
97
|
+
return entry.bytes;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function recomputeReplayOldestCandidate(): void {
|
|
101
|
+
replayOldestSessionKey = undefined;
|
|
102
|
+
replayOldestAt = null;
|
|
103
|
+
for (const [key, entry] of replayCache) {
|
|
104
|
+
if (entry.oldestAtMs === null || (replayOldestAt !== null && entry.oldestAtMs >= replayOldestAt)) continue;
|
|
105
|
+
replayOldestSessionKey = key;
|
|
106
|
+
replayOldestAt = entry.oldestAtMs;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function refreshReplaySessionCandidate(key: string, entry: ReplayEntry): void {
|
|
111
|
+
entry.oldestAtMs = entry.byCall.values().next().value?.touchedAtMs ?? null;
|
|
112
|
+
if (replayOldestSessionKey === key) {
|
|
113
|
+
recomputeReplayOldestCandidate();
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (entry.oldestAtMs !== null && (replayOldestAt === null || entry.oldestAtMs < replayOldestAt)) {
|
|
117
|
+
replayOldestSessionKey = key;
|
|
118
|
+
replayOldestAt = entry.oldestAtMs;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function deleteExpiredReplaySessions(now: number): void {
|
|
123
|
+
for (const [key, entry] of replayCache) if (entry.expiresAtMs <= now) deleteReplaySession(key);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function sweepExpiredAntigravityReplay(now = Date.now()): number {
|
|
127
|
+
const before = replayCache.size;
|
|
128
|
+
deleteExpiredReplaySessions(now);
|
|
129
|
+
return before - replayCache.size;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function deleteReplayCall(entry: ReplayEntry, callKey: string): number {
|
|
133
|
+
const call = entry.byCall.get(callKey);
|
|
134
|
+
if (!call) return 0;
|
|
135
|
+
entry.byCall.delete(callKey);
|
|
136
|
+
entry.bytes -= call.sizeBytes;
|
|
137
|
+
replayBytes -= call.sizeBytes;
|
|
138
|
+
return call.sizeBytes;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function evictInnerCalls(entry: ReplayEntry): void {
|
|
142
|
+
while (
|
|
143
|
+
entry.byCall.size > replayLimits.maxCallsPerSession
|
|
144
|
+
|| entry.bytes > replayLimits.maxBytesPerSession
|
|
145
|
+
) {
|
|
146
|
+
const oldest = entry.byCall.keys().next().value;
|
|
147
|
+
if (oldest === undefined) break;
|
|
148
|
+
deleteReplayCall(entry, oldest);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function evictIfNeeded(): void {
|
|
153
|
+
if (replayCache.size > ANTIGRAVITY_REPLAY_MAX_ENTRIES) {
|
|
154
|
+
const oldest = [...replayCache.entries()]
|
|
155
|
+
.sort((a, b) => a[1].expiresAtMs - b[1].expiresAtMs)
|
|
156
|
+
.slice(0, REPLAY_EVICT_BATCH);
|
|
157
|
+
for (const [key] of oldest) deleteReplaySession(key);
|
|
158
|
+
}
|
|
159
|
+
while (replayBytes > ANTIGRAVITY_REPLAY_MAX_TOTAL_BYTES) {
|
|
160
|
+
const oldestKey = [...replayCache.entries()]
|
|
161
|
+
.sort((a, b) => a[1].expiresAtMs - b[1].expiresAtMs)[0]?.[0];
|
|
162
|
+
if (oldestKey === undefined) return;
|
|
163
|
+
deleteReplaySession(oldestKey);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** Gemini/Flash/Agent use the replay cache; Claude does not (inline sanitization instead). */
|
|
168
|
+
export function antigravityUsesReplayCache(model: string): boolean {
|
|
169
|
+
return !/claude/i.test(model);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Observe a parsed CCA chunk's `candidates[0].content.parts` and record thought signatures keyed by
|
|
174
|
+
* the functionCall identity (name + args). Accumulates across the whole session so a sequential
|
|
175
|
+
* multi-step tool loop keeps EVERY prior call's signature, not just the latest part-index slot.
|
|
176
|
+
* `parts` is the already-unwrapped `response.candidates[0].content.parts`.
|
|
177
|
+
*/
|
|
178
|
+
export function observeAntigravityReplay(model: string, sessionId: string, parts: unknown[]): void {
|
|
179
|
+
if (!antigravityUsesReplayCache(model) || !Array.isArray(parts) || parts.length === 0) return;
|
|
180
|
+
const now = Date.now();
|
|
181
|
+
deleteExpiredReplaySessions(now);
|
|
182
|
+
const key = replayKey(model, sessionId);
|
|
183
|
+
const entry = replayCache.get(key) ?? {
|
|
184
|
+
byCall: new Map<string, ReplayCall>(),
|
|
185
|
+
bytes: 0,
|
|
186
|
+
expiresAtMs: 0,
|
|
187
|
+
oldestAtMs: null,
|
|
188
|
+
};
|
|
189
|
+
let inserted = false;
|
|
190
|
+
for (const raw of parts) {
|
|
191
|
+
if (!raw || typeof raw !== "object") continue;
|
|
192
|
+
const part = raw as Record<string, unknown>;
|
|
193
|
+
const sig = extractSignature(part);
|
|
194
|
+
if (!sig) continue;
|
|
195
|
+
const fc = part.functionCall as { name?: unknown; args?: unknown } | undefined;
|
|
196
|
+
const ck = fc ? functionCallKey(fc.name, fc.args) : undefined;
|
|
197
|
+
if (!ck) continue; // only function-call signatures are replayable by identity
|
|
198
|
+
const signatureBytes = utf8.encode(sig).byteLength;
|
|
199
|
+
const sizeBytes = utf8.encode(ck).byteLength + signatureBytes;
|
|
200
|
+
if (signatureBytes > replayLimits.maxSignatureBytes || sizeBytes > replayLimits.maxBytesPerSession) continue;
|
|
201
|
+
deleteReplayCall(entry, ck);
|
|
202
|
+
entry.byCall.set(ck, { signature: sig, sizeBytes, touchedAtMs: now });
|
|
203
|
+
entry.bytes += sizeBytes;
|
|
204
|
+
replayBytes += sizeBytes;
|
|
205
|
+
inserted = true;
|
|
206
|
+
}
|
|
207
|
+
if (!inserted) return;
|
|
208
|
+
evictInnerCalls(entry);
|
|
209
|
+
entry.expiresAtMs = now + REPLAY_TTL_MS;
|
|
210
|
+
replayCache.set(key, entry);
|
|
211
|
+
refreshReplaySessionCandidate(key, entry);
|
|
212
|
+
evictIfNeeded();
|
|
213
|
+
enforceAppOwnedMemoryBudget();
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Re-inject cached thought signatures into the outgoing `request.contents`, matched by functionCall
|
|
218
|
+
* identity across ALL model turns (not just the last one). Only fills a functionCall part that
|
|
219
|
+
* lacks a real signature. Returns the same array reference (mutated in place).
|
|
220
|
+
*/
|
|
221
|
+
export function applyAntigravityReplay(model: string, sessionId: string, contents: unknown[]): unknown[] {
|
|
222
|
+
if (!antigravityUsesReplayCache(model) || !Array.isArray(contents)) return contents;
|
|
223
|
+
const now = Date.now();
|
|
224
|
+
deleteExpiredReplaySessions(now);
|
|
225
|
+
const entry = replayCache.get(replayKey(model, sessionId));
|
|
226
|
+
if (!entry) {
|
|
227
|
+
return contents;
|
|
228
|
+
}
|
|
229
|
+
let touched = false;
|
|
230
|
+
for (const c of contents as { role?: string; parts?: unknown[] }[]) {
|
|
231
|
+
if (!c || typeof c !== "object" || c.role !== "model" || !Array.isArray(c.parts)) continue;
|
|
232
|
+
for (const raw of c.parts) {
|
|
233
|
+
if (!raw || typeof raw !== "object") continue;
|
|
234
|
+
const part = raw as Record<string, unknown>;
|
|
235
|
+
const fc = part.functionCall as { name?: unknown; args?: unknown } | undefined;
|
|
236
|
+
if (!fc) continue;
|
|
237
|
+
if (part.thoughtSignature !== undefined || part.thought_signature !== undefined) continue;
|
|
238
|
+
const ck = functionCallKey(fc.name, fc.args);
|
|
239
|
+
const call = ck ? entry.byCall.get(ck) : undefined;
|
|
240
|
+
if (call && ck) {
|
|
241
|
+
part.thoughtSignature = call.signature;
|
|
242
|
+
entry.byCall.delete(ck);
|
|
243
|
+
entry.byCall.set(ck, { ...call, touchedAtMs: now });
|
|
244
|
+
touched = true;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
if (touched) refreshReplaySessionCandidate(replayKey(model, sessionId), entry);
|
|
249
|
+
return contents;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/** Drop the cache entry when upstream rejects a signature (clear-on-invalid). */
|
|
253
|
+
export function clearAntigravityReplay(model: string, sessionId: string): void {
|
|
254
|
+
deleteReplaySession(replayKey(model, sessionId));
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export function antigravityReplayMetrics(): {
|
|
258
|
+
sessions: number;
|
|
259
|
+
calls: number;
|
|
260
|
+
totalBytes: number;
|
|
261
|
+
largestSessionBytes: number;
|
|
262
|
+
} {
|
|
263
|
+
let calls = 0;
|
|
264
|
+
let largestSessionBytes = 0;
|
|
265
|
+
for (const entry of replayCache.values()) {
|
|
266
|
+
calls += entry.byCall.size;
|
|
267
|
+
largestSessionBytes = Math.max(largestSessionBytes, entry.bytes);
|
|
268
|
+
}
|
|
269
|
+
return { sessions: replayCache.size, calls, totalBytes: replayBytes, largestSessionBytes };
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export function antigravityReplayRetainedStoreSnapshot(): {
|
|
273
|
+
count: number;
|
|
274
|
+
bytes: number;
|
|
275
|
+
evictableBytes: number;
|
|
276
|
+
pinnedBytes: number;
|
|
277
|
+
oldestAt: number | null;
|
|
278
|
+
} {
|
|
279
|
+
return {
|
|
280
|
+
count: replayCache.size,
|
|
281
|
+
bytes: replayBytes,
|
|
282
|
+
evictableBytes: replayBytes,
|
|
283
|
+
pinnedBytes: 0,
|
|
284
|
+
oldestAt: replayOldestAt,
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export function evictOldestAntigravityReplayForBudget(): number {
|
|
289
|
+
return replayOldestSessionKey === undefined ? 0 : deleteReplaySession(replayOldestSessionKey);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export function setAntigravityReplayLimitsForTests(limits?: Partial<ReplayLimits>): void {
|
|
293
|
+
__resetAntigravityReplayCache();
|
|
294
|
+
replayLimits = limits ? { ...DEFAULT_REPLAY_LIMITS, ...limits } : { ...DEFAULT_REPLAY_LIMITS };
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/** Test seam. */
|
|
298
|
+
export function __resetAntigravityReplayCache(): void {
|
|
299
|
+
replayCache.clear();
|
|
300
|
+
replayBytes = 0;
|
|
301
|
+
replayOldestSessionKey = undefined;
|
|
302
|
+
replayOldestAt = null;
|
|
303
|
+
}
|