@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,576 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { enforceAppOwnedMemoryBudget } from "../../lib/app-owned-memory";
|
|
3
|
+
import { create } from "@bufbuild/protobuf";
|
|
4
|
+
import {
|
|
5
|
+
DiagnosticsErrorSchema,
|
|
6
|
+
DiagnosticsResultSchema,
|
|
7
|
+
ErrorSchema,
|
|
8
|
+
GetBlobResultSchema,
|
|
9
|
+
KvClientMessageSchema,
|
|
10
|
+
McpErrorSchema,
|
|
11
|
+
McpResultSchema,
|
|
12
|
+
RequestContextResultSchema,
|
|
13
|
+
RequestContextSchema,
|
|
14
|
+
RequestContextSuccessSchema,
|
|
15
|
+
SetBlobResultSchema,
|
|
16
|
+
type ExecServerMessage,
|
|
17
|
+
type KvServerMessage,
|
|
18
|
+
} from "./gen/agent_pb";
|
|
19
|
+
import {
|
|
20
|
+
deleteExec,
|
|
21
|
+
grepExec,
|
|
22
|
+
lsExec,
|
|
23
|
+
readExec,
|
|
24
|
+
rejectDeleteExecForApplyPatch,
|
|
25
|
+
rejectDeleteExecForPolicy,
|
|
26
|
+
rejectGrepExecForPolicy,
|
|
27
|
+
rejectLsExecForPolicy,
|
|
28
|
+
rejectReadExecForPolicy,
|
|
29
|
+
rejectWriteExecForApplyPatch,
|
|
30
|
+
rejectWriteExecForPolicy,
|
|
31
|
+
writeExec,
|
|
32
|
+
} from "./native-exec-fs";
|
|
33
|
+
import { debugProviderDiagnostic } from "../../lib/debug";
|
|
34
|
+
import { fetchExec, rejectFetchExecForPolicy, type CursorNativeNetworkDeps } from "./native-exec-network";
|
|
35
|
+
import {
|
|
36
|
+
backgroundShellSpawnExec,
|
|
37
|
+
rejectBackgroundShellSpawnExecForPolicy,
|
|
38
|
+
rejectShellExecForPolicy,
|
|
39
|
+
rejectShellStreamExecForPolicy,
|
|
40
|
+
rejectWriteShellStdinExecForPolicy,
|
|
41
|
+
shellExec,
|
|
42
|
+
shellStreamExec,
|
|
43
|
+
writeShellStdinExec,
|
|
44
|
+
} from "./native-exec-shell";
|
|
45
|
+
import {
|
|
46
|
+
computerUseExec,
|
|
47
|
+
listMcpResourcesExec,
|
|
48
|
+
mcpExec,
|
|
49
|
+
readMcpResourceExec,
|
|
50
|
+
recordScreenExec,
|
|
51
|
+
type CursorNativeToolDeps,
|
|
52
|
+
} from "./native-exec-tools";
|
|
53
|
+
import { clientBytes, execBytes } from "./native-exec-common";
|
|
54
|
+
import type { McpToolDefinition } from "./gen/agent_pb";
|
|
55
|
+
import { OCX_RESPONSES_TOOL_PROVIDER } from "./tool-definitions";
|
|
56
|
+
|
|
57
|
+
export type CursorNativeExecDeps = CursorNativeNetworkDeps & CursorNativeToolDeps;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Execution context for a Cursor stream: the per-call executors plus the MCP tool definitions
|
|
61
|
+
* advertised to the server via `requestContextResult`. Without `mcpToolDefs`, the server is
|
|
62
|
+
* never told any MCP tools exist, so it never sends `mcpArgs`.
|
|
63
|
+
*/
|
|
64
|
+
export interface CursorNativeExecContext extends CursorNativeExecDeps {
|
|
65
|
+
/** Stable owner for background shells created by this transport session. */
|
|
66
|
+
sessionId?: string;
|
|
67
|
+
mcpToolDefs?: McpToolDefinition[];
|
|
68
|
+
clientToolDefs?: McpToolDefinition[];
|
|
69
|
+
/** Unsafe opt-in escape hatch for Cursor server-driven local fs/shell/fetch execution. */
|
|
70
|
+
unsafeAllowNativeLocalExec?: boolean;
|
|
71
|
+
/** apply_patch is visible for this request; Cursor-native write/delete must not bypass Codex. */
|
|
72
|
+
rejectNativeFileMutations?: boolean;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function cursorUnsafeNativeLocalExecEnabled(input: Pick<CursorNativeExecContext, "unsafeAllowNativeLocalExec"> = {}): boolean {
|
|
76
|
+
return input.unsafeAllowNativeLocalExec === true;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Content-addressed blob store shared across streams. Bounded: without eviction a long-running
|
|
81
|
+
* proxy accumulates every conversation's prompt blobs forever (unbounded memory) and any stale
|
|
82
|
+
* blob stays servable indefinitely — a cross-conversation contamination enabler if Cursor's
|
|
83
|
+
* server-side state ever references old ids (devlog 260702 P0). Continuation requests re-store
|
|
84
|
+
* their blobs on every turn (`rootPromptMessages` → `storeCursorBlob`), so TTL + cap eviction is
|
|
85
|
+
* safe for live sessions: only genuinely abandoned entries age out.
|
|
86
|
+
*/
|
|
87
|
+
const BLOB_TTL_MS = 15 * 60_000;
|
|
88
|
+
const BLOB_MAX_ENTRIES = 4_096;
|
|
89
|
+
const BLOB_MAX_ENTRY_BYTES = 16 * 1024 * 1024;
|
|
90
|
+
export const CURSOR_BLOB_MAX_TOTAL_BYTES = 64 * 1024 * 1024;
|
|
91
|
+
|
|
92
|
+
type CursorBlobProvenance = "local-regenerated" | "remote-setBlobArgs";
|
|
93
|
+
export type CursorBlobRequestScopeToken = symbol;
|
|
94
|
+
|
|
95
|
+
interface CursorBlobEntry {
|
|
96
|
+
data: Uint8Array;
|
|
97
|
+
storedAt: number;
|
|
98
|
+
sizeBytes: number;
|
|
99
|
+
provenance: CursorBlobProvenance;
|
|
100
|
+
requestPins: Set<CursorBlobRequestScopeToken>;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
type CursorBlobRejectionReason = "entry_too_large" | "pinned_saturation" | "request_pinned_conflict";
|
|
104
|
+
type CursorBlobAdmission =
|
|
105
|
+
| { admitted: true; replaced: boolean }
|
|
106
|
+
| { admitted: false; reason: CursorBlobRejectionReason };
|
|
107
|
+
|
|
108
|
+
interface CursorBlobLimits {
|
|
109
|
+
ttlMs: number;
|
|
110
|
+
maxEntries: number;
|
|
111
|
+
maxEntryBytes: number;
|
|
112
|
+
maxTotalBytes: number;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
interface CursorBlobRequestScopeState {
|
|
116
|
+
keys: Set<string>;
|
|
117
|
+
sealed: boolean;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const DEFAULT_BLOB_LIMITS: CursorBlobLimits = {
|
|
121
|
+
ttlMs: BLOB_TTL_MS,
|
|
122
|
+
maxEntries: BLOB_MAX_ENTRIES,
|
|
123
|
+
maxEntryBytes: BLOB_MAX_ENTRY_BYTES,
|
|
124
|
+
maxTotalBytes: CURSOR_BLOB_MAX_TOTAL_BYTES,
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const blobs = new Map<string, CursorBlobEntry>();
|
|
128
|
+
const blobRequestScopes = new Map<CursorBlobRequestScopeToken, CursorBlobRequestScopeState>();
|
|
129
|
+
let blobLimits = { ...DEFAULT_BLOB_LIMITS };
|
|
130
|
+
let blobBytes = 0;
|
|
131
|
+
let blobLocalBytes = 0;
|
|
132
|
+
let blobPinnedBytes = 0;
|
|
133
|
+
let blobEvictableBytes = 0;
|
|
134
|
+
let blobOldestEvictableAt: number | null = null;
|
|
135
|
+
let rejectedEntryTooLarge = 0;
|
|
136
|
+
let rejectedPinnedSaturation = 0;
|
|
137
|
+
let blobExpiryAccountingTimer: ReturnType<typeof setTimeout> | undefined;
|
|
138
|
+
|
|
139
|
+
function isExpired(entry: CursorBlobEntry, now: number): boolean {
|
|
140
|
+
return now - entry.storedAt >= blobLimits.ttlMs;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function bytesEqual(a: Uint8Array, b: Uint8Array): boolean {
|
|
144
|
+
if (a.byteLength !== b.byteLength) return false;
|
|
145
|
+
for (let i = 0; i < a.byteLength; i++) if (a[i] !== b[i]) return false;
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function recomputeBlobClassAccounting(): void {
|
|
150
|
+
const now = Date.now();
|
|
151
|
+
let localBytes = 0;
|
|
152
|
+
let pinnedBytes = 0;
|
|
153
|
+
let evictableBytes = 0;
|
|
154
|
+
let oldestAt: number | null = null;
|
|
155
|
+
for (const entry of blobs.values()) {
|
|
156
|
+
const requestPinned = entry.requestPins.size > 0;
|
|
157
|
+
const provenancePinned = entry.provenance === "remote-setBlobArgs" && !isExpired(entry, now);
|
|
158
|
+
if (entry.provenance === "local-regenerated") localBytes += entry.sizeBytes;
|
|
159
|
+
if (requestPinned || provenancePinned) pinnedBytes += entry.sizeBytes;
|
|
160
|
+
if (!requestPinned && (entry.provenance === "local-regenerated" || isExpired(entry, now))) {
|
|
161
|
+
evictableBytes += entry.sizeBytes;
|
|
162
|
+
oldestAt = oldestAt === null ? entry.storedAt : Math.min(oldestAt, entry.storedAt);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
blobLocalBytes = localBytes;
|
|
166
|
+
blobPinnedBytes = pinnedBytes;
|
|
167
|
+
blobEvictableBytes = evictableBytes;
|
|
168
|
+
blobOldestEvictableAt = oldestAt;
|
|
169
|
+
scheduleBlobExpiryAccounting(now);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function reconcileBlobClassAccountingAndEnforce(): void {
|
|
173
|
+
recomputeBlobClassAccounting();
|
|
174
|
+
enforceAppOwnedMemoryBudget();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function scheduleBlobExpiryAccounting(now: number): void {
|
|
178
|
+
if (blobExpiryAccountingTimer) clearTimeout(blobExpiryAccountingTimer);
|
|
179
|
+
blobExpiryAccountingTimer = undefined;
|
|
180
|
+
let nextExpiry = Number.POSITIVE_INFINITY;
|
|
181
|
+
for (const entry of blobs.values()) {
|
|
182
|
+
if (entry.provenance !== "remote-setBlobArgs" || entry.requestPins.size > 0) continue;
|
|
183
|
+
const expiresAt = entry.storedAt + blobLimits.ttlMs;
|
|
184
|
+
if (expiresAt > now) nextExpiry = Math.min(nextExpiry, expiresAt);
|
|
185
|
+
}
|
|
186
|
+
if (!Number.isFinite(nextExpiry)) return;
|
|
187
|
+
blobExpiryAccountingTimer = setTimeout(() => {
|
|
188
|
+
blobExpiryAccountingTimer = undefined;
|
|
189
|
+
reconcileBlobClassAccountingAndEnforce();
|
|
190
|
+
}, Math.max(0, nextExpiry - now));
|
|
191
|
+
blobExpiryAccountingTimer.unref?.();
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function deleteBlob(k: string, recompute = true): number {
|
|
195
|
+
const entry = blobs.get(k);
|
|
196
|
+
if (!entry) return 0;
|
|
197
|
+
blobs.delete(k);
|
|
198
|
+
blobBytes -= entry.sizeBytes;
|
|
199
|
+
for (const scope of entry.requestPins) blobRequestScopes.get(scope)?.keys.delete(k);
|
|
200
|
+
if (recompute) recomputeBlobClassAccounting();
|
|
201
|
+
return entry.sizeBytes;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function releaseHydratedBlob(k: string, requestScope?: CursorBlobRequestScopeToken): void {
|
|
205
|
+
const entry = blobs.get(k);
|
|
206
|
+
if (!entry) return;
|
|
207
|
+
const scopes = requestScope ? [requestScope] : [...entry.requestPins];
|
|
208
|
+
let changed = false;
|
|
209
|
+
for (const scope of scopes) {
|
|
210
|
+
if (!entry.requestPins.delete(scope)) continue;
|
|
211
|
+
changed = true;
|
|
212
|
+
const state = blobRequestScopes.get(scope);
|
|
213
|
+
state?.keys.delete(k);
|
|
214
|
+
if (state?.sealed && state.keys.size === 0) blobRequestScopes.delete(scope);
|
|
215
|
+
}
|
|
216
|
+
if (changed) reconcileBlobClassAccountingAndEnforce();
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function setBlob(
|
|
220
|
+
k: string,
|
|
221
|
+
data: Uint8Array,
|
|
222
|
+
provenance: CursorBlobProvenance,
|
|
223
|
+
requestScope?: CursorBlobRequestScopeToken,
|
|
224
|
+
): CursorBlobAdmission {
|
|
225
|
+
if (data.byteLength > blobLimits.maxEntryBytes) {
|
|
226
|
+
rejectedEntryTooLarge++;
|
|
227
|
+
return { admitted: false, reason: "entry_too_large" };
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const now = Date.now();
|
|
231
|
+
const existing = blobs.get(k);
|
|
232
|
+
const sameData = existing ? bytesEqual(existing.data, data) : false;
|
|
233
|
+
if (existing && !sameData && existing.requestPins.size > 0) {
|
|
234
|
+
return { admitted: false, reason: "request_pinned_conflict" };
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const removals = new Set<string>();
|
|
238
|
+
for (const [candidateKey, entry] of blobs) {
|
|
239
|
+
if (candidateKey === k && sameData) continue;
|
|
240
|
+
if (entry.requestPins.size === 0 && isExpired(entry, now)) removals.add(candidateKey);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const existingRemovedByTtl = existing !== undefined && removals.has(k);
|
|
244
|
+
const reuseExisting = existing !== undefined && sameData && !existingRemovedByTtl;
|
|
245
|
+
const mergedProvenance: CursorBlobProvenance = reuseExisting && existing.provenance === "remote-setBlobArgs"
|
|
246
|
+
? "remote-setBlobArgs"
|
|
247
|
+
: provenance;
|
|
248
|
+
const storedAt = reuseExisting && existing.provenance === "remote-setBlobArgs" && provenance === "local-regenerated"
|
|
249
|
+
? existing.storedAt
|
|
250
|
+
: now;
|
|
251
|
+
|
|
252
|
+
let projectedBytes = blobBytes;
|
|
253
|
+
let projectedCount = blobs.size;
|
|
254
|
+
for (const candidateKey of removals) {
|
|
255
|
+
const entry = blobs.get(candidateKey);
|
|
256
|
+
if (!entry) continue;
|
|
257
|
+
projectedBytes -= entry.sizeBytes;
|
|
258
|
+
projectedCount--;
|
|
259
|
+
}
|
|
260
|
+
if (existing && !existingRemovedByTtl) {
|
|
261
|
+
projectedBytes -= existing.sizeBytes;
|
|
262
|
+
projectedCount--;
|
|
263
|
+
}
|
|
264
|
+
projectedBytes += data.byteLength;
|
|
265
|
+
projectedCount++;
|
|
266
|
+
|
|
267
|
+
if (projectedBytes > blobLimits.maxTotalBytes || projectedCount > blobLimits.maxEntries) {
|
|
268
|
+
const localVictims = [...blobs.entries()].sort((a, b) => a[1].storedAt - b[1].storedAt);
|
|
269
|
+
for (const [candidateKey, entry] of localVictims) {
|
|
270
|
+
if (
|
|
271
|
+
candidateKey === k
|
|
272
|
+
|| removals.has(candidateKey)
|
|
273
|
+
|| entry.requestPins.size > 0
|
|
274
|
+
|| entry.provenance !== "local-regenerated"
|
|
275
|
+
) continue;
|
|
276
|
+
removals.add(candidateKey);
|
|
277
|
+
projectedBytes -= entry.sizeBytes;
|
|
278
|
+
projectedCount--;
|
|
279
|
+
if (projectedBytes <= blobLimits.maxTotalBytes && projectedCount <= blobLimits.maxEntries) break;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (projectedBytes > blobLimits.maxTotalBytes || projectedCount > blobLimits.maxEntries) {
|
|
284
|
+
rejectedPinnedSaturation++;
|
|
285
|
+
return { admitted: false, reason: "pinned_saturation" };
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Carry forward only pins whose scope still has live state; a dead scope's
|
|
289
|
+
// token can never be released again (review C1-1).
|
|
290
|
+
const requestPins = new Set<CursorBlobRequestScopeToken>();
|
|
291
|
+
if (reuseExisting) {
|
|
292
|
+
for (const scope of existing.requestPins) {
|
|
293
|
+
if (blobRequestScopes.has(scope)) requestPins.add(scope);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
// A pin is only attached when the scope still has live UNSEALED state:
|
|
297
|
+
// sealing fixes the advertised key set (locked contract), so a remote
|
|
298
|
+
// setBlobArgs arriving after seal must not extend the pin set — and a
|
|
299
|
+
// sealed scope whose advertised keys have all hydrated is already deleted,
|
|
300
|
+
// so attaching its stale token would create a permanent untracked pin the
|
|
301
|
+
// terminal release could never clear (reviews C1-1/C2-1).
|
|
302
|
+
const scopeState = requestScope ? blobRequestScopes.get(requestScope) : undefined;
|
|
303
|
+
if (requestScope && scopeState && !scopeState.sealed) requestPins.add(requestScope);
|
|
304
|
+
const entry: CursorBlobEntry = {
|
|
305
|
+
data: reuseExisting ? existing.data : data.slice(),
|
|
306
|
+
storedAt,
|
|
307
|
+
sizeBytes: data.byteLength,
|
|
308
|
+
provenance: mergedProvenance,
|
|
309
|
+
requestPins,
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
for (const candidateKey of removals) deleteBlob(candidateKey, false);
|
|
313
|
+
if (blobs.has(k)) deleteBlob(k, false);
|
|
314
|
+
blobs.set(k, entry);
|
|
315
|
+
blobBytes += entry.sizeBytes;
|
|
316
|
+
for (const scope of entry.requestPins) blobRequestScopes.get(scope)?.keys.add(k);
|
|
317
|
+
reconcileBlobClassAccountingAndEnforce();
|
|
318
|
+
return { admitted: true, replaced: existing !== undefined };
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function getBlob(k: string): Uint8Array | undefined {
|
|
322
|
+
const entry = blobs.get(k);
|
|
323
|
+
if (!entry) return undefined;
|
|
324
|
+
if (entry.requestPins.size === 0 && isExpired(entry, Date.now())) {
|
|
325
|
+
deleteBlob(k);
|
|
326
|
+
return undefined;
|
|
327
|
+
}
|
|
328
|
+
return entry.data;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function key(bytes: Uint8Array): string {
|
|
332
|
+
return Buffer.from(bytes).toString("hex");
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Store a blob (SHA-256 keyed) in the shared map that `handleCursorNativeKv` serves, and return its
|
|
337
|
+
* blob id. Cursor's `rootPromptMessagesJson`/turn entries are blob IDS, not inline content — the
|
|
338
|
+
* server fetches the bytes back via `getBlobArgs`. Mirrors jawcode `createBlobId`/`storeCursorBlob`.
|
|
339
|
+
*/
|
|
340
|
+
export class CursorBlobAdmissionError extends Error {
|
|
341
|
+
readonly code = "cursor_blob_capacity";
|
|
342
|
+
|
|
343
|
+
constructor(readonly reason: CursorBlobRejectionReason) {
|
|
344
|
+
super("Cursor blob capacity is exhausted; retry with a smaller request.");
|
|
345
|
+
this.name = "CursorBlobAdmissionError";
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
let blobScopeSequence = 0;
|
|
350
|
+
|
|
351
|
+
export function createCursorBlobRequestScope(): CursorBlobRequestScopeToken {
|
|
352
|
+
// Unique description per token so debug snapshots expose exact pin
|
|
353
|
+
// IDENTITY, not just pin counts (review C2-2: identical descriptions made
|
|
354
|
+
// scope-swap bugs invisible to deep comparison).
|
|
355
|
+
const scope = Symbol(`cursor-blob-request-${++blobScopeSequence}`);
|
|
356
|
+
blobRequestScopes.set(scope, { keys: new Set(), sealed: false });
|
|
357
|
+
return scope;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
export function sealCursorBlobRequestScope(scope: CursorBlobRequestScopeToken): void {
|
|
361
|
+
const state = blobRequestScopes.get(scope);
|
|
362
|
+
if (!state) return;
|
|
363
|
+
state.sealed = true;
|
|
364
|
+
if (state.keys.size === 0) blobRequestScopes.delete(scope);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
export function releaseCursorBlobRequestScope(scope: CursorBlobRequestScopeToken): void {
|
|
368
|
+
const state = blobRequestScopes.get(scope);
|
|
369
|
+
if (!state) return;
|
|
370
|
+
for (const k of state.keys) blobs.get(k)?.requestPins.delete(scope);
|
|
371
|
+
blobRequestScopes.delete(scope);
|
|
372
|
+
reconcileBlobClassAccountingAndEnforce();
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
export function storeCursorBlob(data: Uint8Array, requestScope?: CursorBlobRequestScopeToken): Uint8Array {
|
|
376
|
+
const blobId = new Uint8Array(createHash("sha256").update(data).digest());
|
|
377
|
+
const admission = setBlob(key(blobId), data, "local-regenerated", requestScope);
|
|
378
|
+
if (!admission.admitted) throw new CursorBlobAdmissionError(admission.reason);
|
|
379
|
+
return blobId;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
export interface CursorBlobMetrics {
|
|
383
|
+
count: number;
|
|
384
|
+
totalBytes: number;
|
|
385
|
+
localBytes: number;
|
|
386
|
+
pinnedBytes: number;
|
|
387
|
+
rejectedEntryTooLarge: number;
|
|
388
|
+
rejectedPinnedSaturation: number;
|
|
389
|
+
oldestAt: number | null;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
export function cursorBlobMetrics(): CursorBlobMetrics {
|
|
393
|
+
return {
|
|
394
|
+
count: blobs.size,
|
|
395
|
+
totalBytes: blobBytes,
|
|
396
|
+
localBytes: blobLocalBytes,
|
|
397
|
+
pinnedBytes: blobPinnedBytes,
|
|
398
|
+
rejectedEntryTooLarge,
|
|
399
|
+
rejectedPinnedSaturation,
|
|
400
|
+
oldestAt: blobOldestEvictableAt,
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
export function cursorBlobRetainedStoreSnapshot(): {
|
|
405
|
+
count: number;
|
|
406
|
+
bytes: number;
|
|
407
|
+
evictableBytes: number;
|
|
408
|
+
pinnedBytes: number;
|
|
409
|
+
oldestAt: number | null;
|
|
410
|
+
} {
|
|
411
|
+
return {
|
|
412
|
+
count: blobs.size,
|
|
413
|
+
bytes: blobBytes,
|
|
414
|
+
evictableBytes: blobEvictableBytes,
|
|
415
|
+
pinnedBytes: blobPinnedBytes,
|
|
416
|
+
oldestAt: blobOldestEvictableAt,
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
export function evictOldestCursorBlobForBudget(): number {
|
|
421
|
+
const now = Date.now();
|
|
422
|
+
let oldest: [string, CursorBlobEntry] | undefined;
|
|
423
|
+
for (const candidate of blobs) {
|
|
424
|
+
const entry = candidate[1];
|
|
425
|
+
if (entry.requestPins.size > 0) continue;
|
|
426
|
+
if (entry.provenance !== "local-regenerated" && !isExpired(entry, now)) continue;
|
|
427
|
+
if (!oldest || entry.storedAt < oldest[1].storedAt) oldest = candidate;
|
|
428
|
+
}
|
|
429
|
+
return oldest ? deleteBlob(oldest[0]) : 0;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
export function setCursorBlobLimitsForTests(limits?: Partial<CursorBlobLimits>): void {
|
|
433
|
+
resetCursorBlobStateForTests();
|
|
434
|
+
blobLimits = limits ? { ...DEFAULT_BLOB_LIMITS, ...limits } : { ...DEFAULT_BLOB_LIMITS };
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
export function resetCursorBlobStateForTests(): void {
|
|
438
|
+
if (blobExpiryAccountingTimer) clearTimeout(blobExpiryAccountingTimer);
|
|
439
|
+
blobExpiryAccountingTimer = undefined;
|
|
440
|
+
blobs.clear();
|
|
441
|
+
blobRequestScopes.clear();
|
|
442
|
+
blobBytes = 0;
|
|
443
|
+
rejectedEntryTooLarge = 0;
|
|
444
|
+
rejectedPinnedSaturation = 0;
|
|
445
|
+
recomputeBlobClassAccounting();
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
export function cursorBlobStoreDebugSnapshotForTests(): Array<{
|
|
449
|
+
key: string;
|
|
450
|
+
sizeBytes: number;
|
|
451
|
+
storedAt: number;
|
|
452
|
+
provenance: CursorBlobProvenance;
|
|
453
|
+
requestPins: number;
|
|
454
|
+
dataDigest: string;
|
|
455
|
+
pinTokens: string[];
|
|
456
|
+
}> {
|
|
457
|
+
return [...blobs].map(([blobKey, entry]) => ({
|
|
458
|
+
key: blobKey,
|
|
459
|
+
sizeBytes: entry.sizeBytes,
|
|
460
|
+
storedAt: entry.storedAt,
|
|
461
|
+
provenance: entry.provenance,
|
|
462
|
+
requestPins: entry.requestPins.size,
|
|
463
|
+
// Byte-for-byte content identity + exact pin-set membership so rollback
|
|
464
|
+
// tests can deep-compare complete state (review C1-2).
|
|
465
|
+
dataDigest: createHash("sha256").update(entry.data).digest("hex"),
|
|
466
|
+
pinTokens: [...entry.requestPins].map(scope => String(scope.description ?? "scope")).sort(),
|
|
467
|
+
}));
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
export async function handleCursorNativeExec(execMsg: ExecServerMessage, deps: CursorNativeExecContext = {}): Promise<Uint8Array[]> {
|
|
471
|
+
const execCase = execMsg.message.case;
|
|
472
|
+
if (execCase === "requestContextArgs") {
|
|
473
|
+
const tools = [...(deps.mcpToolDefs ?? []), ...(deps.clientToolDefs ?? [])];
|
|
474
|
+
return [execBytes(execMsg, "requestContextResult", create(RequestContextResultSchema, {
|
|
475
|
+
result: { case: "success", value: create(RequestContextSuccessSchema, { requestContext: create(RequestContextSchema, { tools }) }) },
|
|
476
|
+
}))];
|
|
477
|
+
}
|
|
478
|
+
if (!cursorUnsafeNativeLocalExecEnabled(deps)) {
|
|
479
|
+
if (execCase === "readArgs") return [rejectReadExecForPolicy(execMsg)];
|
|
480
|
+
if (execCase === "writeArgs") return [rejectWriteExecForPolicy(execMsg)];
|
|
481
|
+
if (execCase === "deleteArgs") return [rejectDeleteExecForPolicy(execMsg)];
|
|
482
|
+
if (execCase === "lsArgs") return [rejectLsExecForPolicy(execMsg)];
|
|
483
|
+
if (execCase === "grepArgs") return [rejectGrepExecForPolicy(execMsg)];
|
|
484
|
+
if (execCase === "shellArgs") return [rejectShellExecForPolicy(execMsg)];
|
|
485
|
+
if (execCase === "shellStreamArgs") return rejectShellStreamExecForPolicy(execMsg);
|
|
486
|
+
if (execCase === "backgroundShellSpawnArgs") return [rejectBackgroundShellSpawnExecForPolicy(execMsg)];
|
|
487
|
+
if (execCase === "writeShellStdinArgs") return [rejectWriteShellStdinExecForPolicy(execMsg)];
|
|
488
|
+
if (execCase === "fetchArgs") return [rejectFetchExecForPolicy(execMsg)];
|
|
489
|
+
}
|
|
490
|
+
if (execCase === "readArgs") return [readExec(execMsg)];
|
|
491
|
+
if (execCase === "writeArgs") return [deps.rejectNativeFileMutations ? rejectWriteExecForApplyPatch(execMsg) : writeExec(execMsg)];
|
|
492
|
+
if (execCase === "deleteArgs") return [deps.rejectNativeFileMutations ? rejectDeleteExecForApplyPatch(execMsg) : deleteExec(execMsg)];
|
|
493
|
+
if (execCase === "lsArgs") return [lsExec(execMsg)];
|
|
494
|
+
if (execCase === "grepArgs") return [grepExec(execMsg)];
|
|
495
|
+
if (execCase === "shellArgs") return [shellExec(execMsg)];
|
|
496
|
+
if (execCase === "shellStreamArgs") return shellStreamExec(execMsg);
|
|
497
|
+
if (execCase === "backgroundShellSpawnArgs") return [backgroundShellSpawnExec(execMsg, deps.sessionId ?? "")];
|
|
498
|
+
if (execCase === "writeShellStdinArgs") return [writeShellStdinExec(execMsg, deps.sessionId ?? "")];
|
|
499
|
+
if (execCase === "fetchArgs") return [await fetchExec(execMsg, deps)];
|
|
500
|
+
if (execCase === "mcpArgs" && execMsg.message.value.providerIdentifier === OCX_RESPONSES_TOOL_PROVIDER) {
|
|
501
|
+
return [execBytes(execMsg, "mcpResult", create(McpResultSchema, {
|
|
502
|
+
result: {
|
|
503
|
+
case: "error",
|
|
504
|
+
value: create(McpErrorSchema, { error: "Cursor requested a client Responses tool through the native exec channel; bridge suspension is not implemented." }),
|
|
505
|
+
},
|
|
506
|
+
}))];
|
|
507
|
+
}
|
|
508
|
+
if (execCase === "mcpArgs") return [await mcpExec(execMsg, deps)];
|
|
509
|
+
if (execCase === "listMcpResourcesExecArgs") return [await listMcpResourcesExec(execMsg, deps)];
|
|
510
|
+
if (execCase === "readMcpResourceExecArgs") return [await readMcpResourceExec(execMsg, deps)];
|
|
511
|
+
if (execCase === "computerUseArgs") return [await computerUseExec(execMsg, deps)];
|
|
512
|
+
if (execCase === "recordScreenArgs") return [await recordScreenExec(execMsg, deps)];
|
|
513
|
+
if (execCase === "diagnosticsArgs") {
|
|
514
|
+
const path = execMsg.message.value.path;
|
|
515
|
+
return [execBytes(execMsg, "diagnosticsResult", create(DiagnosticsResultSchema, {
|
|
516
|
+
result: {
|
|
517
|
+
case: "error",
|
|
518
|
+
value: create(DiagnosticsErrorSchema, {
|
|
519
|
+
path,
|
|
520
|
+
error: "Diagnostics are not supported by the opencodex Cursor transport.",
|
|
521
|
+
}),
|
|
522
|
+
},
|
|
523
|
+
}))];
|
|
524
|
+
}
|
|
525
|
+
// Unknown exec case — Cursor added a new native exec type that our protobuf definition does not
|
|
526
|
+
// include yet. Return an empty reply so the stream stays alive instead of throwing (which kills
|
|
527
|
+
// the entire gRPC connection via failAndClear). Same class of bug as #116.
|
|
528
|
+
debugProviderDiagnostic("cursor", "unknown-exec-case", { execCase: execCase ?? "unknown", execId: execMsg.execId });
|
|
529
|
+
return [];
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
|
|
533
|
+
export function handleCursorNativeKv(
|
|
534
|
+
kvMsg: KvServerMessage,
|
|
535
|
+
requestScope?: CursorBlobRequestScopeToken,
|
|
536
|
+
): Uint8Array {
|
|
537
|
+
if (kvMsg.message.case === "getBlobArgs") {
|
|
538
|
+
const blobKey = key(kvMsg.message.value.blobId);
|
|
539
|
+
const blobData = getBlob(blobKey);
|
|
540
|
+
if (blobData) releaseHydratedBlob(blobKey, requestScope);
|
|
541
|
+
return clientBytes({
|
|
542
|
+
message: {
|
|
543
|
+
case: "kvClientMessage",
|
|
544
|
+
value: create(KvClientMessageSchema, {
|
|
545
|
+
id: kvMsg.id,
|
|
546
|
+
message: { case: "getBlobResult", value: create(GetBlobResultSchema, blobData ? { blobData } : {}) },
|
|
547
|
+
}),
|
|
548
|
+
},
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
if (kvMsg.message.case === "setBlobArgs") {
|
|
552
|
+
const admission = setBlob(
|
|
553
|
+
key(kvMsg.message.value.blobId),
|
|
554
|
+
kvMsg.message.value.blobData,
|
|
555
|
+
"remote-setBlobArgs",
|
|
556
|
+
requestScope,
|
|
557
|
+
);
|
|
558
|
+
return clientBytes({
|
|
559
|
+
message: {
|
|
560
|
+
case: "kvClientMessage",
|
|
561
|
+
value: create(KvClientMessageSchema, {
|
|
562
|
+
id: kvMsg.id,
|
|
563
|
+
message: {
|
|
564
|
+
case: "setBlobResult",
|
|
565
|
+
value: create(SetBlobResultSchema, admission.admitted ? {} : {
|
|
566
|
+
error: create(ErrorSchema, {
|
|
567
|
+
message: "Cursor blob capacity is exhausted; the blob was not stored.",
|
|
568
|
+
}),
|
|
569
|
+
}),
|
|
570
|
+
},
|
|
571
|
+
}),
|
|
572
|
+
},
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
return clientBytes({ message: { case: "kvClientMessage", value: create(KvClientMessageSchema, { id: kvMsg.id }) } });
|
|
576
|
+
}
|