@gajae-code/ai 0.4.1 → 0.4.2

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.4.2] - 2026-06-09
6
+
7
+ ### Fixed
8
+
9
+ - Treated `gpt-5.5` as a 400K-context model wherever context caps / auto-promote thresholds are resolved, so a ~272K session is no longer considered over-cap and no longer demotes to `gpt-5.4`. Pinned the OpenAI Codex `gpt-5.5` context window to 400K and removed its `gpt-5.4` promotion target (the smaller window made it a demotion) ([#428](https://github.com/Yeachan-Heo/gajae-code/issues/428)).
10
+
5
11
  ## [0.4.0] - 2026-06-06
6
12
 
7
13
  ### Added
@@ -40,7 +40,9 @@ export declare function applyGeneratedModelPolicies(models: ApiModel<Api>[]): vo
40
40
  * When a model's context is exhausted, the agent can promote to a sibling
41
41
  * model with a larger context window on the same provider:
42
42
  * - `OpenAI code backend-spark` variants promote to `gpt-5.5`.
43
- * - `gpt-5.5` (270K input) promotes to `gpt-5.4` (1M input).
43
+ *
44
+ * `gpt-5.5` itself is a 400K-context model and is not demoted to `gpt-5.4`
45
+ * (which has a smaller window), so it has no promotion target.
44
46
  */
45
47
  export declare function linkOpenAIPromotionTargets(models: ApiModel<Api>[]): void;
46
48
  /**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@gajae-code/ai",
4
- "version": "0.4.1",
4
+ "version": "0.4.2",
5
5
  "description": "Unified LLM API with automatic model discovery and provider configuration",
6
6
  "homepage": "https://gaebal-gajae.dev",
7
7
  "author": "Yeachan-Heo",
@@ -43,7 +43,7 @@
43
43
  "dependencies": {
44
44
  "@anthropic-ai/sdk": "^0.94.0",
45
45
  "@bufbuild/protobuf": "^2.12.0",
46
- "@gajae-code/utils": "0.4.1",
46
+ "@gajae-code/utils": "0.4.2",
47
47
  "openai": "^6.36.0",
48
48
  "partial-json": "^0.1.7",
49
49
  "zod": "4.4.3"
@@ -195,7 +195,9 @@ export function applyGeneratedModelPolicies(models: ApiModel<Api>[]): void {
195
195
  * When a model's context is exhausted, the agent can promote to a sibling
196
196
  * model with a larger context window on the same provider:
197
197
  * - `OpenAI code backend-spark` variants promote to `gpt-5.5`.
198
- * - `gpt-5.5` (270K input) promotes to `gpt-5.4` (1M input).
198
+ *
199
+ * `gpt-5.5` itself is a 400K-context model and is not demoted to `gpt-5.4`
200
+ * (which has a smaller window), so it has no promotion target.
199
201
  */
200
202
  export function linkOpenAIPromotionTargets(models: ApiModel<Api>[]): void {
201
203
  for (const candidate of models) {
@@ -204,8 +206,6 @@ export function linkOpenAIPromotionTargets(models: ApiModel<Api>[]): void {
204
206
  let targetId: string | undefined;
205
207
  if (parsedCandidate.variant === "codex-spark") {
206
208
  targetId = "gpt-5.5";
207
- } else if (parsedCandidate.variant === "base" && semverEqual(parsedCandidate.version, "5.5")) {
208
- targetId = "gpt-5.4";
209
209
  } else {
210
210
  continue;
211
211
  }
@@ -430,7 +430,22 @@ function inferGeneratedApplyPatchToolType(
430
430
  return undefined;
431
431
  }
432
432
 
433
+ function applyGpt55ContextWindow(model: ApiModel<Api>, parsedModel: OpenAIModel): boolean {
434
+ // gpt-5.5 is a 400K-context model. OpenAI code backend discovery can omit the
435
+ // context window, falling back to the 272K default, which incorrectly trips
436
+ // context-cap / auto-promote thresholds (a ~272K session would look over-cap
437
+ // and demote to gpt-5.4). Pin gpt-5.5 to its true 400K window.
438
+ if (parsedModel.variant === "base" && semverEqual(parsedModel.version, "5.5")) {
439
+ model.contextWindow = 400000;
440
+ return true;
441
+ }
442
+ return false;
443
+ }
444
+
433
445
  function applyOpenAICatalogPolicy(model: ApiModel<Api>, parsedModel: OpenAIModel): void {
446
+ if (applyGpt55ContextWindow(model, parsedModel)) {
447
+ return;
448
+ }
434
449
  // OpenAI code backend models: 400K figure includes output budget; input window is 272K.
435
450
  if (parsedModel.variant.startsWith("codex") && parsedModel.variant !== "codex-spark") {
436
451
  model.contextWindow = 272000;
package/src/models.json CHANGED
@@ -34660,7 +34660,7 @@
34660
34660
  },
34661
34661
  "minimax-m3": {
34662
34662
  "id": "minimax-m3",
34663
- "name": "MiniMax M3",
34663
+ "name": "MiniMax-M3",
34664
34664
  "api": "anthropic-messages",
34665
34665
  "provider": "minimax",
34666
34666
  "baseUrl": "https://api.minimax.io/anthropic",
@@ -34855,7 +34855,7 @@
34855
34855
  },
34856
34856
  "minimax-m3": {
34857
34857
  "id": "minimax-m3",
34858
- "name": "MiniMax M3",
34858
+ "name": "MiniMax-M3",
34859
34859
  "api": "anthropic-messages",
34860
34860
  "provider": "minimax-cn",
34861
34861
  "baseUrl": "https://api.minimaxi.com/anthropic",
@@ -35122,7 +35122,7 @@
35122
35122
  },
35123
35123
  "minimax-m3": {
35124
35124
  "id": "minimax-m3",
35125
- "name": "MiniMax M3",
35125
+ "name": "MiniMax-M3",
35126
35126
  "api": "openai-completions",
35127
35127
  "provider": "minimax-code",
35128
35128
  "baseUrl": "https://api.minimax.io/v1",
@@ -35395,7 +35395,7 @@
35395
35395
  },
35396
35396
  "minimax-m3": {
35397
35397
  "id": "minimax-m3",
35398
- "name": "MiniMax M3",
35398
+ "name": "MiniMax-M3",
35399
35399
  "api": "openai-completions",
35400
35400
  "provider": "minimax-code-cn",
35401
35401
  "baseUrl": "https://api.minimaxi.com/v1",
@@ -53264,7 +53264,7 @@
53264
53264
  "cacheRead": 0.5,
53265
53265
  "cacheWrite": 0
53266
53266
  },
53267
- "contextWindow": 272000,
53267
+ "contextWindow": 400000,
53268
53268
  "maxTokens": 128000,
53269
53269
  "preferWebsockets": true,
53270
53270
  "priority": 9,
@@ -53274,8 +53274,7 @@
53274
53274
  "maxLevel": "xhigh",
53275
53275
  "defaultLevel": "xhigh"
53276
53276
  },
53277
- "applyPatchToolType": "freeform",
53278
- "contextPromotionTarget": "openai-codex/gpt-5.4"
53277
+ "applyPatchToolType": "freeform"
53279
53278
  }
53280
53279
  },
53281
53280
  "opencode": {
@@ -157,10 +157,14 @@ export async function* iterateWithIdleTimeout<T>(
157
157
  activeTimeoutMs = firstItemTimeoutMs;
158
158
  } else if (options.idleTimeoutMs !== undefined && options.idleTimeoutMs > 0) {
159
159
  activeTimeoutMs = options.idleTimeoutMs - (Date.now() - lastProgressAt);
160
- if (activeTimeoutMs <= 0) {
161
- options.onIdle?.();
162
- closeIterator();
163
- throw new Error(options.errorMessage);
160
+ // The idle deadline may already have elapsed because the *consumer*
161
+ // was slow, not because the provider stalled — and the next item may
162
+ // already be buffered and ready to deliver. Clamp to 0 instead of
163
+ // throwing eagerly so the next() race below still gets a chance to
164
+ // win (it settles on a microtask, ahead of the 0ms timer). Only a
165
+ // genuinely hung iterator loses that race and surfaces as a stall.
166
+ if (activeTimeoutMs < 0) {
167
+ activeTimeoutMs = 0;
164
168
  }
165
169
  }
166
170
 
@@ -177,7 +181,10 @@ export async function* iterateWithIdleTimeout<T>(
177
181
 
178
182
  let timer: NodeJS.Timeout | undefined;
179
183
  let resolveTimeout: ((value: { kind: "timeout" }) => void) | undefined;
180
- const enforceTimeout = !noTimeoutEnforced && activeTimeoutMs !== undefined && activeTimeoutMs > 0;
184
+ const enforceTimeout =
185
+ !noTimeoutEnforced &&
186
+ activeTimeoutMs !== undefined &&
187
+ (awaitingFirstItem ? activeTimeoutMs > 0 : activeTimeoutMs >= 0);
181
188
  if (enforceTimeout) {
182
189
  const { promise, resolve } = Promise.withResolvers<{ kind: "timeout" }>();
183
190
  resolveTimeout = resolve;