@gajae-code/ai 0.11.6 → 0.11.8
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 +22 -0
- package/dist/types/auth-broker/client.d.ts +2 -1
- package/dist/types/auth-broker/remote-store.d.ts +2 -1
- package/dist/types/auth-broker/types.d.ts +3 -1
- package/dist/types/auth-broker/wire-schemas.d.ts +68 -0
- package/dist/types/auth-storage.d.ts +21 -1
- package/dist/types/provider-models/openai-compat.d.ts +2 -2
- package/dist/types/providers/anthropic.d.ts +9 -0
- package/dist/types/providers/transform-messages.d.ts +1 -0
- package/dist/types/types.d.ts +3 -1
- package/dist/types/utils/oauth/alibaba-token-plan.d.ts +19 -0
- package/dist/types/utils/oauth/types.d.ts +1 -1
- package/package.json +2 -2
- package/src/auth-broker/client.ts +13 -0
- package/src/auth-broker/refresher.ts +1 -0
- package/src/auth-broker/remote-store.ts +25 -0
- package/src/auth-broker/server.ts +10 -2
- package/src/auth-broker/types.ts +4 -0
- package/src/auth-broker/wire-schemas.ts +17 -1
- package/src/auth-storage.ts +199 -30
- package/src/model-thinking.ts +11 -3
- package/src/models.json +5362 -1219
- package/src/provider-models/descriptors.ts +5 -6
- package/src/provider-models/openai-compat.ts +12 -12
- package/src/providers/amazon-bedrock.ts +4 -0
- package/src/providers/anthropic.ts +78 -22
- package/src/providers/openai-completions-compat.ts +2 -2
- package/src/providers/openai-completions.ts +4 -1
- package/src/providers/openai-responses.ts +4 -1
- package/src/providers/transform-messages.ts +25 -6
- package/src/stream.ts +1 -1
- package/src/types.ts +7 -2
- package/src/utils/oauth/{alibaba-coding-plan.ts → alibaba-token-plan.ts} +12 -11
- package/src/utils/oauth/index.ts +3 -2
- package/src/utils/oauth/types.ts +1 -1
- package/src/utils/validation.ts +17 -2
- package/src/utils.ts +41 -4
- package/dist/types/utils/oauth/alibaba-coding-plan.d.ts +0 -18
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.11.8] - 2026-07-23
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- OpenAI Responses / Codex native history replay no longer submits missing resident-image placeholders as `input_image.image_url`. Invalid values (including `[Session resident imageUrl blob missing: …]`) are dropped, or retained as `file_id`-only parts when a non-empty `file_id` is present, so a single unavailable historical image cannot brick `/retry` (#2924).
|
|
10
|
+
- Raised the first-event stream timeout floor to five minutes for `alibaba-token-plan` models using the OpenAI Completions and Responses APIs, while preserving caller and environment overrides and the existing inter-event idle timeout.
|
|
11
|
+
- OAuth refresh peer-rotation recovery now runs before failure classification instead of only on the definitive-failure path, and the definitive matcher recognizes the "grant is invalid" phrasing. Providers whose invalid-grant response does not contain the literal `invalid_grant` (e.g. Kimi's 400 "The provided authorization grant is invalid") previously had rotation races misclassified as transient, temp-blocking a healthy credential for five minutes on every race; with Kimi's ~12-minute access tokens and multiple processes sharing the credential store this surfaced as repeated logouts. Genuine revocations are now disabled with a cause instead of looping temp-blocks.
|
|
12
|
+
- Anthropic 400 `Invalid \`signature\` in \`thinking\` block` responses now trigger the one-shot thinking replay repair instead of failing the turn. The existing repair matcher only recognized the "latest assistant message ... cannot be modified" wording, so the signature-validation variant — which can cite a `thinking`/`redacted_thinking` block anywhere in the replayed history (e.g. after compaction/pruning rewrote an earlier turn) — was treated as a fatal request error. The retry now rebuilds the request with thinking blocks dropped from every replayed assistant message (`repairAllAssistantThinking`), while the latest-message mutation variant keeps the targeted latest-only repair.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- Raw tool-argument rejection hooks can now select from bounded, authority-controlled correction codes. Unknown or extension-supplied values retain the byte-for-byte generic rejection instead of reaching model-visible validation errors.
|
|
17
|
+
|
|
18
|
+
## [0.11.7] - 2026-07-22
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
- Replaced the `alibaba-coding-plan` provider with first-class `alibaba-token-plan` support. The `/login` OAuth list, provider descriptor, model manager, models.dev descriptor, and bundled `models.json` now target the maintained Alibaba Token Plan endpoint (`https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1`, env `ALIBABA_TOKEN_PLAN_API_KEY`) and validate logins against `deepseek-v4-pro`. The retired `alibaba-coding-plan` provider pointed at `coding-intl.dashscope.aliyuncs.com`, which rejected real token-plan keys with 401 and was the only Alibaba entry exposed in `/login`.
|
|
23
|
+
|
|
5
24
|
## [0.11.4] - 2026-07-20
|
|
6
25
|
|
|
7
26
|
### Added
|
|
@@ -32,6 +51,9 @@
|
|
|
32
51
|
### Fixed
|
|
33
52
|
|
|
34
53
|
- Fixed frequent `Request blocked (code=invalid_prompt)` failures on gpt-5.6 (Sol/Terra/Luna) subagent, default-agent, and compaction turns (ref openai/codex#32028, oh-my-pi#5184). Leaked Harmony control-token markers (e.g. `<|channel|>analysis`) were only neutralized on the replayed-history payload path, so markers in assistant reasoning summaries, live-converted message/tool-output text, and user-authored content reached the OpenAI Responses and OpenAI-codex-responses transports verbatim and wedged the session (the poisoned item was re-sent every turn). Both transports now neutralize reserved control tokens across the entire outgoing `input` array at the request boundary via an idempotent zero-width-space insertion that keeps the text human-readable.
|
|
54
|
+
### Fixed
|
|
55
|
+
|
|
56
|
+
- Fixed Fable 5 adaptive thinking being billed but never displayed: model discovery now classifies `claude-fable-*` as `anthropic-adaptive` (was cached as `budget`, sending `enabled`+`budget_tokens` that Fable answers with signature-only thinking), and `supportsAdaptiveThinkingDisplay` opts Fable into `display: "summarized"` on both Anthropic Messages and Bedrock Converse transports (#2791).
|
|
35
57
|
|
|
36
58
|
## [0.10.0] - 2026-07-12
|
|
37
59
|
### Fixed
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AuthCredential } from "../auth-storage";
|
|
2
|
-
import type { CredentialDisableResponse, CredentialIfAbsentUploadResponse, CredentialRefreshResponse, CredentialUploadResponse, HealthzResponse, SnapshotResponse, SnapshotStreamEvent, UsageResponse } from "./types";
|
|
2
|
+
import type { CredentialDisableResponse, CredentialIfAbsentUploadResponse, 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;
|
|
@@ -61,6 +61,7 @@ export declare class AuthBrokerClient {
|
|
|
61
61
|
}): AsyncGenerator<SnapshotStreamEvent>;
|
|
62
62
|
fetchUsage(signal?: AbortSignal): Promise<UsageResponse>;
|
|
63
63
|
refreshCredential(id: number, signal?: AbortSignal): Promise<CredentialRefreshResponse>;
|
|
64
|
+
refreshMCPCredential(id: number, body: CredentialRefreshRequest, signal?: AbortSignal): Promise<CredentialRefreshResponse>;
|
|
64
65
|
disableCredential(id: number, cause: string, signal?: AbortSignal): Promise<CredentialDisableResponse>;
|
|
65
66
|
uploadCredential(provider: string, credential: AuthCredential, signal?: AbortSignal): Promise<CredentialUploadResponse>;
|
|
66
67
|
uploadCredentialIfAbsent(provider: string, credential: AuthCredential, signal?: AbortSignal): Promise<CredentialIfAbsentUploadResponse>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type AuthCredential, type AuthCredentialIfAbsentResult, type AuthCredentialStore, type OAuthCredential, type StoredAuthCredential } from "../auth-storage";
|
|
1
|
+
import { type AuthCredential, type AuthCredentialIfAbsentResult, type AuthCredentialStore, type MCPOAuthRefreshClient, type OAuthCredential, type StoredAuthCredential } from "../auth-storage";
|
|
2
2
|
import type { Provider } from "../types";
|
|
3
3
|
import type { UsageReport } from "../usage";
|
|
4
4
|
import type { OAuthCredentials } from "../utils/oauth/types";
|
|
@@ -78,6 +78,7 @@ export declare class RemoteAuthCredentialStore implements AuthCredentialStore {
|
|
|
78
78
|
* the `refresh` slot.
|
|
79
79
|
*/
|
|
80
80
|
refreshOAuthCredential(_provider: Provider, credentialId: number, _credential: OAuthCredential, signal?: AbortSignal): Promise<OAuthCredentials>;
|
|
81
|
+
refreshMCPOAuthCredential(credentialId: number, credential: OAuthCredential, client: MCPOAuthRefreshClient, signal?: AbortSignal): Promise<OAuthCredential>;
|
|
81
82
|
/**
|
|
82
83
|
* Store-level hook consumed by `AuthStorage.fetchUsageReports()` — proxies
|
|
83
84
|
* to the broker's `/v1/usage` endpoint. The broker's egress IP isn't
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* clients use `access` tokens directly and call back to the broker when a
|
|
6
6
|
* credential expires or a 401 surfaces on a supposedly-fresh credential.
|
|
7
7
|
*/
|
|
8
|
-
import type { AuthCredential, AuthCredentialIfAbsentReason, AuthCredentialSnapshot, AuthCredentialSnapshotEntry } from "../auth-storage";
|
|
8
|
+
import type { AuthCredential, AuthCredentialIfAbsentReason, AuthCredentialSnapshot, AuthCredentialSnapshotEntry, MCPOAuthRefreshClient } from "../auth-storage";
|
|
9
9
|
import type { UsageReport } from "../usage";
|
|
10
10
|
/** GET /v1/healthz response body. */
|
|
11
11
|
export interface HealthzResponse {
|
|
@@ -36,6 +36,8 @@ export interface UsageResponse {
|
|
|
36
36
|
export interface CredentialRefreshResponse {
|
|
37
37
|
entry: AuthCredentialSnapshotEntry;
|
|
38
38
|
}
|
|
39
|
+
/** Optional MCP client metadata; the broker still selects the stored token endpoint. */
|
|
40
|
+
export type CredentialRefreshRequest = MCPOAuthRefreshClient;
|
|
39
41
|
/** POST /v1/credential/:id/disable request body. */
|
|
40
42
|
export interface CredentialDisableRequest {
|
|
41
43
|
cause: string;
|
|
@@ -11,6 +11,10 @@
|
|
|
11
11
|
* `hasOnlyFields` allowlist for the same effect.
|
|
12
12
|
*/
|
|
13
13
|
import * as z from "zod/v4";
|
|
14
|
+
export declare const mcpOAuthBindingSchema: z.ZodObject<{
|
|
15
|
+
resourceOrigin: z.ZodString;
|
|
16
|
+
tokenEndpoint: z.ZodString;
|
|
17
|
+
}, z.core.$strict>;
|
|
14
18
|
/** Real OAuth credential (broker-side) — refresh token is the actual upstream value. */
|
|
15
19
|
export declare const oauthCredentialSchema: z.ZodObject<{
|
|
16
20
|
type: z.ZodLiteral<"oauth">;
|
|
@@ -21,6 +25,10 @@ export declare const oauthCredentialSchema: z.ZodObject<{
|
|
|
21
25
|
projectId: z.ZodOptional<z.ZodString>;
|
|
22
26
|
email: z.ZodOptional<z.ZodString>;
|
|
23
27
|
accountId: z.ZodOptional<z.ZodString>;
|
|
28
|
+
mcpBinding: z.ZodOptional<z.ZodObject<{
|
|
29
|
+
resourceOrigin: z.ZodString;
|
|
30
|
+
tokenEndpoint: z.ZodString;
|
|
31
|
+
}, z.core.$strict>>;
|
|
24
32
|
}, z.core.$strict>;
|
|
25
33
|
/** OAuth credential as it appears in broker snapshots — refresh replaced with sentinel. */
|
|
26
34
|
export declare const remoteOauthCredentialSchema: z.ZodObject<{
|
|
@@ -31,6 +39,10 @@ export declare const remoteOauthCredentialSchema: z.ZodObject<{
|
|
|
31
39
|
projectId: z.ZodOptional<z.ZodString>;
|
|
32
40
|
email: z.ZodOptional<z.ZodString>;
|
|
33
41
|
accountId: z.ZodOptional<z.ZodString>;
|
|
42
|
+
mcpBinding: z.ZodOptional<z.ZodObject<{
|
|
43
|
+
resourceOrigin: z.ZodString;
|
|
44
|
+
tokenEndpoint: z.ZodString;
|
|
45
|
+
}, z.core.$strict>>;
|
|
34
46
|
refresh: z.ZodLiteral<"__remote__">;
|
|
35
47
|
}, z.core.$strict>;
|
|
36
48
|
export declare const apiKeyCredentialSchema: z.ZodObject<{
|
|
@@ -47,6 +59,10 @@ export declare const writableAuthCredentialSchema: z.ZodDiscriminatedUnion<[z.Zo
|
|
|
47
59
|
projectId: z.ZodOptional<z.ZodString>;
|
|
48
60
|
email: z.ZodOptional<z.ZodString>;
|
|
49
61
|
accountId: z.ZodOptional<z.ZodString>;
|
|
62
|
+
mcpBinding: z.ZodOptional<z.ZodObject<{
|
|
63
|
+
resourceOrigin: z.ZodString;
|
|
64
|
+
tokenEndpoint: z.ZodString;
|
|
65
|
+
}, z.core.$strict>>;
|
|
50
66
|
}, z.core.$strict>, z.ZodObject<{
|
|
51
67
|
type: z.ZodLiteral<"api_key">;
|
|
52
68
|
key: z.ZodString;
|
|
@@ -60,6 +76,10 @@ export declare const snapshotCredentialSchema: z.ZodDiscriminatedUnion<[z.ZodObj
|
|
|
60
76
|
projectId: z.ZodOptional<z.ZodString>;
|
|
61
77
|
email: z.ZodOptional<z.ZodString>;
|
|
62
78
|
accountId: z.ZodOptional<z.ZodString>;
|
|
79
|
+
mcpBinding: z.ZodOptional<z.ZodObject<{
|
|
80
|
+
resourceOrigin: z.ZodString;
|
|
81
|
+
tokenEndpoint: z.ZodString;
|
|
82
|
+
}, z.core.$strict>>;
|
|
63
83
|
refresh: z.ZodLiteral<"__remote__">;
|
|
64
84
|
}, z.core.$strict>, z.ZodObject<{
|
|
65
85
|
type: z.ZodLiteral<"api_key">;
|
|
@@ -76,6 +96,10 @@ export declare const credentialSnapshotEntrySchema: z.ZodObject<{
|
|
|
76
96
|
projectId: z.ZodOptional<z.ZodString>;
|
|
77
97
|
email: z.ZodOptional<z.ZodString>;
|
|
78
98
|
accountId: z.ZodOptional<z.ZodString>;
|
|
99
|
+
mcpBinding: z.ZodOptional<z.ZodObject<{
|
|
100
|
+
resourceOrigin: z.ZodString;
|
|
101
|
+
tokenEndpoint: z.ZodString;
|
|
102
|
+
}, z.core.$strict>>;
|
|
79
103
|
refresh: z.ZodLiteral<"__remote__">;
|
|
80
104
|
}, z.core.$strict>, z.ZodObject<{
|
|
81
105
|
type: z.ZodLiteral<"api_key">;
|
|
@@ -94,6 +118,10 @@ export declare const snapshotEntrySchema: z.ZodObject<{
|
|
|
94
118
|
projectId: z.ZodOptional<z.ZodString>;
|
|
95
119
|
email: z.ZodOptional<z.ZodString>;
|
|
96
120
|
accountId: z.ZodOptional<z.ZodString>;
|
|
121
|
+
mcpBinding: z.ZodOptional<z.ZodObject<{
|
|
122
|
+
resourceOrigin: z.ZodString;
|
|
123
|
+
tokenEndpoint: z.ZodString;
|
|
124
|
+
}, z.core.$strict>>;
|
|
97
125
|
refresh: z.ZodLiteral<"__remote__">;
|
|
98
126
|
}, z.core.$strict>, z.ZodObject<{
|
|
99
127
|
type: z.ZodLiteral<"api_key">;
|
|
@@ -129,6 +157,10 @@ export declare const snapshotResponseSchema: z.ZodObject<{
|
|
|
129
157
|
projectId: z.ZodOptional<z.ZodString>;
|
|
130
158
|
email: z.ZodOptional<z.ZodString>;
|
|
131
159
|
accountId: z.ZodOptional<z.ZodString>;
|
|
160
|
+
mcpBinding: z.ZodOptional<z.ZodObject<{
|
|
161
|
+
resourceOrigin: z.ZodString;
|
|
162
|
+
tokenEndpoint: z.ZodString;
|
|
163
|
+
}, z.core.$strict>>;
|
|
132
164
|
refresh: z.ZodLiteral<"__remote__">;
|
|
133
165
|
}, z.core.$strict>, z.ZodObject<{
|
|
134
166
|
type: z.ZodLiteral<"api_key">;
|
|
@@ -160,6 +192,10 @@ export declare const snapshotStreamSnapshotEventSchema: z.ZodObject<{
|
|
|
160
192
|
projectId: z.ZodOptional<z.ZodString>;
|
|
161
193
|
email: z.ZodOptional<z.ZodString>;
|
|
162
194
|
accountId: z.ZodOptional<z.ZodString>;
|
|
195
|
+
mcpBinding: z.ZodOptional<z.ZodObject<{
|
|
196
|
+
resourceOrigin: z.ZodString;
|
|
197
|
+
tokenEndpoint: z.ZodString;
|
|
198
|
+
}, z.core.$strict>>;
|
|
163
199
|
refresh: z.ZodLiteral<"__remote__">;
|
|
164
200
|
}, z.core.$strict>, z.ZodObject<{
|
|
165
201
|
type: z.ZodLiteral<"api_key">;
|
|
@@ -192,6 +228,10 @@ export declare const snapshotStreamEntryEventSchema: z.ZodObject<{
|
|
|
192
228
|
projectId: z.ZodOptional<z.ZodString>;
|
|
193
229
|
email: z.ZodOptional<z.ZodString>;
|
|
194
230
|
accountId: z.ZodOptional<z.ZodString>;
|
|
231
|
+
mcpBinding: z.ZodOptional<z.ZodObject<{
|
|
232
|
+
resourceOrigin: z.ZodString;
|
|
233
|
+
tokenEndpoint: z.ZodString;
|
|
234
|
+
}, z.core.$strict>>;
|
|
195
235
|
refresh: z.ZodLiteral<"__remote__">;
|
|
196
236
|
}, z.core.$strict>, z.ZodObject<{
|
|
197
237
|
type: z.ZodLiteral<"api_key">;
|
|
@@ -236,6 +276,10 @@ export declare const snapshotStreamEventSchema: z.ZodDiscriminatedUnion<[z.ZodOb
|
|
|
236
276
|
projectId: z.ZodOptional<z.ZodString>;
|
|
237
277
|
email: z.ZodOptional<z.ZodString>;
|
|
238
278
|
accountId: z.ZodOptional<z.ZodString>;
|
|
279
|
+
mcpBinding: z.ZodOptional<z.ZodObject<{
|
|
280
|
+
resourceOrigin: z.ZodString;
|
|
281
|
+
tokenEndpoint: z.ZodString;
|
|
282
|
+
}, z.core.$strict>>;
|
|
239
283
|
refresh: z.ZodLiteral<"__remote__">;
|
|
240
284
|
}, z.core.$strict>, z.ZodObject<{
|
|
241
285
|
type: z.ZodLiteral<"api_key">;
|
|
@@ -266,6 +310,10 @@ export declare const snapshotStreamEventSchema: z.ZodDiscriminatedUnion<[z.ZodOb
|
|
|
266
310
|
projectId: z.ZodOptional<z.ZodString>;
|
|
267
311
|
email: z.ZodOptional<z.ZodString>;
|
|
268
312
|
accountId: z.ZodOptional<z.ZodString>;
|
|
313
|
+
mcpBinding: z.ZodOptional<z.ZodObject<{
|
|
314
|
+
resourceOrigin: z.ZodString;
|
|
315
|
+
tokenEndpoint: z.ZodString;
|
|
316
|
+
}, z.core.$strict>>;
|
|
269
317
|
refresh: z.ZodLiteral<"__remote__">;
|
|
270
318
|
}, z.core.$strict>, z.ZodObject<{
|
|
271
319
|
type: z.ZodLiteral<"api_key">;
|
|
@@ -348,6 +396,10 @@ export declare const usageResponseSchema: z.ZodObject<{
|
|
|
348
396
|
raw: z.ZodOptional<z.ZodUnknown>;
|
|
349
397
|
}, z.core.$strip>>;
|
|
350
398
|
}, z.core.$strict>;
|
|
399
|
+
export declare const credentialRefreshRequestSchema: z.ZodObject<{
|
|
400
|
+
clientId: z.ZodOptional<z.ZodString>;
|
|
401
|
+
clientSecret: z.ZodOptional<z.ZodString>;
|
|
402
|
+
}, z.core.$strict>;
|
|
351
403
|
export declare const credentialRefreshResponseSchema: z.ZodObject<{
|
|
352
404
|
entry: z.ZodObject<{
|
|
353
405
|
id: z.ZodNumber;
|
|
@@ -360,6 +412,10 @@ export declare const credentialRefreshResponseSchema: z.ZodObject<{
|
|
|
360
412
|
projectId: z.ZodOptional<z.ZodString>;
|
|
361
413
|
email: z.ZodOptional<z.ZodString>;
|
|
362
414
|
accountId: z.ZodOptional<z.ZodString>;
|
|
415
|
+
mcpBinding: z.ZodOptional<z.ZodObject<{
|
|
416
|
+
resourceOrigin: z.ZodString;
|
|
417
|
+
tokenEndpoint: z.ZodString;
|
|
418
|
+
}, z.core.$strict>>;
|
|
363
419
|
refresh: z.ZodLiteral<"__remote__">;
|
|
364
420
|
}, z.core.$strict>, z.ZodObject<{
|
|
365
421
|
type: z.ZodLiteral<"api_key">;
|
|
@@ -385,6 +441,10 @@ export declare const credentialUploadRequestSchema: z.ZodObject<{
|
|
|
385
441
|
projectId: z.ZodOptional<z.ZodString>;
|
|
386
442
|
email: z.ZodOptional<z.ZodString>;
|
|
387
443
|
accountId: z.ZodOptional<z.ZodString>;
|
|
444
|
+
mcpBinding: z.ZodOptional<z.ZodObject<{
|
|
445
|
+
resourceOrigin: z.ZodString;
|
|
446
|
+
tokenEndpoint: z.ZodString;
|
|
447
|
+
}, z.core.$strict>>;
|
|
388
448
|
}, z.core.$strict>, z.ZodObject<{
|
|
389
449
|
type: z.ZodLiteral<"api_key">;
|
|
390
450
|
key: z.ZodString;
|
|
@@ -402,6 +462,10 @@ export declare const credentialUploadResponseSchema: z.ZodObject<{
|
|
|
402
462
|
projectId: z.ZodOptional<z.ZodString>;
|
|
403
463
|
email: z.ZodOptional<z.ZodString>;
|
|
404
464
|
accountId: z.ZodOptional<z.ZodString>;
|
|
465
|
+
mcpBinding: z.ZodOptional<z.ZodObject<{
|
|
466
|
+
resourceOrigin: z.ZodString;
|
|
467
|
+
tokenEndpoint: z.ZodString;
|
|
468
|
+
}, z.core.$strict>>;
|
|
405
469
|
refresh: z.ZodLiteral<"__remote__">;
|
|
406
470
|
}, z.core.$strict>, z.ZodObject<{
|
|
407
471
|
type: z.ZodLiteral<"api_key">;
|
|
@@ -433,6 +497,10 @@ export declare const credentialIfAbsentUploadResponseSchema: z.ZodObject<{
|
|
|
433
497
|
projectId: z.ZodOptional<z.ZodString>;
|
|
434
498
|
email: z.ZodOptional<z.ZodString>;
|
|
435
499
|
accountId: z.ZodOptional<z.ZodString>;
|
|
500
|
+
mcpBinding: z.ZodOptional<z.ZodObject<{
|
|
501
|
+
resourceOrigin: z.ZodString;
|
|
502
|
+
tokenEndpoint: z.ZodString;
|
|
503
|
+
}, z.core.$strict>>;
|
|
436
504
|
refresh: z.ZodLiteral<"__remote__">;
|
|
437
505
|
}, z.core.$strict>, z.ZodObject<{
|
|
438
506
|
type: z.ZodLiteral<"api_key">;
|
|
@@ -15,10 +15,26 @@ export type ApiKeyCredential = {
|
|
|
15
15
|
type: "api_key";
|
|
16
16
|
key: string;
|
|
17
17
|
};
|
|
18
|
+
export interface MCPOAuthBinding {
|
|
19
|
+
/** Exact HTTP(S) origin of the MCP resource endpoint. */
|
|
20
|
+
resourceOrigin: string;
|
|
21
|
+
/** Exact canonical HTTP(S) token endpoint used to create and refresh the credential. */
|
|
22
|
+
tokenEndpoint: string;
|
|
23
|
+
}
|
|
24
|
+
export declare function resolveMCPOAuthResourceOrigin(value: string): string | undefined;
|
|
25
|
+
export declare function resolveMCPOAuthTokenEndpoint(value: string): string | undefined;
|
|
26
|
+
export declare function isCanonicalMCPOAuthBinding(binding: MCPOAuthBinding): boolean;
|
|
27
|
+
export declare function assertCanonicalMCPOAuthBinding(binding: MCPOAuthBinding | undefined): asserts binding is MCPOAuthBinding;
|
|
18
28
|
export type OAuthCredential = {
|
|
19
29
|
type: "oauth";
|
|
30
|
+
/** Present only for credentials created by runtime MCP OAuth. */
|
|
31
|
+
mcpBinding?: MCPOAuthBinding;
|
|
20
32
|
} & OAuthCredentials;
|
|
21
33
|
export type AuthCredential = ApiKeyCredential | OAuthCredential;
|
|
34
|
+
export interface MCPOAuthRefreshClient {
|
|
35
|
+
clientId?: string;
|
|
36
|
+
clientSecret?: string;
|
|
37
|
+
}
|
|
22
38
|
export type AuthCredentialEntry = AuthCredential | AuthCredential[];
|
|
23
39
|
export type AuthStorageData = Record<string, AuthCredentialEntry>;
|
|
24
40
|
/**
|
|
@@ -161,6 +177,8 @@ export interface AuthCredentialStore {
|
|
|
161
177
|
* for `timeoutMs * (maxRetries + 1)`.
|
|
162
178
|
*/
|
|
163
179
|
refreshOAuthCredential?(provider: Provider, credentialId: number, credential: OAuthCredential, signal?: AbortSignal): Promise<OAuthCredentials>;
|
|
180
|
+
/** Broker-backed MCP refresh using the broker's stored token endpoint and refresh secret. */
|
|
181
|
+
refreshMCPOAuthCredential?(credentialId: number, credential: OAuthCredential, client: MCPOAuthRefreshClient, signal?: AbortSignal): Promise<OAuthCredential>;
|
|
164
182
|
/**
|
|
165
183
|
* Optional async pre-read hook invoked after AuthStorage selects a stored
|
|
166
184
|
* credential but before it returns that credential for an outbound request.
|
|
@@ -597,7 +615,7 @@ export declare class AuthStorage {
|
|
|
597
615
|
* refresh attempt, which is required for providers that rotate refresh tokens
|
|
598
616
|
* on every successful refresh.
|
|
599
617
|
*/
|
|
600
|
-
refreshCredentialById(id: number, signal?: AbortSignal): Promise<AuthCredentialSnapshotEntry>;
|
|
618
|
+
refreshCredentialById(id: number, signal?: AbortSignal, mcpClient?: MCPOAuthRefreshClient): Promise<AuthCredentialSnapshotEntry>;
|
|
601
619
|
/**
|
|
602
620
|
* Force-refresh the OAuth credential with the given id, bypassing the
|
|
603
621
|
* not-yet-expired guard. Used by the auth-broker server to honour
|
|
@@ -607,6 +625,8 @@ export declare class AuthStorage {
|
|
|
607
625
|
* Throws when no OAuth credential with that id is loaded.
|
|
608
626
|
*/
|
|
609
627
|
forceRefreshCredentialById(id: number, signal?: AbortSignal): Promise<AuthCredentialSnapshotEntry>;
|
|
628
|
+
/** Force-refresh the first OAuth credential stored for a provider. */
|
|
629
|
+
forceRefreshOAuthCredential(provider: string, expected: OAuthCredential, client?: MCPOAuthRefreshClient, signal?: AbortSignal): Promise<OAuthCredential>;
|
|
610
630
|
/**
|
|
611
631
|
* Disable the credential with the given id and emit a
|
|
612
632
|
* {@link CredentialDisabledEvent}. Used by the auth-broker server to honour
|
|
@@ -116,11 +116,11 @@ export interface KiloModelManagerConfig {
|
|
|
116
116
|
baseUrl?: string;
|
|
117
117
|
}
|
|
118
118
|
export declare function kiloModelManagerOptions(config?: KiloModelManagerConfig): ModelManagerOptions<"openai-completions">;
|
|
119
|
-
export interface
|
|
119
|
+
export interface AlibabaTokenPlanModelManagerConfig {
|
|
120
120
|
apiKey?: string;
|
|
121
121
|
baseUrl?: string;
|
|
122
122
|
}
|
|
123
|
-
export declare function
|
|
123
|
+
export declare function alibabaTokenPlanModelManagerOptions(config?: AlibabaTokenPlanModelManagerConfig): ModelManagerOptions<"openai-completions">;
|
|
124
124
|
export interface VercelAiGatewayModelManagerConfig {
|
|
125
125
|
apiKey?: string;
|
|
126
126
|
baseUrl?: string;
|
|
@@ -42,6 +42,14 @@ type AnthropicCacheControl = {
|
|
|
42
42
|
export declare function clearAnthropicFastModeFallback(providerSessionState: Map<string, ProviderSessionState> | undefined): void;
|
|
43
43
|
export declare function isAnthropicFastModeUnsupportedError(error: unknown): boolean;
|
|
44
44
|
export declare function isAnthropicThinkingBlockMutationError(error: unknown): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* 400 shape where a replayed `thinking`/`redacted_thinking` block fails signature
|
|
47
|
+
* validation, e.g. `messages.5.content.24: Invalid \`signature\` in \`thinking\` block`.
|
|
48
|
+
* Unlike the latest-assistant mutation error above, the cited block can sit anywhere
|
|
49
|
+
* in the replayed history, so recovery must repair every assistant message rather
|
|
50
|
+
* than only the latest one.
|
|
51
|
+
*/
|
|
52
|
+
export declare function isAnthropicThinkingSignatureInvalidError(error: unknown): boolean;
|
|
45
53
|
export declare const claudeCodeVersion = "2.1.63";
|
|
46
54
|
export declare const claudeToolPrefix: string;
|
|
47
55
|
export declare const claudeCodeSystemInstruction = "You are a Claude agent, built on Anthropic's Claude Agent SDK.";
|
|
@@ -184,6 +192,7 @@ export declare function buildAnthropicClientOptions(args: AnthropicClientOptions
|
|
|
184
192
|
export declare function normalizeCacheControlTtlOrdering(params: MessageCreateParamsStreaming): void;
|
|
185
193
|
export declare function convertAnthropicMessages(messages: Message[], model: Model<"anthropic-messages">, isOAuthToken: boolean, options?: {
|
|
186
194
|
repairLatestAssistantThinking?: boolean;
|
|
195
|
+
repairAllAssistantThinking?: boolean;
|
|
187
196
|
}): MessageParam[];
|
|
188
197
|
/**
|
|
189
198
|
* Normalize a JSON Schema node for Anthropic tool `input_schema`.
|
|
@@ -11,4 +11,5 @@ import type { Api, AssistantMessage, Message, Model } from "../types";
|
|
|
11
11
|
*/
|
|
12
12
|
export declare function transformMessages<TApi extends Api>(messages: Message[], model: Model<TApi>, normalizeToolCallId?: (id: string, model: Model<TApi>, source: AssistantMessage) => string, options?: {
|
|
13
13
|
repairLatestAssistantThinking?: boolean;
|
|
14
|
+
repairAllAssistantThinking?: boolean;
|
|
14
15
|
}): Message[];
|
package/dist/types/types.d.ts
CHANGED
|
@@ -51,7 +51,7 @@ export interface ThinkingConfig {
|
|
|
51
51
|
/** Provider-specific transport used to encode the selected effort. */
|
|
52
52
|
mode: ThinkingControlMode;
|
|
53
53
|
}
|
|
54
|
-
export type KnownProvider = "alibaba-
|
|
54
|
+
export type KnownProvider = "alibaba-token-plan" | "amazon-bedrock" | "azure-openai" | "anthropic" | "google" | "google-gemini-cli" | "google-antigravity" | "google-vertex" | "openai" | "openai-codex" | "kimi-code" | "minimax-code" | "minimax-code-cn" | "github-copilot" | "fireworks" | "firepass" | "fugu" | "gitlab-duo" | "cursor" | "deepseek" | "deepinfra" | "xai" | "groq" | "cerebras" | "openrouter" | "kilo" | "vercel-ai-gateway" | "zai" | "glm-zcode" | "mistral" | "minimax" | "opencode-go" | "opencode-zen" | "synthetic" | "cloudflare-ai-gateway" | "huggingface" | "litellm" | "moonshot" | "nvidia" | "nanogpt" | "ollama" | "ollama-cloud" | "qianfan" | "qwen-portal" | "together" | "venice" | "vllm" | "xiaomi" | "xiaomi-token-plan-sgp" | "xiaomi-token-plan-ams" | "xiaomi-token-plan-cn" | "zenmux" | "lm-studio";
|
|
55
55
|
export type Provider = KnownProvider | string;
|
|
56
56
|
import type { Effort } from "./model-thinking";
|
|
57
57
|
/** Token budgets for each thinking level (token-based providers only) */
|
|
@@ -519,6 +519,7 @@ export type TSchema = ZodType | TJsonSchema;
|
|
|
519
519
|
export type Static<S> = S extends ZodType ? z.infer<S> : S extends {
|
|
520
520
|
static: infer T;
|
|
521
521
|
} ? T : unknown;
|
|
522
|
+
export type RawArgumentRejectionCode = "ask-intent-review-requires-positive-round" | "ask-intent-contract-requires-non-empty-authority" | "ask-deep-interview-metadata-requires-deep-interview-gate";
|
|
522
523
|
export type RawArgumentValidationResult = {
|
|
523
524
|
outcome: "passthrough";
|
|
524
525
|
} | {
|
|
@@ -526,6 +527,7 @@ export type RawArgumentValidationResult = {
|
|
|
526
527
|
arguments: ToolCall["arguments"];
|
|
527
528
|
} | {
|
|
528
529
|
outcome: "reject";
|
|
530
|
+
code?: RawArgumentRejectionCode;
|
|
529
531
|
};
|
|
530
532
|
export interface Tool<TParameters extends TSchema = TSchema> {
|
|
531
533
|
name: string;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Alibaba Token Plan login flow.
|
|
3
|
+
*
|
|
4
|
+
* Alibaba Token Plan provides OpenAI-compatible models via
|
|
5
|
+
* https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1.
|
|
6
|
+
*
|
|
7
|
+
* This is not OAuth - it's a simple API key flow:
|
|
8
|
+
* 1. Open browser to Alibaba Cloud Model Studio console
|
|
9
|
+
* 2. User copies their API key
|
|
10
|
+
* 3. User pastes the API key into the CLI
|
|
11
|
+
*/
|
|
12
|
+
import type { OAuthController } from "./types";
|
|
13
|
+
/**
|
|
14
|
+
* Login to Alibaba Token Plan.
|
|
15
|
+
*
|
|
16
|
+
* Opens browser to API keys page, prompts user to paste their API key.
|
|
17
|
+
* Returns the API key directly (not OAuthCredentials - this isn't OAuth).
|
|
18
|
+
*/
|
|
19
|
+
export declare function loginAlibabaTokenPlan(options: OAuthController): Promise<string>;
|
|
@@ -7,7 +7,7 @@ export type OAuthCredentials = {
|
|
|
7
7
|
email?: string;
|
|
8
8
|
accountId?: string;
|
|
9
9
|
};
|
|
10
|
-
export type OAuthProvider = "alibaba-
|
|
10
|
+
export type OAuthProvider = "alibaba-token-plan" | "anthropic" | "cerebras" | "cloudflare-ai-gateway" | "cursor" | "deepseek" | "deepinfra" | "fireworks" | "firepass" | "fugu" | "github-copilot" | "google-gemini-cli" | "google-antigravity" | "gitlab-duo" | "huggingface" | "kimi-code" | "kilo" | "kagi" | "litellm" | "lm-studio" | "minimax-code" | "minimax-code-cn" | "moonshot" | "nvidia" | "nanogpt" | "ollama" | "ollama-cloud" | "openai-codex" | "openai-codex-device" | "opencode-go" | "opencode-zen" | "parallel" | "perplexity" | "qianfan" | "qwen-portal" | "synthetic" | "tavily" | "together" | "venice" | "vercel-ai-gateway" | "vllm" | "xai" | "glm-zcode" | "xiaomi" | "xiaomi-token-plan-sgp" | "xiaomi-token-plan-ams" | "xiaomi-token-plan-cn" | "zenmux" | "zai";
|
|
11
11
|
export type OAuthProviderId = OAuthProvider | (string & {});
|
|
12
12
|
export type OAuthPrompt = {
|
|
13
13
|
message: string;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@gajae-code/ai",
|
|
4
|
-
"version": "0.11.
|
|
4
|
+
"version": "0.11.8",
|
|
5
5
|
"description": "Unified LLM API with automatic model discovery and provider configuration",
|
|
6
6
|
"homepage": "https://gajae-code.com",
|
|
7
7
|
"author": "Yeachan-Heo and Gajae Code Contributors",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@anthropic-ai/sdk": "^0.94.0",
|
|
42
42
|
"@bufbuild/protobuf": "^2.12.0",
|
|
43
|
-
"@gajae-code/utils": "0.11.
|
|
43
|
+
"@gajae-code/utils": "0.11.8",
|
|
44
44
|
"openai": "^6.36.0",
|
|
45
45
|
"partial-json": "^0.1.7",
|
|
46
46
|
"zod": "4.4.3"
|
|
@@ -12,6 +12,7 @@ import type {
|
|
|
12
12
|
CredentialDisableRequest,
|
|
13
13
|
CredentialDisableResponse,
|
|
14
14
|
CredentialIfAbsentUploadResponse,
|
|
15
|
+
CredentialRefreshRequest,
|
|
15
16
|
CredentialRefreshResponse,
|
|
16
17
|
CredentialUploadRequest,
|
|
17
18
|
CredentialUploadResponse,
|
|
@@ -242,6 +243,18 @@ export class AuthBrokerClient {
|
|
|
242
243
|
}) as Promise<CredentialRefreshResponse>;
|
|
243
244
|
}
|
|
244
245
|
|
|
246
|
+
async refreshMCPCredential(
|
|
247
|
+
id: number,
|
|
248
|
+
body: CredentialRefreshRequest,
|
|
249
|
+
signal?: AbortSignal,
|
|
250
|
+
): Promise<CredentialRefreshResponse> {
|
|
251
|
+
return this.#request("POST", `/v1/credential/${id}/refresh`, {
|
|
252
|
+
body,
|
|
253
|
+
schema: credentialRefreshResponseSchema,
|
|
254
|
+
signal,
|
|
255
|
+
}) as Promise<CredentialRefreshResponse>;
|
|
256
|
+
}
|
|
257
|
+
|
|
245
258
|
async disableCredential(id: number, cause: string, signal?: AbortSignal): Promise<CredentialDisableResponse> {
|
|
246
259
|
const body: CredentialDisableRequest = { cause };
|
|
247
260
|
return this.#request("POST", `/v1/credential/${id}/disable`, {
|
|
@@ -96,6 +96,7 @@ export class AuthBrokerRefresher {
|
|
|
96
96
|
const targets: number[] = [];
|
|
97
97
|
for (const entry of snapshot.credentials) {
|
|
98
98
|
if (entry.credential.type !== "oauth") continue;
|
|
99
|
+
if (entry.credential.mcpBinding) continue;
|
|
99
100
|
const expires = entry.credential.expires;
|
|
100
101
|
if (typeof expires !== "number" || !Number.isFinite(expires)) continue;
|
|
101
102
|
if (expires > deadline) continue;
|
|
@@ -14,6 +14,8 @@ import {
|
|
|
14
14
|
type AuthCredentialIfAbsentResult,
|
|
15
15
|
type AuthCredentialSnapshotEntry,
|
|
16
16
|
type AuthCredentialStore,
|
|
17
|
+
assertCanonicalMCPOAuthBinding,
|
|
18
|
+
type MCPOAuthRefreshClient,
|
|
17
19
|
type OAuthCredential,
|
|
18
20
|
REMOTE_REFRESH_SENTINEL,
|
|
19
21
|
type StoredAuthCredential,
|
|
@@ -512,6 +514,29 @@ export class RemoteAuthCredentialStore implements AuthCredentialStore {
|
|
|
512
514
|
};
|
|
513
515
|
}
|
|
514
516
|
|
|
517
|
+
async refreshMCPOAuthCredential(
|
|
518
|
+
credentialId: number,
|
|
519
|
+
credential: OAuthCredential,
|
|
520
|
+
client: MCPOAuthRefreshClient,
|
|
521
|
+
signal?: AbortSignal,
|
|
522
|
+
): Promise<OAuthCredential> {
|
|
523
|
+
const { entry } = await this.#client.refreshMCPCredential(credentialId, client, signal);
|
|
524
|
+
if (entry.credential.type !== "oauth") {
|
|
525
|
+
throw new Error(`Broker returned non-OAuth credential for id=${credentialId}`);
|
|
526
|
+
}
|
|
527
|
+
assertCanonicalMCPOAuthBinding(credential.mcpBinding);
|
|
528
|
+
assertCanonicalMCPOAuthBinding(entry.credential.mcpBinding);
|
|
529
|
+
if (
|
|
530
|
+
entry.credential.mcpBinding.resourceOrigin !== credential.mcpBinding.resourceOrigin ||
|
|
531
|
+
entry.credential.mcpBinding.tokenEndpoint !== credential.mcpBinding.tokenEndpoint
|
|
532
|
+
) {
|
|
533
|
+
throw new Error("Broker returned mismatched MCP OAuth credential binding");
|
|
534
|
+
}
|
|
535
|
+
this.#applyCredentialEntry(entry);
|
|
536
|
+
this.#maybeRefreshSnapshot("MCP credential refresh");
|
|
537
|
+
return entry.credential;
|
|
538
|
+
}
|
|
539
|
+
|
|
515
540
|
/**
|
|
516
541
|
* Store-level hook consumed by `AuthStorage.fetchUsageReports()` — proxies
|
|
517
542
|
* to the broker's `/v1/usage` endpoint. The broker's egress IP isn't
|
|
@@ -16,6 +16,7 @@ import { AuthBrokerRefresher, type AuthBrokerRefresherSchedule } from "./refresh
|
|
|
16
16
|
import type {
|
|
17
17
|
CredentialDisableResponse,
|
|
18
18
|
CredentialIfAbsentUploadResponse,
|
|
19
|
+
CredentialRefreshRequest,
|
|
19
20
|
CredentialRefreshResponse,
|
|
20
21
|
CredentialUploadResponse,
|
|
21
22
|
HealthzResponse,
|
|
@@ -33,7 +34,11 @@ import {
|
|
|
33
34
|
DEFAULT_SERVER_IDLE_TIMEOUT_S,
|
|
34
35
|
DEFAULT_STREAM_KEEPALIVE_MS,
|
|
35
36
|
} from "./types";
|
|
36
|
-
import {
|
|
37
|
+
import {
|
|
38
|
+
credentialDisableRequestSchema,
|
|
39
|
+
credentialRefreshRequestSchema,
|
|
40
|
+
credentialUploadRequestSchema,
|
|
41
|
+
} from "./wire-schemas";
|
|
37
42
|
|
|
38
43
|
export interface AuthBrokerServerOptions {
|
|
39
44
|
/** Underlying credential storage (wraps the local SQLite store on the broker). */
|
|
@@ -561,7 +566,10 @@ export function startAuthBroker(opts: AuthBrokerServerOptions): AuthBrokerServer
|
|
|
561
566
|
if (refreshMatch) {
|
|
562
567
|
const id = Number.parseInt(refreshMatch[1], 10);
|
|
563
568
|
try {
|
|
564
|
-
const
|
|
569
|
+
const parsed = await parseBody(req, credentialRefreshRequestSchema, { allowEmpty: true });
|
|
570
|
+
if (!parsed.ok) return parsed.response;
|
|
571
|
+
const refreshRequest: CredentialRefreshRequest = parsed.data;
|
|
572
|
+
const entry = await opts.storage.refreshCredentialById(id, req.signal, refreshRequest);
|
|
565
573
|
const body: CredentialRefreshResponse = { entry };
|
|
566
574
|
logger.info("auth-broker credential refreshed", {
|
|
567
575
|
id,
|
package/src/auth-broker/types.ts
CHANGED
|
@@ -11,6 +11,7 @@ import type {
|
|
|
11
11
|
AuthCredentialIfAbsentReason,
|
|
12
12
|
AuthCredentialSnapshot,
|
|
13
13
|
AuthCredentialSnapshotEntry,
|
|
14
|
+
MCPOAuthRefreshClient,
|
|
14
15
|
} from "../auth-storage";
|
|
15
16
|
import type { UsageReport } from "../usage";
|
|
16
17
|
|
|
@@ -49,6 +50,9 @@ export interface CredentialRefreshResponse {
|
|
|
49
50
|
entry: AuthCredentialSnapshotEntry;
|
|
50
51
|
}
|
|
51
52
|
|
|
53
|
+
/** Optional MCP client metadata; the broker still selects the stored token endpoint. */
|
|
54
|
+
export type CredentialRefreshRequest = MCPOAuthRefreshClient;
|
|
55
|
+
|
|
52
56
|
/** POST /v1/credential/:id/disable request body. */
|
|
53
57
|
export interface CredentialDisableRequest {
|
|
54
58
|
cause: string;
|
|
@@ -11,11 +11,19 @@
|
|
|
11
11
|
* `hasOnlyFields` allowlist for the same effect.
|
|
12
12
|
*/
|
|
13
13
|
import * as z from "zod/v4";
|
|
14
|
-
import { REMOTE_REFRESH_SENTINEL } from "../auth-storage";
|
|
14
|
+
import { isCanonicalMCPOAuthBinding, REMOTE_REFRESH_SENTINEL } from "../auth-storage";
|
|
15
15
|
import { usageReportSchema } from "../usage";
|
|
16
16
|
|
|
17
17
|
// ─── Credential payloads ───────────────────────────────────────────────────
|
|
18
18
|
|
|
19
|
+
export const mcpOAuthBindingSchema = z
|
|
20
|
+
.object({
|
|
21
|
+
resourceOrigin: z.string().min(1),
|
|
22
|
+
tokenEndpoint: z.string().min(1),
|
|
23
|
+
})
|
|
24
|
+
.strict()
|
|
25
|
+
.refine(isCanonicalMCPOAuthBinding, { message: "MCP OAuth binding must use canonical HTTP(S) URLs" });
|
|
26
|
+
|
|
19
27
|
/** Real OAuth credential (broker-side) — refresh token is the actual upstream value. */
|
|
20
28
|
export const oauthCredentialSchema = z
|
|
21
29
|
.object({
|
|
@@ -37,6 +45,7 @@ export const oauthCredentialSchema = z
|
|
|
37
45
|
projectId: z.string().optional(),
|
|
38
46
|
email: z.string().optional(),
|
|
39
47
|
accountId: z.string().optional(),
|
|
48
|
+
mcpBinding: mcpOAuthBindingSchema.optional(),
|
|
40
49
|
})
|
|
41
50
|
.strict();
|
|
42
51
|
|
|
@@ -164,6 +173,13 @@ export const usageResponseSchema = z
|
|
|
164
173
|
|
|
165
174
|
// ─── Refresh ───────────────────────────────────────────────────────────────
|
|
166
175
|
|
|
176
|
+
export const credentialRefreshRequestSchema = z
|
|
177
|
+
.object({
|
|
178
|
+
clientId: z.string().optional(),
|
|
179
|
+
clientSecret: z.string().optional(),
|
|
180
|
+
})
|
|
181
|
+
.strict();
|
|
182
|
+
|
|
167
183
|
export const credentialRefreshResponseSchema = z
|
|
168
184
|
.object({
|
|
169
185
|
entry: credentialSnapshotEntrySchema,
|