@linxiraos/pi-catalog 1.1.6 → 1.1.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,10 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [1.1.7] - 2026-09-01
6
+
7
+ - 同步上游 OMP v18.0.11(`b8ce33a58911c26bed1d84f0db9a5e2e727c49a2`)。
8
+
5
9
  ## [1.1.6] - 2026-08-30
6
10
 
7
11
  - 同步上游 OMP v18.0.9(`cc14e04f075d`)。
@@ -21,3 +21,9 @@ export interface CursorModelDiscoveryOptions {
21
21
  * Returns `[]` only when the endpoint responds successfully with no usable models.
22
22
  */
23
23
  export declare function fetchCursorUsableModels(options: CursorModelDiscoveryOptions): Promise<ModelSpec<"cursor-agent">[] | null>;
24
+ /**
25
+ * Resolves input modalities from a bundled reference when available, except
26
+ * for Cursor-only families whose image support is known independently. Without
27
+ * a reference, native multimodal families fall back to id classification.
28
+ */
29
+ export declare function resolveCursorInput(id: string, referenceInput?: ("text" | "image")[]): ("text" | "image")[];
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@linxiraos/pi-catalog",
4
- "version": "1.1.6",
4
+ "version": "1.1.7",
5
5
  "description": "Model catalog for omp: bundled model database, provider discovery descriptors, model identity, classification, and equivalence",
6
6
  "homepage": "https://linxira-os.github.io/zeta/",
7
7
  "author": "Can Boluk",
@@ -34,11 +34,11 @@
34
34
  "gen:proto": "bun scripts/generate-protocols.ts"
35
35
  },
36
36
  "dependencies": {
37
- "@linxiraos/pi-omptype": "1.1.6",
38
- "@linxiraos/pi-utils": "1.1.6"
37
+ "@linxiraos/pi-omptype": "1.1.7",
38
+ "@linxiraos/pi-utils": "1.1.7"
39
39
  },
40
40
  "devDependencies": {
41
- "@linxiraos/pi-ai": "1.1.6",
41
+ "@linxiraos/pi-ai": "1.1.7",
42
42
  "@types/bun": "^1.3.14"
43
43
  },
44
44
  "engines": {
@@ -33,14 +33,20 @@ const CURSOR_KIMI_K3_BARE_ID_PATTERN = /(^|\/)k3$/i;
33
33
  * reasoning models whose effort is carried in the per-tier sibling id.
34
34
  * `GetUsableModels` ships no `thinkingDetails` and the bundled references read
35
35
  * `reasoning: false`, so classification falls back to the id. The non-reasoning
36
- * `grok-code-*` coding models lack the version digit and stay out.
36
+ * `grok-code-fast-*` family deliberately stays out.
37
37
  */
38
38
  const CURSOR_GROK_REASONING_ID_PATTERN = /^cursor-grok-\d/i;
39
39
 
40
+ /**
41
+ * Cursor-only families verified to accept `selectedImages` even though
42
+ * `GetUsableModels` does not advertise input modalities.
43
+ */
44
+ const CURSOR_GROK_4_MULTIMODAL_ID_PATTERN = /^cursor-grok-4(?:[.:_-]|$)/i;
45
+ const CURSOR_COMPOSER_25_MULTIMODAL_ID_PATTERN = /^composer-2\.5(?:[.:_-]|$)/i;
46
+
40
47
  /**
41
48
  * Model-id families whose native catalogs (anthropic, openai/openai-codex,
42
- * google) are multimodal. Cursor-only or text-only families (`composer-*`,
43
- * `grok-code-*`) intentionally stay outside this pattern.
49
+ * google) are multimodal. Cursor-only verified families are handled separately.
44
50
  */
45
51
  const CURSOR_MULTIMODAL_ID_PATTERN = /claude|gemini|gpt-|codex/;
46
52
 
@@ -319,6 +325,7 @@ function normalizeCursorModel(
319
325
  name,
320
326
  baseUrl: baseUrlOverride ?? reference.baseUrl,
321
327
  reasoning,
328
+ input: resolveCursorInput(id, reference.input),
322
329
  contextWindow: resolveCursorContextWindow(details, id, reference.contextWindow),
323
330
  cursorMaxMode: details.maxMode,
324
331
  };
@@ -330,7 +337,7 @@ function normalizeCursorModel(
330
337
  provider: "cursor",
331
338
  baseUrl: baseUrlOverride ?? CURSOR_DEFAULT_BASE_URL,
332
339
  reasoning,
333
- input: inferInputFromCursorId(id),
340
+ input: resolveCursorInput(id),
334
341
  cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
335
342
  contextWindow: resolveCursorContextWindow(details, id, DEFAULT_CONTEXT_WINDOW),
336
343
  maxTokens: DEFAULT_MAX_TOKENS,
@@ -394,14 +401,22 @@ function pickModelDisplayName(model: CursorModelDetailsValue, fallbackId: string
394
401
  }
395
402
 
396
403
  /**
397
- * Infers input modalities for Cursor models without a bundled reference.
398
- *
399
- * `GetUsableModels` carries no per-model modality metadata, so classification
400
- * falls back to the model family: families that are multimodal in OMP's own
401
- * native catalogs accept images, everything else stays text-only. Mirrors
402
- * `inferInputFromGeminiId` in ./gemini.ts.
404
+ * Resolves input modalities from a bundled reference when available, except
405
+ * for Cursor-only families whose image support is known independently. Without
406
+ * a reference, native multimodal families fall back to id classification.
403
407
  */
404
- function inferInputFromCursorId(id: string): ("text" | "image")[] {
408
+ export function resolveCursorInput(id: string, referenceInput?: ("text" | "image")[]): ("text" | "image")[] {
409
+ if (
410
+ isKimiK3ModelId(id) ||
411
+ CURSOR_KIMI_K3_BARE_ID_PATTERN.test(id) ||
412
+ CURSOR_GROK_4_MULTIMODAL_ID_PATTERN.test(id) ||
413
+ CURSOR_COMPOSER_25_MULTIMODAL_ID_PATTERN.test(id)
414
+ ) {
415
+ return ["text", "image"];
416
+ }
417
+ if (referenceInput) {
418
+ return referenceInput;
419
+ }
405
420
  if (CURSOR_MULTIMODAL_ID_PATTERN.test(id.toLowerCase())) {
406
421
  return ["text", "image"];
407
422
  }
@@ -429,11 +429,14 @@ function getModelDefinedEfforts<TApi extends Api>(
429
429
  if (isDeepseekV4FlashModelId(spec.id)) {
430
430
  return LOW_HIGH_MAX_REASONING_EFFORTS;
431
431
  }
432
- if (bareModelId(spec.id).toLowerCase().includes("deepseek-v4")) {
432
+ const bareId = bareModelId(spec.id).toLowerCase();
433
+ if (bareId.includes("deepseek-v4")) {
433
434
  if (!isOpenRouterThinkingFormat(compat)) {
434
435
  return LOW_HIGH_MAX_REASONING_EFFORTS;
435
436
  }
436
- return bareModelId(spec.id).toLowerCase() === "deepseek-v4-pro-0813"
437
+ // Route suffixes are OpenRouter-specific and not part of the model identity.
438
+ const openRouterBaseId = bareId.replace(/:[^:]+$/, "");
439
+ return openRouterBaseId === "deepseek-v4-pro-0813"
437
440
  ? LOW_HIGH_MAX_REASONING_EFFORTS
438
441
  : HIGH_ONLY_REASONING_EFFORTS;
439
442
  }