@gajae-code/ai 0.16.3 → 0.16.6
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 +26 -0
- package/dist/types/auth-gateway/server.d.ts +4 -2
- package/dist/types/bedrock-claude-cache-policy.d.ts +21 -0
- package/dist/types/openai-completions-compat.d.ts +22 -0
- package/dist/types/providers/amazon-bedrock.d.ts +17 -2
- package/dist/types/providers/anthropic.d.ts +1 -0
- package/dist/types/providers/openai-completions-compat.d.ts +1 -1
- package/dist/types/providers/opencode-go-session.d.ts +5 -0
- package/dist/types/types.d.ts +9 -2
- package/dist/types/utils/codex-entitlement.d.ts +10 -0
- package/dist/types/utils/provider-response.d.ts +1 -0
- package/package.json +3 -3
- package/src/auth-gateway/server.ts +5 -0
- package/src/auth-storage.ts +17 -8
- package/src/bedrock-claude-cache-policy.d.ts +21 -0
- package/src/bedrock-claude-cache-policy.ts +68 -0
- package/src/model-pricing.ts +11 -0
- package/src/model-thinking.ts +43 -8
- package/src/models.json +37 -0
- package/src/openai-completions-compat.d.ts +22 -0
- package/src/openai-completions-compat.ts +77 -3
- package/src/providers/amazon-bedrock.d.ts +17 -2
- package/src/providers/amazon-bedrock.ts +19 -11
- package/src/providers/anthropic.d.ts +1 -0
- package/src/providers/anthropic.ts +44 -22
- package/src/providers/azure-openai-responses.ts +5 -2
- package/src/providers/cursor.ts +12 -6
- package/src/providers/google-gemini-cli.ts +6 -1
- package/src/providers/google-shared.ts +1 -1
- package/src/providers/kiro-api-key.ts +5 -2
- package/src/providers/kiro-codewhisperer.ts +10 -2
- package/src/providers/mock.ts +1 -0
- package/src/providers/ollama.ts +1 -1
- package/src/providers/openai-codex/response-handler.ts +1 -1
- package/src/providers/openai-codex-responses.ts +10 -2
- package/src/providers/openai-completions-compat.d.ts +1 -1
- package/src/providers/openai-completions-compat.ts +8 -1
- package/src/providers/openai-completions.ts +25 -3
- package/src/providers/openai-responses.ts +21 -3
- package/src/providers/opencode-go-session.d.ts +5 -0
- package/src/providers/opencode-go-session.ts +57 -0
- package/src/stream.ts +1 -0
- package/src/types.d.ts +9 -2
- package/src/types.ts +9 -0
- package/src/utils/codex-entitlement.d.ts +10 -0
- package/src/utils/codex-entitlement.ts +21 -0
- package/src/utils/discovery/antigravity.ts +2 -1
- package/src/utils/discovery/gemini.ts +2 -1
- package/src/utils/provider-response.d.ts +1 -0
- package/src/utils/provider-response.ts +9 -2
|
@@ -93,6 +93,11 @@ import {
|
|
|
93
93
|
processResponsesStream,
|
|
94
94
|
repairOrphanResponsesToolOutputs,
|
|
95
95
|
} from "./openai-responses-shared";
|
|
96
|
+
import {
|
|
97
|
+
applyOpenCodeGoSessionHeader,
|
|
98
|
+
resolveOpenCodeGoSessionId,
|
|
99
|
+
wrapFetchForOpenCodeGoSession,
|
|
100
|
+
} from "./opencode-go-session";
|
|
96
101
|
import { transformMessages } from "./transform-messages";
|
|
97
102
|
|
|
98
103
|
/**
|
|
@@ -378,6 +383,7 @@ export const streamOpenAIResponses: StreamFunction<"openai-responses"> = (
|
|
|
378
383
|
options?.headers,
|
|
379
384
|
options?.initiatorOverride,
|
|
380
385
|
cacheSessionId,
|
|
386
|
+
options?.providerSessionId,
|
|
381
387
|
cacheRetention,
|
|
382
388
|
options?.onSseEvent,
|
|
383
389
|
options?.fetch,
|
|
@@ -389,9 +395,17 @@ export const streamOpenAIResponses: StreamFunction<"openai-responses"> = (
|
|
|
389
395
|
);
|
|
390
396
|
const premiumRequestsTotal = copilotPremiumRequests;
|
|
391
397
|
const providerSessionState = getOpenAIResponsesProviderSessionState(model, options?.providerSessionState);
|
|
392
|
-
|
|
398
|
+
let { params } = buildParams(model, context, options, providerSessionState, cacheRetention, baseUrl);
|
|
393
399
|
const idleTimeoutMs = options?.streamIdleTimeoutMs ?? getOpenAIStreamIdleTimeoutMs(model.provider, model.id);
|
|
394
|
-
options?.onPayload?.(
|
|
400
|
+
const replacementPayload = await options?.onPayload?.(
|
|
401
|
+
params,
|
|
402
|
+
undefined,
|
|
403
|
+
options?.attemptScope,
|
|
404
|
+
options?.signal,
|
|
405
|
+
);
|
|
406
|
+
if (replacementPayload !== undefined) {
|
|
407
|
+
params = replacementPayload as typeof params;
|
|
408
|
+
}
|
|
395
409
|
rawRequestDump = {
|
|
396
410
|
provider: model.provider,
|
|
397
411
|
api: output.api,
|
|
@@ -556,6 +570,7 @@ function createClient(
|
|
|
556
570
|
extraHeaders?: Record<string, string>,
|
|
557
571
|
initiatorOverride?: MessageAttribution,
|
|
558
572
|
sessionId?: string,
|
|
573
|
+
providerSessionId?: string,
|
|
559
574
|
cacheRetention?: CacheRetention,
|
|
560
575
|
onSseEvent?: OpenAIResponsesOptions["onSseEvent"],
|
|
561
576
|
fetchOverride?: FetchImpl,
|
|
@@ -618,6 +633,8 @@ function createClient(
|
|
|
618
633
|
}
|
|
619
634
|
headers = applyOpenAIRequestTransformHeaders(headers, model.requestTransform, `Gajae-Code/${packageJson.version}`);
|
|
620
635
|
const { baseUrl: clientBaseUrl, query: endpointQuery } = splitBaseUrlQuery(baseUrl);
|
|
636
|
+
const openCodeGoSessionId = resolveOpenCodeGoSessionId(model, baseUrl, providerSessionId, "openai");
|
|
637
|
+
headers = applyOpenCodeGoSessionHeader(headers, openCodeGoSessionId);
|
|
621
638
|
const baseFetch = fetchOverride ?? fetch;
|
|
622
639
|
const queryFetch = Object.assign(
|
|
623
640
|
async (input: string | URL | Request, init?: RequestInit): Promise<Response> => {
|
|
@@ -626,8 +643,9 @@ function createClient(
|
|
|
626
643
|
baseFetch.preconnect ? { preconnect: baseFetch.preconnect } : {},
|
|
627
644
|
);
|
|
628
645
|
const boundedFetch = wrapOpenAIFetchForBoundedRateLimits(queryFetch, maxRetryDelayMs);
|
|
646
|
+
const providerScopedFetch = wrapFetchForOpenCodeGoSession(boundedFetch, openCodeGoSessionId);
|
|
629
647
|
const transformedFetch = wrapFetchForOpenAIRequestTransform(
|
|
630
|
-
|
|
648
|
+
providerScopedFetch,
|
|
631
649
|
model.requestTransform,
|
|
632
650
|
`Gajae-Code/${packageJson.version}`,
|
|
633
651
|
);
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { FetchImpl, Model } from "../types";
|
|
2
|
+
export type OpenCodeGoApiFamily = "openai" | "anthropic";
|
|
3
|
+
export declare function resolveOpenCodeGoSessionId(model: Pick<Model, "provider">, baseUrl: string | undefined, providerSessionId: string | undefined, apiFamily: OpenCodeGoApiFamily): string | undefined;
|
|
4
|
+
export declare function applyOpenCodeGoSessionHeader(headers: Record<string, string>, sessionId: string | undefined): Record<string, string>;
|
|
5
|
+
export declare function wrapFetchForOpenCodeGoSession(baseFetch: FetchImpl, sessionId: string | undefined): FetchImpl;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { FetchImpl, Model } from "../types";
|
|
2
|
+
|
|
3
|
+
const OPENCODE_GO_ORIGIN = "https://opencode.ai";
|
|
4
|
+
const OPENCODE_GO_OPENAI_BASE_PATH = "/zen/go/v1";
|
|
5
|
+
const OPENCODE_GO_ANTHROPIC_BASE_PATH = "/zen/go";
|
|
6
|
+
|
|
7
|
+
export type OpenCodeGoApiFamily = "openai" | "anthropic";
|
|
8
|
+
|
|
9
|
+
function expectedBasePath(apiFamily: OpenCodeGoApiFamily): string {
|
|
10
|
+
return apiFamily === "openai" ? OPENCODE_GO_OPENAI_BASE_PATH : OPENCODE_GO_ANTHROPIC_BASE_PATH;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function resolveOpenCodeGoSessionId(
|
|
14
|
+
model: Pick<Model, "provider">,
|
|
15
|
+
baseUrl: string | undefined,
|
|
16
|
+
providerSessionId: string | undefined,
|
|
17
|
+
apiFamily: OpenCodeGoApiFamily,
|
|
18
|
+
): string | undefined {
|
|
19
|
+
if (model.provider !== "opencode-go" || !baseUrl || !providerSessionId) return undefined;
|
|
20
|
+
try {
|
|
21
|
+
const url = new URL(baseUrl);
|
|
22
|
+
if (url.origin !== OPENCODE_GO_ORIGIN) return undefined;
|
|
23
|
+
if (url.username !== "" || url.password !== "" || url.search !== "" || url.hash !== "") return undefined;
|
|
24
|
+
if (url.pathname.replace(/\/+$/u, "") !== expectedBasePath(apiFamily)) return undefined;
|
|
25
|
+
return providerSessionId;
|
|
26
|
+
} catch {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function applyOpenCodeGoSessionHeader(
|
|
32
|
+
headers: Record<string, string>,
|
|
33
|
+
sessionId: string | undefined,
|
|
34
|
+
): Record<string, string> {
|
|
35
|
+
const normalizedHeaders = new Headers(headers);
|
|
36
|
+
normalizedHeaders.delete("x-opencode-session");
|
|
37
|
+
if (sessionId) normalizedHeaders.set("x-opencode-session", sessionId);
|
|
38
|
+
return Object.fromEntries(normalizedHeaders.entries());
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function wrapFetchForOpenCodeGoSession(baseFetch: FetchImpl, sessionId: string | undefined): FetchImpl {
|
|
42
|
+
return Object.assign(
|
|
43
|
+
async (input: string | URL | Request, init?: RequestInit): Promise<Response> => {
|
|
44
|
+
if (input instanceof Request) {
|
|
45
|
+
const request = new Request(input, init);
|
|
46
|
+
request.headers.delete("x-opencode-session");
|
|
47
|
+
if (sessionId) request.headers.set("x-opencode-session", sessionId);
|
|
48
|
+
return baseFetch(request);
|
|
49
|
+
}
|
|
50
|
+
const headers = new Headers(init?.headers);
|
|
51
|
+
headers.delete("x-opencode-session");
|
|
52
|
+
if (sessionId) headers.set("x-opencode-session", sessionId);
|
|
53
|
+
return baseFetch(input, { ...init, headers });
|
|
54
|
+
},
|
|
55
|
+
baseFetch.preconnect ? { preconnect: baseFetch.preconnect } : {},
|
|
56
|
+
);
|
|
57
|
+
}
|
package/src/stream.ts
CHANGED
|
@@ -955,6 +955,7 @@ function mapOptionsForApi<TApi extends Api>(
|
|
|
955
955
|
streamMaxRetries: options?.fallbackManaged ? 0 : options?.streamMaxRetries,
|
|
956
956
|
metadata: options?.metadata,
|
|
957
957
|
sessionId: options?.sessionId,
|
|
958
|
+
providerSessionId: options?.providerSessionId,
|
|
958
959
|
providerSessionState: options?.providerSessionState,
|
|
959
960
|
onPayload: options?.onPayload,
|
|
960
961
|
onResponse: options?.onResponse,
|
package/src/types.d.ts
CHANGED
|
@@ -239,6 +239,13 @@ export interface StreamOptions {
|
|
|
239
239
|
* session-aware features. Ignored by providers that don't support it.
|
|
240
240
|
*/
|
|
241
241
|
sessionId?: string;
|
|
242
|
+
/**
|
|
243
|
+
* Opaque conversation identity owned by the calling agent/session lifecycle.
|
|
244
|
+
* Unlike `sessionId`, this MUST NOT be synthesized from prompts, credentials,
|
|
245
|
+
* paths, cache keys, or other request content. Providers with a dedicated
|
|
246
|
+
* conversation header may use this only when their endpoint policy permits it.
|
|
247
|
+
*/
|
|
248
|
+
providerSessionId?: string;
|
|
242
249
|
/**
|
|
243
250
|
* Provider-scoped mutable state store for this agent session.
|
|
244
251
|
* Providers can use this to persist transport/session state between turns.
|
|
@@ -249,12 +256,12 @@ export interface StreamOptions {
|
|
|
249
256
|
* Return undefined to keep the payload unchanged.
|
|
250
257
|
* The `scope` parameter carries the per-attempt identity for execution attribution.
|
|
251
258
|
*/
|
|
252
|
-
onPayload?: (payload: unknown, model?: Model<Api>, scope?: AttemptScopeRef) => unknown | undefined | Promise<unknown | undefined>;
|
|
259
|
+
onPayload?: (payload: unknown, model?: Model<Api>, scope?: AttemptScopeRef, signal?: AbortSignal) => unknown | undefined | Promise<unknown | undefined>;
|
|
253
260
|
/**
|
|
254
261
|
* Optional callback for provider response metadata after headers are received.
|
|
255
262
|
* The `scope` parameter carries the per-attempt identity for execution attribution.
|
|
256
263
|
*/
|
|
257
|
-
onResponse?: (response: ProviderResponseMetadata, model?: Model<Api>, scope?: AttemptScopeRef) => void | Promise<void>;
|
|
264
|
+
onResponse?: (response: ProviderResponseMetadata, model?: Model<Api>, scope?: AttemptScopeRef, signal?: AbortSignal) => void | Promise<void>;
|
|
258
265
|
/**
|
|
259
266
|
* Internal dispatch-admission hook. Providers invoke this immediately before
|
|
260
267
|
* submitting an outbound request; stream forwarding retains a first-response
|
package/src/types.ts
CHANGED
|
@@ -443,6 +443,13 @@ export interface StreamOptions {
|
|
|
443
443
|
* session-aware features. Ignored by providers that don't support it.
|
|
444
444
|
*/
|
|
445
445
|
sessionId?: string;
|
|
446
|
+
/**
|
|
447
|
+
* Opaque conversation identity owned by the calling agent/session lifecycle.
|
|
448
|
+
* Unlike `sessionId`, this MUST NOT be synthesized from prompts, credentials,
|
|
449
|
+
* paths, cache keys, or other request content. Providers with a dedicated
|
|
450
|
+
* conversation header may use this only when their endpoint policy permits it.
|
|
451
|
+
*/
|
|
452
|
+
providerSessionId?: string;
|
|
446
453
|
/**
|
|
447
454
|
* Provider-scoped mutable state store for this agent session.
|
|
448
455
|
* Providers can use this to persist transport/session state between turns.
|
|
@@ -457,6 +464,7 @@ export interface StreamOptions {
|
|
|
457
464
|
payload: unknown,
|
|
458
465
|
model?: Model<Api>,
|
|
459
466
|
scope?: AttemptScopeRef,
|
|
467
|
+
signal?: AbortSignal,
|
|
460
468
|
) => unknown | undefined | Promise<unknown | undefined>;
|
|
461
469
|
/**
|
|
462
470
|
* Optional callback for provider response metadata after headers are received.
|
|
@@ -466,6 +474,7 @@ export interface StreamOptions {
|
|
|
466
474
|
response: ProviderResponseMetadata,
|
|
467
475
|
model?: Model<Api>,
|
|
468
476
|
scope?: AttemptScopeRef,
|
|
477
|
+
signal?: AbortSignal,
|
|
469
478
|
) => void | Promise<void>;
|
|
470
479
|
/**
|
|
471
480
|
* Internal dispatch-admission hook. Providers invoke this immediately before
|
|
@@ -6,6 +6,16 @@
|
|
|
6
6
|
* authority for the account tier; this module only names the model policy and
|
|
7
7
|
* keeps the provider's deterministic rejection wording in one place.
|
|
8
8
|
*/
|
|
9
|
+
export type OpenAICodexProEntitlement = "entitled" | "denied" | "unknown";
|
|
10
|
+
/**
|
|
11
|
+
* Classify a ChatGPT `plan_type` for strict Pro-tier Codex models.
|
|
12
|
+
*
|
|
13
|
+
* The usage endpoint remains authoritative: only exact, documented tier names
|
|
14
|
+
* are classified. Known Free/Plus tiers can be rejected locally, while missing
|
|
15
|
+
* or unfamiliar values stay unknown and reach the provider instead of being
|
|
16
|
+
* guessed from a substring.
|
|
17
|
+
*/
|
|
18
|
+
export declare function classifyOpenAICodexProEntitlement(planType: string | undefined): OpenAICodexProEntitlement;
|
|
9
19
|
export declare function requiresOpenAICodexProModel(provider: string, modelId: string | undefined): boolean;
|
|
10
20
|
export declare function requiresStrictOpenAICodexProModel(provider: string, modelId: string | undefined): boolean;
|
|
11
21
|
export declare function isOpenAICodexChatGPTEntitlementError(message: string | undefined, code?: string): boolean;
|
|
@@ -7,6 +7,27 @@
|
|
|
7
7
|
* keeps the provider's deterministic rejection wording in one place.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
const OPENAI_CODEX_PRO_ENTITLED_PLAN_TYPES = new Set(["pro", "business", "enterprise", "team"]);
|
|
11
|
+
const OPENAI_CODEX_PRO_DENIED_PLAN_TYPES = new Set(["free", "plus"]);
|
|
12
|
+
|
|
13
|
+
export type OpenAICodexProEntitlement = "entitled" | "denied" | "unknown";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Classify a ChatGPT `plan_type` for strict Pro-tier Codex models.
|
|
17
|
+
*
|
|
18
|
+
* The usage endpoint remains authoritative: only exact, documented tier names
|
|
19
|
+
* are classified. Known Free/Plus tiers can be rejected locally, while missing
|
|
20
|
+
* or unfamiliar values stay unknown and reach the provider instead of being
|
|
21
|
+
* guessed from a substring.
|
|
22
|
+
*/
|
|
23
|
+
export function classifyOpenAICodexProEntitlement(planType: string | undefined): OpenAICodexProEntitlement {
|
|
24
|
+
const normalized = planType?.trim().toLowerCase();
|
|
25
|
+
if (!normalized) return "unknown";
|
|
26
|
+
if (OPENAI_CODEX_PRO_ENTITLED_PLAN_TYPES.has(normalized)) return "entitled";
|
|
27
|
+
if (OPENAI_CODEX_PRO_DENIED_PLAN_TYPES.has(normalized)) return "denied";
|
|
28
|
+
return "unknown";
|
|
29
|
+
}
|
|
30
|
+
|
|
10
31
|
export function requiresOpenAICodexProModel(provider: string, modelId: string | undefined): boolean {
|
|
11
32
|
return (
|
|
12
33
|
provider === "openai-codex" &&
|
|
@@ -180,6 +180,7 @@ export async function fetchAntigravityDiscoveryModels(
|
|
|
180
180
|
options: FetchAntigravityDiscoveryModelsOptions,
|
|
181
181
|
): Promise<Model<"google-gemini-cli">[] | null> {
|
|
182
182
|
const fetcher = options.fetcher ?? fetch;
|
|
183
|
+
const signal = options.signal ?? AbortSignal.timeout(5_000);
|
|
183
184
|
const targetProvider = options.targetProvider ?? "google-antigravity";
|
|
184
185
|
const endpoints = options.endpoint
|
|
185
186
|
? [trimTrailingSlashes(options.endpoint)]
|
|
@@ -196,7 +197,7 @@ export async function fetchAntigravityDiscoveryModels(
|
|
|
196
197
|
"User-Agent": options.userAgent ?? getAntigravityUserAgent(),
|
|
197
198
|
},
|
|
198
199
|
body: JSON.stringify({}),
|
|
199
|
-
signal
|
|
200
|
+
signal,
|
|
200
201
|
});
|
|
201
202
|
} catch {
|
|
202
203
|
continue;
|
|
@@ -72,6 +72,7 @@ export async function fetchGeminiModels(
|
|
|
72
72
|
const baseUrl = normalizeBaseUrl(options.baseUrl);
|
|
73
73
|
const pageSize = normalizePositiveInt(options.pageSize, DEFAULT_PAGE_SIZE);
|
|
74
74
|
const maxPages = normalizePositiveInt(options.maxPages, DEFAULT_MAX_PAGES);
|
|
75
|
+
const signal = options.signal ?? AbortSignal.timeout(5_000);
|
|
75
76
|
|
|
76
77
|
const bundledById = new Map(
|
|
77
78
|
getBundledModels("google").map(model => [model.id, model as Model<"google-generative-ai">]),
|
|
@@ -86,7 +87,7 @@ export async function fetchGeminiModels(
|
|
|
86
87
|
try {
|
|
87
88
|
response = await fetchImpl(requestUrl, {
|
|
88
89
|
method: "GET",
|
|
89
|
-
signal
|
|
90
|
+
signal,
|
|
90
91
|
});
|
|
91
92
|
} catch {
|
|
92
93
|
return null;
|
|
@@ -3,4 +3,5 @@ export declare function normalizeProviderResponse(response: Response, requestId?
|
|
|
3
3
|
export declare function notifyProviderResponse(options: {
|
|
4
4
|
onResponse?: StreamOptions["onResponse"];
|
|
5
5
|
attemptScope?: AttemptScopeRef;
|
|
6
|
+
signal?: AbortSignal;
|
|
6
7
|
} | undefined, response: Response, model?: Model<Api>, requestId?: string | null, metadata?: Record<string, unknown>): Promise<void>;
|
|
@@ -19,12 +19,19 @@ export function normalizeProviderResponse(
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export async function notifyProviderResponse(
|
|
22
|
-
options:
|
|
22
|
+
options:
|
|
23
|
+
| { onResponse?: StreamOptions["onResponse"]; attemptScope?: AttemptScopeRef; signal?: AbortSignal }
|
|
24
|
+
| undefined,
|
|
23
25
|
response: Response,
|
|
24
26
|
model?: Model<Api>,
|
|
25
27
|
requestId?: string | null,
|
|
26
28
|
metadata?: Record<string, unknown>,
|
|
27
29
|
): Promise<void> {
|
|
28
30
|
if (!options?.onResponse) return;
|
|
29
|
-
await options.onResponse(
|
|
31
|
+
await options.onResponse(
|
|
32
|
+
normalizeProviderResponse(response, requestId, metadata),
|
|
33
|
+
model,
|
|
34
|
+
options.attemptScope,
|
|
35
|
+
options.signal,
|
|
36
|
+
);
|
|
30
37
|
}
|