@jeffreycao/copilot-api 1.10.1 → 1.10.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  import { t as PATHS } from "./paths-DC-mqCY3.js";
2
- import { D as generateTraceId, E as prepareMessageProxyHeaders, I as state, M as compactMessageSections, O as requestContext, P as compactSystemPromptStarts, T as prepareInteractionHeaders, _ as copilotWebSocketHeaders, c as getUUID, d as sleep, f as getCopilotUsage, g as copilotHeaders, h as copilotBaseUrl, j as compactAutoContinuePromptStarts, k as resolveTraceId$1, l as isNullish, m as forwardError, n as cacheModels, o as generateRequestIdFromPayload, p as HTTPError, s as getRootSessionId, u as parseUserIdMetadata, w as prepareForCompact } from "./utils-C5ej0z8n.js";
3
- import { a as getConfig, c as getReasoningEffortForModel, d as isResponsesApiContextManagementModel, f as isResponsesApiWebSearchEnabled, i as getClaudeTokenMultiplier, l as getSmallModel, o as getExtraPromptForModel, p as isResponsesApiWebSocketEnabled, r as getAnthropicApiKey, s as getProviderConfig, t as getProxyEnvDispatcher, u as isMessagesApiEnabled } from "./proxy-De0Po8kG.js";
2
+ import { D as generateTraceId, E as prepareMessageProxyHeaders, I as state, M as compactMessageSections, O as requestContext, P as compactSystemPromptStarts, T as prepareInteractionHeaders, _ as copilotWebSocketHeaders, c as getUUID, d as sleep, f as getCopilotUsage, g as copilotHeaders, h as copilotBaseUrl, j as compactAutoContinuePromptStarts, k as resolveTraceId$1, l as isNullish, m as forwardError, n as cacheModels, o as generateRequestIdFromPayload, p as HTTPError, s as getRootSessionId, u as parseUserIdMetadata, w as prepareForCompact } from "./utils-jHLgqAq2.js";
3
+ import { _ as setModelMappings, a as getConfig, c as getProviderConfig, d as isMessagesApiEnabled, f as isResponsesApiContextManagementModel, g as resolveMappedModel, i as getClaudeTokenMultiplier, l as getReasoningEffortForModel, m as isResponsesApiWebSocketEnabled, o as getExtraPromptForModel, p as isResponsesApiWebSearchEnabled, r as getAnthropicApiKey, s as getModelMappings, t as getProxyEnvDispatcher, u as getSmallModel } from "./proxy-DQLzdeq3.js";
4
4
  import consola from "consola";
5
5
  import fs from "node:fs/promises";
6
6
  import path from "node:path";
@@ -12,6 +12,7 @@ import fs$1, { readFileSync } from "node:fs";
12
12
  import { streamSSE } from "hono/streaming";
13
13
  import util from "node:util";
14
14
  import { events } from "fetch-event-stream";
15
+ import { z } from "zod";
15
16
  import { WebSocket } from "undici";
16
17
  //#region src/lib/request-auth.ts
17
18
  function normalizeApiKeys(apiKeys) {
@@ -26,6 +27,14 @@ function normalizeApiKeys(apiKeys) {
26
27
  function getConfiguredApiKeys() {
27
28
  return normalizeApiKeys(getConfig().auth?.apiKeys);
28
29
  }
30
+ function normalizeApiKey(apiKey) {
31
+ if (typeof apiKey !== "string") return null;
32
+ return apiKey.trim() || null;
33
+ }
34
+ function getConfiguredAdminApiKeys() {
35
+ const adminApiKey = normalizeApiKey(getConfig().auth?.adminApiKey);
36
+ return adminApiKey ? [adminApiKey] : [];
37
+ }
29
38
  function extractRequestApiKey(c) {
30
39
  const xApiKey = c.req.header("x-api-key")?.trim();
31
40
  if (xApiKey) return xApiKey;
@@ -46,11 +55,14 @@ function createAuthMiddleware(options = {}) {
46
55
  const getApiKeys = options.getApiKeys ?? getConfiguredApiKeys;
47
56
  const allowUnauthenticatedPaths = options.allowUnauthenticatedPaths ?? ["/"];
48
57
  const allowOptionsBypass = options.allowOptionsBypass ?? true;
58
+ const allowWhenNoApiKeys = options.allowWhenNoApiKeys ?? true;
59
+ const shouldSkipPath = options.shouldSkipPath ?? (() => false);
49
60
  return async (c, next) => {
50
61
  if (allowOptionsBypass && c.req.method === "OPTIONS") return next();
62
+ if (shouldSkipPath(c.req.path)) return next();
51
63
  if (allowUnauthenticatedPaths.includes(c.req.path)) return next();
52
64
  const apiKeys = getApiKeys();
53
- if (apiKeys.length === 0) return next();
65
+ if (apiKeys.length === 0) return allowWhenNoApiKeys ? next() : createUnauthorizedResponse(c);
54
66
  const requestApiKey = extractRequestApiKey(c);
55
67
  if (!requestApiKey || !apiKeys.includes(requestApiKey)) return createUnauthorizedResponse(c);
56
68
  return next();
@@ -1085,6 +1097,32 @@ completionRoutes.post("/", async (c) => {
1085
1097
  }
1086
1098
  });
1087
1099
  //#endregion
1100
+ //#region src/routes/admin/config/route.ts
1101
+ const configRoutes = new Hono();
1102
+ const modelMappingsRequestSchema = z.object({ modelMappings: z.record(z.string(), z.string()) });
1103
+ configRoutes.get("/model-mappings", (c) => {
1104
+ return c.json({
1105
+ configPath: PATHS.CONFIG_PATH,
1106
+ modelMappings: getModelMappings()
1107
+ });
1108
+ });
1109
+ configRoutes.post("/model-mappings", async (c) => {
1110
+ try {
1111
+ const parseResult = modelMappingsRequestSchema.safeParse(await c.req.json());
1112
+ if (!parseResult.success) return c.json({ error: {
1113
+ message: parseResult.error.issues[0]?.message ?? "Invalid request body.",
1114
+ type: "invalid_request_error"
1115
+ } }, 400);
1116
+ const updatedModelMappings = setModelMappings(parseResult.data.modelMappings);
1117
+ return c.json({
1118
+ configPath: PATHS.CONFIG_PATH,
1119
+ modelMappings: updatedModelMappings
1120
+ });
1121
+ } catch (error) {
1122
+ return await forwardError(c, error);
1123
+ }
1124
+ });
1125
+ //#endregion
1088
1126
  //#region src/services/copilot/create-embeddings.ts
1089
1127
  const createEmbeddings = async (payload) => {
1090
1128
  if (!state.copilotToken) throw new Error("Copilot token not found");
@@ -1819,6 +1857,7 @@ async function countTokensViaAnthropic(c, payload) {
1819
1857
  */
1820
1858
  async function handleCountTokens(c) {
1821
1859
  const anthropicPayload = await c.req.json();
1860
+ anthropicPayload.model = resolveMappedModel(anthropicPayload.model);
1822
1861
  const providerModelAlias = parseProviderModelAlias(anthropicPayload.model);
1823
1862
  if (providerModelAlias) {
1824
1863
  anthropicPayload.model = providerModelAlias.model;
@@ -2777,6 +2816,12 @@ const removeResponsesWebSocketPoolEntry = (poolKey, entry) => {
2777
2816
  const unrefTimer = (timer) => {
2778
2817
  if (typeof timer === "object" && "unref" in timer && typeof timer.unref === "function") timer.unref();
2779
2818
  };
2819
+ const createResponsesWebSocketError = (message, event) => {
2820
+ const reason = event?.error ?? event?.message;
2821
+ if (reason === void 0 || reason === "") return new Error(message);
2822
+ const cause = toError(reason);
2823
+ return new Error(`${message}: ${cause.message}`, { cause });
2824
+ };
2780
2825
  const openResponsesWebSocket = async ({ headers, url }) => await new Promise((resolve, reject) => {
2781
2826
  const dispatcher = getProxyEnvDispatcher();
2782
2827
  const websocket = new WebSocket(url, dispatcher ? {
@@ -2791,9 +2836,9 @@ const openResponsesWebSocket = async ({ headers, url }) => await new Promise((re
2791
2836
  cleanup();
2792
2837
  resolve(websocket);
2793
2838
  };
2794
- const onError = () => {
2839
+ const onError = (event) => {
2795
2840
  cleanup();
2796
- reject(/* @__PURE__ */ new Error("Failed to create responses websocket"));
2841
+ reject(createResponsesWebSocketError("Failed to create responses websocket", event));
2797
2842
  };
2798
2843
  websocket.addEventListener("open", onOpen);
2799
2844
  websocket.addEventListener("error", onError);
@@ -2815,8 +2860,8 @@ const createWebSocketMessageStream = async function* (websocket) {
2815
2860
  closed = true;
2816
2861
  wake();
2817
2862
  };
2818
- const onError = () => {
2819
- error = /* @__PURE__ */ new Error("Responses websocket stream error");
2863
+ const onError = (event) => {
2864
+ error = createResponsesWebSocketError("Responses websocket stream error", event);
2820
2865
  wake();
2821
2866
  };
2822
2867
  websocket.addEventListener("message", onMessage);
@@ -2905,14 +2950,23 @@ const MESSAGE_TYPE = "message";
2905
2950
  const COMPACTION_SIGNATURE_PREFIX = "cm1#";
2906
2951
  const COMPACTION_SIGNATURE_SEPARATOR = "@";
2907
2952
  const THINKING_TEXT = "Thinking...";
2908
- const translateAnthropicMessagesToResponsesPayload = (payload) => {
2953
+ const buildPromptCacheKey = (basePromptCacheKey, subagentAgentId) => {
2954
+ if (!basePromptCacheKey) return null;
2955
+ const normalizedSubagentAgentId = subagentAgentId?.trim() || null;
2956
+ if (!normalizedSubagentAgentId) return basePromptCacheKey;
2957
+ return `${basePromptCacheKey}:agent:${normalizedSubagentAgentId}`;
2958
+ };
2959
+ const translateAnthropicMessagesToResponsesPayload = (payload, subagentAgentId) => {
2909
2960
  const input = [];
2910
2961
  const applyPhase = shouldApplyPhase(payload.model);
2911
2962
  for (const message of payload.messages) input.push(...translateMessage(message, payload.model, applyPhase));
2963
+ const hasOriginalTools = Array.isArray(payload.tools) && payload.tools.length > 0;
2912
2964
  const translatedTools = convertAnthropicTools(payload.tools);
2913
2965
  const toolChoice = convertAnthropicToolChoice(payload.tool_choice);
2914
- const { sessionId: promptCacheKey } = parseUserIdMetadata(payload.metadata?.user_id);
2915
- return {
2966
+ const { sessionId: metadataPromptCacheKey } = parseUserIdMetadata(payload.metadata?.user_id);
2967
+ const sessionAffinity = requestContext.getStore()?.sessionAffinity?.trim() || null;
2968
+ const promptCacheKey = buildPromptCacheKey(metadataPromptCacheKey ?? sessionAffinity, subagentAgentId);
2969
+ const responsesPayload = {
2916
2970
  model: payload.model,
2917
2971
  input,
2918
2972
  instructions: translateSystemPrompt(payload.system, payload.model),
@@ -2922,7 +2976,6 @@ const translateAnthropicMessagesToResponsesPayload = (payload) => {
2922
2976
  tools: translatedTools,
2923
2977
  tool_choice: toolChoice,
2924
2978
  metadata: payload.metadata ? { ...payload.metadata } : null,
2925
- prompt_cache_key: promptCacheKey,
2926
2979
  stream: payload.stream ?? null,
2927
2980
  store: false,
2928
2981
  parallel_tool_calls: true,
@@ -2932,6 +2985,8 @@ const translateAnthropicMessagesToResponsesPayload = (payload) => {
2932
2985
  },
2933
2986
  include: ["reasoning.encrypted_content"]
2934
2987
  };
2988
+ if (hasOriginalTools) responsesPayload.prompt_cache_key = promptCacheKey;
2989
+ return responsesPayload;
2935
2990
  };
2936
2991
  const encodeCompactionCarrierSignature = (compaction) => {
2937
2992
  return `${COMPACTION_SIGNATURE_PREFIX}${compaction.encrypted_content}${COMPACTION_SIGNATURE_SEPARATOR}${compaction.id}`;
@@ -3047,8 +3102,8 @@ const resolveAssistantPhase = (_model, content, applyPhase) => {
3047
3102
  if (!content.some((block) => block.type === "text")) return;
3048
3103
  return content.some((block) => block.type === "tool_use") ? "commentary" : "final_answer";
3049
3104
  };
3050
- const shouldApplyPhase = (model) => {
3051
- return getExtraPromptForModel(model).includes("## Intermediary updates");
3105
+ const shouldApplyPhase = (_model) => {
3106
+ return true;
3052
3107
  };
3053
3108
  const createTextContent = (text) => ({
3054
3109
  type: "input_text",
@@ -4116,7 +4171,7 @@ const handleWithChatCompletions = async (c, anthropicPayload, options) => {
4116
4171
  };
4117
4172
  const handleWithResponsesApi = async (c, anthropicPayload, options) => {
4118
4173
  const { logger, selectedModel, ...requestOptions } = options;
4119
- const responsesPayload = translateAnthropicMessagesToResponsesPayload(anthropicPayload);
4174
+ const responsesPayload = translateAnthropicMessagesToResponsesPayload(anthropicPayload, requestOptions.subagentMarker?.agent_id);
4120
4175
  const recordUsage = createCopilotUsageRecorder({
4121
4176
  endpoint: "responses",
4122
4177
  fallbackSessionId: requestOptions.sessionId,
@@ -4322,6 +4377,9 @@ const messagesFlowHandlers = {
4322
4377
  };
4323
4378
  async function handleCompletion(c) {
4324
4379
  const anthropicPayload = await c.req.json();
4380
+ const requestedModel = anthropicPayload.model;
4381
+ anthropicPayload.model = resolveMappedModel(anthropicPayload.model);
4382
+ if (anthropicPayload.model !== requestedModel) logger$3.debug(`Resolved model mapping: ${requestedModel} -> ${anthropicPayload.model}`);
4325
4383
  const providerModelAlias = parseProviderModelAlias(anthropicPayload.model);
4326
4384
  if (providerModelAlias) {
4327
4385
  anthropicPayload.model = providerModelAlias.model;
@@ -4707,11 +4765,19 @@ const server = new Hono();
4707
4765
  server.use(traceIdMiddleware);
4708
4766
  server.use(logger());
4709
4767
  server.use(cors());
4710
- server.use("*", createAuthMiddleware({ allowUnauthenticatedPaths: [
4711
- "/",
4712
- "/usage-viewer",
4713
- "/usage-viewer/"
4714
- ] }));
4768
+ server.use("*", createAuthMiddleware({
4769
+ allowUnauthenticatedPaths: [
4770
+ "/",
4771
+ "/usage-viewer",
4772
+ "/usage-viewer/"
4773
+ ],
4774
+ shouldSkipPath: (path) => path.startsWith("/admin/")
4775
+ }));
4776
+ server.use("/admin/*", createAuthMiddleware({
4777
+ getApiKeys: getConfiguredAdminApiKeys,
4778
+ allowUnauthenticatedPaths: [],
4779
+ allowWhenNoApiKeys: false
4780
+ }));
4715
4781
  server.get("/", (c) => c.text("Server running"));
4716
4782
  server.get("/usage-viewer", (c) => {
4717
4783
  const usageViewerFileUrl = new URL("../pages/index.html", import.meta.url);
@@ -4719,6 +4785,7 @@ server.get("/usage-viewer", (c) => {
4719
4785
  });
4720
4786
  server.get("/usage-viewer/", (c) => c.redirect("/usage-viewer", 301));
4721
4787
  server.route("/chat/completions", completionRoutes);
4788
+ server.route("/admin/config", configRoutes);
4722
4789
  server.route("/models", modelRoutes);
4723
4790
  server.route("/embeddings", embeddingRoutes);
4724
4791
  server.route("/usage", usageRoute);
@@ -4735,4 +4802,4 @@ server.route("/:provider/v1/models", providerModelRoutes);
4735
4802
  //#endregion
4736
4803
  export { server };
4737
4804
 
4738
- //# sourceMappingURL=server-DpVPS3zt.js.map
4805
+ //# sourceMappingURL=server-UsHsMP_2.js.map