@oh-my-pi/pi-catalog 18.4.1 → 18.4.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,39 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [18.4.3] - 2026-09-28
6
+
7
+ ### Added
8
+
9
+ - Added Command Code's typesafe/jev decision model for the judge role ([#13666](https://github.com/can1357/oh-my-pi/pull/13666) by [@riicodespretty](https://github.com/riicodespretty))
10
+ - Added Command Code's DeepSeek V4.1 Flash Fast model ([#13666](https://github.com/can1357/oh-my-pi/pull/13666) by [@riicodespretty](https://github.com/riicodespretty))
11
+ - Added Command Code's Claude Sonnet 5.5 model ([#13666](https://github.com/can1357/oh-my-pi/pull/13666) by [@riicodespretty](https://github.com/riicodespretty))
12
+ - Added `trust-forbidden=#true` for optional login `validate` checks, so a key check that answers 403 keeps the pasted key instead of rejecting it ([#13666](https://github.com/can1357/oh-my-pi/pull/13666) by [@riicodespretty](https://github.com/riicodespretty))
13
+ - `hosted-image #false` and `image-model #false` now remove a hosted image flag or image model that a class rule grants ([#13666](https://github.com/can1357/oh-my-pi/pull/13666) by [@riicodespretty](https://github.com/riicodespretty))
14
+ - Added support for Claude Sonnet 5.5 model with image and text inputs
15
+ - Added new compatibility rules for Anthropic Sonnet family enabling mid‑conversation system features and disabling forced tool choice
16
+ - Added the `web-search-model`, `hosted-image`, and `image-model` catalog axes (`Model.webSearchModel`, `hostedImage`, `imageModel`). `web-search` now comes from the model's lineage and API (GPT-5+ Responses, Claude 4+ Messages, Gemini 2+), so proxies and gateways that expose these models inherit it.
17
+
18
+ ### Changed
19
+
20
+ - Renamed the Codex image model `openai-codex/gpt-image-1` to `openai-codex/gpt-image-2` to match what the Codex backend runs (`gpt-image-2-codex`); saved `openai-codex/gpt-image-1` selectors resolve to the new id.
21
+ - Routed Command Code's 10 GPT models through the OpenAI Responses API ([#13666](https://github.com/can1357/oh-my-pi/pull/13666) by [@riicodespretty](https://github.com/riicodespretty))
22
+ - Command Code login now rejects a key that Command Code answers with 401; a 403 or an unreachable check keeps the pasted key, as the Command Code CLI does ([#13666](https://github.com/can1357/oh-my-pi/pull/13666) by [@riicodespretty](https://github.com/riicodespretty))
23
+ - Command Code GPT models no longer advertise hosted image generation or the `gpt-image-2` image model, which Command Code does not serve ([#13666](https://github.com/can1357/oh-my-pi/pull/13666) by [@riicodespretty](https://github.com/riicodespretty))
24
+ - Command Code Muse Spark models drop the minimal thinking level, and Muse Spark 1.3 adds max, matching the Command Code CLI ([#13666](https://github.com/can1357/oh-my-pi/pull/13666) by [@riicodespretty](https://github.com/riicodespretty))
25
+ - Changed Command Code prices to match the Command Code CLI: DeepSeek V4 Flash and V4 Flash Vision Exp drop from 0.22 / 0.66 to 0.15 / 0.6 per 1M tokens, Step 3.5 Flash input drops from 0.1 to 0.09, and LongCat 2.0 is no longer shown as free (0.3 / 1.2) ([#13666](https://github.com/can1357/oh-my-pi/pull/13666) by [@riicodespretty](https://github.com/riicodespretty))
26
+
27
+ ### Fixed
28
+
29
+ - Fixed missing thinking levels, image input, and prices for Command Code models ([#13666](https://github.com/can1357/oh-my-pi/pull/13666) by [@riicodespretty](https://github.com/riicodespretty))
30
+
31
+ ## [18.4.2] - 2026-09-28
32
+
33
+ ### Changed
34
+
35
+ - Improved model cache invalidation efficiency by implementing deep equality checks on cached rows when database version signals change
36
+ - Optimized read-row cache by enabling granular cache-hit logic across concurrent connection handles
37
+
5
38
  ## [18.4.1] - 2026-09-28
6
39
 
7
40
  ### Added
@@ -350,12 +350,15 @@ export type CompiledAuthValidation = {
350
350
  maxTokensField?: "max_tokens" | "max_completion_tokens";
351
351
  maxTokens?: number;
352
352
  optional?: boolean;
353
+ /** With `optional`: a 403 also trusts the key; only a 401 rejects it. */
354
+ trustForbidden?: boolean;
353
355
  } | {
354
356
  kind: "anthropic-messages";
355
357
  label?: string;
356
358
  baseUrl: string;
357
359
  model: string;
358
360
  optional?: boolean;
361
+ trustForbidden?: boolean;
359
362
  } | {
360
363
  kind: "models-endpoint";
361
364
  label?: string;
@@ -365,6 +368,7 @@ export type CompiledAuthValidation = {
365
368
  /** Hook returning extra request headers (may throw a configuration error). */
366
369
  headersHook?: string;
367
370
  optional?: boolean;
371
+ trustForbidden?: boolean;
368
372
  };
369
373
  /** Paste-an-API-key login: optional browser hint, prompt, optional validation. */
370
374
  export interface CompiledApiKeyLogin {
@@ -648,7 +648,8 @@ export declare function modelsDevCatalogFallback(providerId: string, fetchImpl?:
648
648
  * `baseUrl` overrides the Provider API base path for testing; it is
649
649
  * normalized to the shared `/provider` root (a trailing `/v1` is stripped)
650
650
  * so Claude ids route to the Anthropic-compatible Messages endpoint at the
651
- * root while every other id uses chat completions under `/v1`.
651
+ * root, the ten GPT ids listed in the `api-routes` table to the Responses
652
+ * endpoint under `/v1`, and every other id to chat completions under `/v1`.
652
653
  */
653
654
  export interface CommandCodeModelManagerConfig {
654
655
  apiKey?: string;
@@ -659,12 +660,14 @@ export interface CommandCodeModelManagerConfig {
659
660
  * Builds the Command Code model manager: a mixed-protocol OpenAI-compatible
660
661
  * discovery client. The public `/v1/models` catalog is fetched once per
661
662
  * options instance; `mapModel` pins each row's transport from the
662
- * `api-routes` table (Claude ids to `anthropic-messages`, everything else to
663
- * `openai-completions`) and seeds neutral capability defaults. Reviewed
664
- * Command Code policy (effort ladders, pricing, limits, modalities) is
665
- * applied later by `buildModel` from `providers/commandcode.kdl` — the
666
- * mapper never inherits another provider's reasoning, rates, image support,
667
- * or context window.
663
+ * `api-routes` table (Claude ids to `anthropic-messages`, the listed GPT ids
664
+ * to `openai-responses`, everything else to `openai-completions`) and seeds
665
+ * neutral capability defaults. Reviewed Command Code policy (effort ladders,
666
+ * pricing, limits, modalities) is applied later by `buildModel` from
667
+ * `providers/commandcode.kdl` — the mapper never inherits another provider's
668
+ * reasoning, rates, image support, or context window. A successful fetch also
669
+ * appends the KDL `seed` rows (typesafe/jev), rebased onto the configured
670
+ * base, because `dynamicModelsAuthoritative` would prune `staticModels`.
668
671
  */
669
672
  export declare function commandCodeModelManagerOptions(config?: CommandCodeModelManagerConfig): ModelManagerOptions<Api>;
670
673
  /**
@@ -986,6 +986,12 @@ export interface Model<TApi extends Api = Api> {
986
986
  kind?: ModelKind;
987
987
  /** Grounding transport supported by this chat model. */
988
988
  webSearch?: WebSearchGrounding;
989
+ /** Cheaper same-provider model to run hosted web search in this model's place (model id or provider/id). */
990
+ webSearchModel?: string;
991
+ /** Whether this chat model can carry the Responses `image_generation` tool itself. */
992
+ hostedImage?: boolean;
993
+ /** Same-provider image model to generate images in this model's place (model id or provider/id). */
994
+ imageModel?: string;
989
995
  /**
990
996
  * Structured model identity resolved by the compat engine: vendor lineage
991
997
  * class, product family, and revision. Baked into models.json rows and
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oh-my-pi/pi-catalog",
3
- "version": "18.4.1",
3
+ "version": "18.4.3",
4
4
  "description": "Model catalog for omp: bundled model database, provider discovery descriptors, model identity, classification, and equivalence",
5
5
  "keywords": [
6
6
  "ai",
@@ -93,12 +93,12 @@
93
93
  "gen:proto": "bun scripts/generate-protocols.ts"
94
94
  },
95
95
  "dependencies": {
96
- "@oh-my-pi/omptype": "18.4.1",
97
- "@oh-my-pi/pi-utils": "18.4.1"
96
+ "@oh-my-pi/omptype": "18.4.3",
97
+ "@oh-my-pi/pi-utils": "18.4.3"
98
98
  },
99
99
  "devDependencies": {
100
100
  "@bgotink/kdl": "0.4.0",
101
- "@oh-my-pi/pi-ai": "18.4.1",
101
+ "@oh-my-pi/pi-ai": "18.4.3",
102
102
  "@types/bun": "^1.3.14"
103
103
  },
104
104
  "engines": {
package/src/build.ts CHANGED
@@ -87,6 +87,11 @@ function applyCatalogAssignments<TApi extends Api>(model: Model<TApi>, catalog:
87
87
  ) {
88
88
  model.webSearch = webSearch;
89
89
  }
90
+ if (typeof catalog.webSearchModel === "string") model.webSearchModel = catalog.webSearchModel;
91
+ if (catalog.hostedImage === true) model.hostedImage = true;
92
+ else if (catalog.hostedImage === false) delete model.hostedImage;
93
+ if (typeof catalog.imageModel === "string") model.imageModel = catalog.imageModel;
94
+ else if (catalog.imageModel === false) delete model.imageModel;
90
95
  const serviceTierCost = objectPayload(catalog.serviceTierCost);
91
96
  if (serviceTierCost !== undefined) {
92
97
  const flex = numberField(serviceTierCost, "flex");
@@ -343,6 +343,9 @@ export const AXES: Readonly<Record<string, AxisDef>> = {
343
343
  shape: "scalar",
344
344
  values: ["gemini", "anthropic", "codex", "xai", "openrouter", "openai"],
345
345
  },
346
+ "web-search-model": { key: "webSearchModel", set: "catalog", shape: "scalar" },
347
+ "hosted-image": { key: "hostedImage", set: "catalog", shape: "scalar", values: [true, false] },
348
+ "image-model": { key: "imageModel", set: "catalog", shape: "scalar" },
346
349
  "limits-patch": { key: "limitsPatch", set: "catalog", shape: "object" },
347
350
  "long-context-cost": { key: "longContext", set: "catalog", shape: "object" },
348
351
  "prompt-cache": { key: "promptCache", set: "catalog", shape: "object" },
@@ -1033,8 +1033,17 @@ function retargetCollapsedModelReferences<TSpec extends VariantSpecLike>(specs:
1033
1033
  liveIdsByProvider,
1034
1034
  );
1035
1035
  const compactionModel = resolveCollapsedModelReference(spec.compactionModel, spec.provider, liveIdsByProvider);
1036
- if (contextPromotionTarget === spec.contextPromotionTarget && compactionModel === spec.compactionModel) continue;
1037
- specs[index] = { ...spec, contextPromotionTarget, compactionModel };
1036
+ const webSearchModel = resolveCollapsedModelReference(spec.webSearchModel, spec.provider, liveIdsByProvider);
1037
+ const imageModel = resolveCollapsedModelReference(spec.imageModel, spec.provider, liveIdsByProvider);
1038
+ if (
1039
+ contextPromotionTarget === spec.contextPromotionTarget &&
1040
+ compactionModel === spec.compactionModel &&
1041
+ webSearchModel === spec.webSearchModel &&
1042
+ imageModel === spec.imageModel
1043
+ ) {
1044
+ continue;
1045
+ }
1046
+ specs[index] = { ...spec, contextPromotionTarget, compactionModel, webSearchModel, imageModel };
1038
1047
  }
1039
1048
  }
1040
1049
 
@@ -358,7 +358,7 @@ auth "anthropic" {
358
358
 
359
359
  Login kinds:
360
360
 
361
- - `login "api-key" { auth-url "…"; instructions "…"; prompt "…" placeholder="…"; empty-fallback "…"; normalize "strip-bearer"; validate … }` — `validate "chat-completions" base-url= model= tolerate-model-denied= max-tokens-field= max-tokens=`, `validate "anthropic-messages" base-url= model=`, or `validate "models-endpoint" url= base-url-env= headers-hook=`; all accept `label=` (error-message label, defaults to `name`) and `optional=#true` (only auth failures reject).
361
+ - `login "api-key" { auth-url "…"; instructions "…"; prompt "…" placeholder="…"; empty-fallback "…"; normalize "strip-bearer"; validate … }` — `validate "chat-completions" base-url= model= tolerate-model-denied= max-tokens-field= max-tokens=`, `validate "anthropic-messages" base-url= model=`, or `validate "models-endpoint" url= base-url-env= headers-hook=`; all accept `label=` (error-message label, defaults to `name`), `optional=#true` (only auth failures reject), and `trust-forbidden=#true` (requires `optional=#true`; a 403 no longer rejects, so only a 401 does).
362
362
  - `login "oauth-code" { … }` as above; `token`/`refresh` `params` values may use `{code}`, `{state}`, `{redirect_uri}`, `{code_verifier}`, `{client_id}`, `{client_secret}`, `{refresh_token}`, `{scope}`; the standard grant parameters are sent unless `standard=#false`.
363
363
  - `login "device-code" { client-id …; base-url "…"; scopes …; headers-hook "…"; device url="{base}/…" body="form" { params {…} headers {…} }; token url="…" url-hook="…"; response user-code= device-code= verification-uri= verification-uri-complete= interval= expires-in=; instructions "Enter code: {user_code}"; credential {…}; userinfo …; after-exchange hook=… }`.
364
364
  - `login "custom" hook="name"` — the whole flow is a named `@oh-my-pi/pi-ai` hook (`src/registry/hooks/custom.ts`).
@@ -1,7 +1,12 @@
1
- // No `validate` probe on purpose: GET /provider/v1/models is public (200
2
- // with no or bogus credentials, verified 2026-09-09), so a models-endpoint
3
- // probe could never reject a bad key. A chat-completions probe would bill
4
- // the key and 403 for Go-plan keys that are still valid CLI credentials.
1
+ // `/provider/v1/models` is public: it answers 200 to a missing or bogus key.
2
+ // Login therefore validates against `GET /alpha/whoami`, the identity route
3
+ // that command-code@1.67.0 calls. That route answers 401 to a bad key
4
+ // (verified 2026-09-29) and bills nothing. It is not in the Provider API
5
+ // docs, so the probe is optional and only a 401 rejects the key, matching
6
+ // `validateCommandApiKey` in command-code@1.67.0. A 403, a 404, a 5xx, or a
7
+ // moved route trusts the pasted key.
8
+ // The `models-endpoint` probe only checks for a 2xx answer, so this identity
9
+ // route serves as its target.
5
10
  auth "commandcode" {
6
11
  name "Command Code"
7
12
  env "COMMAND_CODE_API_KEY" "COMMANDCODE_API_KEY"
@@ -9,5 +14,6 @@ auth "commandcode" {
9
14
  auth-url "https://commandcode.ai/studio"
10
15
  instructions "Create or copy a Provider API key from Command Code Studio"
11
16
  prompt "Paste your Command Code API key" placeholder="user_..."
17
+ validate "models-endpoint" label="Command Code" url="https://api.commandcode.ai/alpha/whoami" optional=#true trust-forbidden=#true
12
18
  }
13
19
  }
@@ -61,6 +61,17 @@ class "anthropic" {
61
61
  supports-forced-tool-choice #false
62
62
  }
63
63
  }
64
+ // Sonnet 5.5 rejects forced tool selection like Opus 5.5 (400 `tool_choice:
65
+ // type "tool" and "any" are not supported for this model`); upper-bounded
66
+ // for the same reason as the Opus rule above.
67
+ // https://platform.claude.com/docs/en/models/sonnet-5-5/whats-new-sonnet-5-5
68
+ family "sonnet" {
69
+ revision ">=5.5 <6" {
70
+ supports-forced-tool-choice #false
71
+ // Signed thinking is bound to the exact preceding conversation.
72
+ thinking-prefix-binding #true
73
+ }
74
+ }
64
75
  on "anthropic" "cloudflare-ai-gateway" "google-vertex" {
65
76
  family "fable" {
66
77
  revision ">=5" {
@@ -87,6 +98,15 @@ class "anthropic" {
87
98
  supports-thinking-binding-controls #true
88
99
  }
89
100
  }
101
+ family "sonnet" {
102
+ revision ">=5.5" {
103
+ supports-mid-conversation-system #true
104
+ supports-mid-conversation-tool-changes #true
105
+ supports-turn-scoped-system #true
106
+ supports-per-message-effort #true
107
+ supports-thinking-binding-controls #true
108
+ }
109
+ }
90
110
  family "opus" {
91
111
  revision ">=4.8" {
92
112
  supports-mid-conversation-system #true
@@ -369,3 +389,13 @@ class "anthropic" {
369
389
  antigravity-usage-label "true"
370
390
  }
371
391
  }
392
+
393
+ // Claude 4+ ships the server-side `web_search` tool on any Messages-compatible host.
394
+ class "anthropic" {
395
+ on-api "anthropic-messages" {
396
+ revision ">=4" {
397
+ web-search "anthropic"
398
+ web-search-model "claude-haiku-4-5"
399
+ }
400
+ }
401
+ }
@@ -107,3 +107,14 @@ class "gemini" {
107
107
 
108
108
  thinking-loop-guard "gemini"
109
109
  }
110
+
111
+ // Google Search grounding is a Gemini 2+ lineage capability on every Gemini transport.
112
+ class "gemini" {
113
+ on-api "google-generative-ai" "google-gemini-cli" {
114
+ revision ">=2" {
115
+ web-search "gemini"
116
+ web-search-model "gemini-2.5-flash"
117
+ image-model "gemini-3-pro-image"
118
+ }
119
+ }
120
+ }
@@ -59,3 +59,25 @@ class "openai" {
59
59
  }
60
60
  }
61
61
  }
62
+
63
+ // Hosted Responses tools are a GPT-5+ lineage capability on any
64
+ // Responses-compatible host, so proxies exposing these ids inherit them. The
65
+ // swap targets resolve on the same provider only; when a host lacks them the
66
+ // session model runs the tool itself.
67
+ class "openai" {
68
+ on-api "openai-responses" {
69
+ revision ">=5" {
70
+ web-search "openai"
71
+ web-search-model "gpt-5.6-luna"
72
+ hosted-image #true
73
+ image-model "gpt-image-2"
74
+ }
75
+ }
76
+ on-api "openai-codex-responses" {
77
+ revision ">=5" {
78
+ web-search-model "gpt-5.6-luna"
79
+ hosted-image #true
80
+ image-model "gpt-image-2"
81
+ }
82
+ }
83
+ }
@@ -43,3 +43,9 @@ class "xai" {
43
43
  }
44
44
  thinking-loop-guard "xai"
45
45
  }
46
+
47
+ // Same-provider swap targets for hosted search and image generation.
48
+ class "xai" {
49
+ web-search-model "grok-4.5"
50
+ image-model "grok-imagine-image"
51
+ }
@@ -28,6 +28,12 @@ provider "anthropic" {
28
28
  cost input=3 output=15 cache-read=0.3 cache-write=3.75
29
29
  limits context=1000000 max-tokens=128000
30
30
  }
31
+ model "claude-sonnet-5-5" name="Claude Sonnet 5.5" {
32
+ reasoning #true
33
+ input "text" "image"
34
+ cost input=2 output=10 cache-read=0.2 cache-write=2.5
35
+ limits context=1000000 max-tokens=128000
36
+ }
31
37
  model "claude-fable-5" name="Claude Fable 5" {
32
38
  reasoning #true
33
39
  input "text" "image"