@oh-my-pi/pi-catalog 18.4.3 → 18.4.5

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.
Files changed (68) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/types/compat/anthropic.d.ts +6 -0
  3. package/dist/types/compat/auth-ids.d.ts +2 -2
  4. package/dist/types/compat/axes.d.ts +3 -1
  5. package/dist/types/compat/factory-droid.d.ts +65 -0
  6. package/dist/types/compat/provider-ids.d.ts +1 -1
  7. package/dist/types/compat/resolve.d.ts +8 -2
  8. package/dist/types/compat/types.d.ts +26 -1
  9. package/dist/types/discovery/cursor-proto.d.ts +284 -0
  10. package/dist/types/discovery/cursor.d.ts +12 -6
  11. package/dist/types/discovery/factory-droid.d.ts +28 -0
  12. package/dist/types/discovery/index.d.ts +1 -0
  13. package/dist/types/discovery/protobuf.d.ts +3 -3
  14. package/dist/types/hosts.d.ts +13 -0
  15. package/dist/types/model-manager.d.ts +10 -0
  16. package/dist/types/models.d.ts +5 -1
  17. package/dist/types/provider-models/cache-provider-id.d.ts +2 -1
  18. package/dist/types/provider-models/descriptor-types.d.ts +2 -1
  19. package/dist/types/provider-models/openai-compat.d.ts +21 -0
  20. package/dist/types/provider-models/special.d.ts +2 -0
  21. package/dist/types/types.d.ts +115 -3
  22. package/dist/types/wire/codex.d.ts +5 -3
  23. package/dist/types/wire/cursor.d.ts +20 -0
  24. package/dist/types/wire/factory-droid.d.ts +45 -0
  25. package/package.json +4 -4
  26. package/src/build.ts +19 -0
  27. package/src/compat/anthropic.ts +11 -1
  28. package/src/compat/auth-ids.ts +4 -0
  29. package/src/compat/axes.ts +114 -1
  30. package/src/compat/cascade.ts +8 -1
  31. package/src/compat/collapse.ts +3 -0
  32. package/src/compat/factory-droid.ts +170 -0
  33. package/src/compat/provider-ids.ts +2 -0
  34. package/src/compat/resolve.ts +59 -8
  35. package/src/compat/rules/README.md +15 -7
  36. package/src/compat/rules/auth/_order.kdl +2 -2
  37. package/src/compat/rules/auth/factory-droid.kdl +28 -0
  38. package/src/compat/rules/auth/helmcode.kdl +9 -0
  39. package/src/compat/rules/classes/anthropic.kdl +16 -4
  40. package/src/compat/rules/providers/cursor.kdl +29 -17
  41. package/src/compat/rules/providers/factory-droid.kdl +1166 -0
  42. package/src/compat/rules/providers/helmcode.kdl +120 -0
  43. package/src/compat/rules/providers/openai-codex.kdl +17 -0
  44. package/src/compat/rules/runtime/behavior.kdl +44 -0
  45. package/src/compat/rules/taxonomy/_collapse.kdl +36 -14
  46. package/src/compat/rules.json +1 -1
  47. package/src/compat/types.ts +27 -1
  48. package/src/discovery/codex.ts +16 -0
  49. package/src/discovery/cursor-models.proto +261 -0
  50. package/src/discovery/cursor-proto.ts +471 -0
  51. package/src/discovery/cursor.ts +817 -88
  52. package/src/discovery/devin.ts +1 -3
  53. package/src/discovery/factory-droid.ts +296 -0
  54. package/src/discovery/index.ts +1 -0
  55. package/src/discovery/protobuf.ts +16 -7
  56. package/src/hosts.ts +47 -0
  57. package/src/model-manager.ts +13 -0
  58. package/src/models.json +1 -1
  59. package/src/models.ts +13 -0
  60. package/src/provider-models/cache-provider-id.ts +37 -6
  61. package/src/provider-models/descriptor-types.ts +2 -1
  62. package/src/provider-models/descriptors.ts +4 -0
  63. package/src/provider-models/openai-compat.ts +83 -17
  64. package/src/provider-models/special.ts +26 -2
  65. package/src/types.ts +129 -1
  66. package/src/wire/codex.ts +5 -3
  67. package/src/wire/cursor.ts +32 -0
  68. package/src/wire/factory-droid.ts +130 -0
@@ -0,0 +1,28 @@
1
+ import { type FactoryDroidRegistryModel } from "../compat/factory-droid.js";
2
+ import type { FetchImpl, ModelSpec } from "../types.js";
3
+ import { type AccountScope } from "../wire/factory-droid.js";
4
+ export interface FactoryDroidModelDiscoveryOptions extends AccountScope {
5
+ /** OMP-stored WorkOS access token (from `/login factory-droid`), when present. */
6
+ apiKey?: string;
7
+ fetch?: FetchImpl;
8
+ }
9
+ /**
10
+ * Offline seed: the registry narrowed by the same availability rule live
11
+ * discovery applies, with no flags or org policy known yet. Feature-gated and
12
+ * explicit-opt-in models stay hidden until an online refresh proves access.
13
+ */
14
+ export declare function factoryDroidSeedModels(scope: AccountScope): ModelSpec<"factory-droid-agent">[];
15
+ /**
16
+ * Availability filter, not a catalog: Factory has no model-listing endpoint,
17
+ * so the KDL registry is narrowed live with `GET /api/feature-flags`
18
+ * (Statsig gates) and the org model policy in
19
+ * `GET /api/organization/managed-settings`. Returns null when no credential
20
+ * resolves or the flags fetch fails — callers keep an offline snapshot, not
21
+ * evidence of current entitlement. Missing policy denies explicit-opt-in models.
22
+ */
23
+ export declare function fetchFactoryDroidModels(options?: FactoryDroidModelDiscoveryOptions): Promise<ModelSpec<"factory-droid-agent">[] | null>;
24
+ /** Account scope plus the upstream rotation resolved for that account. */
25
+ export interface FactoryDroidModelBuildOptions extends AccountScope {
26
+ apiProviders?: readonly string[];
27
+ }
28
+ export declare function buildFactoryDroidModel({ spec, policy }: FactoryDroidRegistryModel, options?: FactoryDroidModelBuildOptions): ModelSpec<"factory-droid-agent">;
@@ -1,5 +1,6 @@
1
1
  export * from "./antigravity.js";
2
2
  export * from "./codex.js";
3
+ export * from "./factory-droid.js";
3
4
  export * from "./gemini.js";
4
5
  export * from "./gemini-cli.js";
5
6
  export * from "./gitlab-duo-workflow.js";
@@ -21,12 +21,12 @@ export interface ProtoMessage {
21
21
  }
22
22
  /** A bidirectional codec for one protobuf message type. */
23
23
  export interface MessageCodec<T extends ProtoMessage = ProtoMessage> {
24
- (value: T): Uint8Array;
24
+ (value: T): Uint8Array<ArrayBuffer>;
25
25
  (value: Uint8Array): T;
26
26
  /** Creates a message with protobuf defaults for omitted fields. */
27
27
  create(value?: Partial<T>): T;
28
28
  /** Encodes one message into protobuf wire bytes. */
29
- encode(value: T): Uint8Array;
29
+ encode(value: T): Uint8Array<ArrayBuffer>;
30
30
  /** Decodes one protobuf message from wire bytes. */
31
31
  decode(value: Uint8Array): T;
32
32
  /** Converts a message to its protobuf JSON representation. */
@@ -91,7 +91,7 @@ export declare function pb<T extends ProtoMessage = ProtoMessage>(typeName: stri
91
91
  /** Creates a message using its codec's protobuf defaults. */
92
92
  export declare function create<TMessage extends ProtoMessage>(codec: MessageCodec<TMessage>, value?: Partial<TMessage>): TMessage;
93
93
  /** Encodes a message using its codec. */
94
- export declare function toBinary<TMessage extends ProtoMessage>(codec: MessageCodec<TMessage>, value: TMessage): Uint8Array;
94
+ export declare function toBinary<TMessage extends ProtoMessage>(codec: MessageCodec<TMessage>, value: TMessage): Uint8Array<ArrayBuffer>;
95
95
  /** Decodes wire bytes using a message codec. */
96
96
  export declare function fromBinary<TMessage extends ProtoMessage>(codec: MessageCodec<TMessage>, value: Uint8Array): TMessage;
97
97
  /** Converts a message to protobuf JSON using its codec. */
@@ -165,5 +165,18 @@ export declare function isVertexExpressOpenAIUrl(baseUrl: string): boolean;
165
165
  export declare function isVertexRawPredictUrl(baseUrl: string): boolean;
166
166
  /** Azure OpenAI deployment-scoped path (`…/deployments/<name>/…`). */
167
167
  export declare function isAzureDeploymentsUrl(baseUrl: string): boolean;
168
+ /**
169
+ * Amazon Bedrock API route on a bedrock-runtime or bedrock-mantle endpoint
170
+ * (public, FIPS, or PrivateLink hostname):
171
+ * - `anthropic`: the Anthropic Messages API under `/anthropic`.
172
+ * - `openai`: the OpenAI-compatible APIs under `/openai`, plus Mantle's
173
+ * documented `/v1` base (`https://bedrock-mantle.<region>.api.aws/v1`).
174
+ *
175
+ * Hostnames are parsed strictly so proxies that embed these hosts in a path
176
+ * do not match.
177
+ */
178
+ export declare function isBedrockRouteUrl(baseUrl: string | undefined, route: "openai" | "anthropic"): boolean;
179
+ /** Amazon Bedrock's OpenAI-compatible routes; see {@link isBedrockRouteUrl}. */
180
+ export declare function isBedrockOpenAIUrl(baseUrl: string | undefined): boolean;
168
181
  /** Alibaba DashScope consumer `compatible-mode` endpoint (rejects multimodal arrays for some text-only SKUs). */
169
182
  export declare function isDashscopeCompatibleModeUrl(baseUrl: string): boolean;
@@ -35,6 +35,16 @@ export interface ModelManagerOptions<TApi extends Api = Api, TModelsDevPayload =
35
35
  cacheTtlMs?: number;
36
36
  /** When true, a successful dynamic fetch is the complete provider catalog and prunes static-only models. */
37
37
  dynamicModelsAuthoritative?: boolean;
38
+ /**
39
+ * When true, a fresh cache never satisfies an online-eligible refresh: the
40
+ * dynamic fetch always runs (an explicit `"offline"` strategy is still
41
+ * honored), and the cache serves only as the fetch-failure fallback. For
42
+ * providers whose dynamic result encodes fast-changing live state that a
43
+ * TTL cache would replay wrongly — e.g. Factory discovery includes live
44
+ * organization model policy and upstream blocks. Cached eligibility is
45
+ * an offline snapshot, not current entitlement.
46
+ */
47
+ alwaysRefetchDynamicModels?: boolean;
38
48
  /** Cached model ids whose presence forces refresh when the static or migration-policy fingerprint changes. */
39
49
  dropCachedModelIdsOnStaticMismatch?: readonly string[];
40
50
  /**
@@ -1,9 +1,13 @@
1
1
  import MODELS from "./models.json";
2
- import type { Api, KnownProvider, Model, ModelCost, Usage } from "./types.js";
2
+ import type { Api, KnownProvider, Model, ModelCost, ModelPricingStatus, Usage } from "./types.js";
3
3
  export type GeneratedProvider = keyof typeof MODELS;
4
+ /** Whether `provider` has bundled rows in models.json (e.g. a provider id authored in KDL). */
5
+ export declare function isGeneratedProvider(provider: string): provider is GeneratedProvider;
4
6
  export declare function getBundledModel<TApi extends Api = Api>(provider: GeneratedProvider, modelId: string): Model<TApi>;
5
7
  export declare function getBundledProviders(): KnownProvider[];
6
8
  export declare function getBundledModels(provider: GeneratedProvider): Model<Api>[];
9
+ /** Resolve display semantics without mistaking an absent rate card for a free model. */
10
+ export declare function getModelPricingStatus(model: Pick<Model<Api>, "cost" | "pricingStatus">): ModelPricingStatus;
7
11
  /** Return the recurring UTC tariff period, independently of its monetary multiplier. */
8
12
  export declare function getTimeBasedPricingPeriod(cost: ModelCost, timestamp?: number): "peak" | "off-peak" | undefined;
9
13
  /** Return the next actual peak/off-peak change strictly after the Unix-ms timestamp. */
@@ -1,4 +1,5 @@
1
- export interface ModelCacheProviderIdOptions {
1
+ import { type AccountScope } from "../wire/factory-droid.js";
2
+ export interface ModelCacheProviderIdOptions extends AccountScope {
2
3
  apiKey?: string;
3
4
  baseUrl?: string;
4
5
  }
@@ -1,8 +1,9 @@
1
1
  import type { CompiledProviderDiscovery } from "../compat/types.js";
2
2
  import type { ModelManagerOptions } from "../model-manager.js";
3
3
  import type { Api, FetchImpl } from "../types.js";
4
+ import type { AccountScope } from "../wire/factory-droid.js";
4
5
  /** Config passed to a provider's runtime model-manager factory. */
5
- export type ModelManagerConfig = {
6
+ export type ModelManagerConfig = AccountScope & {
6
7
  apiKey?: string;
7
8
  baseUrl?: string;
8
9
  fetch?: FetchImpl;
@@ -106,6 +106,27 @@ export interface HuggingfaceModelManagerConfig {
106
106
  fetch?: FetchImpl;
107
107
  }
108
108
  export declare function huggingfaceModelManagerOptions(config?: HuggingfaceModelManagerConfig): ModelManagerOptions<"openai-completions">;
109
+ export interface HelmcodeModelManagerConfig {
110
+ apiKey?: string;
111
+ baseUrl?: string;
112
+ fetch?: FetchImpl;
113
+ }
114
+ /**
115
+ * Helmcode model manager: OpenAI-compatible chat completions at
116
+ * `api.helmcode.com/v1`. `/v1/models` also lists embedding, rerank, TTS, and
117
+ * STT models; the exclusion policy lives in `runtime/behavior.kdl`
118
+ * (`exclude-models provider="helmcode"`).
119
+ *
120
+ * `/v1/models` carries no capability data. Resold frontier ids (Claude, GPT,
121
+ * Gemini) take only capability facts from the first-party vendor's bundled
122
+ * row: reasoning, modalities, context window, output cap, and list price. The
123
+ * rest of that row (thinking shape, compat, native web search, tool dialects,
124
+ * cache semantics) describes the vendor's own API, not this chat-completions
125
+ * proxy; the host's `reasoning_effort` ladders and cache-write pricing live in
126
+ * `providers/helmcode.kdl`. Ids with no Helmcode or vendor row (e.g. a new
127
+ * open-weight model) inherit nothing from other gateways.
128
+ */
129
+ export declare function helmcodeModelManagerOptions(config?: HelmcodeModelManagerConfig): ModelManagerOptions<"openai-completions">;
109
130
  export interface NvidiaModelManagerConfig {
110
131
  apiKey?: string;
111
132
  baseUrl?: string;
@@ -1,4 +1,5 @@
1
1
  import type { DevinModelDiscoveryOptions } from "../discovery/devin.js";
2
+ import { type FactoryDroidModelDiscoveryOptions } from "../discovery/factory-droid.js";
2
3
  import type { ModelManagerOptions } from "../model-manager.js";
3
4
  import type { Api, FetchImpl, Model } from "../types.js";
4
5
  /** One Codex OAuth account to fetch a catalog for. */
@@ -55,6 +56,7 @@ export interface DevinModelManagerConfig {
55
56
  fetch?: DevinModelDiscoveryOptions["fetch"];
56
57
  }
57
58
  export declare function devinModelManagerOptions(config?: DevinModelManagerConfig): ModelManagerOptions<"devin-agent">;
59
+ export declare function factoryDroidModelManagerOptions(config?: FactoryDroidModelDiscoveryOptions): ModelManagerOptions<"factory-droid-agent">;
58
60
  export declare function localModelManagerOptions(): ModelManagerOptions<"local-inference">;
59
61
  export declare function webModelManagerOptions(): ModelManagerOptions<"web-search">;
60
62
  /** Credentials and endpoint overrides for the TypeSafe catalog manager. */
@@ -2,7 +2,7 @@ import type { ModelIdentity } from "./compat/types.js";
2
2
  import type { Effort } from "./effort.js";
3
3
  export type { FetchImpl } from "@oh-my-pi/pi-utils";
4
4
  export type { KnownProvider } from "./provider-models/descriptors.js";
5
- export type KnownApi = "openai-completions" | "openai-responses" | "openrouter" | "openai-codex-responses" | "azure-openai-responses" | "anthropic-messages" | "bedrock-converse-stream" | "google-generative-ai" | "google-gemini-cli" | "google-vertex" | "ollama-chat" | "cursor-agent" | "gitlab-duo-agent" | "devin-agent" | "apple-foundation-models";
5
+ export type KnownApi = "openai-completions" | "openai-responses" | "openrouter" | "openai-codex-responses" | "azure-openai-responses" | "anthropic-messages" | "bedrock-converse-stream" | "google-generative-ai" | "google-gemini-cli" | "google-vertex" | "ollama-chat" | "cursor-agent" | "factory-droid-agent" | "gitlab-duo-agent" | "devin-agent" | "apple-foundation-models";
6
6
  export type Api = KnownApi | (string & {});
7
7
  /** Catalog kinds used to isolate role-specific runners from session chat models. */
8
8
  export declare const MODEL_KINDS: readonly ["chat", "tiny", "image", "tts", "stt", "search", "judge", "embedding", "rerank", "video"];
@@ -206,12 +206,21 @@ export interface OpenAICompat {
206
206
  thinkingKeep?: "all" | false;
207
207
  /** Which reasoning content field to emit on assistant messages. Default: auto-detected. */
208
208
  reasoningContentField?: "reasoning_content" | "reasoning" | "reasoning_text";
209
+ /** Decode Mistral thinking parts in streamed content and replay them as typed assistant content. */
210
+ mistralReasoningContentParts?: boolean;
209
211
  /** Whether assistant tool-call messages must include reasoning content. Default: false. */
210
212
  requiresReasoningContentForToolCalls?: boolean;
211
213
  /** Whether all assistant messages must include reasoning content. Default: false. */
212
214
  requiresReasoningContentForAllAssistantTurns?: boolean;
213
215
  /** Whether the provider accepts a synthetic placeholder (e.g. ".") for missing reasoning_content on tool-call turns. Default: true. Set to false for providers like DeepSeek that validate the exact reasoning_content value. */
214
216
  allowsSyntheticReasoningContentForToolCalls?: boolean;
217
+ /**
218
+ * Value emitted for the reasoning field on tool-call turns when the provider
219
+ * requires it (`requiresReasoningContentForToolCalls`) but no reasoning was
220
+ * captured. Default: "". Some upstreams validate the exact value — the droid
221
+ * proxy's DeepSeek family requires a single space.
222
+ */
223
+ syntheticReasoningContentFallback?: string;
215
224
  /**
216
225
  * Replay preserved thinking blocks as `reasoning_content` (or the configured
217
226
  * `reasoningContentField`) on EVERY assistant turn that carried reasoning,
@@ -481,6 +490,15 @@ export interface AnthropicCompat {
481
490
  * `tools.<n>.custom.strict: Extra inputs are not permitted`.
482
491
  */
483
492
  disableStrictTools?: boolean;
493
+ /**
494
+ * The endpoint is Amazon Bedrock's Anthropic Messages API (`/anthropic` on
495
+ * bedrock-runtime or bedrock-mantle). Requests drop tool `strict`, fit
496
+ * `metadata.user_id` to Bedrock's request-metadata pattern, and may use
497
+ * on-demand compaction. Detected from the model `baseUrl`; set it in
498
+ * models.yml `compat` for a route the URL check cannot see (a proxy, an
499
+ * `ANTHROPIC_BASE_URL` reroute) or to `false` to opt out.
500
+ */
501
+ bedrockMessagesApi?: boolean;
484
502
  /**
485
503
  * Map adaptive thinking (`thinking: { type: "adaptive" }`) to
486
504
  * `{ type: "enabled", budget_tokens }`. Vertex AI rejects the `adaptive`
@@ -508,6 +526,13 @@ export interface AnthropicCompat {
508
526
  * `input_transformations` under the thinking-binding-controls beta.
509
527
  */
510
528
  supportsThinkingBindingControls?: boolean;
529
+ /**
530
+ * Whether the model replaces `thinking: { type: "disabled" }` with
531
+ * `thinking: { type: "between_tools" }` (Claude Sonnet 5.5). The disabled
532
+ * form is rejected with a 400; `between_tools` skips up-front thinking and
533
+ * only allows progress updates between tool calls.
534
+ */
535
+ supportsBetweenToolsThinking?: boolean;
511
536
  /**
512
537
  * Whether the model accepts a forced `tool_choice` (`{ type: "any" }` or
513
538
  * `{ type: "tool", name }`). Claude Fable/Mythos 5 reject forced tool use
@@ -578,6 +603,25 @@ export interface AnthropicCompat {
578
603
  stripImageInput?: boolean;
579
604
  /** Thinking-loop watchdog guard family applied to streamed reasoning. */
580
605
  thinkingLoopGuard?: "gemini" | "deepseek" | "xai";
606
+ /**
607
+ * Drop the enabled `thinking` config and replayed thinking blocks when the
608
+ * conversation is not thinking-led (a proxy contract for non-interleaved
609
+ * budget models). Default: false.
610
+ */
611
+ stripThinkingHistory?: boolean;
612
+ /**
613
+ * Send the `effort-2025-11-24` beta exactly when `output_config.effort`
614
+ * rides the request. Unset keeps the transport's legacy heuristic.
615
+ */
616
+ effortBeta?: boolean;
617
+ /**
618
+ * Wire form of a disabled-thinking turn: omit the field, send
619
+ * `{ type: "disabled" }`, or keep adaptive thinking. Unset keeps the
620
+ * direct-provider behavior.
621
+ */
622
+ disabledThinking?: "omit" | "disabled" | "adaptive";
623
+ /** The model is a fast-mode SKU: send `speed: "fast"` with the fast-mode beta. Default: false. */
624
+ fastMode?: boolean;
581
625
  }
582
626
  /**
583
627
  * Compatibility settings for Bedrock Converse prompt caching. Cache pricing is
@@ -679,9 +723,12 @@ export interface ResolvedOpenAISharedCompat {
679
723
  supportsForcedToolChoice: boolean;
680
724
  supportsNamedToolChoice: boolean;
681
725
  reasoningContentField?: OpenAICompat["reasoningContentField"];
726
+ mistralReasoningContentParts?: boolean;
682
727
  requiresReasoningContentForToolCalls: boolean;
683
728
  requiresReasoningContentForAllAssistantTurns: boolean;
684
729
  allowsSyntheticReasoningContentForToolCalls: boolean;
730
+ /** See {@link OpenAICompat.syntheticReasoningContentFallback}. */
731
+ syntheticReasoningContentFallback?: string;
685
732
  replayReasoningContent: boolean;
686
733
  qwenPreserveThinking: boolean;
687
734
  qwenTemplateReasoningEffort: boolean;
@@ -734,7 +781,7 @@ export interface ResolvedOpenAISharedCompat {
734
781
  * `buildModel`; request handlers read fields and never detect, resolve, or
735
782
  * allocate.
736
783
  */
737
- export type ResolvedOpenAICompat = ResolvedOpenAISharedCompat & Required<Omit<OpenAICompat, "supportsDeveloperRole" | "supportsReasoningEffort" | "reasoningEffortMap" | "supportsReasoningParams" | "supportsReasoningSummary" | "supportsSamplingParams" | "supportsPenaltyAndStopParams" | "thinkingFormat" | "kimiApiFormat" | "reasoningDisableMode" | "omitReasoningEffort" | "includeEncryptedReasoning" | "filterReasoningHistory" | "disableReasoningOnForcedToolChoice" | "disableReasoningOnToolChoice" | "disableReasoningWithTools" | "supportsToolChoice" | "supportsForcedToolChoice" | "supportsNamedToolChoice" | "reasoningContentField" | "requiresReasoningContentForToolCalls" | "requiresReasoningContentForAllAssistantTurns" | "allowsSyntheticReasoningContentForToolCalls" | "replayReasoningContent" | "qwenPreserveThinking" | "qwenTemplateReasoningEffort" | "requiresThinkingAsText" | "requiresMistralToolIds" | "requiresToolResultName" | "requiresAssistantAfterToolResult" | "requiresAssistantContentForToolCalls" | "stripDeepseekSpecialTokens" | "streamMarkupHealingPattern" | "reasoningDeltasMayBeCumulative" | "emptyLengthFinishIsContextError" | "usesOpenAIToolCallIdLimit" | "promptCacheSessionHeader" | "supportsPromptCacheBreakpoints" | "promptCacheBreakpointTtl" | "openRouterRouting" | "isOpenRouterHost" | "supportsStrictMode" | "supportsLongPromptCacheRetention" | "alwaysSendMaxTokens" | "clampOutputToModelMax" | "wireModelIdMode" | "vercelGatewayRouting" | "extraBody" | "toolStrictMode" | "toolSchemaFlavor" | "streamFirstEventTimeoutMs" | "streamIdleTimeoutMs" | "streamRevision" | "cacheControlFormat" | "thinkingKeep" | "strictResponsesPairing" | "supportsImageDetailOriginal" | "supportsConfigurationUpdate" | "supportsSteering" | "stripImageInput" | "thinkingLoopGuard" | "whenThinking">> & {
784
+ export type ResolvedOpenAICompat = ResolvedOpenAISharedCompat & Required<Omit<OpenAICompat, "supportsDeveloperRole" | "supportsReasoningEffort" | "reasoningEffortMap" | "supportsReasoningParams" | "supportsReasoningSummary" | "supportsSamplingParams" | "supportsPenaltyAndStopParams" | "thinkingFormat" | "kimiApiFormat" | "reasoningDisableMode" | "omitReasoningEffort" | "includeEncryptedReasoning" | "filterReasoningHistory" | "disableReasoningOnForcedToolChoice" | "disableReasoningOnToolChoice" | "disableReasoningWithTools" | "supportsToolChoice" | "supportsForcedToolChoice" | "supportsNamedToolChoice" | "reasoningContentField" | "mistralReasoningContentParts" | "requiresReasoningContentForToolCalls" | "requiresReasoningContentForAllAssistantTurns" | "allowsSyntheticReasoningContentForToolCalls" | "syntheticReasoningContentFallback" | "replayReasoningContent" | "qwenPreserveThinking" | "qwenTemplateReasoningEffort" | "requiresThinkingAsText" | "requiresMistralToolIds" | "requiresToolResultName" | "requiresAssistantAfterToolResult" | "requiresAssistantContentForToolCalls" | "stripDeepseekSpecialTokens" | "streamMarkupHealingPattern" | "reasoningDeltasMayBeCumulative" | "emptyLengthFinishIsContextError" | "usesOpenAIToolCallIdLimit" | "promptCacheSessionHeader" | "supportsPromptCacheBreakpoints" | "promptCacheBreakpointTtl" | "openRouterRouting" | "isOpenRouterHost" | "supportsStrictMode" | "supportsLongPromptCacheRetention" | "alwaysSendMaxTokens" | "clampOutputToModelMax" | "wireModelIdMode" | "vercelGatewayRouting" | "extraBody" | "toolStrictMode" | "toolSchemaFlavor" | "streamFirstEventTimeoutMs" | "streamIdleTimeoutMs" | "streamRevision" | "cacheControlFormat" | "thinkingKeep" | "strictResponsesPairing" | "supportsImageDetailOriginal" | "supportsConfigurationUpdate" | "supportsSteering" | "stripImageInput" | "thinkingLoopGuard" | "whenThinking">> & {
738
785
  vercelGatewayRouting?: OpenAICompat["vercelGatewayRouting"];
739
786
  extraBody?: OpenAICompat["extraBody"];
740
787
  cacheControlFormat?: OpenAICompat["cacheControlFormat"];
@@ -804,9 +851,23 @@ export interface ResolvedOpenAIResponsesCompat extends ResolvedOpenAISharedCompa
804
851
  */
805
852
  export type ResolvedOpenRouterCompat = ResolvedOpenAICompat & ResolvedOpenAIResponsesCompat;
806
853
  /** Fully-resolved anthropic-messages compat view (same contract as `ResolvedOpenAICompat`). */
807
- export type ResolvedAnthropicCompat = Required<Omit<AnthropicCompat, "streamIdleTimeoutMs" | "thinkingLoopGuard">> & {
854
+ export type ResolvedAnthropicCompat = Required<Omit<AnthropicCompat, "streamIdleTimeoutMs" | "thinkingLoopGuard" | "bedrockMessagesApi" | "effortBeta" | "disabledThinking" | "stripThinkingHistory" | "fastMode">> & {
855
+ /** Effort-beta override; undefined keeps the transport's legacy heuristic. */
856
+ effortBeta?: AnthropicCompat["effortBeta"];
857
+ /** Disabled-thinking wire form; undefined keeps the direct-provider behavior. */
858
+ disabledThinking?: AnthropicCompat["disabledThinking"];
859
+ /** Strip thinking history on non-thinking-led turns; undefined behaves as false. */
860
+ stripThinkingHistory?: AnthropicCompat["stripThinkingHistory"];
861
+ /** Fast-mode SKU; undefined behaves as false. */
862
+ fastMode?: AnthropicCompat["fastMode"];
808
863
  /** Thinking-loop watchdog guard family applied to streamed reasoning. */
809
864
  thinkingLoopGuard?: AnthropicCompat["thinkingLoopGuard"];
865
+ /**
866
+ * Bedrock's Anthropic Messages API (see {@link AnthropicCompat.bedrockMessagesApi}).
867
+ * `true` when detected or configured; otherwise unset, so rows baked before
868
+ * this field keep matching.
869
+ */
870
+ bedrockMessagesApi?: boolean;
810
871
  /**
811
872
  * Stream-watchdog idle-timeout fallback in ms for slow reasoning hosts; 0 disables the idle watchdog.
812
873
  * Undefined defers to `PI_STREAM_IDLE_TIMEOUT_MS`, then the legacy
@@ -923,6 +984,8 @@ export interface TokenCost {
923
984
  cacheRead: number;
924
985
  cacheWrite: number;
925
986
  }
987
+ /** User-facing interpretation of a model's token-rate card. */
988
+ export type ModelPricingStatus = "fixed" | "free" | "included" | "variable" | "unknown";
926
989
  /**
927
990
  * Rates applied to the full request when its prompt exceeds `inputThreshold`,
928
991
  * or reaches it when `inputThresholdInclusive` is true. Prompt input is the
@@ -980,6 +1043,17 @@ export interface ModelAccountAccess {
980
1043
  */
981
1044
  cyberPrograms?: readonly string[];
982
1045
  }
1046
+ /** One Cursor `RequestedModel.parameters` entry recovered from rich discovery. */
1047
+ export interface CursorModelParameter {
1048
+ id: string;
1049
+ value: string;
1050
+ }
1051
+ /** Cursor wire target selected after local effort/variant routing. */
1052
+ export interface CursorModelRoute {
1053
+ modelId: string;
1054
+ parameters: readonly CursorModelParameter[];
1055
+ maxMode?: boolean;
1056
+ }
983
1057
  export interface Model<TApi extends Api = Api> {
984
1058
  id: string;
985
1059
  /** Role-specific runner capability; omitted for ordinary chat models. */
@@ -1088,6 +1162,29 @@ export interface Model<TApi extends Api = Api> {
1088
1162
  * bundled/config rows and on single-account discovery.
1089
1163
  */
1090
1164
  accountAccess?: Readonly<Record<string, ModelAccountAccess>>;
1165
+ /** Cursor `RequestedModel.parameters` for this model's default variant. */
1166
+ cursorModelParameters?: readonly CursorModelParameter[];
1167
+ /**
1168
+ * Per-wire-id Cursor routes recovered from `AvailableModels`. Generic effort
1169
+ * collapse retains these while routing selects one key at request time.
1170
+ */
1171
+ cursorModelRoutes?: Readonly<Record<string, CursorModelRoute>>;
1172
+ /** Cursor's account-relative model price/multiplier; not a per-token USD rate. */
1173
+ cursorPrice?: number;
1174
+ /** Cursor requires data retention to invoke this model. */
1175
+ cursorRequiresDataRetention?: boolean;
1176
+ /** Cursor explicitly reports support for its Agent surface. */
1177
+ cursorSupportsAgent?: boolean;
1178
+ /** Cursor explicitly reports support for sandboxed execution. */
1179
+ cursorSupportsSandboxing?: boolean;
1180
+ /** Factory Droid: account-resolved upstream rotation (first entry is the default `x-api-provider`). */
1181
+ factoryDroidApiProviders?: string[];
1182
+ /** Factory Droid: `configured_order` when live routing chose the rotation; absent means registry order. */
1183
+ factoryDroidRoutingSource?: "configured_order";
1184
+ /** Factory Droid Standard Credits base rate (relative per-token weight, not dollars). */
1185
+ factoryDroidCredits?: number;
1186
+ /** Canonical organization resolved during Factory discovery. */
1187
+ factoryDroidOrgId?: string;
1091
1188
  cost: ModelCost;
1092
1189
  /**
1093
1190
  * Prompt-cache entry lifetime per retention tier, in seconds. Populated only
@@ -1097,9 +1194,16 @@ export interface Model<TApi extends Api = Api> {
1097
1194
  * Custom models and provider overrides opt in via models.yml `promptCache`.
1098
1195
  */
1099
1196
  promptCache?: ModelPromptCache;
1197
+ /**
1198
+ * Interpretation of an all-zero token-rate card. Omitted zero-rate cards
1199
+ * are unknown; any non-zero rate is always treated as fixed pricing.
1200
+ */
1201
+ pricingStatus?: Exclude<ModelPricingStatus, "fixed">;
1100
1202
  /** Premium Copilot requests charged per user-initiated request (defaults to 1). */
1101
1203
  premiumMultiplier?: number;
1102
1204
  contextWindow: number | null;
1205
+ /** Preserve the host's supplied window instead of applying inferred expansion or reference-price caps. */
1206
+ contextWindowAuthoritative?: boolean;
1103
1207
  /** Optional larger prompt window available when extended context is enabled. */
1104
1208
  maxContextWindow?: number;
1105
1209
  maxTokens: number | null;
@@ -1143,6 +1247,12 @@ export interface Model<TApi extends Api = Api> {
1143
1247
  useResponsesLite?: boolean;
1144
1248
  /** Codex Code Mode restriction: model expects tools routed through a programmatic exec surface (mirrors codex-rs `tool_mode`). */
1145
1249
  toolMode?: "code_mode_only";
1250
+ /**
1251
+ * Service-tier ids the provider advertises for this model (Codex discovery
1252
+ * `service_tiers[].id`, e.g. `priority`, `ultrafast`). Absent when the
1253
+ * provider publishes no per-model tier list.
1254
+ */
1255
+ serviceTiers?: readonly string[];
1146
1256
  /** Preferred model to switch to when context promotion is triggered (model id or provider/id). */
1147
1257
  contextPromotionTarget?: string;
1148
1258
  /** Preferred model to use only for compaction (model id or provider/id); the active session model is unchanged. */
@@ -1166,6 +1276,8 @@ export interface Model<TApi extends Api = Api> {
1166
1276
  isNew?: boolean;
1167
1277
  /** Upstream marks this model as beta / preview quality. */
1168
1278
  isBeta?: boolean;
1279
+ /** Authenticated provider catalog marks this as the account's default model. */
1280
+ isProviderDefault?: boolean;
1169
1281
  /** Upstream marks this model as one of its recommended picks. */
1170
1282
  isRecommended?: boolean;
1171
1283
  /** Canonical thinking capability metadata for this model. */
@@ -7,10 +7,12 @@ export declare const CODEX_BASE_URL = "https://chatgpt.com/backend-api";
7
7
  * Updated from npm by `bun run check-spoofed-versions --update`.
8
8
  *
9
9
  * The backend version-gates model availability against this value on both
10
- * `/models?client_version=` and `/responses`; 0.155.1 exposes GPT-6 Sol and Luna.
11
- * An older pin silently hides newer SKUs from discovery.
10
+ * `/models?client_version=` and `/responses`, independently of a model's
11
+ * declared `minimal_client_version`: GPT-6.1 Sol declares 0.153.0 yet is
12
+ * omitted from `/models` at 0.155.1 and listed at 0.159.0. An older pin
13
+ * silently hides newer SKUs from discovery.
12
14
  */
13
- export declare const CODEX_CLIENT_VERSION = "0.155.1";
15
+ export declare const CODEX_CLIENT_VERSION = "0.159.0";
14
16
  export declare const OPENAI_HEADERS: {
15
17
  readonly BETA: "OpenAI-Beta";
16
18
  /** Codex feature-negotiation header; values identify opt-in wire protocols. */
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Cursor wire constants shared by authenticated catalog discovery, the pi-ai
3
+ * provider, and account usage. Keep the announced client build aligned with the
4
+ * released Cursor agent CLI because the service gates protocol features on it.
5
+ */
6
+ /** Default host for Cursor's Connect RPCs and account API. */
7
+ export declare const CURSOR_DEFAULT_BASE_URL = "https://api2.cursor.sh";
8
+ /** Released Cursor agent CLI build whose protocol surface this client mirrors. */
9
+ export declare const CURSOR_CLIENT_VERSION = "cli-2026.09.02-c22c1a3";
10
+ export declare const CURSOR_RUN_PATH = "/agent.v1.AgentService/Run";
11
+ export declare const CURSOR_RUN_SSE_PATH = "/agent.v1.AgentService/RunSSE";
12
+ export declare const CURSOR_BIDI_APPEND_PATH = "/aiserver.v1.BidiService/BidiAppend";
13
+ export declare const CURSOR_GET_USABLE_MODELS_PATH = "/agent.v1.AgentService/GetUsableModels";
14
+ export declare const CURSOR_GET_DEFAULT_MODEL_PATH = "/agent.v1.AgentService/GetDefaultModelForCli";
15
+ export declare const CURSOR_AVAILABLE_MODELS_PATH = "/aiserver.v1.AiService/AvailableModels";
16
+ /** Headers shared by Cursor's authenticated CLI RPCs. */
17
+ export declare function cursorClientHeaders(apiKey: string, options?: {
18
+ clientVersion?: string;
19
+ contentType?: "application/proto" | "application/connect+proto";
20
+ }): Record<string, string>;
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Client version reported to Factory's API. When bumping it, recapture the
3
+ * native request corpus with `packages/ai/scripts/capture-factory-droid-native.ts`
4
+ * against the matching CLI binary and make `factory-droid-native-parity.test.ts` pass.
5
+ */
6
+ export declare const FACTORY_DROID_CLIENT_VERSION = "0.230.0";
7
+ /** Wire protocols the Factory proxy multiplexes; `api-routes provider="factory-droid"` picks one per model. */
8
+ export declare const FACTORY_DROID_WIRES: readonly ["openai-completions", "openai-responses", "anthropic-messages", "google-generate"];
9
+ /** Wire protocol used by the Factory proxy for each model. */
10
+ export type FactoryDroidWire = (typeof FACTORY_DROID_WIRES)[number];
11
+ /**
12
+ * Upstream routers the proxy dispatches to, sent as the `x-api-provider`
13
+ * header. The KDL compiler validates the rotation and region axes against it.
14
+ */
15
+ export declare const FACTORY_DROID_UPSTREAMS: readonly ["fireworks", "baseten", "mistral", "anthropic", "azure_anthropic", "vertex_anthropic", "bedrock_anthropic", "openai", "azure_openai", "bedrock_openai", "google", "xai", "snowflake", "databricks"];
16
+ /** Inference serving region; independent from account/API-host residency. */
17
+ export type FactoryDroidRegion = "global" | "us" | "eu";
18
+ /**
19
+ * Account scope a Factory credential resolves to. Residency (`region`) selects
20
+ * the API host; `inferenceRegion` selects eligible upstreams and capacities.
21
+ * An EU-host account can legitimately have global inference.
22
+ */
23
+ export interface AccountScope {
24
+ region?: string;
25
+ inferenceRegion?: FactoryDroidRegion;
26
+ orgId?: string;
27
+ }
28
+ /** Inference region for an account: explicit scope wins, else EU residency infers EU, else global. */
29
+ export declare function resolveFactoryDroidInferenceRegion(scope: {
30
+ region?: string;
31
+ inferenceRegion?: FactoryDroidRegion;
32
+ }): FactoryDroidRegion;
33
+ /** Factory API host per residency region; EU accounts are served from the EU region. */
34
+ export declare function factoryDroidApiBaseUrl(region: string | undefined): string;
35
+ /** Per-wire base URL for an account region; the stream layer appends the path suffix. */
36
+ export declare function factoryDroidWireBaseUrl(wire: FactoryDroidWire, region: string | undefined): string;
37
+ /**
38
+ * Client identity headers shared by discovery, usage and inference requests.
39
+ * Callers add their own auth, `Accept`, routing and SDK headers.
40
+ */
41
+ export declare function factoryDroidClientHeaders(orgId: string | undefined): Record<string, string>;
42
+ /** Model-cache namespace for a Factory credential and its account scope. */
43
+ export declare function factoryDroidModelCacheProviderId(options: {
44
+ apiKey?: string;
45
+ } & AccountScope): string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oh-my-pi/pi-catalog",
3
- "version": "18.4.3",
3
+ "version": "18.4.5",
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.3",
97
- "@oh-my-pi/pi-utils": "18.4.3"
96
+ "@oh-my-pi/omptype": "18.4.5",
97
+ "@oh-my-pi/pi-utils": "18.4.5"
98
98
  },
99
99
  "devDependencies": {
100
100
  "@bgotink/kdl": "0.4.0",
101
- "@oh-my-pi/pi-ai": "18.4.3",
101
+ "@oh-my-pi/pi-ai": "18.4.5",
102
102
  "@types/bun": "^1.3.14"
103
103
  },
104
104
  "engines": {
package/src/build.ts CHANGED
@@ -76,6 +76,11 @@ function isInputModalities(value: unknown): value is ("text" | "image")[] {
76
76
  function applyCatalogAssignments<TApi extends Api>(model: Model<TApi>, catalog: Record<string, unknown>): void {
77
77
  const kind = MODEL_KINDS.find(value => value === catalog.kind);
78
78
  if (kind !== undefined) model.kind = kind;
79
+ if (catalog.contextWindowAuthoritative === true) {
80
+ model.contextWindowAuthoritative = true;
81
+ } else {
82
+ delete model.contextWindowAuthoritative;
83
+ }
79
84
  const webSearch = catalog.webSearch;
80
85
  if (
81
86
  webSearch === "gemini" ||
@@ -120,6 +125,15 @@ function applyCatalogAssignments<TApi extends Api>(model: Model<TApi>, catalog:
120
125
  if (editPromptVariant === "full" || editPromptVariant === "compact") {
121
126
  model.editPromptVariant = editPromptVariant;
122
127
  }
128
+ const pricingStatus = catalog.pricingStatus;
129
+ if (
130
+ pricingStatus === "free" ||
131
+ pricingStatus === "included" ||
132
+ pricingStatus === "variable" ||
133
+ pricingStatus === "unknown"
134
+ ) {
135
+ model.pricingStatus = pricingStatus;
136
+ }
123
137
  const requiresCursorToolSchemaProjection = catalog.requiresCursorToolSchemaProjection;
124
138
  if (requiresCursorToolSchemaProjection === true) {
125
139
  model.requiresCursorToolSchemaProjection = true;
@@ -137,6 +151,11 @@ function applyCatalogAssignments<TApi extends Api>(model: Model<TApi>, catalog:
137
151
  } else {
138
152
  delete model.supportsAssistantPrefill;
139
153
  }
154
+ // The same KDL output-cap contract gates host options and the Responses
155
+ // transport. Materialize it because the transport consumes the model field.
156
+ if (typeof catalog.omitMaxOutputTokens === "boolean" && model.omitMaxOutputTokens === undefined) {
157
+ model.omitMaxOutputTokens = catalog.omitMaxOutputTokens;
158
+ }
140
159
  const contextPromotionTarget = catalog.contextPromotionTarget;
141
160
  if (typeof contextPromotionTarget === "string" && model.contextPromotionTarget === undefined) {
142
161
  model.contextPromotionTarget = contextPromotionTarget;
@@ -5,7 +5,7 @@
5
5
  * pi-ai both consume these; URL matching is the one detection surface that
6
6
  * stays in code.
7
7
  */
8
- import { hostMatchesUrl } from "../hosts";
8
+ import { hostMatchesUrl, isBedrockRouteUrl } from "../hosts";
9
9
 
10
10
  const OFFICIAL_ANTHROPIC_URL = "https://api.anthropic.com";
11
11
 
@@ -27,6 +27,15 @@ const VERTEX_ANTHROPIC_URL_MARKER = /aiplatform\.googleapis\.com\/.+\/publishers
27
27
  const BEDROCK_ANTHROPIC_URL_MARKER = /(?:^|\/\/|\.)bedrock-runtime\.[a-z0-9-]+\.amazonaws\.com/i;
28
28
  const AZURE_ANTHROPIC_URL_MARKER = /(?:^|\/\/|\.)[a-z0-9-]+\.(?:inference|services)\.ai\.azure\.com/i;
29
29
 
30
+ /**
31
+ * Amazon Bedrock's Anthropic Messages API: the `/anthropic` path on the
32
+ * bedrock-runtime or bedrock-mantle endpoint. It rejects the tool `strict`
33
+ * field and enforces Anthropic signatures on replay.
34
+ */
35
+ export function isBedrockAnthropicRoute(baseUrl?: string): boolean {
36
+ return isBedrockRouteUrl(baseUrl, "anthropic");
37
+ }
38
+
30
39
  /**
31
40
  * Azure AI Inference / Foundry Anthropic route
32
41
  * (`<resource>.inference.ai.azure.com`, `<resource>.services.ai.azure.com`).
@@ -53,6 +62,7 @@ export function isAnthropicSigningProxyUrl(baseUrl?: string): boolean {
53
62
  (CLOUDFLARE_ANTHROPIC_GATEWAY_URL_MARKER.test(baseUrl) ||
54
63
  VERTEX_ANTHROPIC_URL_MARKER.test(baseUrl) ||
55
64
  BEDROCK_ANTHROPIC_URL_MARKER.test(baseUrl))) ||
65
+ isBedrockAnthropicRoute(baseUrl) ||
56
66
  isAzureAnthropicRoute(baseUrl)
57
67
  );
58
68
  }
@@ -24,6 +24,7 @@ export type AuthProviderId =
24
24
  | "deepseek"
25
25
  | "devin"
26
26
  | "exa"
27
+ | "factory-droid"
27
28
  | "firepass"
28
29
  | "fireworks"
29
30
  | "github-copilot"
@@ -35,6 +36,7 @@ export type AuthProviderId =
35
36
  | "google-gemini-cli"
36
37
  | "google-vertex"
37
38
  | "groq"
39
+ | "helmcode"
38
40
  | "huggingface"
39
41
  | "kagi"
40
42
  | "kilo"
@@ -113,6 +115,7 @@ export type LoginProviderId =
113
115
  | "deepseek"
114
116
  | "devin"
115
117
  | "exa"
118
+ | "factory-droid"
116
119
  | "firepass"
117
120
  | "fireworks"
118
121
  | "github-copilot"
@@ -121,6 +124,7 @@ export type LoginProviderId =
121
124
  | "gmi-cloud"
122
125
  | "google-antigravity"
123
126
  | "google-gemini-cli"
127
+ | "helmcode"
124
128
  | "huggingface"
125
129
  | "kagi"
126
130
  | "kilo"