@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,192 @@
|
|
|
1
|
+
export class CodexWarmupError extends Error {
|
|
2
|
+
code: "http_status" | "missing_body" | "stream_failed" | "stream_incomplete" | "stream_error" | "invalid_sse" | "no_terminal" | "transport";
|
|
3
|
+
status?: number;
|
|
4
|
+
/** Upstream error detail extracted from the response body (truncated to 512 chars). */
|
|
5
|
+
upstreamDetail?: string;
|
|
6
|
+
|
|
7
|
+
constructor(
|
|
8
|
+
code: CodexWarmupError["code"],
|
|
9
|
+
message = "Codex warmup failed",
|
|
10
|
+
options: { status?: number; cause?: unknown; upstreamDetail?: string } = {},
|
|
11
|
+
) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.name = "CodexWarmupError";
|
|
14
|
+
this.code = code;
|
|
15
|
+
this.status = options.status;
|
|
16
|
+
this.upstreamDetail = options.upstreamDetail;
|
|
17
|
+
if (options.cause !== undefined) this.cause = options.cause;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface CodexWarmupOptions {
|
|
22
|
+
accessToken: string;
|
|
23
|
+
chatgptAccountId: string;
|
|
24
|
+
model?: string;
|
|
25
|
+
timeoutMs?: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const CODEX_RESPONSES_URL = "https://chatgpt.com/backend-api/codex/responses";
|
|
29
|
+
const DEFAULT_MODEL = "gpt-5.4-mini";
|
|
30
|
+
const FALLBACK_MODELS = ["gpt-5.5"];
|
|
31
|
+
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
32
|
+
const MAX_ERROR_BODY_BYTES = 2048;
|
|
33
|
+
|
|
34
|
+
/** Read the first MAX_ERROR_BODY_BYTES of a response body and extract an error message. */
|
|
35
|
+
async function readErrorDetail(res: Response): Promise<string | undefined> {
|
|
36
|
+
try {
|
|
37
|
+
const text = await res.text();
|
|
38
|
+
const trimmed = text.slice(0, MAX_ERROR_BODY_BYTES);
|
|
39
|
+
try {
|
|
40
|
+
const json = JSON.parse(trimmed) as Record<string, unknown>;
|
|
41
|
+
// ChatGPT backend error shape: { error: { message: "..." } } or { detail: "..." }
|
|
42
|
+
const nested = json.error;
|
|
43
|
+
if (nested && typeof nested === "object" && typeof (nested as Record<string, unknown>).message === "string") {
|
|
44
|
+
return ((nested as Record<string, unknown>).message as string).slice(0, 512);
|
|
45
|
+
}
|
|
46
|
+
if (typeof json.detail === "string") return json.detail.slice(0, 512);
|
|
47
|
+
if (typeof json.error === "string") return (json.error as string).slice(0, 512);
|
|
48
|
+
if (typeof json.message === "string") return json.message.slice(0, 512);
|
|
49
|
+
} catch {
|
|
50
|
+
// Non-JSON response body may contain sensitive data (tokens, credentials).
|
|
51
|
+
// Only surface structured error messages, never raw text.
|
|
52
|
+
}
|
|
53
|
+
return undefined;
|
|
54
|
+
} catch {
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function safeWarmupReason(err: unknown): string {
|
|
60
|
+
if (err instanceof CodexWarmupError) {
|
|
61
|
+
const base = err.status ? `${err.code}:${err.status}` : err.code;
|
|
62
|
+
return err.upstreamDetail ? `${base} — ${err.upstreamDetail}` : base;
|
|
63
|
+
}
|
|
64
|
+
return "transport";
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function codexWarmupFailureReason(err: unknown): string {
|
|
68
|
+
return safeWarmupReason(err);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function eventTypeFromData(data: unknown): string | undefined {
|
|
72
|
+
if (!data || typeof data !== "object") return undefined;
|
|
73
|
+
const record = data as Record<string, unknown>;
|
|
74
|
+
return typeof record.type === "string" ? record.type : undefined;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function parseSseFrame(frame: string): unknown | null {
|
|
78
|
+
const dataLines = frame
|
|
79
|
+
.split(/\r?\n/)
|
|
80
|
+
.filter(line => line.startsWith("data:"))
|
|
81
|
+
.map(line => line.slice(5).trimStart());
|
|
82
|
+
if (dataLines.length === 0) return null;
|
|
83
|
+
const data = dataLines.join("\n").trim();
|
|
84
|
+
if (!data || data === "[DONE]") return null;
|
|
85
|
+
try {
|
|
86
|
+
return JSON.parse(data) as unknown;
|
|
87
|
+
} catch (err) {
|
|
88
|
+
throw new CodexWarmupError("invalid_sse", "Codex warmup received invalid SSE", { cause: err });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function drainWarmupSse(body: ReadableStream<Uint8Array>): Promise<void> {
|
|
93
|
+
const reader = body.getReader();
|
|
94
|
+
const decoder = new TextDecoder();
|
|
95
|
+
let buffer = "";
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
for (;;) {
|
|
99
|
+
const { done, value } = await reader.read();
|
|
100
|
+
if (done) break;
|
|
101
|
+
buffer += decoder.decode(value, { stream: true });
|
|
102
|
+
|
|
103
|
+
for (;;) {
|
|
104
|
+
const frameEnd = buffer.search(/\r?\n\r?\n/);
|
|
105
|
+
if (frameEnd < 0) break;
|
|
106
|
+
const frame = buffer.slice(0, frameEnd);
|
|
107
|
+
const delimiterLength = buffer[frameEnd] === "\r" ? 4 : 2;
|
|
108
|
+
buffer = buffer.slice(frameEnd + delimiterLength);
|
|
109
|
+
const parsed = parseSseFrame(frame);
|
|
110
|
+
const type = eventTypeFromData(parsed);
|
|
111
|
+
if (type === "response.completed") return;
|
|
112
|
+
if (type === "response.failed") throw new CodexWarmupError("stream_failed");
|
|
113
|
+
if (type === "response.incomplete") throw new CodexWarmupError("stream_incomplete");
|
|
114
|
+
if (type === "error") throw new CodexWarmupError("stream_error");
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (buffer.trim()) {
|
|
119
|
+
const parsed = parseSseFrame(buffer);
|
|
120
|
+
const type = eventTypeFromData(parsed);
|
|
121
|
+
if (type === "response.completed") return;
|
|
122
|
+
if (type === "response.failed") throw new CodexWarmupError("stream_failed");
|
|
123
|
+
if (type === "response.incomplete") throw new CodexWarmupError("stream_incomplete");
|
|
124
|
+
if (type === "error") throw new CodexWarmupError("stream_error");
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
throw new CodexWarmupError("no_terminal", "Codex warmup ended before completion");
|
|
128
|
+
} finally {
|
|
129
|
+
reader.releaseLock();
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async function tryWarmup(options: CodexWarmupOptions, model: string): Promise<void> {
|
|
134
|
+
let res: Response;
|
|
135
|
+
try {
|
|
136
|
+
res = await fetch(CODEX_RESPONSES_URL, {
|
|
137
|
+
method: "POST",
|
|
138
|
+
headers: {
|
|
139
|
+
Authorization: `Bearer ${options.accessToken}`,
|
|
140
|
+
"ChatGPT-Account-Id": options.chatgptAccountId,
|
|
141
|
+
"Content-Type": "application/json",
|
|
142
|
+
},
|
|
143
|
+
body: JSON.stringify({
|
|
144
|
+
model,
|
|
145
|
+
instructions: "Reply with OK.",
|
|
146
|
+
input: [{ type: "message", role: "user", content: [{ type: "input_text", text: "hi" }] }],
|
|
147
|
+
stream: true,
|
|
148
|
+
store: false,
|
|
149
|
+
}),
|
|
150
|
+
signal: AbortSignal.timeout(options.timeoutMs ?? DEFAULT_TIMEOUT_MS),
|
|
151
|
+
});
|
|
152
|
+
} catch (err) {
|
|
153
|
+
throw new CodexWarmupError("transport", "Codex warmup request failed", { cause: err });
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (!res.ok) {
|
|
157
|
+
const upstreamDetail = await readErrorDetail(res);
|
|
158
|
+
throw new CodexWarmupError("http_status", "Codex warmup was rejected", {
|
|
159
|
+
status: res.status,
|
|
160
|
+
upstreamDetail,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
if (!res.body) throw new CodexWarmupError("missing_body");
|
|
164
|
+
|
|
165
|
+
try {
|
|
166
|
+
await drainWarmupSse(res.body);
|
|
167
|
+
} finally {
|
|
168
|
+
await res.body?.cancel().catch(() => {});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export async function warmCodexAccount(options: CodexWarmupOptions): Promise<void> {
|
|
173
|
+
const primaryModel = options.model?.trim() || DEFAULT_MODEL;
|
|
174
|
+
try {
|
|
175
|
+
await tryWarmup(options, primaryModel);
|
|
176
|
+
return;
|
|
177
|
+
} catch (err) {
|
|
178
|
+
// Retry with fallback models on 400 (model may not be available for this account).
|
|
179
|
+
if (!(err instanceof CodexWarmupError) || err.status !== 400) throw err;
|
|
180
|
+
let lastErr = err;
|
|
181
|
+
for (const fallback of FALLBACK_MODELS) {
|
|
182
|
+
if (fallback === primaryModel) continue;
|
|
183
|
+
try {
|
|
184
|
+
await tryWarmup(options, fallback);
|
|
185
|
+
return;
|
|
186
|
+
} catch (retryErr) {
|
|
187
|
+
if (retryErr instanceof CodexWarmupError) lastErr = retryErr;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
throw lastErr;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { ServerWebSocket } from "bun";
|
|
2
|
+
import type { WsData } from "../server/ws-bridge";
|
|
3
|
+
import { createAdmissionGate, type AdmissionMetrics, type AdmissionReservation } from "../lib/admission";
|
|
4
|
+
|
|
5
|
+
export const MAX_TRACKED_CODEX_WEBSOCKETS = 128;
|
|
6
|
+
const websocketGate = createAdmissionGate("codex_websockets", MAX_TRACKED_CODEX_WEBSOCKETS);
|
|
7
|
+
|
|
8
|
+
export function tryReserveCodexWebSocket(): AdmissionReservation<ServerWebSocket<WsData>> | null {
|
|
9
|
+
const gateLease = websocketGate.tryAcquire();
|
|
10
|
+
if (!gateLease) return null;
|
|
11
|
+
let socket: ServerWebSocket<WsData> | undefined;
|
|
12
|
+
let active = true;
|
|
13
|
+
return {
|
|
14
|
+
bind(value) {
|
|
15
|
+
if (!active) return;
|
|
16
|
+
socket = value;
|
|
17
|
+
},
|
|
18
|
+
release() {
|
|
19
|
+
if (!active) return;
|
|
20
|
+
active = false;
|
|
21
|
+
socket = undefined;
|
|
22
|
+
gateLease.release();
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function codexWebSocketAdmissionMetrics(): AdmissionMetrics {
|
|
28
|
+
return websocketGate.metrics();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const socketsByAccount = new Map<string, Set<ServerWebSocket<WsData>>>();
|
|
32
|
+
|
|
33
|
+
function trackedAccountId(ws: ServerWebSocket<WsData>): string | null {
|
|
34
|
+
const ctx = ws.data.authContext;
|
|
35
|
+
return ctx?.kind === "pool" || ctx?.kind === "main-pool" ? ctx.accountId : null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function addSocketForAccount(accountId: string | null, ws: ServerWebSocket<WsData>): void {
|
|
39
|
+
if (!accountId) return;
|
|
40
|
+
let sockets = socketsByAccount.get(accountId);
|
|
41
|
+
if (!sockets) {
|
|
42
|
+
sockets = new Set();
|
|
43
|
+
socketsByAccount.set(accountId, sockets);
|
|
44
|
+
}
|
|
45
|
+
sockets.add(ws);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function removeSocketForAccount(accountId: string | null, ws: ServerWebSocket<WsData>): void {
|
|
49
|
+
if (!accountId) return;
|
|
50
|
+
const sockets = socketsByAccount.get(accountId);
|
|
51
|
+
if (!sockets) return;
|
|
52
|
+
sockets.delete(ws);
|
|
53
|
+
if (sockets.size === 0) socketsByAccount.delete(accountId);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function registerCodexWebSocket(ws: ServerWebSocket<WsData>): void {
|
|
57
|
+
addSocketForAccount(trackedAccountId(ws), ws);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function unregisterCodexWebSocket(ws: ServerWebSocket<WsData>): void {
|
|
61
|
+
removeSocketForAccount(trackedAccountId(ws), ws);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function updateCodexWebSocketAuthContext(
|
|
65
|
+
ws: ServerWebSocket<WsData>,
|
|
66
|
+
authContext: WsData["authContext"],
|
|
67
|
+
): void {
|
|
68
|
+
const before = trackedAccountId(ws);
|
|
69
|
+
removeSocketForAccount(before, ws);
|
|
70
|
+
ws.data.authContext = authContext;
|
|
71
|
+
addSocketForAccount(trackedAccountId(ws), ws);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function invalidateCodexWebSocketsForAccount(accountId: string): number {
|
|
75
|
+
const sockets = socketsByAccount.get(accountId);
|
|
76
|
+
if (!sockets) return 0;
|
|
77
|
+
const snapshot = [...sockets];
|
|
78
|
+
socketsByAccount.delete(accountId);
|
|
79
|
+
for (const ws of snapshot) {
|
|
80
|
+
try {
|
|
81
|
+
ws.data.cancel?.();
|
|
82
|
+
} catch {
|
|
83
|
+
/* ignore cancel callbacks during invalidation */
|
|
84
|
+
}
|
|
85
|
+
try {
|
|
86
|
+
ws.close(4001, "Codex account invalidated");
|
|
87
|
+
} catch {
|
|
88
|
+
/* socket may already be closing */
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return snapshot.length;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function getTrackedCodexWebSocketCountForAccount(accountId: string): number {
|
|
95
|
+
return socketsByAccount.get(accountId)?.size ?? 0;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function clearCodexWebSocketRegistry(): void {
|
|
99
|
+
socketsByAccount.clear();
|
|
100
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { classifyError, isCyberPolicyCode } from "../lib/errors";
|
|
2
|
+
import type { OcxComboTarget } from "../types";
|
|
3
|
+
import { targetKey } from "./types";
|
|
4
|
+
import {
|
|
5
|
+
captureConfigGeneration,
|
|
6
|
+
sweepExpiredOnWrite,
|
|
7
|
+
type GenerationContext,
|
|
8
|
+
} from "../lib/state-store-sweeper";
|
|
9
|
+
|
|
10
|
+
interface TargetCooldown {
|
|
11
|
+
cooldownUntil: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const DEFAULT_COOLDOWN_MS = 60_000;
|
|
15
|
+
const MAX_COOLDOWN_MS = 10 * 60_000;
|
|
16
|
+
|
|
17
|
+
/** Map<`${comboId}\0${provider/model}`, TargetCooldown> */
|
|
18
|
+
const targetCooldowns = new Map<string, TargetCooldown>();
|
|
19
|
+
let lastReconciledGeneration = 0;
|
|
20
|
+
let liveComboTargets = new Set<string>();
|
|
21
|
+
|
|
22
|
+
function cooldownMapKey(
|
|
23
|
+
comboId: string,
|
|
24
|
+
target: Pick<OcxComboTarget, "provider" | "model">,
|
|
25
|
+
): string {
|
|
26
|
+
return `${comboId}\0${targetKey(target)}`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function parseRetryAfterMs(
|
|
30
|
+
value: string | null | undefined,
|
|
31
|
+
now = Date.now(),
|
|
32
|
+
): number | undefined {
|
|
33
|
+
const text = value?.trim();
|
|
34
|
+
if (!text) return undefined;
|
|
35
|
+
if (/^\d+(?:\.\d+)?$/.test(text)) {
|
|
36
|
+
const seconds = Number(text);
|
|
37
|
+
if (Number.isFinite(seconds) && seconds > 0) {
|
|
38
|
+
return Math.min(Math.max(Math.ceil(seconds * 1000), 1), MAX_COOLDOWN_MS);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const timestamp = Date.parse(text);
|
|
42
|
+
if (!Number.isFinite(timestamp)) return undefined;
|
|
43
|
+
const delay = timestamp - now;
|
|
44
|
+
return delay > 0 ? Math.min(delay, MAX_COOLDOWN_MS) : undefined;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function isComboTargetInCooldown(
|
|
48
|
+
comboId: string,
|
|
49
|
+
target: Pick<OcxComboTarget, "provider" | "model">,
|
|
50
|
+
now = Date.now(),
|
|
51
|
+
): boolean {
|
|
52
|
+
const key = cooldownMapKey(comboId, target);
|
|
53
|
+
const entry = targetCooldowns.get(key);
|
|
54
|
+
if (!entry) return false;
|
|
55
|
+
if (entry.cooldownUntil <= now) {
|
|
56
|
+
targetCooldowns.delete(key);
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function coolComboTarget(
|
|
63
|
+
comboId: string,
|
|
64
|
+
target: Pick<OcxComboTarget, "provider" | "model">,
|
|
65
|
+
options?: { retryAfter?: string | null; now?: number; cooldownMs?: number; writerGeneration?: number },
|
|
66
|
+
): void {
|
|
67
|
+
const now = options?.now ?? Date.now();
|
|
68
|
+
const writerGeneration = options?.writerGeneration ?? captureConfigGeneration();
|
|
69
|
+
const ownerKey = `${comboId}::${targetKey(target)}`;
|
|
70
|
+
if (writerGeneration < lastReconciledGeneration && !liveComboTargets.has(ownerKey)) return;
|
|
71
|
+
const cooldownMs = options?.cooldownMs
|
|
72
|
+
?? parseRetryAfterMs(options?.retryAfter, now)
|
|
73
|
+
?? DEFAULT_COOLDOWN_MS;
|
|
74
|
+
targetCooldowns.set(cooldownMapKey(comboId, target), {
|
|
75
|
+
cooldownUntil: now + Math.min(Math.max(cooldownMs, 1), MAX_COOLDOWN_MS),
|
|
76
|
+
});
|
|
77
|
+
sweepExpiredOnWrite(now);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function reconcileComboTargetCooldowns(context: GenerationContext): number {
|
|
81
|
+
if (context.generation <= lastReconciledGeneration) return 0;
|
|
82
|
+
liveComboTargets = new Set(context.comboTargets);
|
|
83
|
+
lastReconciledGeneration = context.generation;
|
|
84
|
+
return 0;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function sweepExpiredComboTargetCooldowns(now = Date.now()): number {
|
|
88
|
+
let removed = 0;
|
|
89
|
+
for (const [key, cooldown] of targetCooldowns) {
|
|
90
|
+
if (cooldown.cooldownUntil > now) continue;
|
|
91
|
+
targetCooldowns.delete(key);
|
|
92
|
+
removed += 1;
|
|
93
|
+
}
|
|
94
|
+
return removed;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function clearComboTargetCooldowns(comboId?: string): void {
|
|
98
|
+
if (comboId === undefined) {
|
|
99
|
+
targetCooldowns.clear();
|
|
100
|
+
liveComboTargets.clear();
|
|
101
|
+
lastReconciledGeneration = 0;
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const prefix = `${comboId}\0`;
|
|
105
|
+
for (const key of targetCooldowns.keys()) {
|
|
106
|
+
if (key.startsWith(prefix)) targetCooldowns.delete(key);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export type ComboFailureDecision = "hop" | "stop";
|
|
111
|
+
|
|
112
|
+
export function comboFailureDecision(
|
|
113
|
+
status: number,
|
|
114
|
+
message: string,
|
|
115
|
+
options?: { code?: string | null },
|
|
116
|
+
): ComboFailureDecision {
|
|
117
|
+
if (status === 499) return "stop";
|
|
118
|
+
if (message.toLowerCase().includes("origin_rejected")) return "stop";
|
|
119
|
+
// Cyber policy is a hard non-retryable refusal — honor structured code even when
|
|
120
|
+
// classificationText was truncated before the JSON code field.
|
|
121
|
+
if (isCyberPolicyCode(options?.code)) return "stop";
|
|
122
|
+
const error = classifyError(status, "upstream_error", message);
|
|
123
|
+
if (isCyberPolicyCode(error.code)) return "stop";
|
|
124
|
+
if (["origin_rejected", "context_length_exceeded", "invalid_request_error"].includes(error.code ?? "")) {
|
|
125
|
+
return "stop";
|
|
126
|
+
}
|
|
127
|
+
if ([401, 403, 404, 408, 429].includes(status) || status >= 500) return "hop";
|
|
128
|
+
if ([
|
|
129
|
+
"permission_denied",
|
|
130
|
+
"subscription_required",
|
|
131
|
+
"invalid_api_key",
|
|
132
|
+
"insufficient_quota",
|
|
133
|
+
"rate_limit_exceeded",
|
|
134
|
+
"server_is_overloaded",
|
|
135
|
+
"upstream_server_error",
|
|
136
|
+
].includes(error.code ?? "")) {
|
|
137
|
+
return "hop";
|
|
138
|
+
}
|
|
139
|
+
return "stop";
|
|
140
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export {
|
|
2
|
+
COMBO_NAMESPACE,
|
|
3
|
+
comboAliasIssues,
|
|
4
|
+
comboConfigError,
|
|
5
|
+
comboConfigIssues,
|
|
6
|
+
comboDefaultEffort,
|
|
7
|
+
comboModelId,
|
|
8
|
+
comboPublicModelId,
|
|
9
|
+
getCombo,
|
|
10
|
+
isValidComboId,
|
|
11
|
+
listComboIds,
|
|
12
|
+
normalizeComboConfig,
|
|
13
|
+
parseComboModelId,
|
|
14
|
+
preservesPhysicalComboProvider,
|
|
15
|
+
resolveComboId,
|
|
16
|
+
targetKey,
|
|
17
|
+
} from "./types";
|
|
18
|
+
export {
|
|
19
|
+
advanceComboAfterFailure,
|
|
20
|
+
clearComboSelectionState,
|
|
21
|
+
NoAvailableComboTargetsError,
|
|
22
|
+
noteComboFailure,
|
|
23
|
+
noteComboSuccess,
|
|
24
|
+
pickComboTarget,
|
|
25
|
+
tryPickComboModel,
|
|
26
|
+
UnknownComboError,
|
|
27
|
+
type ComboPick,
|
|
28
|
+
} from "./resolve";
|
|
29
|
+
export {
|
|
30
|
+
clearComboTargetCooldowns,
|
|
31
|
+
coolComboTarget,
|
|
32
|
+
isComboTargetInCooldown,
|
|
33
|
+
parseRetryAfterMs,
|
|
34
|
+
comboFailureDecision,
|
|
35
|
+
type ComboFailureDecision,
|
|
36
|
+
} from "./failover";
|
|
37
|
+
export {
|
|
38
|
+
comboIdFromRawBody,
|
|
39
|
+
concreteComboRequestBody,
|
|
40
|
+
resetComboEffortWarningStateForTests,
|
|
41
|
+
} from "./request";
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { OcxComboDefaultEffort, OcxComboTarget, OcxConfig } from "../types";
|
|
2
|
+
import { resolveComboId } from "./types";
|
|
3
|
+
|
|
4
|
+
const warnedUnsupportedDefaults = new Set<string>();
|
|
5
|
+
let lastWarningReconciledGeneration = 0;
|
|
6
|
+
|
|
7
|
+
export function reconcileComboWarningMemos(generation: number): number {
|
|
8
|
+
if (generation <= lastWarningReconciledGeneration) return 0;
|
|
9
|
+
const removed = warnedUnsupportedDefaults.size;
|
|
10
|
+
warnedUnsupportedDefaults.clear();
|
|
11
|
+
lastWarningReconciledGeneration = generation;
|
|
12
|
+
return removed;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function resetComboEffortWarningStateForTests(): void {
|
|
16
|
+
warnedUnsupportedDefaults.clear();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function comboIdFromRawBody(body: unknown, config: OcxConfig): string | null {
|
|
20
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) return null;
|
|
21
|
+
const model = (body as { model?: unknown }).model;
|
|
22
|
+
if (typeof model !== "string") return null;
|
|
23
|
+
return resolveComboId(config, model);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function concreteComboRequestBody(
|
|
27
|
+
body: unknown,
|
|
28
|
+
target: Pick<OcxComboTarget, "provider" | "model">,
|
|
29
|
+
defaultEffort: OcxComboDefaultEffort | null,
|
|
30
|
+
targetReasoningEfforts: readonly string[] | undefined,
|
|
31
|
+
): Record<string, unknown> {
|
|
32
|
+
const clone = structuredClone(body) as Record<string, unknown>;
|
|
33
|
+
clone.model = `${target.provider}/${target.model}`;
|
|
34
|
+
if (!defaultEffort) return clone;
|
|
35
|
+
const reasoning = clone.reasoning;
|
|
36
|
+
const needsDefault = reasoning === undefined || (
|
|
37
|
+
reasoning
|
|
38
|
+
&& typeof reasoning === "object"
|
|
39
|
+
&& !Array.isArray(reasoning)
|
|
40
|
+
&& !Object.prototype.hasOwnProperty.call(reasoning, "effort")
|
|
41
|
+
);
|
|
42
|
+
if (!needsDefault) return clone;
|
|
43
|
+
if (!targetReasoningEfforts?.includes(defaultEffort)) {
|
|
44
|
+
const key = `${target.provider}/${target.model}:${defaultEffort}`;
|
|
45
|
+
if (!warnedUnsupportedDefaults.has(key)) {
|
|
46
|
+
warnedUnsupportedDefaults.add(key);
|
|
47
|
+
console.debug("[opencodex] combo default effort omitted", {
|
|
48
|
+
provider: target.provider,
|
|
49
|
+
model: target.model,
|
|
50
|
+
requestedEffort: defaultEffort,
|
|
51
|
+
capability: targetReasoningEfforts === undefined ? "unknown" : "unsupported",
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
return clone;
|
|
55
|
+
}
|
|
56
|
+
if (reasoning === undefined) {
|
|
57
|
+
clone.reasoning = { effort: defaultEffort };
|
|
58
|
+
} else {
|
|
59
|
+
clone.reasoning = { ...(reasoning as Record<string, unknown>), effort: defaultEffort };
|
|
60
|
+
}
|
|
61
|
+
return clone;
|
|
62
|
+
}
|