@linxiraos/pi-ai 1.1.26 → 1.2.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 +9 -8
- package/README.md +15 -0
- package/dist/types/auth/affinity.d.ts +33 -6
- package/dist/types/auth/cascade.d.ts +44 -7
- package/dist/types/auth/oauth.d.ts +9 -6
- package/dist/types/auth/policy.d.ts +1 -0
- package/dist/types/auth/refresh.d.ts +1 -2
- package/dist/types/auth/select.d.ts +6 -4
- package/dist/types/auth/sqlite-credential-store.d.ts +21 -1
- package/dist/types/auth/store.d.ts +8 -2
- package/dist/types/auth/types.d.ts +99 -15
- package/dist/types/auth-broker/client.d.ts +3 -3
- package/dist/types/auth-broker/discover.d.ts +1 -1
- package/dist/types/auth-broker/refresher.d.ts +6 -1
- package/dist/types/auth-broker/remote-store.d.ts +6 -4
- package/dist/types/auth-broker/server.d.ts +2 -0
- package/dist/types/auth-broker/snapshot-cache.d.ts +12 -0
- package/dist/types/auth-gateway/dispatch.d.ts +13 -5
- package/dist/types/auth-gateway/http.d.ts +0 -2
- package/dist/types/auth-gateway/index.d.ts +1 -0
- package/dist/types/auth-gateway/routes/embeddings.d.ts +2 -2
- package/dist/types/auth-gateway/routes/images.d.ts +3 -3
- package/dist/types/auth-gateway/routes/rerank.d.ts +2 -2
- package/dist/types/auth-gateway/routes/speech.d.ts +2 -2
- package/dist/types/auth-gateway/routes/systemone.d.ts +2 -2
- package/dist/types/auth-gateway/routes/transcriptions.d.ts +2 -2
- package/dist/types/auth-gateway/routes/video.d.ts +4 -4
- package/dist/types/auth-gateway/server.d.ts +22 -5
- package/dist/types/auth-gateway/session-state.d.ts +6 -0
- package/dist/types/auth-gateway/stdio.d.ts +12 -0
- package/dist/types/auth-storage.d.ts +7 -6
- package/dist/types/dialect/terminator-wait.d.ts +19 -0
- package/dist/types/env-api-key.d.ts +21 -0
- package/dist/types/error/auth.d.ts +10 -0
- package/dist/types/error/flags.d.ts +38 -0
- package/dist/types/images/index.d.ts +2 -3
- package/dist/types/images/shared.d.ts +6 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/types/judgment/index.d.ts +2 -1
- package/dist/types/judgment/native.d.ts +80 -0
- package/dist/types/judgment/openai-decisions.d.ts +3 -0
- package/dist/types/judgment/question-file.d.ts +52 -0
- package/dist/types/judgment/systemone.d.ts +7 -0
- package/dist/types/judgment/types.d.ts +2 -2
- package/dist/types/judgment/wire.d.ts +36 -0
- package/dist/types/provider-details.d.ts +1 -1
- package/dist/types/provider-session-state.d.ts +2 -2
- package/dist/types/providers/anthropic-client.d.ts +10 -0
- package/dist/types/providers/anthropic-state.d.ts +2 -0
- package/dist/types/providers/anthropic.d.ts +38 -3
- package/dist/types/providers/aws-credentials.d.ts +4 -8
- package/dist/types/providers/connect-error-detail.d.ts +9 -0
- package/dist/types/providers/connect-frame.d.ts +44 -0
- package/dist/types/providers/cursor/interaction-query.d.ts +0 -2
- package/dist/types/providers/cursor.d.ts +8 -8
- package/dist/types/providers/google-auth.d.ts +4 -1
- package/dist/types/providers/google-shared.d.ts +24 -4
- package/dist/types/providers/inference-headers.d.ts +6 -0
- package/dist/types/providers/mock.d.ts +3 -1
- package/dist/types/providers/openai-codex/request-transformer.d.ts +2 -0
- package/dist/types/providers/openai-codex-responses.d.ts +1 -1
- package/dist/types/providers/openai-configuration-update.d.ts +20 -11
- package/dist/types/providers/openai-responses-wire.d.ts +1 -55
- package/dist/types/providers/openai-responses.d.ts +40 -3
- package/dist/types/providers/openai-shared.d.ts +30 -5
- package/dist/types/providers/pi-native-client.d.ts +1 -1
- package/dist/types/providers/transform-messages.d.ts +25 -0
- package/dist/types/providers/vision-guard.d.ts +1 -1
- package/dist/types/registry/oauth/cloudflare-workers-ai.d.ts +3 -0
- package/dist/types/registry/oauth/cursor.d.ts +10 -2
- package/dist/types/registry/oauth/index.d.ts +2 -0
- package/dist/types/registry/oauth/snowflake.d.ts +3 -0
- package/dist/types/registry/snowflake.d.ts +4 -0
- package/dist/types/stream.d.ts +0 -21
- package/dist/types/transcription/types.d.ts +2 -1
- package/dist/types/types.d.ts +116 -14
- package/dist/types/usage.d.ts +2 -0
- package/dist/types/utils/anthropic-auth.d.ts +21 -7
- package/dist/types/utils/aws-profile.d.ts +5 -0
- package/dist/types/utils/block-symbols.d.ts +14 -0
- package/dist/types/utils/dsml-leak.d.ts +29 -0
- package/dist/types/utils/glyph-codec.d.ts +1 -1
- package/dist/types/utils/idle-iterator.d.ts +11 -0
- package/dist/types/utils/leaked-thinking-stream.d.ts +1 -1
- package/dist/types/utils/resolve-peer.d.ts +2 -0
- package/dist/types/utils/retry-wait.d.ts +9 -0
- package/dist/types/utils/schema/normalize.d.ts +8 -3
- package/dist/types/utils/stream-markup-healing.d.ts +4 -4
- package/dist/types/utils/tool-call-arguments.d.ts +8 -0
- package/dist/types/utils.d.ts +6 -0
- package/package.json +6 -6
- package/src/auth/affinity.ts +130 -37
- package/src/auth/blocks.ts +94 -15
- package/src/auth/cascade.ts +112 -17
- package/src/auth/health.ts +3 -2
- package/src/auth/oauth.ts +25 -10
- package/src/auth/policy.ts +10 -1
- package/src/auth/pool.ts +0 -5
- package/src/auth/refresh.ts +17 -12
- package/src/auth/resets.ts +1 -1
- package/src/auth/rotation.ts +8 -2
- package/src/auth/select.ts +102 -30
- package/src/auth/sqlite-credential-store.ts +119 -49
- package/src/auth/store.ts +8 -2
- package/src/auth/types.ts +108 -17
- package/src/auth/usage-cache.ts +16 -5
- package/src/auth/usage.ts +2 -2
- package/src/auth-broker/client.ts +7 -4
- package/src/auth-broker/discover.ts +6 -5
- package/src/auth-broker/refresher.ts +8 -1
- package/src/auth-broker/remote-store.ts +200 -62
- package/src/auth-broker/server.ts +316 -130
- package/src/auth-broker/snapshot-cache.ts +142 -2
- package/src/auth-gateway/dispatch.ts +15 -7
- package/src/auth-gateway/http.ts +0 -8
- package/src/auth-gateway/index.ts +1 -0
- package/src/auth-gateway/routes/embeddings.ts +2 -2
- package/src/auth-gateway/routes/images.ts +4 -4
- package/src/auth-gateway/routes/rerank.ts +2 -2
- package/src/auth-gateway/routes/speech.ts +2 -2
- package/src/auth-gateway/routes/systemone.ts +22 -14
- package/src/auth-gateway/routes/transcriptions.ts +2 -2
- package/src/auth-gateway/routes/video.ts +7 -7
- package/src/auth-gateway/server.ts +175 -134
- package/src/auth-gateway/session-state.ts +51 -9
- package/src/auth-gateway/stdio.ts +132 -0
- package/src/auth-storage.ts +23 -9
- package/src/dialect/deepseek.ts +67 -8
- package/src/dialect/gemini.ts +8 -2
- package/src/dialect/hermes.ts +8 -2
- package/src/dialect/kimi.ts +8 -2
- package/src/dialect/qwen-xml.ts +8 -2
- package/src/dialect/qwen3.ts +8 -2
- package/src/dialect/terminator-wait.ts +42 -0
- package/src/env-api-key.ts +78 -0
- package/src/error/auth.ts +20 -0
- package/src/error/flags.ts +143 -24
- package/src/error/rate-limit.ts +8 -6
- package/src/images/google-antigravity.ts +0 -1
- package/src/images/index.ts +3 -17
- package/src/images/openai-hosted.ts +13 -4
- package/src/images/openai-images.ts +19 -13
- package/src/images/shared.ts +10 -2
- package/src/index.ts +2 -1
- package/src/judgment/index.ts +2 -1
- package/src/judgment/native.ts +333 -0
- package/src/judgment/openai-decisions-predicate.md +9 -0
- package/src/judgment/openai-decisions.ts +129 -0
- package/src/judgment/question-file.ts +90 -0
- package/src/judgment/systemone.ts +65 -0
- package/src/judgment/text.ts +9 -1
- package/src/judgment/types.ts +2 -2
- package/src/judgment/wire.ts +42 -0
- package/src/provider-details.ts +1 -1
- package/src/provider-session-state.ts +2 -2
- package/src/providers/amazon-bedrock.ts +19 -8
- package/src/providers/anthropic-client.ts +16 -1
- package/src/providers/anthropic-state.ts +6 -0
- package/src/providers/anthropic.ts +962 -238
- package/src/providers/apple-foundation-models.ts +10 -4
- package/src/providers/aws-credentials.ts +65 -5
- package/src/providers/azure-openai-responses.ts +1 -1
- package/src/providers/connect-error-detail.ts +21 -0
- package/src/providers/connect-frame.ts +107 -0
- package/src/providers/cursor/interaction-query.ts +1 -9
- package/src/providers/cursor.ts +590 -133
- package/src/providers/devin.ts +68 -58
- package/src/providers/factory-droid/gemini.ts +38 -51
- package/src/providers/factory-droid.ts +2 -0
- package/src/providers/gitlab-duo-workflow.ts +5 -2
- package/src/providers/gitlab-duo.ts +1 -0
- package/src/providers/google-auth.ts +10 -3
- package/src/providers/google-gemini-cli.ts +4 -0
- package/src/providers/google-shared.ts +134 -105
- package/src/providers/google-vertex.ts +0 -1
- package/src/providers/google.ts +1 -1
- package/src/providers/inference-headers.ts +19 -0
- package/src/providers/mock.ts +4 -0
- package/src/providers/ollama.ts +1 -1
- package/src/providers/openai-anthropic-shim.ts +1 -0
- package/src/providers/openai-codex/request-transformer.ts +16 -11
- package/src/providers/openai-codex-responses.ts +201 -76
- package/src/providers/openai-completions.ts +12 -2
- package/src/providers/openai-configuration-update.ts +54 -15
- package/src/providers/openai-responses-wire.ts +1 -55
- package/src/providers/openai-responses.ts +437 -33
- package/src/providers/openai-shared.ts +267 -72
- package/src/providers/pi-native-client.ts +2 -2
- package/src/providers/pi-native-server.ts +1 -0
- package/src/providers/systemone-server.ts +1 -1
- package/src/providers/transcriptions-server.ts +1 -1
- package/src/providers/transform-messages.ts +240 -84
- package/src/providers/vision-guard.ts +1 -1
- package/src/registry/aws.ts +51 -14
- package/src/registry/cloudflare-ai-gateway.ts +26 -3
- package/src/registry/hooks/api-key.ts +1 -0
- package/src/registry/hooks/custom.ts +2 -0
- package/src/registry/hooks/env.ts +2 -5
- package/src/registry/oauth/callback-server.ts +6 -1
- package/src/registry/oauth/cloudflare-ai-gateway.ts +2 -2
- package/src/registry/oauth/cloudflare-workers-ai.ts +32 -0
- package/src/registry/oauth/cursor.ts +80 -17
- package/src/registry/oauth/index.ts +5 -0
- package/src/registry/oauth/snowflake.ts +183 -0
- package/src/registry/registry.ts +2 -0
- package/src/registry/snowflake.ts +149 -0
- package/src/stream.ts +85 -107
- package/src/transcription/types.ts +2 -1
- package/src/types.ts +142 -15
- package/src/usage/claude.ts +19 -12
- package/src/usage/commandcode.ts +1 -1
- package/src/usage/cursor.ts +14 -25
- package/src/usage/github-copilot.ts +1 -1
- package/src/usage/kimi.ts +1 -1
- package/src/usage/minimax-code.ts +8 -1
- package/src/usage.ts +8 -0
- package/src/utils/anthropic-auth.ts +34 -10
- package/src/utils/aws-profile.ts +10 -4
- package/src/utils/block-symbols.ts +17 -0
- package/src/utils/dsml-leak.ts +109 -0
- package/src/utils/empty-completion-retry.ts +2 -3
- package/src/utils/glyph-codec.ts +200 -7
- package/src/utils/idle-iterator.ts +80 -35
- package/src/utils/leaked-thinking-stream.ts +47 -25
- package/src/utils/resolve-peer.ts +7 -0
- package/src/utils/retry-wait.ts +19 -0
- package/src/utils/schema/CONSTRAINTS.md +9 -7
- package/src/utils/schema/fields.ts +20 -0
- package/src/utils/schema/normalize.ts +65 -23
- package/src/utils/schema/wire.ts +3 -3
- package/src/utils/stream-markup-healing.ts +4 -4
- package/src/utils/thinking-loop.ts +36 -13
- package/src/utils/tool-call-arguments.ts +16 -0
- package/src/utils/transport-fetch.ts +1 -1
- package/src/utils.ts +41 -10
- package/dist/types/judgment/typesafe.d.ts +0 -49
- package/src/judgment/typesafe.ts +0 -183
package/CHANGELOG.md
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
1
|
## [Unreleased]
|
|
4
2
|
|
|
5
|
-
## [1.
|
|
6
|
-
|
|
7
|
-
- 版本线推进至 1.1.26;本版无独立用户可见变化。
|
|
8
|
-
|
|
9
|
-
## [1.1.25] - 2026-10-03
|
|
3
|
+
## [1.2.0] - 2026-10-12
|
|
10
4
|
|
|
11
|
-
- 版本线推进至 1.
|
|
5
|
+
- 版本线推进至 1.2.0;本版无独立用户可见变化。
|
|
12
6
|
|
|
13
7
|
## [1.1.24] - 2026-10-03
|
|
14
8
|
|
|
15
9
|
- 网关身份头统一 `x-zeta-*`(消费方同步);新增 `extractHttpStatusFromError`/`extractRetryHint` 导出。
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- Fixed Antigravity chat and image requests sending an outdated client version when the model list came from cache, which could make newer models such as Claude Opus 5.5 unavailable.
|
|
14
|
+
- When a DeepSeek model writes a broken DSML tool call (for example with the opening `<|DSML|tool_calls>` and `<|DSML|invoke>` tags missing), its closing tags are now kept in the streamed text instead of being dropped. This lets the agent remove exactly the broken call while keeping any text after it ([#14202](https://github.com/can1357/oh-my-pi/pull/14202) by [@H4vC](https://github.com/H4vC)).
|
|
15
|
+
|
|
16
|
+
Older entries are archived in [packages/ai/CHANGELOG.md@a20cc0d04b64](https://github.com/can1357/oh-my-pi/blob/a20cc0d04b64f4a68fcc559cbd258743d984c50e/packages/ai/CHANGELOG.md).
|
package/README.md
CHANGED
|
@@ -77,6 +77,7 @@ Unified LLM API with automatic model discovery, provider configuration, token an
|
|
|
77
77
|
- **QwenCloud Token Plan** (supports `/login alibaba-token-plan`, `ALIBABA_TOKEN_PLAN_API_KEY`, or `BAILIAN_TOKEN_PLAN_API_KEY`; interactive login first selects a region — International (Singapore, default), China (Beijing) for 百炼 Token Plan keys, or a custom base URL — since region keys are non-interchangeable, then optionally stores a `home.qwencloud.com` Cookie request header for best-effort 5-hour and 7-day quota reporting)
|
|
78
78
|
To enable quota reporting, sign in to the Token Plan dashboard, copy the `Cookie` request-header value from a `home.qwencloud.com` request in browser developer tools, and paste it at the second login prompt. Press Enter to skip; the Cookie is sensitive and session-lived, so rerun login when it expires.
|
|
79
79
|
- **Cloudflare AI Gateway** (supports `/login cloudflare-ai-gateway`, or `CLOUDFLARE_AI_GATEWAY_API_KEY` with `CLOUDFLARE_ACCOUNT_ID` and `CLOUDFLARE_GATEWAY_ID`)
|
|
80
|
+
- **Snowflake Cortex** (browser `/login snowflake`, or `SNOWFLAKE_ACCOUNT` + `SNOWFLAKE_PAT`; account-billed Cortex REST API)
|
|
80
81
|
- **Ollama** (local OpenAI-compatible runtime; optional `OLLAMA_API_KEY`)
|
|
81
82
|
- **Ollama Cloud** (hosted native Ollama API; requires `OLLAMA_CLOUD_API_KEY`)
|
|
82
83
|
- **llama.cpp** (local OpenAI and Anthropic compatible inference server)
|
|
@@ -961,6 +962,7 @@ In Node.js environments, you can set environment variables to avoid passing API
|
|
|
961
962
|
| ZenMux | `ZENMUX_API_KEY` |
|
|
962
963
|
| vLLM | `VLLM_API_KEY` |
|
|
963
964
|
| Cloudflare AI Gateway | `CLOUDFLARE_AI_GATEWAY_API_KEY` + `CLOUDFLARE_ACCOUNT_ID` + `CLOUDFLARE_GATEWAY_ID` |
|
|
965
|
+
| Snowflake Cortex | `SNOWFLAKE_PAT` + `SNOWFLAKE_ACCOUNT` |
|
|
964
966
|
| GitHub Copilot | `COPILOT_GITHUB_TOKEN` or `GH_TOKEN` or `GITHUB_TOKEN` |
|
|
965
967
|
|
|
966
968
|
`/login cloudflare-ai-gateway` collects and stores the gateway token, account ID, and gateway ID. For environment configuration, set all three Cloudflare values above. OMP derives provider endpoints from the account and gateway IDs.
|
|
@@ -1029,9 +1031,22 @@ Several providers support OAuth authentication (some also support static API key
|
|
|
1029
1031
|
- **Google Gemini CLI** (Gemini 2.0/2.5 via Google Cloud Code Assist; free tier or paid subscription)
|
|
1030
1032
|
- **Antigravity** (Free Gemini 3, Claude, GPT-OSS via Google Cloud)
|
|
1031
1033
|
- **Qwen Portal** (Qwen OAuth token or API key)
|
|
1034
|
+
- **Snowflake Cortex** (Snowflake account OAuth with PKCE and refresh, subject to the account's local-application integration)
|
|
1032
1035
|
|
|
1033
1036
|
For paid Cloud Code Assist subscriptions, set `GOOGLE_CLOUD_PROJECT` or `GOOGLE_CLOUD_PROJECT_ID` to your project ID.
|
|
1034
1037
|
|
|
1038
|
+
### Snowflake Cortex
|
|
1039
|
+
|
|
1040
|
+
`/login snowflake` prompts for an account identifier (for example, `myorg-myaccount`), an HTTPS Snowflake account URL, or a Snowsight account link. It opens Snowflake's built-in `SNOWFLAKE$LOCAL_APPLICATION` OAuth flow using PKCE and a `127.0.0.1` callback (port 54551, with an ephemeral-port fallback). The stored account selects the inference endpoint; OAuth credentials cannot be sent to a different account through a custom model endpoint.
|
|
1041
|
+
|
|
1042
|
+
The default inference role must hold either `SNOWFLAKE.CORTEX_USER` or the narrower, REST-only `SNOWFLAKE.CORTEX_REST_API_USER` (recommended for least privilege). `CORTEX_REST_API_USER` cannot be granted to a user directly; grant it to a custom role, and revoke `CORTEX_USER` from users who already hold it so the narrower control applies. The Cortex REST API is not available in China-region (`.snowflakecomputing.cn`) accounts. An administrator can check local-app availability with `SHOW SECURITY INTEGRATIONS LIKE 'SNOWFLAKE$LOCAL_APPLICATION'` and enable the integration or refresh issuance if necessary. When Snowflake issues a refresh token, omp refreshes the access token automatically. Without one, login remains valid until expiry; then sign in again or use the PAT environment alternative.
|
|
1043
|
+
|
|
1044
|
+
For PAT authentication, set both `SNOWFLAKE_ACCOUNT` and `SNOWFLAKE_PAT`. Prefer a short-lived PAT restricted to a dedicated inference role and the required user-scoped network allowlist; do not disable network-policy enforcement. See [Snowflake PAT setup](https://docs.snowflake.com/en/user-guide/programmatic-access-tokens) and [local-application OAuth](https://docs.snowflake.com/en/user-guide/oauth-local-applications).
|
|
1045
|
+
|
|
1046
|
+
Claude models use `/api/v2/cortex/v1/messages`; OpenAI models use `/api/v2/cortex/v1/chat/completions`. Both reuse omp's streaming transports and local tool execution. The 15 bundled models are a documented roster, not an account entitlement check: availability still depends on Cortex REST API enablement, role grants, model allowlists, and region. If authentication succeeds but Cortex returns `003001` ("This account is not allowed to access this endpoint"), Snowflake has not enabled Cortex REST for the account. [Self-service trial accounts disable AI features until a credit card is added](https://docs.snowflake.com/en/user-guide/admin-trial-account#label-trial-account-ai-features) (adding one does not end the trial); REST access can remain disabled even after AI SQL functions work, in which case Snowflake support must enable it. Cross-region inference requires an account administrator's explicit decision. `zetacode models snowflake --json` lists available models only after credentials are configured.
|
|
1047
|
+
|
|
1048
|
+
This provider uses the [public, account-billed Cortex REST API](https://docs.snowflake.com/en/user-guide/snowflake-cortex/cortex-rest-api). CoCo-subscription coverage is unverified. Catalog prices are per-million-token estimates from [Consumption Table 6(b)](https://www.snowflake.com/legal-files/CreditConsumptionTable.pdf) at [$2 per AI Credit](https://docs.snowflake.com/en/user-guide/snowflake-cortex/pricing) for on-demand global routing, not your contract price. Output is capped at the REST API's documented 16,384-token maximum; context windows are conservative AI_COMPLETE limits, not verified REST maxima. Cortex compatibility rules limit explicit cache retention to five minutes. Paste an account identifier or current Snowsight link; legacy `app.snowflake.com/<region>/<locator>` links are rejected because they don't map uniformly to an account host.
|
|
1049
|
+
|
|
1035
1050
|
### Vertex AI (ADC)
|
|
1036
1051
|
|
|
1037
1052
|
Vertex AI models use Application Default Credentials (ADC):
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AuthCredential, OAuthCredential, SessionsApi } from "./types.js";
|
|
1
|
+
import type { AuthCredential, OAuthCredential, SessionRestrictionLease, SessionsApi } from "./types.js";
|
|
2
2
|
import type { AuthCredentialStore } from "./store.js";
|
|
3
3
|
import type { CredentialPool } from "./pool.js";
|
|
4
4
|
import type { KeyOverrides } from "./cascade.js";
|
|
@@ -13,10 +13,31 @@ export type SessionCredential = {
|
|
|
13
13
|
/** Set only by the public user-facing pin API; automatic warm affinity leaves it absent. */
|
|
14
14
|
explicit?: true;
|
|
15
15
|
};
|
|
16
|
+
/** One installed restriction: the allowed OAuth identity keys and the lease that lifts it. */
|
|
17
|
+
type SessionRestriction = {
|
|
18
|
+
readonly allowed: ReadonlySet<string>;
|
|
19
|
+
readonly lease: SessionRestrictionLease;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Provider → session id → installed restriction. Owned by `AuthStorage` so
|
|
23
|
+
* restrictions outlive store replacement, which rebuilds every store-bound
|
|
24
|
+
* module (pins included) around the same identities.
|
|
25
|
+
*/
|
|
26
|
+
export type SessionRestrictions = Map<string, Map<string, SessionRestriction>>;
|
|
16
27
|
/** Session → credential affinity (pins), persisted in the store cache. */
|
|
17
28
|
export declare class SessionAffinity implements SessionsApi {
|
|
18
29
|
#private;
|
|
19
|
-
constructor(store: AuthCredentialStore, pool: CredentialPool, overrides: KeyOverrides);
|
|
30
|
+
constructor(store: AuthCredentialStore, pool: CredentialPool, overrides: KeyOverrides, restrictions: SessionRestrictions);
|
|
31
|
+
restrict(provider: string, sessionId: string, identityKeys: readonly string[]): SessionRestrictionLease;
|
|
32
|
+
unrestrict(provider: string, sessionId: string, lease: SessionRestrictionLease): void;
|
|
33
|
+
/** True when {@link restrict} limits `sessionId` for `provider`. */
|
|
34
|
+
isRestricted(provider: string, sessionId: string | undefined): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* True when `credential` may serve `sessionId`: always for an unrestricted
|
|
37
|
+
* session, otherwise only an OAuth credential whose identity key is allowed.
|
|
38
|
+
*/
|
|
39
|
+
allows(provider: string, sessionId: string | undefined, credential: AuthCredential): boolean;
|
|
40
|
+
permits(provider: string, sessionId: string, credentialId: number): boolean;
|
|
20
41
|
/** Drop every pin for a provider, both in memory and in the persisted cache. */
|
|
21
42
|
clearProvider(provider: string): void;
|
|
22
43
|
/** Drop only this in-memory session pin after OAuth selection falls through. */
|
|
@@ -33,7 +54,11 @@ export declare class SessionAffinity implements SessionsApi {
|
|
|
33
54
|
* rewrites were pure database churn.
|
|
34
55
|
*/
|
|
35
56
|
record(provider: string, sessionId: string | undefined, type: AuthCredential["type"], index: number, lastUsedAtMs?: number, explicit?: boolean): void;
|
|
36
|
-
/**
|
|
57
|
+
/**
|
|
58
|
+
* Retrieves the last credential used by a session. A restricted session
|
|
59
|
+
* never sees a pin outside its allowlist — inherited, restored, or recorded
|
|
60
|
+
* before the restriction — so it re-ranks inside the allowlist instead.
|
|
61
|
+
*/
|
|
37
62
|
get(provider: string, sessionId: string | undefined): SessionCredential | undefined;
|
|
38
63
|
/** Clears the last credential used by a session for a provider. */
|
|
39
64
|
clear(provider: string, sessionId: string | undefined): void;
|
|
@@ -55,14 +80,15 @@ export declare class SessionAffinity implements SessionsApi {
|
|
|
55
80
|
restoredAtMs?: number;
|
|
56
81
|
}): boolean;
|
|
57
82
|
/**
|
|
58
|
-
* Copy
|
|
83
|
+
* Copy stored credential affinities from one live session to another.
|
|
59
84
|
*
|
|
60
85
|
* The target receives its own sticky entries, so request resolution, usage
|
|
61
86
|
* blocking, credential rotation, metadata, and persisted pins all continue
|
|
62
87
|
* through the target session id without retaining a live dependency on the
|
|
63
|
-
* source session.
|
|
88
|
+
* source session. `include` limits the copy to the providers it accepts,
|
|
89
|
+
* given whether the source's affinity is an explicit user pin.
|
|
64
90
|
*/
|
|
65
|
-
inherit(sourceSessionId: string, targetSessionId: string): number;
|
|
91
|
+
inherit(sourceSessionId: string, targetSessionId: string, include?: (provider: string, explicit: boolean) => boolean): number;
|
|
66
92
|
/**
|
|
67
93
|
* Release a session's sticky credential so its next `KeyCascade.get` call
|
|
68
94
|
* re-runs native pool ranking. This never blocks or penalizes the released
|
|
@@ -71,3 +97,4 @@ export declare class SessionAffinity implements SessionsApi {
|
|
|
71
97
|
*/
|
|
72
98
|
release(provider: string, sessionId: string): boolean;
|
|
73
99
|
}
|
|
100
|
+
export {};
|
|
@@ -2,16 +2,31 @@ import { type ApiKeyResolver, type ResolvedApiKey } from "../auth-retry.js";
|
|
|
2
2
|
import type { SessionAffinity } from "./affinity.js";
|
|
3
3
|
import type { CredentialPool } from "./pool.js";
|
|
4
4
|
import type { CredentialSelector } from "./select.js";
|
|
5
|
-
import type { AuthApiKeyOptions, AuthCredential, AuthSource, AuthSourceOptions, KeysApi, LimitsApi
|
|
5
|
+
import type { AuthApiKeyOptions, AuthCredential, AuthSource, AuthSourceOptions, KeysApi, LimitsApi } from "./types.js";
|
|
6
6
|
/** Runtime (--api-key) and config (models.yml) key overrides plus the config-value resolver. */
|
|
7
7
|
export declare class KeyOverrides {
|
|
8
8
|
#private;
|
|
9
9
|
constructor(resolver?: (config: string) => Promise<string | undefined>);
|
|
10
10
|
has(provider: string): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Whether an override replaces OAuth for a session. Any override does for an
|
|
13
|
+
* unrestricted session. A restricted session skips the runtime key, but a
|
|
14
|
+
* config key still blocks OAuth: it marks the provider's endpoint (often a
|
|
15
|
+
* proxy) as taking that key, so the session fails closed instead of sending
|
|
16
|
+
* a pooled OAuth token there (see `KeyCascade.get`).
|
|
17
|
+
*/
|
|
18
|
+
suppressesOAuth(provider: string, restricted: boolean): boolean;
|
|
11
19
|
runtimeKey(provider: string): string | undefined;
|
|
12
20
|
configKey(provider: string): string | undefined;
|
|
13
21
|
/** Config value consulted only after stored OAuth/login credentials. */
|
|
14
22
|
fallbackKey(provider: string): string | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* Config value consulted only after every other source, including stored
|
|
25
|
+
* api_key credentials — the lowest cascade tier. The OMP compatibility
|
|
26
|
+
* overlay uses it so an upstream key is a pure fallback: any credential
|
|
27
|
+
* the user later stores locally wins without needing to evict the mirror.
|
|
28
|
+
*/
|
|
29
|
+
mirrorKey(provider: string): string | undefined;
|
|
15
30
|
/** Resolve a config value (env var name, "!command", literal) to the secret. */
|
|
16
31
|
resolve(config: string): Promise<string | undefined>;
|
|
17
32
|
/**
|
|
@@ -37,12 +52,17 @@ export declare class KeyOverrides {
|
|
|
37
52
|
* credentials (at the env-var tier). Providers that own a `/login` flow use
|
|
38
53
|
* this so their default key reference (e.g. an unset env-var name, which
|
|
39
54
|
* resolves to its literal text) cannot shadow the key the user logged in with.
|
|
55
|
+
*
|
|
56
|
+
* `mirror: true` ranks the value below everything else, under stored
|
|
57
|
+
* api_key credentials — a read-only compatibility mirror (upstream OMP
|
|
58
|
+
* agent.db key) that must never shadow a key the user stores locally.
|
|
40
59
|
*/
|
|
41
60
|
setConfig(provider: string, apiKeyConfig: string, options?: {
|
|
42
61
|
fallback?: boolean;
|
|
62
|
+
mirror?: boolean;
|
|
43
63
|
}): void;
|
|
44
64
|
/**
|
|
45
|
-
* Remove a single config-sourced API key (override or
|
|
65
|
+
* Remove a single config-sourced API key (override, fallback, or mirror).
|
|
46
66
|
*/
|
|
47
67
|
removeConfig(provider: string): void;
|
|
48
68
|
/**
|
|
@@ -68,7 +88,7 @@ export interface KeyCascadeDeps {
|
|
|
68
88
|
rotate: LimitsApi["rotate"];
|
|
69
89
|
sourceLabel?: string;
|
|
70
90
|
}
|
|
71
|
-
/** The provider auth precedence cascade (runtime → config → OAuth → login key → env → stored key). */
|
|
91
|
+
/** The provider auth precedence cascade (runtime → config → OAuth → login key → env → stored key → compat mirror). */
|
|
72
92
|
export declare class KeyCascade implements KeysApi {
|
|
73
93
|
#private;
|
|
74
94
|
constructor(deps: KeyCascadeDeps);
|
|
@@ -94,8 +114,8 @@ export declare class KeyCascade implements KeysApi {
|
|
|
94
114
|
/**
|
|
95
115
|
* Classify where a provider's auth comes from, following the same precedence
|
|
96
116
|
* as {@link KeyCascade.get}: runtime override → config override →
|
|
97
|
-
* stored OAuth → login-stored api_key → config fallback → env var →
|
|
98
|
-
* Returns undefined when no auth is configured.
|
|
117
|
+
* stored OAuth → login-stored api_key → config fallback → env var →
|
|
118
|
+
* stored api_key → compat mirror. Returns undefined when no auth is configured.
|
|
99
119
|
*
|
|
100
120
|
* Compact, structured counterpart to {@link KeyCascade.describe}; `env`
|
|
101
121
|
* selects dedicated-only, alias-aware, or no environment fallback.
|
|
@@ -109,14 +129,29 @@ export declare class KeyCascade implements KeysApi {
|
|
|
109
129
|
* credential to reach the correct host.
|
|
110
130
|
*/
|
|
111
131
|
peek(provider: string): Promise<string | undefined>;
|
|
112
|
-
/**
|
|
132
|
+
/**
|
|
133
|
+
* Resolve a bearer together with the stored row that supplied it, for a request.
|
|
134
|
+
* Same precedence as {@link KeyCascade.get}, but rejects with
|
|
135
|
+
* {@link AIError.OAuthRefreshUnavailableError} (transient, retryable) when OAuth
|
|
136
|
+
* refresh failed with a retryable error and no other permitted source could serve
|
|
137
|
+
* the request, so request paths retry instead of reporting a missing key.
|
|
138
|
+
*/
|
|
113
139
|
getWithCredential(provider: string, sessionId?: string, options?: AuthApiKeyOptions): Promise<ResolvedApiKey | undefined>;
|
|
114
140
|
/**
|
|
115
141
|
* Get API key for a provider.
|
|
116
142
|
* Priority (first match wins): runtime override, config override, OAuth,
|
|
143
|
+
* login API key, environment variable, another stored API key, then the
|
|
144
|
+
* lowest-tier OMP compat mirror.
|
|
117
145
|
* login API key, environment variable, then another stored API key.
|
|
146
|
+
* A session restricted by `sessions.restrict` resolves only its allowed
|
|
147
|
+
* OAuth accounts and throws {@link AIError.MissingApiKeyError} when none
|
|
148
|
+
* can serve or a config key (a models.yml `apiKey`, often for a proxy
|
|
149
|
+
* `baseUrl`) owns the provider, rather than using any other credential.
|
|
150
|
+
* A transient OAuth refresh failure resolves `undefined`, so availability
|
|
151
|
+
* probes move on to their next candidate; request paths use
|
|
152
|
+
* {@link KeyCascade.getWithCredential}, which surfaces it.
|
|
118
153
|
*/
|
|
119
|
-
get(provider: string, sessionId?: string, options?: AuthApiKeyOptions
|
|
154
|
+
get(provider: string, sessionId?: string, options?: AuthApiKeyOptions): Promise<string | undefined>;
|
|
120
155
|
/**
|
|
121
156
|
* Build an {@link ApiKeyResolver} backed by this storage, implementing the
|
|
122
157
|
* central a/b/c auth-retry policy:
|
|
@@ -143,6 +178,7 @@ export declare class KeyCascade implements KeysApi {
|
|
|
143
178
|
* 4. API key persisted by a successful `/login`.
|
|
144
179
|
* 5. Env var — overrides a stored static api_key (e.g. a stale broker copy).
|
|
145
180
|
* 6. Stored api_key credential.
|
|
181
|
+
* 7. OMP compatibility mirror (upstream agent.db fallback key).
|
|
146
182
|
*
|
|
147
183
|
* The string is purely informational; consumers must not parse it.
|
|
148
184
|
*/
|
|
@@ -151,6 +187,7 @@ export declare class KeyCascade implements KeysApi {
|
|
|
151
187
|
removeRuntime(provider: string): void;
|
|
152
188
|
setConfig(provider: string, apiKeyConfig: string, options?: {
|
|
153
189
|
fallback?: boolean;
|
|
190
|
+
mirror?: boolean;
|
|
154
191
|
}): void;
|
|
155
192
|
removeConfig(provider: string): void;
|
|
156
193
|
clearConfig(): void;
|
|
@@ -37,9 +37,11 @@ export declare class OAuthAccounts implements OAuthApi {
|
|
|
37
37
|
* `enterpriseUrl`). For pure "give me the bytes for `Authorization`"
|
|
38
38
|
* scenarios, prefer API-key resolution.
|
|
39
39
|
*
|
|
40
|
-
* Returns `undefined` when no OAuth credential is available
|
|
41
|
-
*
|
|
42
|
-
* OAuth with an explicit API key.
|
|
40
|
+
* Returns `undefined` when no usable OAuth credential is available
|
|
41
|
+
* (none stored, or every one definitively failed to refresh) or
|
|
42
|
+
* runtime/config overrides have replaced OAuth with an explicit API key.
|
|
43
|
+
* Rejects with {@link AIError.OAuthRefreshUnavailableError} (transient,
|
|
44
|
+
* retryable) when a retryable refresh failure left no usable credential.
|
|
43
45
|
*/
|
|
44
46
|
access(provider: string, sessionId?: string, options?: AuthApiKeyOptions): Promise<OAuthAccess | undefined>;
|
|
45
47
|
/**
|
|
@@ -65,9 +67,10 @@ export declare class OAuthAccounts implements OAuthApi {
|
|
|
65
67
|
* Resolve one stored OAuth credential by its durable storage row id.
|
|
66
68
|
*
|
|
67
69
|
* Unlike the normal session resolver, this method never ranks, rotates, or
|
|
68
|
-
* falls back to sibling credentials. A forced refresh re-mints only the
|
|
69
|
-
* requested row
|
|
70
|
-
*
|
|
70
|
+
* falls back to sibling credentials. A generic forced refresh re-mints only the
|
|
71
|
+
* requested row; with `refreshReason: "auth-recovery"` it may instead reuse a
|
|
72
|
+
* still-usable recent mint of that row. Either way exact-account affinity holds
|
|
73
|
+
* for operations whose provenance and policy boundary are tied to one workspace.
|
|
71
74
|
*
|
|
72
75
|
* Returns `undefined` when the row does not exist for `provider` or an
|
|
73
76
|
* explicit runtime/config API-key override suppresses OAuth.
|
|
@@ -14,6 +14,7 @@ export declare class AccountPolicies {
|
|
|
14
14
|
*/
|
|
15
15
|
replace(policies: AuthAccountPolicies, defaultReservePct: number | undefined, storedCredentials?: ReadonlyMap<string, readonly AuthCredential[]>): void;
|
|
16
16
|
validateUsageCapability(provider: string, canFetchUsage: boolean): void;
|
|
17
|
+
/** Throw on ambiguous selectors for `provider`; a selector whose account is absent is inert and warns once. */
|
|
17
18
|
validateFor(provider: string, credentials: readonly AuthCredential[]): void;
|
|
18
19
|
/**
|
|
19
20
|
* Return the configured account policy matching an OAuth identity.
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { OAuthCredentials } from "../registry/oauth/types.js";
|
|
2
2
|
import type { Provider } from "../types.js";
|
|
3
3
|
import { type CredentialPool } from "./pool.js";
|
|
4
|
-
import type { AccountPolicies } from "./policy.js";
|
|
5
4
|
import { type AuthCredentialStore } from "./store.js";
|
|
6
5
|
import { type AuthCredentialSnapshotEntry, type AuthStorageOptions, type OAuthCredential, type OAuthRefreshByIdOptions, type OAuthRefreshReason, type StoredOAuthRefreshOptions, type StoredOAuthRefreshResult } from "./types.js";
|
|
7
6
|
/**
|
|
@@ -30,8 +29,8 @@ export declare function mergeRefreshedCredential<T extends OAuthCredential>(curr
|
|
|
30
29
|
export interface OAuthRefresherDeps {
|
|
31
30
|
store: AuthCredentialStore;
|
|
32
31
|
pool: CredentialPool;
|
|
33
|
-
policies: AccountPolicies;
|
|
34
32
|
override?: AuthStorageOptions["refreshOAuthCredential"];
|
|
33
|
+
overrideMints?: AuthStorageOptions["refreshOAuthCredentialMints"];
|
|
35
34
|
}
|
|
36
35
|
/** Single-flighted, lease-guarded OAuth refresh with compare-and-set persistence. */
|
|
37
36
|
export declare class OAuthRefresher {
|
|
@@ -8,7 +8,7 @@ import { type CredentialPool } from "./pool.js";
|
|
|
8
8
|
import { type ApiKeySelection, type OAuthSelection } from "./rank.js";
|
|
9
9
|
import { type OAuthRefresher } from "./refresh.js";
|
|
10
10
|
import type { AuthCredentialStore } from "./store.js";
|
|
11
|
-
import type
|
|
11
|
+
import { type ApiKeyCredential, type AuthApiKeyOptions, type AuthCredential, type OAuthCredential } from "./types.js";
|
|
12
12
|
import type { UsageService } from "./usage.js";
|
|
13
13
|
/** Temporary block after a transient OAuth refresh failure. */
|
|
14
14
|
export declare const OAUTH_REFRESH_FAILURE_BACKOFF_MS: number;
|
|
@@ -32,6 +32,8 @@ export type TryOAuthOptions = {
|
|
|
32
32
|
blockScopes?: readonly string[];
|
|
33
33
|
/** When false, a definitive failure of THIS credential returns undefined instead of falling back to the ranked/round-robin selector (target-only resolution). */
|
|
34
34
|
allowFallback?: boolean;
|
|
35
|
+
/** Receives a non-definitive refresh failure that left this credential unusable; the caller filters for retryable ones. */
|
|
36
|
+
onTransientRefreshFailure?: (error: unknown) => void;
|
|
35
37
|
};
|
|
36
38
|
/** Services consulted by CredentialSelector for policy, usage, blocks, refresh, and session affinity. */
|
|
37
39
|
export interface CredentialSelectorDeps {
|
|
@@ -69,9 +71,9 @@ export declare class CredentialSelector {
|
|
|
69
71
|
* 1. strict: unblocked credentials only, usage limits respected, plan
|
|
70
72
|
* filter enforced (when any account is confirmed eligible);
|
|
71
73
|
* 2. plan-fitting last resort: same plan filter, but blocked/exhausted
|
|
72
|
-
* accounts are allowed (
|
|
73
|
-
* first) so the caller gets real
|
|
74
|
-
* instead of a missing key;
|
|
74
|
+
* accounts are allowed (an explicit session pin first, then blocked
|
|
75
|
+
* candidates earliest-unblocking first) so the caller gets real
|
|
76
|
+
* usage-limit semantics from the wire instead of a missing key;
|
|
75
77
|
* 3. unfiltered last resort: the plan filter matched nothing usable —
|
|
76
78
|
* skip it and try every account once; the server is the final arbiter
|
|
77
79
|
* of model access.
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
import type { Database } from "bun:sqlite";
|
|
8
8
|
import { isSqliteBusyError, isSqliteCorruptionError } from "@linxiraos/pi-utils";
|
|
9
9
|
import type { AuthCredentialStore, CredentialRefreshLeaseFence } from "./store.js";
|
|
10
|
-
import type { AuthCredential, DisabledCredentialSummary, StoredAuthCredential, StoredCredentialBlock } from "./types.js";
|
|
10
|
+
import type { AuthCredential, DisabledCredentialSummary, OAuthCredential, StoredAuthCredential, StoredCredentialBlock } from "./types.js";
|
|
11
11
|
import type { OAuthCredentials } from "../registry/oauth/types.js";
|
|
12
12
|
import type { ClientUsageReport, ClientUsageSummary, UsageHistoryEntry, UsageHistoryQuery } from "../usage.js";
|
|
13
13
|
export declare const USAGE_REPORT_TTL_MS: number;
|
|
@@ -18,7 +18,26 @@ type SerializedCredentialRecord = {
|
|
|
18
18
|
};
|
|
19
19
|
export { isSqliteBusyError, isSqliteCorruptionError };
|
|
20
20
|
export declare function serializeCredential(provider: string, credential: AuthCredential): SerializedCredentialRecord | null;
|
|
21
|
+
/**
|
|
22
|
+
* Identity key of a credential: the `identityKey` that broker snapshots carry
|
|
23
|
+
* and that broker account pools and `sessions.restrict` match, such as
|
|
24
|
+
* `email:<address>|org:<id>` for org-scoped providers. `null` for an API key or
|
|
25
|
+
* an OAuth credential without any account, email, project, or org identity.
|
|
26
|
+
*/
|
|
21
27
|
export declare function resolveCredentialIdentityKey(provider: string, credential: AuthCredential): string | null;
|
|
28
|
+
/**
|
|
29
|
+
* Whether upserting `incoming` replaces the active row holding `existing` (stored
|
|
30
|
+
* under `existingIdentityKey`) instead of inserting a new row beside it.
|
|
31
|
+
*/
|
|
32
|
+
export declare function matchesReplacementCredential(provider: string, existing: AuthCredential | null, existingIdentityKey: string | null, incoming: AuthCredential): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Whether two OAuth credentials belong to the same account of the same organization,
|
|
35
|
+
* whichever identity key each is stored under. The email decides when both carry one;
|
|
36
|
+
* otherwise a matching account id does, unless it is the organization's own id (a
|
|
37
|
+
* ChatGPT workspace id shared by every member). Project ids are shared between users
|
|
38
|
+
* (every consumer Antigravity login has the same one) and never count.
|
|
39
|
+
*/
|
|
40
|
+
export declare function isSameOAuthAccount(a: OAuthCredential, b: OAuthCredential): boolean;
|
|
22
41
|
/**
|
|
23
42
|
* Default SQLite-backed implementation of {@link AuthCredentialStore}.
|
|
24
43
|
*
|
|
@@ -60,6 +79,7 @@ export declare class SqliteAuthCredentialStore implements AuthCredentialStore {
|
|
|
60
79
|
deleteCachePrefix(prefix: string): void;
|
|
61
80
|
cleanExpiredCache(): void;
|
|
62
81
|
getCredentialBlock(credentialId: number, providerKey: string, blockScope: string): number | undefined;
|
|
82
|
+
getCredentialBlockScopes(credentialId: number, providerKey: string): Map<string, number>;
|
|
63
83
|
getCredentialBlockReconcileAfter(credentialId: number, providerKey: string, blockScope: string): number | undefined;
|
|
64
84
|
upsertCredentialBlock(block: StoredCredentialBlock): void;
|
|
65
85
|
deleteCredentialBlock(credentialId: number, providerKey: string, blockScope: string): void;
|
|
@@ -71,6 +71,12 @@ export interface CredentialCacheStore {
|
|
|
71
71
|
export interface CredentialBlockStore {
|
|
72
72
|
/** Non-expired block for one (credential, providerKey, scope) key, or undefined. */
|
|
73
73
|
getCredentialBlock(credentialId: number, providerKey: string, blockScope: string): number | undefined;
|
|
74
|
+
/**
|
|
75
|
+
* Non-expired blocks for one credential and providerKey, keyed by block
|
|
76
|
+
* scope (`""` is unscoped), in one read. Optional: selectors fall back to
|
|
77
|
+
* {@link CredentialBlockStore.getCredentialBlock} per scope when absent.
|
|
78
|
+
*/
|
|
79
|
+
getCredentialBlockScopes?(credentialId: number, providerKey: string): ReadonlyMap<string, number>;
|
|
74
80
|
/** Earliest time a shared-store block should be eligible for live-usage reconciliation. */
|
|
75
81
|
getCredentialBlockReconcileAfter(credentialId: number, providerKey: string, blockScope: string): number | undefined;
|
|
76
82
|
/** Upsert with MAX semantics: keep the later blockedUntilMs on conflict. */
|
|
@@ -116,8 +122,8 @@ export interface UsageLedgerStore {
|
|
|
116
122
|
}
|
|
117
123
|
/** Broker-delegated OAuth refresh and usage-report operations. */
|
|
118
124
|
export interface CredentialUpstream {
|
|
119
|
-
/** Optional hook to notify the underlying store that usage
|
|
120
|
-
invalidateUsageCache(signal?: AbortSignal): Promise<void>;
|
|
125
|
+
/** Optional hook to notify the underlying store that usage reports (one provider's, when given) are stale. */
|
|
126
|
+
invalidateUsageCache(provider?: string, signal?: AbortSignal): Promise<void>;
|
|
121
127
|
/**
|
|
122
128
|
* Optional store-supplied OAuth refresh. When present, `AuthStorage` uses
|
|
123
129
|
* it before the per-provider local refresh path. `RemoteAuthCredentialStore`
|
|
@@ -220,6 +220,8 @@ export interface CheckCredentialsOptions {
|
|
|
220
220
|
completionProbe?: CompletionProbe;
|
|
221
221
|
/** Per-credential completion probe timeout (ms). Defaults to `timeoutMs`. */
|
|
222
222
|
completionTimeoutMs?: number;
|
|
223
|
+
/** Providers whose credentials are skipped entirely (not probed, not reported). */
|
|
224
|
+
excludeProviders?: ReadonlySet<string>;
|
|
223
225
|
}
|
|
224
226
|
/**
|
|
225
227
|
* Sentinel value placed in OAuth `refresh` fields when a credential is shared
|
|
@@ -314,6 +316,12 @@ export type AuthStorageOptions = {
|
|
|
314
316
|
* the actual refresh token never leaves the broker.
|
|
315
317
|
*/
|
|
316
318
|
refreshOAuthCredential?: (provider: Provider, credentialId: number, credential: OAuthCredential, signal?: AbortSignal, reason?: OAuthRefreshReason) => Promise<OAuthCredentials>;
|
|
319
|
+
/**
|
|
320
|
+
* Set when {@link refreshOAuthCredential} exchanges the refresh token with the
|
|
321
|
+
* provider itself instead of delegating (e.g. to a broker), so its tokens count
|
|
322
|
+
* as this process's mints for auth-recovery reuse.
|
|
323
|
+
*/
|
|
324
|
+
refreshOAuthCredentialMints?: boolean;
|
|
317
325
|
/**
|
|
318
326
|
* Human-readable description of the credential store backing this
|
|
319
327
|
* AuthStorage instance. Surfaced through {@link AuthStorage.keys.describe}
|
|
@@ -407,7 +415,7 @@ export interface ModelUsageHealthOptions {
|
|
|
407
415
|
export type AuthApiKeyOptions = {
|
|
408
416
|
baseUrl?: string;
|
|
409
417
|
modelId?: string;
|
|
410
|
-
/**
|
|
418
|
+
/** {@link oauthAccountKey}s of accounts known to serve `modelId` from multi-account discovery; OAuth selection prefers them and tries other accounts only as a last resort. */
|
|
411
419
|
accountIds?: readonly string[];
|
|
412
420
|
/**
|
|
413
421
|
* Caller's cancel signal. Threaded into any broker-bound OAuth refresh so
|
|
@@ -424,7 +432,22 @@ export type AuthApiKeyOptions = {
|
|
|
424
432
|
forceRefresh?: boolean;
|
|
425
433
|
/** Explicit provider-401 recovery; generic force refreshes leave this unset. */
|
|
426
434
|
refreshReason?: OAuthRefreshReason;
|
|
435
|
+
/** When false, select as `sessionId` would without recording the choice as that session's sticky credential. */
|
|
436
|
+
recordAffinity?: boolean;
|
|
427
437
|
};
|
|
438
|
+
/**
|
|
439
|
+
* Identity that multi-account discovery records in `Model.accountAccess` and
|
|
440
|
+
* that {@link AuthApiKeyOptions.accountIds} selection matches: the provider
|
|
441
|
+
* account id (Codex `chatgpt_account_id`), else the login email for providers
|
|
442
|
+
* whose credentials carry no account id (Antigravity), else the project id for
|
|
443
|
+
* a login whose email lookup failed. Same precedence as the credential store's
|
|
444
|
+
* identity key.
|
|
445
|
+
*/
|
|
446
|
+
export declare function oauthAccountKey(identity: {
|
|
447
|
+
accountId?: string;
|
|
448
|
+
email?: string;
|
|
449
|
+
projectId?: string;
|
|
450
|
+
}): string | undefined;
|
|
428
451
|
/** Non-secret identity bound to the OAuth credential selected for one request attempt. */
|
|
429
452
|
export interface OAuthRequestIdentity {
|
|
430
453
|
orgId?: string;
|
|
@@ -808,9 +831,22 @@ export interface KeysApi {
|
|
|
808
831
|
* 4. API key persisted by a successful `/login`
|
|
809
832
|
* 5. Environment variable
|
|
810
833
|
* 6. Stored API key (e.g. a broker-migrated copy) — last resort, so an explicit env var wins
|
|
834
|
+
* 7. OMP compatibility mirror — below every stored credential, so a key the
|
|
835
|
+
* user saves locally always wins over the upstream fallback
|
|
836
|
+
*
|
|
837
|
+
* Resolves `undefined` when no permitted credential can serve, including when
|
|
838
|
+
* OAuth refresh failed transiently (network, timeout, 5xx), so availability
|
|
839
|
+
* probes move on to their next candidate. Request paths use
|
|
840
|
+
* {@link KeysApi.getWithCredential}, which surfaces that failure.
|
|
811
841
|
*/
|
|
812
842
|
get(provider: string, sessionId?: string, options?: AuthApiKeyOptions): Promise<string | undefined>;
|
|
813
|
-
/**
|
|
843
|
+
/**
|
|
844
|
+
* Resolve a bearer together with its durable stored credential row id, when known.
|
|
845
|
+
* Same precedence as {@link KeysApi.get}, but rejects with
|
|
846
|
+
* `OAuthRefreshUnavailableError` (transient, retryable) when OAuth refresh failed
|
|
847
|
+
* with a retryable error and no other permitted source could serve the request;
|
|
848
|
+
* the stored credential is kept.
|
|
849
|
+
*/
|
|
814
850
|
getWithCredential(provider: string, sessionId?: string, options?: AuthApiKeyOptions): Promise<ResolvedApiKey | undefined>;
|
|
815
851
|
/**
|
|
816
852
|
* Peek at API key for a provider without refreshing OAuth tokens.
|
|
@@ -865,12 +901,17 @@ export interface KeysApi {
|
|
|
865
901
|
* `fallback: true` ranks the value below stored OAuth and `/login`
|
|
866
902
|
* credentials instead, so a provider's default key reference cannot shadow
|
|
867
903
|
* a key the user logged in with.
|
|
904
|
+
*
|
|
905
|
+
* `mirror: true` ranks the value below everything else, under stored
|
|
906
|
+
* api_key credentials — a read-only compatibility mirror (upstream OMP
|
|
907
|
+
* agent.db key) that must never shadow a locally stored key.
|
|
868
908
|
*/
|
|
869
909
|
setConfig(provider: string, apiKeyConfig: string, options?: {
|
|
870
910
|
fallback?: boolean;
|
|
911
|
+
mirror?: boolean;
|
|
871
912
|
}): void;
|
|
872
913
|
/**
|
|
873
|
-
* Remove a single config-sourced API key (override or
|
|
914
|
+
* Remove a single config-sourced API key (override, fallback, or mirror).
|
|
874
915
|
*/
|
|
875
916
|
removeConfig(provider: string): void;
|
|
876
917
|
/**
|
|
@@ -902,7 +943,7 @@ export interface KeysApi {
|
|
|
902
943
|
modelId?: string;
|
|
903
944
|
}): ApiKeyResolver;
|
|
904
945
|
}
|
|
905
|
-
/** Controls whether a row-id refresh may reuse a
|
|
946
|
+
/** Controls whether a row-id refresh may reuse a recently minted token. */
|
|
906
947
|
export interface OAuthRefreshByIdOptions {
|
|
907
948
|
/**
|
|
908
949
|
* Return the stored credential when it still holds a fresh access token this
|
|
@@ -910,6 +951,12 @@ export interface OAuthRefreshByIdOptions {
|
|
|
910
951
|
* refresh tokens repeatedly when a provider rejects every valid bearer.
|
|
911
952
|
*/
|
|
912
953
|
reuseRecentMint?: boolean;
|
|
954
|
+
/**
|
|
955
|
+
* Provider-401 recovery intent. Forwarded to a delegated refresh (an auth
|
|
956
|
+
* broker), which may then return a token it minted in the last five minutes.
|
|
957
|
+
* Local reuse still requires {@link reuseRecentMint}.
|
|
958
|
+
*/
|
|
959
|
+
reason?: OAuthRefreshReason;
|
|
913
960
|
}
|
|
914
961
|
/** OAuth login, access, account identity, and refresh operations. */
|
|
915
962
|
export interface OAuthApi {
|
|
@@ -931,9 +978,11 @@ export interface OAuthApi {
|
|
|
931
978
|
* `enterpriseUrl`). For pure "give me the bytes for `Authorization`"
|
|
932
979
|
* scenarios, prefer {@link AuthStorage.keys.get}.
|
|
933
980
|
*
|
|
934
|
-
* Returns `undefined` when no OAuth credential is available
|
|
935
|
-
*
|
|
936
|
-
* OAuth with an explicit API key.
|
|
981
|
+
* Returns `undefined` when no usable OAuth credential is available (none
|
|
982
|
+
* stored, or every one definitively failed to refresh) or runtime/config
|
|
983
|
+
* overrides have replaced OAuth with an explicit API key. Rejects with
|
|
984
|
+
* `OAuthRefreshUnavailableError` (transient, retryable) when a retryable
|
|
985
|
+
* refresh failure left no usable credential.
|
|
937
986
|
*/
|
|
938
987
|
access(provider: string, sessionId?: string, options?: AuthApiKeyOptions): Promise<OAuthAccess | undefined>;
|
|
939
988
|
/**
|
|
@@ -949,9 +998,10 @@ export interface OAuthApi {
|
|
|
949
998
|
* Resolve one stored OAuth credential by its durable storage row id.
|
|
950
999
|
*
|
|
951
1000
|
* Unlike the normal session resolver, this method never ranks, rotates, or
|
|
952
|
-
* falls back to sibling credentials. A forced refresh re-mints only the
|
|
953
|
-
* requested row
|
|
954
|
-
*
|
|
1001
|
+
* falls back to sibling credentials. A generic forced refresh re-mints only the
|
|
1002
|
+
* requested row; with `refreshReason: "auth-recovery"` it may instead reuse a
|
|
1003
|
+
* still-usable recent mint of that row. Either way exact-account affinity holds
|
|
1004
|
+
* for operations whose provenance and policy boundary are tied to one workspace.
|
|
955
1005
|
*
|
|
956
1006
|
* Returns `undefined` when the row does not exist for `provider` or an
|
|
957
1007
|
* explicit runtime/config API-key override suppresses OAuth.
|
|
@@ -985,8 +1035,9 @@ export interface OAuthApi {
|
|
|
985
1035
|
* Refresh the OAuth credential with the given id through a per-credential
|
|
986
1036
|
* single-flight. Concurrent callers for the same row await the same upstream
|
|
987
1037
|
* refresh attempt, which is required for providers that rotate refresh tokens
|
|
988
|
-
* on every successful refresh. Mints
|
|
989
|
-
*
|
|
1038
|
+
* on every successful refresh. Mints unless {@link OAuthRefreshByIdOptions.reuseRecentMint}
|
|
1039
|
+
* finds a recent local mint, or a delegated refresh given
|
|
1040
|
+
* {@link OAuthRefreshByIdOptions.reason} `"auth-recovery"` returns its recent mint.
|
|
990
1041
|
*/
|
|
991
1042
|
refresh(id: number, signal?: AbortSignal, options?: OAuthRefreshByIdOptions): Promise<AuthCredentialSnapshotEntry>;
|
|
992
1043
|
/**
|
|
@@ -994,6 +1045,11 @@ export interface OAuthApi {
|
|
|
994
1045
|
*/
|
|
995
1046
|
refreshStored<T extends OAuthCredential = OAuthCredential>(provider: string, options: StoredOAuthRefreshOptions<T>): Promise<StoredOAuthRefreshResult<T>>;
|
|
996
1047
|
}
|
|
1048
|
+
/**
|
|
1049
|
+
* Handle for one {@link SessionsApi.restrict} call. {@link SessionsApi.unrestrict}
|
|
1050
|
+
* lifts a restriction only with the lease of the call that installed it.
|
|
1051
|
+
*/
|
|
1052
|
+
export type SessionRestrictionLease = symbol;
|
|
997
1053
|
/** Session credential affinity operations. */
|
|
998
1054
|
export interface SessionsApi {
|
|
999
1055
|
/**
|
|
@@ -1013,14 +1069,42 @@ export interface SessionsApi {
|
|
|
1013
1069
|
restoredAtMs?: number;
|
|
1014
1070
|
}): boolean;
|
|
1015
1071
|
/**
|
|
1016
|
-
* Copy
|
|
1072
|
+
* Copy stored credential affinities from one live session to another.
|
|
1017
1073
|
*
|
|
1018
1074
|
* The target receives its own sticky entries, so request resolution, usage
|
|
1019
1075
|
* blocking, credential rotation, metadata, and persisted pins all continue
|
|
1020
1076
|
* through the target session id without retaining a live dependency on the
|
|
1021
|
-
* source session.
|
|
1077
|
+
* source session. `include` limits the copy to the providers it accepts,
|
|
1078
|
+
* given whether the source's affinity is an explicit user pin.
|
|
1079
|
+
*/
|
|
1080
|
+
inherit(sourceSessionId: string, targetSessionId: string, include?: (provider: string, explicit: boolean) => boolean): number;
|
|
1081
|
+
/**
|
|
1082
|
+
* Restrict one session's credentials for `provider` to the OAuth accounts
|
|
1083
|
+
* whose identity key (`email:<address>|org:<id>` for org-scoped providers;
|
|
1084
|
+
* otherwise `account:`, `email:`, or `project:` — the keys broker account
|
|
1085
|
+
* pools use) is listed. Selection, session pins, inherited affinity,
|
|
1086
|
+
* fallback passes, and rotation never leave the list, and runtime, config,
|
|
1087
|
+
* environment, and stored API keys are not used for that session; when no
|
|
1088
|
+
* listed account can serve, key resolution fails instead of borrowing
|
|
1089
|
+
* another account. An empty list allows no account. Replaces any earlier
|
|
1090
|
+
* restriction for the same provider and session; {@link inherit} does not
|
|
1091
|
+
* copy restrictions. Restrictions are never evicted, because that would
|
|
1092
|
+
* widen a live session; the owner passes the returned lease to
|
|
1093
|
+
* {@link unrestrict} when the session ends.
|
|
1094
|
+
*/
|
|
1095
|
+
restrict(provider: string, sessionId: string, identityKeys: readonly string[]): SessionRestrictionLease;
|
|
1096
|
+
/**
|
|
1097
|
+
* Lift the restriction that `lease` installed. Once a later {@link restrict}
|
|
1098
|
+
* call has replaced it for the same provider and session, this does nothing,
|
|
1099
|
+
* so a stale owner never lifts the restriction a newer owner relies on.
|
|
1100
|
+
*/
|
|
1101
|
+
unrestrict(provider: string, sessionId: string, lease: SessionRestrictionLease): void;
|
|
1102
|
+
/**
|
|
1103
|
+
* Whether stored credential `credentialId` may serve `sessionId`: always for
|
|
1104
|
+
* an unrestricted session, otherwise only an OAuth account its
|
|
1105
|
+
* {@link restrict} list names.
|
|
1022
1106
|
*/
|
|
1023
|
-
|
|
1107
|
+
permits(provider: string, sessionId: string, credentialId: number): boolean;
|
|
1024
1108
|
/**
|
|
1025
1109
|
* Release a session's sticky credential so its next {@link getApiKey} call
|
|
1026
1110
|
* re-runs native pool ranking. This never blocks or penalizes the released
|