@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,714 @@
|
|
|
1
|
+
import { create, fromBinary, toBinary, toJson } from "@bufbuild/protobuf";
|
|
2
|
+
import { fromJson, type JsonValue } from "@bufbuild/protobuf";
|
|
3
|
+
import { ValueSchema } from "@bufbuild/protobuf/wkt";
|
|
4
|
+
import type { OcxAssistantContentPart, OcxMessage, OcxToolResultMessage } from "../../types";
|
|
5
|
+
import { namespacedToolName } from "../../types";
|
|
6
|
+
import type { CursorRunRequest } from "./types";
|
|
7
|
+
import { isCursorExternalWireModel } from "./discovery";
|
|
8
|
+
import { debugProviderDiagnostic } from "../../lib/debug";
|
|
9
|
+
import {
|
|
10
|
+
createCursorBlobRequestScope,
|
|
11
|
+
releaseCursorBlobRequestScope,
|
|
12
|
+
sealCursorBlobRequestScope,
|
|
13
|
+
storeCursorBlob,
|
|
14
|
+
type CursorBlobRequestScopeToken,
|
|
15
|
+
} from "./native-exec";
|
|
16
|
+
import { estimateTokens } from "../../lib/token-estimate";
|
|
17
|
+
import {
|
|
18
|
+
AgentClientMessageSchema,
|
|
19
|
+
AgentConversationTurnStructureSchema,
|
|
20
|
+
AssistantMessageSchema,
|
|
21
|
+
AgentRunRequestSchema,
|
|
22
|
+
ConversationActionSchema,
|
|
23
|
+
ConversationStepSchema,
|
|
24
|
+
ConversationStateStructureSchema,
|
|
25
|
+
ConversationTurnStructureSchema,
|
|
26
|
+
McpArgsSchema,
|
|
27
|
+
McpSuccessSchema,
|
|
28
|
+
McpTextContentSchema,
|
|
29
|
+
McpToolCallSchema,
|
|
30
|
+
McpToolResultContentItemSchema,
|
|
31
|
+
McpToolResultSchema,
|
|
32
|
+
McpToolsSchema,
|
|
33
|
+
type McpToolDefinition,
|
|
34
|
+
ModelDetailsSchema,
|
|
35
|
+
RequestedModelSchema,
|
|
36
|
+
RequestedModel_ModelParameterbytesSchema,
|
|
37
|
+
ResumeActionSchema,
|
|
38
|
+
RequestContextSchema,
|
|
39
|
+
RequestContextEnvSchema,
|
|
40
|
+
ThinkingMessageSchema,
|
|
41
|
+
ToolCallSchema,
|
|
42
|
+
UserMessageActionSchema,
|
|
43
|
+
UserMessageSchema,
|
|
44
|
+
} from "./gen/agent_pb";
|
|
45
|
+
import {
|
|
46
|
+
appendCursorGenericToolUseHint,
|
|
47
|
+
cursorToolsForActivePrompt,
|
|
48
|
+
buildCursorToolGuidanceSystemNote,
|
|
49
|
+
buildCursorToolDefinitions,
|
|
50
|
+
cursorRequestHasShellAlias,
|
|
51
|
+
CURSOR_SHELL_ALIAS_SYSTEM_NOTE,
|
|
52
|
+
OCX_RESPONSES_TOOL_PROVIDER,
|
|
53
|
+
} from "./tool-definitions";
|
|
54
|
+
|
|
55
|
+
const encoder = new TextEncoder();
|
|
56
|
+
const decoder = new TextDecoder();
|
|
57
|
+
|
|
58
|
+
/** Parameter id advertised by Cursor's `default` model for its Cost/Balance/Intelligence control. */
|
|
59
|
+
export const CURSOR_ROUTING_LEVEL_PARAMETER_ID = "optimization";
|
|
60
|
+
// Cursor external workers reject oversized root replay sets with a late invalid_argument after
|
|
61
|
+
// hydrating every blob (observed at 208 roots with usedTokens=0). Keep headroom below that boundary,
|
|
62
|
+
// retaining all system prompts and the newest model-visible history. Cursor IDE similarly bounds /
|
|
63
|
+
// compacts long conversations rather than replaying an unbounded message list.
|
|
64
|
+
export const CURSOR_EXTERNAL_ROOT_BLOB_LIMIT = 192;
|
|
65
|
+
/** Approximate prompt-size guard; tool schemas and protocol framing consume context separately. */
|
|
66
|
+
export const CURSOR_EXTERNAL_ROOT_BYTE_LIMIT = 512 * 1024;
|
|
67
|
+
|
|
68
|
+
/** Runtime timezone for protobuf RequestContextEnv (dynamic, never hardcoded). */
|
|
69
|
+
function runtimeTimeZone(): string {
|
|
70
|
+
try {
|
|
71
|
+
return Intl.DateTimeFormat().resolvedOptions().timeZone ?? "UTC";
|
|
72
|
+
} catch {
|
|
73
|
+
return "UTC";
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Builds a RequestContext with env.timeZone populated dynamically. */
|
|
78
|
+
function buildRequestContext() {
|
|
79
|
+
return create(RequestContextSchema, {
|
|
80
|
+
env: create(RequestContextEnvSchema, {
|
|
81
|
+
timeZone: runtimeTimeZone(),
|
|
82
|
+
}),
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function jsonBlob(value: unknown): { data: Uint8Array; serialized: string } {
|
|
87
|
+
const serialized = JSON.stringify(value);
|
|
88
|
+
return { data: encoder.encode(serialized), serialized };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
type RootBlobCandidate = {
|
|
92
|
+
data: Uint8Array;
|
|
93
|
+
byteLength: number;
|
|
94
|
+
/**
|
|
95
|
+
* The exact JSON handed to storeCursorBlob(). Retained so a token estimate can read
|
|
96
|
+
* what the wire actually carries without re-serializing — and without drifting from
|
|
97
|
+
* it after pruning or truncation (#373).
|
|
98
|
+
*/
|
|
99
|
+
serialized: string;
|
|
100
|
+
role: "system" | "user" | "assistant" | "toolResult";
|
|
101
|
+
messageIndex?: number;
|
|
102
|
+
/** Original JSON text payload used when an active tool result must be truncated to fit. */
|
|
103
|
+
text?: string;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
function rootBlobCandidate(
|
|
107
|
+
value: unknown,
|
|
108
|
+
role: RootBlobCandidate["role"],
|
|
109
|
+
opts?: { messageIndex?: number; text?: string },
|
|
110
|
+
): RootBlobCandidate {
|
|
111
|
+
const { data, serialized } = jsonBlob(value);
|
|
112
|
+
return {
|
|
113
|
+
data,
|
|
114
|
+
byteLength: data.byteLength,
|
|
115
|
+
serialized,
|
|
116
|
+
role,
|
|
117
|
+
...(opts?.messageIndex !== undefined ? { messageIndex: opts.messageIndex } : {}),
|
|
118
|
+
...(opts?.text !== undefined ? { text: opts.text } : {}),
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function truncateToolResultBlob(entry: RootBlobCandidate, maxBytes: number): RootBlobCandidate | null {
|
|
123
|
+
if (entry.byteLength <= maxBytes) return entry;
|
|
124
|
+
if (entry.role !== "toolResult" || entry.text === undefined) return null;
|
|
125
|
+
const marker = "\n…[truncated for Cursor external replay budget]";
|
|
126
|
+
const encoded = encoder.encode(entry.text);
|
|
127
|
+
// Leave headroom for JSON envelope (`role`/`content` wrapper) around the truncated text.
|
|
128
|
+
let keepBytes = Math.min(encoded.byteLength, Math.max(0, maxBytes - encoder.encode(marker).byteLength - 96));
|
|
129
|
+
for (let attempt = 0; attempt < 8; attempt++) {
|
|
130
|
+
let end = keepBytes;
|
|
131
|
+
while (end > 0 && end < encoded.byteLength && (encoded[end]! & 0xc0) === 0x80) end -= 1;
|
|
132
|
+
const truncated = `${decoder.decode(encoded.subarray(0, end))}${marker}`;
|
|
133
|
+
const result = rootBlobCandidate(
|
|
134
|
+
{ role: "user", content: [{ type: "text", text: truncated }] },
|
|
135
|
+
"toolResult",
|
|
136
|
+
{ messageIndex: entry.messageIndex, text: truncated },
|
|
137
|
+
);
|
|
138
|
+
if (result.byteLength <= maxBytes) return result;
|
|
139
|
+
if (end === 0) break;
|
|
140
|
+
keepBytes = Math.max(0, end - (result.byteLength - maxBytes) - 16);
|
|
141
|
+
}
|
|
142
|
+
const markerOnly = rootBlobCandidate(
|
|
143
|
+
{ role: "user", content: [{ type: "text", text: marker.trimStart() }] },
|
|
144
|
+
"toolResult",
|
|
145
|
+
{ messageIndex: entry.messageIndex, text: marker.trimStart() },
|
|
146
|
+
);
|
|
147
|
+
return markerOnly.byteLength <= maxBytes ? markerOnly : null;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function systemPromptBlobs(request: CursorRunRequest): RootBlobCandidate[] {
|
|
151
|
+
const prompts = request.system.length > 0 ? [...request.system] : ["You are a helpful assistant."];
|
|
152
|
+
if (cursorRequestHasShellAlias(request.tools)) prompts.push(CURSOR_SHELL_ALIAS_SYSTEM_NOTE);
|
|
153
|
+
const cursorToolGuidance = buildCursorToolGuidanceSystemNote(
|
|
154
|
+
cursorToolsForActivePrompt(request.tools, activePromptText(request), request.toolChoice),
|
|
155
|
+
request.toolChoice,
|
|
156
|
+
);
|
|
157
|
+
if (cursorToolGuidance) prompts.push(cursorToolGuidance);
|
|
158
|
+
return prompts.map(content => rootBlobCandidate({ role: "system", content }, "system"));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function assistantRootText(
|
|
162
|
+
message: Extract<OcxMessage, { role: "assistant" }>,
|
|
163
|
+
includeThinking: boolean,
|
|
164
|
+
): string {
|
|
165
|
+
if (typeof message.content === "string") return message.content;
|
|
166
|
+
return message.content
|
|
167
|
+
.map(part => (part.type === "text" ? part.text : includeThinking && part.type === "thinking" ? part.thinking : undefined))
|
|
168
|
+
.filter((value): value is string => typeof value === "string" && value.length > 0)
|
|
169
|
+
.join("\n");
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Cursor builds the actual model prompt from rootPromptMessagesJson (turns[] is UI/display metadata),
|
|
173
|
+
// so prior history — including assistant tool calls and tool results — must be replayed here or a
|
|
174
|
+
// ResumeAction has nothing model-visible to continue from. The active user message is excluded
|
|
175
|
+
// because it travels in the action. Tool results are rendered as user-role text with a marker, and
|
|
176
|
+
// each entry is a SHA-256 blob ID (Cursor fetches the bytes back via getBlobArgs). Mirrors the
|
|
177
|
+
// danger-pi reference buildRootPromptMessagesJson.
|
|
178
|
+
function rootPromptMessages(request: CursorRunRequest, requestScope: CursorBlobRequestScopeToken): {
|
|
179
|
+
ids: Uint8Array[];
|
|
180
|
+
byteLength: number;
|
|
181
|
+
historyMessageStart: number;
|
|
182
|
+
/** Serialized text of the roots that survived pruning, in wire order. */
|
|
183
|
+
serialized: string[];
|
|
184
|
+
} {
|
|
185
|
+
const entries = systemPromptBlobs(request);
|
|
186
|
+
const systemEntryCount = entries.length;
|
|
187
|
+
const messages = request.rawMessages;
|
|
188
|
+
if (!messages?.length) {
|
|
189
|
+
return {
|
|
190
|
+
ids: entries.map(entry => storeCursorBlob(entry.data, requestScope)),
|
|
191
|
+
byteLength: entries.reduce((sum, entry) => sum + entry.byteLength, 0),
|
|
192
|
+
historyMessageStart: 0,
|
|
193
|
+
serialized: entries.map(entry => entry.serialized),
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const externalModel = isCursorExternalWireModel(request.modelId);
|
|
198
|
+
const lastRawIsToolResult = messages.at(-1)?.role === "toolResult";
|
|
199
|
+
const activeUserIndex = lastRawIsToolResult ? -1 : lastActionIndex(messages);
|
|
200
|
+
|
|
201
|
+
for (let i = 0; i < messages.length; i++) {
|
|
202
|
+
if (i === activeUserIndex) break;
|
|
203
|
+
const message = messages[i];
|
|
204
|
+
if (!message) continue;
|
|
205
|
+
if (message.role === "user" || message.role === "developer") {
|
|
206
|
+
const text = contentText(message).trim();
|
|
207
|
+
// Cursor root replay expects OpenAI-style content parts for historical user messages.
|
|
208
|
+
// A bare string survives blob hydration but external workers reject the completed replay
|
|
209
|
+
// before tokenization (`usedTokens: 0`, then invalid_argument).
|
|
210
|
+
if (text.length > 0) {
|
|
211
|
+
entries.push(rootBlobCandidate({
|
|
212
|
+
role: "user",
|
|
213
|
+
content: [{ type: "text", text }],
|
|
214
|
+
}, "user", { messageIndex: i }));
|
|
215
|
+
}
|
|
216
|
+
} else if (message.role === "assistant") {
|
|
217
|
+
// External Cursor clients do not replay hidden reasoning as assistant-visible prompt text.
|
|
218
|
+
// Native Composer state can preserve it through ThinkingMessage/history structures.
|
|
219
|
+
const text = assistantRootText(message, !externalModel).trim();
|
|
220
|
+
if (text.length > 0) {
|
|
221
|
+
entries.push(rootBlobCandidate(
|
|
222
|
+
{ role: "assistant", content: [{ type: "text", text }] },
|
|
223
|
+
"assistant",
|
|
224
|
+
{ messageIndex: i },
|
|
225
|
+
));
|
|
226
|
+
}
|
|
227
|
+
// Assistant tool CALLS are intentionally NOT replayed as visible "[Tool Call]" text here.
|
|
228
|
+
} else if (message.role === "toolResult") {
|
|
229
|
+
const prefix = message.isError ? "[Tool Error]" : "[Tool Result]";
|
|
230
|
+
const text = `${prefix}\n${toolResultToText(message)}`;
|
|
231
|
+
entries.push(rootBlobCandidate(
|
|
232
|
+
{ role: "user", content: [{ type: "text", text }] },
|
|
233
|
+
"toolResult",
|
|
234
|
+
{ messageIndex: i, text },
|
|
235
|
+
));
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
let selected = entries;
|
|
240
|
+
let historyMessageStart = 0;
|
|
241
|
+
if (externalModel) {
|
|
242
|
+
const systemEntries = entries.slice(0, systemEntryCount);
|
|
243
|
+
const history = entries.slice(systemEntryCount);
|
|
244
|
+
const systemBytes = systemEntries.reduce((sum, entry) => sum + entry.byteLength, 0);
|
|
245
|
+
const historyLimit = Math.max(0, CURSOR_EXTERNAL_ROOT_BLOB_LIMIT - systemEntryCount);
|
|
246
|
+
const historyBudget = Math.max(0, CURSOR_EXTERNAL_ROOT_BYTE_LIMIT - systemBytes);
|
|
247
|
+
|
|
248
|
+
// Retain the active trailing tool-result block when it fits (may truncate text).
|
|
249
|
+
// If even a truncation marker cannot fit the remaining budget, omit it rather than
|
|
250
|
+
// emitting an oversized root blob.
|
|
251
|
+
let activeStart = history.length;
|
|
252
|
+
while (activeStart > 0 && history[activeStart - 1]?.role === "toolResult") activeStart -= 1;
|
|
253
|
+
const active = history
|
|
254
|
+
.slice(activeStart)
|
|
255
|
+
.map(entry => truncateToolResultBlob(entry, historyBudget))
|
|
256
|
+
.filter((entry): entry is RootBlobCandidate => entry !== null);
|
|
257
|
+
let activeBytes = active.reduce((sum, entry) => sum + entry.byteLength, 0);
|
|
258
|
+
while (active.length > 1 && activeBytes > historyBudget) {
|
|
259
|
+
const dropped = active.shift();
|
|
260
|
+
activeBytes -= dropped?.byteLength ?? 0;
|
|
261
|
+
}
|
|
262
|
+
if (active.length === 1 && active[0] && activeBytes > historyBudget) {
|
|
263
|
+
const truncated = truncateToolResultBlob(active[0], historyBudget);
|
|
264
|
+
if (truncated) {
|
|
265
|
+
active[0] = truncated;
|
|
266
|
+
activeBytes = truncated.byteLength;
|
|
267
|
+
} else {
|
|
268
|
+
active.length = 0;
|
|
269
|
+
activeBytes = 0;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const prior = history.slice(0, activeStart);
|
|
274
|
+
const keptPrior: RootBlobCandidate[] = [];
|
|
275
|
+
let priorBytes = 0;
|
|
276
|
+
// Take complete turns from the end: a turn starts at a user/developer root entry.
|
|
277
|
+
let i = prior.length - 1;
|
|
278
|
+
while (i >= 0 && keptPrior.length + active.length < historyLimit) {
|
|
279
|
+
let turnStart = i;
|
|
280
|
+
while (turnStart > 0 && prior[turnStart]?.role !== "user") turnStart -= 1;
|
|
281
|
+
const turn = prior.slice(turnStart, i + 1);
|
|
282
|
+
const turnBytes = turn.reduce((sum, entry) => sum + entry.byteLength, 0);
|
|
283
|
+
if (
|
|
284
|
+
keptPrior.length + active.length + turn.length > historyLimit
|
|
285
|
+
|| priorBytes + activeBytes + turnBytes > historyBudget
|
|
286
|
+
) {
|
|
287
|
+
break;
|
|
288
|
+
}
|
|
289
|
+
keptPrior.unshift(...turn);
|
|
290
|
+
priorBytes += turnBytes;
|
|
291
|
+
i = turnStart - 1;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const historyEntries = [...keptPrior, ...active];
|
|
295
|
+
// Guard against orphan assistant / toolResult at the start of the retained suffix.
|
|
296
|
+
while (historyEntries[0]?.role === "assistant" || historyEntries[0]?.role === "toolResult") {
|
|
297
|
+
// Never drop the sole active tool-result block.
|
|
298
|
+
if (historyEntries.length <= active.length) break;
|
|
299
|
+
historyEntries.shift();
|
|
300
|
+
}
|
|
301
|
+
selected = [...systemEntries, ...historyEntries];
|
|
302
|
+
const firstKept = historyEntries.find(entry => entry.messageIndex !== undefined);
|
|
303
|
+
historyMessageStart = firstKept?.messageIndex ?? (messages.length);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
return {
|
|
307
|
+
ids: selected.map(entry => storeCursorBlob(entry.data, requestScope)),
|
|
308
|
+
byteLength: selected.reduce((sum, entry) => sum + entry.byteLength, 0),
|
|
309
|
+
historyMessageStart,
|
|
310
|
+
serialized: selected.map(entry => entry.serialized),
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function contentText(message: OcxMessage): string {
|
|
315
|
+
if (message.role === "toolResult") return toolResultToText(message);
|
|
316
|
+
if (typeof message.content === "string") return message.content;
|
|
317
|
+
return message.content
|
|
318
|
+
.map(part => {
|
|
319
|
+
if (part.type === "text") return part.text;
|
|
320
|
+
if (part.type === "thinking") return part.thinking;
|
|
321
|
+
if (part.type === "image") return `[image input unsupported by Cursor adapter phase 3: ${part.detail ?? "auto"}]`;
|
|
322
|
+
return undefined;
|
|
323
|
+
})
|
|
324
|
+
.filter((value): value is string => typeof value === "string" && value.length > 0)
|
|
325
|
+
.join("\n");
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function contentToText(content: OcxToolResultMessage["content"]): string {
|
|
329
|
+
if (typeof content === "string") return content;
|
|
330
|
+
return content
|
|
331
|
+
.map(part => part.type === "text" ? part.text : `[image input unsupported by Cursor adapter phase 3: ${part.detail ?? "auto"}]`)
|
|
332
|
+
.join("\n");
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function toolResultToText(message: OcxToolResultMessage): string {
|
|
336
|
+
return [
|
|
337
|
+
"[tool_result]",
|
|
338
|
+
`call_id: ${message.toolCallId}`,
|
|
339
|
+
`name: ${namespacedToolName(message.toolNamespace, message.toolName)}`,
|
|
340
|
+
`is_error: ${message.isError}`,
|
|
341
|
+
"output:",
|
|
342
|
+
contentToText(message.content),
|
|
343
|
+
].join("\n");
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function argBytes(value: unknown): Uint8Array {
|
|
347
|
+
try {
|
|
348
|
+
return toBinary(ValueSchema, fromJson(ValueSchema, value as JsonValue));
|
|
349
|
+
} catch {
|
|
350
|
+
return encoder.encode(JSON.stringify(value));
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function toolCallStep(
|
|
355
|
+
part: Extract<OcxAssistantContentPart, { type: "toolCall" }>,
|
|
356
|
+
requestScope: CursorBlobRequestScopeToken,
|
|
357
|
+
result?: OcxToolResultMessage,
|
|
358
|
+
): Uint8Array {
|
|
359
|
+
const args: Record<string, Uint8Array> = {};
|
|
360
|
+
for (const [key, value] of Object.entries(part.arguments ?? {})) args[key] = argBytes(value);
|
|
361
|
+
const toolName = namespacedToolName(part.namespace, part.name);
|
|
362
|
+
return storeCursorBlob(toBinary(ConversationStepSchema, create(ConversationStepSchema, {
|
|
363
|
+
message: {
|
|
364
|
+
case: "toolCall",
|
|
365
|
+
value: create(ToolCallSchema, {
|
|
366
|
+
tool: {
|
|
367
|
+
case: "mcpToolCall",
|
|
368
|
+
value: create(McpToolCallSchema, {
|
|
369
|
+
args: create(McpArgsSchema, {
|
|
370
|
+
name: toolName,
|
|
371
|
+
toolName,
|
|
372
|
+
toolCallId: part.id,
|
|
373
|
+
providerIdentifier: OCX_RESPONSES_TOOL_PROVIDER,
|
|
374
|
+
args,
|
|
375
|
+
}),
|
|
376
|
+
...(result ? { result: toolResultPart(result) } : {}),
|
|
377
|
+
}),
|
|
378
|
+
},
|
|
379
|
+
}),
|
|
380
|
+
},
|
|
381
|
+
})), requestScope);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
function toolResultPart(message: OcxToolResultMessage) {
|
|
385
|
+
return create(McpToolResultSchema, {
|
|
386
|
+
result: {
|
|
387
|
+
case: "success",
|
|
388
|
+
value: create(McpSuccessSchema, {
|
|
389
|
+
isError: message.isError,
|
|
390
|
+
content: [create(McpToolResultContentItemSchema, {
|
|
391
|
+
content: { case: "text", value: create(McpTextContentSchema, { text: contentToText(message.content) }) },
|
|
392
|
+
})],
|
|
393
|
+
}),
|
|
394
|
+
},
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function assistantStep(part: OcxAssistantContentPart, requestScope: CursorBlobRequestScopeToken): Uint8Array | undefined {
|
|
399
|
+
if (part.type === "toolCall") return toolCallStep(part, requestScope);
|
|
400
|
+
if (part.type === "thinking") {
|
|
401
|
+
return storeCursorBlob(toBinary(ConversationStepSchema, create(ConversationStepSchema, {
|
|
402
|
+
message: {
|
|
403
|
+
case: "thinkingMessage",
|
|
404
|
+
value: create(ThinkingMessageSchema, { text: part.thinking }),
|
|
405
|
+
},
|
|
406
|
+
})), requestScope);
|
|
407
|
+
}
|
|
408
|
+
if (part.text.length === 0) return undefined;
|
|
409
|
+
return storeCursorBlob(toBinary(ConversationStepSchema, create(ConversationStepSchema, {
|
|
410
|
+
message: {
|
|
411
|
+
case: "assistantMessage",
|
|
412
|
+
value: create(AssistantMessageSchema, { text: part.text }),
|
|
413
|
+
},
|
|
414
|
+
})), requestScope);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function lastActionIndex(messages: readonly OcxMessage[] | undefined): number {
|
|
418
|
+
if (!messages) return -1;
|
|
419
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
420
|
+
const role = messages[i]?.role;
|
|
421
|
+
if (role === "user" || role === "developer") return i;
|
|
422
|
+
if (role === "toolResult") continue;
|
|
423
|
+
}
|
|
424
|
+
return -1;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
function conversationTurns(
|
|
428
|
+
request: CursorRunRequest,
|
|
429
|
+
requestScope: CursorBlobRequestScopeToken,
|
|
430
|
+
historyMessageStart = 0,
|
|
431
|
+
): Uint8Array[] {
|
|
432
|
+
const messages = request.rawMessages;
|
|
433
|
+
if (!messages?.length) return [];
|
|
434
|
+
const end = lastActionIndex(messages);
|
|
435
|
+
const externalModel = isCursorExternalWireModel(request.modelId);
|
|
436
|
+
const historyEnd = messages.at(-1)?.role === "toolResult" ? messages.length : Math.max(0, end);
|
|
437
|
+
const start = externalModel ? Math.max(0, historyMessageStart) : 0;
|
|
438
|
+
const turns: Uint8Array[] = [];
|
|
439
|
+
let current: { userMessage: Uint8Array; steps: Uint8Array[] } | undefined;
|
|
440
|
+
const pendingToolCalls = new Map<string, Extract<OcxAssistantContentPart, { type: "toolCall" }>>();
|
|
441
|
+
const flush = () => {
|
|
442
|
+
if (!current) return;
|
|
443
|
+
for (const part of pendingToolCalls.values()) current.steps.push(toolCallStep(part, requestScope));
|
|
444
|
+
turns.push(storeCursorBlob(toBinary(ConversationTurnStructureSchema, create(ConversationTurnStructureSchema, {
|
|
445
|
+
turn: {
|
|
446
|
+
case: "agentConversationTurn",
|
|
447
|
+
value: create(AgentConversationTurnStructureSchema, current),
|
|
448
|
+
},
|
|
449
|
+
})), requestScope));
|
|
450
|
+
current = undefined;
|
|
451
|
+
pendingToolCalls.clear();
|
|
452
|
+
};
|
|
453
|
+
|
|
454
|
+
for (const message of messages.slice(start, historyEnd)) {
|
|
455
|
+
if (message.role === "assistant") {
|
|
456
|
+
if (!current) continue;
|
|
457
|
+
for (const part of message.content) {
|
|
458
|
+
if (externalModel) {
|
|
459
|
+
// Working external-model clients replay only assistant text. Native mcpToolCall and
|
|
460
|
+
// ThinkingMessage structures are Composer state and cause external workers to hydrate
|
|
461
|
+
// the blobs, reach stepCompleted, then reject the turn with invalid_argument.
|
|
462
|
+
if (part.type === "text" && part.text.length > 0) {
|
|
463
|
+
current.steps.push(storeCursorBlob(toBinary(ConversationStepSchema, create(ConversationStepSchema, {
|
|
464
|
+
message: {
|
|
465
|
+
case: "assistantMessage",
|
|
466
|
+
value: create(AssistantMessageSchema, { text: part.text }),
|
|
467
|
+
},
|
|
468
|
+
})), requestScope));
|
|
469
|
+
}
|
|
470
|
+
continue;
|
|
471
|
+
}
|
|
472
|
+
if (part.type === "toolCall") {
|
|
473
|
+
pendingToolCalls.set(part.id, part);
|
|
474
|
+
continue;
|
|
475
|
+
}
|
|
476
|
+
const step = assistantStep(part, requestScope);
|
|
477
|
+
if (step) current.steps.push(step);
|
|
478
|
+
}
|
|
479
|
+
continue;
|
|
480
|
+
}
|
|
481
|
+
if (message.role === "toolResult") {
|
|
482
|
+
if (!current) continue;
|
|
483
|
+
if (externalModel) {
|
|
484
|
+
const prefix = message.isError ? "[Tool Error]" : "[Tool Result]";
|
|
485
|
+
current.steps.push(storeCursorBlob(toBinary(ConversationStepSchema, create(ConversationStepSchema, {
|
|
486
|
+
message: {
|
|
487
|
+
case: "assistantMessage",
|
|
488
|
+
value: create(AssistantMessageSchema, { text: `${prefix}\n${contentToText(message.content)}` }),
|
|
489
|
+
},
|
|
490
|
+
})), requestScope));
|
|
491
|
+
continue;
|
|
492
|
+
}
|
|
493
|
+
const priorCall = pendingToolCalls.get(message.toolCallId);
|
|
494
|
+
if (priorCall) {
|
|
495
|
+
current.steps.push(toolCallStep(priorCall, requestScope, message));
|
|
496
|
+
pendingToolCalls.delete(message.toolCallId);
|
|
497
|
+
} else {
|
|
498
|
+
current.steps.push(storeCursorBlob(toBinary(ConversationStepSchema, create(ConversationStepSchema, {
|
|
499
|
+
message: {
|
|
500
|
+
case: "assistantMessage",
|
|
501
|
+
value: create(AssistantMessageSchema, { text: toolResultToText(message) }),
|
|
502
|
+
},
|
|
503
|
+
})), requestScope));
|
|
504
|
+
}
|
|
505
|
+
continue;
|
|
506
|
+
}
|
|
507
|
+
flush();
|
|
508
|
+
current = {
|
|
509
|
+
userMessage: storeCursorBlob(toBinary(UserMessageSchema, create(UserMessageSchema, {
|
|
510
|
+
text: contentText(message),
|
|
511
|
+
messageId: crypto.randomUUID(),
|
|
512
|
+
})), requestScope),
|
|
513
|
+
steps: [],
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
flush();
|
|
517
|
+
return turns;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
export function activePromptText(request: CursorRunRequest): string {
|
|
521
|
+
const last = request.messages.at(-1);
|
|
522
|
+
if (last?.role === "user" || last?.role === "developer") return last.content;
|
|
523
|
+
for (let i = (request.rawMessages?.length ?? 0) - 1; i >= 0; i--) {
|
|
524
|
+
const message = request.rawMessages?.[i];
|
|
525
|
+
if (message?.role === "user" || message?.role === "developer") {
|
|
526
|
+
const text = contentText(message);
|
|
527
|
+
if (text.trim().length > 0) return text;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
return last?.role === "tool" ? last.content : "";
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* The model-visible text of one finalized tool definition. The schema travels as
|
|
535
|
+
* packed protobuf bytes, so it is decoded back to JSON to be counted the way the
|
|
536
|
+
* model reads it.
|
|
537
|
+
*/
|
|
538
|
+
function modelVisibleToolText(definition: McpToolDefinition): string {
|
|
539
|
+
let inputSchema: unknown;
|
|
540
|
+
try {
|
|
541
|
+
inputSchema = toJson(ValueSchema, fromBinary(ValueSchema, definition.inputSchema));
|
|
542
|
+
} catch {
|
|
543
|
+
inputSchema = undefined;
|
|
544
|
+
}
|
|
545
|
+
return JSON.stringify({
|
|
546
|
+
name: definition.toolName || definition.name,
|
|
547
|
+
description: definition.description,
|
|
548
|
+
...(inputSchema !== undefined ? { inputSchema } : {}),
|
|
549
|
+
});
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
export interface PreparedCursorRunRequest {
|
|
553
|
+
bytes: Uint8Array;
|
|
554
|
+
blobRequestScope: CursorBlobRequestScopeToken;
|
|
555
|
+
/** Only present when the caller asked for it; see prepareCursorRunRequest(). */
|
|
556
|
+
estimatedInputTokens?: number;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
/**
|
|
560
|
+
* Build the wire payload once, and optionally derive a token estimate from the very
|
|
561
|
+
* same roots, action text, and tool definitions that produced it.
|
|
562
|
+
*
|
|
563
|
+
* Cursor only reports absolute context size in checkpoint frames, which live in a
|
|
564
|
+
* process-local map — so after a restart a turn with no checkpoint reports
|
|
565
|
+
* inputTokens=0 and Codex sees an almost-empty context (#373). The estimate fills
|
|
566
|
+
* that gap. Deriving it here, rather than from the original request, is what keeps
|
|
567
|
+
* it honest: history the pruner dropped and tools the filter removed are already
|
|
568
|
+
* gone by this point.
|
|
569
|
+
*/
|
|
570
|
+
function buildPreparedCursorRunRequest(
|
|
571
|
+
request: CursorRunRequest,
|
|
572
|
+
requestScope: CursorBlobRequestScopeToken,
|
|
573
|
+
options?: { estimateInputTokens?: boolean },
|
|
574
|
+
): PreparedCursorRunRequest {
|
|
575
|
+
const rawText = activePromptText(request);
|
|
576
|
+
const lastRole = request.messages.at(-1)?.role;
|
|
577
|
+
const text = lastRole === "user" || lastRole === "developer"
|
|
578
|
+
? appendCursorGenericToolUseHint(request.tools, rawText)
|
|
579
|
+
: rawText;
|
|
580
|
+
// Tool-result-only turns resume the remembered Cursor conversation with results in history.
|
|
581
|
+
const lastRawIsToolResult = request.rawMessages?.at(-1)?.role === "toolResult";
|
|
582
|
+
const actionCase = !lastRawIsToolResult && text.trim().length > 0
|
|
583
|
+
? "userMessageAction"
|
|
584
|
+
: "resumeAction";
|
|
585
|
+
const action = create(ConversationActionSchema, {
|
|
586
|
+
action: actionCase === "userMessageAction"
|
|
587
|
+
? {
|
|
588
|
+
case: "userMessageAction",
|
|
589
|
+
value: create(UserMessageActionSchema, {
|
|
590
|
+
userMessage: create(UserMessageSchema, {
|
|
591
|
+
text,
|
|
592
|
+
messageId: crypto.randomUUID(),
|
|
593
|
+
}),
|
|
594
|
+
requestContext: buildRequestContext(),
|
|
595
|
+
}),
|
|
596
|
+
}
|
|
597
|
+
: {
|
|
598
|
+
case: "resumeAction",
|
|
599
|
+
value: create(ResumeActionSchema, {
|
|
600
|
+
requestContext: buildRequestContext(),
|
|
601
|
+
}),
|
|
602
|
+
},
|
|
603
|
+
});
|
|
604
|
+
const rootPromptMessagesState = rootPromptMessages(request, requestScope);
|
|
605
|
+
const rootPromptMessageIds = rootPromptMessagesState.ids;
|
|
606
|
+
const turnIds = conversationTurns(request, requestScope, rootPromptMessagesState.historyMessageStart);
|
|
607
|
+
// Hoisted out of the mcp_tools spread below so the estimate can read the same
|
|
608
|
+
// filtered definitions the wire carries. Both helpers are pure.
|
|
609
|
+
const visibleTools = cursorToolsForActivePrompt(request.tools, rawText, request.toolChoice);
|
|
610
|
+
const mcpToolDefs = buildCursorToolDefinitions(visibleTools, request.toolChoice);
|
|
611
|
+
debugProviderDiagnostic("cursor", "run-request", {
|
|
612
|
+
wireModel: request.modelId,
|
|
613
|
+
action: actionCase,
|
|
614
|
+
conversationId: request.conversationId,
|
|
615
|
+
turnType: lastRawIsToolResult ? "tool-continuation" : "initial",
|
|
616
|
+
externalModel: isCursorExternalWireModel(request.modelId),
|
|
617
|
+
rawMessages: request.rawMessages?.length ?? 0,
|
|
618
|
+
rootBlobs: rootPromptMessageIds.length,
|
|
619
|
+
rootBytes: rootPromptMessagesState.byteLength,
|
|
620
|
+
turnBlobs: turnIds.length,
|
|
621
|
+
tools: request.tools?.length ?? 0,
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
const runRequest = create(AgentRunRequestSchema, {
|
|
625
|
+
conversationId: request.conversationId,
|
|
626
|
+
conversationState: create(ConversationStateStructureSchema, {
|
|
627
|
+
rootPromptMessagesJson: rootPromptMessageIds,
|
|
628
|
+
turns: turnIds,
|
|
629
|
+
todos: [],
|
|
630
|
+
pendingToolCalls: [],
|
|
631
|
+
previousWorkspaceUris: [],
|
|
632
|
+
fileStates: {},
|
|
633
|
+
fileStatesV2: {},
|
|
634
|
+
summaryArchives: [],
|
|
635
|
+
turnTimings: [],
|
|
636
|
+
subagentStates: {},
|
|
637
|
+
readPaths: [],
|
|
638
|
+
}),
|
|
639
|
+
action,
|
|
640
|
+
modelDetails: create(ModelDetailsSchema, {
|
|
641
|
+
modelId: request.modelId,
|
|
642
|
+
displayModelId: request.modelId,
|
|
643
|
+
displayName: request.modelId,
|
|
644
|
+
displayNameShort: request.modelId,
|
|
645
|
+
aliases: [],
|
|
646
|
+
}),
|
|
647
|
+
// requested_model is currently a Cursor Router-only surface. External model clients still
|
|
648
|
+
// send model_details alone; sending both makes external workers reach stepCompleted and then
|
|
649
|
+
// reject the turn with invalid_argument.
|
|
650
|
+
...(request.routingLevel ? {
|
|
651
|
+
requestedModel: create(RequestedModelSchema, {
|
|
652
|
+
modelId: request.modelId,
|
|
653
|
+
maxMode: false,
|
|
654
|
+
parameters: [create(RequestedModel_ModelParameterbytesSchema, {
|
|
655
|
+
id: CURSOR_ROUTING_LEVEL_PARAMETER_ID,
|
|
656
|
+
value: request.routingLevel,
|
|
657
|
+
})],
|
|
658
|
+
}),
|
|
659
|
+
} : {}),
|
|
660
|
+
// Mirror the client (Responses) tool definitions into the top-level AgentRunRequest.mcp_tools
|
|
661
|
+
// channel. Advertising them ONLY via native-exec `requestContextArgs` (RequestContext.tools) is
|
|
662
|
+
// insufficient: cursor models report those tools as unavailable and fall back to native tools.
|
|
663
|
+
// Populating mcp_tools registers them into the model's callable catalog (verified live: the
|
|
664
|
+
// model actually calls the injected tool on gpt-5.6-luna and claude-4.5-sonnet). Phase 42 tried
|
|
665
|
+
// this but assigned the field with the wrong shape and crashed Cursor's binary parser ("illegal
|
|
666
|
+
// tag"); the correct `McpTools` wrapper is wire-compatible (verified — no parse crash on either
|
|
667
|
+
// model family). See devlog/260711_cursor_browser_bridge/004.
|
|
668
|
+
//
|
|
669
|
+
// Use the SAME `cursorToolsForActivePrompt`-filtered visible set that RequestContext.tools and
|
|
670
|
+
// the event-state `clientToolNames` use (live-transport.ts). Advertising the raw `request.tools`
|
|
671
|
+
// here would let mcp_tools expose a tool that the event state does not recognize for a generic
|
|
672
|
+
// tool-count prompt, so a call to it would be rejected as an unknown Responses tool.
|
|
673
|
+
...(mcpToolDefs.length > 0 ? { mcpTools: create(McpToolsSchema, { mcpTools: mcpToolDefs }) } : {}),
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
const message = create(AgentClientMessageSchema, {
|
|
677
|
+
message: { case: "runRequest", value: runRequest },
|
|
678
|
+
});
|
|
679
|
+
const bytes = toBinary(AgentClientMessageSchema, message);
|
|
680
|
+
if (!options?.estimateInputTokens) return { bytes, blobRequestScope: requestScope };
|
|
681
|
+
|
|
682
|
+
// Same instances that produced `bytes`, so the estimate cannot count history or
|
|
683
|
+
// tools the payload dropped — the defect that blocked PR #376.
|
|
684
|
+
const modelVisibleParts = [
|
|
685
|
+
...rootPromptMessagesState.serialized,
|
|
686
|
+
...(actionCase === "userMessageAction" ? [text] : []),
|
|
687
|
+
...mcpToolDefs.map(modelVisibleToolText),
|
|
688
|
+
];
|
|
689
|
+
return {
|
|
690
|
+
bytes,
|
|
691
|
+
blobRequestScope: requestScope,
|
|
692
|
+
estimatedInputTokens: estimateTokens(modelVisibleParts.join("\n"), request.modelId),
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
export function prepareCursorRunRequest(
|
|
697
|
+
request: CursorRunRequest,
|
|
698
|
+
options?: { estimateInputTokens?: boolean },
|
|
699
|
+
): PreparedCursorRunRequest {
|
|
700
|
+
const requestScope = createCursorBlobRequestScope();
|
|
701
|
+
try {
|
|
702
|
+
const prepared = buildPreparedCursorRunRequest(request, requestScope, options);
|
|
703
|
+
sealCursorBlobRequestScope(requestScope);
|
|
704
|
+
return prepared;
|
|
705
|
+
} catch (error) {
|
|
706
|
+
releaseCursorBlobRequestScope(requestScope);
|
|
707
|
+
throw error;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
/** Back-compat wrapper: callers that only need the wire bytes. */
|
|
712
|
+
export function encodeCursorRunRequest(request: CursorRunRequest): Uint8Array {
|
|
713
|
+
return prepareCursorRunRequest(request).bytes;
|
|
714
|
+
}
|