@gajae-code/ai 0.13.2 → 0.14.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 +61 -2
- package/dist/types/auth-broker/client.d.ts +9 -1
- package/dist/types/auth-broker/redact.d.ts +7 -0
- package/dist/types/auth-broker/remote-store.d.ts +50 -9
- package/dist/types/auth-broker/types.d.ts +14 -0
- package/dist/types/auth-broker/wire-schemas.d.ts +25 -0
- package/dist/types/auth-storage.d.ts +200 -6
- package/dist/types/core.d.ts +1 -0
- package/dist/types/model-cache.d.ts +4 -1
- package/dist/types/model-manager.d.ts +11 -0
- package/dist/types/provider-models/openai-compat.d.ts +5 -0
- package/dist/types/provider-models/special.d.ts +3 -0
- package/dist/types/providers/anthropic.d.ts +31 -0
- package/dist/types/providers/cursor.d.ts +9 -1
- package/dist/types/providers/kiro-codewhisperer.d.ts +8 -0
- package/dist/types/providers/mock.d.ts +8 -0
- package/dist/types/providers/register-builtins.d.ts +1 -0
- package/dist/types/providers/transform-messages.d.ts +18 -0
- package/dist/types/types.d.ts +34 -8
- package/dist/types/usage/grok-cli.d.ts +5 -0
- package/dist/types/usage.d.ts +6 -0
- package/dist/types/utils/discovery/openai-compatible.d.ts +5 -0
- package/dist/types/utils/event-stream.d.ts +4 -2
- package/dist/types/utils/fallback-transport.d.ts +10 -0
- package/dist/types/utils/http-inspector.d.ts +1 -0
- package/dist/types/utils/idle-iterator.d.ts +13 -1
- package/dist/types/utils/json-parse.d.ts +19 -0
- package/dist/types/utils/oauth/callback-server.d.ts +13 -0
- package/dist/types/utils/oauth/kiro.d.ts +71 -0
- package/dist/types/utils/oauth/types.d.ts +1 -1
- package/dist/types/utils/parse-bind.d.ts +8 -5
- package/dist/types/utils/tool-call-healing.d.ts +7 -0
- package/dist/types/utils/tool-choice-capability.d.ts +11 -0
- package/package.json +3 -2
- package/src/auth-broker/client.ts +30 -0
- package/src/auth-broker/redact.ts +15 -0
- package/src/auth-broker/refresher.ts +4 -2
- package/src/auth-broker/remote-store.ts +693 -70
- package/src/auth-broker/server.ts +57 -12
- package/src/auth-broker/types.ts +16 -0
- package/src/auth-broker/wire-schemas.ts +21 -0
- package/src/auth-gateway/server.ts +84 -19
- package/src/auth-storage.ts +985 -41
- package/src/core.ts +1 -0
- package/src/model-cache.ts +23 -4
- package/src/model-manager.ts +70 -11
- package/src/model-thinking.ts +45 -1
- package/src/models.json +9604 -1932
- package/src/openai-completions-compat.ts +2 -1
- package/src/provider-models/descriptors.ts +7 -1
- package/src/provider-models/openai-compat.ts +52 -28
- package/src/provider-models/special.ts +12 -0
- package/src/providers/amazon-bedrock.ts +2 -1
- package/src/providers/anthropic.ts +831 -27
- package/src/providers/cursor.ts +83 -3
- package/src/providers/kiro-codewhisperer.ts +572 -0
- package/src/providers/mock.ts +15 -2
- package/src/providers/ollama.ts +9 -2
- package/src/providers/openai-codex-responses.ts +16 -9
- package/src/providers/openai-completions.ts +6 -1
- package/src/providers/openai-responses-shared.ts +180 -18
- package/src/providers/register-builtins.ts +24 -2
- package/src/providers/transform-messages.ts +64 -1
- package/src/stream.ts +25 -2
- package/src/types.ts +36 -7
- package/src/usage/grok-cli.ts +86 -1
- package/src/usage.ts +7 -0
- package/src/utils/discovery/openai-compatible.ts +89 -4
- package/src/utils/event-stream.ts +11 -2
- package/src/utils/fallback-transport.ts +44 -2
- package/src/utils/http-inspector.ts +1 -0
- package/src/utils/idle-iterator.ts +29 -6
- package/src/utils/json-parse.ts +80 -0
- package/src/utils/oauth/callback-server.ts +31 -1
- package/src/utils/oauth/index.ts +14 -1
- package/src/utils/oauth/kiro.ts +448 -0
- package/src/utils/oauth/synthetic.ts +2 -3
- package/src/utils/oauth/types.ts +1 -0
- package/src/utils/parse-bind.ts +27 -0
- package/src/utils/tool-call-healing.ts +13 -2
- package/src/utils/tool-choice-capability.ts +386 -6
package/CHANGELOG.md
CHANGED
|
@@ -2,8 +2,69 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.14.0] - 2026-08-17
|
|
6
|
+
- Cursor native tool calls (shell/read/write/… oneof variants) now convert their protobuf payloads into plain JSON-safe data before attaching them as toolCall `arguments`: `$typeName` markers are stripped, safe-range bigints become numbers (decimal strings beyond `Number.MAX_SAFE_INTEGER`), byte arrays become base64 strings, and cycles/functions collapse to null. Raw protobuf-es payloads carry `bigint` fields (`fileSize`, `durationMs`, `fileOutputThresholdBytes`, …) that defeat `JSON.stringify`, which broke managed snapshot staging, JSONL transcript persistence, and provider replay — the issue #4578 local-snapshot producer defect class fixed at its producer boundary.
|
|
7
|
+
- Generic OpenAI-compatible `/v1/models` discovery now reads served context-window and output-limit metadata instead of defaulting every dynamically listed model to the unknown-window sentinel. `max_model_len` (vLLM/SGLang/oMLX), `context_length`, `context_window`, `max_context_length` (LM Studio), and `max_position_embeddings` populate `contextWindow` in that precedence order, while `max_tokens`/`max_output_tokens` populate `maxTokens`; total-window fields never leak into the output-token ceiling. Malformed values (non-finite, zero, negative, non-numeric) are rejected per-field with fallback to the next candidate, so a `1e400`-style catalog entry can no longer poison compaction thresholds or compact-input budgets.
|
|
8
|
+
- Codex websocket requests now abort and close their transport when the downstream event-stream consumer returns early (including managed provisional-buffer rejection), so the next turn opens a clean connection instead of inheriting `websocket request already in progress` (#4534).
|
|
9
|
+
- Refreshed the bundled ZAI catalog with GLM-5.3 and made it the provider's default model.
|
|
10
|
+
- Added the typed `local_snapshot_failure` and `local_buffer_overflow` assistant error kinds so downstream retry policy can distinguish local event-snapshot and staging-buffer failures from provider failures.
|
|
11
|
+
- Anthropic first-event timeouts now report safe elapsed time, serialized request bytes, canonical-vs-custom endpoint class, and the `PI_STREAM_FIRST_EVENT_TIMEOUT_MS` override without exposing URL credentials, query tokens, or body content. Large requests through custom endpoints receive one bounded two-minute observation grace so a slightly later proxy 529 can surface without extending explicit-zero, small-request, or canonical deadlines; full-window multi-megabyte requests are never automatically re-uploaded and small requests get at most one session replay. Credit: @probepark (#4464).
|
|
12
|
+
- A ceiling-bound Anthropic upload that fails before stream iteration begins (for example an immediate 529 surfacing from response setup) now carries the one-attempt upload ceiling to the outer provider retry loop as well. Previously only the SDK's internal retries were suppressed for these requests, so the multi-megabyte body could still be re-uploaded up to the default `streamMaxRetries` budget. The delayed-setup grace test was also rescaled from a 1ms window/5ms setup pair — which made the grace-boundary outcome a coin flip on runner scheduling latency — to a regime where the contract is decidable, with a new complementary test proving an in-window 529 after delayed setup keeps its ordinary retry budget. Credit: @probepark (#4464).
|
|
13
|
+
- Unknown Anthropic stream rejections are normalized to a mutable `Error` before retry facts are stamped. An injected custom client rejecting `withResponse()` with a primitive string previously had its facts assigned onto a temporary boxed value — silently discarding them — so a ceiling-bound multi-megabyte upload slipped past the one-attempt ceiling and a string-matched corrective branch (CPA tool-alias restore) re-uploaded the body. Credit: @probepark (#4464).
|
|
14
|
+
- Defense-in-depth: a completed Anthropic stream whose assembled assistant content carries directly adjacent `thinking`/`redacted_thinking` blocks now emits a bounded diagnostic per stream invocation naming only the envelope shape (block count, adjacency presence, model, provider), never raw thinking text, signatures, or redacted payloads. The send-boundary collapse remains the wire source of truth; this is a read-only observation that helps surface upstream producers of the rejected shape (#4443).
|
|
15
|
+
|
|
16
|
+
- The adjacent-thinking-block collapse now treats `thinking` and `redacted_thinking` as one adjacency class at the final Anthropic send boundary, matching the maintainer-approved send-time invariant (#4425). The earlier replay-phase collapse (#4418) and its test asserted that a `redacted_thinking` block following a `thinking` block survived; the final send-boundary pass (#4425) correctly collapses that pair, and the replay-phase test and doc comment are reconciled to that authoritative behavior (#4382).
|
|
17
|
+
- Assistant turns that carry two directly adjacent `thinking` blocks are now collapsed to the first block before replay on `anthropic-messages`. Anthropic rejects that shape with `messages.N.content.M: thinking or redacted_thinking blocks in the latest assistant message cannot be modified`, citing the second block of the pair, and because the offending message keeps its index as the transcript grows, one such turn made every later request in the session fail - observed on two live sessions stuck for 9+ hours, each turn spending two rejected ~1.5 MB uploads, with the cited index frozen (`messages.5.content.118`) while the history grew from 103 to 430 messages. Verified against the captured transcript: the unmodified 430-message replay returns 400 and the collapsed replay returns 200. Thinking blocks separated by a `tool_use` are ordinary interleaved-thinking shape and are preserved, and a `redacted_thinking` blob following a thinking block is untouched (#4416).
|
|
18
|
+
- Dev CI now runs each AI test file in a fresh process instead of sharing one Bun test runtime across the package. This prevents leaked fetch spies, fake timers, environment overrides, auth-broker state, and module caches from contaminating later files while using the same bounded, root-preloaded harness as coding-agent shards (#4378).
|
|
19
|
+
- The deterministic Anthropic cache-eval fixture moved from the untracked root `artifacts/` directory to `packages/ai/test/fixtures/issue-3670-anthropic-cache-eval.json` where test fixtures belong. The integration test import path is updated accordingly (#4420).
|
|
20
|
+
- Anthropic thinking-replay repair now fingerprints the exact serialized outbound body before any application-level resend. A no-op latest-assistant transform is skipped in favor of a safe all-assistant transform, and if neither changes the body the turn fails without uploading the same large request again. The rejected request, transform disposition, hashes, sizes, and cited `messages.N.content.M` mismatch are recorded through the existing redacted HTTP-400 capture and warn path; thinking mode and the `context-management-2025-06-27` beta remain unchanged (#4382).
|
|
21
|
+
- Anthropic 400 errors that name a `clear_thinking_*` context-management strategy now explain when GJC's captured outgoing body contains neither `thinking` nor `context_management`: an intermediary at the redacted configured base URL likely injected the strategy. The diagnostic recommends explicitly enabling thinking or fixing/replacing the intermediary, never silently enables thinking or retries, and annotates the sanitized raw-request capture without changing its body. Credit: @probepark (#4380).
|
|
22
|
+
- Anthropic streams now repair the CPA Claude-OAuth "cannot restore tool alias" 500 instead of blindly resending the unchanged request: the exact SSE signature is classified, the base tool name is extracted, and the request is corrected exactly once with steering that names the unique callable tool (or directs tool discovery when no unique match exists). Recurrence surfaces an actionable terminal error with no transport facts, so neither the provider generic 5xx retry nor the managed fallback controller re-sends; managed attempts record the steering against the same turn and the next attempt applies it (#4338).
|
|
23
|
+
- Tool calls whose raw argument JSON spells a printable non-ASCII character as a `\uXXXX` escape instead of literal UTF-8 are now flagged with `ToolCall.escapedNonAsciiArguments`. Such a payload parses cleanly but is unverifiable — one mistyped hex digit decodes to a different, equally valid character — so the agent loop rejects it instead of executing silently corrupted text. Required control escapes and lone surrogates never trip the flag, and `\\uXXXX` (the source syntax of code being written) is not an escape and is ignored. New `findUnnecessaryUnicodeEscape` helper in `utils/json-parse`. The Kimi tool-call healer samples the signal from the raw leaked payload before its normalizing JSON round-trip, which would otherwise decode the escapes and erase the evidence.
|
|
24
|
+
- Persist learned tool-choice incapabilities in a bounded, expiring, digest-keyed cache so fresh processes avoid repeating known-invalid forced-choice probes while retaining automatic revalidation and existing first-discovery fallback behavior. Credit: @probepark (#4319).
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
- Added first-class support for oMLX (Apple Silicon MLX local inference server), including automatic discovery at `http://127.0.0.1:8080/v1`, `OMLX_BASE_URL`, and `OMLX_API_KEY`.
|
|
28
|
+
|
|
29
|
+
- Added first-class direct xAI `grok-4.6` catalog support over the existing xAI OAuth/subscription transport. Grok 4.5 now exposes exactly `low` through `high` reasoning effort, Grok 4.6 exposes exactly `low` through `xhigh`, and both serialize the selected level through `reasoning_effort`.
|
|
30
|
+
- `AuthStorage.switchSessionCredential(provider, sessionId, selector)` forces a running session's OAuth credential for a provider to a specific stored row, independent of quota/rate-limit state — backing the coding-agent `/credential` slash command for a manual mid-session account switch (as opposed to the existing automatic quota-triggered fallback). Mutates only the session-scoped sticky pointer keyed by `sessionId` (never a provider-wide runtime override), so it cannot bleed into other sessions with a different credential identity; subagents/team workers inherit the parent's credential identity by design and switch with it. Fails closed when a stronger override already re-decides the provider's credential every call (`--credential` hard pin, `--api-key` runtime override, or a config-sourced `models.yml` `apiKey`), and deliberately leaves credential-blocked state untouched so a switch to a still-backoff-blocked row safely falls back to a usable account instead of drawing another 429/quota error.
|
|
31
|
+
|
|
32
|
+
- Added the authoritative OpenRouter `meta/muse-spark-1.2` catalog fallback with a 1,048,576-token context window and `minimal` through `xhigh` reasoning effort, so stale or credential-limited catalog generation still closes the Muse Spark preset alias deterministically.
|
|
33
|
+
- `AuthStorage` supports a soft, per-provider preferred OAuth credential (`setRuntimePreferredCredentialSelector` / `hasRuntimePreferredCredentialSelector` / `removeRuntimePreferredCredentialSelector`, plus `AuthApiKeyOptions.preferredCredentialSelector`) that backs the coding-agent `--prefer-credential` CLI flag. A usable preferred row is placed ahead of the provider's normal balanced/earliest-reset ranking; a content-free quota or rate-limit failure marks it blocked and rotates to another active credential immediately, same as the existing session-stickiness fallback. It is mutually exclusive with the existing hard `setRuntimeCredentialSelector` pin (`--credential`) for the same provider, and `resolveRuntimePreferredCredentialSelectorProvider` resolves an unqualified selector to its single matching active OAuth provider or fails closed on ambiguity.
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
|
|
37
|
+
- Documented multi-account auth behavior: credential ranking (`balanced` or `earliest-reset`) runs at session start or after a blocked preferred account, explicit OAuth checks run sequentially, and API-key sources remain visible/checkable without entering OAuth pooling, pinning, or removal.
|
|
38
|
+
|
|
39
|
+
- Model discovery now retains the authoritative dynamic provider model IDs separately from the merged static/cache catalog. Consumers can distinguish a fresh provider omission from bundled offline availability through `ModelResolutionResult.dynamicModelIds`; cache schema v5 persists those IDs through fresh-cache reuse and static transport re-merges, scoped to the credential-and-endpoint provenance that produced them.
|
|
40
|
+
|
|
41
|
+
### Fixed
|
|
42
|
+
|
|
43
|
+
- Grok Build `/usage` now uses the weekly credits quota when the monthly billing endpoint reports a zero limit, avoiding misleading `100% free` and `used/0` output.
|
|
44
|
+
- A deterministic Anthropic thinking-replay rejection now converges under a managed fallback attempt instead of repeating forever (#4262). Every coding-agent turn prompts with `fallbackManaged: true`, and the whole in-provider thinking-replay repair sits behind `!options?.fallbackManaged` because the fallback controller owns retries — the shipping CLI therefore never repaired anything: each turn rebuilt the same replay from the same history, drew the same 400, and the session burned one rejected request per turn without ever self-healing (reported as ~1 rejected 1.3 MB request every 12s, ~300/h, never converging). The provider still does not retry inside a managed attempt — it records the full-history repair escalation on the provider session state instead, so the next managed attempt builds a repaired replay at the cost of zero extra round trips. Only the two deterministic rejections (`blocks ... cannot be modified`, ``Invalid `signature` in `thinking` block``) qualify; the proxy-masked generic `api_error` names no cause and may be a transient blip, so it still never costs the session its native replay.
|
|
45
|
+
- A long-lived process now recovers when the OAuth row it holds in memory is soft-disabled or replaced in the shared store by a peer (`invalid_grant` disable, credential removal, or a re-login that inserts a new row). The refresh lease answers `missing` for a row that is no longer active, which surfaced as `OAuth refresh credential disappeared` — an error the failure classifier reads as transient, so the vanished row was temp-blocked and retried for the life of the process while a valid re-login row sat unused in the store. Resumed sessions therefore failed every request with the misleading `No credentials found for <provider>` until the CLI was restarted, even though `gjc` had just been logged back in (observed as a per-request refresh-failure flood against `anthropic`). `#tryOAuthCredential` now detects that the attempted row is gone from the store, reloads the snapshot, and re-resolves within the existing reload budget, alongside the peer-rotation recovery it already performed.
|
|
46
|
+
- OpenAI Responses streams now preserve canonical terminal function-call arguments when compatible relays such as llama.cpp emit an empty placeholder in `response.output_item.added`. Tool-call correlation also accepts llama.cpp’s `call_id`-only event shape, while conflicting streamed/terminal arguments, malformed terminal JSON, and terminal payloads that decode to something other than a JSON object fail closed instead of reaching execution. Argument source precedence is terminal item, then streamed deltas, then the `output_item.added` snapshot, so a relay that only ever populates the added snapshot keeps its real payload.
|
|
47
|
+
|
|
48
|
+
- Added bundled Gemini 3.7 Flash next to every existing Gemini 3.6 Flash catalog selector (`google`, `google-gemini-cli`, `github-copilot`, `google-antigravity` effort variants, `opencode-zen`, `openrouter`, `vercel-ai-gateway`, `cursor`, `kilo`, `venice`). Gemini 3.7 Flash accepts only `low`, `medium`, and `high` thinking levels; `minimal` is rejected because the official API returns an error, so the Cursor `-minimal` suffix is not cloned.
|
|
49
|
+
|
|
50
|
+
## [0.13.3] - 2026-08-15
|
|
51
|
+
|
|
52
|
+
### Added
|
|
53
|
+
|
|
54
|
+
- Added first-class direct xAI `grok-4.6` catalog support over the existing xAI OAuth/subscription transport. Grok 4.5 exposes `low` through `high` reasoning effort and Grok 4.6 exposes `low` through `xhigh`.
|
|
55
|
+
- Added a native TypeScript Kiro (Amazon Q Developer / CodeWhisperer) provider: AWS SSO OIDC device-code login, bearer-token transport to the CodeWhisperer streaming endpoint over `application/vnd.amazon.eventstream`, and a `kiro` model-manager descriptor for Claude 3.7 Sonnet (#4304).
|
|
56
|
+
- Added the authoritative OpenRouter `meta/muse-spark-1.2` catalog fallback with a 1,048,576-token context window and `minimal` through `xhigh` reasoning effort, so stale or credential-limited catalog generation still closes the Muse Spark preset alias deterministically.
|
|
57
|
+
|
|
58
|
+
### Fixed
|
|
59
|
+
- Validate Synthetic API key via models endpoint, not retired Kimi probe (#4385).
|
|
60
|
+
- Tool-call arguments that spell printable non-ASCII text as `\uXXXX` escapes are now flagged on the raw wire (`escapedNonAsciiArguments`) by the Anthropic, OpenAI Responses, and OpenAI Completions streams — after JSON decode the defect is unobservable, and a mistyped hex nibble silently becomes a different character (#4515).
|
|
61
|
+
|
|
5
62
|
## [0.13.2] - 2026-08-13
|
|
6
63
|
|
|
64
|
+
### Fixed
|
|
65
|
+
|
|
66
|
+
- `clear_thinking`-emptied Anthropic thinking blocks now drop stale signatures before replay, preventing the next request from failing with `Invalid signature in thinking block` after a provider-side reasoning clear (#4247, reported by @probepark).
|
|
67
|
+
|
|
7
68
|
## [0.13.1] - 2026-08-11
|
|
8
69
|
|
|
9
70
|
### Fixed
|
|
@@ -64,7 +125,6 @@
|
|
|
64
125
|
|
|
65
126
|
### Fixed
|
|
66
127
|
|
|
67
|
-
|
|
68
128
|
- `todo_write` raw argument rejections now carry bounded, authority-controlled correction codes for each rejected shape: unknown root keys, unknown operation-entry keys, done/drop entries missing a task or phase target, and unknown init list-entry keys. Each code maps to a fixed correction message naming the accepted shape (never echoing the offending input), so invalid calls surface specific guidance while valid payloads keep the existing passthrough/coercion path (#3916).
|
|
69
129
|
- Anthropic Sonnet 5 now exposes Anthropic's real `xhigh` and `max` thinking efforts on the Messages API (`minimal`/`low`/`medium`/`high`/`xhigh`/`max`), matching official support. The previous generic `kind === opus` gate excluded it from the full preset range; the capability predicate is now an explicit version-scoped list (Opus 4.7+, Sonnet 5+), so older Sonnet generations and Bedrock Converse routes stay fail-closed at their previously advertised levels (issue #3913).
|
|
70
130
|
- Alibaba Token Plan now exposes Qwen 3.8 Max under the provider-supported `qwen3.8-max` wire id instead of the rejected `qwen-3.8-max` spelling; catalog regeneration canonicalizes a legacy discovered alias rather than retaining a broken duplicate (#3909).
|
|
@@ -129,7 +189,6 @@
|
|
|
129
189
|
|
|
130
190
|
- Reproducible Alibaba Token Plan header-parity A/B latency benchmark (`packages/ai/scripts/alibaba-token-plan-latency-ab.ts`): a fixed-seed interleaved A/B comparison of legacy vs Qwen-identical headers against a deterministic local HTTP server, reporting n/success/error/timeout and TTFT/total latency median/p90/p95/mean/stddev. No live credentials are required; a public-safe blocked-live-data receipt is included (`packages/ai/test/fixtures/alibaba-token-plan-latency-blocked-receipt.md`) (#3557).
|
|
131
191
|
|
|
132
|
-
|
|
133
192
|
## [0.12.4] - 2026-07-30
|
|
134
193
|
|
|
135
194
|
### Fixed
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AuthCredential } from "../auth-storage";
|
|
2
|
-
import type { CredentialDisableResponse, CredentialIfAbsentUploadResponse, CredentialRefreshRequest, CredentialRefreshResponse, CredentialUploadResponse, HealthzResponse, SnapshotResponse, SnapshotStreamEvent, UsageResponse } from "./types";
|
|
2
|
+
import type { CredentialDisableResponse, CredentialIfAbsentUploadResponse, CredentialMetadataResponse, CredentialRefreshRequest, CredentialRefreshResponse, CredentialUploadResponse, HealthzResponse, SnapshotResponse, SnapshotStreamEvent, UsageResponse } from "./types";
|
|
3
3
|
export interface AuthBrokerClientOptions {
|
|
4
4
|
/** Base URL (e.g. `https://broker.tailnet:8765`). Trailing slashes are trimmed. */
|
|
5
5
|
url: string;
|
|
@@ -29,6 +29,10 @@ export declare class AuthBrokerError extends Error {
|
|
|
29
29
|
export declare class AuthBrokerStreamUnsupportedError extends AuthBrokerError {
|
|
30
30
|
constructor(message?: string);
|
|
31
31
|
}
|
|
32
|
+
/** Thrown when a broker responds 404 to `GET /v1/credentials/metadata`. */
|
|
33
|
+
export declare class AuthBrokerCredentialMetadataUnsupportedError extends AuthBrokerError {
|
|
34
|
+
constructor(message?: string);
|
|
35
|
+
}
|
|
32
36
|
export interface FetchSnapshotOptions {
|
|
33
37
|
ifGenerationGt?: number;
|
|
34
38
|
waitMs?: number;
|
|
@@ -45,8 +49,12 @@ export type FetchSnapshotResult = {
|
|
|
45
49
|
export declare class AuthBrokerClient {
|
|
46
50
|
#private;
|
|
47
51
|
constructor(opts: AuthBrokerClientOptions);
|
|
52
|
+
/** Normalized broker origin used for non-secret local presentation partitioning. */
|
|
53
|
+
get baseUrl(): string;
|
|
48
54
|
healthz(signal?: AbortSignal): Promise<HealthzResponse>;
|
|
49
55
|
fetchSnapshot(opts?: FetchSnapshotOptions): Promise<FetchSnapshotResult>;
|
|
56
|
+
/** Fetches the generation-aware, secret-free credential inventory projection. */
|
|
57
|
+
fetchCredentialMetadata(signal?: AbortSignal): Promise<CredentialMetadataResponse>;
|
|
50
58
|
/**
|
|
51
59
|
* Subscribe to the broker's SSE snapshot stream. The first frame is always
|
|
52
60
|
* a full `snapshot`; subsequent frames are `entry` upserts / refreshes or
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Keep provider and upstream failure text safe for less-trusted surfaces.
|
|
3
|
+
*
|
|
4
|
+
* This intentionally mirrors the bounded reason scrubber used by the account
|
|
5
|
+
* management CLI without taking a dependency on coding-agent.
|
|
6
|
+
*/
|
|
7
|
+
export declare function cleanReason(value: unknown): string | undefined;
|
|
@@ -1,9 +1,30 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Client-side {@link AuthCredentialStore} that mirrors a remote broker's
|
|
3
|
+
* snapshot. Refresh tokens never leave the broker; mutating methods (`replace*`,
|
|
4
|
+
* `upsert*`, `delete*ForProvider`) throw because login flows are server-side.
|
|
5
|
+
*
|
|
6
|
+
* Cache (`getCache`/`setCache`/`cleanExpiredCache`) is in-memory and ephemeral —
|
|
7
|
+
* usage reports cache TTL is 5 minutes per credential, so durability across
|
|
8
|
+
* runs isn't required.
|
|
9
|
+
*/
|
|
10
|
+
import { type AuthCredential, type AuthCredentialIfAbsentResult, type AuthCredentialStore, type CachedCredentialHealth, type CachedUsagePresentation, type CredentialInventoryRecord, type MCPOAuthRefreshClient, type OAuthCredential, type SafeUsageReport, type StoredAuthCredential } from "../auth-storage";
|
|
2
11
|
import type { Provider } from "../types";
|
|
3
12
|
import type { UsageReport } from "../usage";
|
|
4
13
|
import type { OAuthCredentials } from "../utils/oauth/types";
|
|
5
14
|
import { type AuthBrokerClient } from "./client";
|
|
6
15
|
import type { SnapshotResponse } from "./types";
|
|
16
|
+
export type CredentialInventoryMetadataCapability = "pending" | "supported" | "unsupported" | "mismatch" | "failed";
|
|
17
|
+
export interface CachedInventoryNotice {
|
|
18
|
+
status: Exclude<CredentialInventoryMetadataCapability, "supported">;
|
|
19
|
+
reason: string;
|
|
20
|
+
generation?: number;
|
|
21
|
+
}
|
|
22
|
+
export interface CredentialInventoryMetadataState {
|
|
23
|
+
capability: CredentialInventoryMetadataCapability;
|
|
24
|
+
generation: number;
|
|
25
|
+
records: readonly CredentialInventoryRecord[];
|
|
26
|
+
notice?: CachedInventoryNotice;
|
|
27
|
+
}
|
|
7
28
|
export interface RemoteAuthCredentialStoreOptions {
|
|
8
29
|
client: AuthBrokerClient;
|
|
9
30
|
/**
|
|
@@ -16,12 +37,26 @@ export interface RemoteAuthCredentialStoreOptions {
|
|
|
16
37
|
* to long-poll permanently when the broker returns 404. Default `true`.
|
|
17
38
|
*/
|
|
18
39
|
streamSnapshots?: boolean;
|
|
40
|
+
/** Override the local redacted presentation sidecar path (primarily for tests). */
|
|
41
|
+
presentationPath?: string;
|
|
19
42
|
}
|
|
20
43
|
export declare class RemoteAuthCredentialStore implements AuthCredentialStore {
|
|
21
44
|
#private;
|
|
22
45
|
constructor(opts: RemoteAuthCredentialStoreOptions);
|
|
23
46
|
get client(): AuthBrokerClient;
|
|
47
|
+
/** Wait for redacted presentation hydration and initial inventory metadata. */
|
|
48
|
+
waitForReady(): Promise<void>;
|
|
49
|
+
/** Await pending atomic sidecar writes (useful to bounded shutdown callers). */
|
|
50
|
+
flushPresentationPersistence(): Promise<void>;
|
|
24
51
|
get snapshot(): SnapshotResponse;
|
|
52
|
+
getInventoryMetadataState(): Readonly<CredentialInventoryMetadataState>;
|
|
53
|
+
syncInventoryMetadata(): Promise<Readonly<CredentialInventoryMetadataState>>;
|
|
54
|
+
/**
|
|
55
|
+
* Payload-free inventory view. This method never performs network I/O; metadata
|
|
56
|
+
* rows appear only after an explicit or background metadata synchronization for
|
|
57
|
+
* the current snapshot generation.
|
|
58
|
+
*/
|
|
59
|
+
listCredentialInventory(provider?: string): CredentialInventoryRecord[];
|
|
25
60
|
/** Re-hydrate the in-memory snapshot from the broker. */
|
|
26
61
|
refreshSnapshot(): Promise<SnapshotResponse>;
|
|
27
62
|
listAuthCredentials(provider?: string): StoredAuthCredential[];
|
|
@@ -31,8 +66,8 @@ export declare class RemoteAuthCredentialStore implements AuthCredentialStore {
|
|
|
31
66
|
* authoritative row, so we just mirror it.
|
|
32
67
|
*/
|
|
33
68
|
updateAuthCredential(id: number, credential: AuthCredential): void;
|
|
34
|
-
deleteAuthCredential(
|
|
35
|
-
tryDisableAuthCredentialIfMatches(
|
|
69
|
+
deleteAuthCredential(_id: number, _disabledCause: string): void;
|
|
70
|
+
tryDisableAuthCredentialIfMatches(_id: number, _expectedData: string, _disabledCause: string): boolean;
|
|
36
71
|
waitForFreshSnapshot(maxWaitMs: number, opts?: {
|
|
37
72
|
signal?: AbortSignal;
|
|
38
73
|
}): Promise<boolean>;
|
|
@@ -46,6 +81,8 @@ export declare class RemoteAuthCredentialStore implements AuthCredentialStore {
|
|
|
46
81
|
upsertAuthCredentialForProvider(_provider: string, _credential: AuthCredential): StoredAuthCredential[];
|
|
47
82
|
upsertAuthCredentialForProviderIfAbsent(_provider: string, _credential: AuthCredential): AuthCredentialIfAbsentResult;
|
|
48
83
|
deleteAuthCredentialsForProvider(_provider: string, _disabledCause: string): void;
|
|
84
|
+
/** Logout authority remains on the broker; the client only mirrors its result. */
|
|
85
|
+
deleteAuthCredentialsRemote(provider: string, disabledCause: string): Promise<void>;
|
|
49
86
|
/**
|
|
50
87
|
* Upsert a single credential through the broker. The broker server is the
|
|
51
88
|
* canonical writer — see `POST /v1/credential`. The redacted snapshot
|
|
@@ -61,12 +98,6 @@ export declare class RemoteAuthCredentialStore implements AuthCredentialStore {
|
|
|
61
98
|
* key clobbers any previously stored key for the same provider.
|
|
62
99
|
*/
|
|
63
100
|
replaceAuthCredentialsRemote(provider: string, credentials: AuthCredential[]): Promise<StoredAuthCredential[]>;
|
|
64
|
-
/**
|
|
65
|
-
* Logout: disable every active credential for the provider on the broker,
|
|
66
|
-
* then drop them from the local snapshot. Refresh fetches the authoritative
|
|
67
|
-
* post-state in the background.
|
|
68
|
-
*/
|
|
69
|
-
deleteAuthCredentialsRemote(provider: string, disabledCause: string): Promise<void>;
|
|
70
101
|
getCache(key: string): string | null;
|
|
71
102
|
setCache(key: string, value: string, expiresAtSec: number): void;
|
|
72
103
|
cleanExpiredCache(): void;
|
|
@@ -86,6 +117,16 @@ export declare class RemoteAuthCredentialStore implements AuthCredentialStore {
|
|
|
86
117
|
* residential laptop is, so all credentials surface every cycle.
|
|
87
118
|
*/
|
|
88
119
|
fetchUsageReports(signal?: AbortSignal): Promise<UsageReport[] | null>;
|
|
120
|
+
/** Synchronous, zero-network usage presentation read. */
|
|
121
|
+
peekCachedUsagePresentation(provider: Provider, credentialId: number): CachedUsagePresentation | undefined;
|
|
122
|
+
/** Synchronous, zero-network health presentation read backed by the redacted sidecar. */
|
|
123
|
+
peekCachedCredentialHealth(provider: Provider, credentialId: number): CachedCredentialHealth | undefined;
|
|
124
|
+
/** Persist a safe health result without credential or bearer-token material. */
|
|
125
|
+
recordCredentialHealth(provider: Provider, credentialId: number, health: CachedCredentialHealth): void;
|
|
126
|
+
/** Record a safe usage observation after an explicit broker usage/check call. */
|
|
127
|
+
recordUsagePresentation(observation: CachedUsagePresentation): void;
|
|
128
|
+
/** Persist an explicit usage/check report for the current credential identity. */
|
|
129
|
+
recordCredentialUsage(provider: Provider, credentialId: number, report: SafeUsageReport): void;
|
|
89
130
|
/**
|
|
90
131
|
* Per-credential usage hook consumed by `AuthStorage.#getUsageReport`. Pulls
|
|
91
132
|
* the aggregate broker `/v1/usage` once and serves all callers from the
|
|
@@ -7,6 +7,20 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import type { AuthCredential, AuthCredentialIfAbsentReason, AuthCredentialSnapshot, AuthCredentialSnapshotEntry, MCPOAuthRefreshClient } from "../auth-storage";
|
|
9
9
|
import type { UsageReport } from "../usage";
|
|
10
|
+
/** A single credential metadata projection; secret-bearing fields are intentionally absent. */
|
|
11
|
+
export interface CredentialMetadataRecord {
|
|
12
|
+
id: number;
|
|
13
|
+
provider: string;
|
|
14
|
+
type: "oauth" | "api_key";
|
|
15
|
+
identity: string | null;
|
|
16
|
+
disabledCause: string | null;
|
|
17
|
+
}
|
|
18
|
+
/** GET /v1/credentials/metadata response body. */
|
|
19
|
+
export interface CredentialMetadataResponse {
|
|
20
|
+
generation: number;
|
|
21
|
+
generatedAt: number;
|
|
22
|
+
credentials: CredentialMetadataRecord[];
|
|
23
|
+
}
|
|
10
24
|
/** GET /v1/healthz response body. */
|
|
11
25
|
export interface HealthzResponse {
|
|
12
26
|
ok: boolean;
|
|
@@ -170,6 +170,31 @@ export declare const snapshotResponseSchema: z.ZodObject<{
|
|
|
170
170
|
rotatesInMs: z.ZodNullable<z.ZodNumber>;
|
|
171
171
|
}, z.core.$strict>>;
|
|
172
172
|
}, z.core.$strict>;
|
|
173
|
+
/** Closed redacted projection: no token, key, identity object, or extension fields. */
|
|
174
|
+
export declare const credentialMetadataRecordSchema: z.ZodObject<{
|
|
175
|
+
id: z.ZodNumber;
|
|
176
|
+
provider: z.ZodString;
|
|
177
|
+
type: z.ZodEnum<{
|
|
178
|
+
api_key: "api_key";
|
|
179
|
+
oauth: "oauth";
|
|
180
|
+
}>;
|
|
181
|
+
identity: z.ZodNullable<z.ZodString>;
|
|
182
|
+
disabledCause: z.ZodNullable<z.ZodString>;
|
|
183
|
+
}, z.core.$strict>;
|
|
184
|
+
export declare const credentialMetadataResponseSchema: z.ZodObject<{
|
|
185
|
+
generation: z.ZodNumber;
|
|
186
|
+
generatedAt: z.ZodNumber;
|
|
187
|
+
credentials: z.ZodArray<z.ZodObject<{
|
|
188
|
+
id: z.ZodNumber;
|
|
189
|
+
provider: z.ZodString;
|
|
190
|
+
type: z.ZodEnum<{
|
|
191
|
+
api_key: "api_key";
|
|
192
|
+
oauth: "oauth";
|
|
193
|
+
}>;
|
|
194
|
+
identity: z.ZodNullable<z.ZodString>;
|
|
195
|
+
disabledCause: z.ZodNullable<z.ZodString>;
|
|
196
|
+
}, z.core.$strict>>;
|
|
197
|
+
}, z.core.$strict>;
|
|
173
198
|
/** First frame on connect — full snapshot embedded inline with a `kind` tag. */
|
|
174
199
|
export declare const snapshotStreamSnapshotEventSchema: z.ZodObject<{
|
|
175
200
|
generation: z.ZodNumber;
|
|
@@ -59,6 +59,86 @@ export interface StoredAuthCredential {
|
|
|
59
59
|
provider: string;
|
|
60
60
|
credential: AuthCredential;
|
|
61
61
|
disabledCause: string | null;
|
|
62
|
+
/** Monotonic local row revision used by optimistic hard-removal actions. */
|
|
63
|
+
revision?: number;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Payload-free inventory projection used by account-management and presentation
|
|
67
|
+
* surfaces. This deliberately has no credential/token fields; `listAuthCredentials`
|
|
68
|
+
* remains the active full-fidelity selection contract.
|
|
69
|
+
*/
|
|
70
|
+
export interface CredentialInventoryRecord {
|
|
71
|
+
id: number;
|
|
72
|
+
provider: string;
|
|
73
|
+
credentialKind: "oauth" | "api_key";
|
|
74
|
+
identityLabel: string | null;
|
|
75
|
+
accountId?: string;
|
|
76
|
+
email?: string;
|
|
77
|
+
projectId?: string;
|
|
78
|
+
disabled: boolean;
|
|
79
|
+
disabledCause: string | null;
|
|
80
|
+
}
|
|
81
|
+
/** Safe usage observation supplied by a remote store's presentation cache. */
|
|
82
|
+
export interface CachedUsagePresentation {
|
|
83
|
+
credentialId: number;
|
|
84
|
+
provider: string;
|
|
85
|
+
inventoryGeneration: number;
|
|
86
|
+
identityDigest: string;
|
|
87
|
+
usage: SafeUsageReport;
|
|
88
|
+
fetchedAt: number;
|
|
89
|
+
freshUntil: number;
|
|
90
|
+
retainUntil: number;
|
|
91
|
+
}
|
|
92
|
+
/** Opaque local action target for an all-or-nothing OAuth hard removal. */
|
|
93
|
+
export interface CredentialRemovalTarget {
|
|
94
|
+
id: number;
|
|
95
|
+
provider: string;
|
|
96
|
+
expectedRevision: number;
|
|
97
|
+
}
|
|
98
|
+
export type AuthCredentialHardRemovalResult = {
|
|
99
|
+
kind: "removed";
|
|
100
|
+
ids: readonly number[];
|
|
101
|
+
} | {
|
|
102
|
+
kind: "conflict";
|
|
103
|
+
currentIds: readonly number[];
|
|
104
|
+
};
|
|
105
|
+
/** Usage report projection safe to cross a presentation boundary. */
|
|
106
|
+
export type SafeUsageReport = Omit<UsageReport, "raw">;
|
|
107
|
+
export type CachedUsageFreshness = "fresh" | "stale-last-good";
|
|
108
|
+
export interface CachedUsageReport {
|
|
109
|
+
report: SafeUsageReport;
|
|
110
|
+
fetchedAt: number;
|
|
111
|
+
freshUntil: number;
|
|
112
|
+
retainUntil: number;
|
|
113
|
+
freshness: CachedUsageFreshness;
|
|
114
|
+
}
|
|
115
|
+
export type CachedCredentialHealthStatus = "ok" | "failed" | "unverifiable" | "unknown";
|
|
116
|
+
export interface CachedCredentialHealth {
|
|
117
|
+
status: CachedCredentialHealthStatus;
|
|
118
|
+
reason: string | null;
|
|
119
|
+
checkedAt?: number;
|
|
120
|
+
retainUntil?: number;
|
|
121
|
+
}
|
|
122
|
+
/** Safe result from an explicit API-key probe whose key bytes are invocation-only. */
|
|
123
|
+
export interface ApiKeyCredentialCheckResult {
|
|
124
|
+
provider: string;
|
|
125
|
+
type: "api_key";
|
|
126
|
+
ok: boolean | null;
|
|
127
|
+
reason?: string;
|
|
128
|
+
report?: SafeUsageReport;
|
|
129
|
+
}
|
|
130
|
+
/** Typed failure raised when an OAuth-only selector cannot be applied. */
|
|
131
|
+
export type OAuthCredentialSelectorFailureReason = "api-key-row" | "api-key-provider" | "override-active" | "not-found" | "disabled" | "ambiguous" | "gateway-managed";
|
|
132
|
+
export declare class OAuthCredentialSelectorError extends Error {
|
|
133
|
+
readonly reason: OAuthCredentialSelectorFailureReason;
|
|
134
|
+
readonly provider: string;
|
|
135
|
+
readonly selector: AuthCredentialSelector;
|
|
136
|
+
readonly candidateIds: readonly number[];
|
|
137
|
+
constructor(reason: OAuthCredentialSelectorFailureReason, provider: string, selector: AuthCredentialSelector, message: string, candidateIds?: readonly number[]);
|
|
138
|
+
}
|
|
139
|
+
export interface OAuthPinTarget {
|
|
140
|
+
credentialId: number;
|
|
141
|
+
canonicalSelector: AuthCredentialSelector;
|
|
62
142
|
}
|
|
63
143
|
/**
|
|
64
144
|
* Per-credential health record returned by {@link AuthStorage.checkCredentials}.
|
|
@@ -89,16 +169,22 @@ export interface CredentialHealthResult {
|
|
|
89
169
|
ok: boolean | null;
|
|
90
170
|
/** Failure / unverifiable reason; absent when `ok === true`. */
|
|
91
171
|
reason?: string;
|
|
92
|
-
|
|
93
|
-
report?: Omit<UsageReport, "raw">;
|
|
172
|
+
report?: SafeUsageReport;
|
|
94
173
|
}
|
|
95
174
|
export interface CheckCredentialsOptions {
|
|
96
175
|
signal?: AbortSignal;
|
|
176
|
+
provider?: string;
|
|
97
177
|
/** Per-credential probe timeout (ms). Defaults to the configured usage request timeout. */
|
|
98
178
|
timeoutMs?: number;
|
|
99
179
|
/** Provider → base URL override, same shape as {@link AuthStorage.fetchUsageReports}. */
|
|
100
180
|
baseUrlResolver?: (provider: Provider) => string | undefined;
|
|
101
181
|
}
|
|
182
|
+
/** Options for the explicit, invocation-only API-key probe. */
|
|
183
|
+
export interface ApiKeyCredentialCheckOptions {
|
|
184
|
+
signal?: AbortSignal;
|
|
185
|
+
timeoutMs?: number;
|
|
186
|
+
baseUrl?: string;
|
|
187
|
+
}
|
|
102
188
|
/**
|
|
103
189
|
* Sentinel value placed in OAuth `refresh` fields when a credential is shared
|
|
104
190
|
* via {@link AuthStorage.exportSnapshot}. Refresh tokens never leave the broker;
|
|
@@ -170,6 +256,12 @@ export type OAuthRefreshLeaseClaim = {
|
|
|
170
256
|
export interface AuthCredentialStore {
|
|
171
257
|
close(): void;
|
|
172
258
|
listAuthCredentials(provider?: string): StoredAuthCredential[];
|
|
259
|
+
/** Payload-free account inventory; active and soft-disabled rows are included. */
|
|
260
|
+
listCredentialInventory?(provider?: string): CredentialInventoryRecord[];
|
|
261
|
+
/** Local opaque removal targets; remote stores may omit this capability. */
|
|
262
|
+
listCredentialRemovalTargets?(provider?: string): CredentialRemovalTarget[];
|
|
263
|
+
/** Transactional local hard removal; remote stores must reject this capability. */
|
|
264
|
+
removeAuthCredentialsHard?(provider: string, targets: readonly CredentialRemovalTarget[]): AuthCredentialHardRemovalResult;
|
|
173
265
|
updateAuthCredential(id: number, credential: AuthCredential): void;
|
|
174
266
|
deleteAuthCredential(id: number, disabledCause: string): void;
|
|
175
267
|
tryDisableAuthCredentialIfMatches(id: number, expectedData: string, disabledCause: string): boolean;
|
|
@@ -228,6 +320,21 @@ export interface AuthCredentialStore {
|
|
|
228
320
|
* `signal` propagates the agent's cancel down to the broker fetch.
|
|
229
321
|
*/
|
|
230
322
|
fetchUsageReports?(signal?: AbortSignal): Promise<UsageReport[] | null>;
|
|
323
|
+
/** Synchronous, zero-network usage presentation peek. */
|
|
324
|
+
peekCachedUsagePresentation?(provider: Provider, credentialId: number): CachedUsagePresentation | undefined;
|
|
325
|
+
/** Record a safe usage observation after an explicit fetch/check. */
|
|
326
|
+
recordUsagePresentation?(observation: CachedUsagePresentation): void;
|
|
327
|
+
/** Read a safe, durable health observation for one credential row. */
|
|
328
|
+
peekCachedCredentialHealth?(provider: Provider, credentialId: number): CachedCredentialHealth | undefined;
|
|
329
|
+
/** Persist a safe health observation for one credential row. */
|
|
330
|
+
recordCredentialHealth?(provider: Provider, credentialId: number, health: CachedCredentialHealth): void;
|
|
331
|
+
/** Persist a safe usage observation without exposing credential payloads. */
|
|
332
|
+
recordCredentialUsage?(provider: Provider, credentialId: number, report: SafeUsageReport): void;
|
|
333
|
+
/**
|
|
334
|
+
* Optional readiness hook for stores that must hydrate payload-free metadata
|
|
335
|
+
* before one-shot inventory consumers read their first snapshot.
|
|
336
|
+
*/
|
|
337
|
+
waitForReady?(): Promise<void>;
|
|
231
338
|
/**
|
|
232
339
|
* Optional store-supplied per-credential usage report lookup. When present,
|
|
233
340
|
* `AuthStorage` consults this before its own per-credential upstream fetch
|
|
@@ -273,8 +380,8 @@ export interface AuthCredentialStore {
|
|
|
273
380
|
replaceAuthCredentialsRemote?(provider: string, credentials: AuthCredential[]): Promise<StoredAuthCredential[]>;
|
|
274
381
|
/**
|
|
275
382
|
* Optional async write hook for clearing every credential for a provider
|
|
276
|
-
* (logout).
|
|
277
|
-
*
|
|
383
|
+
* (logout or a provider-wide invalidation). Remote stores must perform this
|
|
384
|
+
* through their authoritative broker rather than mutating the client cache.
|
|
278
385
|
*/
|
|
279
386
|
deleteAuthCredentialsRemote?(provider: string, disabledCause: string): Promise<void>;
|
|
280
387
|
}
|
|
@@ -373,6 +480,8 @@ type AuthApiKeyOptions = {
|
|
|
373
480
|
signal?: AbortSignal;
|
|
374
481
|
/** Pin selection to one stored credential instead of using round-robin/ranking. */
|
|
375
482
|
credentialSelector?: AuthCredentialSelector;
|
|
483
|
+
/** Prefer one stored OAuth credential while preserving quota-triggered fallback. */
|
|
484
|
+
preferredCredentialSelector?: AuthCredentialSelector;
|
|
376
485
|
};
|
|
377
486
|
export type AuthCredentialSelectorKind = "id" | "email" | "account" | "project";
|
|
378
487
|
export interface AuthCredentialSelector {
|
|
@@ -453,10 +562,52 @@ export declare class AuthStorage {
|
|
|
453
562
|
* Used for CLI --credential.
|
|
454
563
|
*/
|
|
455
564
|
setRuntimeCredentialSelector(provider: string, selector: AuthCredentialSelector): void;
|
|
565
|
+
/** Acquire a reference-counted credential scope for a session or shared subagent scope. */
|
|
566
|
+
acquireCredentialScope(scopeId: string): void;
|
|
567
|
+
/** Whether a credential scope already has at least one live owner. */
|
|
568
|
+
hasCredentialScopeLease(scopeId: string): boolean;
|
|
569
|
+
/** Release one credential-scope lease; final release clears only that scope's derived state. */
|
|
570
|
+
releaseCredentialScope(scopeId: string): void;
|
|
571
|
+
/** Set the selector derived from a durable session pin or a session seed. */
|
|
572
|
+
setSessionCredentialSelector(scopeId: string, provider: string, selector: AuthCredentialSelector): void;
|
|
573
|
+
/** Explicitly mask persistent/process-global selection and return the provider to AUTO for one scope. */
|
|
574
|
+
setSessionCredentialAuto(provider: string, scopeId: string): void;
|
|
575
|
+
/** Clear a scope's explicit selector and AUTO mask, restoring normal precedence. */
|
|
576
|
+
clearSessionCredentialSelector(provider: string, scopeId: string): void;
|
|
577
|
+
/** Whether the effective selection for a scope is explicitly pinned (AUTO masks are not pins). */
|
|
578
|
+
hasSessionCredentialSelector(provider: string, scopeId?: string): boolean;
|
|
579
|
+
/** Whether this scope explicitly masks provider pins and uses AUTO ranking. */
|
|
580
|
+
hasSessionCredentialAuto(provider: string, scopeId?: string): boolean;
|
|
581
|
+
/** Resolve the effective selector precedence for a provider/scope. */
|
|
582
|
+
resolveEffectiveCredentialSelector(provider: string, scopeId?: string, explicitSelector?: AuthCredentialSelector): AuthCredentialSelector | undefined;
|
|
583
|
+
/** Validate and canonicalize an OAuth-only selector for account pinning. */
|
|
584
|
+
resolveOAuthPinTarget(provider: string, selector: AuthCredentialSelector): OAuthPinTarget;
|
|
585
|
+
/** Return all local inventory rows, including soft-disabled metadata, without payloads. */
|
|
586
|
+
listCredentialInventory(provider?: string): CredentialInventoryRecord[];
|
|
587
|
+
/** Return local OAuth hard-removal action targets, including disabled rows. */
|
|
588
|
+
listCredentialRemovalTargets(provider?: string): CredentialRemovalTarget[];
|
|
589
|
+
/** Remove selected local OAuth rows atomically; conflict leaves all rows intact. */
|
|
590
|
+
removeAuthCredentialsHard(provider: string, targets: readonly CredentialRemovalTarget[]): AuthCredentialHardRemovalResult;
|
|
456
591
|
/**
|
|
457
592
|
* Remove a runtime credential selector.
|
|
458
593
|
*/
|
|
459
594
|
removeRuntimeCredentialSelector(provider: string): void;
|
|
595
|
+
/** Whether a provider currently has a soft runtime credential preference. */
|
|
596
|
+
hasRuntimePreferredCredentialSelector(provider: string): boolean;
|
|
597
|
+
/** Resolve an unqualified preferred selector to the single active OAuth provider it matches. */
|
|
598
|
+
resolveRuntimePreferredCredentialSelectorProvider(selector: AuthCredentialSelector): string;
|
|
599
|
+
/**
|
|
600
|
+
* Prefer one stored OAuth credential for a provider while retaining quota
|
|
601
|
+
* fallback to the rest of the pool (not persisted to disk). Used for CLI
|
|
602
|
+
* `--prefer-credential`. Unlike {@link setRuntimeCredentialSelector}, a
|
|
603
|
+
* quota/rate-limit failure on the preferred row still rotates to another
|
|
604
|
+
* active credential instead of failing the session.
|
|
605
|
+
*/
|
|
606
|
+
setRuntimePreferredCredentialSelector(provider: string, selector: AuthCredentialSelector): void;
|
|
607
|
+
/**
|
|
608
|
+
* Remove a runtime preferred credential selector.
|
|
609
|
+
*/
|
|
610
|
+
removeRuntimePreferredCredentialSelector(provider: string): void;
|
|
460
611
|
/**
|
|
461
612
|
* Remove a runtime API key override.
|
|
462
613
|
*/
|
|
@@ -475,6 +626,8 @@ export declare class AuthStorage {
|
|
|
475
626
|
* credential have to consult BOTH.
|
|
476
627
|
*/
|
|
477
628
|
hasRuntimeCredentialSelector(provider: string): boolean;
|
|
629
|
+
/** Whether the effective selector for a session scope is pinned. */
|
|
630
|
+
hasEffectiveCredentialSelector(provider: string, sessionId?: string): boolean;
|
|
478
631
|
/**
|
|
479
632
|
* Opaque stored row id of the credential this session is currently using.
|
|
480
633
|
*
|
|
@@ -488,6 +641,36 @@ export declare class AuthStorage {
|
|
|
488
641
|
* resolver rather than a stored row.
|
|
489
642
|
*/
|
|
490
643
|
getSessionCredentialRowId(provider: string, sessionId?: string): number | undefined;
|
|
644
|
+
/**
|
|
645
|
+
* Force a running session's OAuth credential for a provider to a specific
|
|
646
|
+
* stored row, independent of quota/rate-limit state. Used for a mid-session
|
|
647
|
+
* `/credential <selector>` switch that has nothing to do with exhaustion —
|
|
648
|
+
* the user just wants a different account for the rest of the session.
|
|
649
|
+
*
|
|
650
|
+
* This mutates ONLY the session-scoped sticky pointer
|
|
651
|
+
* ({@link AuthStorage.#recordSessionCredential}), never a provider-wide
|
|
652
|
+
* runtime override, so it cannot bleed into other sessions in the same
|
|
653
|
+
* process whose credential identity differs. The sticky pointer is keyed by
|
|
654
|
+
* `sessionId`, and subagents/team workers inherit their parent's
|
|
655
|
+
* `credentialSessionId` by design so they keep using the same account as
|
|
656
|
+
* the parent — a switch therefore applies to the whole session family
|
|
657
|
+
* sharing that identity, not to unrelated sessions.
|
|
658
|
+
*
|
|
659
|
+
* Fails closed rather than silently no-op when a stronger override already
|
|
660
|
+
* decides this provider's credential every call: a hard pin
|
|
661
|
+
* ({@link AuthStorage.setRuntimeCredentialSelector}, `--credential`), a
|
|
662
|
+
* runtime API-key override (`--api-key`), or a config-sourced API key
|
|
663
|
+
* (`models.yml` `apiKey`) would each re-decide the credential on the very
|
|
664
|
+
* next {@link AuthStorage.getApiKey} call and make this switch appear to
|
|
665
|
+
* silently do nothing.
|
|
666
|
+
*
|
|
667
|
+
* Deliberately does not touch credential-blocked state: if the target row
|
|
668
|
+
* is still backoff-blocked from a prior quota failure, the existing
|
|
669
|
+
* `#resolveOAuthSelection` ranking safely ignores this sticky pointer and
|
|
670
|
+
* falls back to a usable account instead of re-issuing a request that would
|
|
671
|
+
* just draw another 429/quota error.
|
|
672
|
+
*/
|
|
673
|
+
switchSessionCredential(provider: string, sessionId: string, selector: AuthCredentialSelector): void;
|
|
491
674
|
/**
|
|
492
675
|
* Register a per-provider API key sourced from user configuration
|
|
493
676
|
* (e.g. `models.yml` `providers.<name>.apiKey`). Higher priority than
|
|
@@ -540,7 +723,7 @@ export declare class AuthStorage {
|
|
|
540
723
|
* Check if credentials exist for a provider in storage.
|
|
541
724
|
*/
|
|
542
725
|
has(provider: string): boolean;
|
|
543
|
-
hasAuth(provider: string): boolean;
|
|
726
|
+
hasAuth(provider: string, sessionId?: string): boolean;
|
|
544
727
|
/**
|
|
545
728
|
* Credential type that a provider/session will dispatch first without performing I/O.
|
|
546
729
|
* Mirrors getApiKey selector validation, overrides, session OAuth stickiness,
|
|
@@ -558,7 +741,7 @@ export declare class AuthStorage {
|
|
|
558
741
|
/**
|
|
559
742
|
* Get OAuth credentials for a provider.
|
|
560
743
|
*/
|
|
561
|
-
getOAuthCredential(provider: string): OAuthCredential | undefined;
|
|
744
|
+
getOAuthCredential(provider: string, sessionId?: string): OAuthCredential | undefined;
|
|
562
745
|
/**
|
|
563
746
|
* Get the OAuth `accountId` for a provider, preferring the credential that is
|
|
564
747
|
* session-sticky for `sessionId` when multiple OAuth credentials are configured.
|
|
@@ -617,6 +800,14 @@ export declare class AuthStorage {
|
|
|
617
800
|
* Environment-variable API keys are not enumerated — the caller's intent
|
|
618
801
|
* here is "which of my stored credentials is broken".
|
|
619
802
|
*/
|
|
803
|
+
/** Return a safe cache-only usage observation. */
|
|
804
|
+
getCachedUsageReport(provider: Provider, credentialId: number, baseUrl?: string): CachedUsageReport | undefined;
|
|
805
|
+
/** Cache-only health observation; unknown means no retained explicit check. */
|
|
806
|
+
getCachedCredentialHealth(credentialId: number): CachedCredentialHealth;
|
|
807
|
+
peekCachedCredentialHealthForSource(provider: string, source: "env" | "config" | "runtime"): CachedCredentialHealth;
|
|
808
|
+
recordCredentialHealthForSource(provider: string, source: "env" | "config" | "runtime", health: CachedCredentialHealth): void;
|
|
809
|
+
/** Explicit API-key probe; key bytes are not retained in the returned result. */
|
|
810
|
+
checkApiKeyCredential(provider: Provider, apiKey: string, options?: ApiKeyCredentialCheckOptions): Promise<ApiKeyCredentialCheckResult>;
|
|
620
811
|
checkCredentials(options?: CheckCredentialsOptions): Promise<CredentialHealthResult[]>;
|
|
621
812
|
/**
|
|
622
813
|
* Marks the current session's credential as temporarily blocked due to usage limits.
|
|
@@ -736,6 +927,9 @@ export declare class SqliteAuthCredentialStore implements AuthCredentialStore {
|
|
|
736
927
|
constructor(db: Database);
|
|
737
928
|
static open(dbPath?: string): Promise<SqliteAuthCredentialStore>;
|
|
738
929
|
listAuthCredentials(provider?: string): StoredAuthCredential[];
|
|
930
|
+
listCredentialInventory(provider?: string): CredentialInventoryRecord[];
|
|
931
|
+
listCredentialRemovalTargets(provider?: string): CredentialRemovalTarget[];
|
|
932
|
+
removeAuthCredentialsHard(provider: string, targets: readonly CredentialRemovalTarget[]): AuthCredentialHardRemovalResult;
|
|
739
933
|
claimOAuthRefreshLease(credentialId: number, expectedRefresh: string, force: boolean, owner: string, nowMs: number, leaseMs: number): OAuthRefreshLeaseClaim;
|
|
740
934
|
completeOAuthRefreshLease(lease: OAuthRefreshLease, credential: OAuthCredential): boolean;
|
|
741
935
|
releaseOAuthRefreshLease(lease: OAuthRefreshLease): void;
|
package/dist/types/core.d.ts
CHANGED
|
@@ -19,6 +19,7 @@ export * from "./model-thinking";
|
|
|
19
19
|
export * from "./models";
|
|
20
20
|
export * from "./provider-models";
|
|
21
21
|
export { getProviderRuntimeDescriptor, PROVIDER_RUNTIME_DESCRIPTORS, type ProviderRuntimeDescriptor, } from "./providers/register-builtins";
|
|
22
|
+
export { hasAdjacentPrivateThinkingBlocks } from "./providers/transform-messages";
|
|
22
23
|
export * from "./rate-limit-utils";
|
|
23
24
|
export * from "./stream";
|
|
24
25
|
export * from "./types";
|
|
@@ -11,9 +11,12 @@ interface CacheEntry<TApi extends Api = Api> {
|
|
|
11
11
|
* match — the cache already incorporates the same static state.
|
|
12
12
|
*/
|
|
13
13
|
staticFingerprint: string;
|
|
14
|
+
/** IDs returned by the authoritative dynamic provider catalog, when retained. */
|
|
15
|
+
dynamicModelIds: string[] | undefined;
|
|
16
|
+
dynamicModelProvenance: string | undefined;
|
|
14
17
|
}
|
|
15
18
|
/** Close the shared cache only when it owns the exact requested database path. */
|
|
16
19
|
export declare function closeModelCache(dbPath?: string): boolean;
|
|
17
20
|
export declare function readModelCache<TApi extends Api>(providerId: string, ttlMs: number, now: () => number, dbPath?: string): CacheEntry<TApi> | null;
|
|
18
|
-
export declare function writeModelCache<TApi extends Api>(providerId: string, updatedAt: number, models: Model<TApi>[], authoritative: boolean, staticFingerprint: string, dbPath?: string): void;
|
|
21
|
+
export declare function writeModelCache<TApi extends Api>(providerId: string, updatedAt: number, models: Model<TApi>[], authoritative: boolean, staticFingerprint: string, dbPath?: string, dynamicModelIds?: readonly string[], dynamicModelProvenance?: string): void;
|
|
19
22
|
export {};
|