@gajae-code/ai 0.11.5 → 0.11.7

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,12 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.11.7] - 2026-07-22
6
+
7
+ ### Changed
8
+
9
+ - Replaced the `alibaba-coding-plan` provider with first-class `alibaba-token-plan` support. The `/login` OAuth list, provider descriptor, model manager, models.dev descriptor, and bundled `models.json` now target the maintained Alibaba Token Plan endpoint (`https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1`, env `ALIBABA_TOKEN_PLAN_API_KEY`) and validate logins against `deepseek-v4-pro`. The retired `alibaba-coding-plan` provider pointed at `coding-intl.dashscope.aliyuncs.com`, which rejected real token-plan keys with 401 and was the only Alibaba entry exposed in `/login`.
10
+
5
11
  ## [0.11.4] - 2026-07-20
6
12
 
7
13
  ### Added
@@ -32,6 +38,9 @@
32
38
  ### Fixed
33
39
 
34
40
  - Fixed frequent `Request blocked (code=invalid_prompt)` failures on gpt-5.6 (Sol/Terra/Luna) subagent, default-agent, and compaction turns (ref openai/codex#32028, oh-my-pi#5184). Leaked Harmony control-token markers (e.g. `<|channel|>analysis`) were only neutralized on the replayed-history payload path, so markers in assistant reasoning summaries, live-converted message/tool-output text, and user-authored content reached the OpenAI Responses and OpenAI-codex-responses transports verbatim and wedged the session (the poisoned item was re-sent every turn). Both transports now neutralize reserved control tokens across the entire outgoing `input` array at the request boundary via an idempotent zero-width-space insertion that keeps the text human-readable.
41
+ ### Fixed
42
+
43
+ - Fixed Fable 5 adaptive thinking being billed but never displayed: model discovery now classifies `claude-fable-*` as `anthropic-adaptive` (was cached as `budget`, sending `enabled`+`budget_tokens` that Fable answers with signature-only thinking), and `supportsAdaptiveThinkingDisplay` opts Fable into `display: "summarized"` on both Anthropic Messages and Bedrock Converse transports (#2791).
35
44
 
36
45
  ## [0.10.0] - 2026-07-12
37
46
  ### Fixed
@@ -116,11 +116,11 @@ export interface KiloModelManagerConfig {
116
116
  baseUrl?: string;
117
117
  }
118
118
  export declare function kiloModelManagerOptions(config?: KiloModelManagerConfig): ModelManagerOptions<"openai-completions">;
119
- export interface AlibabaCodingPlanModelManagerConfig {
119
+ export interface AlibabaTokenPlanModelManagerConfig {
120
120
  apiKey?: string;
121
121
  baseUrl?: string;
122
122
  }
123
- export declare function alibabaCodingPlanModelManagerOptions(config?: AlibabaCodingPlanModelManagerConfig): ModelManagerOptions<"openai-completions">;
123
+ export declare function alibabaTokenPlanModelManagerOptions(config?: AlibabaTokenPlanModelManagerConfig): ModelManagerOptions<"openai-completions">;
124
124
  export interface VercelAiGatewayModelManagerConfig {
125
125
  apiKey?: string;
126
126
  baseUrl?: string;
@@ -51,7 +51,7 @@ export interface ThinkingConfig {
51
51
  /** Provider-specific transport used to encode the selected effort. */
52
52
  mode: ThinkingControlMode;
53
53
  }
54
- export type KnownProvider = "alibaba-coding-plan" | "amazon-bedrock" | "azure-openai" | "anthropic" | "google" | "google-gemini-cli" | "google-antigravity" | "google-vertex" | "openai" | "openai-codex" | "kimi-code" | "minimax-code" | "minimax-code-cn" | "github-copilot" | "fireworks" | "firepass" | "fugu" | "gitlab-duo" | "cursor" | "deepseek" | "deepinfra" | "xai" | "groq" | "cerebras" | "openrouter" | "kilo" | "vercel-ai-gateway" | "zai" | "glm-zcode" | "mistral" | "minimax" | "opencode-go" | "opencode-zen" | "synthetic" | "cloudflare-ai-gateway" | "huggingface" | "litellm" | "moonshot" | "nvidia" | "nanogpt" | "ollama" | "ollama-cloud" | "qianfan" | "qwen-portal" | "together" | "venice" | "vllm" | "xiaomi" | "xiaomi-token-plan-sgp" | "xiaomi-token-plan-ams" | "xiaomi-token-plan-cn" | "zenmux" | "lm-studio";
54
+ export type KnownProvider = "alibaba-token-plan" | "amazon-bedrock" | "azure-openai" | "anthropic" | "google" | "google-gemini-cli" | "google-antigravity" | "google-vertex" | "openai" | "openai-codex" | "kimi-code" | "minimax-code" | "minimax-code-cn" | "github-copilot" | "fireworks" | "firepass" | "fugu" | "gitlab-duo" | "cursor" | "deepseek" | "deepinfra" | "xai" | "groq" | "cerebras" | "openrouter" | "kilo" | "vercel-ai-gateway" | "zai" | "glm-zcode" | "mistral" | "minimax" | "opencode-go" | "opencode-zen" | "synthetic" | "cloudflare-ai-gateway" | "huggingface" | "litellm" | "moonshot" | "nvidia" | "nanogpt" | "ollama" | "ollama-cloud" | "qianfan" | "qwen-portal" | "together" | "venice" | "vllm" | "xiaomi" | "xiaomi-token-plan-sgp" | "xiaomi-token-plan-ams" | "xiaomi-token-plan-cn" | "zenmux" | "lm-studio";
55
55
  export type Provider = KnownProvider | string;
56
56
  import type { Effort } from "./model-thinking";
57
57
  /** Token budgets for each thinking level (token-based providers only) */
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Alibaba Token Plan login flow.
3
+ *
4
+ * Alibaba Token Plan provides OpenAI-compatible models via
5
+ * https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1.
6
+ *
7
+ * This is not OAuth - it's a simple API key flow:
8
+ * 1. Open browser to Alibaba Cloud Model Studio console
9
+ * 2. User copies their API key
10
+ * 3. User pastes the API key into the CLI
11
+ */
12
+ import type { OAuthController } from "./types";
13
+ /**
14
+ * Login to Alibaba Token Plan.
15
+ *
16
+ * Opens browser to API keys page, prompts user to paste their API key.
17
+ * Returns the API key directly (not OAuthCredentials - this isn't OAuth).
18
+ */
19
+ export declare function loginAlibabaTokenPlan(options: OAuthController): Promise<string>;
@@ -7,7 +7,7 @@ export type OAuthCredentials = {
7
7
  email?: string;
8
8
  accountId?: string;
9
9
  };
10
- export type OAuthProvider = "alibaba-coding-plan" | "anthropic" | "cerebras" | "cloudflare-ai-gateway" | "cursor" | "deepseek" | "deepinfra" | "fireworks" | "firepass" | "fugu" | "github-copilot" | "google-gemini-cli" | "google-antigravity" | "gitlab-duo" | "huggingface" | "kimi-code" | "kilo" | "kagi" | "litellm" | "lm-studio" | "minimax-code" | "minimax-code-cn" | "moonshot" | "nvidia" | "nanogpt" | "ollama" | "ollama-cloud" | "openai-codex" | "openai-codex-device" | "opencode-go" | "opencode-zen" | "parallel" | "perplexity" | "qianfan" | "qwen-portal" | "synthetic" | "tavily" | "together" | "venice" | "vercel-ai-gateway" | "vllm" | "xai" | "glm-zcode" | "xiaomi" | "xiaomi-token-plan-sgp" | "xiaomi-token-plan-ams" | "xiaomi-token-plan-cn" | "zenmux" | "zai";
10
+ export type OAuthProvider = "alibaba-token-plan" | "anthropic" | "cerebras" | "cloudflare-ai-gateway" | "cursor" | "deepseek" | "deepinfra" | "fireworks" | "firepass" | "fugu" | "github-copilot" | "google-gemini-cli" | "google-antigravity" | "gitlab-duo" | "huggingface" | "kimi-code" | "kilo" | "kagi" | "litellm" | "lm-studio" | "minimax-code" | "minimax-code-cn" | "moonshot" | "nvidia" | "nanogpt" | "ollama" | "ollama-cloud" | "openai-codex" | "openai-codex-device" | "opencode-go" | "opencode-zen" | "parallel" | "perplexity" | "qianfan" | "qwen-portal" | "synthetic" | "tavily" | "together" | "venice" | "vercel-ai-gateway" | "vllm" | "xai" | "glm-zcode" | "xiaomi" | "xiaomi-token-plan-sgp" | "xiaomi-token-plan-ams" | "xiaomi-token-plan-cn" | "zenmux" | "zai";
11
11
  export type OAuthProviderId = OAuthProvider | (string & {});
12
12
  export type OAuthPrompt = {
13
13
  message: string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@gajae-code/ai",
4
- "version": "0.11.5",
4
+ "version": "0.11.7",
5
5
  "description": "Unified LLM API with automatic model discovery and provider configuration",
6
6
  "homepage": "https://gajae-code.com",
7
7
  "author": "Yeachan-Heo and Gajae Code Contributors",
@@ -40,7 +40,7 @@
40
40
  "dependencies": {
41
41
  "@anthropic-ai/sdk": "^0.94.0",
42
42
  "@bufbuild/protobuf": "^2.12.0",
43
- "@gajae-code/utils": "0.11.5",
43
+ "@gajae-code/utils": "0.11.7",
44
44
  "openai": "^6.36.0",
45
45
  "partial-json": "^0.1.7",
46
46
  "zod": "4.4.3"
@@ -1587,9 +1587,9 @@ export class AuthStorage {
1587
1587
  });
1588
1588
  break;
1589
1589
  }
1590
- case "alibaba-coding-plan": {
1591
- const { loginAlibabaCodingPlan } = await import("./utils/oauth/alibaba-coding-plan");
1592
- const apiKey = await loginAlibabaCodingPlan(ctrl);
1590
+ case "alibaba-token-plan": {
1591
+ const { loginAlibabaTokenPlan } = await import("./utils/oauth/alibaba-token-plan");
1592
+ const apiKey = await loginAlibabaTokenPlan(ctrl);
1593
1593
  await saveApiKeyCredential(apiKey);
1594
1594
  return;
1595
1595
  }
@@ -62,7 +62,7 @@ type SemVer = {
62
62
  };
63
63
 
64
64
  type GeminiKind = "pro" | "flash";
65
- type AnthropicKind = "opus" | "sonnet";
65
+ type AnthropicKind = "opus" | "sonnet" | "fable";
66
66
  type OpenAIVariant =
67
67
  | "base"
68
68
  | "codex"
@@ -638,6 +638,11 @@ function inferAnthropicSupportedEfforts<TApi extends Api>(
638
638
  (model.api === "anthropic-messages" || model.api === "bedrock-converse-stream") &&
639
639
  semverGte(parsedModel.version, "4.6")
640
640
  ) {
641
+ if (parsedModel.kind === "fable") {
642
+ // Fable exposes Anthropic's Messages-only xhigh preset; Bedrock
643
+ // Converse lacks it (same split as Opus 4.7+ below).
644
+ return model.api === "anthropic-messages" ? DEFAULT_REASONING_EFFORTS_WITH_XHIGH : DEFAULT_REASONING_EFFORTS;
645
+ }
641
646
  if (parsedModel.kind !== "opus") return DEFAULT_REASONING_EFFORTS;
642
647
  return anthropicModelHasRealXHighEffort(model)
643
648
  ? DEFAULT_REASONING_EFFORTS_WITH_XHIGH_AND_MAX
@@ -697,7 +702,10 @@ function inferThinkingControlMode<TApi extends Api>(
697
702
 
698
703
  case "bedrock-converse-stream":
699
704
  if (parsedModel.family === "anthropic") {
700
- if (semverGte(parsedModel.version, "4.6") && parsedModel.kind === "opus") {
705
+ if (
706
+ semverGte(parsedModel.version, "4.6") &&
707
+ (parsedModel.kind === "opus" || parsedModel.kind === "fable")
708
+ ) {
701
709
  return "anthropic-adaptive";
702
710
  }
703
711
  if (semverGte(parsedModel.version, "4.5")) {
@@ -737,7 +745,7 @@ function parseGeminiModel(modelId: string): GeminiModel | null {
737
745
  }
738
746
 
739
747
  function parseAnthropicModel(modelId: string): AnthropicModel | null {
740
- const match = /claude-(opus|sonnet)-(\d{1,2}(?:[.-]\d{1,2}){0,2})\b/.exec(modelId);
748
+ const match = /claude-(opus|sonnet|fable)-(\d{1,2}(?:[.-]\d{1,2}){0,2})\b/.exec(modelId);
741
749
  if (!match) {
742
750
  return null;
743
751
  }