@gajae-code/ai 0.15.5 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +40 -0
- package/dist/types/adapter-internals/aws-region.d.ts +7 -0
- package/dist/types/auth-broker/client.d.ts +6 -2
- package/dist/types/auth-broker/remote-store.d.ts +14 -2
- package/dist/types/auth-broker/types.d.ts +6 -0
- package/dist/types/auth-broker/wire-schemas.d.ts +19 -0
- package/dist/types/auth-gateway/server.d.ts +39 -5
- package/dist/types/auth-gateway/types.d.ts +16 -2
- package/dist/types/auth-storage.d.ts +92 -24
- package/dist/types/core.d.ts +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/provider-models/openai-compat.d.ts +1 -0
- package/dist/types/providers/anthropic.d.ts +1 -1
- package/dist/types/providers/google-gemini-headers.d.ts +1 -1
- package/dist/types/providers/openai-codex-responses.d.ts +6 -0
- package/dist/types/providers/register-builtins.d.ts +12 -12
- package/dist/types/stream.d.ts +2 -1
- package/dist/types/types.d.ts +22 -2
- package/dist/types/utils/oauth/api-key-login.d.ts +4 -1
- package/dist/types/utils/oauth/api-key-validation.d.ts +12 -6
- package/dist/types/utils/oauth/commandcode.d.ts +1 -0
- package/dist/types/utils/oauth/types.d.ts +1 -1
- package/dist/types/utils/retry.d.ts +2 -0
- package/dist/types/utils/schema/normalize.d.ts +0 -5
- package/dist/types/utils/sqlite-errors.d.ts +4 -0
- package/package.json +3 -3
- package/src/adapter-internals/aws-region.d.ts +7 -0
- package/src/adapter-internals/aws-region.ts +14 -0
- package/src/auth-broker/client.ts +41 -13
- package/src/auth-broker/redact.ts +25 -1
- package/src/auth-broker/remote-store.ts +374 -115
- package/src/auth-broker/server.ts +131 -91
- package/src/auth-broker/types.ts +6 -0
- package/src/auth-broker/wire-schemas.ts +6 -0
- package/src/auth-gateway/server.ts +447 -79
- package/src/auth-gateway/types.ts +28 -2
- package/src/auth-storage.ts +672 -168
- package/src/cli.ts +1 -0
- package/src/core.ts +1 -0
- package/src/index.ts +1 -0
- package/src/model-thinking.ts +8 -0
- package/src/models.json +1224 -3
- package/src/provider-models/descriptors.ts +3 -1
- package/src/provider-models/openai-compat.ts +102 -1
- package/src/providers/amazon-bedrock.ts +5 -1
- package/src/providers/anthropic.d.ts +1 -1
- package/src/providers/anthropic.ts +8 -2
- package/src/providers/aws-credentials.ts +6 -0
- package/src/providers/azure-openai-responses.ts +4 -1
- package/src/providers/cursor.ts +256 -101
- package/src/providers/gitlab-duo.ts +18 -1
- package/src/providers/google-gemini-cli.ts +3 -0
- package/src/providers/google-gemini-headers.d.ts +1 -1
- package/src/providers/google-gemini-headers.ts +1 -1
- package/src/providers/google-shared.ts +3 -0
- package/src/providers/kiro-api-key.ts +33 -8
- package/src/providers/kiro-codewhisperer.ts +28 -9
- package/src/providers/ollama.ts +3 -0
- package/src/providers/openai-codex-responses.d.ts +6 -0
- package/src/providers/openai-codex-responses.ts +37 -8
- package/src/providers/openai-completions.ts +11 -1
- package/src/providers/openai-responses.ts +10 -1
- package/src/providers/pi-native-client.ts +25 -1
- package/src/providers/pi-native-server.ts +24 -0
- package/src/providers/register-builtins.d.ts +12 -12
- package/src/providers/register-builtins.ts +16 -3
- package/src/stream.d.ts +2 -1
- package/src/stream.ts +175 -67
- package/src/types.d.ts +22 -2
- package/src/types.ts +27 -1
- package/src/utils/oauth/api-key-login.ts +13 -2
- package/src/utils/oauth/api-key-validation.ts +242 -41
- package/src/utils/oauth/commandcode.ts +17 -0
- package/src/utils/oauth/index.ts +20 -5
- package/src/utils/oauth/kiro.ts +91 -22
- package/src/utils/oauth/types.d.ts +1 -1
- package/src/utils/oauth/types.ts +1 -0
- package/src/utils/retry.d.ts +2 -0
- package/src/utils/retry.ts +15 -2
- package/src/utils/schema/dereference.ts +169 -49
- package/src/utils/schema/draft.ts +46 -23
- package/src/utils/schema/normalize.d.ts +0 -5
- package/src/utils/schema/normalize.ts +396 -119
- package/src/utils/schema/types.ts +3 -1
- package/src/utils/schema/zod-decontaminate.ts +83 -29
- package/src/utils/sqlite-errors.d.ts +4 -0
- package/src/utils/sqlite-errors.ts +13 -0
- package/src/utils/tool-choice-capability.ts +2 -3
|
@@ -252,10 +252,17 @@ function forwardStream<TApi extends Api>(
|
|
|
252
252
|
model: Model<TApi>,
|
|
253
253
|
options: OptionsForApi<TApi>,
|
|
254
254
|
abortTracker: AbortSourceTracker,
|
|
255
|
+
onStreamCreated?: () => void,
|
|
255
256
|
limits?: LazyStreamLimits,
|
|
256
257
|
): void {
|
|
257
258
|
(async () => {
|
|
258
259
|
try {
|
|
260
|
+
let admitted = false;
|
|
261
|
+
const markAdmission = (): void => {
|
|
262
|
+
if (admitted) return;
|
|
263
|
+
admitted = true;
|
|
264
|
+
onStreamCreated?.();
|
|
265
|
+
};
|
|
259
266
|
let watchedSource = source;
|
|
260
267
|
if (!limits?.providerOwnsWatchdog) {
|
|
261
268
|
const idleTimeoutMs = options.streamIdleTimeoutMs ?? getStreamIdleTimeoutMs(limits?.defaultIdleTimeoutMs);
|
|
@@ -286,6 +293,7 @@ function forwardStream<TApi extends Api>(
|
|
|
286
293
|
}
|
|
287
294
|
|
|
288
295
|
for await (const event of watchedSource) {
|
|
296
|
+
if (event.type !== "start") markAdmission();
|
|
289
297
|
target.push(event);
|
|
290
298
|
}
|
|
291
299
|
if (hasFinalResult(source)) {
|
|
@@ -337,8 +345,13 @@ function createLazyLoadErrorMessage<TApi extends Api>(
|
|
|
337
345
|
function createLazyStream<TApi extends Api>(
|
|
338
346
|
loadModule: () => Promise<LazyProviderModule<TApi>>,
|
|
339
347
|
limits?: LazyStreamLimits,
|
|
340
|
-
): (
|
|
341
|
-
|
|
348
|
+
): (
|
|
349
|
+
model: Model<TApi>,
|
|
350
|
+
context: Context,
|
|
351
|
+
options: OptionsForApi<TApi>,
|
|
352
|
+
onStreamCreated?: () => void,
|
|
353
|
+
) => EventStreamImpl {
|
|
354
|
+
return (model, context, options, onStreamCreated) => {
|
|
342
355
|
let abortTracker: AbortSourceTracker | undefined;
|
|
343
356
|
const outer = new EventStreamImpl(() =>
|
|
344
357
|
abortTracker?.abortLocally(new Error("Provider stream consumer stopped before completion")),
|
|
@@ -350,7 +363,7 @@ function createLazyStream<TApi extends Api>(
|
|
|
350
363
|
abortTracker = createAbortSourceTracker(streamOptions.signal);
|
|
351
364
|
const providerOptions = { ...streamOptions, signal: abortTracker.requestSignal } as OptionsForApi<TApi>;
|
|
352
365
|
const inner = module.stream(model, context, providerOptions);
|
|
353
|
-
forwardStream(outer, inner, model, streamOptions, abortTracker, limits);
|
|
366
|
+
forwardStream(outer, inner, model, streamOptions, abortTracker, onStreamCreated, limits);
|
|
354
367
|
})
|
|
355
368
|
.catch(error => {
|
|
356
369
|
const message = createLazyLoadErrorMessage(model, error);
|
package/src/stream.d.ts
CHANGED
|
@@ -29,12 +29,13 @@ export declare function listProvidersWithEnvKey(): string[];
|
|
|
29
29
|
* handling, so callers can append it unconditionally.
|
|
30
30
|
*/
|
|
31
31
|
export declare function formatProviderCredentialHint(provider: string): string;
|
|
32
|
+
export declare function streamFromLazyImport(createInner: () => Promise<AssistantMessageEventStream>, signal?: AbortSignal, onStreamCreated?: () => void): AssistantMessageEventStream;
|
|
32
33
|
/**
|
|
33
34
|
* Build an actionable "missing API key" error for a provider, used by the
|
|
34
35
|
* low-level `stream`/`complete` entry points (#755).
|
|
35
36
|
*/
|
|
36
37
|
export declare function formatMissingApiKeyError(provider: string): string;
|
|
37
|
-
export declare function stream<TApi extends Api>(model: Model<TApi>, context: Context, options?: OptionsForApi<TApi
|
|
38
|
+
export declare function stream<TApi extends Api>(model: Model<TApi>, context: Context, options?: OptionsForApi<TApi>, onStreamCreated?: () => void): AssistantMessageEventStream;
|
|
38
39
|
export declare function complete<TApi extends Api>(model: Model<TApi>, context: Context, options?: OptionsForApi<TApi>): Promise<AssistantMessage>;
|
|
39
40
|
export declare function streamSimple<TApi extends Api>(model: Model<TApi>, context: Context, options?: SimpleStreamOptions): AssistantMessageEventStream;
|
|
40
41
|
export declare function completeSimple<TApi extends Api>(model: Model<TApi>, context: Context, options?: SimpleStreamOptions): Promise<AssistantMessage>;
|
package/src/stream.ts
CHANGED
|
@@ -67,6 +67,7 @@ import type {
|
|
|
67
67
|
Api,
|
|
68
68
|
AssistantMessage,
|
|
69
69
|
AssistantMessageEvent,
|
|
70
|
+
AuthRetryCredential,
|
|
70
71
|
Context,
|
|
71
72
|
Model,
|
|
72
73
|
OptionsForApi,
|
|
@@ -115,6 +116,7 @@ const serviceProviderMap: Record<string, KeyResolver> = {
|
|
|
115
116
|
mistral: "MISTRAL_API_KEY",
|
|
116
117
|
minimax: "MINIMAX_API_KEY",
|
|
117
118
|
"minimax-code": "MINIMAX_CODE_API_KEY",
|
|
119
|
+
"commandcode-goat": "CMD_API_KEY",
|
|
118
120
|
"minimax-code-cn": "MINIMAX_CODE_CN_API_KEY",
|
|
119
121
|
"opencode-go": "OPENCODE_API_KEY",
|
|
120
122
|
"opencode-zen": "OPENCODE_API_KEY",
|
|
@@ -221,6 +223,7 @@ export function listProvidersWithEnvKey(): string[] {
|
|
|
221
223
|
* Used to give OpenCode users an accurate headless auth diagnostic (#755).
|
|
222
224
|
*/
|
|
223
225
|
const OPENCODE_SUBSCRIPTION_PROVIDERS = new Set(["opencode-go", "opencode-zen"]);
|
|
226
|
+
const API_KEY_LOGIN_PROVIDERS = new Set(["commandcode-goat"]);
|
|
224
227
|
|
|
225
228
|
/**
|
|
226
229
|
* Provider-specific credential guidance appended to "no credential" errors.
|
|
@@ -239,12 +242,16 @@ export function formatProviderCredentialHint(provider: string): string {
|
|
|
239
242
|
const resolver = serviceProviderMap[provider];
|
|
240
243
|
const envVar = typeof resolver === "string" ? resolver : undefined;
|
|
241
244
|
const isOpenCodeSubscription = OPENCODE_SUBSCRIPTION_PROVIDERS.has(provider);
|
|
245
|
+
const isApiKeyLoginProvider = API_KEY_LOGIN_PROVIDERS.has(provider);
|
|
242
246
|
const parts: string[] = [];
|
|
243
247
|
if (isOpenCodeSubscription) {
|
|
244
248
|
parts.push(
|
|
245
249
|
"OpenCode subscriptions authenticate with an API key (created at https://opencode.ai/auth), not a separate session/OAuth token.",
|
|
246
250
|
);
|
|
247
251
|
}
|
|
252
|
+
if (isApiKeyLoginProvider) {
|
|
253
|
+
parts.push("Command Code GOAT uses an API key from https://commandcode.ai/studio/#api-keys.");
|
|
254
|
+
}
|
|
248
255
|
if (provider === "jetbrains-junie") {
|
|
249
256
|
parts.push(
|
|
250
257
|
"JetBrains AI (Junie) authenticates with an access token generated at https://junie.jetbrains.com/cli; there is no OAuth login for this provider.",
|
|
@@ -267,10 +274,18 @@ function pipeAssistantStream(
|
|
|
267
274
|
outer: AssistantMessageEventStream,
|
|
268
275
|
inner: AssistantMessageEventStream,
|
|
269
276
|
signal?: AbortSignal,
|
|
277
|
+
onStreamCreated?: () => void,
|
|
270
278
|
): void {
|
|
271
279
|
void (async () => {
|
|
272
280
|
try {
|
|
281
|
+
let admitted = false;
|
|
282
|
+
const markAdmission = (): void => {
|
|
283
|
+
if (admitted) return;
|
|
284
|
+
admitted = true;
|
|
285
|
+
onStreamCreated?.();
|
|
286
|
+
};
|
|
273
287
|
for await (const event of inner) {
|
|
288
|
+
if (event.type !== "start") markAdmission();
|
|
274
289
|
outer.push(event);
|
|
275
290
|
// The inner provider stream owns abort semantics (it receives the
|
|
276
291
|
// same signal), but stop forwarding as soon as the consumer
|
|
@@ -288,14 +303,16 @@ function pipeAssistantStream(
|
|
|
288
303
|
})();
|
|
289
304
|
}
|
|
290
305
|
|
|
291
|
-
function streamFromLazyImport(
|
|
306
|
+
export function streamFromLazyImport(
|
|
292
307
|
createInner: () => Promise<AssistantMessageEventStream>,
|
|
293
308
|
signal?: AbortSignal,
|
|
309
|
+
onStreamCreated?: () => void,
|
|
294
310
|
): AssistantMessageEventStream {
|
|
295
311
|
const outer = new AssistantMessageEventStream();
|
|
296
312
|
void (async () => {
|
|
297
313
|
try {
|
|
298
|
-
|
|
314
|
+
const inner = await createInner();
|
|
315
|
+
pipeAssistantStream(outer, inner, signal, onStreamCreated);
|
|
299
316
|
} catch (error) {
|
|
300
317
|
outer.fail(error);
|
|
301
318
|
}
|
|
@@ -317,6 +334,7 @@ export function stream<TApi extends Api>(
|
|
|
317
334
|
model: Model<TApi>,
|
|
318
335
|
context: Context,
|
|
319
336
|
options?: OptionsForApi<TApi>,
|
|
337
|
+
onStreamCreated?: () => void,
|
|
320
338
|
): AssistantMessageEventStream {
|
|
321
339
|
if (!hasValidatedManagedAttempt(options)) assertManagedAttempt(options);
|
|
322
340
|
if (options?.fallbackManaged) {
|
|
@@ -350,6 +368,7 @@ export function stream<TApi extends Api>(
|
|
|
350
368
|
return streamGitLabDuo(model, context, adapterOptions);
|
|
351
369
|
},
|
|
352
370
|
(options as StreamOptions | undefined)?.signal,
|
|
371
|
+
onStreamCreated,
|
|
353
372
|
);
|
|
354
373
|
}
|
|
355
374
|
|
|
@@ -362,15 +381,22 @@ export function stream<TApi extends Api>(
|
|
|
362
381
|
isProviderSafetyStopModelTrusted(model)
|
|
363
382
|
? withProviderSafetyStopAdapterInvocation(vertexOptions)
|
|
364
383
|
: vertexOptions,
|
|
384
|
+
onStreamCreated,
|
|
365
385
|
);
|
|
366
386
|
} else if (model.api === "bedrock-converse-stream") {
|
|
367
387
|
// Bedrock doesn't have any API keys instead it sources credentials from standard AWS env variables or from given AWS profile.
|
|
368
|
-
return streamBedrock(
|
|
388
|
+
return streamBedrock(
|
|
389
|
+
model as Model<"bedrock-converse-stream">,
|
|
390
|
+
context,
|
|
391
|
+
(options || {}) as BedrockOptions,
|
|
392
|
+
onStreamCreated,
|
|
393
|
+
);
|
|
369
394
|
} else if (model.api === "kiro-codewhisperer-stream") {
|
|
370
395
|
return streamKiroCodeWhisperer(
|
|
371
396
|
model as Model<"kiro-codewhisperer-stream">,
|
|
372
397
|
context,
|
|
373
398
|
(options || {}) as KiroCodeWhispererOptions,
|
|
399
|
+
onStreamCreated,
|
|
374
400
|
);
|
|
375
401
|
}
|
|
376
402
|
|
|
@@ -387,23 +413,39 @@ export function stream<TApi extends Api>(
|
|
|
387
413
|
switch (api) {
|
|
388
414
|
case "anthropic-messages": {
|
|
389
415
|
const anthropicOptions = adapterProviderOptions as AnthropicOptions;
|
|
390
|
-
return streamAnthropic(
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
416
|
+
return streamAnthropic(
|
|
417
|
+
model as Model<"anthropic-messages">,
|
|
418
|
+
context,
|
|
419
|
+
{
|
|
420
|
+
...anthropicOptions,
|
|
421
|
+
isOAuth: anthropicOptions.isOAuth ?? model.isOAuth,
|
|
422
|
+
},
|
|
423
|
+
onStreamCreated,
|
|
424
|
+
);
|
|
394
425
|
}
|
|
395
426
|
|
|
396
427
|
case "openai-completions":
|
|
397
|
-
return streamOpenAICompletions(
|
|
428
|
+
return streamOpenAICompletions(
|
|
429
|
+
model as Model<"openai-completions">,
|
|
430
|
+
context,
|
|
431
|
+
adapterProviderOptions as any,
|
|
432
|
+
onStreamCreated,
|
|
433
|
+
);
|
|
398
434
|
|
|
399
435
|
case "openai-responses":
|
|
400
|
-
return streamOpenAIResponses(
|
|
436
|
+
return streamOpenAIResponses(
|
|
437
|
+
model as Model<"openai-responses">,
|
|
438
|
+
context,
|
|
439
|
+
adapterProviderOptions as any,
|
|
440
|
+
onStreamCreated,
|
|
441
|
+
);
|
|
401
442
|
|
|
402
443
|
case "azure-openai-responses":
|
|
403
444
|
return streamAzureOpenAIResponses(
|
|
404
445
|
model as Model<"azure-openai-responses">,
|
|
405
446
|
context,
|
|
406
447
|
adapterProviderOptions as any,
|
|
448
|
+
onStreamCreated,
|
|
407
449
|
);
|
|
408
450
|
|
|
409
451
|
case "openai-codex-responses":
|
|
@@ -411,23 +453,35 @@ export function stream<TApi extends Api>(
|
|
|
411
453
|
model as Model<"openai-codex-responses">,
|
|
412
454
|
context,
|
|
413
455
|
adapterProviderOptions as any,
|
|
456
|
+
onStreamCreated,
|
|
414
457
|
);
|
|
415
458
|
|
|
416
459
|
case "google-generative-ai":
|
|
417
|
-
return streamGoogle(model as Model<"google-generative-ai">, context, adapterProviderOptions);
|
|
460
|
+
return streamGoogle(model as Model<"google-generative-ai">, context, adapterProviderOptions, onStreamCreated);
|
|
418
461
|
|
|
419
462
|
case "google-gemini-cli":
|
|
420
463
|
return streamGoogleGeminiCli(
|
|
421
464
|
model as Model<"google-gemini-cli">,
|
|
422
465
|
context,
|
|
423
466
|
adapterProviderOptions as GoogleGeminiCliOptions,
|
|
467
|
+
onStreamCreated,
|
|
424
468
|
);
|
|
425
469
|
|
|
426
470
|
case "ollama-chat":
|
|
427
|
-
return streamOllama(
|
|
471
|
+
return streamOllama(
|
|
472
|
+
model as Model<"ollama-chat">,
|
|
473
|
+
context,
|
|
474
|
+
adapterProviderOptions as OllamaChatOptions,
|
|
475
|
+
onStreamCreated,
|
|
476
|
+
);
|
|
428
477
|
|
|
429
478
|
case "cursor-agent":
|
|
430
|
-
return streamCursor(
|
|
479
|
+
return streamCursor(
|
|
480
|
+
model as Model<"cursor-agent">,
|
|
481
|
+
context,
|
|
482
|
+
adapterProviderOptions as CursorOptions,
|
|
483
|
+
onStreamCreated,
|
|
484
|
+
);
|
|
431
485
|
|
|
432
486
|
default:
|
|
433
487
|
throw new Error(`Unhandled API: ${api}`);
|
|
@@ -539,9 +593,19 @@ export function streamSimple<TApi extends Api>(
|
|
|
539
593
|
? AbortSignal.any([options.signal, consumerAbortController.signal])
|
|
540
594
|
: consumerAbortController.signal;
|
|
541
595
|
const onAuthError = options!.onAuthError!;
|
|
542
|
-
const runAttempt = async (
|
|
596
|
+
const runAttempt = async (
|
|
597
|
+
apiKey: string,
|
|
598
|
+
captureAuthFailure: boolean,
|
|
599
|
+
onStreamCreated?: () => void,
|
|
600
|
+
): Promise<AuthRetryFailure | undefined> => {
|
|
543
601
|
const bufferedEvents: AssistantMessageEvent[] = [];
|
|
544
602
|
let emittedReplayUnsafeEvent = false;
|
|
603
|
+
let admitted = false;
|
|
604
|
+
const markAdmission = (): void => {
|
|
605
|
+
if (admitted) return;
|
|
606
|
+
admitted = true;
|
|
607
|
+
onStreamCreated?.();
|
|
608
|
+
};
|
|
545
609
|
const flushBuffered = (): void => {
|
|
546
610
|
emitBufferedEvents(outer, bufferedEvents);
|
|
547
611
|
bufferedEvents.length = 0;
|
|
@@ -552,6 +616,7 @@ export function streamSimple<TApi extends Api>(
|
|
|
552
616
|
...options,
|
|
553
617
|
apiKey,
|
|
554
618
|
onAuthError: undefined,
|
|
619
|
+
onStreamCreated: markAdmission,
|
|
555
620
|
signal: requestSignal,
|
|
556
621
|
});
|
|
557
622
|
for await (const event of inner) {
|
|
@@ -587,6 +652,12 @@ export function streamSimple<TApi extends Api>(
|
|
|
587
652
|
}
|
|
588
653
|
flushBuffered();
|
|
589
654
|
outer.fail(error);
|
|
655
|
+
} finally {
|
|
656
|
+
// A lazy import or a synchronous provider failure can happen before
|
|
657
|
+
// the admission hook is reached. Release that attempt's lease in
|
|
658
|
+
// the failure path without extending a successful request's lease
|
|
659
|
+
// through the response lifetime.
|
|
660
|
+
if (!admitted) markAdmission();
|
|
590
661
|
}
|
|
591
662
|
return undefined;
|
|
592
663
|
};
|
|
@@ -600,19 +671,22 @@ export function streamSimple<TApi extends Api>(
|
|
|
600
671
|
};
|
|
601
672
|
|
|
602
673
|
void (async () => {
|
|
603
|
-
const failure = await runAttempt(retryApiKey, true);
|
|
674
|
+
const failure = await runAttempt(retryApiKey, true, options?.onStreamCreated);
|
|
604
675
|
if (!failure) return;
|
|
605
|
-
let
|
|
676
|
+
let nextCredential: string | AuthRetryCredential | undefined;
|
|
606
677
|
try {
|
|
607
|
-
|
|
678
|
+
nextCredential = await onAuthError(model.provider, retryApiKey, failure.error);
|
|
608
679
|
} catch {
|
|
609
|
-
|
|
680
|
+
nextCredential = undefined;
|
|
610
681
|
}
|
|
611
|
-
|
|
682
|
+
const retryCredential: AuthRetryCredential | undefined =
|
|
683
|
+
typeof nextCredential === "string" ? { apiKey: nextCredential } : nextCredential;
|
|
684
|
+
if (!retryCredential?.apiKey || retryCredential.apiKey === retryApiKey) {
|
|
685
|
+
if (retryCredential) retryCredential.onStreamCreated?.();
|
|
612
686
|
emitFailure(failure);
|
|
613
687
|
return;
|
|
614
688
|
}
|
|
615
|
-
await runAttempt(
|
|
689
|
+
await runAttempt(retryCredential.apiKey, false, retryCredential.onStreamCreated);
|
|
616
690
|
})();
|
|
617
691
|
return outer;
|
|
618
692
|
}
|
|
@@ -625,26 +699,39 @@ export function streamSimple<TApi extends Api>(
|
|
|
625
699
|
// pi-native transport.
|
|
626
700
|
const resolvedRequestMaxTokens = resolveDefaultRequestMaxTokens(model, options?.maxTokens);
|
|
627
701
|
if (model.transport === "pi-native") {
|
|
628
|
-
return streamFromLazyImport(
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
702
|
+
return streamFromLazyImport(
|
|
703
|
+
async () => {
|
|
704
|
+
const { streamPiNative } = await import("./providers/pi-native-client");
|
|
705
|
+
return streamPiNative(model, context, { ...options, maxTokens: resolvedRequestMaxTokens });
|
|
706
|
+
},
|
|
707
|
+
options?.signal,
|
|
708
|
+
options?.onStreamCreated,
|
|
709
|
+
);
|
|
632
710
|
}
|
|
633
711
|
|
|
634
712
|
// Check custom API registry (extension-provided APIs)
|
|
635
713
|
const customApiProvider = getCustomApi(model.api);
|
|
636
714
|
if (customApiProvider) {
|
|
637
|
-
|
|
715
|
+
const events = customApiProvider.streamSimple(model, context, {
|
|
716
|
+
...options,
|
|
717
|
+
maxTokens: resolvedRequestMaxTokens,
|
|
718
|
+
});
|
|
719
|
+
if (!options?.onStreamCreated) return events;
|
|
720
|
+
const forwarded = new AssistantMessageEventStream();
|
|
721
|
+
pipeAssistantStream(forwarded, events, options.signal, options.onStreamCreated);
|
|
722
|
+
return forwarded;
|
|
638
723
|
}
|
|
639
724
|
|
|
640
725
|
// Vertex AI uses Application Default Credentials, not API keys
|
|
641
726
|
if (model.api === "google-vertex") {
|
|
642
727
|
const providerOptions = mapOptionsForApi(model, options, undefined);
|
|
643
|
-
|
|
728
|
+
const events = stream(model, context, providerOptions, options?.onStreamCreated);
|
|
729
|
+
return events;
|
|
644
730
|
} else if (model.api === "bedrock-converse-stream") {
|
|
645
731
|
// Bedrock doesn't have any API keys instead it sources credentials from standard AWS env variables or from given AWS profile.
|
|
646
732
|
const providerOptions = mapOptionsForApi(model, options, undefined);
|
|
647
|
-
|
|
733
|
+
const events = stream(model, context, providerOptions, options?.onStreamCreated);
|
|
734
|
+
return events;
|
|
648
735
|
}
|
|
649
736
|
|
|
650
737
|
const apiKey = options?.apiKey || getEnvApiKey(model.provider);
|
|
@@ -658,58 +745,71 @@ export function streamSimple<TApi extends Api>(
|
|
|
658
745
|
|
|
659
746
|
// GitLab Duo - wraps Anthropic/OpenAI behind GitLab AI Gateway direct access tokens
|
|
660
747
|
if (model.provider === "gitlab-duo") {
|
|
661
|
-
return streamFromLazyImport(
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
748
|
+
return streamFromLazyImport(
|
|
749
|
+
async () => {
|
|
750
|
+
const { streamGitLabDuo } = await import("./providers/gitlab-duo");
|
|
751
|
+
return streamGitLabDuo(
|
|
752
|
+
model,
|
|
753
|
+
context,
|
|
754
|
+
copyProviderSafetyStopAdapterInvocation(adapterOptions, {
|
|
755
|
+
...adapterOptions,
|
|
756
|
+
apiKey,
|
|
757
|
+
maxTokens: resolvedSpecialProviderMaxTokens,
|
|
758
|
+
}),
|
|
759
|
+
);
|
|
760
|
+
},
|
|
761
|
+
options?.signal,
|
|
762
|
+
options?.onStreamCreated,
|
|
763
|
+
);
|
|
673
764
|
}
|
|
674
765
|
|
|
675
766
|
// Kimi Code - route to dedicated handler that wraps OpenAI or Anthropic API
|
|
676
767
|
if (model.provider === "kimi-code") {
|
|
677
|
-
return streamFromLazyImport(
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
768
|
+
return streamFromLazyImport(
|
|
769
|
+
async () => {
|
|
770
|
+
const { streamKimi } = await import("./providers/kimi");
|
|
771
|
+
// Pass raw SimpleStreamOptions - streamKimi handles mapping internally
|
|
772
|
+
return streamKimi(
|
|
773
|
+
model as Model<"openai-completions">,
|
|
774
|
+
context,
|
|
775
|
+
copyProviderSafetyStopAdapterInvocation(adapterOptions, {
|
|
776
|
+
...adapterOptions,
|
|
777
|
+
apiKey,
|
|
778
|
+
maxTokens: resolvedSpecialProviderMaxTokens,
|
|
779
|
+
format: options?.kimiApiFormat ?? "anthropic",
|
|
780
|
+
}),
|
|
781
|
+
);
|
|
782
|
+
},
|
|
783
|
+
options?.signal,
|
|
784
|
+
options?.onStreamCreated,
|
|
785
|
+
);
|
|
691
786
|
}
|
|
692
787
|
|
|
693
788
|
// Synthetic - route to dedicated handler that wraps OpenAI or Anthropic API
|
|
694
789
|
if (model.provider === "synthetic") {
|
|
695
|
-
return streamFromLazyImport(
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
790
|
+
return streamFromLazyImport(
|
|
791
|
+
async () => {
|
|
792
|
+
const { streamSynthetic } = await import("./providers/synthetic");
|
|
793
|
+
// Pass raw SimpleStreamOptions - streamSynthetic handles mapping internally
|
|
794
|
+
return streamSynthetic(
|
|
795
|
+
model as Model<"openai-completions">,
|
|
796
|
+
context,
|
|
797
|
+
copyProviderSafetyStopAdapterInvocation(adapterOptions, {
|
|
798
|
+
...adapterOptions,
|
|
799
|
+
apiKey,
|
|
800
|
+
maxTokens: resolvedSpecialProviderMaxTokens,
|
|
801
|
+
format: options?.syntheticApiFormat ?? "openai", // Default to OpenAI format
|
|
802
|
+
}),
|
|
803
|
+
);
|
|
804
|
+
},
|
|
805
|
+
options?.signal,
|
|
806
|
+
options?.onStreamCreated,
|
|
807
|
+
);
|
|
709
808
|
}
|
|
710
809
|
|
|
711
810
|
const providerOptions = mapOptionsForApi(model, options, apiKey);
|
|
712
|
-
|
|
811
|
+
const events = stream(model, context, providerOptions, options?.onStreamCreated);
|
|
812
|
+
return events;
|
|
713
813
|
}
|
|
714
814
|
|
|
715
815
|
export async function completeSimple<TApi extends Api>(
|
|
@@ -858,6 +958,8 @@ function mapOptionsForApi<TApi extends Api>(
|
|
|
858
958
|
providerSessionState: options?.providerSessionState,
|
|
859
959
|
onPayload: options?.onPayload,
|
|
860
960
|
onResponse: options?.onResponse,
|
|
961
|
+
onStreamCreated: options?.onStreamCreated,
|
|
962
|
+
disableProviderRetries: options?.disableProviderRetries,
|
|
861
963
|
onSseEvent: options?.onSseEvent,
|
|
862
964
|
attemptScope: options?.attemptScope,
|
|
863
965
|
execHandlers: options?.execHandlers,
|
|
@@ -1162,6 +1264,12 @@ function mapOptionsForApi<TApi extends Api>(
|
|
|
1162
1264
|
});
|
|
1163
1265
|
}
|
|
1164
1266
|
|
|
1267
|
+
case "kiro-codewhisperer-stream":
|
|
1268
|
+
return castApi<"kiro-codewhisperer-stream">({
|
|
1269
|
+
...base,
|
|
1270
|
+
reasoning: options?.reasoning,
|
|
1271
|
+
});
|
|
1272
|
+
|
|
1165
1273
|
default:
|
|
1166
1274
|
throw new Error(`Unhandled API in mapOptionsForApi: ${model.api}`);
|
|
1167
1275
|
}
|
package/src/types.d.ts
CHANGED
|
@@ -54,7 +54,7 @@ export interface ThinkingConfig {
|
|
|
54
54
|
/** Provider-specific transport used to encode the selected effort. */
|
|
55
55
|
mode: ThinkingControlMode;
|
|
56
56
|
}
|
|
57
|
-
export declare const KNOWN_PROVIDERS: readonly ["alibaba-token-plan", "amazon-bedrock", "kiro", "azure-openai", "anthropic", "google", "google-gemini-cli", "google-antigravity", "google-vertex", "openai", "openai-codex", "opencodex", "kimi-code", "minimax-code", "minimax-code-cn", "github-copilot", "fireworks", "firepass", "fugu", "gitlab-duo", "cursor", "jetbrains-junie", "deepseek", "deepinfra", "xai", "groq", "cerebras", "openrouter", "kilo", "vercel-ai-gateway", "zai", "glm-zcode", "mistral", "minimax", "opencode-go", "opencode-zen", "opengateway", "bizrouter", "mara", "synthetic", "cloudflare-ai-gateway", "huggingface", "litellm", "moonshot", "nvidia", "nanogpt", "ollama", "ollama-cloud", "qianfan", "qwen-portal", "sglang", "together", "venice", "vllm", "xiaomi", "xiaomi-token-plan-sgp", "xiaomi-token-plan-ams", "xiaomi-token-plan-cn", "zenmux", "lm-studio", "omlx"];
|
|
57
|
+
export declare const KNOWN_PROVIDERS: readonly ["alibaba-token-plan", "amazon-bedrock", "kiro", "azure-openai", "anthropic", "google", "google-gemini-cli", "google-antigravity", "google-vertex", "openai", "openai-codex", "opencodex", "kimi-code", "minimax-code", "minimax-code-cn", "github-copilot", "fireworks", "firepass", "fugu", "gitlab-duo", "cursor", "jetbrains-junie", "deepseek", "deepinfra", "xai", "groq", "cerebras", "openrouter", "kilo", "vercel-ai-gateway", "zai", "glm-zcode", "mistral", "minimax", "opencode-go", "commandcode-goat", "opencode-zen", "opengateway", "bizrouter", "mara", "synthetic", "cloudflare-ai-gateway", "huggingface", "litellm", "moonshot", "nvidia", "nanogpt", "ollama", "ollama-cloud", "qianfan", "qwen-portal", "sglang", "together", "venice", "vllm", "xiaomi", "xiaomi-token-plan-sgp", "xiaomi-token-plan-ams", "xiaomi-token-plan-cn", "zenmux", "lm-studio", "omlx"];
|
|
58
58
|
export type KnownProvider = (typeof KNOWN_PROVIDERS)[number];
|
|
59
59
|
export declare function isKnownProvider(provider: string): provider is KnownProvider;
|
|
60
60
|
export type Provider = KnownProvider | string;
|
|
@@ -155,6 +155,18 @@ export interface RawSseEvent {
|
|
|
155
155
|
export type FetchImpl = ((input: string | URL | Request, init?: RequestInit) => Promise<Response>) & {
|
|
156
156
|
preconnect?: typeof globalThis.fetch.preconnect;
|
|
157
157
|
};
|
|
158
|
+
/**
|
|
159
|
+
* Credential returned by an auth retry resolver.
|
|
160
|
+
*
|
|
161
|
+
* The optional admission callback lets an authority-bearing caller retain a
|
|
162
|
+
* credential lease until the replacement provider request is actually
|
|
163
|
+
* admitted. Ordinary callers can continue returning a string from
|
|
164
|
+
* {@link StreamOptions.onAuthError}.
|
|
165
|
+
*/
|
|
166
|
+
export interface AuthRetryCredential {
|
|
167
|
+
apiKey: string;
|
|
168
|
+
onStreamCreated?: () => void;
|
|
169
|
+
}
|
|
158
170
|
export interface StreamOptions {
|
|
159
171
|
temperature?: number;
|
|
160
172
|
topP?: number;
|
|
@@ -186,7 +198,7 @@ export interface StreamOptions {
|
|
|
186
198
|
* event has been emitted. Returning a different key retries the provider
|
|
187
199
|
* request once.
|
|
188
200
|
*/
|
|
189
|
-
onAuthError?: (provider: string, apiKey: string, error: unknown) => Promise<string | undefined>;
|
|
201
|
+
onAuthError?: (provider: string, apiKey: string, error: unknown) => Promise<string | AuthRetryCredential | undefined>;
|
|
190
202
|
cacheRetention?: CacheRetention;
|
|
191
203
|
/**
|
|
192
204
|
* Additional headers to include in provider requests.
|
|
@@ -243,6 +255,14 @@ export interface StreamOptions {
|
|
|
243
255
|
* The `scope` parameter carries the per-attempt identity for execution attribution.
|
|
244
256
|
*/
|
|
245
257
|
onResponse?: (response: ProviderResponseMetadata, model?: Model<Api>, scope?: AttemptScopeRef) => void | Promise<void>;
|
|
258
|
+
/**
|
|
259
|
+
* Internal dispatch-admission hook. Providers invoke this immediately before
|
|
260
|
+
* submitting an outbound request; stream forwarding retains a first-response
|
|
261
|
+
* fallback for custom providers that do not expose a transport seam.
|
|
262
|
+
*/
|
|
263
|
+
onStreamCreated?: () => void;
|
|
264
|
+
/** Internal authority policy: disable provider-owned retries and corrective replays. */
|
|
265
|
+
disableProviderRetries?: boolean;
|
|
246
266
|
/**
|
|
247
267
|
* Optional callback for raw Server-Sent Events as they arrive from HTTP streaming providers.
|
|
248
268
|
*
|
package/src/types.ts
CHANGED
|
@@ -167,6 +167,7 @@ export const KNOWN_PROVIDERS = [
|
|
|
167
167
|
"mistral",
|
|
168
168
|
"minimax",
|
|
169
169
|
"opencode-go",
|
|
170
|
+
"commandcode-goat",
|
|
170
171
|
"opencode-zen",
|
|
171
172
|
"opengateway",
|
|
172
173
|
"bizrouter",
|
|
@@ -353,6 +354,19 @@ export type FetchImpl = ((input: string | URL | Request, init?: RequestInit) =>
|
|
|
353
354
|
preconnect?: typeof globalThis.fetch.preconnect;
|
|
354
355
|
};
|
|
355
356
|
|
|
357
|
+
/**
|
|
358
|
+
* Credential returned by an auth retry resolver.
|
|
359
|
+
*
|
|
360
|
+
* The optional admission callback lets an authority-bearing caller retain a
|
|
361
|
+
* credential lease until the replacement provider request is actually
|
|
362
|
+
* admitted. Ordinary callers can continue returning a string from
|
|
363
|
+
* {@link StreamOptions.onAuthError}.
|
|
364
|
+
*/
|
|
365
|
+
export interface AuthRetryCredential {
|
|
366
|
+
apiKey: string;
|
|
367
|
+
onStreamCreated?: () => void;
|
|
368
|
+
}
|
|
369
|
+
|
|
356
370
|
export interface StreamOptions {
|
|
357
371
|
temperature?: number;
|
|
358
372
|
topP?: number;
|
|
@@ -384,7 +398,11 @@ export interface StreamOptions {
|
|
|
384
398
|
* event has been emitted. Returning a different key retries the provider
|
|
385
399
|
* request once.
|
|
386
400
|
*/
|
|
387
|
-
onAuthError?: (
|
|
401
|
+
onAuthError?: (
|
|
402
|
+
provider: string,
|
|
403
|
+
apiKey: string,
|
|
404
|
+
error: unknown,
|
|
405
|
+
) => Promise<string | AuthRetryCredential | undefined>;
|
|
388
406
|
cacheRetention?: CacheRetention;
|
|
389
407
|
/**
|
|
390
408
|
* Additional headers to include in provider requests.
|
|
@@ -449,6 +467,14 @@ export interface StreamOptions {
|
|
|
449
467
|
model?: Model<Api>,
|
|
450
468
|
scope?: AttemptScopeRef,
|
|
451
469
|
) => void | Promise<void>;
|
|
470
|
+
/**
|
|
471
|
+
* Internal dispatch-admission hook. Providers invoke this immediately before
|
|
472
|
+
* submitting an outbound request; stream forwarding retains a first-response
|
|
473
|
+
* fallback for custom providers that do not expose a transport seam.
|
|
474
|
+
*/
|
|
475
|
+
onStreamCreated?: () => void;
|
|
476
|
+
/** Internal authority policy: disable provider-owned retries and corrective replays. */
|
|
477
|
+
disableProviderRetries?: boolean;
|
|
452
478
|
/**
|
|
453
479
|
* Optional callback for raw Server-Sent Events as they arrive from HTTP streaming providers.
|
|
454
480
|
*
|