@linxiraos/pi-ai 1.0.0 → 1.0.2
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 +123 -30
- package/package.json +135 -136
- package/src/auth/sqlite-credential-store.ts +0 -56
- package/src/auth-retry.ts +5 -4
- package/src/auth-storage.ts +122 -112
- package/src/dialect/owned-stream.ts +3 -0
- package/src/error/auth-classify.ts +7 -6
- package/src/error/aws.ts +5 -1
- package/src/error/flags.ts +25 -1
- package/src/error/provider.ts +6 -5
- package/src/error/rate-limit.ts +71 -1
- package/src/providers/amazon-bedrock.ts +38 -0
- package/src/providers/anthropic-client.ts +6 -10
- package/src/providers/anthropic.ts +107 -259
- package/src/providers/aws-credentials.ts +222 -29
- package/src/providers/azure-openai-responses.ts +1 -0
- package/src/providers/cursor/exec-modern.ts +1 -0
- package/src/providers/cursor-pi-args.ts +22 -0
- package/src/providers/cursor.ts +81 -1
- package/src/providers/devin.ts +3 -3
- package/src/providers/google-antigravity-forced-tool.md +1 -0
- package/src/providers/google-gemini-cli.ts +83 -40
- package/src/providers/google-shared.ts +22 -8
- package/src/providers/ollama.ts +15 -3
- package/src/providers/openai-codex/request-transformer.ts +47 -21
- package/src/providers/openai-codex-responses.ts +3 -3
- package/src/providers/openai-reasoning-fallback.ts +21 -8
- package/src/providers/openai-responses.ts +77 -12
- package/src/providers/openai-shared.ts +114 -15
- package/src/providers/pi-native-server.ts +1 -0
- package/src/providers/register-builtins.ts +18 -14
- package/src/providers/vision-guard.ts +15 -3
- package/src/registry/amazon-bedrock.ts +9 -14
- package/src/registry/api-key-login.ts +26 -12
- package/src/registry/aws.ts +16 -8
- package/src/registry/cloudflare-ai-gateway.ts +10 -29
- package/src/registry/kagi.ts +11 -29
- package/src/registry/litellm.ts +11 -29
- package/src/registry/llama-cpp.ts +11 -21
- package/src/registry/lm-studio.ts +9 -20
- package/src/registry/oauth/callback-server.ts +93 -5
- package/src/registry/oauth/google-antigravity.ts +99 -44
- package/src/registry/oauth/perplexity.ts +32 -4
- package/src/registry/parallel.ts +10 -28
- package/src/registry/tavily.ts +9 -27
- package/src/registry/together.ts +7 -3
- package/src/registry/vercel-ai-gateway.ts +10 -28
- package/src/registry/vllm.ts +11 -21
- package/src/stream.ts +459 -53
- package/src/types.ts +23 -0
- package/src/usage/alibaba-token-plan.ts +8 -15
- package/src/usage/claude.ts +12 -19
- package/src/usage/cursor.ts +179 -59
- package/src/usage/gemini.ts +4 -3
- package/src/usage/github-copilot.ts +3 -2
- package/src/usage/google-antigravity.ts +10 -17
- package/src/usage/kimi.ts +35 -17
- package/src/usage/minimax-code.ts +7 -27
- package/src/usage/openai-codex-reset.ts +3 -2
- package/src/usage/openai-codex.ts +5 -5
- package/src/usage/opencode-go.ts +170 -60
- package/src/usage/shared.ts +33 -10
- package/src/usage/synthetic.ts +4 -11
- package/src/usage/umans.ts +2 -2
- package/src/usage/xai-oauth.ts +62 -34
- package/src/usage/zai.ts +4 -6
- package/src/usage.ts +0 -20
- package/src/utils/aws-profile.ts +39 -1
- package/src/utils/block-symbols.ts +18 -0
- package/src/utils/empty-completion-retry.ts +4 -1
- package/src/utils/leaked-thinking-stream.ts +3 -0
- package/src/utils/openrouter-headers.ts +4 -4
- package/src/utils/thinking-loop.ts +17 -38
- package/dist/types/api-registry.d.ts +0 -30
- package/dist/types/auth/sqlite-credential-store.d.ts +0 -117
- package/dist/types/auth-broker/client.d.ts +0 -93
- package/dist/types/auth-broker/discover.d.ts +0 -39
- package/dist/types/auth-broker/index.d.ts +0 -7
- package/dist/types/auth-broker/refresher.d.ts +0 -25
- package/dist/types/auth-broker/remote-store.d.ts +0 -136
- package/dist/types/auth-broker/server.d.ts +0 -48
- package/dist/types/auth-broker/snapshot-cache.d.ts +0 -17
- package/dist/types/auth-broker/types.d.ts +0 -152
- package/dist/types/auth-broker/wire-schema-resource.d.ts +0 -53
- package/dist/types/auth-broker/wire-schemas.d.ts +0 -5
- package/dist/types/auth-gateway/http.d.ts +0 -56
- package/dist/types/auth-gateway/index.d.ts +0 -3
- package/dist/types/auth-gateway/server.d.ts +0 -36
- package/dist/types/auth-gateway/types.d.ts +0 -127
- package/dist/types/auth-retry.d.ts +0 -150
- package/dist/types/auth-storage.d.ts +0 -1258
- package/dist/types/dialect/anthropic.d.ts +0 -15
- package/dist/types/dialect/catalog.d.ts +0 -3
- package/dist/types/dialect/coercion.d.ts +0 -23
- package/dist/types/dialect/deepseek.d.ts +0 -14
- package/dist/types/dialect/demotion.d.ts +0 -30
- package/dist/types/dialect/examples.d.ts +0 -18
- package/dist/types/dialect/factory.d.ts +0 -3
- package/dist/types/dialect/fenced-thinking.d.ts +0 -53
- package/dist/types/dialect/gemini.d.ts +0 -17
- package/dist/types/dialect/gemma.d.ts +0 -15
- package/dist/types/dialect/glm.d.ts +0 -9
- package/dist/types/dialect/harmony.d.ts +0 -8
- package/dist/types/dialect/hermes.d.ts +0 -9
- package/dist/types/dialect/history.d.ts +0 -3
- package/dist/types/dialect/index.d.ts +0 -11
- package/dist/types/dialect/inventory.d.ts +0 -9
- package/dist/types/dialect/kimi.d.ts +0 -14
- package/dist/types/dialect/minimax.d.ts +0 -3
- package/dist/types/dialect/owned-stream.d.ts +0 -4
- package/dist/types/dialect/qwen3.d.ts +0 -9
- package/dist/types/dialect/rendering.d.ts +0 -54
- package/dist/types/dialect/thinking.d.ts +0 -6
- package/dist/types/dialect/types.d.ts +0 -68
- package/dist/types/dialect/xml.d.ts +0 -9
- package/dist/types/error/abort.d.ts +0 -14
- package/dist/types/error/auth-classify.d.ts +0 -20
- package/dist/types/error/auth.d.ts +0 -27
- package/dist/types/error/aws.d.ts +0 -27
- package/dist/types/error/classes.d.ts +0 -106
- package/dist/types/error/finalize.d.ts +0 -39
- package/dist/types/error/flags.d.ts +0 -94
- package/dist/types/error/format.d.ts +0 -20
- package/dist/types/error/gateway.d.ts +0 -20
- package/dist/types/error/index.d.ts +0 -14
- package/dist/types/error/oauth.d.ts +0 -43
- package/dist/types/error/provider.d.ts +0 -42
- package/dist/types/error/rate-limit.d.ts +0 -80
- package/dist/types/error/retryable.d.ts +0 -27
- package/dist/types/error/validation.d.ts +0 -32
- package/dist/types/index.d.ts +0 -51
- package/dist/types/provider-details.d.ts +0 -24
- package/dist/types/providers/amazon-bedrock.d.ts +0 -39
- package/dist/types/providers/anthropic-client.d.ts +0 -106
- package/dist/types/providers/anthropic-messages-server-schema.d.ts +0 -937
- package/dist/types/providers/anthropic-messages-server.d.ts +0 -17
- package/dist/types/providers/anthropic-wire.d.ts +0 -345
- package/dist/types/providers/anthropic.d.ts +0 -261
- package/dist/types/providers/aws-credentials.d.ts +0 -48
- package/dist/types/providers/aws-eventstream.d.ts +0 -39
- package/dist/types/providers/aws-sigv4.d.ts +0 -55
- package/dist/types/providers/azure-openai-responses.d.ts +0 -16
- package/dist/types/providers/bedrock-mantle.d.ts +0 -13
- package/dist/types/providers/claude-code-fingerprint.d.ts +0 -19
- package/dist/types/providers/cowork-fetch.d.ts +0 -3
- package/dist/types/providers/cursor/exec-modern.d.ts +0 -98
- package/dist/types/providers/cursor-pi-args.d.ts +0 -105
- package/dist/types/providers/cursor.d.ts +0 -221
- package/dist/types/providers/devin.d.ts +0 -12
- package/dist/types/providers/error-message.d.ts +0 -25
- package/dist/types/providers/github-copilot-headers.d.ts +0 -40
- package/dist/types/providers/gitlab-duo-workflow.d.ts +0 -254
- package/dist/types/providers/gitlab-duo.d.ts +0 -27
- package/dist/types/providers/google-auth.d.ts +0 -24
- package/dist/types/providers/google-gemini-cli.d.ts +0 -120
- package/dist/types/providers/google-shared.d.ts +0 -203
- package/dist/types/providers/google-types.d.ts +0 -155
- package/dist/types/providers/google-vertex.d.ts +0 -7
- package/dist/types/providers/google.d.ts +0 -4
- package/dist/types/providers/grammar.d.ts +0 -1
- package/dist/types/providers/kimi.d.ts +0 -27
- package/dist/types/providers/mock.d.ts +0 -179
- package/dist/types/providers/ollama.d.ts +0 -8
- package/dist/types/providers/openai-anthropic-shim.d.ts +0 -35
- package/dist/types/providers/openai-chat-server-schema.d.ts +0 -1311
- package/dist/types/providers/openai-chat-server.d.ts +0 -16
- package/dist/types/providers/openai-chat-wire.d.ts +0 -669
- package/dist/types/providers/openai-codex/request-transformer.d.ts +0 -109
- package/dist/types/providers/openai-codex/response-handler.d.ts +0 -26
- package/dist/types/providers/openai-codex-responses.d.ts +0 -238
- package/dist/types/providers/openai-completions.d.ts +0 -48
- package/dist/types/providers/openai-reasoning-fallback.d.ts +0 -25
- package/dist/types/providers/openai-responses-server-schema.d.ts +0 -1314
- package/dist/types/providers/openai-responses-server.d.ts +0 -17
- package/dist/types/providers/openai-responses-wire.d.ts +0 -6099
- package/dist/types/providers/openai-responses.d.ts +0 -131
- package/dist/types/providers/openai-shared.d.ts +0 -624
- package/dist/types/providers/pi-native-client.d.ts +0 -13
- package/dist/types/providers/pi-native-server.d.ts +0 -69
- package/dist/types/providers/register-builtins.d.ts +0 -37
- package/dist/types/providers/synthetic.d.ts +0 -26
- package/dist/types/providers/transform-messages.d.ts +0 -32
- package/dist/types/providers/vision-guard.d.ts +0 -20
- package/dist/types/registry/aiand.d.ts +0 -7
- package/dist/types/registry/aimlapi.d.ts +0 -4
- package/dist/types/registry/alibaba-coding-plan.d.ts +0 -8
- package/dist/types/registry/alibaba-token-plan.d.ts +0 -18
- package/dist/types/registry/amazon-bedrock.d.ts +0 -5
- package/dist/types/registry/anthropic.d.ts +0 -10
- package/dist/types/registry/api-key-login.d.ts +0 -42
- package/dist/types/registry/api-key-validation.d.ts +0 -43
- package/dist/types/registry/aws.d.ts +0 -13
- package/dist/types/registry/azure.d.ts +0 -4
- package/dist/types/registry/baseten.d.ts +0 -7
- package/dist/types/registry/bedrock-mantle.d.ts +0 -22
- package/dist/types/registry/cerebras.d.ts +0 -7
- package/dist/types/registry/cloudflare-ai-gateway.d.ts +0 -13
- package/dist/types/registry/coreweave.d.ts +0 -7
- package/dist/types/registry/cursor.d.ts +0 -7
- package/dist/types/registry/deepseek.d.ts +0 -8
- package/dist/types/registry/derived.d.ts +0 -5
- package/dist/types/registry/devin.d.ts +0 -8
- package/dist/types/registry/exa.d.ts +0 -8
- package/dist/types/registry/firepass.d.ts +0 -16
- package/dist/types/registry/fireworks.d.ts +0 -7
- package/dist/types/registry/github-copilot.d.ts +0 -7
- package/dist/types/registry/gitlab-duo-workflow.d.ts +0 -10
- package/dist/types/registry/gitlab-duo.d.ts +0 -9
- package/dist/types/registry/gmi-cloud.d.ts +0 -7
- package/dist/types/registry/google-antigravity.d.ts +0 -9
- package/dist/types/registry/google-gemini-cli.d.ts +0 -9
- package/dist/types/registry/google-vertex.d.ts +0 -5
- package/dist/types/registry/google.d.ts +0 -4
- package/dist/types/registry/groq.d.ts +0 -4
- package/dist/types/registry/huggingface.d.ts +0 -7
- package/dist/types/registry/index.d.ts +0 -4
- package/dist/types/registry/kagi.d.ts +0 -14
- package/dist/types/registry/kilo.d.ts +0 -7
- package/dist/types/registry/kimi-code.d.ts +0 -7
- package/dist/types/registry/litellm.d.ts +0 -13
- package/dist/types/registry/llama-cpp.d.ts +0 -8
- package/dist/types/registry/lm-studio.d.ts +0 -8
- package/dist/types/registry/meta.d.ts +0 -7
- package/dist/types/registry/minimax-code-cn.d.ts +0 -6
- package/dist/types/registry/minimax-code.d.ts +0 -6
- package/dist/types/registry/minimax.d.ts +0 -4
- package/dist/types/registry/mistral.d.ts +0 -4
- package/dist/types/registry/moonshot.d.ts +0 -7
- package/dist/types/registry/nanogpt.d.ts +0 -7
- package/dist/types/registry/novita.d.ts +0 -6
- package/dist/types/registry/nvidia.d.ts +0 -7
- package/dist/types/registry/oauth/anthropic-constants.d.ts +0 -12
- package/dist/types/registry/oauth/anthropic.d.ts +0 -24
- package/dist/types/registry/oauth/callback-server.d.ts +0 -74
- package/dist/types/registry/oauth/cursor.d.ts +0 -16
- package/dist/types/registry/oauth/device-code.d.ts +0 -25
- package/dist/types/registry/oauth/devin.d.ts +0 -5
- package/dist/types/registry/oauth/github-copilot.d.ts +0 -30
- package/dist/types/registry/oauth/gitlab-duo-workflow.d.ts +0 -6
- package/dist/types/registry/oauth/gitlab-duo.d.ts +0 -3
- package/dist/types/registry/oauth/google-antigravity.d.ts +0 -11
- package/dist/types/registry/oauth/google-gemini-cli.d.ts +0 -22
- package/dist/types/registry/oauth/google-oauth-shared.d.ts +0 -56
- package/dist/types/registry/oauth/index.d.ts +0 -45
- package/dist/types/registry/oauth/kimi.d.ts +0 -21
- package/dist/types/registry/oauth/minimax-code.d.ts +0 -27
- package/dist/types/registry/oauth/openai-codex.d.ts +0 -33
- package/dist/types/registry/oauth/opencode.d.ts +0 -18
- package/dist/types/registry/oauth/perplexity.d.ts +0 -9
- package/dist/types/registry/oauth/pkce.d.ts +0 -8
- package/dist/types/registry/oauth/types.d.ts +0 -87
- package/dist/types/registry/oauth/wafer.d.ts +0 -1
- package/dist/types/registry/oauth/xai-oauth.d.ts +0 -46
- package/dist/types/registry/oauth/xiaomi.d.ts +0 -25
- package/dist/types/registry/oauth/zai.d.ts +0 -25
- package/dist/types/registry/ollama-cloud.d.ts +0 -7
- package/dist/types/registry/ollama.d.ts +0 -12
- package/dist/types/registry/openai-codex-device.d.ts +0 -8
- package/dist/types/registry/openai-codex.d.ts +0 -9
- package/dist/types/registry/openai.d.ts +0 -4
- package/dist/types/registry/opencode-go.d.ts +0 -6
- package/dist/types/registry/opencode-zen.d.ts +0 -6
- package/dist/types/registry/openrouter.d.ts +0 -13
- package/dist/types/registry/parallel.d.ts +0 -14
- package/dist/types/registry/perplexity.d.ts +0 -7
- package/dist/types/registry/qianfan.d.ts +0 -7
- package/dist/types/registry/qwen-portal.d.ts +0 -7
- package/dist/types/registry/registry.d.ts +0 -367
- package/dist/types/registry/sakana.d.ts +0 -7
- package/dist/types/registry/siliconflow-cn.d.ts +0 -7
- package/dist/types/registry/siliconflow.d.ts +0 -7
- package/dist/types/registry/synthetic.d.ts +0 -6
- package/dist/types/registry/tavily.d.ts +0 -14
- package/dist/types/registry/together.d.ts +0 -6
- package/dist/types/registry/types.d.ts +0 -75
- package/dist/types/registry/umans.d.ts +0 -7
- package/dist/types/registry/venice.d.ts +0 -13
- package/dist/types/registry/vercel-ai-gateway.d.ts +0 -7
- package/dist/types/registry/vllm.d.ts +0 -7
- package/dist/types/registry/wafer-serverless.d.ts +0 -6
- package/dist/types/registry/xai-oauth.d.ts +0 -7
- package/dist/types/registry/xai.d.ts +0 -7
- package/dist/types/registry/xiaomi-token-plan-ams.d.ts +0 -6
- package/dist/types/registry/xiaomi-token-plan-cn.d.ts +0 -6
- package/dist/types/registry/xiaomi-token-plan-sgp.d.ts +0 -6
- package/dist/types/registry/xiaomi.d.ts +0 -6
- package/dist/types/registry/zai.d.ts +0 -15
- package/dist/types/registry/zenmux.d.ts +0 -7
- package/dist/types/registry/zhipu-coding-plan.d.ts +0 -7
- package/dist/types/stream.d.ts +0 -46
- package/dist/types/types.d.ts +0 -1064
- package/dist/types/usage/alibaba-token-plan.d.ts +0 -3
- package/dist/types/usage/claude.d.ts +0 -4
- package/dist/types/usage/cursor.d.ts +0 -4
- package/dist/types/usage/gemini.d.ts +0 -2
- package/dist/types/usage/github-copilot.d.ts +0 -7
- package/dist/types/usage/google-antigravity.d.ts +0 -15
- package/dist/types/usage/kimi.d.ts +0 -2
- package/dist/types/usage/minimax-code.d.ts +0 -3
- package/dist/types/usage/ollama.d.ts +0 -5
- package/dist/types/usage/openai-codex-base-url.d.ts +0 -18
- package/dist/types/usage/openai-codex-reset.d.ts +0 -88
- package/dist/types/usage/openai-codex.d.ts +0 -10
- package/dist/types/usage/opencode-go.d.ts +0 -2
- package/dist/types/usage/shared.d.ts +0 -1
- package/dist/types/usage/synthetic.d.ts +0 -2
- package/dist/types/usage/umans.d.ts +0 -2
- package/dist/types/usage/xai-oauth.d.ts +0 -12
- package/dist/types/usage/zai.d.ts +0 -3
- package/dist/types/usage.d.ts +0 -527
- package/dist/types/utils/abort.d.ts +0 -25
- package/dist/types/utils/anthropic-auth.d.ts +0 -35
- package/dist/types/utils/aws-profile.d.ts +0 -17
- package/dist/types/utils/block-symbols.d.ts +0 -62
- package/dist/types/utils/deterministic-id.d.ts +0 -16
- package/dist/types/utils/empty-completion-retry.d.ts +0 -21
- package/dist/types/utils/event-stream.d.ts +0 -39
- package/dist/types/utils/foundry.d.ts +0 -1
- package/dist/types/utils/google-validation.d.ts +0 -2
- package/dist/types/utils/harmony-leak.d.ts +0 -135
- package/dist/types/utils/http-inspector.d.ts +0 -49
- package/dist/types/utils/idle-iterator.d.ts +0 -149
- package/dist/types/utils/leaked-thinking-stream.d.ts +0 -33
- package/dist/types/utils/openai-http.d.ts +0 -48
- package/dist/types/utils/openrouter-headers.d.ts +0 -1
- package/dist/types/utils/parse-bind.d.ts +0 -23
- package/dist/types/utils/provider-response.d.ts +0 -3
- package/dist/types/utils/proxy.d.ts +0 -39
- package/dist/types/utils/request-debug.d.ts +0 -29
- package/dist/types/utils/retry-after.d.ts +0 -4
- package/dist/types/utils/retry.d.ts +0 -14
- package/dist/types/utils/schema/adapt.d.ts +0 -24
- package/dist/types/utils/schema/compatibility.d.ts +0 -30
- package/dist/types/utils/schema/dereference.d.ts +0 -11
- package/dist/types/utils/schema/draft.d.ts +0 -10
- package/dist/types/utils/schema/equality.d.ts +0 -4
- package/dist/types/utils/schema/fields.d.ts +0 -54
- package/dist/types/utils/schema/index.d.ts +0 -14
- package/dist/types/utils/schema/json-schema-validator.d.ts +0 -20
- package/dist/types/utils/schema/meta-validator.d.ts +0 -2
- package/dist/types/utils/schema/normalize.d.ts +0 -153
- package/dist/types/utils/schema/spill.d.ts +0 -8
- package/dist/types/utils/schema/stamps.d.ts +0 -17
- package/dist/types/utils/schema/strict-tool-validation.d.ts +0 -16
- package/dist/types/utils/schema/types.d.ts +0 -4
- package/dist/types/utils/schema/typescript.d.ts +0 -24
- package/dist/types/utils/schema/wire.d.ts +0 -52
- package/dist/types/utils/sdk-stream-timeout.d.ts +0 -33
- package/dist/types/utils/sse-debug.d.ts +0 -5
- package/dist/types/utils/stream-markup-healing.d.ts +0 -87
- package/dist/types/utils/thinking-loop.d.ts +0 -102
- package/dist/types/utils/tool-call-loop-guard.d.ts +0 -26
- package/dist/types/utils/tool-choice.d.ts +0 -52
- package/dist/types/utils/validation.d.ts +0 -28
- package/dist/types/utils.d.ts +0 -57
package/src/auth-storage.ts
CHANGED
|
@@ -36,8 +36,6 @@ import type {
|
|
|
36
36
|
CredentialRankingContext,
|
|
37
37
|
CredentialRankingStrategy,
|
|
38
38
|
ObservedUsageEntry,
|
|
39
|
-
UsageCostHistoryEntry,
|
|
40
|
-
UsageCostHistoryQuery,
|
|
41
39
|
UsageCredential,
|
|
42
40
|
UsageFetchContext,
|
|
43
41
|
UsageFetchParams,
|
|
@@ -66,7 +64,7 @@ import {
|
|
|
66
64
|
listCodexResetCredits,
|
|
67
65
|
pickSoonestExpiringCredit,
|
|
68
66
|
} from "./usage/openai-codex-reset";
|
|
69
|
-
import { opencodeGoUsageProvider } from "./usage/opencode-go";
|
|
67
|
+
import { opencodeGoRankingStrategy, opencodeGoUsageProvider } from "./usage/opencode-go";
|
|
70
68
|
import { syntheticUsageProvider } from "./usage/synthetic";
|
|
71
69
|
import { umansUsageProvider } from "./usage/umans";
|
|
72
70
|
import { xaiOauthUsageProvider } from "./usage/xai-oauth";
|
|
@@ -454,10 +452,6 @@ export interface AuthCredentialStore {
|
|
|
454
452
|
* skipped — the broker host records into its own database instead.
|
|
455
453
|
*/
|
|
456
454
|
recordUsageSnapshots?(entries: UsageHistoryEntry[]): void;
|
|
457
|
-
/** Append observed request costs for providers without upstream usage APIs. */
|
|
458
|
-
recordUsageCosts?(entries: UsageCostHistoryEntry[]): void;
|
|
459
|
-
/** Read observed request costs, oldest first. */
|
|
460
|
-
listUsageCosts?(query?: UsageCostHistoryQuery): UsageCostHistoryEntry[];
|
|
461
455
|
/** Read recorded usage-limit snapshots, oldest first. */
|
|
462
456
|
listUsageHistory?(query?: UsageHistoryQuery): UsageHistoryEntry[];
|
|
463
457
|
/**
|
|
@@ -625,7 +619,7 @@ export type AuthStorageOptions = {
|
|
|
625
619
|
* so the TUI can show where a token came from (broker URL or local SQLite path).
|
|
626
620
|
*
|
|
627
621
|
* Examples:
|
|
628
|
-
* - `"local ~/.
|
|
622
|
+
* - `"local ~/.zeta/agent/agent.db"`
|
|
629
623
|
* - `"broker http://omp.internal:8765"`
|
|
630
624
|
*/
|
|
631
625
|
sourceLabel?: string;
|
|
@@ -698,6 +692,10 @@ const DEFAULT_USAGE_REQUEST_TIMEOUT_MS = 10_000;
|
|
|
698
692
|
const USAGE_REPORT_CACHE_KEY_VERSION_OVERRIDES: Partial<Record<Provider, number>> = {
|
|
699
693
|
"google-antigravity": 2,
|
|
700
694
|
zai: 2,
|
|
695
|
+
// v2: retires cached reports from the OMP-observed spend estimator (dollar
|
|
696
|
+
// units) now that limits come from the upstream percent-based `/usage`
|
|
697
|
+
// endpoint; the 24h last-good retention would otherwise keep serving them.
|
|
698
|
+
"opencode-go": 2,
|
|
701
699
|
// v2: cache identity gained an `org:` component so two subscriptions on one
|
|
702
700
|
// account email stop sharing a slot. v3 retires parsed reports created before
|
|
703
701
|
// Anthropic extra-usage rows existed; header ingestion can otherwise keep
|
|
@@ -1072,6 +1070,7 @@ const DEFAULT_RANKING_STRATEGIES = new Map<Provider, CredentialRankingStrategy>(
|
|
|
1072
1070
|
["anthropic", claudeRankingStrategy],
|
|
1073
1071
|
["google-antigravity", antigravityRankingStrategy],
|
|
1074
1072
|
["zai", zaiRankingStrategy],
|
|
1073
|
+
["opencode-go", opencodeGoRankingStrategy],
|
|
1075
1074
|
]);
|
|
1076
1075
|
|
|
1077
1076
|
function resolveDefaultRankingStrategy(provider: Provider): CredentialRankingStrategy | undefined {
|
|
@@ -1211,6 +1210,14 @@ type OAuthCandidate = UsageCandidate<OAuthCredential>;
|
|
|
1211
1210
|
type ApiKeyCandidate = UsageCandidate<ApiKeyCredential>;
|
|
1212
1211
|
type UsageRankingResult<T extends AuthCredential> = UsageCandidate<T> & { blockedUntil: number | undefined };
|
|
1213
1212
|
|
|
1213
|
+
type CredentialBlockRouting = {
|
|
1214
|
+
providerKey: string;
|
|
1215
|
+
strategy: CredentialRankingStrategy | undefined;
|
|
1216
|
+
rankingContext: CredentialRankingContext;
|
|
1217
|
+
blockScope: string | undefined;
|
|
1218
|
+
siblingBlockScopes: readonly string[];
|
|
1219
|
+
};
|
|
1220
|
+
|
|
1214
1221
|
type UsageRankedCandidate<T extends AuthCredential> = UsageCandidate<T> & {
|
|
1215
1222
|
blocked: boolean;
|
|
1216
1223
|
blockedUntil?: number;
|
|
@@ -3167,7 +3174,6 @@ export class AuthStorage {
|
|
|
3167
3174
|
const report = await providerImpl.fetchUsage(params, {
|
|
3168
3175
|
fetch: this.#usageFetch,
|
|
3169
3176
|
logger: this.#usageLogger,
|
|
3170
|
-
listUsageCosts: query => this.#store.listUsageCosts?.(query) ?? [],
|
|
3171
3177
|
});
|
|
3172
3178
|
// Attribute the report to the credential's organization. The orgId and
|
|
3173
3179
|
// orgName fallbacks apply independently: Claude's usage endpoint stamps
|
|
@@ -3189,6 +3195,13 @@ export class AuthStorage {
|
|
|
3189
3195
|
}
|
|
3190
3196
|
return report;
|
|
3191
3197
|
} catch (error) {
|
|
3198
|
+
if (error instanceof AIError.ProviderHttpError && (error.status === 401 || error.status === 403)) {
|
|
3199
|
+
// Definitive auth failure (revoked key, lapsed subscription): purge
|
|
3200
|
+
// the last-good report so #fetchUsageCached's failure branch can't
|
|
3201
|
+
// keep rendering and ranking from stale quota the way it does for
|
|
3202
|
+
// transient failures. Mirrors the definitive-OAuth-refresh path.
|
|
3203
|
+
this.#usageCache.set(this.#buildUsageReportCacheKey(request), { value: null, expiresAt: 0 });
|
|
3204
|
+
}
|
|
3192
3205
|
logger.debug("AuthStorage usage fetch failed", {
|
|
3193
3206
|
provider: request.provider,
|
|
3194
3207
|
error: String(error),
|
|
@@ -3289,42 +3302,6 @@ export class AuthStorage {
|
|
|
3289
3302
|
return this.#store.listUsageHistory?.(query) ?? [];
|
|
3290
3303
|
}
|
|
3291
3304
|
|
|
3292
|
-
/** Record one observed provider request cost for later local usage aggregation. */
|
|
3293
|
-
recordUsageCost(
|
|
3294
|
-
provider: Provider,
|
|
3295
|
-
costUsd: number,
|
|
3296
|
-
options?: { sessionId?: string; recordedAt?: number; baseUrl?: string },
|
|
3297
|
-
): boolean {
|
|
3298
|
-
if (!Number.isFinite(costUsd) || costUsd <= 0) return false;
|
|
3299
|
-
const record = this.#store.recordUsageCosts;
|
|
3300
|
-
if (!record) return false;
|
|
3301
|
-
const credential = this.#resolveObservedUsageCredential(provider, options?.sessionId);
|
|
3302
|
-
if (!credential) return false;
|
|
3303
|
-
const entry: UsageCostHistoryEntry = {
|
|
3304
|
-
recordedAt: options?.recordedAt ?? Date.now(),
|
|
3305
|
-
provider,
|
|
3306
|
-
accountKey: this.#buildUsageCacheIdentity(credential),
|
|
3307
|
-
costUsd,
|
|
3308
|
-
};
|
|
3309
|
-
try {
|
|
3310
|
-
record.call(this.#store, [entry]);
|
|
3311
|
-
const cacheKey = this.#buildUsageReportCacheKey({
|
|
3312
|
-
provider,
|
|
3313
|
-
credential,
|
|
3314
|
-
baseUrl: options?.baseUrl,
|
|
3315
|
-
});
|
|
3316
|
-
const existing = this.#usageCache.getStale<UsageReport | null>(cacheKey);
|
|
3317
|
-
this.#usageCache.set(cacheKey, { value: existing?.value ?? null, expiresAt: Date.now() - 1 });
|
|
3318
|
-
return true;
|
|
3319
|
-
} catch (error) {
|
|
3320
|
-
this.#usageLogger?.debug("usage cost record failed", {
|
|
3321
|
-
provider,
|
|
3322
|
-
error: String(error),
|
|
3323
|
-
});
|
|
3324
|
-
return false;
|
|
3325
|
-
}
|
|
3326
|
-
}
|
|
3327
|
-
|
|
3328
3305
|
/**
|
|
3329
3306
|
* Forward one completed request's usage to the store's observer hook.
|
|
3330
3307
|
* Broker-backed stores batch these into per-install reports so the broker
|
|
@@ -3375,28 +3352,6 @@ export class AuthStorage {
|
|
|
3375
3352
|
return this.#store.getClientUsageSummary?.(sinceMs) ?? { clients: [] };
|
|
3376
3353
|
}
|
|
3377
3354
|
|
|
3378
|
-
#resolveObservedUsageCredential(provider: Provider, sessionId?: string): UsageCredential | undefined {
|
|
3379
|
-
const entries = this.#getStoredCredentials(provider);
|
|
3380
|
-
const sessionCredential = this.#getSessionCredential(provider, sessionId);
|
|
3381
|
-
if (sessionCredential) {
|
|
3382
|
-
const credential = entries[sessionCredential.index]?.credential;
|
|
3383
|
-
if (credential) {
|
|
3384
|
-
return credential.type === "api_key"
|
|
3385
|
-
? { type: "api_key", apiKey: credential.key }
|
|
3386
|
-
: this.#buildUsageCredential(credential);
|
|
3387
|
-
}
|
|
3388
|
-
}
|
|
3389
|
-
if (entries.length === 1) {
|
|
3390
|
-
const credential = entries[0]!.credential;
|
|
3391
|
-
return credential.type === "api_key"
|
|
3392
|
-
? { type: "api_key", apiKey: credential.key }
|
|
3393
|
-
: this.#buildUsageCredential(credential);
|
|
3394
|
-
}
|
|
3395
|
-
const envKey = getEnvApiKey(provider);
|
|
3396
|
-
if (envKey) return { type: "api_key", apiKey: envKey };
|
|
3397
|
-
return undefined;
|
|
3398
|
-
}
|
|
3399
|
-
|
|
3400
3355
|
ingestUsageHeaders(
|
|
3401
3356
|
provider: Provider,
|
|
3402
3357
|
headers: Record<string, string>,
|
|
@@ -3480,9 +3435,9 @@ export class AuthStorage {
|
|
|
3480
3435
|
return true;
|
|
3481
3436
|
}
|
|
3482
3437
|
|
|
3483
|
-
#collectUsageRequests(options?: {
|
|
3438
|
+
async #collectUsageRequests(options?: {
|
|
3484
3439
|
baseUrlResolver?: (provider: Provider) => string | undefined;
|
|
3485
|
-
}): UsageRequestDescriptor[] {
|
|
3440
|
+
}): Promise<UsageRequestDescriptor[]> {
|
|
3486
3441
|
const resolver = this.#usageProviderResolver;
|
|
3487
3442
|
if (!resolver) return [];
|
|
3488
3443
|
|
|
@@ -3542,10 +3497,19 @@ export class AuthStorage {
|
|
|
3542
3497
|
|
|
3543
3498
|
for (const entry of entries) {
|
|
3544
3499
|
const credential = entry.credential;
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3500
|
+
let request: UsageRequestDescriptor;
|
|
3501
|
+
if (credential.type === "api_key") {
|
|
3502
|
+
// Stored keys may be references (env var name, "!command") —
|
|
3503
|
+
// resolve to the actual secret before it reaches a provider
|
|
3504
|
+
// fetcher's Authorization header. Unresolvable references are
|
|
3505
|
+
// skipped: probing with the literal reference string would
|
|
3506
|
+
// 401 and flag a working credential as bad.
|
|
3507
|
+
const apiKey = await this.#configValueResolver(credential.key);
|
|
3508
|
+
if (!apiKey) continue;
|
|
3509
|
+
request = this.#buildUsageRequest(provider, { type: "api_key", apiKey }, baseUrl);
|
|
3510
|
+
} else {
|
|
3511
|
+
request = this.#buildUsageRequestForOauth(provider, credential, baseUrl);
|
|
3512
|
+
}
|
|
3549
3513
|
if (providerImpl.supports && !providerImpl.supports(request)) continue;
|
|
3550
3514
|
requests.push(request);
|
|
3551
3515
|
}
|
|
@@ -3997,7 +3961,7 @@ export class AuthStorage {
|
|
|
3997
3961
|
}
|
|
3998
3962
|
if (!this.#usageProviderResolver) return null;
|
|
3999
3963
|
|
|
4000
|
-
const requests = this.#collectUsageRequests(options);
|
|
3964
|
+
const requests = await this.#collectUsageRequests(options);
|
|
4001
3965
|
if (requests.length === 0) return [];
|
|
4002
3966
|
|
|
4003
3967
|
this.#usageLogger?.debug("Usage fetch requested", {
|
|
@@ -4093,7 +4057,6 @@ export class AuthStorage {
|
|
|
4093
4057
|
const ctx: UsageFetchContext = {
|
|
4094
4058
|
fetch: this.#usageFetch,
|
|
4095
4059
|
logger: this.#usageLogger,
|
|
4096
|
-
listUsageCosts: query => this.#store.listUsageCosts?.(query) ?? [],
|
|
4097
4060
|
};
|
|
4098
4061
|
|
|
4099
4062
|
const results: CredentialHealthResult[] = [];
|
|
@@ -4115,10 +4078,21 @@ export class AuthStorage {
|
|
|
4115
4078
|
|
|
4116
4079
|
const baseUrl = options?.baseUrlResolver?.(row.provider as Provider);
|
|
4117
4080
|
const cred = row.credential;
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
4081
|
+
let initialRequest: UsageRequestDescriptor;
|
|
4082
|
+
if (cred.type === "api_key") {
|
|
4083
|
+
// Stored keys may be references (env var name, "!command") — probe
|
|
4084
|
+
// with the resolved secret, not the reference string, so both the
|
|
4085
|
+
// usage probe and the completion probe exercise the real bytes.
|
|
4086
|
+
const apiKey = await this.#configValueResolver(cred.key);
|
|
4087
|
+
if (!apiKey) {
|
|
4088
|
+
base.reason = "api key reference could not be resolved";
|
|
4089
|
+
results.push(base);
|
|
4090
|
+
continue;
|
|
4091
|
+
}
|
|
4092
|
+
initialRequest = this.#buildUsageRequest(row.provider as Provider, { type: "api_key", apiKey }, baseUrl);
|
|
4093
|
+
} else {
|
|
4094
|
+
initialRequest = this.#buildUsageRequestForOauth(row.provider as Provider, cred, baseUrl);
|
|
4095
|
+
}
|
|
4122
4096
|
|
|
4123
4097
|
const timeoutSignal = AbortSignal.timeout(timeoutMs);
|
|
4124
4098
|
const probeSignal = options?.signal ? AbortSignal.any([options.signal, timeoutSignal]) : timeoutSignal;
|
|
@@ -4271,6 +4245,58 @@ export class AuthStorage {
|
|
|
4271
4245
|
return sessionCredential ? { ...sessionCredential, explicit: false } : undefined;
|
|
4272
4246
|
}
|
|
4273
4247
|
|
|
4248
|
+
#credentialBlockRouting(
|
|
4249
|
+
provider: string,
|
|
4250
|
+
credentialType: AuthCredential["type"],
|
|
4251
|
+
modelId: string | undefined,
|
|
4252
|
+
): CredentialBlockRouting {
|
|
4253
|
+
const providerKey = this.#getProviderTypeKey(provider, credentialType);
|
|
4254
|
+
const strategy = this.#rankingStrategyResolver?.(provider);
|
|
4255
|
+
const rankingContext: CredentialRankingContext = { modelId };
|
|
4256
|
+
const blockScope = strategy?.blockScope?.(rankingContext);
|
|
4257
|
+
return {
|
|
4258
|
+
providerKey,
|
|
4259
|
+
strategy,
|
|
4260
|
+
rankingContext,
|
|
4261
|
+
blockScope,
|
|
4262
|
+
siblingBlockScopes: strategy?.blockScopes?.(rankingContext) ?? (blockScope ? [blockScope] : []),
|
|
4263
|
+
};
|
|
4264
|
+
}
|
|
4265
|
+
|
|
4266
|
+
#blockCredentialForRotation(
|
|
4267
|
+
provider: string,
|
|
4268
|
+
credentialType: AuthCredential["type"],
|
|
4269
|
+
targetIndex: number,
|
|
4270
|
+
blockedUntil: number,
|
|
4271
|
+
routing: CredentialBlockRouting,
|
|
4272
|
+
): UsageLimitMarkResult {
|
|
4273
|
+
if (targetIndex >= 0) {
|
|
4274
|
+
this.#markCredentialBlocked(provider, routing.providerKey, targetIndex, blockedUntil, routing.blockScope);
|
|
4275
|
+
}
|
|
4276
|
+
|
|
4277
|
+
const remainingCredentials = this.#getCredentialsForProvider(provider)
|
|
4278
|
+
.map((credential, index) => ({ credential, index }))
|
|
4279
|
+
.filter(
|
|
4280
|
+
(entry): entry is { credential: AuthCredential; index: number } =>
|
|
4281
|
+
entry.credential.type === credentialType && entry.index !== targetIndex,
|
|
4282
|
+
);
|
|
4283
|
+
|
|
4284
|
+
let retryAtMs: number | undefined;
|
|
4285
|
+
for (const candidate of remainingCredentials) {
|
|
4286
|
+
// Sibling availability must use the same scope set selection reads, or
|
|
4287
|
+
// this reports a sibling as free that selection will then refuse.
|
|
4288
|
+
const candidateBlockedUntil = this.#getCredentialBlockedUntil(
|
|
4289
|
+
provider,
|
|
4290
|
+
routing.providerKey,
|
|
4291
|
+
candidate.index,
|
|
4292
|
+
routing.siblingBlockScopes,
|
|
4293
|
+
);
|
|
4294
|
+
if (candidateBlockedUntil === undefined) return { switched: true };
|
|
4295
|
+
if (retryAtMs === undefined || candidateBlockedUntil < retryAtMs) retryAtMs = candidateBlockedUntil;
|
|
4296
|
+
}
|
|
4297
|
+
return { switched: false, retryAtMs };
|
|
4298
|
+
}
|
|
4299
|
+
|
|
4274
4300
|
/**
|
|
4275
4301
|
* Marks the current session's credential as temporarily blocked due to usage limits.
|
|
4276
4302
|
* Uses usage reports to determine accurate reset time when available.
|
|
@@ -4313,21 +4339,17 @@ export class AuthStorage {
|
|
|
4313
4339
|
const credentialType = sessionCredential.type;
|
|
4314
4340
|
const targetCredentialId = target.id;
|
|
4315
4341
|
|
|
4316
|
-
const
|
|
4317
|
-
const strategy = this.#rankingStrategyResolver?.(provider);
|
|
4318
|
-
const rankingContext: CredentialRankingContext = { modelId: options?.modelId };
|
|
4319
|
-
const blockScope = strategy?.blockScope?.(rankingContext);
|
|
4320
|
-
const siblingBlockScopes = strategy?.blockScopes?.(rankingContext) ?? (blockScope ? [blockScope] : []);
|
|
4342
|
+
const routing = this.#credentialBlockRouting(provider, credentialType, options?.modelId);
|
|
4321
4343
|
const now = Date.now();
|
|
4322
4344
|
let blockedUntil = now + (options?.retryAfterMs ?? AuthStorage.#defaultBackoffMs);
|
|
4323
4345
|
|
|
4324
|
-
if (credentialType === "oauth" && target.credential.type === "oauth" && strategy) {
|
|
4346
|
+
if (credentialType === "oauth" && target.credential.type === "oauth" && routing.strategy) {
|
|
4325
4347
|
const report = await raceUsageWithSignal(
|
|
4326
4348
|
this.#getUsageReport(provider, target.credential, options),
|
|
4327
4349
|
options?.signal,
|
|
4328
4350
|
);
|
|
4329
4351
|
if (report) {
|
|
4330
|
-
const scopedLimits = this.#getScopedUsageLimits(strategy, report, rankingContext);
|
|
4352
|
+
const scopedLimits = this.#getScopedUsageLimits(routing.strategy, report, routing.rankingContext);
|
|
4331
4353
|
if (this.#isUsageLimitReached(scopedLimits)) {
|
|
4332
4354
|
const resetAtMs = this.#getUsageResetAtMs(scopedLimits, Date.now());
|
|
4333
4355
|
if (resetAtMs && resetAtMs > blockedUntil) {
|
|
@@ -4343,32 +4365,7 @@ export class AuthStorage {
|
|
|
4343
4365
|
const targetIndex = this.#getStoredCredentials(provider).findIndex(
|
|
4344
4366
|
entry => entry.id === targetCredentialId && entry.credential.type === credentialType,
|
|
4345
4367
|
);
|
|
4346
|
-
|
|
4347
|
-
this.#markCredentialBlocked(provider, providerKey, targetIndex, blockedUntil, blockScope);
|
|
4348
|
-
}
|
|
4349
|
-
|
|
4350
|
-
const remainingCredentials = this.#getCredentialsForProvider(provider)
|
|
4351
|
-
.map((credential, index) => ({ credential, index }))
|
|
4352
|
-
.filter(
|
|
4353
|
-
(entry): entry is { credential: AuthCredential; index: number } =>
|
|
4354
|
-
entry.credential.type === credentialType && entry.index !== targetIndex,
|
|
4355
|
-
);
|
|
4356
|
-
|
|
4357
|
-
let retryAtMs: number | undefined;
|
|
4358
|
-
for (const candidate of remainingCredentials) {
|
|
4359
|
-
// Sibling availability must use the same scope set selection reads, or
|
|
4360
|
-
// this reports a sibling as free that selection will then refuse, most
|
|
4361
|
-
// visibly when the sibling still carries a legacy shared block.
|
|
4362
|
-
const candidateBlockedUntil = this.#getCredentialBlockedUntil(
|
|
4363
|
-
provider,
|
|
4364
|
-
providerKey,
|
|
4365
|
-
candidate.index,
|
|
4366
|
-
siblingBlockScopes,
|
|
4367
|
-
);
|
|
4368
|
-
if (candidateBlockedUntil === undefined) return { switched: true };
|
|
4369
|
-
if (retryAtMs === undefined || candidateBlockedUntil < retryAtMs) retryAtMs = candidateBlockedUntil;
|
|
4370
|
-
}
|
|
4371
|
-
return { switched: false, retryAtMs };
|
|
4368
|
+
return this.#blockCredentialForRotation(provider, credentialType, targetIndex, blockedUntil, routing);
|
|
4372
4369
|
}
|
|
4373
4370
|
|
|
4374
4371
|
#resolveWindowResetAt(window: UsageLimit["window"]): number | undefined {
|
|
@@ -6078,6 +6075,8 @@ export class AuthStorage {
|
|
|
6078
6075
|
* - usage-limit / account-rate-limit error → {@link AuthStorage.markUsageLimitReached}
|
|
6079
6076
|
* (temporary block via its own backoff — default plus server usage-report
|
|
6080
6077
|
* reset; sticky left intact so the next resolve re-ranks around the block).
|
|
6078
|
+
* - account-scoped policy denial → temporarily block that account without
|
|
6079
|
+
* marking its credential suspect, then rotate through eligible siblings.
|
|
6081
6080
|
* - otherwise (hard 401 / auth failure) → mark the credential suspect (or
|
|
6082
6081
|
* reload when no broker hook is wired) and block it, then drop matching
|
|
6083
6082
|
* sticky state.
|
|
@@ -6114,6 +6113,17 @@ export class AuthStorage {
|
|
|
6114
6113
|
});
|
|
6115
6114
|
if (!sessionCredential) return false;
|
|
6116
6115
|
|
|
6116
|
+
if (AIError.isAccountPolicyError(error)) {
|
|
6117
|
+
const routing = this.#credentialBlockRouting(provider, sessionCredential.type, options?.modelId);
|
|
6118
|
+
return this.#blockCredentialForRotation(
|
|
6119
|
+
provider,
|
|
6120
|
+
sessionCredential.type,
|
|
6121
|
+
sessionCredential.index,
|
|
6122
|
+
Date.now() + AuthStorage.#defaultBackoffMs,
|
|
6123
|
+
routing,
|
|
6124
|
+
).switched;
|
|
6125
|
+
}
|
|
6126
|
+
|
|
6117
6127
|
const providerKey = this.#getProviderTypeKey(provider, sessionCredential.type);
|
|
6118
6128
|
// Snapshot sibling availability before mutating so a soft-deleting
|
|
6119
6129
|
// suspect hook can't reindex the answer out from under us.
|
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
} from "../types";
|
|
8
8
|
import {
|
|
9
9
|
clearStreamingPartialJson,
|
|
10
|
+
copyCursorExecResolved,
|
|
10
11
|
getStreamingPartialJson,
|
|
11
12
|
type StreamingPartialJsonCarrier,
|
|
12
13
|
setStreamingPartialJson,
|
|
@@ -54,6 +55,7 @@ function cloneToolCall(source: StreamingToolCall): StreamingToolCall {
|
|
|
54
55
|
};
|
|
55
56
|
const partialJson = getStreamingPartialJson(source);
|
|
56
57
|
if (partialJson !== undefined) setStreamingPartialJson(block, partialJson);
|
|
58
|
+
copyCursorExecResolved(block, source);
|
|
57
59
|
return block;
|
|
58
60
|
}
|
|
59
61
|
|
|
@@ -65,6 +67,7 @@ function syncToolCall(target: StreamingToolCall, source: StreamingToolCall): voi
|
|
|
65
67
|
const partialJson = getStreamingPartialJson(source);
|
|
66
68
|
if (partialJson === undefined) clearStreamingPartialJson(target);
|
|
67
69
|
else setStreamingPartialJson(target, partialJson);
|
|
70
|
+
copyCursorExecResolved(target, source);
|
|
68
71
|
}
|
|
69
72
|
|
|
70
73
|
function hasNamedNativeToolCall(source: StreamingToolCall | undefined): source is StreamingToolCall {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { extractHttpStatusFromError } from "@linxiraos/pi-utils";
|
|
2
|
-
import { isOAuthExpiry, isUsageLimit } from "./flags";
|
|
2
|
+
import { isAccountPolicyError, isOAuthExpiry, isUsageLimit } from "./flags";
|
|
3
3
|
import { isConcurrencyCapExclusion, isUsageLimitOutcome } from "./rate-limit";
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -27,15 +27,16 @@ export function isInvalidatedOAuthTokenError(error: unknown): boolean {
|
|
|
27
27
|
/**
|
|
28
28
|
* Whether an upstream failure should rotate to a sibling credential: a hard
|
|
29
29
|
* `401`, a `403` (token valid but access denied — plan, model policy, or org
|
|
30
|
-
* restriction a sibling account may not share),
|
|
31
|
-
*
|
|
30
|
+
* restriction a sibling account may not share), an account-scoped policy
|
|
31
|
+
* denial such as Codex `cyber_policy`, a body-classified usage limit (Codex
|
|
32
|
+
* `usage_limit_reached`, Anthropic account rate-limit, Google
|
|
32
33
|
* `resource_exhausted`, OpenAI `insufficient_quota`, …), or a bare `429`
|
|
33
|
-
* whose payload did not preserve a richer quota code.
|
|
34
|
-
*
|
|
35
|
-
* upstream-backoff lane.
|
|
34
|
+
* whose payload did not preserve a richer quota code. Transient 429s
|
|
35
|
+
* (`Too many requests`, per-minute caps) stay in the upstream-backoff lane.
|
|
36
36
|
*/
|
|
37
37
|
export function isAuthRetryableError(error: unknown): boolean {
|
|
38
38
|
if (isUsageLimit(error)) return true;
|
|
39
|
+
if (isAccountPolicyError(error)) return true;
|
|
39
40
|
if (isInvalidatedOAuthTokenError(error)) return true;
|
|
40
41
|
const httpStatus = extractHttpStatusFromError(error);
|
|
41
42
|
const message = error instanceof Error ? error.message : typeof error === "string" ? error : undefined;
|
package/src/error/aws.ts
CHANGED
|
@@ -13,7 +13,11 @@ export type AwsCredentialsErrorKind =
|
|
|
13
13
|
/** STS web-identity exchange failed or returned malformed credentials. */
|
|
14
14
|
| "web-identity"
|
|
15
15
|
/** ECS/container credential endpoint failed or returned malformed credentials. */
|
|
16
|
-
| "container"
|
|
16
|
+
| "container"
|
|
17
|
+
/** Shared-config role chain is misconfigured (cycle, missing source_profile, unsupported credential_source). */
|
|
18
|
+
| "profile"
|
|
19
|
+
/** STS `AssumeRole` call failed or returned malformed credentials. */
|
|
20
|
+
| "assume-role";
|
|
17
21
|
|
|
18
22
|
/** A failure resolving AWS credentials for the Bedrock provider. */
|
|
19
23
|
export class AwsCredentialsError extends Error {
|
package/src/error/flags.ts
CHANGED
|
@@ -24,7 +24,10 @@ export const Flag = {
|
|
|
24
24
|
StaleResponsesItem: 0x0010_0000,
|
|
25
25
|
MalformedFunctionCall: 0x0020_0000,
|
|
26
26
|
ProviderFinishError: 0x0040_0000,
|
|
27
|
+
EmptyResponse: 0x0000_2000,
|
|
27
28
|
ContentBlocked: 0x0000_8000,
|
|
29
|
+
/** Account-scoped provider policy denial that may succeed with another credential. */
|
|
30
|
+
AccountPolicy: 0x0000_4000,
|
|
28
31
|
ContextOverflow: 0x0080_0000,
|
|
29
32
|
AuthFailed: 0x0100_0000,
|
|
30
33
|
SilentAbort: 0x0200_0000,
|
|
@@ -48,7 +51,9 @@ const KIND_MASK =
|
|
|
48
51
|
Flag.StaleResponsesItem |
|
|
49
52
|
Flag.MalformedFunctionCall |
|
|
50
53
|
Flag.ProviderFinishError |
|
|
54
|
+
Flag.EmptyResponse |
|
|
51
55
|
Flag.ContentBlocked |
|
|
56
|
+
Flag.AccountPolicy |
|
|
52
57
|
Flag.ContextOverflow |
|
|
53
58
|
Flag.AuthFailed |
|
|
54
59
|
Flag.SilentAbort |
|
|
@@ -59,7 +64,12 @@ const KIND_MASK =
|
|
|
59
64
|
Flag.OAuthExpiry;
|
|
60
65
|
|
|
61
66
|
const RETRIABLE_KINDS =
|
|
62
|
-
Flag.Transient |
|
|
67
|
+
Flag.Transient |
|
|
68
|
+
Flag.UsageLimit |
|
|
69
|
+
Flag.ThinkingLoop |
|
|
70
|
+
Flag.StaleResponsesItem |
|
|
71
|
+
Flag.ProviderFinishError |
|
|
72
|
+
Flag.EmptyResponse;
|
|
63
73
|
|
|
64
74
|
const OVERFLOW_PATTERNS = [
|
|
65
75
|
/prompt is too long/i, // Anthropic
|
|
@@ -101,7 +111,9 @@ const AUTH_FAILURE_PATTERN =
|
|
|
101
111
|
/\b(?:401|403|unauthorized|forbidden|authentication|auth[_ ]?unavailable|no auth available|(?:invalid|no)[_ ]?api[_ ]?key)\b/i;
|
|
102
112
|
const MALFORMED_FUNCTION_CALL_PATTERN = /\bmalformed.?function.?call\b/i;
|
|
103
113
|
const PROVIDER_FINISH_ERROR_PATTERN = /\bProvider (?:returned error finish_reason|finish_reason:\s*error)\b/i;
|
|
114
|
+
const EMPTY_RESPONSE_PATTERN = /\bthought-only response without final output\b/i;
|
|
104
115
|
const CONTENT_FILTER_PATTERN = /\b(?:incomplete:\s*)?content_filter\b/i;
|
|
116
|
+
const ACCOUNT_POLICY_PATTERN = /\bcyber_policy\b|trusted access for cyber/i;
|
|
105
117
|
const STALE_RESPONSE_ITEM_PATTERNS = [/\bItem with id ['"][^'"]+['"] not found\.?/i, /previous[ _]?response/i] as const;
|
|
106
118
|
const STALE_RESPONSE_ITEM_DETAIL_PATTERN = /not[ _]?found|invalid|expired|stale|zero[ _-]?data[ _-]?retention/i;
|
|
107
119
|
/**
|
|
@@ -193,7 +205,9 @@ const ERROR_KIND_LABELS: readonly [Flag, string][] = [
|
|
|
193
205
|
[Flag.StaleResponsesItem, "stale-responses-item"],
|
|
194
206
|
[Flag.MalformedFunctionCall, "malformed-function-call"],
|
|
195
207
|
[Flag.ProviderFinishError, "provider-finish-error"],
|
|
208
|
+
[Flag.EmptyResponse, "empty-response"],
|
|
196
209
|
[Flag.ContentBlocked, "content-blocked"],
|
|
210
|
+
[Flag.AccountPolicy, "account-policy"],
|
|
197
211
|
[Flag.ContextOverflow, "context-overflow"],
|
|
198
212
|
[Flag.AuthFailed, "auth-failed"],
|
|
199
213
|
[Flag.SilentAbort, "silent-abort"],
|
|
@@ -335,7 +349,9 @@ function classifyText(errorMessage: string | undefined, errorStatus: number | un
|
|
|
335
349
|
if (matchesOverflowText(errorMessage)) kinds |= Flag.ContextOverflow;
|
|
336
350
|
if (isMalformedFunctionCallText(errorMessage)) kinds |= Flag.MalformedFunctionCall;
|
|
337
351
|
if (isProviderFinishErrorText(errorMessage)) kinds |= Flag.ProviderFinishError;
|
|
352
|
+
if (EMPTY_RESPONSE_PATTERN.test(errorMessage)) kinds |= Flag.EmptyResponse | Flag.Transient;
|
|
338
353
|
if (isContentBlockedText(errorMessage)) kinds |= Flag.ContentBlocked;
|
|
354
|
+
if (ACCOUNT_POLICY_PATTERN.test(errorMessage)) kinds |= Flag.AccountPolicy | Flag.ContentBlocked;
|
|
339
355
|
if (isAuthFailureText(errorMessage)) kinds |= Flag.AuthFailed;
|
|
340
356
|
|
|
341
357
|
const statusClean = errorStatus ? errorStatus : (status({ message: errorMessage }) ?? undefined);
|
|
@@ -398,6 +414,9 @@ export function classify(error: unknown, api?: Api): number {
|
|
|
398
414
|
if ("errorId" in link && typeof (link as { errorId: unknown }).errorId === "number") {
|
|
399
415
|
kinds |= (link as { errorId: number }).errorId & KIND_MASK;
|
|
400
416
|
}
|
|
417
|
+
if ("code" in link && typeof link.code === "string" && ACCOUNT_POLICY_PATTERN.test(link.code)) {
|
|
418
|
+
kinds |= Flag.AccountPolicy | Flag.ContentBlocked;
|
|
419
|
+
}
|
|
401
420
|
}
|
|
402
421
|
|
|
403
422
|
if (link instanceof AwsCredentialsError) {
|
|
@@ -475,6 +494,11 @@ export function isUsageLimit(error: unknown, api?: Api): boolean {
|
|
|
475
494
|
return is(classify(error, api), Flag.UsageLimit);
|
|
476
495
|
}
|
|
477
496
|
|
|
497
|
+
/** Whether an upstream rejection is an account-scoped policy denial worth retrying with a sibling credential. */
|
|
498
|
+
export function isAccountPolicyError(error: unknown, api?: Api): boolean {
|
|
499
|
+
return is(classify(error, api), Flag.AccountPolicy);
|
|
500
|
+
}
|
|
501
|
+
|
|
478
502
|
/**
|
|
479
503
|
* Strict-tool rejection: grammar too large, schema too complex, or structured
|
|
480
504
|
* outputs unsupported by the model/endpoint.
|
package/src/error/provider.ts
CHANGED
|
@@ -9,6 +9,8 @@ export type ProviderResponseErrorKind =
|
|
|
9
9
|
| "output"
|
|
10
10
|
/** Response body was empty/missing when content was required. */
|
|
11
11
|
| "empty-body"
|
|
12
|
+
/** Response completed without actionable output (for example, thoughts only). */
|
|
13
|
+
| "empty-output"
|
|
12
14
|
/** Malformed wire envelope (unexpected message ordering / shape). */
|
|
13
15
|
| "envelope"
|
|
14
16
|
/** Content was blocked by a provider safety filter. */
|
|
@@ -38,11 +40,10 @@ export class ProviderResponseError extends Error {
|
|
|
38
40
|
this.kind = options.kind ?? "output";
|
|
39
41
|
// A safety filter block is terminal and intentionally non-retryable.
|
|
40
42
|
if (this.kind === "content-blocked") attach(this, create(Flag.ContentBlocked));
|
|
41
|
-
//
|
|
42
|
-
//
|
|
43
|
-
//
|
|
44
|
-
|
|
45
|
-
// output was already emitted.
|
|
43
|
+
// A logically empty completed output needs a session-level reminder that
|
|
44
|
+
// asks for the missing final answer. Empty bodies and incomplete streams
|
|
45
|
+
// stay on the generic transient retry/model-fallback path.
|
|
46
|
+
else if (this.kind === "empty-output") attach(this, create(Flag.Transient, Flag.EmptyResponse));
|
|
46
47
|
else if (this.kind === "incomplete-stream" || this.kind === "empty-body") attach(this, create(Flag.Transient));
|
|
47
48
|
}
|
|
48
49
|
}
|