@oh-my-pi/pi-catalog 16.0.8 → 16.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [16.0.9] - 2026-06-18
6
+
7
+ ### Fixed
8
+
9
+ - Fixed GitHub Copilot's `anthropic-messages` proxy being misclassified as a non-signing reasoning endpoint (`replayUnsignedThinking: true`). It forwards to signature-enforcing Anthropic, so replaying a stripped/unsigned historical `thinking` block as `signature: ""` — most visibly an end_turn-bound checkpoint/branch-return turn whose signature the transform must strip — caused a `400 Invalid signature` that corrupted the session and re-tripped on every full history re-send (e.g. after toggling MCP servers). Copilot now degrades such blocks to text like the official API. ([#2851](https://github.com/can1357/oh-my-pi/issues/2851))
10
+ - Added a `supportsImageDetailOriginal` compat flag that resolves to `false` for GitHub Copilot, whose Responses endpoint rejects the `detail: "original"` image hint with a 400, and `true` for every other host. ([#2822](https://github.com/can1357/oh-my-pi/issues/2822))
11
+
5
12
  ## [16.0.8] - 2026-06-18
6
13
 
7
14
  ### Changed
@@ -247,6 +247,8 @@ export interface OpenAICompat {
247
247
  alwaysSendMaxTokens?: boolean;
248
248
  /** Whether Responses-API tool-call/result history must be strictly paired. Default: auto-detected (Azure OpenAI, GitHub Copilot). */
249
249
  strictResponsesPairing?: boolean;
250
+ /** Whether the Responses API accepts the `detail: "original"` image hint. Default: auto-detected (false for GitHub Copilot, which rejects it with a 400). */
251
+ supportsImageDetailOriginal?: boolean;
250
252
  /**
251
253
  * Append a trailing `# Juice: 0 !important` developer item when the caller
252
254
  * did not request reasoning, suppressing default reasoning on models that
@@ -416,7 +418,7 @@ export interface ResolvedOpenAISharedCompat {
416
418
  * `buildModel`; request handlers read fields and never detect, resolve, or
417
419
  * allocate.
418
420
  */
419
- export type ResolvedOpenAICompat = ResolvedOpenAISharedCompat & Required<Omit<OpenAICompat, "supportsDeveloperRole" | "supportsReasoningEffort" | "reasoningEffortMap" | "supportsReasoningParams" | "thinkingFormat" | "reasoningDisableMode" | "omitReasoningEffort" | "includeEncryptedReasoning" | "filterReasoningHistory" | "disableReasoningOnForcedToolChoice" | "disableReasoningOnToolChoice" | "supportsToolChoice" | "supportsForcedToolChoice" | "reasoningContentField" | "requiresReasoningContentForToolCalls" | "requiresReasoningContentForAllAssistantTurns" | "allowsSyntheticReasoningContentForToolCalls" | "requiresThinkingAsText" | "requiresMistralToolIds" | "requiresToolResultName" | "requiresAssistantAfterToolResult" | "requiresAssistantContentForToolCalls" | "stripDeepseekSpecialTokens" | "streamMarkupHealingPattern" | "reasoningDeltasMayBeCumulative" | "emptyLengthFinishIsContextError" | "usesOpenAIToolCallIdLimit" | "promptCacheSessionHeader" | "openRouterRouting" | "isOpenRouterHost" | "supportsStrictMode" | "supportsLongPromptCacheRetention" | "alwaysSendMaxTokens" | "wireModelIdMode" | "vercelGatewayRouting" | "extraBody" | "toolStrictMode" | "toolSchemaFlavor" | "streamIdleTimeoutMs" | "cacheControlFormat" | "thinkingKeep" | "strictResponsesPairing" | "requiresJuiceZeroHack" | "enableGeminiThinkingLoopGuard" | "whenThinking">> & {
421
+ export type ResolvedOpenAICompat = ResolvedOpenAISharedCompat & Required<Omit<OpenAICompat, "supportsDeveloperRole" | "supportsReasoningEffort" | "reasoningEffortMap" | "supportsReasoningParams" | "thinkingFormat" | "reasoningDisableMode" | "omitReasoningEffort" | "includeEncryptedReasoning" | "filterReasoningHistory" | "disableReasoningOnForcedToolChoice" | "disableReasoningOnToolChoice" | "supportsToolChoice" | "supportsForcedToolChoice" | "reasoningContentField" | "requiresReasoningContentForToolCalls" | "requiresReasoningContentForAllAssistantTurns" | "allowsSyntheticReasoningContentForToolCalls" | "requiresThinkingAsText" | "requiresMistralToolIds" | "requiresToolResultName" | "requiresAssistantAfterToolResult" | "requiresAssistantContentForToolCalls" | "stripDeepseekSpecialTokens" | "streamMarkupHealingPattern" | "reasoningDeltasMayBeCumulative" | "emptyLengthFinishIsContextError" | "usesOpenAIToolCallIdLimit" | "promptCacheSessionHeader" | "openRouterRouting" | "isOpenRouterHost" | "supportsStrictMode" | "supportsLongPromptCacheRetention" | "alwaysSendMaxTokens" | "wireModelIdMode" | "vercelGatewayRouting" | "extraBody" | "toolStrictMode" | "toolSchemaFlavor" | "streamIdleTimeoutMs" | "cacheControlFormat" | "thinkingKeep" | "strictResponsesPairing" | "supportsImageDetailOriginal" | "requiresJuiceZeroHack" | "enableGeminiThinkingLoopGuard" | "whenThinking">> & {
420
422
  vercelGatewayRouting?: OpenAICompat["vercelGatewayRouting"];
421
423
  extraBody?: OpenAICompat["extraBody"];
422
424
  cacheControlFormat?: OpenAICompat["cacheControlFormat"];
@@ -434,6 +436,7 @@ export type ResolvedOpenAICompat = ResolvedOpenAISharedCompat & Required<Omit<Op
434
436
  export interface ResolvedOpenAIResponsesCompat extends ResolvedOpenAISharedCompat {
435
437
  supportsLongPromptCacheRetention: boolean;
436
438
  strictResponsesPairing: boolean;
439
+ supportsImageDetailOriginal: boolean;
437
440
  requiresJuiceZeroHack: boolean;
438
441
  supportsObfuscationOptOut: boolean;
439
442
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-catalog",
4
- "version": "16.0.8",
4
+ "version": "16.0.9",
5
5
  "description": "Model catalog for omp: bundled model database, provider discovery descriptors, model identity, classification, and equivalence",
6
6
  "homepage": "https://omp.sh",
7
7
  "author": "Can Boluk",
@@ -34,12 +34,12 @@
34
34
  },
35
35
  "dependencies": {
36
36
  "@bufbuild/protobuf": "^2.12.0",
37
- "@oh-my-pi/pi-utils": "16.0.8",
37
+ "@oh-my-pi/pi-utils": "16.0.9",
38
38
  "arktype": "^2.2.0",
39
39
  "zod": "^4"
40
40
  },
41
41
  "devDependencies": {
42
- "@oh-my-pi/pi-ai": "16.0.8",
42
+ "@oh-my-pi/pi-ai": "16.0.9",
43
43
  "@types/bun": "^1.3.14"
44
44
  },
45
45
  "engines": {
@@ -66,7 +66,16 @@ export function buildAnthropicCompat(spec: ModelSpec<"anthropic-messages">): Res
66
66
  // loses the reasoning chain and can destabilize the next tool-call
67
67
  // arguments (#2005). Known non-signing hosts (Z.AI, DeepSeek) are also
68
68
  // preserved for compatibility.
69
- replayUnsignedThinking: isZai || modelMatchesHost(spec, "deepseekFamily") || (spec.reasoning && !official),
69
+ //
70
+ // GitHub Copilot's `anthropic-messages` proxy is excluded: it forwards to
71
+ // signature-enforcing Anthropic and returns full thinking signatures, so it
72
+ // is a SIGNING endpoint. Replaying a stripped/unsigned thinking block as
73
+ // `signature: ""` there 400s the whole request ("Invalid signature") — most
74
+ // visibly when a checkpoint/branch-return turn's end_turn-bound signature is
75
+ // stripped on replay (issue #2851). Treating it like official Anthropic
76
+ // degrades such blocks to text instead, which the API accepts.
77
+ replayUnsignedThinking:
78
+ !isCopilot && (isZai || modelMatchesHost(spec, "deepseekFamily") || (spec.reasoning && !official)),
70
79
  escapeBuiltinToolNames: modelMatchesHost(spec, "umans"),
71
80
  };
72
81
  applyCompatOverrides(compat, spec.compat);
@@ -459,6 +459,12 @@ export function buildOpenAIResponsesCompat(spec: OpenAIResponsesSpecLike): Resol
459
459
  // Azure OpenAI and GitHub Copilot Responses paths require tool results
460
460
  // to strictly match prior tool calls when building Responses inputs.
461
461
  strictResponsesPairing: isAzure || spec.provider === "github-copilot",
462
+ // GitHub Copilot's Responses endpoint rejects the `detail: "original"`
463
+ // image hint with a 400; every other host preserves native-resolution
464
+ // frames (snapcompact relies on `original`). Detect Copilot by provider id
465
+ // or base-URL host (mirroring the Anthropic compat builder) so a model
466
+ // pointed at the Copilot host under a different provider id still clamps.
467
+ supportsImageDetailOriginal: !modelMatchesHost({ provider: spec.provider, baseUrl }, "githubCopilot"),
462
468
  requiresJuiceZeroHack: spec.name.toLowerCase().startsWith("gpt-5"),
463
469
  reasoningEffortMap: {},
464
470
  supportsReasoningParams: true,
@@ -514,6 +520,7 @@ function pickResponsesOnly(compat: ResolvedOpenAIResponsesCompat): ResponsesOnly
514
520
  return {
515
521
  supportsLongPromptCacheRetention: compat.supportsLongPromptCacheRetention,
516
522
  strictResponsesPairing: compat.strictResponsesPairing,
523
+ supportsImageDetailOriginal: compat.supportsImageDetailOriginal,
517
524
  requiresJuiceZeroHack: compat.requiresJuiceZeroHack,
518
525
  supportsObfuscationOptOut: compat.supportsObfuscationOptOut,
519
526
  } satisfies ResponsesOnlyCompat;
package/src/types.ts CHANGED
@@ -283,6 +283,8 @@ export interface OpenAICompat {
283
283
  alwaysSendMaxTokens?: boolean;
284
284
  /** Whether Responses-API tool-call/result history must be strictly paired. Default: auto-detected (Azure OpenAI, GitHub Copilot). */
285
285
  strictResponsesPairing?: boolean;
286
+ /** Whether the Responses API accepts the `detail: "original"` image hint. Default: auto-detected (false for GitHub Copilot, which rejects it with a 400). */
287
+ supportsImageDetailOriginal?: boolean;
286
288
  /**
287
289
  * Append a trailing `# Juice: 0 !important` developer item when the caller
288
290
  * did not request reasoning, suppressing default reasoning on models that
@@ -504,6 +506,7 @@ export type ResolvedOpenAICompat = ResolvedOpenAISharedCompat &
504
506
  | "cacheControlFormat"
505
507
  | "thinkingKeep"
506
508
  | "strictResponsesPairing"
509
+ | "supportsImageDetailOriginal"
507
510
  | "requiresJuiceZeroHack"
508
511
  | "enableGeminiThinkingLoopGuard"
509
512
  | "whenThinking"
@@ -527,6 +530,7 @@ export type ResolvedOpenAICompat = ResolvedOpenAISharedCompat &
527
530
  export interface ResolvedOpenAIResponsesCompat extends ResolvedOpenAISharedCompat {
528
531
  supportsLongPromptCacheRetention: boolean;
529
532
  strictResponsesPairing: boolean;
533
+ supportsImageDetailOriginal: boolean;
530
534
  requiresJuiceZeroHack: boolean;
531
535
  supportsObfuscationOptOut: boolean;
532
536
  }