@gajae-code/ai 0.13.1 → 0.13.3

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 CHANGED
@@ -2,6 +2,24 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.13.3] - 2026-08-15
6
+
7
+ ### Added
8
+
9
+ - Added first-class direct xAI `grok-4.6` catalog support over the existing xAI OAuth/subscription transport. Grok 4.5 exposes `low` through `high` reasoning effort and Grok 4.6 exposes `low` through `xhigh`.
10
+ - Added a native TypeScript Kiro (Amazon Q Developer / CodeWhisperer) provider: AWS SSO OIDC device-code login, bearer-token transport to the CodeWhisperer streaming endpoint over `application/vnd.amazon.eventstream`, and a `kiro` model-manager descriptor for Claude 3.7 Sonnet (#4304).
11
+ - Added the authoritative OpenRouter `meta/muse-spark-1.2` catalog fallback with a 1,048,576-token context window and `minimal` through `xhigh` reasoning effort, so stale or credential-limited catalog generation still closes the Muse Spark preset alias deterministically.
12
+
13
+ ### Fixed
14
+ - Validate Synthetic API key via models endpoint, not retired Kimi probe (#4385).
15
+ - Tool-call arguments that spell printable non-ASCII text as `\uXXXX` escapes are now flagged on the raw wire (`escapedNonAsciiArguments`) by the Anthropic, OpenAI Responses, and OpenAI Completions streams — after JSON decode the defect is unobservable, and a mistyped hex nibble silently becomes a different character (#4515).
16
+
17
+ ## [0.13.2] - 2026-08-13
18
+
19
+ ### Fixed
20
+
21
+ - `clear_thinking`-emptied Anthropic thinking blocks now drop stale signatures before replay, preventing the next request from failing with `Invalid signature in thinking block` after a provider-side reasoning clear (#4247, reported by @probepark).
22
+
5
23
  ## [0.13.1] - 2026-08-11
6
24
 
7
25
  ### Fixed
@@ -21,3 +21,6 @@ export declare function glmZcodeModelManagerOptions(_config?: GlmZcodeModelManag
21
21
  export interface JetBrainsJunieModelManagerConfig {
22
22
  }
23
23
  export declare function jetbrainsJunieModelManagerOptions(_config?: JetBrainsJunieModelManagerConfig): ModelManagerOptions<"anthropic-messages">;
24
+ export interface KiroModelManagerConfig {
25
+ }
26
+ export declare function kiroModelManagerOptions(_config?: KiroModelManagerConfig): ModelManagerOptions<"kiro-codewhisperer-stream">;
@@ -0,0 +1,8 @@
1
+ import type { StreamFunction, StreamOptions } from "../types";
2
+ export interface KiroCodeWhispererOptions extends StreamOptions {
3
+ /** AWS region for the CodeWhisperer streaming endpoint. */
4
+ region?: string;
5
+ /** Profile ARN for enterprise IAM Identity Center accounts. */
6
+ profileArn?: string;
7
+ }
8
+ export declare const streamKiroCodeWhisperer: StreamFunction<"kiro-codewhisperer-stream">;
@@ -62,6 +62,8 @@ export type MockContent = string | {
62
62
  arguments: Record<string, unknown> | string;
63
63
  /** Simulate a provider-flagged truncated call (cut off mid-arguments). */
64
64
  incompleteArguments?: boolean;
65
+ /** Simulate a provider-flagged `\uXXXX`-escaped-arguments call. */
66
+ escapedNonAsciiArguments?: boolean;
65
67
  };
66
68
  /** One scripted response. */
67
69
  export interface MockResponse {
@@ -55,4 +55,5 @@ export declare const streamOpenAIResponses: (model: Model<"openai-responses">, c
55
55
  export declare const streamCursor: (model: Model<"cursor-agent">, context: Context, options: OptionsForApi<"cursor-agent">) => EventStreamImpl;
56
56
  export declare const streamOllama: (model: Model<"ollama-chat">, context: Context, options: OptionsForApi<"ollama-chat">) => EventStreamImpl;
57
57
  export declare const streamBedrock: (model: Model<"bedrock-converse-stream">, context: Context, options: OptionsForApi<"bedrock-converse-stream">) => EventStreamImpl;
58
+ export declare const streamKiroCodeWhisperer: (model: Model<"kiro-codewhisperer-stream">, context: Context, options: OptionsForApi<"kiro-codewhisperer-stream">) => EventStreamImpl;
58
59
  export {};
@@ -7,6 +7,7 @@ import type { DeleteArgs, DeleteResult, DiagnosticsArgs, DiagnosticsResult, Grep
7
7
  import type { GoogleOptions } from "./providers/google";
8
8
  import type { GoogleGeminiCliOptions } from "./providers/google-gemini-cli";
9
9
  import type { GoogleVertexOptions } from "./providers/google-vertex";
10
+ import type { KiroCodeWhispererOptions } from "./providers/kiro-codewhisperer";
10
11
  import type { OllamaChatOptions } from "./providers/ollama";
11
12
  import type { OpenAICodexResponsesOptions } from "./providers/openai-codex-responses";
12
13
  import type { OpenAICompletionsOptions } from "./providers/openai-completions";
@@ -14,7 +15,7 @@ import type { OpenAIResponsesOptions } from "./providers/openai-responses";
14
15
  import type { AssistantMessageEventStream } from "./utils/event-stream";
15
16
  import type { FallbackAttemptToken, TransportFailureFacts } from "./utils/fallback-transport";
16
17
  export type { AssistantMessageEventStream } from "./utils/event-stream";
17
- export type KnownApi = "openai-completions" | "openai-responses" | "openai-codex-responses" | "azure-openai-responses" | "anthropic-messages" | "bedrock-converse-stream" | "google-generative-ai" | "google-gemini-cli" | "google-vertex" | "ollama-chat" | "cursor-agent";
18
+ export type KnownApi = "openai-completions" | "openai-responses" | "openai-codex-responses" | "azure-openai-responses" | "anthropic-messages" | "bedrock-converse-stream" | "google-generative-ai" | "google-gemini-cli" | "google-vertex" | "ollama-chat" | "cursor-agent" | "kiro-codewhisperer-stream";
18
19
  export type Api = KnownApi | (string & {});
19
20
  export interface ApiOptionsMap {
20
21
  "anthropic-messages": AnthropicOptions;
@@ -28,6 +29,7 @@ export interface ApiOptionsMap {
28
29
  "google-vertex": GoogleVertexOptions;
29
30
  "ollama-chat": OllamaChatOptions;
30
31
  "cursor-agent": CursorOptions;
32
+ "kiro-codewhisperer-stream": KiroCodeWhispererOptions;
31
33
  }
32
34
  export type OptionsForApi<TApi extends Api> = StreamOptions | (TApi extends keyof ApiOptionsMap ? ApiOptionsMap[TApi] : never);
33
35
  /** Canonical thinking transport used by a model. */
@@ -51,7 +53,7 @@ export interface ThinkingConfig {
51
53
  /** Provider-specific transport used to encode the selected effort. */
52
54
  mode: ThinkingControlMode;
53
55
  }
54
- export declare const KNOWN_PROVIDERS: readonly ["alibaba-token-plan", "amazon-bedrock", "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", "together", "venice", "vllm", "xiaomi", "xiaomi-token-plan-sgp", "xiaomi-token-plan-ams", "xiaomi-token-plan-cn", "zenmux", "lm-studio"];
56
+ 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", "together", "venice", "vllm", "xiaomi", "xiaomi-token-plan-sgp", "xiaomi-token-plan-ams", "xiaomi-token-plan-cn", "zenmux", "lm-studio"];
55
57
  export type KnownProvider = (typeof KNOWN_PROVIDERS)[number];
56
58
  export declare function isKnownProvider(provider: string): provider is KnownProvider;
57
59
  export type Provider = KnownProvider | string;
@@ -381,6 +383,16 @@ export interface ToolCall {
381
383
  * rejects the call with a retryable error instead.
382
384
  */
383
385
  incompleteArguments?: boolean;
386
+ /**
387
+ * Set when the provider saw the argument JSON spell a printable non-ASCII
388
+ * character as a `\uXXXX` escape instead of literal UTF-8. Hand-written hex
389
+ * is where models mistype digits, and a mistyped nibble decodes to a
390
+ * different but equally valid character, so the decoded arguments cannot be
391
+ * verified or repaired after parsing. The agent loop treats such a turn as a
392
+ * sampling accident: managed runs discard and re-request it, and execution
393
+ * rejects the call rather than running on silently corrupted text.
394
+ */
395
+ escapedNonAsciiArguments?: boolean;
384
396
  }
385
397
  export interface Usage {
386
398
  /** Non-cached input tokens (matches the bucket the provider bills as new input). */
@@ -1,4 +1,23 @@
1
1
  export declare function repairJson(json: string): string;
2
+ /**
3
+ * First unnecessary `\uXXXX` escape in a JSON document, or `undefined` when the
4
+ * document contains none.
5
+ *
6
+ * "Unnecessary" means the escape encodes a character JSON can carry literally:
7
+ * any non-ASCII printable character. Control characters (< U+0020) MUST be
8
+ * escaped, and an unpaired surrogate CANNOT be written literally, so neither
9
+ * counts. A `\\uXXXX` sequence is a literal backslash followed by `u` — the
10
+ * intended source syntax when the model is writing code or a nested JSON
11
+ * document — and is skipped, which is why this scans the raw text with the same
12
+ * string/escape state machine as {@link repairJson} instead of using a regex.
13
+ *
14
+ * Models that spell non-ASCII text as hand-written hex instead of literal UTF-8
15
+ * mistype the digits, and every mistyped nibble silently decodes to a different
16
+ * but perfectly valid character (`\uc7a5` vs `\uc7a4`). The resulting arguments
17
+ * parse cleanly and cannot be repaired after the fact, so the escape itself is
18
+ * the only observable evidence that the payload is untrustworthy.
19
+ */
20
+ export declare function findUnnecessaryUnicodeEscape(json: string): string | undefined;
2
21
  export declare function parseJsonWithRepair<T>(json: string): T;
3
22
  /**
4
23
  * Attempts to parse potentially incomplete JSON during streaming.
@@ -0,0 +1,71 @@
1
+ import type { OAuthCredentials } from "./types";
2
+ interface StartDeviceAuthorizationResponse {
3
+ deviceCode: string;
4
+ userCode: string;
5
+ verificationUri: string;
6
+ verificationUriComplete?: string;
7
+ interval: number;
8
+ expiresIn: number;
9
+ }
10
+ interface CreateTokenSuccess {
11
+ accessToken: string;
12
+ tokenType: string;
13
+ expiresIn: number;
14
+ refreshToken?: string;
15
+ }
16
+ interface ClientRegistration {
17
+ clientId: string;
18
+ clientSecret: string;
19
+ expiresAt: number;
20
+ }
21
+ /**
22
+ * Register a public SSO OIDC client. Registration responses include an expiry
23
+ * timestamp (`clientSecretExpiresAt`); we cache until then to avoid re-registering
24
+ * on every login attempt.
25
+ *
26
+ * The SSO OIDC `RegisterClient` endpoint is public (no authentication required).
27
+ */
28
+ export declare function registerClient(region: string, startUrl: string, signal?: AbortSignal): Promise<ClientRegistration>;
29
+ /** Drop cached client registration — used by tests. */
30
+ export declare function clearClientRegistrationCache(): void;
31
+ /**
32
+ * Start device authorization. The SSO OIDC `StartDeviceAuthorization` endpoint
33
+ * is public (requires registered clientId/clientSecret, not SigV4).
34
+ */
35
+ export declare function startDeviceAuthorization(region: string, startUrl: string, registration: ClientRegistration, signal?: AbortSignal): Promise<StartDeviceAuthorizationResponse>;
36
+ /**
37
+ * Poll `CreateToken` until the user completes authorization or the device code
38
+ * expires. Handles `authorization_pending` (continue polling) and `slow_down`
39
+ * (increase interval) per the published SSO OIDC model.
40
+ */
41
+ export declare function pollForToken(region: string, registration: ClientRegistration, deviceCode: string, intervalSeconds: number, expiresInSeconds: number, signal?: AbortSignal): Promise<CreateTokenSuccess>;
42
+ /**
43
+ * Refresh an expired access token using the stored refresh token via
44
+ * `CreateToken` with `grantType: "refresh_token"`.
45
+ *
46
+ * Rotation is published behavior: the response includes a new `refreshToken`.
47
+ * If the server does not return a new one, the old refresh token is retained.
48
+ */
49
+ export declare function refreshKiroToken(credentials: OAuthCredentials): Promise<OAuthCredentials>;
50
+ export interface KiroLoginOptions {
51
+ onAuth: (url: string, instructions?: string) => void;
52
+ onPrompt: (prompt: {
53
+ message: string;
54
+ placeholder?: string;
55
+ allowEmpty?: boolean;
56
+ }) => Promise<string>;
57
+ onProgress?: (message: string) => void;
58
+ signal?: AbortSignal;
59
+ /** Override for tests. */
60
+ fetchImpl?: typeof globalThis.fetch;
61
+ }
62
+ export declare function loginKiro(options: KiroLoginOptions): Promise<OAuthCredentials>;
63
+ /**
64
+ * Attempt to import a cached SSO access token from `~/.aws/sso/cache/`.
65
+ * Returns the token if a valid (non-expired) one exists, otherwise undefined.
66
+ *
67
+ * This reuses the documented AWS CLI SSO cache location, not any third-party
68
+ * credential store.
69
+ */
70
+ export declare function importSsoCacheToken(): OAuthCredentials | undefined;
71
+ export {};
@@ -7,7 +7,7 @@ export type OAuthCredentials = {
7
7
  email?: string;
8
8
  accountId?: string;
9
9
  };
10
- export type OAuthProvider = "alibaba-token-plan" | "anthropic" | "bizrouter" | "mara" | "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" | "opengateway" | "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" | "opencodex" | "zai";
10
+ export type OAuthProvider = "kiro" | "alibaba-token-plan" | "anthropic" | "bizrouter" | "mara" | "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" | "opengateway" | "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" | "opencodex" | "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.13.1",
4
+ "version": "0.13.3",
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.13.1",
43
+ "@gajae-code/utils": "0.13.3",
44
44
  "openai": "^6.36.0",
45
45
  "partial-json": "^0.1.7",
46
46
  "zod": "4.4.3"
@@ -58,6 +58,8 @@ const GPT_5_6_PLUS_EFFORTS: readonly Effort[] = [Effort.Low, Effort.Medium, Effo
58
58
  const GPT_5_5_DEFAULT_EFFORT = Effort.XHigh;
59
59
  const KIMI_K3_EFFORTS: readonly Effort[] = [Effort.Low, Effort.High, Effort.Max];
60
60
  const DEEPSEEK_V4_FLASH_0731_EFFORTS: readonly Effort[] = [Effort.Low, Effort.High, Effort.Max];
61
+ const GROK_4_5_EFFORTS: readonly Effort[] = [Effort.Low, Effort.Medium, Effort.High];
62
+ const GROK_4_6_EFFORTS: readonly Effort[] = [Effort.Low, Effort.Medium, Effort.High, Effort.XHigh];
61
63
 
62
64
  const GPT_5_1_CODEX_MINI_EFFORTS: readonly Effort[] = [Effort.Medium, Effort.High];
63
65
  const CLOUDFLARE_AI_GATEWAY_BASE_URL = "https://gateway.ai.cloudflare.com/v1/<account>/<gateway>/anthropic";
@@ -206,10 +208,16 @@ export function refreshModelThinking<TApi extends Api>(model: ApiModel<TApi>): A
206
208
  export function applyGeneratedModelPolicies(models: ApiModel<Api>[]): void {
207
209
  for (let index = 0; index < models.length; index++) {
208
210
  const source = models[index]!;
211
+ if (source.provider === "xai" && (source.id === "grok-4.5" || source.id === "grok-4.6")) {
212
+ source.reasoning = true;
213
+ }
209
214
  if (source.provider === "alibaba-token-plan" && source.id === "deepseek-v4-flash-0731") {
210
215
  source.reasoning = true;
211
216
  source.name = "DeepSeek V4 Flash 0731";
212
217
  }
218
+ if (source.id.split("/").at(-1)?.toLowerCase() === "muse-spark-1.2") {
219
+ source.reasoning = true;
220
+ }
213
221
  const model = refreshModelThinking(source);
214
222
  applyGeneratedModelPolicy(model);
215
223
  models[index] = model;
@@ -471,6 +479,9 @@ function applyGeneratedModelPolicy(model: ApiModel<Api>): void {
471
479
  requiresReasoningContentForToolCalls: true,
472
480
  };
473
481
  }
482
+ if (model.provider === "xai" && (model.id === "grok-4.5" || model.id === "grok-4.6")) {
483
+ model.maxTokens = Math.min(model.maxTokens, 64_000);
484
+ }
474
485
  // MiniMax-M3's official Token Plan routes expose a 1M context window.
475
486
  // Scope the correction to the four first-class regional MiniMax routes
476
487
  // (canonical id plus the Anthropic Token Plan `[1m]` id); unrelated
@@ -664,6 +675,12 @@ function expandEffortRange(thinking: ThinkingConfig): readonly Effort[] {
664
675
  }
665
676
 
666
677
  function inferSupportedEfforts<TApi extends Api>(parsedModel: ParsedModel, model: ApiModel<TApi>): readonly Effort[] {
678
+ if (model.provider === "xai" && model.id === "grok-4.5") {
679
+ return GROK_4_5_EFFORTS;
680
+ }
681
+ if (model.provider === "xai" && model.id === "grok-4.6") {
682
+ return GROK_4_6_EFFORTS;
683
+ }
667
684
  if (model.provider === "kimi-code" && model.id === "k3") {
668
685
  return KIMI_K3_EFFORTS;
669
686
  }
@@ -730,6 +747,13 @@ function inferAnthropicSupportedEfforts<TApi extends Api>(
730
747
  }
731
748
 
732
749
  function inferFallbackEfforts<TApi extends Api>(model: ApiModel<TApi>): readonly Effort[] {
750
+ // Meta documents Muse Spark 1.2 as accepting the full minimal..xhigh
751
+ // reasoning range. Keep that capability provider-independent so runtime
752
+ // model discovery/merge cannot downgrade the bundled OpenRouter entry to
753
+ // the generic openai-completions ceiling of `high`.
754
+ if (model.id.split("/").at(-1)?.toLowerCase() === "muse-spark-1.2") {
755
+ return DEFAULT_REASONING_EFFORTS_WITH_XHIGH;
756
+ }
733
757
  if (model.api === "anthropic-messages") {
734
758
  return DEFAULT_REASONING_EFFORTS_WITH_XHIGH;
735
759
  }