@oh-my-pi/pi-catalog 18.4.4 → 18.4.6

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 (61) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/dist/types/compat/auth-ids.d.ts +2 -2
  3. package/dist/types/compat/axes.d.ts +3 -1
  4. package/dist/types/compat/factory-droid.d.ts +65 -0
  5. package/dist/types/compat/provider-ids.d.ts +1 -1
  6. package/dist/types/compat/resolve.d.ts +8 -2
  7. package/dist/types/compat/types.d.ts +26 -1
  8. package/dist/types/discovery/cursor-proto.d.ts +284 -0
  9. package/dist/types/discovery/cursor.d.ts +12 -6
  10. package/dist/types/discovery/factory-droid.d.ts +28 -0
  11. package/dist/types/discovery/index.d.ts +1 -0
  12. package/dist/types/discovery/protobuf.d.ts +3 -3
  13. package/dist/types/model-manager.d.ts +10 -0
  14. package/dist/types/models.d.ts +5 -1
  15. package/dist/types/provider-models/cache-provider-id.d.ts +2 -1
  16. package/dist/types/provider-models/descriptor-types.d.ts +2 -1
  17. package/dist/types/provider-models/openai-compat.d.ts +21 -0
  18. package/dist/types/provider-models/special.d.ts +2 -0
  19. package/dist/types/types.d.ts +100 -3
  20. package/dist/types/wire/cursor.d.ts +20 -0
  21. package/dist/types/wire/factory-droid.d.ts +45 -0
  22. package/package.json +4 -4
  23. package/src/build.ts +21 -0
  24. package/src/compat/auth-ids.ts +4 -0
  25. package/src/compat/axes.ts +113 -2
  26. package/src/compat/cascade.ts +8 -1
  27. package/src/compat/collapse.ts +3 -0
  28. package/src/compat/factory-droid.ts +170 -0
  29. package/src/compat/provider-ids.ts +2 -0
  30. package/src/compat/resolve.ts +47 -5
  31. package/src/compat/rules/README.md +18 -8
  32. package/src/compat/rules/auth/_order.kdl +2 -2
  33. package/src/compat/rules/auth/factory-droid.kdl +28 -0
  34. package/src/compat/rules/auth/helmcode.kdl +9 -0
  35. package/src/compat/rules/classes/anthropic.kdl +195 -80
  36. package/src/compat/rules/providers/commandcode.kdl +21 -5
  37. package/src/compat/rules/providers/cursor.kdl +29 -17
  38. package/src/compat/rules/providers/factory-droid.kdl +1166 -0
  39. package/src/compat/rules/providers/helmcode.kdl +120 -0
  40. package/src/compat/rules/runtime/behavior.kdl +65 -4
  41. package/src/compat/rules/taxonomy/_collapse.kdl +36 -14
  42. package/src/compat/rules.json +1 -1
  43. package/src/compat/types.ts +27 -1
  44. package/src/discovery/cursor-models.proto +261 -0
  45. package/src/discovery/cursor-proto.ts +471 -0
  46. package/src/discovery/cursor.ts +817 -88
  47. package/src/discovery/devin.ts +1 -3
  48. package/src/discovery/factory-droid.ts +296 -0
  49. package/src/discovery/index.ts +1 -0
  50. package/src/discovery/protobuf.ts +16 -7
  51. package/src/model-manager.ts +13 -0
  52. package/src/models.json +1 -1
  53. package/src/models.ts +13 -0
  54. package/src/provider-models/cache-provider-id.ts +37 -6
  55. package/src/provider-models/descriptor-types.ts +2 -1
  56. package/src/provider-models/descriptors.ts +4 -0
  57. package/src/provider-models/openai-compat.ts +83 -17
  58. package/src/provider-models/special.ts +26 -2
  59. package/src/types.ts +112 -1
  60. package/src/wire/cursor.ts +32 -0
  61. package/src/wire/factory-droid.ts +130 -0
@@ -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. */
@@ -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,
@@ -594,6 +603,25 @@ export interface AnthropicCompat {
594
603
  stripImageInput?: boolean;
595
604
  /** Thinking-loop watchdog guard family applied to streamed reasoning. */
596
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;
597
625
  }
598
626
  /**
599
627
  * Compatibility settings for Bedrock Converse prompt caching. Cache pricing is
@@ -623,6 +651,12 @@ export interface BedrockCompat {
623
651
  * `PI_OPENAI_STREAM_IDLE_TIMEOUT_MS` alias, then the 300s default.
624
652
  */
625
653
  streamIdleTimeoutMs?: number;
654
+ /**
655
+ * Whether the model accepts a forced `toolChoice` (`any` / `tool`). Claude
656
+ * Opus/Sonnet 5.5 reject it outright; the request builder downgrades forced
657
+ * choices to `auto` when this is false. Default: true.
658
+ */
659
+ supportsForcedToolChoice?: boolean;
626
660
  }
627
661
  /** Fully-resolved Bedrock Converse prompt-cache capabilities, materialized once by `buildModel`. */
628
662
  export interface ResolvedBedrockCompat {
@@ -632,6 +666,7 @@ export interface ResolvedBedrockCompat {
632
666
  supportsLongPromptCacheRetention: boolean;
633
667
  promptCacheMinimumTokens: number;
634
668
  promptCacheMaximumCheckpoints: number;
669
+ supportsForcedToolChoice: boolean;
635
670
  /**
636
671
  * Stream-watchdog idle-timeout fallback in ms for hosts with no keepalive
637
672
  * events; 0 disables the idle watchdog. Undefined defers to
@@ -695,9 +730,12 @@ export interface ResolvedOpenAISharedCompat {
695
730
  supportsForcedToolChoice: boolean;
696
731
  supportsNamedToolChoice: boolean;
697
732
  reasoningContentField?: OpenAICompat["reasoningContentField"];
733
+ mistralReasoningContentParts?: boolean;
698
734
  requiresReasoningContentForToolCalls: boolean;
699
735
  requiresReasoningContentForAllAssistantTurns: boolean;
700
736
  allowsSyntheticReasoningContentForToolCalls: boolean;
737
+ /** See {@link OpenAICompat.syntheticReasoningContentFallback}. */
738
+ syntheticReasoningContentFallback?: string;
701
739
  replayReasoningContent: boolean;
702
740
  qwenPreserveThinking: boolean;
703
741
  qwenTemplateReasoningEffort: boolean;
@@ -750,7 +788,7 @@ export interface ResolvedOpenAISharedCompat {
750
788
  * `buildModel`; request handlers read fields and never detect, resolve, or
751
789
  * allocate.
752
790
  */
753
- 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">> & {
791
+ 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">> & {
754
792
  vercelGatewayRouting?: OpenAICompat["vercelGatewayRouting"];
755
793
  extraBody?: OpenAICompat["extraBody"];
756
794
  cacheControlFormat?: OpenAICompat["cacheControlFormat"];
@@ -820,7 +858,15 @@ export interface ResolvedOpenAIResponsesCompat extends ResolvedOpenAISharedCompa
820
858
  */
821
859
  export type ResolvedOpenRouterCompat = ResolvedOpenAICompat & ResolvedOpenAIResponsesCompat;
822
860
  /** Fully-resolved anthropic-messages compat view (same contract as `ResolvedOpenAICompat`). */
823
- export type ResolvedAnthropicCompat = Required<Omit<AnthropicCompat, "streamIdleTimeoutMs" | "thinkingLoopGuard" | "bedrockMessagesApi">> & {
861
+ export type ResolvedAnthropicCompat = Required<Omit<AnthropicCompat, "streamIdleTimeoutMs" | "thinkingLoopGuard" | "bedrockMessagesApi" | "effortBeta" | "disabledThinking" | "stripThinkingHistory" | "fastMode">> & {
862
+ /** Effort-beta override; undefined keeps the transport's legacy heuristic. */
863
+ effortBeta?: AnthropicCompat["effortBeta"];
864
+ /** Disabled-thinking wire form; undefined keeps the direct-provider behavior. */
865
+ disabledThinking?: AnthropicCompat["disabledThinking"];
866
+ /** Strip thinking history on non-thinking-led turns; undefined behaves as false. */
867
+ stripThinkingHistory?: AnthropicCompat["stripThinkingHistory"];
868
+ /** Fast-mode SKU; undefined behaves as false. */
869
+ fastMode?: AnthropicCompat["fastMode"];
824
870
  /** Thinking-loop watchdog guard family applied to streamed reasoning. */
825
871
  thinkingLoopGuard?: AnthropicCompat["thinkingLoopGuard"];
826
872
  /**
@@ -945,6 +991,8 @@ export interface TokenCost {
945
991
  cacheRead: number;
946
992
  cacheWrite: number;
947
993
  }
994
+ /** User-facing interpretation of a model's token-rate card. */
995
+ export type ModelPricingStatus = "fixed" | "free" | "included" | "variable" | "unknown";
948
996
  /**
949
997
  * Rates applied to the full request when its prompt exceeds `inputThreshold`,
950
998
  * or reaches it when `inputThresholdInclusive` is true. Prompt input is the
@@ -1002,6 +1050,17 @@ export interface ModelAccountAccess {
1002
1050
  */
1003
1051
  cyberPrograms?: readonly string[];
1004
1052
  }
1053
+ /** One Cursor `RequestedModel.parameters` entry recovered from rich discovery. */
1054
+ export interface CursorModelParameter {
1055
+ id: string;
1056
+ value: string;
1057
+ }
1058
+ /** Cursor wire target selected after local effort/variant routing. */
1059
+ export interface CursorModelRoute {
1060
+ modelId: string;
1061
+ parameters: readonly CursorModelParameter[];
1062
+ maxMode?: boolean;
1063
+ }
1005
1064
  export interface Model<TApi extends Api = Api> {
1006
1065
  id: string;
1007
1066
  /** Role-specific runner capability; omitted for ordinary chat models. */
@@ -1110,6 +1169,29 @@ export interface Model<TApi extends Api = Api> {
1110
1169
  * bundled/config rows and on single-account discovery.
1111
1170
  */
1112
1171
  accountAccess?: Readonly<Record<string, ModelAccountAccess>>;
1172
+ /** Cursor `RequestedModel.parameters` for this model's default variant. */
1173
+ cursorModelParameters?: readonly CursorModelParameter[];
1174
+ /**
1175
+ * Per-wire-id Cursor routes recovered from `AvailableModels`. Generic effort
1176
+ * collapse retains these while routing selects one key at request time.
1177
+ */
1178
+ cursorModelRoutes?: Readonly<Record<string, CursorModelRoute>>;
1179
+ /** Cursor's account-relative model price/multiplier; not a per-token USD rate. */
1180
+ cursorPrice?: number;
1181
+ /** Cursor requires data retention to invoke this model. */
1182
+ cursorRequiresDataRetention?: boolean;
1183
+ /** Cursor explicitly reports support for its Agent surface. */
1184
+ cursorSupportsAgent?: boolean;
1185
+ /** Cursor explicitly reports support for sandboxed execution. */
1186
+ cursorSupportsSandboxing?: boolean;
1187
+ /** Factory Droid: account-resolved upstream rotation (first entry is the default `x-api-provider`). */
1188
+ factoryDroidApiProviders?: string[];
1189
+ /** Factory Droid: `configured_order` when live routing chose the rotation; absent means registry order. */
1190
+ factoryDroidRoutingSource?: "configured_order";
1191
+ /** Factory Droid Standard Credits base rate (relative per-token weight, not dollars). */
1192
+ factoryDroidCredits?: number;
1193
+ /** Canonical organization resolved during Factory discovery. */
1194
+ factoryDroidOrgId?: string;
1113
1195
  cost: ModelCost;
1114
1196
  /**
1115
1197
  * Prompt-cache entry lifetime per retention tier, in seconds. Populated only
@@ -1119,9 +1201,22 @@ export interface Model<TApi extends Api = Api> {
1119
1201
  * Custom models and provider overrides opt in via models.yml `promptCache`.
1120
1202
  */
1121
1203
  promptCache?: ModelPromptCache;
1204
+ /**
1205
+ * Verbatim configured lifetimes (models.yml, `modelOverrides`, runtime
1206
+ * registrations). `buildModel` applies them over catalog `prompt-cache`
1207
+ * rules on every rebuild; `{}` keeps warming disabled.
1208
+ */
1209
+ promptCacheConfig?: ModelPromptCache;
1210
+ /**
1211
+ * Interpretation of an all-zero token-rate card. Omitted zero-rate cards
1212
+ * are unknown; any non-zero rate is always treated as fixed pricing.
1213
+ */
1214
+ pricingStatus?: Exclude<ModelPricingStatus, "fixed">;
1122
1215
  /** Premium Copilot requests charged per user-initiated request (defaults to 1). */
1123
1216
  premiumMultiplier?: number;
1124
1217
  contextWindow: number | null;
1218
+ /** Preserve the host's supplied window instead of applying inferred expansion or reference-price caps. */
1219
+ contextWindowAuthoritative?: boolean;
1125
1220
  /** Optional larger prompt window available when extended context is enabled. */
1126
1221
  maxContextWindow?: number;
1127
1222
  maxTokens: number | null;
@@ -1194,6 +1289,8 @@ export interface Model<TApi extends Api = Api> {
1194
1289
  isNew?: boolean;
1195
1290
  /** Upstream marks this model as beta / preview quality. */
1196
1291
  isBeta?: boolean;
1292
+ /** Authenticated provider catalog marks this as the account's default model. */
1293
+ isProviderDefault?: boolean;
1197
1294
  /** Upstream marks this model as one of its recommended picks. */
1198
1295
  isRecommended?: boolean;
1199
1296
  /** Canonical thinking capability metadata for this model. */
@@ -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.4",
3
+ "version": "18.4.6",
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.4",
97
- "@oh-my-pi/pi-utils": "18.4.4"
96
+ "@oh-my-pi/omptype": "18.4.6",
97
+ "@oh-my-pi/pi-utils": "18.4.6"
98
98
  },
99
99
  "devDependencies": {
100
100
  "@bgotink/kdl": "0.4.0",
101
- "@oh-my-pi/pi-ai": "18.4.4",
101
+ "@oh-my-pi/pi-ai": "18.4.6",
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;
@@ -344,5 +363,7 @@ export function buildModel<TApi extends Api>(spec: ModelSpec<TApi>): Model<TApi>
344
363
  };
345
364
  applyCatalogAssignments(model, policy.catalog);
346
365
  applyCatalogCorrections(model, policy.catalog);
366
+ // Configured lifetimes replace catalog lifetimes rather than merging with them.
367
+ if (spec.promptCacheConfig !== undefined) model.promptCache = { ...spec.promptCacheConfig };
347
368
  return model;
348
369
  }
@@ -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"