@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
|
@@ -48,10 +48,57 @@ function parseHostname(baseUrl: string): string | undefined {
|
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
function isProductionXaiApiUrl(baseUrl: string): boolean {
|
|
52
|
+
if (
|
|
53
|
+
/[\u0000-\u0020\u007f]/u.test(baseUrl) ||
|
|
54
|
+
baseUrl.includes("?") ||
|
|
55
|
+
baseUrl.includes("#") ||
|
|
56
|
+
baseUrl.includes("\\")
|
|
57
|
+
) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
const authorityTail = baseUrl.slice("https://".length);
|
|
61
|
+
const authorityEnd = authorityTail.search(/[/?#]/u);
|
|
62
|
+
const authority = authorityTail.slice(0, authorityEnd === -1 ? undefined : authorityEnd);
|
|
63
|
+
if (!/^api\.x\.ai(?::443)?$/iu.test(authority)) return false;
|
|
64
|
+
try {
|
|
65
|
+
const url = new URL(baseUrl);
|
|
66
|
+
return (
|
|
67
|
+
url.protocol === "https:" &&
|
|
68
|
+
url.hostname.toLowerCase() === "api.x.ai" &&
|
|
69
|
+
url.port === "" &&
|
|
70
|
+
url.username === "" &&
|
|
71
|
+
url.password === "" &&
|
|
72
|
+
url.search === "" &&
|
|
73
|
+
url.hash === ""
|
|
74
|
+
);
|
|
75
|
+
} catch {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
51
80
|
function hostnameMatches(hostname: string | undefined, suffix: string): boolean {
|
|
52
81
|
return hostname !== undefined && (hostname === suffix || hostname.endsWith(`.${suffix}`));
|
|
53
82
|
}
|
|
54
83
|
|
|
84
|
+
export type GrokGeneration = {
|
|
85
|
+
major: number;
|
|
86
|
+
minor: number;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
function parseGrokGeneration(modelId: string): GrokGeneration | undefined {
|
|
90
|
+
const canonicalId = modelId.startsWith("x-ai/") ? modelId.slice("x-ai/".length) : modelId;
|
|
91
|
+
const match =
|
|
92
|
+
/^grok-([1-9]\d?)(?:\.(0|[1-9]\d?))?(?:-(latest|preview|\d{8}|reasoning|\d{4}-reasoning|beta-latest-reasoning|multi-agent-beta-latest))?$/.exec(
|
|
93
|
+
canonicalId,
|
|
94
|
+
);
|
|
95
|
+
if (!match) return undefined;
|
|
96
|
+
return {
|
|
97
|
+
major: Number(match[1]),
|
|
98
|
+
minor: match[2] === undefined ? 0 : Number(match[2]),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
55
102
|
/** Returns whether the request endpoint is an audited reasoning-control transport. */
|
|
56
103
|
export function isAuditedOpenAIReasoningTransport(
|
|
57
104
|
model: { provider: string; baseUrl?: string },
|
|
@@ -63,6 +110,32 @@ export function isAuditedOpenAIReasoningTransport(
|
|
|
63
110
|
return AUDITED_REASONING_EFFORT_HOST_SUFFIXES.some(suffix => hostnameMatches(hostname, suffix));
|
|
64
111
|
}
|
|
65
112
|
|
|
113
|
+
/**
|
|
114
|
+
* xAI's first-party API accepts `reasoning_effort` on Grok 4.5 and later.
|
|
115
|
+
* Provider labels are user-configurable, so both the provider and the official
|
|
116
|
+
* API origin must match. Unknown variants fail closed instead of inheriting a
|
|
117
|
+
* capability from a loose model-id prefix.
|
|
118
|
+
*/
|
|
119
|
+
export function parseDirectXaiReasoningEffortGeneration(
|
|
120
|
+
model: { provider: string; id: string; api?: string; baseUrl?: string },
|
|
121
|
+
resolvedBaseUrl?: string,
|
|
122
|
+
): GrokGeneration | undefined {
|
|
123
|
+
if (model.provider !== "xai") return undefined;
|
|
124
|
+
if (model.api !== "openai-completions") return undefined;
|
|
125
|
+
if (!isProductionXaiApiUrl(resolvedBaseUrl ?? model.baseUrl ?? "")) return undefined;
|
|
126
|
+
if (model.id.includes("/")) return undefined;
|
|
127
|
+
const generation = parseGrokGeneration(model.id);
|
|
128
|
+
if (!generation) return undefined;
|
|
129
|
+
return generation.major > 4 || (generation.major === 4 && generation.minor >= 5) ? generation : undefined;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function isDirectXaiReasoningEffortModel(
|
|
133
|
+
model: { provider: string; id: string; api?: string; baseUrl?: string },
|
|
134
|
+
resolvedBaseUrl?: string,
|
|
135
|
+
): boolean {
|
|
136
|
+
return parseDirectXaiReasoningEffortGeneration(model, resolvedBaseUrl) !== undefined;
|
|
137
|
+
}
|
|
138
|
+
|
|
66
139
|
export type ResolvedOpenAICompat = Required<
|
|
67
140
|
Omit<
|
|
68
141
|
OpenAICompat,
|
|
@@ -184,8 +257,8 @@ export function detectOpenAICompat(model: Model<"openai-completions">, resolvedB
|
|
|
184
257
|
baseUrl.includes("chutes.ai") ||
|
|
185
258
|
baseUrl.includes("fireworks.ai") ||
|
|
186
259
|
isDirectDeepseekApi;
|
|
187
|
-
const isGrok = provider === "xai" || baseUrl.includes("api.x.ai");
|
|
188
|
-
|
|
260
|
+
const isGrok = provider === "xai" || baseUrl.includes("api.x.ai") || parseGrokGeneration(model.id) !== undefined;
|
|
261
|
+
|
|
189
262
|
const isMistral = provider === "mistral" || baseUrl.includes("mistral.ai");
|
|
190
263
|
|
|
191
264
|
// Hosts whose chat-completions endpoints are known to accept multiple
|
|
@@ -278,7 +351,8 @@ export function detectOpenAICompat(model: Model<"openai-completions">, resolvedB
|
|
|
278
351
|
supportsResponsesSessionAffinity: false,
|
|
279
352
|
supportsMultipleSystemMessages: supportsMultipleSystemMessagesDefault,
|
|
280
353
|
supportsReasoningEffort:
|
|
281
|
-
hasAuditedReasoningEffortTransport &&
|
|
354
|
+
hasAuditedReasoningEffortTransport &&
|
|
355
|
+
((!isGrok && !isZai) || isOpenRouter || isDirectXaiReasoningEffortModel(model, resolvedBaseUrl)),
|
|
282
356
|
reasoningEffortMap,
|
|
283
357
|
supportsUsageInStreaming: !isCerebras,
|
|
284
358
|
disableReasoningOnForcedToolChoice: isKimiModel || isAnthropicModel || isOpenCodeGoReasoning,
|
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
* No `@aws-sdk/*`, no `@smithy/*`, no `proxy-agent`. Proxies are honored via
|
|
7
7
|
* Bun's native `HTTPS_PROXY` support.
|
|
8
8
|
*/
|
|
9
|
+
import { parseBedrockClaudeGeneration } from "../bedrock-claude-cache-policy";
|
|
9
10
|
import type { Effort } from "../model-thinking";
|
|
10
|
-
import type { StreamFunction, StreamOptions, ThinkingBudgets, Tool, ToolChoice } from "../types";
|
|
11
|
+
import type { Model, StreamFunction, StreamOptions, ThinkingBudgets, Tool, ToolChoice } from "../types";
|
|
11
12
|
export type BedrockThinkingDisplay = "summarized" | "omitted";
|
|
12
13
|
export interface BedrockOptions extends StreamOptions {
|
|
13
14
|
region?: string;
|
|
@@ -51,10 +52,24 @@ interface WireToolConfig {
|
|
|
51
52
|
toolChoice?: WireToolChoice;
|
|
52
53
|
}
|
|
53
54
|
export declare const streamBedrock: StreamFunction<"bedrock-converse-stream">;
|
|
55
|
+
/**
|
|
56
|
+
* Check if the model supports prompt caching.
|
|
57
|
+
* Supported: Claude 3.5 Haiku, Claude 3.7 Sonnet, and every later Claude
|
|
58
|
+
* generation:
|
|
59
|
+
* https://docs.aws.amazon.com/bedrock/latest/userguide/prompt-caching.html
|
|
60
|
+
*
|
|
61
|
+
* For base models and system-defined inference profiles the model ID / ARN
|
|
62
|
+
* contains the model name, so we can decide locally.
|
|
63
|
+
*
|
|
64
|
+
* For application inference profiles (whose ARNs don't contain the model name),
|
|
65
|
+
* set AWS_BEDROCK_FORCE_CACHE=1 to enable cache points. Amazon Nova models
|
|
66
|
+
* have automatic caching and don't need explicit cache points.
|
|
67
|
+
*/
|
|
68
|
+
export declare function supportsPromptCaching(model: Model<"bedrock-converse-stream">): boolean;
|
|
69
|
+
export { parseBedrockClaudeGeneration };
|
|
54
70
|
export declare function stripBedrockForcedToolChoiceForRetry<T extends {
|
|
55
71
|
toolConfig?: {
|
|
56
72
|
toolChoice?: unknown;
|
|
57
73
|
};
|
|
58
74
|
}>(body: T): T;
|
|
59
75
|
export declare function convertToolConfig(tools: Tool[] | undefined, toolChoice: BedrockOptions["toolChoice"]): WireToolConfig | undefined;
|
|
60
|
-
export {};
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import { $credentialEnv, $env, $flag, extractHttpStatusFromError, fetchWithRetry } from "@gajae-code/utils";
|
|
11
11
|
import { assertAwsRegionLabel } from "../adapter-internals/aws-region";
|
|
12
|
+
import { parseBedrockClaudeGeneration, supportsBedrockClaudePromptCaching } from "../bedrock-claude-cache-policy";
|
|
12
13
|
import type { Effort } from "../model-thinking";
|
|
13
14
|
import {
|
|
14
15
|
mapEffortToAnthropicAdaptiveEffort,
|
|
@@ -219,7 +220,7 @@ export const streamBedrock: StreamFunction<"bedrock-converse-stream"> = (
|
|
|
219
220
|
if (tc.any || tc.tool) additionalModelRequestFields = undefined;
|
|
220
221
|
}
|
|
221
222
|
|
|
222
|
-
|
|
223
|
+
let commandInput: ConverseStreamRequest = {
|
|
223
224
|
messages: convertMessages(context, model, cacheRetention),
|
|
224
225
|
system: buildSystemPrompt(context.systemPrompt, model, cacheRetention),
|
|
225
226
|
inferenceConfig: {
|
|
@@ -230,7 +231,15 @@ export const streamBedrock: StreamFunction<"bedrock-converse-stream"> = (
|
|
|
230
231
|
toolConfig,
|
|
231
232
|
additionalModelRequestFields,
|
|
232
233
|
};
|
|
233
|
-
options?.onPayload?.(
|
|
234
|
+
const replacementPayload = await options?.onPayload?.(
|
|
235
|
+
commandInput,
|
|
236
|
+
model,
|
|
237
|
+
options?.attemptScope,
|
|
238
|
+
options?.signal,
|
|
239
|
+
);
|
|
240
|
+
if (replacementPayload !== undefined) {
|
|
241
|
+
commandInput = replacementPayload as typeof commandInput;
|
|
242
|
+
}
|
|
234
243
|
|
|
235
244
|
const host = `bedrock-runtime.${region}.amazonaws.com`;
|
|
236
245
|
const url = `https://${host}/model/${encodeURIComponent(model.id)}/converse-stream`;
|
|
@@ -610,7 +619,9 @@ function handleContentBlockStop(
|
|
|
610
619
|
|
|
611
620
|
/**
|
|
612
621
|
* Check if the model supports prompt caching.
|
|
613
|
-
* Supported:
|
|
622
|
+
* Supported: Claude 3.5 Haiku, Claude 3.7 Sonnet, and every later Claude
|
|
623
|
+
* generation:
|
|
624
|
+
* https://docs.aws.amazon.com/bedrock/latest/userguide/prompt-caching.html
|
|
614
625
|
*
|
|
615
626
|
* For base models and system-defined inference profiles the model ID / ARN
|
|
616
627
|
* contains the model name, so we can decide locally.
|
|
@@ -619,21 +630,18 @@ function handleContentBlockStop(
|
|
|
619
630
|
* set AWS_BEDROCK_FORCE_CACHE=1 to enable cache points. Amazon Nova models
|
|
620
631
|
* have automatic caching and don't need explicit cache points.
|
|
621
632
|
*/
|
|
622
|
-
function supportsPromptCaching(model: Model<"bedrock-converse-stream">): boolean {
|
|
633
|
+
export function supportsPromptCaching(model: Model<"bedrock-converse-stream">): boolean {
|
|
634
|
+
const claudeSupport = supportsBedrockClaudePromptCaching(model.id);
|
|
635
|
+
if (claudeSupport !== undefined) return claudeSupport;
|
|
623
636
|
if (model.cost.cacheRead || model.cost.cacheWrite) return true;
|
|
624
|
-
const id = model.id.toLowerCase();
|
|
625
|
-
// Anthropic model 4.x models (opus-4, sonnet-4, haiku-4)
|
|
626
|
-
if (id.includes("claude") && (id.includes("-4-") || id.includes("-4."))) return true;
|
|
627
|
-
// Anthropic model 3.5 Haiku, Anthropic model 3.7 Sonnet (legacy naming)
|
|
628
|
-
if (id.includes("claude-3-7-sonnet") || id.includes("claude-3-5-haiku")) return true;
|
|
629
|
-
// Anthropic model Haiku 4.5+ (new naming)
|
|
630
|
-
if (id.includes("claude-haiku")) return true;
|
|
631
637
|
// Application inference profiles don't contain the model name in the ARN.
|
|
632
638
|
// Allow users to force cache points via environment variable.
|
|
633
639
|
if (typeof process !== "undefined" && $flag("AWS_BEDROCK_FORCE_CACHE")) return true;
|
|
634
640
|
return false;
|
|
635
641
|
}
|
|
636
642
|
|
|
643
|
+
export { parseBedrockClaudeGeneration };
|
|
644
|
+
|
|
637
645
|
/**
|
|
638
646
|
* Check if the model supports thinking signatures in reasoningContent.
|
|
639
647
|
* Only Anthropic Anthropic model models support the signature field.
|
|
@@ -203,6 +203,7 @@ export type AnthropicClientOptionsArgs = {
|
|
|
203
203
|
maxRetryDelayMs?: number;
|
|
204
204
|
streamFirstEventTimeoutMs?: number;
|
|
205
205
|
streamIdleTimeoutMs?: number;
|
|
206
|
+
providerSessionId?: string;
|
|
206
207
|
};
|
|
207
208
|
export type AnthropicClientOptionsResult = {
|
|
208
209
|
isOAuthToken: boolean;
|
|
@@ -111,6 +111,11 @@ import {
|
|
|
111
111
|
hasCopilotVisionInput,
|
|
112
112
|
resolveGitHubCopilotBaseUrl,
|
|
113
113
|
} from "./github-copilot-headers";
|
|
114
|
+
import {
|
|
115
|
+
applyOpenCodeGoSessionHeader,
|
|
116
|
+
resolveOpenCodeGoSessionId,
|
|
117
|
+
wrapFetchForOpenCodeGoSession,
|
|
118
|
+
} from "./opencode-go-session";
|
|
114
119
|
import { hasAdjacentPrivateThinkingBlocks, transformMessages } from "./transform-messages";
|
|
115
120
|
import { NON_VISION_IMAGE_PLACEHOLDER } from "./vision-guard";
|
|
116
121
|
|
|
@@ -1126,6 +1131,7 @@ export type AnthropicClientOptionsArgs = {
|
|
|
1126
1131
|
maxRetryDelayMs?: number;
|
|
1127
1132
|
streamFirstEventTimeoutMs?: number;
|
|
1128
1133
|
streamIdleTimeoutMs?: number;
|
|
1134
|
+
providerSessionId?: string;
|
|
1129
1135
|
};
|
|
1130
1136
|
|
|
1131
1137
|
export type AnthropicClientOptionsResult = {
|
|
@@ -1940,6 +1946,7 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = (
|
|
|
1940
1946
|
maxRetryDelayMs: options?.maxRetryDelayMs,
|
|
1941
1947
|
streamFirstEventTimeoutMs: options?.streamFirstEventTimeoutMs,
|
|
1942
1948
|
streamIdleTimeoutMs: options?.streamIdleTimeoutMs,
|
|
1949
|
+
providerSessionId: options?.providerSessionId,
|
|
1943
1950
|
});
|
|
1944
1951
|
client = created.client;
|
|
1945
1952
|
isOAuthToken = created.isOAuthToken;
|
|
@@ -1992,7 +1999,12 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = (
|
|
|
1992
1999
|
if (dropFastMode) {
|
|
1993
2000
|
dropAnthropicFastMode(nextParams);
|
|
1994
2001
|
}
|
|
1995
|
-
const replacementPayload = await options?.onPayload?.(
|
|
2002
|
+
const replacementPayload = await options?.onPayload?.(
|
|
2003
|
+
nextParams,
|
|
2004
|
+
model,
|
|
2005
|
+
options?.attemptScope,
|
|
2006
|
+
options?.signal,
|
|
2007
|
+
);
|
|
1996
2008
|
if (replacementPayload !== undefined) {
|
|
1997
2009
|
nextParams = replacementPayload as typeof nextParams;
|
|
1998
2010
|
}
|
|
@@ -3107,7 +3119,11 @@ export function buildAnthropicClientOptions(args: AnthropicClientOptionsArgs): A
|
|
|
3107
3119
|
const tlsFetchOptions = buildClaudeCodeTlsFetchOptions(model, baseUrl);
|
|
3108
3120
|
const baseFetch = args.fetch ?? fetch;
|
|
3109
3121
|
const boundedFetch = wrapAnthropicFetchForBoundedRateLimits(baseFetch, args.maxRetryDelayMs);
|
|
3110
|
-
const
|
|
3122
|
+
const openCodeGoSessionId = resolveOpenCodeGoSessionId(model, baseUrl, args.providerSessionId, "anthropic");
|
|
3123
|
+
const providerScopedFetch = wrapFetchForOpenCodeGoSession(boundedFetch, openCodeGoSessionId);
|
|
3124
|
+
const debugFetch = onSseEvent
|
|
3125
|
+
? wrapFetchForSseDebug(providerScopedFetch, event => onSseEvent(event, model))
|
|
3126
|
+
: providerScopedFetch;
|
|
3111
3127
|
// Bound the connect/headers phase. The first-event watchdog arms only after
|
|
3112
3128
|
// response headers arrive, so a request whose connection dies before headers
|
|
3113
3129
|
// was previously governed only by the Anthropic SDK's 10-minute default per
|
|
@@ -3124,16 +3140,19 @@ export function buildAnthropicClientOptions(args: AnthropicClientOptionsArgs): A
|
|
|
3124
3140
|
if (needsFineGrainedToolStreamingBeta) {
|
|
3125
3141
|
betaFeatures.push(fineGrainedToolStreamingBeta);
|
|
3126
3142
|
}
|
|
3127
|
-
const defaultHeaders =
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
|
|
3136
|
-
|
|
3143
|
+
const defaultHeaders = applyOpenCodeGoSessionHeader(
|
|
3144
|
+
mergeHeaders(
|
|
3145
|
+
{
|
|
3146
|
+
Accept: stream ? "text/event-stream" : "application/json",
|
|
3147
|
+
"Anthropic-Dangerous-Direct-Browser-Access": "true",
|
|
3148
|
+
Authorization: `Bearer ${copilotApiKey}`,
|
|
3149
|
+
...(betaFeatures.length > 0 ? { "anthropic-beta": buildBetaHeader([], betaFeatures) } : {}),
|
|
3150
|
+
},
|
|
3151
|
+
model.headers,
|
|
3152
|
+
dynamicHeaders,
|
|
3153
|
+
headers,
|
|
3154
|
+
),
|
|
3155
|
+
openCodeGoSessionId,
|
|
3137
3156
|
);
|
|
3138
3157
|
|
|
3139
3158
|
return {
|
|
@@ -3159,16 +3178,19 @@ export function buildAnthropicClientOptions(args: AnthropicClientOptionsArgs): A
|
|
|
3159
3178
|
betaFeatures.push(interleavedThinkingBeta);
|
|
3160
3179
|
}
|
|
3161
3180
|
|
|
3162
|
-
const defaultHeaders =
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3181
|
+
const defaultHeaders = applyOpenCodeGoSessionHeader(
|
|
3182
|
+
buildAnthropicHeaders({
|
|
3183
|
+
apiKey,
|
|
3184
|
+
baseUrl,
|
|
3185
|
+
isOAuth: oauthToken,
|
|
3186
|
+
extraBetas: betaFeatures,
|
|
3187
|
+
stream,
|
|
3188
|
+
modelHeaders: mergeHeaders(model.headers, foundryCustomHeaders, headers, dynamicHeaders),
|
|
3189
|
+
isCloudflareAiGateway: model.provider === "cloudflare-ai-gateway",
|
|
3190
|
+
zcodeSourceHeaders: model.provider === "glm-zcode",
|
|
3191
|
+
}),
|
|
3192
|
+
openCodeGoSessionId,
|
|
3193
|
+
);
|
|
3172
3194
|
|
|
3173
3195
|
if (model.provider === "cloudflare-ai-gateway") {
|
|
3174
3196
|
return {
|
|
@@ -129,9 +129,12 @@ export const streamAzureOpenAIResponses: StreamFunction<"azure-openai-responses"
|
|
|
129
129
|
const apiKey = options?.apiKey || getEnvApiKey(model.provider) || "";
|
|
130
130
|
const client = createClient(model, apiKey, options);
|
|
131
131
|
const { baseUrl } = resolveAzureConfig(model, options);
|
|
132
|
-
|
|
132
|
+
let params = buildParams(model, context, options, deploymentName, baseUrl);
|
|
133
133
|
const idleTimeoutMs = options?.streamIdleTimeoutMs ?? getOpenAIStreamIdleTimeoutMs();
|
|
134
|
-
options?.onPayload?.(params, model, options?.attemptScope);
|
|
134
|
+
const replacementPayload = await options?.onPayload?.(params, model, options?.attemptScope, options?.signal);
|
|
135
|
+
if (replacementPayload !== undefined) {
|
|
136
|
+
params = replacementPayload as typeof params;
|
|
137
|
+
}
|
|
135
138
|
rawRequestDump = {
|
|
136
139
|
provider: model.provider,
|
|
137
140
|
api: output.api,
|
package/src/providers/cursor.ts
CHANGED
|
@@ -689,7 +689,7 @@ export const streamCursor: StreamFunction<"cursor-agent"> = (
|
|
|
689
689
|
conversationUsageContextCache.set(conversationId, usageContext);
|
|
690
690
|
const reusableCachedState =
|
|
691
691
|
cachedState && canReuseCursorUsageContext(previousUsageContext, usageContext) ? cachedState : undefined;
|
|
692
|
-
const { requestBytes, conversationState } = buildGrpcRequest(model, context, options, {
|
|
692
|
+
const { requestBytes, conversationState } = await buildGrpcRequest(model, context, options, {
|
|
693
693
|
conversationId,
|
|
694
694
|
blobStore,
|
|
695
695
|
conversationState: reusableCachedState,
|
|
@@ -3459,7 +3459,7 @@ function extractImages(content: (TextContent | ImageContent)[]) {
|
|
|
3459
3459
|
);
|
|
3460
3460
|
}
|
|
3461
3461
|
|
|
3462
|
-
function buildGrpcRequest(
|
|
3462
|
+
async function buildGrpcRequest(
|
|
3463
3463
|
model: Model<"cursor-agent">,
|
|
3464
3464
|
context: Context,
|
|
3465
3465
|
options: CursorOptions | undefined,
|
|
@@ -3468,11 +3468,11 @@ function buildGrpcRequest(
|
|
|
3468
3468
|
blobStore: Map<string, Uint8Array>;
|
|
3469
3469
|
conversationState?: ConversationStateStructure;
|
|
3470
3470
|
},
|
|
3471
|
-
): {
|
|
3471
|
+
): Promise<{
|
|
3472
3472
|
requestBytes: Uint8Array;
|
|
3473
3473
|
blobStore: Map<string, Uint8Array>;
|
|
3474
3474
|
conversationState: ConversationStateStructure;
|
|
3475
|
-
} {
|
|
3475
|
+
}> {
|
|
3476
3476
|
const blobStore = state.blobStore;
|
|
3477
3477
|
|
|
3478
3478
|
const systemPromptIds = buildCursorSystemPromptJsons(context.systemPrompt, model.id).map(json =>
|
|
@@ -3558,7 +3558,7 @@ function buildGrpcRequest(
|
|
|
3558
3558
|
displayName: model.name,
|
|
3559
3559
|
});
|
|
3560
3560
|
|
|
3561
|
-
|
|
3561
|
+
let runRequest = create(AgentRunRequestSchema, {
|
|
3562
3562
|
conversationState,
|
|
3563
3563
|
action,
|
|
3564
3564
|
modelDetails,
|
|
@@ -3573,7 +3573,13 @@ function buildGrpcRequest(
|
|
|
3573
3573
|
: {}),
|
|
3574
3574
|
});
|
|
3575
3575
|
|
|
3576
|
-
options?.onPayload
|
|
3576
|
+
if (options?.onPayload) {
|
|
3577
|
+
const payload = toJson(AgentRunRequestSchema, runRequest);
|
|
3578
|
+
const replacement = await options.onPayload(payload, model, options.attemptScope, options.signal);
|
|
3579
|
+
if (replacement !== undefined) {
|
|
3580
|
+
runRequest = fromJson(AgentRunRequestSchema, replacement as JsonValue);
|
|
3581
|
+
}
|
|
3582
|
+
}
|
|
3577
3583
|
|
|
3578
3584
|
// Tools are sent later via requestContext (exec handshake)
|
|
3579
3585
|
|
|
@@ -354,7 +354,12 @@ export const streamGoogleGeminiCli: StreamFunction<"google-gemini-cli"> = (
|
|
|
354
354
|
const endpoints = baseUrl ? [baseUrl] : isAntigravity ? ANTIGRAVITY_ENDPOINT_FALLBACKS : [DEFAULT_ENDPOINT];
|
|
355
355
|
|
|
356
356
|
let requestBody = buildRequest(model, context, projectId, options, isAntigravity);
|
|
357
|
-
const replacementPayload = await options?.onPayload?.(
|
|
357
|
+
const replacementPayload = await options?.onPayload?.(
|
|
358
|
+
requestBody,
|
|
359
|
+
model,
|
|
360
|
+
options?.attemptScope,
|
|
361
|
+
options?.signal,
|
|
362
|
+
);
|
|
358
363
|
if (replacementPayload !== undefined) {
|
|
359
364
|
requestBody = replacementPayload as typeof requestBody;
|
|
360
365
|
}
|
|
@@ -899,7 +899,7 @@ export function streamGoogleGenAI<T extends "google-generative-ai" | "google-ver
|
|
|
899
899
|
try {
|
|
900
900
|
const plan = await prepare();
|
|
901
901
|
let params = plan.params;
|
|
902
|
-
const replacement = await options?.onPayload?.(params, model, options?.attemptScope);
|
|
902
|
+
const replacement = await options?.onPayload?.(params, model, options?.attemptScope, options?.signal);
|
|
903
903
|
if (replacement !== undefined) {
|
|
904
904
|
params = replacement as GenerateContentParameters;
|
|
905
905
|
}
|
|
@@ -611,8 +611,11 @@ export const streamKiroApiKey: StreamFunction<"kiro-codewhisperer-stream"> = (
|
|
|
611
611
|
const configuredBaseUrl = model.baseUrl;
|
|
612
612
|
const usesExplicitBaseUrl = Boolean(configuredBaseUrl) && !isRegionDerivedKiroApiBaseUrl(configuredBaseUrl);
|
|
613
613
|
const endpoint = configuredBaseUrl || kiroApiBaseUrl(kiroApiRegion(options));
|
|
614
|
-
|
|
615
|
-
options?.onPayload?.(request, model, options?.attemptScope);
|
|
614
|
+
let request = buildApiKeyRequest(model, context, options);
|
|
615
|
+
const replacementPayload = await options?.onPayload?.(request, model, options?.attemptScope, options?.signal);
|
|
616
|
+
if (replacementPayload !== undefined) {
|
|
617
|
+
request = replacementPayload;
|
|
618
|
+
}
|
|
616
619
|
|
|
617
620
|
const response = await fetch(endpoint, {
|
|
618
621
|
method: "POST",
|
|
@@ -193,11 +193,19 @@ export const streamKiroCodeWhisperer: StreamFunction<"kiro-codewhisperer-stream"
|
|
|
193
193
|
|
|
194
194
|
// Build request
|
|
195
195
|
const conversationState = buildConversationState(context, model, options);
|
|
196
|
-
|
|
196
|
+
let requestBody: GenerateAssistantResponseRequest = {
|
|
197
197
|
conversationState,
|
|
198
198
|
};
|
|
199
199
|
|
|
200
|
-
options?.onPayload?.(
|
|
200
|
+
const replacementPayload = await options?.onPayload?.(
|
|
201
|
+
requestBody,
|
|
202
|
+
model,
|
|
203
|
+
options?.attemptScope,
|
|
204
|
+
options?.signal,
|
|
205
|
+
);
|
|
206
|
+
if (replacementPayload !== undefined) {
|
|
207
|
+
requestBody = replacementPayload as typeof requestBody;
|
|
208
|
+
}
|
|
201
209
|
|
|
202
210
|
const host = `${STREAMING_SERVICE_NAME}.${region}.amazonaws.com`;
|
|
203
211
|
const url = `https://${host}/`;
|
package/src/providers/mock.ts
CHANGED
package/src/providers/ollama.ts
CHANGED
|
@@ -407,7 +407,7 @@ export const streamOllama: StreamFunction<"ollama-chat"> = (
|
|
|
407
407
|
const baseUrl = normalizeBaseUrl(model.baseUrl);
|
|
408
408
|
let body = createChatBody(model, context, options);
|
|
409
409
|
const sentForcedToolChoice = body.tool_choice === "required";
|
|
410
|
-
const replacementPayload = await options.onPayload?.(body, model, options?.attemptScope);
|
|
410
|
+
const replacementPayload = await options.onPayload?.(body, model, options?.attemptScope, options?.signal);
|
|
411
411
|
if (replacementPayload !== undefined) {
|
|
412
412
|
body = replacementPayload as typeof body;
|
|
413
413
|
}
|
|
@@ -25,7 +25,7 @@ const REQUEST_BLOCKED_MESSAGE_RE = /^\s*request blocked\b/i;
|
|
|
25
25
|
|
|
26
26
|
export async function parseCodexError(response: Response): Promise<CodexErrorInfo> {
|
|
27
27
|
const raw = await response.text();
|
|
28
|
-
let message = raw || response.statusText ||
|
|
28
|
+
let message = raw || response.statusText || `Codex request failed (HTTP ${response.status})`;
|
|
29
29
|
let friendlyMessage: string | undefined;
|
|
30
30
|
let rateLimits: CodexRateLimits | undefined;
|
|
31
31
|
let code: string | undefined;
|
|
@@ -717,8 +717,16 @@ async function buildCodexRequestContext(
|
|
|
717
717
|
const baseUrl = model.baseUrl || CODEX_BASE_URL;
|
|
718
718
|
const url = resolveCodexResponsesUrl(baseUrl);
|
|
719
719
|
const promptCacheKey = normalizeOpenAIResponsesPromptCacheKey(options?.sessionId);
|
|
720
|
-
|
|
721
|
-
options?.onPayload?.(
|
|
720
|
+
let transformedBody = await buildTransformedCodexRequestBody(model, context, options);
|
|
721
|
+
const replacementPayload = await options?.onPayload?.(
|
|
722
|
+
transformedBody,
|
|
723
|
+
model,
|
|
724
|
+
options?.attemptScope,
|
|
725
|
+
options?.signal,
|
|
726
|
+
);
|
|
727
|
+
if (replacementPayload !== undefined) {
|
|
728
|
+
transformedBody = replacementPayload as typeof transformedBody;
|
|
729
|
+
}
|
|
722
730
|
|
|
723
731
|
const requestHeaders = { ...(model.headers ?? {}), ...(options?.headers ?? {}) };
|
|
724
732
|
const rawRequestDump: RawHttpRequestDump = {
|
|
@@ -3,4 +3,4 @@
|
|
|
3
3
|
* the core-safe module so model metadata can use it without loading provider
|
|
4
4
|
* implementations during startup.
|
|
5
5
|
*/
|
|
6
|
-
export { detectOpenAICompat, type ResolvedOpenAICompat, resolveOpenAICompat } from "../openai-completions-compat";
|
|
6
|
+
export { detectOpenAICompat, type GrokGeneration, isDirectXaiReasoningEffortModel, parseDirectXaiReasoningEffortGeneration, type ResolvedOpenAICompat, resolveOpenAICompat, } from "../openai-completions-compat";
|
|
@@ -3,4 +3,11 @@
|
|
|
3
3
|
* the core-safe module so model metadata can use it without loading provider
|
|
4
4
|
* implementations during startup.
|
|
5
5
|
*/
|
|
6
|
-
export {
|
|
6
|
+
export {
|
|
7
|
+
detectOpenAICompat,
|
|
8
|
+
type GrokGeneration,
|
|
9
|
+
isDirectXaiReasoningEffortModel,
|
|
10
|
+
parseDirectXaiReasoningEffortGeneration,
|
|
11
|
+
type ResolvedOpenAICompat,
|
|
12
|
+
resolveOpenAICompat,
|
|
13
|
+
} from "../openai-completions-compat";
|
|
@@ -100,6 +100,11 @@ import {
|
|
|
100
100
|
wrapFetchForOpenAIRequestTransform,
|
|
101
101
|
} from "./openai-request-transform";
|
|
102
102
|
import { createInitialResponsesAssistantMessage } from "./openai-responses-shared";
|
|
103
|
+
import {
|
|
104
|
+
applyOpenCodeGoSessionHeader,
|
|
105
|
+
resolveOpenCodeGoSessionId,
|
|
106
|
+
wrapFetchForOpenCodeGoSession,
|
|
107
|
+
} from "./opencode-go-session";
|
|
103
108
|
import { transformMessages } from "./transform-messages";
|
|
104
109
|
import { joinTextWithImagePlaceholder, NON_VISION_IMAGE_PLACEHOLDER } from "./vision-guard";
|
|
105
110
|
|
|
@@ -602,6 +607,7 @@ export const streamOpenAICompletions: StreamFunction<"openai-completions"> = (
|
|
|
602
607
|
options?.authCredentialType,
|
|
603
608
|
options?.requestMaxRetries,
|
|
604
609
|
options?.sessionId,
|
|
610
|
+
options?.providerSessionId,
|
|
605
611
|
options?.maxRetryDelayMs,
|
|
606
612
|
options?.attemptScope,
|
|
607
613
|
);
|
|
@@ -618,15 +624,24 @@ export const streamOpenAICompletions: StreamFunction<"openai-completions"> = (
|
|
|
618
624
|
const createCompletionsStream = async (toolStrictModeOverride?: ToolStrictModeOverride) => {
|
|
619
625
|
clearCapturedErrorResponse();
|
|
620
626
|
const effectiveToolStrictModeOverride = disableStrictTools ? "none" : toolStrictModeOverride;
|
|
621
|
-
const { params, toolStrictMode } = buildParams(
|
|
627
|
+
const { params: builtParams, toolStrictMode } = buildParams(
|
|
622
628
|
model,
|
|
623
629
|
context,
|
|
624
630
|
options,
|
|
625
631
|
baseUrl,
|
|
626
632
|
effectiveToolStrictModeOverride,
|
|
627
633
|
);
|
|
634
|
+
let params = builtParams;
|
|
628
635
|
appliedToolStrictMode = toolStrictMode;
|
|
629
|
-
options?.onPayload?.(
|
|
636
|
+
const replacementPayload = await options?.onPayload?.(
|
|
637
|
+
params,
|
|
638
|
+
undefined,
|
|
639
|
+
options?.attemptScope,
|
|
640
|
+
options?.signal,
|
|
641
|
+
);
|
|
642
|
+
if (replacementPayload !== undefined) {
|
|
643
|
+
params = replacementPayload as typeof params;
|
|
644
|
+
}
|
|
630
645
|
rawRequestDump = {
|
|
631
646
|
provider: model.provider,
|
|
632
647
|
api: output.api,
|
|
@@ -1259,6 +1274,7 @@ async function createClient(
|
|
|
1259
1274
|
authCredentialType?: OpenAICompletionsOptions["authCredentialType"],
|
|
1260
1275
|
requestMaxRetries?: number,
|
|
1261
1276
|
sessionId?: string,
|
|
1277
|
+
providerSessionId?: string,
|
|
1262
1278
|
maxRetryDelayMs?: number,
|
|
1263
1279
|
attemptScope?: import("../types.js").AttemptScopeRef,
|
|
1264
1280
|
): Promise<{
|
|
@@ -1356,6 +1372,11 @@ async function createClient(
|
|
|
1356
1372
|
const endpointRequestQuery = endpointQuery;
|
|
1357
1373
|
const requestQuery =
|
|
1358
1374
|
[endpointRequestQuery, azureQuery].filter((query): query is string => query !== undefined).join("&") || undefined;
|
|
1375
|
+
const openCodeGoSessionId = resolveOpenCodeGoSessionId(model, baseUrl, providerSessionId, "openai");
|
|
1376
|
+
// Reserve the provider-specific header on every OpenAI-compatible route. Caller,
|
|
1377
|
+
// model, and transform values are removed unless the exact OpenCode Go endpoint
|
|
1378
|
+
// has an opaque identity owned by the agent's conversation lifecycle.
|
|
1379
|
+
headers = applyOpenCodeGoSessionHeader(headers, openCodeGoSessionId);
|
|
1359
1380
|
let capturedErrorResponse: CapturedHttpErrorResponse | undefined;
|
|
1360
1381
|
const baseFetch = fetchOverride ?? fetch;
|
|
1361
1382
|
const wrappedFetch = Object.assign(
|
|
@@ -1389,8 +1410,9 @@ async function createClient(
|
|
|
1389
1410
|
baseFetch.preconnect ? { preconnect: baseFetch.preconnect } : {},
|
|
1390
1411
|
);
|
|
1391
1412
|
const boundedFetch = wrapOpenAIFetchForBoundedRateLimits(wrappedFetch, maxRetryDelayMs);
|
|
1413
|
+
const providerScopedFetch = wrapFetchForOpenCodeGoSession(boundedFetch, openCodeGoSessionId);
|
|
1392
1414
|
const transformedFetch = wrapFetchForOpenAIRequestTransform(
|
|
1393
|
-
|
|
1415
|
+
providerScopedFetch,
|
|
1394
1416
|
model.requestTransform,
|
|
1395
1417
|
`Gajae-Code/${packageJson.version}`,
|
|
1396
1418
|
);
|