@linxiraos/pi-ai 1.0.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/CHANGELOG.md +5066 -0
- package/README.md +1195 -0
- package/dist/types/api-registry.d.ts +30 -0
- package/dist/types/auth/sqlite-credential-store.d.ts +117 -0
- package/dist/types/auth-broker/client.d.ts +93 -0
- package/dist/types/auth-broker/discover.d.ts +39 -0
- package/dist/types/auth-broker/index.d.ts +7 -0
- package/dist/types/auth-broker/refresher.d.ts +25 -0
- package/dist/types/auth-broker/remote-store.d.ts +136 -0
- package/dist/types/auth-broker/server.d.ts +48 -0
- package/dist/types/auth-broker/snapshot-cache.d.ts +17 -0
- package/dist/types/auth-broker/types.d.ts +152 -0
- package/dist/types/auth-broker/wire-schema-resource.d.ts +53 -0
- package/dist/types/auth-broker/wire-schemas.d.ts +5 -0
- package/dist/types/auth-gateway/http.d.ts +56 -0
- package/dist/types/auth-gateway/index.d.ts +3 -0
- package/dist/types/auth-gateway/server.d.ts +36 -0
- package/dist/types/auth-gateway/types.d.ts +127 -0
- package/dist/types/auth-retry.d.ts +150 -0
- package/dist/types/auth-storage.d.ts +1258 -0
- package/dist/types/dialect/anthropic.d.ts +15 -0
- package/dist/types/dialect/catalog.d.ts +3 -0
- package/dist/types/dialect/coercion.d.ts +23 -0
- package/dist/types/dialect/deepseek.d.ts +14 -0
- package/dist/types/dialect/demotion.d.ts +30 -0
- package/dist/types/dialect/examples.d.ts +18 -0
- package/dist/types/dialect/factory.d.ts +3 -0
- package/dist/types/dialect/fenced-thinking.d.ts +53 -0
- package/dist/types/dialect/gemini.d.ts +17 -0
- package/dist/types/dialect/gemma.d.ts +15 -0
- package/dist/types/dialect/glm.d.ts +9 -0
- package/dist/types/dialect/harmony.d.ts +8 -0
- package/dist/types/dialect/hermes.d.ts +9 -0
- package/dist/types/dialect/history.d.ts +3 -0
- package/dist/types/dialect/index.d.ts +11 -0
- package/dist/types/dialect/inventory.d.ts +9 -0
- package/dist/types/dialect/kimi.d.ts +14 -0
- package/dist/types/dialect/minimax.d.ts +3 -0
- package/dist/types/dialect/owned-stream.d.ts +4 -0
- package/dist/types/dialect/qwen3.d.ts +9 -0
- package/dist/types/dialect/rendering.d.ts +54 -0
- package/dist/types/dialect/thinking.d.ts +6 -0
- package/dist/types/dialect/types.d.ts +68 -0
- package/dist/types/dialect/xml.d.ts +9 -0
- package/dist/types/error/abort.d.ts +14 -0
- package/dist/types/error/auth-classify.d.ts +20 -0
- package/dist/types/error/auth.d.ts +27 -0
- package/dist/types/error/aws.d.ts +27 -0
- package/dist/types/error/classes.d.ts +106 -0
- package/dist/types/error/finalize.d.ts +39 -0
- package/dist/types/error/flags.d.ts +94 -0
- package/dist/types/error/format.d.ts +20 -0
- package/dist/types/error/gateway.d.ts +20 -0
- package/dist/types/error/index.d.ts +14 -0
- package/dist/types/error/oauth.d.ts +43 -0
- package/dist/types/error/provider.d.ts +42 -0
- package/dist/types/error/rate-limit.d.ts +80 -0
- package/dist/types/error/retryable.d.ts +27 -0
- package/dist/types/error/validation.d.ts +32 -0
- package/dist/types/index.d.ts +51 -0
- package/dist/types/provider-details.d.ts +24 -0
- package/dist/types/providers/amazon-bedrock.d.ts +39 -0
- package/dist/types/providers/anthropic-client.d.ts +106 -0
- package/dist/types/providers/anthropic-messages-server-schema.d.ts +937 -0
- package/dist/types/providers/anthropic-messages-server.d.ts +17 -0
- package/dist/types/providers/anthropic-wire.d.ts +345 -0
- package/dist/types/providers/anthropic.d.ts +261 -0
- package/dist/types/providers/aws-credentials.d.ts +48 -0
- package/dist/types/providers/aws-eventstream.d.ts +39 -0
- package/dist/types/providers/aws-sigv4.d.ts +55 -0
- package/dist/types/providers/azure-openai-responses.d.ts +16 -0
- package/dist/types/providers/bedrock-mantle.d.ts +13 -0
- package/dist/types/providers/claude-code-fingerprint.d.ts +19 -0
- package/dist/types/providers/cowork-fetch.d.ts +3 -0
- package/dist/types/providers/cursor/exec-modern.d.ts +98 -0
- package/dist/types/providers/cursor-pi-args.d.ts +105 -0
- package/dist/types/providers/cursor.d.ts +221 -0
- package/dist/types/providers/devin.d.ts +12 -0
- package/dist/types/providers/error-message.d.ts +25 -0
- package/dist/types/providers/github-copilot-headers.d.ts +40 -0
- package/dist/types/providers/gitlab-duo-workflow.d.ts +254 -0
- package/dist/types/providers/gitlab-duo.d.ts +27 -0
- package/dist/types/providers/google-auth.d.ts +24 -0
- package/dist/types/providers/google-gemini-cli.d.ts +120 -0
- package/dist/types/providers/google-shared.d.ts +203 -0
- package/dist/types/providers/google-types.d.ts +155 -0
- package/dist/types/providers/google-vertex.d.ts +7 -0
- package/dist/types/providers/google.d.ts +4 -0
- package/dist/types/providers/grammar.d.ts +1 -0
- package/dist/types/providers/kimi.d.ts +27 -0
- package/dist/types/providers/mock.d.ts +179 -0
- package/dist/types/providers/ollama.d.ts +8 -0
- package/dist/types/providers/openai-anthropic-shim.d.ts +35 -0
- package/dist/types/providers/openai-chat-server-schema.d.ts +1311 -0
- package/dist/types/providers/openai-chat-server.d.ts +16 -0
- package/dist/types/providers/openai-chat-wire.d.ts +669 -0
- package/dist/types/providers/openai-codex/request-transformer.d.ts +109 -0
- package/dist/types/providers/openai-codex/response-handler.d.ts +26 -0
- package/dist/types/providers/openai-codex-responses.d.ts +238 -0
- package/dist/types/providers/openai-completions.d.ts +48 -0
- package/dist/types/providers/openai-reasoning-fallback.d.ts +25 -0
- package/dist/types/providers/openai-responses-server-schema.d.ts +1314 -0
- package/dist/types/providers/openai-responses-server.d.ts +17 -0
- package/dist/types/providers/openai-responses-wire.d.ts +6099 -0
- package/dist/types/providers/openai-responses.d.ts +131 -0
- package/dist/types/providers/openai-shared.d.ts +624 -0
- package/dist/types/providers/pi-native-client.d.ts +13 -0
- package/dist/types/providers/pi-native-server.d.ts +69 -0
- package/dist/types/providers/register-builtins.d.ts +37 -0
- package/dist/types/providers/synthetic.d.ts +26 -0
- package/dist/types/providers/transform-messages.d.ts +32 -0
- package/dist/types/providers/vision-guard.d.ts +20 -0
- package/dist/types/registry/aiand.d.ts +7 -0
- package/dist/types/registry/aimlapi.d.ts +4 -0
- package/dist/types/registry/alibaba-coding-plan.d.ts +8 -0
- package/dist/types/registry/alibaba-token-plan.d.ts +18 -0
- package/dist/types/registry/amazon-bedrock.d.ts +5 -0
- package/dist/types/registry/anthropic.d.ts +10 -0
- package/dist/types/registry/api-key-login.d.ts +42 -0
- package/dist/types/registry/api-key-validation.d.ts +43 -0
- package/dist/types/registry/aws.d.ts +13 -0
- package/dist/types/registry/azure.d.ts +4 -0
- package/dist/types/registry/baseten.d.ts +7 -0
- package/dist/types/registry/bedrock-mantle.d.ts +22 -0
- package/dist/types/registry/cerebras.d.ts +7 -0
- package/dist/types/registry/cloudflare-ai-gateway.d.ts +13 -0
- package/dist/types/registry/coreweave.d.ts +7 -0
- package/dist/types/registry/cursor.d.ts +7 -0
- package/dist/types/registry/deepseek.d.ts +8 -0
- package/dist/types/registry/derived.d.ts +5 -0
- package/dist/types/registry/devin.d.ts +8 -0
- package/dist/types/registry/exa.d.ts +8 -0
- package/dist/types/registry/firepass.d.ts +16 -0
- package/dist/types/registry/fireworks.d.ts +7 -0
- package/dist/types/registry/github-copilot.d.ts +7 -0
- package/dist/types/registry/gitlab-duo-workflow.d.ts +10 -0
- package/dist/types/registry/gitlab-duo.d.ts +9 -0
- package/dist/types/registry/gmi-cloud.d.ts +7 -0
- package/dist/types/registry/google-antigravity.d.ts +9 -0
- package/dist/types/registry/google-gemini-cli.d.ts +9 -0
- package/dist/types/registry/google-vertex.d.ts +5 -0
- package/dist/types/registry/google.d.ts +4 -0
- package/dist/types/registry/groq.d.ts +4 -0
- package/dist/types/registry/huggingface.d.ts +7 -0
- package/dist/types/registry/index.d.ts +4 -0
- package/dist/types/registry/kagi.d.ts +14 -0
- package/dist/types/registry/kilo.d.ts +7 -0
- package/dist/types/registry/kimi-code.d.ts +7 -0
- package/dist/types/registry/litellm.d.ts +13 -0
- package/dist/types/registry/llama-cpp.d.ts +8 -0
- package/dist/types/registry/lm-studio.d.ts +8 -0
- package/dist/types/registry/meta.d.ts +7 -0
- package/dist/types/registry/minimax-code-cn.d.ts +6 -0
- package/dist/types/registry/minimax-code.d.ts +6 -0
- package/dist/types/registry/minimax.d.ts +4 -0
- package/dist/types/registry/mistral.d.ts +4 -0
- package/dist/types/registry/moonshot.d.ts +7 -0
- package/dist/types/registry/nanogpt.d.ts +7 -0
- package/dist/types/registry/novita.d.ts +6 -0
- package/dist/types/registry/nvidia.d.ts +7 -0
- package/dist/types/registry/oauth/anthropic-constants.d.ts +12 -0
- package/dist/types/registry/oauth/anthropic.d.ts +24 -0
- package/dist/types/registry/oauth/callback-server.d.ts +74 -0
- package/dist/types/registry/oauth/cursor.d.ts +16 -0
- package/dist/types/registry/oauth/device-code.d.ts +25 -0
- package/dist/types/registry/oauth/devin.d.ts +5 -0
- package/dist/types/registry/oauth/github-copilot.d.ts +30 -0
- package/dist/types/registry/oauth/gitlab-duo-workflow.d.ts +6 -0
- package/dist/types/registry/oauth/gitlab-duo.d.ts +3 -0
- package/dist/types/registry/oauth/google-antigravity.d.ts +11 -0
- package/dist/types/registry/oauth/google-gemini-cli.d.ts +22 -0
- package/dist/types/registry/oauth/google-oauth-shared.d.ts +56 -0
- package/dist/types/registry/oauth/index.d.ts +45 -0
- package/dist/types/registry/oauth/kimi.d.ts +21 -0
- package/dist/types/registry/oauth/minimax-code.d.ts +27 -0
- package/dist/types/registry/oauth/openai-codex.d.ts +33 -0
- package/dist/types/registry/oauth/opencode.d.ts +18 -0
- package/dist/types/registry/oauth/perplexity.d.ts +9 -0
- package/dist/types/registry/oauth/pkce.d.ts +8 -0
- package/dist/types/registry/oauth/types.d.ts +87 -0
- package/dist/types/registry/oauth/wafer.d.ts +1 -0
- package/dist/types/registry/oauth/xai-oauth.d.ts +46 -0
- package/dist/types/registry/oauth/xiaomi.d.ts +25 -0
- package/dist/types/registry/oauth/zai.d.ts +25 -0
- package/dist/types/registry/ollama-cloud.d.ts +7 -0
- package/dist/types/registry/ollama.d.ts +12 -0
- package/dist/types/registry/openai-codex-device.d.ts +8 -0
- package/dist/types/registry/openai-codex.d.ts +9 -0
- package/dist/types/registry/openai.d.ts +4 -0
- package/dist/types/registry/opencode-go.d.ts +6 -0
- package/dist/types/registry/opencode-zen.d.ts +6 -0
- package/dist/types/registry/openrouter.d.ts +13 -0
- package/dist/types/registry/parallel.d.ts +14 -0
- package/dist/types/registry/perplexity.d.ts +7 -0
- package/dist/types/registry/qianfan.d.ts +7 -0
- package/dist/types/registry/qwen-portal.d.ts +7 -0
- package/dist/types/registry/registry.d.ts +367 -0
- package/dist/types/registry/sakana.d.ts +7 -0
- package/dist/types/registry/siliconflow-cn.d.ts +7 -0
- package/dist/types/registry/siliconflow.d.ts +7 -0
- package/dist/types/registry/synthetic.d.ts +6 -0
- package/dist/types/registry/tavily.d.ts +14 -0
- package/dist/types/registry/together.d.ts +6 -0
- package/dist/types/registry/types.d.ts +75 -0
- package/dist/types/registry/umans.d.ts +7 -0
- package/dist/types/registry/venice.d.ts +13 -0
- package/dist/types/registry/vercel-ai-gateway.d.ts +7 -0
- package/dist/types/registry/vllm.d.ts +7 -0
- package/dist/types/registry/wafer-serverless.d.ts +6 -0
- package/dist/types/registry/xai-oauth.d.ts +7 -0
- package/dist/types/registry/xai.d.ts +7 -0
- package/dist/types/registry/xiaomi-token-plan-ams.d.ts +6 -0
- package/dist/types/registry/xiaomi-token-plan-cn.d.ts +6 -0
- package/dist/types/registry/xiaomi-token-plan-sgp.d.ts +6 -0
- package/dist/types/registry/xiaomi.d.ts +6 -0
- package/dist/types/registry/zai.d.ts +15 -0
- package/dist/types/registry/zenmux.d.ts +7 -0
- package/dist/types/registry/zhipu-coding-plan.d.ts +7 -0
- package/dist/types/stream.d.ts +46 -0
- package/dist/types/types.d.ts +1064 -0
- package/dist/types/usage/alibaba-token-plan.d.ts +3 -0
- package/dist/types/usage/claude.d.ts +4 -0
- package/dist/types/usage/cursor.d.ts +4 -0
- package/dist/types/usage/gemini.d.ts +2 -0
- package/dist/types/usage/github-copilot.d.ts +7 -0
- package/dist/types/usage/google-antigravity.d.ts +15 -0
- package/dist/types/usage/kimi.d.ts +2 -0
- package/dist/types/usage/minimax-code.d.ts +3 -0
- package/dist/types/usage/ollama.d.ts +5 -0
- package/dist/types/usage/openai-codex-base-url.d.ts +18 -0
- package/dist/types/usage/openai-codex-reset.d.ts +88 -0
- package/dist/types/usage/openai-codex.d.ts +10 -0
- package/dist/types/usage/opencode-go.d.ts +2 -0
- package/dist/types/usage/shared.d.ts +1 -0
- package/dist/types/usage/synthetic.d.ts +2 -0
- package/dist/types/usage/umans.d.ts +2 -0
- package/dist/types/usage/xai-oauth.d.ts +12 -0
- package/dist/types/usage/zai.d.ts +3 -0
- package/dist/types/usage.d.ts +527 -0
- package/dist/types/utils/abort.d.ts +25 -0
- package/dist/types/utils/anthropic-auth.d.ts +35 -0
- package/dist/types/utils/aws-profile.d.ts +17 -0
- package/dist/types/utils/block-symbols.d.ts +62 -0
- package/dist/types/utils/deterministic-id.d.ts +16 -0
- package/dist/types/utils/empty-completion-retry.d.ts +21 -0
- package/dist/types/utils/event-stream.d.ts +39 -0
- package/dist/types/utils/foundry.d.ts +1 -0
- package/dist/types/utils/google-validation.d.ts +2 -0
- package/dist/types/utils/harmony-leak.d.ts +135 -0
- package/dist/types/utils/http-inspector.d.ts +49 -0
- package/dist/types/utils/idle-iterator.d.ts +149 -0
- package/dist/types/utils/leaked-thinking-stream.d.ts +33 -0
- package/dist/types/utils/openai-http.d.ts +48 -0
- package/dist/types/utils/openrouter-headers.d.ts +1 -0
- package/dist/types/utils/parse-bind.d.ts +23 -0
- package/dist/types/utils/provider-response.d.ts +3 -0
- package/dist/types/utils/proxy.d.ts +39 -0
- package/dist/types/utils/request-debug.d.ts +29 -0
- package/dist/types/utils/retry-after.d.ts +4 -0
- package/dist/types/utils/retry.d.ts +14 -0
- package/dist/types/utils/schema/adapt.d.ts +24 -0
- package/dist/types/utils/schema/compatibility.d.ts +30 -0
- package/dist/types/utils/schema/dereference.d.ts +11 -0
- package/dist/types/utils/schema/draft.d.ts +10 -0
- package/dist/types/utils/schema/equality.d.ts +4 -0
- package/dist/types/utils/schema/fields.d.ts +54 -0
- package/dist/types/utils/schema/index.d.ts +14 -0
- package/dist/types/utils/schema/json-schema-validator.d.ts +20 -0
- package/dist/types/utils/schema/meta-validator.d.ts +2 -0
- package/dist/types/utils/schema/normalize.d.ts +153 -0
- package/dist/types/utils/schema/spill.d.ts +8 -0
- package/dist/types/utils/schema/stamps.d.ts +17 -0
- package/dist/types/utils/schema/strict-tool-validation.d.ts +16 -0
- package/dist/types/utils/schema/types.d.ts +4 -0
- package/dist/types/utils/schema/typescript.d.ts +24 -0
- package/dist/types/utils/schema/wire.d.ts +52 -0
- package/dist/types/utils/sdk-stream-timeout.d.ts +33 -0
- package/dist/types/utils/sse-debug.d.ts +5 -0
- package/dist/types/utils/stream-markup-healing.d.ts +87 -0
- package/dist/types/utils/thinking-loop.d.ts +102 -0
- package/dist/types/utils/tool-call-loop-guard.d.ts +26 -0
- package/dist/types/utils/tool-choice.d.ts +52 -0
- package/dist/types/utils/validation.d.ts +28 -0
- package/dist/types/utils.d.ts +57 -0
- package/package.json +138 -0
- package/src/api-registry.ts +109 -0
- package/src/auth/sqlite-credential-store.ts +2066 -0
- package/src/auth-broker/client.ts +471 -0
- package/src/auth-broker/discover.ts +310 -0
- package/src/auth-broker/index.ts +7 -0
- package/src/auth-broker/refresher.ts +117 -0
- package/src/auth-broker/remote-store.ts +1332 -0
- package/src/auth-broker/server.ts +898 -0
- package/src/auth-broker/snapshot-cache.ts +200 -0
- package/src/auth-broker/types.ts +193 -0
- package/src/auth-broker/wire-schema-resource.ts +487 -0
- package/src/auth-broker/wire-schemas.ts +43 -0
- package/src/auth-gateway/http.ts +227 -0
- package/src/auth-gateway/index.ts +3 -0
- package/src/auth-gateway/server.ts +836 -0
- package/src/auth-gateway/types.ts +153 -0
- package/src/auth-retry.ts +401 -0
- package/src/auth-storage.ts +6540 -0
- package/src/dialect/anthropic.md +31 -0
- package/src/dialect/anthropic.ts +608 -0
- package/src/dialect/catalog.ts +29 -0
- package/src/dialect/coercion.ts +136 -0
- package/src/dialect/deepseek.md +24 -0
- package/src/dialect/deepseek.ts +609 -0
- package/src/dialect/demotion.ts +40 -0
- package/src/dialect/examples.ts +71 -0
- package/src/dialect/factory.ts +34 -0
- package/src/dialect/fenced-thinking.ts +184 -0
- package/src/dialect/gemini.md +44 -0
- package/src/dialect/gemini.ts +583 -0
- package/src/dialect/gemma.md +33 -0
- package/src/dialect/gemma.ts +387 -0
- package/src/dialect/glm.md +32 -0
- package/src/dialect/glm.ts +579 -0
- package/src/dialect/harmony.md +31 -0
- package/src/dialect/harmony.ts +345 -0
- package/src/dialect/hermes.md +25 -0
- package/src/dialect/hermes.ts +206 -0
- package/src/dialect/history.ts +81 -0
- package/src/dialect/index.ts +15 -0
- package/src/dialect/inventory.ts +30 -0
- package/src/dialect/kimi.md +24 -0
- package/src/dialect/kimi.ts +340 -0
- package/src/dialect/minimax.md +31 -0
- package/src/dialect/minimax.ts +95 -0
- package/src/dialect/owned-stream.ts +481 -0
- package/src/dialect/prompt-template.md +12 -0
- package/src/dialect/qwen3.md +28 -0
- package/src/dialect/qwen3.ts +240 -0
- package/src/dialect/rendering.ts +304 -0
- package/src/dialect/thinking.ts +292 -0
- package/src/dialect/types.ts +56 -0
- package/src/dialect/xml.md +22 -0
- package/src/dialect/xml.ts +90 -0
- package/src/error/abort.ts +18 -0
- package/src/error/auth-classify.ts +47 -0
- package/src/error/auth.ts +48 -0
- package/src/error/aws.ts +35 -0
- package/src/error/classes.ts +281 -0
- package/src/error/finalize.ts +69 -0
- package/src/error/flags.ts +602 -0
- package/src/error/format.ts +45 -0
- package/src/error/gateway.ts +96 -0
- package/src/error/index.ts +14 -0
- package/src/error/oauth.ts +58 -0
- package/src/error/provider.ts +63 -0
- package/src/error/rate-limit.ts +303 -0
- package/src/error/retryable.ts +70 -0
- package/src/error/validation.ts +44 -0
- package/src/index.ts +51 -0
- package/src/provider-details.ts +90 -0
- package/src/providers/amazon-bedrock.ts +1064 -0
- package/src/providers/anthropic-client.ts +317 -0
- package/src/providers/anthropic-messages-server-schema.ts +252 -0
- package/src/providers/anthropic-messages-server.ts +818 -0
- package/src/providers/anthropic-wire.ts +359 -0
- package/src/providers/anthropic.ts +4539 -0
- package/src/providers/aws-credentials.ts +772 -0
- package/src/providers/aws-eventstream.ts +181 -0
- package/src/providers/aws-sigv4.ts +218 -0
- package/src/providers/azure-openai-responses.ts +438 -0
- package/src/providers/bedrock-mantle.ts +110 -0
- package/src/providers/claude-code-fingerprint.ts +20 -0
- package/src/providers/cowork-fetch.ts +201 -0
- package/src/providers/cursor/exec-modern.ts +496 -0
- package/src/providers/cursor/proto/agent.proto +4533 -0
- package/src/providers/cursor/proto/buf.gen.yaml +6 -0
- package/src/providers/cursor/proto/buf.yaml +17 -0
- package/src/providers/cursor-pi-args.ts +165 -0
- package/src/providers/cursor.ts +4689 -0
- package/src/providers/devin/proto/buf/validate/validate.proto +468 -0
- package/src/providers/devin/proto/buf.gen.yaml +33 -0
- package/src/providers/devin/proto/buf.yaml +17 -0
- package/src/providers/devin/proto/cel/expr/checked.proto +103 -0
- package/src/providers/devin/proto/cel/expr/eval.proto +38 -0
- package/src/providers/devin/proto/cel/expr/explain.proto +15 -0
- package/src/providers/devin/proto/cel/expr/syntax.proto +113 -0
- package/src/providers/devin/proto/cel/expr/value.proto +41 -0
- package/src/providers/devin/proto/connectext/grpc/status/v1/status.proto +11 -0
- package/src/providers/devin/proto/errorspb/errors.proto +56 -0
- package/src/providers/devin/proto/errorspb/hintdetail.proto +7 -0
- package/src/providers/devin/proto/errorspb/markers.proto +10 -0
- package/src/providers/devin/proto/errorspb/tags.proto +12 -0
- package/src/providers/devin/proto/errorspb/testing.proto +6 -0
- package/src/providers/devin/proto/exa/analytics_pb/analytics.proto +188 -0
- package/src/providers/devin/proto/exa/api_server_pb/api_server.proto +2461 -0
- package/src/providers/devin/proto/exa/auth_pb/auth.proto +19 -0
- package/src/providers/devin/proto/exa/auto_cascade_common_pb/auto_cascade_common.proto +79 -0
- package/src/providers/devin/proto/exa/browser_preview_pb/browser_preview.proto +32 -0
- package/src/providers/devin/proto/exa/bug_checker_pb/bug_checker.proto +22 -0
- package/src/providers/devin/proto/exa/cascade_plugins_pb/cascade_plugins.proto +262 -0
- package/src/providers/devin/proto/exa/chat_client_server_pb/chat_client_server.proto +57 -0
- package/src/providers/devin/proto/exa/chat_pb/chat.proto +449 -0
- package/src/providers/devin/proto/exa/code_edit/code_edit_pb/code_edit.proto +186 -0
- package/src/providers/devin/proto/exa/codeium_common_pb/codeium_common.proto +4157 -0
- package/src/providers/devin/proto/exa/context_module_pb/context_module.proto +175 -0
- package/src/providers/devin/proto/exa/cortex_pb/cortex.proto +3268 -0
- package/src/providers/devin/proto/exa/dev_pb/dev.proto +26 -0
- package/src/providers/devin/proto/exa/diff_action_pb/diff_action.proto +75 -0
- package/src/providers/devin/proto/exa/eval/pr_eval/datasets_pb/datasets.proto +103 -0
- package/src/providers/devin/proto/exa/eval_pb/eval.proto +1315 -0
- package/src/providers/devin/proto/exa/extension_server_pb/extension_server.proto +556 -0
- package/src/providers/devin/proto/exa/file_system_provider_pb/file_system_provider.proto +75 -0
- package/src/providers/devin/proto/exa/index_pb/index.proto +461 -0
- package/src/providers/devin/proto/exa/knowledge_base_pb/knowledge_base.proto +144 -0
- package/src/providers/devin/proto/exa/language_server_pb/language_server.proto +2385 -0
- package/src/providers/devin/proto/exa/model_management_pb/model_management.proto +186 -0
- package/src/providers/devin/proto/exa/opensearch_clients_pb/opensearch_clients.proto +503 -0
- package/src/providers/devin/proto/exa/product_analytics_pb/product_analytics.proto +37 -0
- package/src/providers/devin/proto/exa/prompt_pb/prompt.proto +92 -0
- package/src/providers/devin/proto/exa/reactive_component_pb/reactive_component.proto +96 -0
- package/src/providers/devin/proto/exa/seat_management_pb/seat_management.proto +2680 -0
- package/src/providers/devin/proto/exa/tokenizer_pb/tokenizer.proto +37 -0
- package/src/providers/devin/proto/exa/trainer_pb/config.proto +647 -0
- package/src/providers/devin/proto/exa/tree_sitter/language_data_pb/language_data.proto +14 -0
- package/src/providers/devin/proto/exa/trust_pb/trust.proto +157 -0
- package/src/providers/devin/proto/exa/user_analytics_pb/user_analytics.proto +519 -0
- package/src/providers/devin/proto/google.golang.org/appengine/internal/base/api_base.proto +28 -0
- package/src/providers/devin/proto/google.golang.org/appengine/internal/datastore/datastore_v3.proto +484 -0
- package/src/providers/devin/proto/google.golang.org/appengine/internal/log/log_service.proto +136 -0
- package/src/providers/devin/proto/google.golang.org/appengine/internal/remote_api/remote_api.proto +42 -0
- package/src/providers/devin/proto/google.golang.org/appengine/internal/urlfetch/urlfetch_service.proto +61 -0
- package/src/providers/devin/proto/grpc/binlog/v1/binarylog.proto +84 -0
- package/src/providers/devin/proto/io/prometheus/client/metrics.proto +98 -0
- package/src/providers/devin.ts +679 -0
- package/src/providers/error-message.ts +23 -0
- package/src/providers/github-copilot-headers.ts +141 -0
- package/src/providers/gitlab-duo-workflow-chatml-note.md +1 -0
- package/src/providers/gitlab-duo-workflow.ts +3135 -0
- package/src/providers/gitlab-duo.ts +399 -0
- package/src/providers/google-auth.ts +330 -0
- package/src/providers/google-gemini-cli.ts +1370 -0
- package/src/providers/google-shared.ts +1122 -0
- package/src/providers/google-types.ts +180 -0
- package/src/providers/google-vertex.ts +135 -0
- package/src/providers/google.ts +47 -0
- package/src/providers/grammar.ts +70 -0
- package/src/providers/kimi.ts +51 -0
- package/src/providers/mock.ts +514 -0
- package/src/providers/ollama.ts +776 -0
- package/src/providers/openai-anthropic-shim.ts +166 -0
- package/src/providers/openai-chat-server-schema.ts +243 -0
- package/src/providers/openai-chat-server.ts +752 -0
- package/src/providers/openai-chat-wire.ts +859 -0
- package/src/providers/openai-codex/request-transformer.ts +491 -0
- package/src/providers/openai-codex/response-handler.ts +102 -0
- package/src/providers/openai-codex-responses.ts +4716 -0
- package/src/providers/openai-completions.ts +2389 -0
- package/src/providers/openai-reasoning-fallback.ts +269 -0
- package/src/providers/openai-responses-server-schema.ts +397 -0
- package/src/providers/openai-responses-server.ts +1466 -0
- package/src/providers/openai-responses-wire.ts +6416 -0
- package/src/providers/openai-responses.ts +1393 -0
- package/src/providers/openai-shared.ts +3500 -0
- package/src/providers/pi-native-client.ts +275 -0
- package/src/providers/pi-native-server.ts +245 -0
- package/src/providers/register-builtins.ts +503 -0
- package/src/providers/synthetic.ts +50 -0
- package/src/providers/transform-messages.ts +1083 -0
- package/src/providers/vision-guard.ts +54 -0
- package/src/registry/aiand.ts +22 -0
- package/src/registry/aimlapi.ts +6 -0
- package/src/registry/alibaba-coding-plan.ts +104 -0
- package/src/registry/alibaba-token-plan.ts +125 -0
- package/src/registry/amazon-bedrock.ts +22 -0
- package/src/registry/anthropic.ts +26 -0
- package/src/registry/api-key-login.ts +115 -0
- package/src/registry/api-key-validation.ts +145 -0
- package/src/registry/aws.ts +57 -0
- package/src/registry/azure.ts +6 -0
- package/src/registry/baseten.ts +22 -0
- package/src/registry/bedrock-mantle.ts +34 -0
- package/src/registry/cerebras.ts +23 -0
- package/src/registry/cloudflare-ai-gateway.ts +45 -0
- package/src/registry/coreweave.ts +40 -0
- package/src/registry/cursor.ts +20 -0
- package/src/registry/deepseek.ts +46 -0
- package/src/registry/derived.ts +9 -0
- package/src/registry/devin.ts +15 -0
- package/src/registry/exa.ts +19 -0
- package/src/registry/firepass.ts +32 -0
- package/src/registry/fireworks.ts +28 -0
- package/src/registry/github-copilot.ts +22 -0
- package/src/registry/gitlab-duo-workflow.ts +20 -0
- package/src/registry/gitlab-duo.ts +19 -0
- package/src/registry/gmi-cloud.ts +22 -0
- package/src/registry/google-antigravity.ts +22 -0
- package/src/registry/google-gemini-cli.ts +22 -0
- package/src/registry/google-vertex.ts +38 -0
- package/src/registry/google.ts +6 -0
- package/src/registry/groq.ts +6 -0
- package/src/registry/huggingface.ts +29 -0
- package/src/registry/index.ts +4 -0
- package/src/registry/kagi.ts +46 -0
- package/src/registry/kilo.ts +114 -0
- package/src/registry/kimi-code.ts +17 -0
- package/src/registry/litellm.ts +45 -0
- package/src/registry/llama-cpp.ts +35 -0
- package/src/registry/lm-studio.ts +31 -0
- package/src/registry/meta.ts +22 -0
- package/src/registry/minimax-code-cn.ts +12 -0
- package/src/registry/minimax-code.ts +12 -0
- package/src/registry/minimax.ts +6 -0
- package/src/registry/mistral.ts +6 -0
- package/src/registry/moonshot.ts +28 -0
- package/src/registry/nanogpt.ts +22 -0
- package/src/registry/novita.ts +25 -0
- package/src/registry/nvidia.ts +61 -0
- package/src/registry/oauth/anthropic-constants.ts +12 -0
- package/src/registry/oauth/anthropic.ts +346 -0
- package/src/registry/oauth/callback-server.ts +438 -0
- package/src/registry/oauth/cursor.ts +187 -0
- package/src/registry/oauth/device-code.ts +92 -0
- package/src/registry/oauth/devin.ts +124 -0
- package/src/registry/oauth/github-copilot.ts +369 -0
- package/src/registry/oauth/gitlab-duo-workflow.ts +146 -0
- package/src/registry/oauth/gitlab-duo.ts +222 -0
- package/src/registry/oauth/google-antigravity.ts +225 -0
- package/src/registry/oauth/google-gemini-cli.ts +297 -0
- package/src/registry/oauth/google-oauth-shared.ts +211 -0
- package/src/registry/oauth/index.ts +187 -0
- package/src/registry/oauth/kimi.ts +297 -0
- package/src/registry/oauth/minimax-code.ts +53 -0
- package/src/registry/oauth/oauth.html +317 -0
- package/src/registry/oauth/openai-codex.ts +384 -0
- package/src/registry/oauth/opencode.ts +50 -0
- package/src/registry/oauth/perplexity.ts +228 -0
- package/src/registry/oauth/pkce.ts +18 -0
- package/src/registry/oauth/types.ts +96 -0
- package/src/registry/oauth/wafer.ts +24 -0
- package/src/registry/oauth/xai-oauth.ts +559 -0
- package/src/registry/oauth/xiaomi.ts +211 -0
- package/src/registry/oauth/zai.ts +285 -0
- package/src/registry/ollama-cloud.ts +36 -0
- package/src/registry/ollama.ts +43 -0
- package/src/registry/openai-codex-device.ts +18 -0
- package/src/registry/openai-codex.ts +19 -0
- package/src/registry/openai.ts +6 -0
- package/src/registry/opencode-go.ts +12 -0
- package/src/registry/opencode-zen.ts +12 -0
- package/src/registry/openrouter.ts +28 -0
- package/src/registry/parallel.ts +45 -0
- package/src/registry/perplexity.ts +13 -0
- package/src/registry/qianfan.ts +27 -0
- package/src/registry/qwen-portal.ts +50 -0
- package/src/registry/registry.ts +182 -0
- package/src/registry/sakana.ts +22 -0
- package/src/registry/siliconflow-cn.ts +22 -0
- package/src/registry/siliconflow.ts +22 -0
- package/src/registry/synthetic.ts +21 -0
- package/src/registry/tavily.ts +45 -0
- package/src/registry/together.ts +22 -0
- package/src/registry/types.ts +86 -0
- package/src/registry/umans.ts +23 -0
- package/src/registry/venice.ts +33 -0
- package/src/registry/vercel-ai-gateway.ts +38 -0
- package/src/registry/vllm.ts +34 -0
- package/src/registry/wafer-serverless.ts +12 -0
- package/src/registry/xai-oauth.ts +17 -0
- package/src/registry/xai.ts +22 -0
- package/src/registry/xiaomi-token-plan-ams.ts +12 -0
- package/src/registry/xiaomi-token-plan-cn.ts +12 -0
- package/src/registry/xiaomi-token-plan-sgp.ts +12 -0
- package/src/registry/xiaomi.ts +12 -0
- package/src/registry/zai.ts +41 -0
- package/src/registry/zenmux.ts +22 -0
- package/src/registry/zhipu-coding-plan.ts +27 -0
- package/src/stream.ts +1944 -0
- package/src/types.ts +1243 -0
- package/src/usage/alibaba-token-plan.ts +230 -0
- package/src/usage/claude.ts +830 -0
- package/src/usage/cursor.ts +335 -0
- package/src/usage/gemini.ts +258 -0
- package/src/usage/github-copilot.ts +424 -0
- package/src/usage/google-antigravity.ts +497 -0
- package/src/usage/kimi.ts +277 -0
- package/src/usage/minimax-code.ts +291 -0
- package/src/usage/ollama.ts +41 -0
- package/src/usage/openai-codex-base-url.ts +35 -0
- package/src/usage/openai-codex-reset.ts +205 -0
- package/src/usage/openai-codex.ts +627 -0
- package/src/usage/opencode-go.ts +89 -0
- package/src/usage/shared.ts +10 -0
- package/src/usage/synthetic.ts +180 -0
- package/src/usage/umans.ts +192 -0
- package/src/usage/xai-oauth.ts +414 -0
- package/src/usage/zai.ts +370 -0
- package/src/usage.ts +411 -0
- package/src/utils/abort.ts +67 -0
- package/src/utils/anthropic-auth.ts +93 -0
- package/src/utils/aws-profile.ts +88 -0
- package/src/utils/block-symbols.ts +78 -0
- package/src/utils/deterministic-id.ts +20 -0
- package/src/utils/empty-completion-retry.ts +161 -0
- package/src/utils/event-stream.ts +202 -0
- package/src/utils/foundry.ts +8 -0
- package/src/utils/google-validation.ts +25 -0
- package/src/utils/harmony-leak.ts +500 -0
- package/src/utils/http-inspector.ts +196 -0
- package/src/utils/idle-iterator.ts +531 -0
- package/src/utils/leaked-thinking-stream.ts +483 -0
- package/src/utils/openai-http.ts +119 -0
- package/src/utils/openrouter-headers.ts +12 -0
- package/src/utils/parse-bind.ts +56 -0
- package/src/utils/provider-response.ts +30 -0
- package/src/utils/proxy.ts +314 -0
- package/src/utils/request-debug.ts +351 -0
- package/src/utils/retry-after.ts +121 -0
- package/src/utils/retry.ts +77 -0
- package/src/utils/schema/CONSTRAINTS.md +168 -0
- package/src/utils/schema/adapt.ts +36 -0
- package/src/utils/schema/compatibility.ts +435 -0
- package/src/utils/schema/dereference.ts +98 -0
- package/src/utils/schema/draft.ts +341 -0
- package/src/utils/schema/equality.ts +97 -0
- package/src/utils/schema/fields.ts +210 -0
- package/src/utils/schema/index.ts +14 -0
- package/src/utils/schema/json-schema-validator.ts +595 -0
- package/src/utils/schema/meta-validator.ts +167 -0
- package/src/utils/schema/normalize.ts +2314 -0
- package/src/utils/schema/spill.ts +43 -0
- package/src/utils/schema/stamps.ts +109 -0
- package/src/utils/schema/strict-tool-validation.ts +117 -0
- package/src/utils/schema/types.ts +10 -0
- package/src/utils/schema/typescript.ts +212 -0
- package/src/utils/schema/wire.ts +662 -0
- package/src/utils/sdk-stream-timeout.ts +43 -0
- package/src/utils/sse-debug.ts +18 -0
- package/src/utils/stream-markup-healing.ts +247 -0
- package/src/utils/thinking-loop.ts +552 -0
- package/src/utils/tool-call-loop-guard.ts +107 -0
- package/src/utils/tool-choice.ts +101 -0
- package/src/utils/validation.ts +1932 -0
- package/src/utils.ts +492 -0
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { isUsageLimit } from "./flags";
|
|
2
|
+
|
|
3
|
+
/** A gateway-facing classification of an arbitrary upstream/internal error. */
|
|
4
|
+
export interface GatewayErrorClassification {
|
|
5
|
+
status: number;
|
|
6
|
+
type: string;
|
|
7
|
+
message: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Classify an upstream / gateway-internal error into a status code and a
|
|
12
|
+
* format-neutral type. The order is intentional:
|
|
13
|
+
*
|
|
14
|
+
* 1. Honour an explicit numeric `status` property on the thrown error.
|
|
15
|
+
* 2. Parse a status code embedded in the message string. Provider errors
|
|
16
|
+
* virtually always carry one (`Google API error (400): …`, `HTTP 429`,
|
|
17
|
+
* `status=503`) and the embedded value is authoritative.
|
|
18
|
+
* 3. Fall through to **word-boundaried** substring heuristics. The old
|
|
19
|
+
* `lower.includes("rate")` test famously matched `GenerateContentRequest`,
|
|
20
|
+
* surfacing every Google 400 as a 429 `rate_limit_error`. The patterns here
|
|
21
|
+
* all require boundaries so they don't collide with provider field names.
|
|
22
|
+
*/
|
|
23
|
+
export function classifyGatewayError(err: unknown): GatewayErrorClassification {
|
|
24
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
25
|
+
|
|
26
|
+
// 1. Custom pi-ai errors may attach a numeric `status` property.
|
|
27
|
+
const statusProp =
|
|
28
|
+
typeof err === "object" && err !== null && typeof (err as { status?: unknown }).status === "number"
|
|
29
|
+
? (err as { status: number }).status | 0
|
|
30
|
+
: undefined;
|
|
31
|
+
if (statusProp !== undefined) return bucketStatus(statusProp, message);
|
|
32
|
+
|
|
33
|
+
if (err instanceof Error && err.name === "AbortError") return { status: 499, type: "request_aborted", message };
|
|
34
|
+
|
|
35
|
+
// 2. Status code embedded in the message. Requires a contextual keyword
|
|
36
|
+
// (`HTTP`, `API error`, `status`, …) or a leading `(NNN)` token so we
|
|
37
|
+
// don't trip on incidental three-digit numbers ("took 200ms").
|
|
38
|
+
const embedded = extractEmbeddedStatus(message);
|
|
39
|
+
if (embedded !== undefined) return bucketStatus(embedded, message);
|
|
40
|
+
|
|
41
|
+
// 3. Word-boundaried substring heuristics.
|
|
42
|
+
if (/\baborted\b|\babort signal\b/i.test(message)) {
|
|
43
|
+
return { status: 499, type: "request_aborted", message };
|
|
44
|
+
}
|
|
45
|
+
if (
|
|
46
|
+
// Match rate-limit phrasings before auth wording: some providers
|
|
47
|
+
// describe throttling as "unauthorized due to rate limit".
|
|
48
|
+
// Keep boundaries so this does not collide with
|
|
49
|
+
// `GenerateContentRequest`, `accelerate`, `iterate`, `deprecated`, etc.
|
|
50
|
+
/\brate[- _]?limit(?:s|ed|ing)?\b|\bquota(?:_exceeded| exceeded)?\b|\btoo[- _]many[- _]requests\b/i.test(
|
|
51
|
+
message,
|
|
52
|
+
) ||
|
|
53
|
+
// Usage-limit phrasings emit no embedded status. Codex friendly text
|
|
54
|
+
// reads "You have hit your ChatGPT usage limit … Try again in ~158
|
|
55
|
+
// min."; the central usage-limit classifier already encodes every known
|
|
56
|
+
// provider variant, so reuse it instead of forking the regex. Without
|
|
57
|
+
// this branch the classifier falls through to the default
|
|
58
|
+
// 502/upstream_error, which is what callers saw when their account
|
|
59
|
+
// hit its cap.
|
|
60
|
+
isUsageLimit(message)
|
|
61
|
+
) {
|
|
62
|
+
return { status: 429, type: "rate_limit_error", message };
|
|
63
|
+
}
|
|
64
|
+
if (/\b(?:unauthorized|forbidden)\b/i.test(message)) {
|
|
65
|
+
return { status: 401, type: "authentication_error", message };
|
|
66
|
+
}
|
|
67
|
+
if (/\b(?:unsupported|invalid_request|invalid request|bad request|malformed)\b/i.test(message)) {
|
|
68
|
+
return { status: 400, type: "invalid_request_error", message };
|
|
69
|
+
}
|
|
70
|
+
return { status: 502, type: "upstream_error", message };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function bucketStatus(status: number, message: string): GatewayErrorClassification {
|
|
74
|
+
if (status === 401 || status === 403) return { status, type: "authentication_error", message };
|
|
75
|
+
if (status === 429) return { status, type: "rate_limit_error", message };
|
|
76
|
+
if (status >= 400 && status < 500) return { status, type: "invalid_request_error", message };
|
|
77
|
+
if (status >= 500) return { status, type: "upstream_error", message };
|
|
78
|
+
return { status: 502, type: "upstream_error", message };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Pull a status code from common error-message shapes. Returns undefined when
|
|
83
|
+
* no contextual keyword is present, so we never guess at incidental numbers.
|
|
84
|
+
*/
|
|
85
|
+
function extractEmbeddedStatus(message: string): number | undefined {
|
|
86
|
+
// `Google API error (400)`, `OpenAI API error (429): …`, `(503)`
|
|
87
|
+
// `HTTP 429: too many requests`
|
|
88
|
+
// `status: 503`, `status_code=429`, `status=400`
|
|
89
|
+
const re = /(?:\bHTTP\b|\bAPI error\b|\bstatus(?:[- _]?code)?\b)\s*[:=]?\s*\(?\s*(\d{3})\b|\((\d{3})\)/i;
|
|
90
|
+
const m = message.match(re);
|
|
91
|
+
if (!m) return undefined;
|
|
92
|
+
const raw = m[1] ?? m[2];
|
|
93
|
+
if (!raw) return undefined;
|
|
94
|
+
const code = Number.parseInt(raw, 10);
|
|
95
|
+
return Number.isFinite(code) && code >= 100 && code < 600 ? code : undefined;
|
|
96
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export * from "./abort";
|
|
2
|
+
export * from "./auth";
|
|
3
|
+
export * from "./auth-classify";
|
|
4
|
+
export * from "./aws";
|
|
5
|
+
export * from "./classes";
|
|
6
|
+
export * from "./finalize";
|
|
7
|
+
export * from "./flags";
|
|
8
|
+
export * from "./format";
|
|
9
|
+
export * from "./gateway";
|
|
10
|
+
export * from "./oauth";
|
|
11
|
+
export * from "./provider";
|
|
12
|
+
export * from "./rate-limit";
|
|
13
|
+
export * from "./retryable";
|
|
14
|
+
export * from "./validation";
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { attach, create, Flag } from "./flags";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* What stage of an OAuth / device-code login flow failed. Discriminates the
|
|
5
|
+
* single {@link OAuthError} class so login flows don't each mint a bespoke
|
|
6
|
+
* error type.
|
|
7
|
+
*/
|
|
8
|
+
export type OAuthErrorKind =
|
|
9
|
+
/** Token-exchange / refresh / discovery HTTP response was non-2xx or unparseable. */
|
|
10
|
+
| "http"
|
|
11
|
+
/** Response body was missing required fields (token, account id, endpoints, …). */
|
|
12
|
+
| "validation"
|
|
13
|
+
/** Authorization-code → token exchange failed. */
|
|
14
|
+
| "token-exchange"
|
|
15
|
+
/** Refresh-token grant failed. */
|
|
16
|
+
| "token-refresh"
|
|
17
|
+
/** Device-code / authorization polling failed (server error, too many retries). */
|
|
18
|
+
| "polling"
|
|
19
|
+
/** The flow exceeded its deadline (device-code expiry, polling timeout). */
|
|
20
|
+
| "timeout"
|
|
21
|
+
/** Device authorization was denied or cancelled by the user/provider. */
|
|
22
|
+
| "device-auth"
|
|
23
|
+
/** Misconfiguration (bad redirect URI, missing projectId, callback bind, …). */
|
|
24
|
+
| "configuration"
|
|
25
|
+
/** Cloud project provisioning / onboarding (loadCodeAssist, onboardUser). */
|
|
26
|
+
| "provisioning"
|
|
27
|
+
/** OIDC / endpoint discovery failed. */
|
|
28
|
+
| "discovery";
|
|
29
|
+
|
|
30
|
+
export interface OAuthErrorOptions {
|
|
31
|
+
kind?: OAuthErrorKind;
|
|
32
|
+
provider?: string;
|
|
33
|
+
status?: number;
|
|
34
|
+
cause?: unknown;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* A failure inside an interactive OAuth / device-code login flow. The `kind`
|
|
39
|
+
* pinpoints the stage. Timeout/polling are classified transient; everything
|
|
40
|
+
* else is a hard auth failure so the credential layer does not silently retry.
|
|
41
|
+
*/
|
|
42
|
+
export class OAuthError extends Error {
|
|
43
|
+
readonly kind: OAuthErrorKind;
|
|
44
|
+
readonly provider: string | undefined;
|
|
45
|
+
readonly status: number | undefined;
|
|
46
|
+
|
|
47
|
+
constructor(message: string, options: OAuthErrorOptions = {}) {
|
|
48
|
+
super(message, options.cause === undefined ? undefined : { cause: options.cause });
|
|
49
|
+
this.name = "OAuthError";
|
|
50
|
+
this.kind = options.kind ?? "http";
|
|
51
|
+
this.provider = options.provider;
|
|
52
|
+
this.status = options.status;
|
|
53
|
+
attach(
|
|
54
|
+
this,
|
|
55
|
+
this.kind === "timeout" || this.kind === "polling" ? create(Flag.Transient) : create(Flag.AuthFailed),
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { ProviderHttpError } from "./classes";
|
|
2
|
+
import { attach, create, Flag } from "./flags";
|
|
3
|
+
|
|
4
|
+
/** Which part of a provider exchange produced a non-HTTP error. */
|
|
5
|
+
export type ProviderResponseErrorKind =
|
|
6
|
+
/** Stream closed before a terminal completion/response event. */
|
|
7
|
+
| "incomplete-stream"
|
|
8
|
+
/** Terminal event carried an error / unexpected stop reason. */
|
|
9
|
+
| "output"
|
|
10
|
+
/** Response body was empty/missing when content was required. */
|
|
11
|
+
| "empty-body"
|
|
12
|
+
/** Malformed wire envelope (unexpected message ordering / shape). */
|
|
13
|
+
| "envelope"
|
|
14
|
+
/** Content was blocked by a provider safety filter. */
|
|
15
|
+
| "content-blocked"
|
|
16
|
+
/** Runtime/namespace resolution or other provider-internal failure. */
|
|
17
|
+
| "runtime";
|
|
18
|
+
|
|
19
|
+
export interface ProviderResponseErrorOptions {
|
|
20
|
+
provider?: string;
|
|
21
|
+
kind?: ProviderResponseErrorKind;
|
|
22
|
+
cause?: unknown;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* A non-HTTP provider failure: a truncated stream, an error stop reason, an
|
|
27
|
+
* empty body, a malformed envelope, or a runtime fault. For non-2xx HTTP
|
|
28
|
+
* responses use {@link ProviderHttpError} (or a provider subclass) instead.
|
|
29
|
+
*/
|
|
30
|
+
export class ProviderResponseError extends Error {
|
|
31
|
+
readonly provider: string | undefined;
|
|
32
|
+
readonly kind: ProviderResponseErrorKind;
|
|
33
|
+
|
|
34
|
+
constructor(message: string, options: ProviderResponseErrorOptions = {}) {
|
|
35
|
+
super(message, options.cause === undefined ? undefined : { cause: options.cause });
|
|
36
|
+
this.name = "ProviderResponseError";
|
|
37
|
+
this.provider = options.provider;
|
|
38
|
+
this.kind = options.kind ?? "output";
|
|
39
|
+
// A safety filter block is terminal and intentionally non-retryable.
|
|
40
|
+
if (this.kind === "content-blocked") attach(this, create(Flag.ContentBlocked));
|
|
41
|
+
// An incomplete stream (connection dropped / truncated before any terminal
|
|
42
|
+
// event) or an empty body never produced any content — the request didn't
|
|
43
|
+
// complete, so it is safe to retry and eligible for model fallback. The
|
|
44
|
+
// retry layer's replay-unsafe guard still blocks a retry when partial tool
|
|
45
|
+
// output was already emitted.
|
|
46
|
+
else if (this.kind === "incomplete-stream" || this.kind === "empty-body") attach(this, create(Flag.Transient));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Non-2xx response from the Devin API. */
|
|
51
|
+
export class DevinApiError extends ProviderHttpError {
|
|
52
|
+
override readonly name = "DevinApiError";
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Non-2xx response from the GitLab Duo direct-access API. */
|
|
56
|
+
export class GitLabDuoApiError extends ProviderHttpError {
|
|
57
|
+
override readonly name = "GitLabDuoApiError";
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Non-2xx response from the GitLab Duo Workflow API. */
|
|
61
|
+
export class GitLabDuoWorkflowApiError extends ProviderHttpError {
|
|
62
|
+
override readonly name = "GitLabDuoWorkflowApiError";
|
|
63
|
+
}
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rate limit reason classification and backoff calculation utilities.
|
|
3
|
+
* Ported from opencode-antigravity-auth plugin for consistency.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export type RateLimitReason =
|
|
7
|
+
| "QUOTA_EXHAUSTED"
|
|
8
|
+
| "RATE_LIMIT_EXCEEDED"
|
|
9
|
+
| "CONCURRENT_LIMIT"
|
|
10
|
+
| "MODEL_CAPACITY_EXHAUSTED"
|
|
11
|
+
| "SERVER_ERROR"
|
|
12
|
+
| "UNKNOWN";
|
|
13
|
+
|
|
14
|
+
const QUOTA_EXHAUSTED_BACKOFF_MS = 30 * 60 * 1000; // 30 min
|
|
15
|
+
const RATE_LIMIT_EXCEEDED_BACKOFF_MS = 30 * 1000; // 30s
|
|
16
|
+
const CONCURRENT_LIMIT_BACKOFF_MS = 5 * 1000; // 5s
|
|
17
|
+
const MODEL_CAPACITY_BASE_MS = 45 * 1000; // 45s base
|
|
18
|
+
const MODEL_CAPACITY_JITTER_MS = 30 * 1000; // ±15s
|
|
19
|
+
const SERVER_ERROR_BACKOFF_MS = 20 * 1000; // 20s
|
|
20
|
+
|
|
21
|
+
const ACCOUNT_RATE_LIMIT_PATTERN =
|
|
22
|
+
/\baccount(?:'s)?\b[^\n]{0,80}\brate.?limit\b|\brate.?limit\b[^\n]{0,80}\baccount\b/i;
|
|
23
|
+
const INSUFFICIENT_BALANCE_PATTERN = /insufficient.?balance/i;
|
|
24
|
+
const SPEND_LIMIT_PATTERN = /spend.?limit/i;
|
|
25
|
+
const SUBSCRIPTION_CAP_PATTERN =
|
|
26
|
+
/\b(?:subscription|plan|membership)\b[^\n]{0,80}\b(?:rate.?limits?|quota|cap)\b|\b(?:rate.?limits?|quota|cap)\b[^\n]{0,80}\b(?:subscription|plan|membership)\b/i;
|
|
27
|
+
const TRANSIENT_INTERVAL_RATE_LIMIT_PATTERN = /\bper\s+(?:second|minute)\b/i;
|
|
28
|
+
|
|
29
|
+
function matchesSubscriptionCapText(errorMessage: string): boolean {
|
|
30
|
+
return SUBSCRIPTION_CAP_PATTERN.test(errorMessage) && !TRANSIENT_INTERVAL_RATE_LIMIT_PATTERN.test(errorMessage);
|
|
31
|
+
}
|
|
32
|
+
const OPENROUTER_DAILY_FREE_LIMIT_PATTERN = /\bfree[-_ ]models[-_ ]per[-_ ]day\b/i;
|
|
33
|
+
// gRPC/Connect end-streams carry the status as its name (`resource_exhausted`),
|
|
34
|
+
// while HTTP bodies use the phrase ("resource exhausted"). Strip either form
|
|
35
|
+
// before classifying explicit details; an otherwise opaque status is transient
|
|
36
|
+
// model capacity, while quota/rate-limit/server wording remains authoritative.
|
|
37
|
+
const RESOURCE_EXHAUSTED_PATTERN = /resource.?exhausted/gi;
|
|
38
|
+
const CONCURRENT_LIMIT_PATTERN =
|
|
39
|
+
// Require an actual cap signal near "concurrent". "Too many concurrent
|
|
40
|
+
// requests" is itself a cap signal; bare feature rejections such as
|
|
41
|
+
// "concurrent invocation is not supported" remain excluded.
|
|
42
|
+
/\btoo many\s+concurren\w*\s+(?:requests?|invocations?)\b|\bconcurren\w*\b[^\n]{0,60}\b(?:limit|quota|exceed\w*|reach\w*)\b|\b(?:limit|quota|exceed\w*|reach\w*)\b[^\n]{0,60}\bconcurren\w*\b|\bconcurren[a-z]*[-_](?:[a-z]+[_-])*(?:limit|quota|exceed\w*|reach\w*)/i;
|
|
43
|
+
const ACCOUNT_SCOPED_403_PATTERN =
|
|
44
|
+
// The bare "limit will reset" / "will reset in" phrasing also appears on
|
|
45
|
+
// statusless per-minute transients ("Rate limit will reset in 30 seconds"),
|
|
46
|
+
// so gate the reset-window alternative on account-specific wording (Devin's
|
|
47
|
+
// "Your limit will reset in …"); the overall/account qualifiers arm above
|
|
48
|
+
// already covers the rest.
|
|
49
|
+
/\b(?:overall|account|organization|team|workspace)\b[^\n]{0,40}\b(?:message |request )?rate.?limit\b|\byour\b[^\n]{0,30}\b(?:limit )?will reset\b/i;
|
|
50
|
+
// Simplified Chinese account-quota exhaustion phrasing. Zhipu Coding Plan
|
|
51
|
+
// returns e.g. "429 已达到 5 小时的使用上限。您的限额将在 2026-08-06 20:06:00 重置。"
|
|
52
|
+
// (type=1308) when the 5h window is spent; other CN providers use 额度已用完 /
|
|
53
|
+
// 配额已耗尽 / 余额不足. These are persistent account-local caps that must
|
|
54
|
+
// rotate to a sibling credential, not transient rate limits, so they are
|
|
55
|
+
// matched before the RATE_LIMIT_EXCEEDED branch. The 上限 arm is anchored on
|
|
56
|
+
// the 使用 token: a rate/concurrency cap phrased as 每分钟请求数已达上限 /
|
|
57
|
+
// 并发请求数已达上限 / 速率达到上限 (no 使用) must NOT match, or it would burn a
|
|
58
|
+
// healthy sibling credential as a false quota. "速率限制" is absent for the
|
|
59
|
+
// same reason.
|
|
60
|
+
const CN_QUOTA_EXHAUSTED_PATTERN = /使用.{0,30}?上限|(?:额度|配额)已?(?:用|耗)(?:完|尽)|限额.{0,30}重置|余额不足/;
|
|
61
|
+
// Simplified Chinese rate/concurrency caps can contain both 使用 and 上限, but
|
|
62
|
+
// remain transient rather than account quota exhaustion.
|
|
63
|
+
const CN_TRANSIENT_CAP_PATTERN =
|
|
64
|
+
/速率.{0,30}上限|频率.{0,30}上限|每分钟.{0,30}上限|并发.{0,30}上限|使用.{0,30}(?:速率|频率|每分钟|并发).{0,30}上限/;
|
|
65
|
+
// Common Simplified Chinese throttle phrasing. Consulted by
|
|
66
|
+
// isOpaqueStatusBody so CN transients stay in the provider backoff lane instead
|
|
67
|
+
// of rotating through the opaque-429 fallback.
|
|
68
|
+
const CN_THROTTLE_PATTERN = /速率(?:限制|过快)|频率(?:过高|过快)|过于频繁|稍后[重再]试/;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Classify a rate-limit error message into a reason category.
|
|
72
|
+
* Priority order: explicit details in a resource-exhausted error > QUOTA
|
|
73
|
+
* (Antigravity "quota will reset") > CONCURRENT_LIMIT > MODEL_CAPACITY >
|
|
74
|
+
* QUOTA (account) > RATE_LIMIT > QUOTA (generic) > SERVER_ERROR > bare resource-exhausted > UNKNOWN.
|
|
75
|
+
*
|
|
76
|
+
* Bare "resource exhausted" / "resource_exhausted" maps to MODEL_CAPACITY (transient, short wait).
|
|
77
|
+
* Explicit details such as "quota exceeded" retain their normal classification.
|
|
78
|
+
*/
|
|
79
|
+
export function parseRateLimitReason(errorMessage: string): RateLimitReason {
|
|
80
|
+
const lowerWithStatus = errorMessage.toLowerCase();
|
|
81
|
+
const lower = lowerWithStatus.replace(RESOURCE_EXHAUSTED_PATTERN, "");
|
|
82
|
+
const hasResourceExhaustedStatus = lower !== lowerWithStatus;
|
|
83
|
+
|
|
84
|
+
// Antigravity / Cloud Code Assist surface multi-hour daily-quota exhaustion as
|
|
85
|
+
// "You have exhausted your capacity on this model. Your quota will reset after …".
|
|
86
|
+
// The literal "capacity" used to pre-empt the QUOTA branch even though "quota
|
|
87
|
+
// will reset" is the long-wait signal — short-circuit here before the
|
|
88
|
+
// MODEL_CAPACITY fallthrough so credential rotation (not 60s backoff) kicks in.
|
|
89
|
+
if (lower.includes("quota will reset") || lower.includes("exhausted your capacity")) {
|
|
90
|
+
return "QUOTA_EXHAUSTED";
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Simplified Chinese quota-exhaustion phrasing (Zhipu Coding Plan and other
|
|
94
|
+
// CN providers). Must precede the MODEL_CAPACITY / RATE_LIMIT branches so an
|
|
95
|
+
// account-local cap rotates instead of backing off as a transient.
|
|
96
|
+
if (CN_QUOTA_EXHAUSTED_PATTERN.test(errorMessage) && !CN_TRANSIENT_CAP_PATTERN.test(errorMessage)) {
|
|
97
|
+
return "QUOTA_EXHAUSTED";
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (CONCURRENT_LIMIT_PATTERN.test(errorMessage)) {
|
|
101
|
+
return "CONCURRENT_LIMIT";
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (lower.includes("capacity") || lower.includes("overloaded") || lower.includes("529") || lower.includes("503")) {
|
|
105
|
+
return "MODEL_CAPACITY_EXHAUSTED";
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (ACCOUNT_RATE_LIMIT_PATTERN.test(errorMessage)) {
|
|
109
|
+
return "QUOTA_EXHAUSTED";
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (SPEND_LIMIT_PATTERN.test(errorMessage)) {
|
|
113
|
+
return "QUOTA_EXHAUSTED";
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (matchesSubscriptionCapText(errorMessage)) {
|
|
117
|
+
return "QUOTA_EXHAUSTED";
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (OPENROUTER_DAILY_FREE_LIMIT_PATTERN.test(errorMessage)) {
|
|
121
|
+
return "QUOTA_EXHAUSTED";
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (
|
|
125
|
+
lower.includes("per minute") ||
|
|
126
|
+
lower.includes("rate limit") ||
|
|
127
|
+
lower.includes("too many requests") ||
|
|
128
|
+
lower.includes("presque")
|
|
129
|
+
) {
|
|
130
|
+
return "RATE_LIMIT_EXCEEDED";
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (
|
|
134
|
+
lower.includes("exhausted") ||
|
|
135
|
+
lower.includes("quota") ||
|
|
136
|
+
lower.includes("usage limit") ||
|
|
137
|
+
// xAI SuperGrok: HTTP 403 "run out of credits" / spending-limit is an
|
|
138
|
+
// account-local cap — rotate, don't treat as auth failure.
|
|
139
|
+
lower.includes("run out of credits") ||
|
|
140
|
+
lower.includes("out of credits") ||
|
|
141
|
+
lower.includes("spending-limit") ||
|
|
142
|
+
lower.includes("spending limit") ||
|
|
143
|
+
INSUFFICIENT_BALANCE_PATTERN.test(errorMessage)
|
|
144
|
+
) {
|
|
145
|
+
return "QUOTA_EXHAUSTED";
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (lower.includes("500") || lower.includes("internal error") || lower.includes("internal server error")) {
|
|
149
|
+
return "SERVER_ERROR";
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (hasResourceExhaustedStatus) {
|
|
153
|
+
return "MODEL_CAPACITY_EXHAUSTED";
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return "UNKNOWN";
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Calculate backoff delay in ms for a given rate limit reason.
|
|
161
|
+
* MODEL_CAPACITY gets jitter to prevent thundering herd.
|
|
162
|
+
*/
|
|
163
|
+
export function calculateRateLimitBackoffMs(reason: RateLimitReason): number {
|
|
164
|
+
switch (reason) {
|
|
165
|
+
case "QUOTA_EXHAUSTED":
|
|
166
|
+
return QUOTA_EXHAUSTED_BACKOFF_MS;
|
|
167
|
+
case "RATE_LIMIT_EXCEEDED":
|
|
168
|
+
return RATE_LIMIT_EXCEEDED_BACKOFF_MS;
|
|
169
|
+
case "CONCURRENT_LIMIT":
|
|
170
|
+
return CONCURRENT_LIMIT_BACKOFF_MS;
|
|
171
|
+
case "MODEL_CAPACITY_EXHAUSTED":
|
|
172
|
+
return MODEL_CAPACITY_BASE_MS + Math.random() * MODEL_CAPACITY_JITTER_MS;
|
|
173
|
+
case "SERVER_ERROR":
|
|
174
|
+
return SERVER_ERROR_BACKOFF_MS;
|
|
175
|
+
default:
|
|
176
|
+
return QUOTA_EXHAUSTED_BACKOFF_MS; // conservative default
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** Detect usage/quota limit errors in error messages (persistent, requires credential switch). */
|
|
181
|
+
const USAGE_LIMIT_PATTERN =
|
|
182
|
+
/usage.?limit|usage_limit_reached|usage_not_included|limit_reached|quota.?(?:exceeded|reached|insufficient)|额度不足|额度耗尽|resource.?exhausted|exhausted your capacity|quota will reset|insufficient.?(?:balance|quota)|balance.?exhausted|run out of credits|out of credits|spending[- _]?limit|personal-team-blocked/i;
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* HTTP status codes that, absent richer body classification, represent an
|
|
186
|
+
* account-local usage cap rather than a bad credential or a transient blip.
|
|
187
|
+
* HTTP 402 Payment Required is categorically an account-billing cap (xAI
|
|
188
|
+
* Grok Build "usage balance exhausted", DeepSeek "Insufficient Balance",
|
|
189
|
+
* OpenRouter credit exhaustion) — never a transient blip or bad credential.
|
|
190
|
+
* Always combine with {@link isUsageLimitOutcome} when a message is available
|
|
191
|
+
* — a 429 carrying transient rate-limit wording is NOT a usage cap.
|
|
192
|
+
*/
|
|
193
|
+
export function isUsageLimitStatus(status: number | undefined): boolean {
|
|
194
|
+
return status === 429 || status === 402;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Returns true for failures that should burn one credential and rotate to a
|
|
199
|
+
* sibling account. Decision tree:
|
|
200
|
+
*
|
|
201
|
+
* 1. Body matches {@link isUsageLimitError} (Codex `usage_limit_reached`,
|
|
202
|
+
* Anthropic account rate-limit, Google `resource_exhausted`, OpenAI
|
|
203
|
+
* `insufficient_quota`, …) → rotate.
|
|
204
|
+
* 2. Status is not a usage-limit status (429/402) → backoff (caller's domain).
|
|
205
|
+
* 3. Body is absent or {@link isOpaqueStatusBody opaque} (just the status,
|
|
206
|
+
* empty JSON, HTTP framing only) → rotate conservatively: the server
|
|
207
|
+
* gave us nothing else to go on.
|
|
208
|
+
* 4. Body has content → defer to {@link parseRateLimitReason}. `QUOTA_EXHAUSTED`
|
|
209
|
+
* rotates; for the categorical 402 billing cap a `CONCURRENT_LIMIT` body
|
|
210
|
+
* also rotates (the cap is concurrent-worded but the status is still an
|
|
211
|
+
* exhausted billing cap). `RATE_LIMIT_EXCEEDED` (`Too many requests`,
|
|
212
|
+
* per-minute caps), `MODEL_CAPACITY_EXHAUSTED` (`Service overloaded`),
|
|
213
|
+
* `SERVER_ERROR`, and `UNKNOWN` (`Please retry in 5s`) stay in the
|
|
214
|
+
* provider's own backoff layer so transient 429s don't burn sibling
|
|
215
|
+
* credentials.
|
|
216
|
+
*/
|
|
217
|
+
export function isUsageLimitOutcome(status: number | undefined, message: string | undefined): boolean {
|
|
218
|
+
// Concurrency caps are shed-and-backoff, not credential-rotatable — but only
|
|
219
|
+
// for quota-worded 429 / other statuses. HTTP 402 is categorically an
|
|
220
|
+
// account-billing cap, so a 402 whose body happens to mention concurrency is
|
|
221
|
+
// still an exhausted billing cap and must rotate; gate the exclusion on the
|
|
222
|
+
// status not being that categorical billing cap.
|
|
223
|
+
const isBillingCapStatus = status === 402;
|
|
224
|
+
if (isConcurrencyCapExclusion(status, message)) return false;
|
|
225
|
+
if (message && matchesUsageLimitText(message)) return true;
|
|
226
|
+
// A 403 is normally an auth failure, but several providers deliver an
|
|
227
|
+
// account-scoped cap with it (Devin/Codeium Connect `permission_denied`,
|
|
228
|
+
// GitHub Copilot). Devin's end-of-stream Connect trailer carries no HTTP
|
|
229
|
+
// status at all (it arrives as a `permission_denied` ValidationError), so
|
|
230
|
+
// accept an undefined status too — but only when the body names a cap that
|
|
231
|
+
// resets, never on a bare 403, which stays an auth failure.
|
|
232
|
+
if ((status === 403 || status === undefined) && message && isAccountScopedCapText(message)) return true;
|
|
233
|
+
if (!isUsageLimitStatus(status)) return false;
|
|
234
|
+
if (!message || isOpaqueStatusBody(message)) return true;
|
|
235
|
+
const reason = parseRateLimitReason(message);
|
|
236
|
+
// For the categorical 402 billing cap a concurrency-worded body is still an
|
|
237
|
+
// exhausted cap (rotate); for 429 / other only QUOTA_EXHAUSTED rotates.
|
|
238
|
+
return reason === "QUOTA_EXHAUSTED" || (isBillingCapStatus && reason === "CONCURRENT_LIMIT");
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* A usage-limit status body is opaque when it carries no signal beyond the
|
|
243
|
+
* status itself — empty, whitespace-only, the status digits with HTTP/JSON
|
|
244
|
+
* framing, or generic punctuation. Anything else (retry hints, capacity
|
|
245
|
+
* wording, error descriptions) is informative enough to defer to the
|
|
246
|
+
* classifier.
|
|
247
|
+
*/
|
|
248
|
+
export function isOpaqueStatusBody(message: string): boolean {
|
|
249
|
+
const cleaned = message
|
|
250
|
+
.replace(/\b(?:429|402)\b/g, "")
|
|
251
|
+
.replace(/\b(?:http|https|status|error|code|response|message)\b/gi, "");
|
|
252
|
+
// A body is informative when the text classifier can act on it. Any Latin
|
|
253
|
+
// word or Simplified Chinese phrasing the classifier recognizes (quota
|
|
254
|
+
// exhaustion or a throttle) defers to parseRateLimitReason; a body that
|
|
255
|
+
// is only status digits / HTTP framing is opaque and rotates conservatively.
|
|
256
|
+
// A Han-only body the classifier cannot interpret (e.g. Japanese Kanji
|
|
257
|
+
// quota text, since Japanese is out of scope) must stay opaque so the
|
|
258
|
+
// opaque-429 fallback still rotates. This keeps the exception scoped to
|
|
259
|
+
// text we actually classify, rather than to any Han ideograph.
|
|
260
|
+
return (
|
|
261
|
+
!/[a-z\d]{3,}/i.test(cleaned) &&
|
|
262
|
+
!CN_QUOTA_EXHAUSTED_PATTERN.test(cleaned) &&
|
|
263
|
+
!CN_TRANSIENT_CAP_PATTERN.test(cleaned) &&
|
|
264
|
+
!CN_THROTTLE_PATTERN.test(cleaned)
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Internal text matcher for usage/quota-limit phrasing. NOT part of the public
|
|
270
|
+
* API — callers classify through {@link import("./flags").isUsageLimit} (the
|
|
271
|
+
* flag accessor). `flags.ts` consumes this to populate `Flag.UsageLimit`, and
|
|
272
|
+
* {@link isUsageLimitOutcome} uses it for the account-rotation decision.
|
|
273
|
+
*/
|
|
274
|
+
export function matchesUsageLimitText(errorMessage: string): boolean {
|
|
275
|
+
return (
|
|
276
|
+
USAGE_LIMIT_PATTERN.test(errorMessage) ||
|
|
277
|
+
(CN_QUOTA_EXHAUSTED_PATTERN.test(errorMessage) && !CN_TRANSIENT_CAP_PATTERN.test(errorMessage)) ||
|
|
278
|
+
SPEND_LIMIT_PATTERN.test(errorMessage) ||
|
|
279
|
+
ACCOUNT_RATE_LIMIT_PATTERN.test(errorMessage) ||
|
|
280
|
+
matchesSubscriptionCapText(errorMessage) ||
|
|
281
|
+
OPENROUTER_DAILY_FREE_LIMIT_PATTERN.test(errorMessage)
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Account-scoped cap phrasing delivered on a 403 (or a statusless Connect
|
|
287
|
+
* trailer): "Reached overall message rate limit", "Your limit will reset in …".
|
|
288
|
+
* Kept separate from {@link matchesUsageLimitText} because the bare wording is
|
|
289
|
+
* ambiguous without the 403 / statusless-account context; consumed by both
|
|
290
|
+
* {@link isUsageLimitOutcome} (rotation decision) and `flags.ts` (Flag.UsageLimit).
|
|
291
|
+
*/
|
|
292
|
+
export function isAccountScopedCapText(message: string): boolean {
|
|
293
|
+
return ACCOUNT_SCOPED_403_PATTERN.test(message);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* A concurrency cap on a non-billing status is shed-and-backoff, not
|
|
298
|
+
* credential-rotatable. This mirrors the exclusion in {@link isUsageLimitOutcome}
|
|
299
|
+
* for the 403 auth-retry entry points. A 402 remains a categorical billing cap.
|
|
300
|
+
*/
|
|
301
|
+
export function isConcurrencyCapExclusion(status: number | undefined, message: string | undefined): boolean {
|
|
302
|
+
return message !== undefined && parseRateLimitReason(message) === "CONCURRENT_LIMIT" && status !== 402;
|
|
303
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { isRetryableError, isUnexpectedSocketCloseMessage } from "@linxiraos/pi-utils";
|
|
2
|
+
import {
|
|
3
|
+
isRetryableStreamEnvelopeError,
|
|
4
|
+
isTransientStreamParseError,
|
|
5
|
+
isUsageLimit,
|
|
6
|
+
status,
|
|
7
|
+
TRANSIENT_TRANSPORT_PATTERN,
|
|
8
|
+
} from "./flags";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Whether a numeric HTTP status is in the canonical transient/retryable set:
|
|
12
|
+
* 408 (Request Timeout), 429 (Too Many Requests), and any 5xx.
|
|
13
|
+
*
|
|
14
|
+
* This is a pure predicate over a status code already in hand — distinct from
|
|
15
|
+
* {@link classify}, which inspects a whole error (including message text) and
|
|
16
|
+
* may match more. Use this when you only have a `status: number`.
|
|
17
|
+
*/
|
|
18
|
+
export function isTransientStatus(status: number | undefined): boolean {
|
|
19
|
+
return status !== undefined && (status === 408 || status === 429 || status >= 500);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Provider-stream transient phrasings not covered by the shared
|
|
23
|
+
// TRANSIENT_TRANSPORT_PATTERN (TLS record corruption, HTTP/2 peer stream
|
|
24
|
+
// errors, upstream code 1302). The shared pattern already covers rate-limit /
|
|
25
|
+
// overloaded / 5xx / timeout / first-event wording.
|
|
26
|
+
const PROVIDER_TRANSIENT_EXTRA_PATTERN = /bad record mac|stream error.*received from peer|1302/i;
|
|
27
|
+
|
|
28
|
+
function isTransientTransportMessage(message: string): boolean {
|
|
29
|
+
return message.includes("tls: bad record mac") || message.includes("type=server_error");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Hook for provider-specific transient detection that the error module must not import directly. */
|
|
33
|
+
export interface ProviderRetryableHooks {
|
|
34
|
+
/** Provider id of the failing request, used to gate provider-specific checks. */
|
|
35
|
+
provider?: string;
|
|
36
|
+
/** Provider-specific transient predicate (e.g. Copilot model-availability 400s). */
|
|
37
|
+
isProviderTransient?: (error: Error) => boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Whether a provider stream error should be retried against the same credential.
|
|
42
|
+
*
|
|
43
|
+
* Account-level usage/quota limits are deliberately treated as **non**-retryable
|
|
44
|
+
* here — they are owned by the credential-rotation layer (auth-gateway /
|
|
45
|
+
* `streamSimple` a/b/c policy), not this seconds-scale provider backoff.
|
|
46
|
+
*
|
|
47
|
+
* Provider-specific transient cases are injected via {@link ProviderRetryableHooks}
|
|
48
|
+
* so this stays free of provider imports.
|
|
49
|
+
*/
|
|
50
|
+
export function isProviderRetryableError(error: unknown, hooks: ProviderRetryableHooks = {}): boolean {
|
|
51
|
+
if (!(error instanceof Error)) return false;
|
|
52
|
+
if (hooks.isProviderTransient?.(error)) return true;
|
|
53
|
+
if (isUsageLimit(error)) return false;
|
|
54
|
+
const httpStatus = status(error);
|
|
55
|
+
if (httpStatus !== undefined && httpStatus >= 400 && httpStatus < 500 && httpStatus !== 408 && httpStatus !== 429) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
const msg = error.message.toLowerCase();
|
|
59
|
+
if (
|
|
60
|
+
isUnexpectedSocketCloseMessage(msg) ||
|
|
61
|
+
isTransientTransportMessage(msg) ||
|
|
62
|
+
TRANSIENT_TRANSPORT_PATTERN.test(msg) ||
|
|
63
|
+
PROVIDER_TRANSIENT_EXTRA_PATTERN.test(msg) ||
|
|
64
|
+
isTransientStreamParseError(error) ||
|
|
65
|
+
isRetryableStreamEnvelopeError(error)
|
|
66
|
+
) {
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
return isRetryableError(error);
|
|
70
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { attach, create, Flag } from "./flags";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Caller-supplied input failed validation before/while building a provider
|
|
5
|
+
* request: bad request body, malformed tool arguments, unsupported content
|
|
6
|
+
* type, a schema that cannot be normalized, an unknown tool, etc.
|
|
7
|
+
*
|
|
8
|
+
* This is a programmer/config/contract error, not a transient provider fault —
|
|
9
|
+
* it is never retried.
|
|
10
|
+
*/
|
|
11
|
+
export class ValidationError extends Error {
|
|
12
|
+
constructor(message: string, options?: { cause?: unknown }) {
|
|
13
|
+
super(message, options?.cause === undefined ? undefined : { cause: options.cause });
|
|
14
|
+
this.name = "ValidationError";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** A referenced tool was not found in the active tool set. */
|
|
19
|
+
export class ToolNotFoundError extends ValidationError {
|
|
20
|
+
constructor(toolName: string) {
|
|
21
|
+
super(`Tool "${toolName}" not found`);
|
|
22
|
+
this.name = "ToolNotFoundError";
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Provider/auth configuration was missing or malformed (env var pointing at a
|
|
28
|
+
* missing file, missing projectId, bad bind string, mTLS half-configured, …).
|
|
29
|
+
*/
|
|
30
|
+
export class ConfigurationError extends Error {
|
|
31
|
+
constructor(message: string, options?: { cause?: unknown }) {
|
|
32
|
+
super(message, options?.cause === undefined ? undefined : { cause: options.cause });
|
|
33
|
+
this.name = "ConfigurationError";
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** A request was abandoned because it exceeded a stream/idle/first-event deadline. */
|
|
38
|
+
export class StreamTimeoutError extends Error {
|
|
39
|
+
constructor(message = "Request timed out.", options?: { cause?: unknown }) {
|
|
40
|
+
super(message, options?.cause === undefined ? undefined : { cause: options.cause });
|
|
41
|
+
this.name = "StreamTimeoutError";
|
|
42
|
+
attach(this, create(Flag.Transient, Flag.Timeout));
|
|
43
|
+
}
|
|
44
|
+
}
|