@jeffreycao/copilot-api 2.1.5 → 2.1.7

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/README.md CHANGED
@@ -398,7 +398,7 @@ requires_openai_auth = true
398
398
  supports_websockets = false
399
399
  wire_api = "responses"
400
400
  request_max_retries = 3
401
- stream_max_retries = 1
401
+ stream_max_retries = 3
402
402
  stream_idle_timeout_ms = 300000
403
403
 
404
404
  [features]
@@ -417,6 +417,8 @@ enabled = false
417
417
 
418
418
  When a Codex client (`User-Agent` starts with `codex`) requests the top-level `GET /v1/models`, the gateway merges native Codex models with models available through the Messages adapter. The latter advertise `use_responses_lite: true`: `/v1/responses` uses **Responses → Messages** for Anthropic providers, while OpenAI-compatible providers and Chat-only Copilot models reuse the existing Messages route for **Responses → Messages → Chat Completions**, then translate streaming or JSON results back to Responses.
419
419
 
420
+ For Codex clients, only `gpt-*` Copilot models use the native Responses API; non-GPT Copilot models always go through the adapter, even when they advertise native `/responses` support.
421
+
420
422
  Responses Lite tool definitions are read from `input.additional_tools`, without relying on top-level `tools`. Function, `namespace`, and custom tools are supported; clients must declare `apply_patch` as `type: "custom"`, and it is not handled as a standalone special tool type. Returned calls recover their original `name` and `namespace`. Tools are collected before old history is trimmed, so compaction requests retain them. The Messages fallback does not support Responses `tool_search` mode. Anthropic `output_config.effort` keeps the project's existing valid levels; Responses `minimal` maps to `low`, while `none` omits Anthropic effort.
421
423
 
422
424
  When Codex uses the top-level GitHub Copilot route with `approvals_reviewer = "auto_review"`, map its internal review model to a Responses-capable Copilot model in the gateway's `config.json`:
package/README.zh-CN.md CHANGED
@@ -426,7 +426,7 @@ requires_openai_auth = true
426
426
  supports_websockets = false
427
427
  wire_api = "responses"
428
428
  request_max_retries = 3
429
- stream_max_retries = 1
429
+ stream_max_retries = 3
430
430
  stream_idle_timeout_ms = 300000
431
431
 
432
432
  [features]
@@ -445,6 +445,8 @@ enabled = false
445
445
 
446
446
  Codex 客户端(`User-Agent` 以 `codex` 开头)请求顶层 `GET /v1/models` 时,网关会把原生 Codex 模型与可通过 Messages 适配的模型合并返回。后者会声明 `use_responses_lite: true`:调用 `/v1/responses` 后,Anthropic provider 走 **Responses → Messages**,OpenAI 兼容 provider 以及只支持 Chat 的 Copilot 模型则复用现有 Messages 路由继续走 **Responses → Messages → Chat Completions**,最终统一翻译回 Responses(包括流式事件)。
447
447
 
448
+ 对 Codex 客户端而言,只有 `gpt-*` Copilot 模型走原生 Responses API;非 GPT Copilot 模型一律走适配路径,即使声明支持原生 `/responses` 也不例外。
449
+
448
450
  Responses Lite 的工具定义从 `input` 中的 `additional_tools` 读取,而不是依赖顶层 `tools`。该适配支持 function、`namespace` 和 custom tool;`apply_patch` 需要由客户端声明为 `type: "custom"`,不会作为独立工具类型特殊处理。工具调用返回时会恢复原始 `name` 与 `namespace`;压缩请求在裁剪旧历史前先保存工具定义,因此压缩期间也不会丢失工具。Messages 回退路径不支持 Responses `tool_search` 模式。Anthropic 的 `output_config.effort` 仍只使用项目既有的合法档位;Responses 的 `minimal` 会降级为 `low`,`none` 则不向 Anthropic 发送 effort。
449
451
 
450
452
  当 Codex 通过顶层 GitHub Copilot 路由并设置 `approvals_reviewer = "auto_review"` 时,可在网关的 `config.json` 中将内部审核模型映射到一个支持 Responses API 的 Copilot 模型:
package/dist/main.js CHANGED
@@ -25,7 +25,7 @@ bindElectronFetch();
25
25
  const { auth } = await import("./auth-t5Gi1JB9.js");
26
26
  const { debug } = await import("./debug-2GkXJBt5.js");
27
27
  const { mcp } = await import("./mcp-BG6fpi6q.js");
28
- const { start } = await import("./start-DzKOeRTn.js");
28
+ const { start } = await import("./start-D5W3xtWv.js");
29
29
  await runMain(defineCommand({
30
30
  meta: {
31
31
  name: "copilot-api",
@@ -3458,6 +3458,7 @@ function translateToOpenAI(payload, options = {}) {
3458
3458
  const model = state.models?.data.find((m) => m.id === modelId);
3459
3459
  const thinkingBudget = getThinkingBudget(payload, model);
3460
3460
  const reasoningEffort = getReasoningEffort(payload, options);
3461
+ const promptCacheKey = requestContext.getStore()?.sessionAffinity?.trim();
3461
3462
  return {
3462
3463
  model: modelId,
3463
3464
  messages: translateAnthropicMessagesToOpenAI(payload, modelId, {
@@ -3473,7 +3474,8 @@ function translateToOpenAI(payload, options = {}) {
3473
3474
  tools: translateAnthropicToolsToOpenAI(payload.tools),
3474
3475
  tool_choice: translateAnthropicToolChoiceToOpenAI(payload.tool_choice),
3475
3476
  thinking_budget: thinkingBudget,
3476
- ...reasoningEffort ? { reasoning_effort: reasoningEffort } : {}
3477
+ ...reasoningEffort ? { reasoning_effort: reasoningEffort } : {},
3478
+ ...promptCacheKey ? { prompt_cache_key: promptCacheKey } : {}
3477
3479
  };
3478
3480
  }
3479
3481
  function getReasoningEffort(payload, options) {
@@ -6355,7 +6357,7 @@ const CODEX_MODELS = [
6355
6357
  ]
6356
6358
  },
6357
6359
  {
6358
- contextWindow: 105e4,
6360
+ contextWindow: 272e3,
6359
6361
  id: "gpt-5.6-sol",
6360
6362
  input: ["text", "image"],
6361
6363
  maxTokens: 128e3,
@@ -6370,7 +6372,7 @@ const CODEX_MODELS = [
6370
6372
  ]
6371
6373
  },
6372
6374
  {
6373
- contextWindow: 105e4,
6375
+ contextWindow: 272e3,
6374
6376
  id: "gpt-5.6-terra",
6375
6377
  input: ["text", "image"],
6376
6378
  maxTokens: 128e3,
@@ -6385,7 +6387,7 @@ const CODEX_MODELS = [
6385
6387
  ]
6386
6388
  },
6387
6389
  {
6388
- contextWindow: 105e4,
6390
+ contextWindow: 272e3,
6389
6391
  id: "gpt-5.6-luna",
6390
6392
  input: ["text", "image"],
6391
6393
  maxTokens: 128e3,
@@ -8090,15 +8092,20 @@ function getCopilotCodexCandidates() {
8090
8092
  function isCopilotCodexCandidate(model) {
8091
8093
  return (model.supported_endpoints ?? []).some((endpoint) => endpoint === MESSAGES_ENDPOINT || endpoint === CHAT_COMPLETIONS_ENDPOINT || RESPONSES_ENDPOINTS.has(endpoint)) && model.capabilities.supports.tool_calls !== false;
8092
8094
  }
8095
+ function describeCopilotAdapter(model) {
8096
+ const supportsResponses = model.supported_endpoints?.some((endpoint) => RESPONSES_ENDPOINTS.has(endpoint));
8097
+ if (model.id.startsWith("gpt") && supportsResponses) return `${model.name} through the Copilot Responses API.`;
8098
+ if (model.supported_endpoints?.includes(MESSAGES_ENDPOINT)) return `${model.name} through the Copilot Messages adapter.`;
8099
+ if (supportsResponses) return `${model.name} through the Copilot Messages-to-Responses adapter.`;
8100
+ return `${model.name} through the Copilot Messages-to-Chat adapter.`;
8101
+ }
8093
8102
  function createCopilotCodexCandidate(model) {
8094
8103
  const reasoningEfforts = normalizeReasoningEfforts(model.capabilities.supports.reasoning_effort);
8095
- const usesNativeResponses = model.supported_endpoints?.some((endpoint) => RESPONSES_ENDPOINTS.has(endpoint));
8096
- const usesNativeMessages = model.supported_endpoints?.includes(MESSAGES_ENDPOINT);
8097
8104
  return {
8098
8105
  slug: toClientModelId(model.id),
8099
8106
  displayName: model.name,
8100
- description: usesNativeResponses ? `${model.name} through the Copilot Responses API.` : usesNativeMessages ? `${model.name} through the Copilot Messages adapter.` : `${model.name} through the Copilot Messages-to-Chat adapter.`,
8101
- contextWindow: positiveNumber(model.capabilities.limits.max_context_window_tokens, 256e3),
8107
+ description: describeCopilotAdapter(model),
8108
+ contextWindow: positiveNumber(model.capabilities.limits.max_prompt_tokens, 256e3),
8102
8109
  maxOutputTokens: positiveNumber(model.capabilities.limits.max_output_tokens, 32e3),
8103
8110
  inputModalities: model.capabilities.supports.vision ? ["text", "image"] : ["text"],
8104
8111
  reasoningEfforts,
@@ -8177,15 +8184,16 @@ function normalizeInputModalities(value) {
8177
8184
  if (!Array.isArray(value)) return [];
8178
8185
  return [...new Set(value.filter((modality) => modality === "text" || modality === "image"))];
8179
8186
  }
8180
- function resolveInputModalities(providerName, configuredModalities, remoteModalities, builtinModalities) {
8181
- if (configuredModalities.length > 0) return configuredModalities;
8182
- if (providerName === "kimi") {
8183
- const modalities = remoteModalities.length > 0 ? remoteModalities : builtinModalities.length > 0 ? builtinModalities : ["text"];
8184
- return [...new Set([...modalities, "image"])];
8185
- }
8187
+ function fallbackModalities(remoteModalities, builtinModalities) {
8186
8188
  if (remoteModalities.length > 0) return remoteModalities;
8187
8189
  return builtinModalities.length > 0 ? builtinModalities : ["text"];
8188
8190
  }
8191
+ function resolveInputModalities(providerName, configuredModalities, remoteModalities, builtinModalities) {
8192
+ if (configuredModalities.length > 0) return configuredModalities;
8193
+ const modalities = fallbackModalities(remoteModalities, builtinModalities);
8194
+ if (providerName === "kimi") return [...new Set([...modalities, "image"])];
8195
+ return modalities;
8196
+ }
8189
8197
  function selectDefaultReasoningEffort(efforts, configured) {
8190
8198
  if (configured && efforts.includes(configured)) return configured;
8191
8199
  if (efforts.includes("max")) return "max";
@@ -8400,6 +8408,7 @@ function translateResponsesToMessages(payload, options) {
8400
8408
  if (messages.length === 0) throw new ResponsesMessagesTranslationError("Responses input must contain at least one translatable message");
8401
8409
  applyEphemeralCacheControl(messages, system);
8402
8410
  const reasoningEffort = translateReasoningEffort(payload.reasoning?.effort);
8411
+ const metadataUserId = resolveMetadataUserId(payload);
8403
8412
  const messagesPayload = {
8404
8413
  model: options.model,
8405
8414
  messages,
@@ -8412,7 +8421,7 @@ function translateResponsesToMessages(payload, options) {
8412
8421
  tool_choice: translateToolChoice(payload.tool_choice, registry),
8413
8422
  ...reasoningEffort ? { output_config: { effort: reasoningEffort } } : {},
8414
8423
  ...payload.service_tier === "auto" || payload.service_tier === "standard_only" ? { service_tier: payload.service_tier } : {},
8415
- ...resolveMetadataUserId(payload) ? { metadata: { user_id: resolveMetadataUserId(payload) } } : {}
8424
+ ...metadataUserId ? { metadata: { user_id: metadataUserId } } : {}
8416
8425
  };
8417
8426
  return {
8418
8427
  compaction: normalized.compaction,
@@ -8920,6 +8929,8 @@ function translateReasoningEffort(effort) {
8920
8929
  if (effort === "low" || effort === "medium" || effort === "high" || effort === "xhigh" || effort === "max") return effort;
8921
8930
  }
8922
8931
  function resolveMetadataUserId(payload) {
8932
+ const sessionAffinity = requestContext.getStore()?.sessionAffinity?.trim();
8933
+ if (sessionAffinity) return sessionAffinity;
8923
8934
  const metadataUserId = payload.metadata?.user_id;
8924
8935
  if (metadataUserId?.trim()) return metadataUserId;
8925
8936
  if (payload.safety_identifier?.trim()) return payload.safety_identifier;
@@ -10422,4 +10433,4 @@ server.route("/:provider/images", providerImageRoutes);
10422
10433
  //#endregion
10423
10434
  export { server };
10424
10435
 
10425
- //# sourceMappingURL=server-DyVtfan1.js.map
10436
+ //# sourceMappingURL=server-s6RzK7Oo.js.map