@oh-my-pi/pi-catalog 18.10.1 → 18.10.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 +38 -0
- package/THIRD-PARTY-NOTICES.txt +3 -3
- package/dist/types/compat/image-tokenization.d.ts +9 -1
- package/dist/types/compat/request-size.d.ts +11 -3
- package/dist/types/compat/thinking-replay.d.ts +9 -0
- package/dist/types/model-thinking.d.ts +11 -1
- package/dist/types/types.d.ts +37 -5
- package/dist/types/wire/codex.d.ts +2 -0
- package/package.json +4 -4
- package/src/build.ts +3 -2
- package/src/compat/axes.ts +44 -2
- package/src/compat/cascade.ts +4 -1
- package/src/compat/image-tokenization.ts +28 -10
- package/src/compat/request-size.ts +18 -5
- package/src/compat/resolve.ts +32 -5
- package/src/compat/rules/README.md +10 -2
- package/src/compat/rules/classes/anthropic.kdl +40 -2
- package/src/compat/rules/classes/meta.kdl +5 -0
- package/src/compat/rules/classes/openai.kdl +26 -3
- package/src/compat/rules/classes/xai.kdl +26 -0
- package/src/compat/rules/providers/amazon-bedrock.kdl +1 -0
- package/src/compat/rules/providers/anthropic.kdl +17 -0
- package/src/compat/rules/providers/cursor.kdl +30 -1
- package/src/compat/rules/providers/google-antigravity.kdl +26 -0
- package/src/compat/rules/providers/google-gemini-cli.kdl +1 -0
- package/src/compat/rules/providers/google-vertex.kdl +1 -0
- package/src/compat/rules/providers/google.kdl +1 -0
- package/src/compat/rules/providers/image-tokenization.kdl +2 -1
- package/src/compat/rules/providers/meta.kdl +16 -0
- package/src/compat/rules/providers/muse-code.kdl +9 -0
- package/src/compat/rules/providers/openai-codex.kdl +8 -0
- package/src/compat/rules/providers/openai.kdl +1 -0
- package/src/compat/rules/providers/openrouter.kdl +1 -0
- package/src/compat/rules/providers/umans.kdl +1 -0
- package/src/compat/rules/providers/xai-oauth.kdl +3 -0
- package/src/compat/rules/providers/xai.kdl +3 -0
- package/src/compat/rules/runtime/behavior.kdl +3 -3
- package/src/compat/rules/taxonomy/meta.kdl +3 -0
- package/src/compat/rules.json +1 -1
- package/src/compat/thinking-replay.ts +33 -0
- package/src/model-thinking.ts +13 -1
- package/src/model-tokenizer.ts +7 -1
- package/src/models.json +1 -1
- package/src/provider-models/bundled-references.ts +5 -2
- package/src/provider-models/openai-compat.ts +3 -1
- package/src/types.ts +39 -4
- package/src/wire/codex.ts +17 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,44 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [18.10.3] - 2026-10-12
|
|
6
|
+
|
|
7
|
+
### Breaking Changes
|
|
8
|
+
|
|
9
|
+
- `resolveInlineImageByteBudget(model, officialEndpoint)` and `resolveImageCountBudget(model, officialEndpoint)` take whether the request reaches the official endpoint instead of reading `compat.officialEndpoint`, which misses request-time reroutes such as `ANTHROPIC_BASE_URL`; pass pi-ai's `reachesOfficialEndpoint(model)` ([#15434](https://github.com/can1357/oh-my-pi/pull/15434) by [@will-bogusz](https://github.com/will-bogusz))
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- Added Muse Wonder (`muse-wonder-1.0`) to Meta and Muse Code with its `minimal`–`max` effort ladder and pricing
|
|
14
|
+
- Added the `thinking-unverified` rule axis: on hosts whose model list carries no capability metadata (Meta, Muse Code), unrecognized models now offer an opt-in thinking ladder (`ThinkingConfig.unverified`) instead of none
|
|
15
|
+
- Added `thinking-unverified` axis for offering reasoning ladders on unclassified model IDs
|
|
16
|
+
- Added `Muse Wonder 1.0` model definition to Meta provider
|
|
17
|
+
- Added the opt-in `snapcompact-image-reads` axis: snapcompact images a fresh `read` result only for readers whose rule sets it; Claude Opus 5.5 and later opt in ([#14249](https://github.com/can1357/oh-my-pi/pull/14249) by [@will-bogusz](https://github.com/will-bogusz))
|
|
18
|
+
- Added the `image-count-budget` rule for how many images one request may carry (Anthropic 90, Codex 200, other hosts unchanged) and the `official-image-count-budget` override that applies only on the official endpoint: Anthropic 600 (100 on 200k-window Claude models), Codex 2,000 ([#15434](https://github.com/can1357/oh-my-pi/pull/15434) by [@will-bogusz](https://github.com/will-bogusz))
|
|
19
|
+
- Added a 24 MB inline-image byte budget for the Codex backend, which now counts as its official endpoint at its default URL and at paths under it such as `/codex/responses` ([#15434](https://github.com/can1357/oh-my-pi/pull/15434) by [@will-bogusz](https://github.com/will-bogusz))
|
|
20
|
+
- Added `isOfficialCodexApiUrl` to `wire/codex`, moved from pi-ai ([#15434](https://github.com/can1357/oh-my-pi/pull/15434) by [@will-bogusz](https://github.com/will-bogusz))
|
|
21
|
+
- Added `verified` to `image-tokenization` rules, marking the Claude 4.7+ high-res tier and the GPT-5.5 to 6.1 and gpt-6-astra rules as matching measured provider billing ([#15442](https://github.com/can1357/oh-my-pi/pull/15442) by [@will-bogusz](https://github.com/will-bogusz))
|
|
22
|
+
- Added the `image-max-edge` axis and `resolveImageMaxEdge()` for hosts that shrink images before the model reads them; Cursor's Claude and GPT models and its `default` (Auto) route fit 1024×1024 ([#15448](https://github.com/can1357/oh-my-pi/pull/15448) by [@will-bogusz](https://github.com/will-bogusz))
|
|
23
|
+
- Added the `thinking-replay-tokens` axis; Claude lines set it to `output` on every host, since a replayed signed thinking block bills the turn's full reasoning, not the signature's bytes ([#15446](https://github.com/can1357/oh-my-pi/pull/15446) by [@will-bogusz](https://github.com/will-bogusz))
|
|
24
|
+
- Added a KDL opt-in for sibling subagent prompt-cache sharing, enabled for Muse Code's Spark 1.3, Wonder 1.0, and Wonder Full 1.0 variants over the Responses API ([#15467](https://github.com/can1357/oh-my-pi/pull/15467) by [@iliaal](https://github.com/iliaal))
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- Updated model definitions and compatibility rules for provider metadata
|
|
29
|
+
- Google Antigravity, xAI and Cursor requests keep up to 60 images, 20 for Claude on Antigravity and Cursor, up from 5; this counts snapcompact frames and live screenshots together ([#15449](https://github.com/can1357/oh-my-pi/pull/15449) by [@will-bogusz](https://github.com/will-bogusz))
|
|
30
|
+
- Snapcompact sizes archives at the frame prices measured for Antigravity Gemini (a fixed 1,089 tokens), Grok 4.6 to 4.20, and Claude Sonnet 4.6's standard tier ([#15449](https://github.com/can1357/oh-my-pi/pull/15449) by [@will-bogusz](https://github.com/will-bogusz))
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
|
|
34
|
+
- Applied `max-image-dimension` and `max-image-payload-bytes` to Google API models, and declared the 5 MiB per-image limit of Claude on Google Antigravity ([#15444](https://github.com/can1357/oh-my-pi/pull/15444) by [@will-bogusz](https://github.com/will-bogusz))
|
|
35
|
+
- Fixed local token counts for OpenAI GPT-5 and later models (Codex, OpenAI, Azure, OpenRouter, Copilot and other hosts) reading about 12% low: their text is now counted with o200k_base instead of a bytes/4 estimate ([#15447](https://github.com/can1357/oh-my-pi/pull/15447) by [@will-bogusz](https://github.com/will-bogusz))
|
|
36
|
+
|
|
37
|
+
## [18.10.2] - 2026-10-12
|
|
38
|
+
|
|
39
|
+
### Added
|
|
40
|
+
|
|
41
|
+
- Added the `honors-claude-code-beta-kill-switch` axis (`compat.honorsClaudeCodeBetaKillSwitch`), on for the `anthropic` provider, so a rule or `models.yml` override decides which providers honor `CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS` ([#15458](https://github.com/can1357/oh-my-pi/pull/15458) by [@H4vC](https://github.com/H4vC))
|
|
42
|
+
|
|
5
43
|
## [18.10.0] - 2026-10-12
|
|
6
44
|
|
|
7
45
|
### Changed
|
package/THIRD-PARTY-NOTICES.txt
CHANGED
|
@@ -79,9 +79,9 @@ uutils coreutils (https://github.com/uutils/coreutils), MIT
|
|
|
79
79
|
--------------------------------------------------------------------------------
|
|
80
80
|
Covers: base32, base64, basename, cat, cksum (shared checksum machinery),
|
|
81
81
|
b2sum, md5sum, sha1sum, sha224sum, sha256sum, sha384sum, sha512sum, comm, cp,
|
|
82
|
-
cut, date, dirname, head, hostname, ln, ls, mkdir, mktemp, mv,
|
|
83
|
-
printenv, readlink, realpath, rm, seq, sort, stat, tac, tail, tee,
|
|
84
|
-
truncate, uname, uniq, wc, whoami, yes.
|
|
82
|
+
cut, date, dirname, head, hostname, ln, ls, mkdir, mktemp, mv, nl, nproc,
|
|
83
|
+
paste, printenv, readlink, realpath, rm, seq, sort, stat, tac, tail, tee,
|
|
84
|
+
touch, tr, truncate, uname, uniq, wc, whoami, yes.
|
|
85
85
|
|
|
86
86
|
Copyright (c) uutils developers
|
|
87
87
|
|
|
@@ -23,7 +23,7 @@ export interface OpenAiPatchTokenization {
|
|
|
23
23
|
auto: "high" | "original";
|
|
24
24
|
}
|
|
25
25
|
/** One model line's image billing rule: the vendor formula and its per-model numbers. */
|
|
26
|
-
export type ImageTokenization = OpenAiPatchTokenization
|
|
26
|
+
export type ImageTokenization = (OpenAiPatchTokenization
|
|
27
27
|
/** Anthropic 28px patches, resized to fit a padded-edge limit and a visual-token budget. */
|
|
28
28
|
| {
|
|
29
29
|
regime: "anthropic-patch";
|
|
@@ -34,6 +34,9 @@ export type ImageTokenization = OpenAiPatchTokenization
|
|
|
34
34
|
| {
|
|
35
35
|
regime: "fixed";
|
|
36
36
|
tokens: number;
|
|
37
|
+
}) & {
|
|
38
|
+
/** Provider billing was measured to match this rule; unset for docs-only rules and wire fallbacks. */
|
|
39
|
+
verified?: boolean;
|
|
37
40
|
};
|
|
38
41
|
/** What reads the image: a built model, any `{ id }` with optional host and identity, or just a wire `{ api }`. */
|
|
39
42
|
export interface ImageTokenizationTarget {
|
|
@@ -48,5 +51,10 @@ export interface ImageTokenizationTarget {
|
|
|
48
51
|
* unregistered API).
|
|
49
52
|
*/
|
|
50
53
|
export declare function resolveImageTokenization(target: ImageTokenizationTarget): ImageTokenization | undefined;
|
|
54
|
+
/**
|
|
55
|
+
* Longest image edge the reader's host passes to the model (`image-max-edge`),
|
|
56
|
+
* or undefined when the host keeps the image's size.
|
|
57
|
+
*/
|
|
58
|
+
export declare function resolveImageMaxEdge(target: ImageTokenizationTarget): number | undefined;
|
|
51
59
|
/** Billed input tokens for one image of `size` under `rule`; only `openai-patch` reads `detail`. */
|
|
52
60
|
export declare function imageTokens(rule: ImageTokenization, size: ImageSize, detail?: ImageDetail): number;
|
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import type { Model } from "../types.js";
|
|
2
2
|
/**
|
|
3
3
|
* Maximum encoded inline-image bytes retained per request for a deployment with a documented body-size limit.
|
|
4
|
-
* The axis is a host contract: a rerouted
|
|
5
|
-
*
|
|
4
|
+
* The axis is a host contract: a rerouted endpoint (proxy, gateway) imposes its own limit, so the budget applies
|
|
5
|
+
* only when `officialEndpoint` says the request reaches the model's official endpoint. `compat.officialEndpoint`
|
|
6
|
+
* classifies only the configured `baseUrl`; pi-ai's `reachesOfficialEndpoint` also follows request-time reroutes.
|
|
6
7
|
*/
|
|
7
|
-
export declare function resolveInlineImageByteBudget(model: Model): number | undefined;
|
|
8
|
+
export declare function resolveInlineImageByteBudget(model: Model, officialEndpoint: boolean): number | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* Most images one request to this deployment may carry, or `undefined` when no rule names the host. The
|
|
11
|
+
* `official-image-count-budget` axis replaces `image-count-budget` only when `officialEndpoint` says the request
|
|
12
|
+
* reaches the model's official endpoint (see {@link resolveInlineImageByteBudget}); a rerouted endpoint keeps the
|
|
13
|
+
* provider's generic budget.
|
|
14
|
+
*/
|
|
15
|
+
export declare function resolveImageCountBudget(model: Model, officialEndpoint: boolean): number | undefined;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ImageTokenizationTarget } from "./image-tokenization.js";
|
|
2
|
+
/**
|
|
3
|
+
* `output`: the reasoning the signature carries, at the size its turn billed
|
|
4
|
+
* as output; the signature's own bytes are free. `signature`: the signature's
|
|
5
|
+
* bytes stand in for that reasoning.
|
|
6
|
+
*/
|
|
7
|
+
export type ThinkingReplayTokens = "output" | "signature";
|
|
8
|
+
/** How `reader` bills replayed thinking; `signature` unless its lineage declares otherwise. */
|
|
9
|
+
export declare function resolveThinkingReplayTokens(reader: ImageTokenizationTarget): ThinkingReplayTokens;
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* id parsing, no host matching, no compat detection per request.
|
|
8
8
|
*/
|
|
9
9
|
import { Effort } from "./effort.js";
|
|
10
|
-
import type { Api, Model, ModelSpec } from "./types.js";
|
|
10
|
+
import type { Api, Model, ModelSpec, ThinkingConfig } from "./types.js";
|
|
11
11
|
/**
|
|
12
12
|
* Runtime helpers read baked metadata only, so they accept both pre-build
|
|
13
13
|
* specs and built models.
|
|
@@ -19,6 +19,16 @@ type ApiModel<TApi extends Api = Api> = ModelSpec<TApi> | Model<TApi>;
|
|
|
19
19
|
* controllable effort surface (`thinking: undefined`).
|
|
20
20
|
*/
|
|
21
21
|
export declare function getSupportedEfforts<TApi extends Api>(model: ApiModel<TApi>): readonly Effort[];
|
|
22
|
+
/**
|
|
23
|
+
* Neutral view of a model whose thinking ladder is unverified: `reasoning: false`
|
|
24
|
+
* and no ladder. Re-entering policy resolution starts from it (the ladder is
|
|
25
|
+
* derived output, not authored input), and requests without an effort send it so
|
|
26
|
+
* no reasoning or disable fields reach an upstream that may reject them.
|
|
27
|
+
*/
|
|
28
|
+
export declare function withoutUnverifiedThinking<T extends {
|
|
29
|
+
reasoning: boolean;
|
|
30
|
+
thinking?: ThinkingConfig;
|
|
31
|
+
}>(model: T): T;
|
|
22
32
|
/**
|
|
23
33
|
* Clamps a requested thinking level against explicit model metadata.
|
|
24
34
|
*
|
package/dist/types/types.d.ts
CHANGED
|
@@ -93,6 +93,14 @@ export interface ThinkingConfig {
|
|
|
93
93
|
* provides an explicit wire off-path.
|
|
94
94
|
*/
|
|
95
95
|
requiresEffort?: boolean;
|
|
96
|
+
/**
|
|
97
|
+
* The catalog has no evidence this model reasons (unclassified id, no
|
|
98
|
+
* capability metadata). The ladder is offered for the user to opt into:
|
|
99
|
+
* sessions leave thinking unset until the user picks an effort, and a
|
|
100
|
+
* request without an effort goes out as non-reasoning (see
|
|
101
|
+
* `withoutUnverifiedThinking`).
|
|
102
|
+
*/
|
|
103
|
+
unverified?: boolean;
|
|
96
104
|
}
|
|
97
105
|
export type Provider = string;
|
|
98
106
|
/** Token budgets for each thinking level (token-based providers only) */
|
|
@@ -492,6 +500,14 @@ export interface AnthropicCompat {
|
|
|
492
500
|
* contract instead. Default: false.
|
|
493
501
|
*/
|
|
494
502
|
firstPartyProvider?: boolean;
|
|
503
|
+
/**
|
|
504
|
+
* Whether the provider honors Claude Code's `CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS`
|
|
505
|
+
* kill switch, which withholds pre-release fields (`context_management`, beta
|
|
506
|
+
* tool fields, task budgets, structured-output format, `thinking.block_binding`,
|
|
507
|
+
* server compaction) with their betas. Rule-owned on the
|
|
508
|
+
* provider whose `ANTHROPIC_*` env routing Claude Code shares. Default: false.
|
|
509
|
+
*/
|
|
510
|
+
honorsClaudeCodeBetaKillSwitch?: boolean;
|
|
495
511
|
/**
|
|
496
512
|
* Whether requests may carry `output_config.effort` (and its effort beta
|
|
497
513
|
* header). Vertex AI rejects the field/header. Default: true.
|
|
@@ -928,9 +944,11 @@ export interface ResolvedOpenAIResponsesCompat extends ResolvedOpenAISharedCompa
|
|
|
928
944
|
isVercelGatewayHost: boolean;
|
|
929
945
|
/**
|
|
930
946
|
* The configured endpoint is first-party OpenAI (`provider === "openai"` on
|
|
931
|
-
* an `api.openai.com` or unset baseUrl)
|
|
932
|
-
*
|
|
933
|
-
*
|
|
947
|
+
* an `api.openai.com` or unset baseUrl), or the Codex backend
|
|
948
|
+
* (`openai-codex-responses` on the default Codex baseUrl). Gates platform
|
|
949
|
+
* OpenAI's official-only Responses behavior (default-on stateful
|
|
950
|
+
* `previous_response_id` chaining and the `text.verbosity` field; the Codex
|
|
951
|
+
* adapter is not governed by either gate) and host request-size budgets.
|
|
934
952
|
*/
|
|
935
953
|
officialEndpoint: boolean;
|
|
936
954
|
/** Run Harmony-protocol leak detection/mitigation on streamed output. */
|
|
@@ -1055,18 +1073,32 @@ export interface GoogleCompat {
|
|
|
1055
1073
|
antigravityUsageLabel?: string;
|
|
1056
1074
|
/** Strip image inputs before encoding (text-only serving of a multimodal id). */
|
|
1057
1075
|
stripImageInput?: boolean;
|
|
1076
|
+
/**
|
|
1077
|
+
* Largest width or height a single image may carry for a Claude reader on
|
|
1078
|
+
* this host (see {@link AnthropicCompat.maxImageDimension}). Setting this or
|
|
1079
|
+
* {@link maxImagePayloadBytes} turns on Claude's image clamp, many-image
|
|
1080
|
+
* cap included; unset leaves images as sent.
|
|
1081
|
+
*/
|
|
1082
|
+
maxImageDimension?: number;
|
|
1083
|
+
/**
|
|
1084
|
+
* Largest base64 payload a single image may carry, in bytes (see
|
|
1085
|
+
* {@link AnthropicCompat.maxImagePayloadBytes}).
|
|
1086
|
+
*/
|
|
1087
|
+
maxImagePayloadBytes?: number;
|
|
1058
1088
|
/** Thinking-loop watchdog guard family applied to streamed reasoning. */
|
|
1059
1089
|
thinkingLoopGuard?: "gemini" | "deepseek" | "xai";
|
|
1060
1090
|
/** See {@link BedrockCompat.supportsSamplingParams}. */
|
|
1061
1091
|
supportsSamplingParams?: boolean;
|
|
1062
1092
|
}
|
|
1063
1093
|
/** Fully-resolved google-API compat view, materialized once by `buildModel`. */
|
|
1064
|
-
export type ResolvedGoogleCompat = Required<Omit<GoogleCompat, "streamFirstEventTimeoutMs" | "streamIdleTimeoutMs" | "thinkingLoopGuard" | "antigravityUsageLabel" | "supportsSamplingParams">> & {
|
|
1094
|
+
export type ResolvedGoogleCompat = Required<Omit<GoogleCompat, "streamFirstEventTimeoutMs" | "streamIdleTimeoutMs" | "thinkingLoopGuard" | "antigravityUsageLabel" | "supportsSamplingParams" | "maxImageDimension" | "maxImagePayloadBytes">> & {
|
|
1065
1095
|
streamFirstEventTimeoutMs?: number;
|
|
1066
1096
|
streamIdleTimeoutMs?: number;
|
|
1067
1097
|
thinkingLoopGuard?: GoogleCompat["thinkingLoopGuard"];
|
|
1068
1098
|
antigravityUsageLabel?: string;
|
|
1069
1099
|
supportsSamplingParams?: boolean;
|
|
1100
|
+
maxImageDimension?: number;
|
|
1101
|
+
maxImagePayloadBytes?: number;
|
|
1070
1102
|
};
|
|
1071
1103
|
/** Sparse, user-authored compat overrides for a given API (models.json / config vocabulary). */
|
|
1072
1104
|
export type CompatConfigOf<TApi extends Api> = TApi extends "openai-completions" | "openrouter" | "openai-responses" | "azure-openai-responses" | "openai-codex-responses" ? OpenAICompat : TApi extends "anthropic-messages" ? AnthropicCompat : TApi extends "bedrock-converse-stream" ? BedrockCompat : TApi extends "devin-agent" ? DevinCompat : TApi extends "google-generative-ai" | "google-vertex" | "google-gemini-cli" ? GoogleCompat : undefined;
|
|
@@ -1145,7 +1177,7 @@ export interface ModelCost extends TokenCost {
|
|
|
1145
1177
|
* generations rather than providers: DeepSeek V3 through V4 share
|
|
1146
1178
|
* `"deepseek-v3"`; Kimi K2 through K3 share `"kimi-k2"`.
|
|
1147
1179
|
*/
|
|
1148
|
-
export type ModelTokenizer = "claude-v3" | "claude-v47" | "claude-v5" | "claude-v5-sonnet" | "qwen3" | "deepseek-v3" | "kimi-k2" | "glm5";
|
|
1180
|
+
export type ModelTokenizer = "claude-v3" | "claude-v47" | "claude-v5" | "claude-v5-sonnet" | "qwen3" | "deepseek-v3" | "kimi-k2" | "glm5" | "o200k";
|
|
1149
1181
|
/** One account's discovered entitlements on a model; see {@link Model.accountAccess}. */
|
|
1150
1182
|
export interface ModelAccountAccess {
|
|
1151
1183
|
/**
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
* Constants for OpenAI Codex (ChatGPT OAuth) backend
|
|
3
3
|
*/
|
|
4
4
|
export declare const CODEX_BASE_URL = "https://chatgpt.com/backend-api";
|
|
5
|
+
/** Strict official-Codex endpoint check; exact origin or a path boundary after {@link CODEX_BASE_URL}. */
|
|
6
|
+
export declare function isOfficialCodexApiUrl(baseUrl: string | undefined): boolean;
|
|
5
7
|
/**
|
|
6
8
|
* Pinned OpenAI Codex client version (corresponds to @openai/codex package version).
|
|
7
9
|
* Updated from npm by `bun run check-spoofed-versions --update`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oh-my-pi/pi-catalog",
|
|
3
|
-
"version": "18.10.
|
|
3
|
+
"version": "18.10.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.10.
|
|
97
|
-
"@oh-my-pi/pi-utils": "18.10.
|
|
96
|
+
"@oh-my-pi/omptype": "18.10.3",
|
|
97
|
+
"@oh-my-pi/pi-utils": "18.10.3"
|
|
98
98
|
},
|
|
99
99
|
"devDependencies": {
|
|
100
100
|
"@bgotink/kdl": "0.4.0",
|
|
101
|
-
"@oh-my-pi/pi-ai": "18.10.
|
|
101
|
+
"@oh-my-pi/pi-ai": "18.10.3",
|
|
102
102
|
"@types/bun": "^1.3.14"
|
|
103
103
|
},
|
|
104
104
|
"engines": {
|
package/src/build.ts
CHANGED
|
@@ -351,8 +351,9 @@ export function buildModel<TApi extends Api>(spec: ModelSpec<TApi>): Model<TApi>
|
|
|
351
351
|
const model: Model<TApi> = {
|
|
352
352
|
...spec,
|
|
353
353
|
// A reviewed `thinking-upgrade-neutral` policy can repair a stale
|
|
354
|
-
// `reasoning: false` discovery default
|
|
355
|
-
//
|
|
354
|
+
// `reasoning: false` discovery default, and an unclassified neutral spec
|
|
355
|
+
// gets an unverified ladder (see `resolveThinkingPolicy`); materialize
|
|
356
|
+
// either for transports and the picker.
|
|
356
357
|
reasoning: spec.reasoning || policy.thinking !== undefined,
|
|
357
358
|
name: cleanModelName(spec.name),
|
|
358
359
|
identity: policy.identity,
|
package/src/compat/axes.ts
CHANGED
|
@@ -242,9 +242,10 @@ export const AXES: Readonly<Record<string, AxisDef>> = {
|
|
|
242
242
|
"escape-builtin-tool-names": wire("escapeBuiltinToolNames", ["anthropic"]),
|
|
243
243
|
"fast-mode": wire("fastMode", ["anthropic"]),
|
|
244
244
|
"first-party-provider": wire("firstPartyProvider", ["anthropic"]),
|
|
245
|
+
"honors-claude-code-beta-kill-switch": wire("honorsClaudeCodeBetaKillSwitch", ["anthropic"]),
|
|
245
246
|
"inject-claude-code-instruction": wire("injectClaudeCodeInstruction", ["anthropic"]),
|
|
246
|
-
"max-image-dimension": wire("maxImageDimension", ["anthropic"]),
|
|
247
|
-
"max-image-payload-bytes": wire("maxImagePayloadBytes", ["anthropic"]),
|
|
247
|
+
"max-image-dimension": wire("maxImageDimension", ["anthropic", "google"]),
|
|
248
|
+
"max-image-payload-bytes": wire("maxImagePayloadBytes", ["anthropic", "google"]),
|
|
248
249
|
"official-endpoint": wire("officialEndpoint", ["anthropic", "openai-responses"]),
|
|
249
250
|
"replay-unsigned-thinking": wire("replayUnsignedThinking", ["anthropic"]),
|
|
250
251
|
"requires-thinking-enabled": wire("requiresThinkingEnabled", ["anthropic"]),
|
|
@@ -328,6 +329,8 @@ export const AXES: Readonly<Record<string, AxisDef>> = {
|
|
|
328
329
|
"thinking-suppress-when-off": { key: "suppressWhenOff", set: "thinking", shape: "scalar" },
|
|
329
330
|
"thinking-supports-display": { key: "supportsDisplay", set: "thinking", shape: "scalar" },
|
|
330
331
|
"thinking-upgrade-neutral": { key: "upgradeNeutral", set: "thinking", shape: "scalar" },
|
|
332
|
+
/** Unclassified neutral ids here are capability-unknown: offer the ladder as opt-in. */
|
|
333
|
+
"thinking-unverified": { key: "unverified", set: "thinking", shape: "scalar" },
|
|
331
334
|
|
|
332
335
|
// ── catalog metadata ──
|
|
333
336
|
"apply-patch-tool-type": {
|
|
@@ -388,6 +391,18 @@ export const AXES: Readonly<Record<string, AxisDef>> = {
|
|
|
388
391
|
shape: "scalar",
|
|
389
392
|
values: [true, false],
|
|
390
393
|
},
|
|
394
|
+
/**
|
|
395
|
+
* How the model line bills a replayed signed thinking block as input:
|
|
396
|
+
* `output` bills the reasoning its signature carries, at the size the turn
|
|
397
|
+
* billed as output, and not the signature's bytes. Unassigned: estimated
|
|
398
|
+
* from the signature's bytes. Read through `./thinking-replay`.
|
|
399
|
+
*/
|
|
400
|
+
"thinking-replay-tokens": {
|
|
401
|
+
key: "thinkingReplayTokens",
|
|
402
|
+
set: "catalog",
|
|
403
|
+
shape: "scalar",
|
|
404
|
+
values: ["output", "signature"],
|
|
405
|
+
},
|
|
391
406
|
"delegation-bias": { key: "delegationBias", set: "catalog", shape: "scalar", values: DELEGATION_BIASES },
|
|
392
407
|
"discovery-api": { key: "discoveryApi", set: "catalog", shape: "scalar" },
|
|
393
408
|
"edit-prompt-variant": { key: "editPromptVariant", set: "catalog", shape: "scalar", values: ["full", "compact"] },
|
|
@@ -404,7 +419,28 @@ export const AXES: Readonly<Record<string, AxisDef>> = {
|
|
|
404
419
|
"hosted-image": { key: "hostedImage", set: "catalog", shape: "scalar", values: [true, false] },
|
|
405
420
|
/** How the model line bills an input image; shape and formulas in `./image-tokenization`. */
|
|
406
421
|
"image-tokenization": { key: "imageTokenization", set: "catalog", shape: "object" },
|
|
422
|
+
/**
|
|
423
|
+
* Most images one request may carry, documents included where the host
|
|
424
|
+
* counts them. Unassigned: unknown host, held to a conservative floor.
|
|
425
|
+
*/
|
|
426
|
+
"image-count-budget": { key: "imageCountBudget", set: "catalog", shape: "scalar" },
|
|
427
|
+
/**
|
|
428
|
+
* Replaces `image-count-budget` while the request reaches the official
|
|
429
|
+
* endpoint (pi-ai `reachesOfficialEndpoint`): a limit measured or documented
|
|
430
|
+
* for the host's own endpoint, which a custom `baseUrl` or an
|
|
431
|
+
* `ANTHROPIC_BASE_URL`/`FOUNDRY_BASE_URL` reroute (proxy, gateway) need not share.
|
|
432
|
+
*/
|
|
433
|
+
"official-image-count-budget": { key: "officialImageCountBudget", set: "catalog", shape: "scalar" },
|
|
434
|
+
/**
|
|
435
|
+
* Opt-in: the reader may get a fresh `read` result as snapcompact frames
|
|
436
|
+
* (`snapcompact.toolResults`), because it edits as exactly from frames as
|
|
437
|
+
* from text. Unassigned: `read` results stay text, since a reader that
|
|
438
|
+
* misreads indentation in frames makes inexact edits.
|
|
439
|
+
*/
|
|
440
|
+
"snapcompact-image-reads": { key: "snapcompactImageReads", set: "catalog", shape: "scalar", values: [true, false] },
|
|
407
441
|
"image-model": { key: "imageModel", set: "catalog", shape: "scalar" },
|
|
442
|
+
/** Longest image edge the host passes to the model: it downscales larger images to fit first. */
|
|
443
|
+
"image-max-edge": { key: "imageMaxEdge", set: "catalog", shape: "scalar" },
|
|
408
444
|
"inline-image-byte-budget": { key: "inlineImageByteBudget", set: "catalog", shape: "scalar" },
|
|
409
445
|
/**
|
|
410
446
|
* Most questions one System One judgment request may carry; the judge
|
|
@@ -414,6 +450,12 @@ export const AXES: Readonly<Record<string, AxisDef>> = {
|
|
|
414
450
|
"limits-patch": { key: "limitsPatch", set: "catalog", shape: "object" },
|
|
415
451
|
"long-context-cost": { key: "longContext", set: "catalog", shape: "object" },
|
|
416
452
|
"prompt-cache": { key: "promptCache", set: "catalog", shape: "object" },
|
|
453
|
+
"task-sibling-prompt-cache": {
|
|
454
|
+
key: "taskSiblingPromptCache",
|
|
455
|
+
set: "catalog",
|
|
456
|
+
shape: "scalar",
|
|
457
|
+
values: [true, false],
|
|
458
|
+
},
|
|
417
459
|
/**
|
|
418
460
|
* Prompt-cache lookback in block positions: how far back from a cache
|
|
419
461
|
* breakpoint the provider looks for an earlier request's cache entry.
|
package/src/compat/cascade.ts
CHANGED
|
@@ -409,13 +409,16 @@ function resolveOverIndex(index: RuleIndex, target: ResolveTarget): ResolvedAxes
|
|
|
409
409
|
reasoning = true;
|
|
410
410
|
}
|
|
411
411
|
}
|
|
412
|
+
// Unclassified targets get their provider's ladder too: it is offered as
|
|
413
|
+
// unverified thinking (`resolveThinkingPolicy`), never as reasoning capability.
|
|
414
|
+
const offersThinking = reasoning || target.class === "unknown";
|
|
412
415
|
const wire: WinnerTable = {};
|
|
413
416
|
const thinking: WinnerTable = {};
|
|
414
417
|
const catalog: WinnerTable = {};
|
|
415
418
|
for (const { rule, rank } of ranked) {
|
|
416
419
|
contest(wire, rule.compiled.wire, rank, rule, target);
|
|
417
420
|
contest(catalog, rule.compiled.catalog, rank, rule, target);
|
|
418
|
-
if (
|
|
421
|
+
if (offersThinking) contest(thinking, rule.compiled.thinking, rank, rule, target);
|
|
419
422
|
}
|
|
420
423
|
return {
|
|
421
424
|
wire: collect(wire, rule => rule.wire),
|
|
@@ -37,12 +37,16 @@ export interface OpenAiPatchTokenization {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
/** One model line's image billing rule: the vendor formula and its per-model numbers. */
|
|
40
|
-
export type ImageTokenization =
|
|
40
|
+
export type ImageTokenization = (
|
|
41
41
|
| OpenAiPatchTokenization
|
|
42
42
|
/** Anthropic 28px patches, resized to fit a padded-edge limit and a visual-token budget. */
|
|
43
43
|
| { regime: "anthropic-patch"; maxEdge: number; maxTokens: number }
|
|
44
44
|
/** A fixed per-image budget regardless of pixels (Gemini 3 `media_resolution`). */
|
|
45
|
-
| { regime: "fixed"; tokens: number }
|
|
45
|
+
| { regime: "fixed"; tokens: number }
|
|
46
|
+
) & {
|
|
47
|
+
/** Provider billing was measured to match this rule; unset for docs-only rules and wire fallbacks. */
|
|
48
|
+
verified?: boolean;
|
|
49
|
+
};
|
|
46
50
|
|
|
47
51
|
/** A `class: "unknown"` identity for targets resolved by wire API alone. */
|
|
48
52
|
const UNCLASSIFIED: Pick<ModelIdentity, "class"> = { class: "unknown" };
|
|
@@ -62,17 +66,13 @@ export interface ImageTokenizationTarget {
|
|
|
62
66
|
identity?: Pick<ModelIdentity, "class" | "family" | "revision">;
|
|
63
67
|
}
|
|
64
68
|
|
|
65
|
-
/**
|
|
66
|
-
|
|
67
|
-
* fallback; undefined when neither covers it (an unclassified model on an
|
|
68
|
-
* unregistered API).
|
|
69
|
-
*/
|
|
70
|
-
export function resolveImageTokenization(target: ImageTokenizationTarget): ImageTokenization | undefined {
|
|
69
|
+
/** Catalog axes for `target`'s identity on its host and wire API. */
|
|
70
|
+
function resolveImageAxes(target: ImageTokenizationTarget): Record<string, unknown> {
|
|
71
71
|
const provider = target.provider ?? "";
|
|
72
72
|
const model = target.id ?? "";
|
|
73
73
|
const identity: Pick<ModelIdentity, "class" | "family" | "revision"> =
|
|
74
74
|
target.identity ?? (model ? classifyModel(provider, model, { lenient: true }) : UNCLASSIFIED);
|
|
75
|
-
|
|
75
|
+
return resolveCascade({
|
|
76
76
|
provider,
|
|
77
77
|
api: target.api ?? "",
|
|
78
78
|
class: identity.class,
|
|
@@ -80,10 +80,28 @@ export function resolveImageTokenization(target: ImageTokenizationTarget): Image
|
|
|
80
80
|
reasoning: false,
|
|
81
81
|
...(identity.family !== undefined && { family: identity.family }),
|
|
82
82
|
...(identity.revision !== undefined && { revision: identity.revision }),
|
|
83
|
-
}).catalog
|
|
83
|
+
}).catalog;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* The reader's image billing rule: its lineage rule, else its wire API's
|
|
88
|
+
* fallback; undefined when neither covers it (an unclassified model on an
|
|
89
|
+
* unregistered API).
|
|
90
|
+
*/
|
|
91
|
+
export function resolveImageTokenization(target: ImageTokenizationTarget): ImageTokenization | undefined {
|
|
92
|
+
const rule = resolveImageAxes(target).imageTokenization;
|
|
84
93
|
return isImageTokenization(rule) ? rule : undefined;
|
|
85
94
|
}
|
|
86
95
|
|
|
96
|
+
/**
|
|
97
|
+
* Longest image edge the reader's host passes to the model (`image-max-edge`),
|
|
98
|
+
* or undefined when the host keeps the image's size.
|
|
99
|
+
*/
|
|
100
|
+
export function resolveImageMaxEdge(target: ImageTokenizationTarget): number | undefined {
|
|
101
|
+
const edge = resolveImageAxes(target).imageMaxEdge;
|
|
102
|
+
return typeof edge === "number" && edge > 0 ? edge : undefined;
|
|
103
|
+
}
|
|
104
|
+
|
|
87
105
|
/** Scale `size` down (never up) so its longest side is at most `maxEdge`. */
|
|
88
106
|
function fitLongEdge(size: ImageSize, maxEdge: number): ImageSize {
|
|
89
107
|
const longest = Math.max(size.width, size.height);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { toModelSpec } from "../provider-models/bundled-references";
|
|
2
2
|
import type { Model } from "../types";
|
|
3
|
+
import { resolveCatalogPolicy } from "./catalog-policy";
|
|
3
4
|
import { resolveCatalogAxes } from "./resolve";
|
|
4
5
|
|
|
5
6
|
const imageBudgetCache = new Map<string, number | null>();
|
|
@@ -7,12 +8,12 @@ const IMAGE_BUDGET_CACHE_MAX = 8192;
|
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Maximum encoded inline-image bytes retained per request for a deployment with a documented body-size limit.
|
|
10
|
-
* The axis is a host contract: a rerouted
|
|
11
|
-
*
|
|
11
|
+
* The axis is a host contract: a rerouted endpoint (proxy, gateway) imposes its own limit, so the budget applies
|
|
12
|
+
* only when `officialEndpoint` says the request reaches the model's official endpoint. `compat.officialEndpoint`
|
|
13
|
+
* classifies only the configured `baseUrl`; pi-ai's `reachesOfficialEndpoint` also follows request-time reroutes.
|
|
12
14
|
*/
|
|
13
|
-
export function resolveInlineImageByteBudget(model: Model): number | undefined {
|
|
14
|
-
|
|
15
|
-
if (!compat || !("officialEndpoint" in compat) || !compat.officialEndpoint) return undefined;
|
|
15
|
+
export function resolveInlineImageByteBudget(model: Model, officialEndpoint: boolean): number | undefined {
|
|
16
|
+
if (!officialEndpoint) return undefined;
|
|
16
17
|
const key = `${model.provider} ${model.id} ${model.api}`;
|
|
17
18
|
let budget = imageBudgetCache.get(key);
|
|
18
19
|
if (budget === undefined) {
|
|
@@ -23,3 +24,15 @@ export function resolveInlineImageByteBudget(model: Model): number | undefined {
|
|
|
23
24
|
}
|
|
24
25
|
return budget ?? undefined;
|
|
25
26
|
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Most images one request to this deployment may carry, or `undefined` when no rule names the host. The
|
|
30
|
+
* `official-image-count-budget` axis replaces `image-count-budget` only when `officialEndpoint` says the request
|
|
31
|
+
* reaches the model's official endpoint (see {@link resolveInlineImageByteBudget}); a rerouted endpoint keeps the
|
|
32
|
+
* provider's generic budget.
|
|
33
|
+
*/
|
|
34
|
+
export function resolveImageCountBudget(model: Model, officialEndpoint: boolean): number | undefined {
|
|
35
|
+
const policy = resolveCatalogPolicy(model);
|
|
36
|
+
const value = (officialEndpoint && policy.officialImageCountBudget) || policy.imageCountBudget;
|
|
37
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0 ? value : undefined;
|
|
38
|
+
}
|
package/src/compat/resolve.ts
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
import { Effort, THINKING_EFFORTS } from "../effort";
|
|
19
19
|
import { hostMatchesUrl, modelMatchesHost } from "../hosts";
|
|
20
|
+
import { withoutUnverifiedThinking } from "../model-thinking";
|
|
20
21
|
import type {
|
|
21
22
|
Api,
|
|
22
23
|
CompatOf,
|
|
@@ -31,6 +32,7 @@ import type {
|
|
|
31
32
|
ResolvedOpenAISharedCompat,
|
|
32
33
|
ThinkingConfig,
|
|
33
34
|
} from "../types";
|
|
35
|
+
import { isOfficialCodexApiUrl } from "../wire/codex";
|
|
34
36
|
import {
|
|
35
37
|
isAnthropicSigningProxyUrl,
|
|
36
38
|
isAzureAnthropicRoute,
|
|
@@ -796,7 +798,11 @@ function resolveOpenAIResponsesPolicy(
|
|
|
796
798
|
supportsObfuscationOptOut: isOpenAIUrl || provider === "openai",
|
|
797
799
|
storeResponses: false,
|
|
798
800
|
connectionBoundNativeHistory: false,
|
|
799
|
-
|
|
801
|
+
// The Codex backend is official at the URL its requests default to when `baseUrl` is unset, and at paths
|
|
802
|
+
// under it such as `/codex` and `/codex/responses`, which its requests resolve to the same endpoint.
|
|
803
|
+
officialEndpoint:
|
|
804
|
+
isOfficialOpenAIEndpoint(provider, baseUrl) ||
|
|
805
|
+
(api === "openai-codex-responses" && isOfficialCodexApiUrl(baseUrl)),
|
|
800
806
|
harmonyLeakMitigation: false,
|
|
801
807
|
rejectRootObjectUnion: false,
|
|
802
808
|
retryWithoutStrictOnGrammarError: false,
|
|
@@ -892,6 +898,7 @@ function resolveAnthropicPolicy(
|
|
|
892
898
|
supportsContextManagement: true,
|
|
893
899
|
supportsServerCompaction: false,
|
|
894
900
|
firstPartyProvider: false,
|
|
901
|
+
honorsClaudeCodeBetaKillSwitch: false,
|
|
895
902
|
supportsOutputEffort: true,
|
|
896
903
|
disableStrictTools: rejectsStrictTools,
|
|
897
904
|
// Present as a key so models.yml `compat` can set it; unset unless detected.
|
|
@@ -979,6 +986,10 @@ function resolveGooglePolicy(
|
|
|
979
986
|
claudeThinkingBetaHeader: false,
|
|
980
987
|
antigravityClaudeToolMode: false,
|
|
981
988
|
stripImageInput: false,
|
|
989
|
+
// Unset means "no image contract": images go out as sent. Keys exist so
|
|
990
|
+
// a KDL rule or a user `compat` block can declare one.
|
|
991
|
+
maxImageDimension: undefined,
|
|
992
|
+
maxImagePayloadBytes: undefined,
|
|
982
993
|
};
|
|
983
994
|
applyWireAxes(compat, axes.wire, spec.api);
|
|
984
995
|
applyCompatOverrides(compat, spec.compat);
|
|
@@ -1088,6 +1099,7 @@ interface RuleThinking {
|
|
|
1088
1099
|
supportsDisplay?: boolean;
|
|
1089
1100
|
prefixBinding?: boolean;
|
|
1090
1101
|
upgradeNeutral?: boolean;
|
|
1102
|
+
unverified?: boolean;
|
|
1091
1103
|
}
|
|
1092
1104
|
|
|
1093
1105
|
function readRuleThinking(axes: ResolvedAxes): RuleThinking {
|
|
@@ -1108,6 +1120,7 @@ function readRuleThinking(axes: ResolvedAxes): RuleThinking {
|
|
|
1108
1120
|
if (typeof raw.supportsDisplay === "boolean") out.supportsDisplay = raw.supportsDisplay;
|
|
1109
1121
|
if (typeof raw.prefixBinding === "boolean") out.prefixBinding = raw.prefixBinding;
|
|
1110
1122
|
if (typeof raw.upgradeNeutral === "boolean") out.upgradeNeutral = raw.upgradeNeutral;
|
|
1123
|
+
if (typeof raw.unverified === "boolean") out.unverified = raw.unverified;
|
|
1111
1124
|
return out;
|
|
1112
1125
|
}
|
|
1113
1126
|
|
|
@@ -1155,8 +1168,16 @@ function resolveThinkingPolicy<TApi extends Api>(
|
|
|
1155
1168
|
// not re-expand it into an unadvertised ladder. Absent metadata is
|
|
1156
1169
|
// repaired only where KDL opts in with `thinking-upgrade-neutral`
|
|
1157
1170
|
// alongside a reviewed `thinking-efforts` ladder (the cascade upgrade for
|
|
1158
|
-
// stale source capability data)
|
|
1159
|
-
|
|
1171
|
+
// stale source capability data). On deployments whose discovery reports no
|
|
1172
|
+
// capabilities (`thinking-unverified`), an unclassified neutral spec — nothing
|
|
1173
|
+
// says whether it reasons — gets an unverified ladder the user may opt into.
|
|
1174
|
+
const unverified = !compatReasoning(spec, axes);
|
|
1175
|
+
if (
|
|
1176
|
+
unverified &&
|
|
1177
|
+
(explicitThinking !== undefined || facts.identity.class !== "unknown" || rule.unverified !== true)
|
|
1178
|
+
) {
|
|
1179
|
+
return undefined;
|
|
1180
|
+
}
|
|
1160
1181
|
if (
|
|
1161
1182
|
spec.provider === "cline-pass" &&
|
|
1162
1183
|
compat !== undefined &&
|
|
@@ -1180,9 +1201,14 @@ function resolveThinkingPolicy<TApi extends Api>(
|
|
|
1180
1201
|
if (config.efforts.length === 0) {
|
|
1181
1202
|
throw new Error(`Model ${spec.provider}/${spec.id} resolved to an empty thinking range`);
|
|
1182
1203
|
}
|
|
1183
|
-
if (rule.defaultLevel !== undefined) config.defaultLevel = rule.defaultLevel;
|
|
1184
1204
|
const effortMap = mergeEffortMap(spec, rule.effortMap, compat, config.efforts);
|
|
1185
1205
|
if (effortMap !== undefined) config.effortMap = effortMap;
|
|
1206
|
+
if (unverified) {
|
|
1207
|
+
// Off-by-default opt-in: no default level, mandatory effort, or wire off-switch.
|
|
1208
|
+
config.unverified = true;
|
|
1209
|
+
return config;
|
|
1210
|
+
}
|
|
1211
|
+
if (rule.defaultLevel !== undefined) config.defaultLevel = rule.defaultLevel;
|
|
1186
1212
|
if (rule.effortBudgets !== undefined) config.effortBudgets = rule.effortBudgets;
|
|
1187
1213
|
const supportsDisplay = rule.supportsDisplay ?? defaultSupportsDisplay(spec, facts);
|
|
1188
1214
|
if (supportsDisplay) config.supportsDisplay = true;
|
|
@@ -1315,7 +1341,8 @@ export function resolveModelPolicy<TApi extends Api>(
|
|
|
1315
1341
|
spec: ModelSpec<TApi>,
|
|
1316
1342
|
route?: ResolveRoute,
|
|
1317
1343
|
): ResolvedModelPolicy<TApi>;
|
|
1318
|
-
export function resolveModelPolicy(
|
|
1344
|
+
export function resolveModelPolicy(input: ModelSpec<Api>, route?: ResolveRoute): ResolvedModelPolicy<Api> {
|
|
1345
|
+
const spec = withoutUnverifiedThinking(input);
|
|
1319
1346
|
const identity = resolveIdentity(spec);
|
|
1320
1347
|
const facts = new IdentityFacts(identity);
|
|
1321
1348
|
const target = buildResolveTarget(spec, identity);
|