@jeffreycao/copilot-api 1.9.15 → 1.10.1

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,9 +1,10 @@
1
1
  import { t as PATHS } from "./paths-DC-mqCY3.js";
2
- import { A as compactAutoContinuePromptStarts, C as prepareForCompact, D as requestContext, E as generateTraceId, F as state, N as compactSystemPromptStarts, O as resolveTraceId$1, T as prepareMessageProxyHeaders, c as getUUID, d as sleep, f as getCopilotUsage, g as copilotHeaders, h as copilotBaseUrl, j as compactMessageSections, l as isNullish, m as forwardError, n as cacheModels, o as generateRequestIdFromPayload, p as HTTPError, s as getRootSessionId, u as parseUserIdMetadata, w as prepareInteractionHeaders } from "./utils-Cj-ToKA6.js";
3
- import { a as getProviderConfig, c as isMessagesApiEnabled, i as getExtraPromptForModel, l as isResponsesApiContextManagementModel, n as getClaudeTokenMultiplier, o as getReasoningEffortForModel, r as getConfig, s as getSmallModel, t as getAnthropicApiKey, u as isResponsesApiWebSearchEnabled } from "./config-DrfmMOO-.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";
4
4
  import consola from "consola";
5
5
  import fs from "node:fs/promises";
6
6
  import path from "node:path";
7
+ import { createHash } from "node:crypto";
7
8
  import { Hono } from "hono";
8
9
  import { cors } from "hono/cors";
9
10
  import { logger } from "hono/logger";
@@ -11,6 +12,7 @@ import fs$1, { readFileSync } from "node:fs";
11
12
  import { streamSSE } from "hono/streaming";
12
13
  import util from "node:util";
13
14
  import { events } from "fetch-event-stream";
15
+ import { WebSocket } from "undici";
14
16
  //#region src/lib/request-auth.ts
15
17
  function normalizeApiKeys(apiKeys) {
16
18
  if (!Array.isArray(apiKeys)) {
@@ -502,19 +504,14 @@ async function flushTokenUsageEvents() {
502
504
  }
503
505
  function getPeriodRange(period, now = /* @__PURE__ */ new Date()) {
504
506
  const start = new Date(now);
507
+ start.setHours(0, 0, 0, 0);
505
508
  switch (period) {
506
- case "day":
507
- start.setHours(0, 0, 0, 0);
508
- break;
509
- case "week": {
510
- const daysSinceMonday = (start.getDay() + 6) % 7;
511
- start.setDate(start.getDate() - daysSinceMonday);
512
- start.setHours(0, 0, 0, 0);
509
+ case "day": break;
510
+ case "week":
511
+ start.setDate(start.getDate() - 6);
513
512
  break;
514
- }
515
513
  case "month":
516
- start.setDate(1);
517
- start.setHours(0, 0, 0, 0);
514
+ start.setDate(start.getDate() - 29);
518
515
  break;
519
516
  default: break;
520
517
  }
@@ -527,7 +524,7 @@ function getPeriodRange(period, now = /* @__PURE__ */ new Date()) {
527
524
  end.setDate(end.getDate() + 7);
528
525
  break;
529
526
  case "month":
530
- end.setMonth(end.getMonth() + 1);
527
+ end.setDate(end.getDate() + 30);
531
528
  break;
532
529
  default: break;
533
530
  }
@@ -536,6 +533,26 @@ function getPeriodRange(period, now = /* @__PURE__ */ new Date()) {
536
533
  startMs: start.getTime()
537
534
  };
538
535
  }
536
+ function formatLocalDate(date) {
537
+ return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
538
+ }
539
+ function createDailyIntervals(range) {
540
+ const intervals = [];
541
+ const cursor = new Date(range.startMs);
542
+ while (cursor.getTime() < range.endMs) {
543
+ const startMs = cursor.getTime();
544
+ const next = new Date(cursor);
545
+ next.setDate(next.getDate() + 1);
546
+ const endMs = Math.min(next.getTime(), range.endMs);
547
+ intervals.push({
548
+ date: formatLocalDate(cursor),
549
+ endMs,
550
+ startMs
551
+ });
552
+ cursor.setTime(endMs);
553
+ }
554
+ return intervals;
555
+ }
539
556
  function createEmptyTotals() {
540
557
  return {
541
558
  cache_creation_input_tokens: 0,
@@ -546,6 +563,14 @@ function createEmptyTotals() {
546
563
  total_tokens: 0
547
564
  };
548
565
  }
566
+ function addTotals(target, next) {
567
+ target.cache_creation_input_tokens += next.cache_creation_input_tokens;
568
+ target.cache_read_input_tokens += next.cache_read_input_tokens;
569
+ target.input_tokens += next.input_tokens;
570
+ target.output_tokens += next.output_tokens;
571
+ target.request_count += next.request_count;
572
+ target.total_tokens += next.total_tokens;
573
+ }
549
574
  function createEmptySummary(period) {
550
575
  const range = getPeriodRange(period);
551
576
  return {
@@ -560,6 +585,27 @@ function createEmptySummary(period) {
560
585
  totals: createEmptyTotals()
561
586
  };
562
587
  }
588
+ function createEmptyDailySummary(period) {
589
+ const range = getPeriodRange(period);
590
+ return {
591
+ byModel: [],
592
+ days: createDailyIntervals(range).map((interval) => ({
593
+ byModel: [],
594
+ date: interval.date,
595
+ end_ms: interval.endMs,
596
+ start_ms: interval.startMs,
597
+ totals: createEmptyTotals()
598
+ })),
599
+ period,
600
+ range: {
601
+ end_ms: range.endMs,
602
+ end_utc: new Date(range.endMs).toISOString(),
603
+ start_ms: range.startMs,
604
+ start_utc: new Date(range.startMs).toISOString()
605
+ },
606
+ totals: createEmptyTotals()
607
+ };
608
+ }
563
609
  function createEmptyEventsPage(input) {
564
610
  const range = getPeriodRange(input.period);
565
611
  return {
@@ -577,6 +623,14 @@ function createEmptyEventsPage(input) {
577
623
  total_pages: 1
578
624
  };
579
625
  }
626
+ function rangePayload(range) {
627
+ return {
628
+ end_ms: range.endMs,
629
+ end_utc: new Date(range.endMs).toISOString(),
630
+ start_ms: range.startMs,
631
+ start_utc: new Date(range.startMs).toISOString()
632
+ };
633
+ }
580
634
  function numberFromRow(row, key) {
581
635
  const value = row?.[key];
582
636
  return typeof value === "number" && Number.isFinite(value) ? value : 0;
@@ -591,6 +645,12 @@ function totalsFromRow(row) {
591
645
  total_tokens: numberFromRow(row, "total_tokens")
592
646
  };
593
647
  }
648
+ function modelSummaryFromRow(row) {
649
+ return {
650
+ ...totalsFromRow(row),
651
+ model: typeof row.model === "string" ? row.model : "unknown"
652
+ };
653
+ }
594
654
  function stringFromRow(row, key) {
595
655
  const value = row[key];
596
656
  return typeof value === "string" ? value : "";
@@ -618,12 +678,8 @@ function usageEventFromRow(row) {
618
678
  user_id: stringFromRow(row, "user_id")
619
679
  };
620
680
  }
621
- async function getTokenUsageSummary(period) {
622
- if (!isTokenUsageStorageEnabled()) return createEmptySummary(period);
623
- await flushTokenUsageEvents();
624
- const range = getPeriodRange(period);
625
- const db = await getDb();
626
- const totalsRow = db.prepare(`
681
+ function getTotalsRow(db, range) {
682
+ return db.prepare(`
627
683
  SELECT
628
684
  COUNT(*) AS request_count,
629
685
  COALESCE(SUM(input_tokens), 0) AS input_tokens,
@@ -634,8 +690,9 @@ async function getTokenUsageSummary(period) {
634
690
  FROM token_usage_events
635
691
  WHERE created_at_ms >= ? AND created_at_ms < ?
636
692
  `).get(range.startMs, range.endMs);
637
- return {
638
- byModel: db.prepare(`
693
+ }
694
+ function getModelRows(db, range) {
695
+ return db.prepare(`
639
696
  SELECT
640
697
  model,
641
698
  COUNT(*) AS request_count,
@@ -650,20 +707,47 @@ async function getTokenUsageSummary(period) {
650
707
  ORDER BY
651
708
  total_tokens DESC,
652
709
  model ASC
653
- `).all(range.startMs, range.endMs).map((row) => ({
654
- ...totalsFromRow(row),
655
- model: typeof row.model === "string" ? row.model : "unknown"
656
- })),
710
+ `).all(range.startMs, range.endMs);
711
+ }
712
+ function createDailyBucket(interval, rows) {
713
+ const byModel = rows.map((row) => modelSummaryFromRow(row));
714
+ const totals = createEmptyTotals();
715
+ for (const model of byModel) addTotals(totals, model);
716
+ return {
717
+ byModel,
718
+ date: interval.date,
719
+ end_ms: interval.endMs,
720
+ start_ms: interval.startMs,
721
+ totals
722
+ };
723
+ }
724
+ async function getTokenUsageSummary(period) {
725
+ if (!isTokenUsageStorageEnabled()) return createEmptySummary(period);
726
+ await flushTokenUsageEvents();
727
+ const range = getPeriodRange(period);
728
+ const db = await getDb();
729
+ const totalsRow = getTotalsRow(db, range);
730
+ return {
731
+ byModel: getModelRows(db, range).map((row) => modelSummaryFromRow(row)),
657
732
  period,
658
- range: {
659
- end_ms: range.endMs,
660
- end_utc: new Date(range.endMs).toISOString(),
661
- start_ms: range.startMs,
662
- start_utc: new Date(range.startMs).toISOString()
663
- },
733
+ range: rangePayload(range),
664
734
  totals: totalsFromRow(totalsRow)
665
735
  };
666
736
  }
737
+ async function getTokenUsageDailySummary(period) {
738
+ if (!isTokenUsageStorageEnabled()) return createEmptyDailySummary(period);
739
+ await flushTokenUsageEvents();
740
+ const range = getPeriodRange(period);
741
+ const db = await getDb();
742
+ const intervals = createDailyIntervals(range);
743
+ return {
744
+ byModel: getModelRows(db, range).map((row) => modelSummaryFromRow(row)),
745
+ days: intervals.map((interval) => createDailyBucket(interval, getModelRows(db, interval))),
746
+ period,
747
+ range: rangePayload(range),
748
+ totals: totalsFromRow(getTotalsRow(db, range))
749
+ };
750
+ }
667
751
  async function getTokenUsageEventsPage(input) {
668
752
  if (!isTokenUsageStorageEnabled()) return createEmptyEventsPage(input);
669
753
  await flushTokenUsageEvents();
@@ -831,10 +915,14 @@ const copilotRateLimitHeaders = {
831
915
  session: "x-usage-ratelimit-session",
832
916
  weekly: "x-usage-ratelimit-weekly"
833
917
  };
918
+ const copilotQuotaSnapshotKeys = {
919
+ session: "5Hour-Session-RateLimits",
920
+ weekly: "Weekly-Session-RateLimits"
921
+ };
834
922
  const hasGetMethod = (headers) => {
835
923
  return "get" in headers && typeof headers.get === "function";
836
924
  };
837
- const getHeaderValue = (headers, headerName) => {
925
+ const getHeaderValue$1 = (headers, headerName) => {
838
926
  if (hasGetMethod(headers)) return headers.get(headerName);
839
927
  const normalizedHeaderName = headerName.toLowerCase();
840
928
  return Object.entries(headers).find(([key]) => key.toLowerCase() === normalizedHeaderName)?.[1] ?? null;
@@ -851,7 +939,7 @@ const parseCopilotRateLimitHeader = (headerValue) => {
851
939
  };
852
940
  const getCopilotRateLimitUsage = (headers, type) => {
853
941
  const headerName = copilotRateLimitHeaders[type];
854
- const headerValue = getHeaderValue(headers, headerName);
942
+ const headerValue = getHeaderValue$1(headers, headerName);
855
943
  if (!headerValue) return null;
856
944
  const parsed = parseCopilotRateLimitHeader(headerValue);
857
945
  if (!parsed) return null;
@@ -860,15 +948,39 @@ const getCopilotRateLimitUsage = (headers, type) => {
860
948
  ...parsed
861
949
  };
862
950
  };
951
+ const getCopilotRateLimitUsageFromSnapshots = (snapshots, type) => {
952
+ const snapshot = snapshots?.[copilotQuotaSnapshotKeys[type]];
953
+ if (!isCopilotQuotaSnapshot(snapshot)) return null;
954
+ return {
955
+ remaining: String(snapshot.percent_remaining),
956
+ resetAt: snapshot.reset_date,
957
+ type
958
+ };
959
+ };
863
960
  const logCopilotRateLimits = (headers) => {
864
961
  for (const type of copilotRateLimitTypes) {
865
962
  const usage = getCopilotRateLimitUsage(headers, type);
866
963
  if (!usage) continue;
867
- const d = new Date(usage.resetAt);
868
- const dateStr = Number.isNaN(d.getTime()) ? usage.resetAt : d.toLocaleString();
869
- consola.info(`Copilot ${usage.type} quota remaining: ${usage.remaining}, resets at: ${dateStr}`);
964
+ logCopilotRateLimitUsage(usage);
965
+ }
966
+ };
967
+ const logCopilotQuotaSnapshots = (snapshots) => {
968
+ for (const type of copilotRateLimitTypes) {
969
+ const usage = getCopilotRateLimitUsageFromSnapshots(snapshots, type);
970
+ if (!usage) continue;
971
+ logCopilotRateLimitUsage(usage);
870
972
  }
871
973
  };
974
+ const logCopilotRateLimitUsage = (usage) => {
975
+ const d = new Date(usage.resetAt);
976
+ const dateStr = Number.isNaN(d.getTime()) ? usage.resetAt : d.toLocaleString();
977
+ consola.info(`Copilot ${usage.type} quota remaining: ${usage.remaining}, resets at: ${dateStr}`);
978
+ };
979
+ const isCopilotQuotaSnapshot = (value) => {
980
+ if (!value || typeof value !== "object") return false;
981
+ const record = value;
982
+ return typeof record.entitlement === "string" && typeof record.percent_remaining === "number" && typeof record.overage_permitted === "boolean" && typeof record.overage_count === "number" && typeof record.reset_date === "string";
983
+ };
872
984
  //#endregion
873
985
  //#region src/services/copilot/create-chat-completions.ts
874
986
  const createChatCompletions = async (payload, options) => {
@@ -2426,9 +2538,76 @@ const adjustInputTokens = (providerConfig, usage) => {
2426
2538
  usage.input_tokens = Math.max(0, (usage.input_tokens ?? 0) - (usage.cache_read_input_tokens ?? 0) - (usage.cache_creation_input_tokens ?? 0));
2427
2539
  debugJson(logger$4, "provider.messages.adjusted_usage:", usage);
2428
2540
  };
2541
+ const responsesUtilsDependencies = {
2542
+ isResponsesApiContextManagementModel,
2543
+ isResponsesApiWebSocketEnabled
2544
+ };
2545
+ const getResponsesRequestOptions = (payload) => {
2546
+ return {
2547
+ vision: hasVisionInput(payload),
2548
+ initiator: hasAgentInitiator(payload) ? "agent" : "user"
2549
+ };
2550
+ };
2551
+ const getResponsesTransportForModel = (selectedModel, options = {}) => {
2552
+ const supportedEndpoints = selectedModel?.supported_endpoints ?? [];
2553
+ const useWebSocket = responsesUtilsDependencies.isResponsesApiWebSocketEnabled();
2554
+ if (options.compactType !== 1 && useWebSocket && supportedEndpoints.includes("ws:/responses")) return "websocket";
2555
+ if (supportedEndpoints.includes("/responses")) return "http";
2556
+ return null;
2557
+ };
2558
+ const hasAgentInitiator = (payload) => {
2559
+ const lastItem = getPayloadItems(payload).at(-1);
2560
+ if (!lastItem) return false;
2561
+ if (!("role" in lastItem) || !lastItem.role) return true;
2562
+ return (typeof lastItem.role === "string" ? lastItem.role.toLowerCase() : "") === "assistant";
2563
+ };
2564
+ const hasVisionInput = (payload) => {
2565
+ return getPayloadItems(payload).some((item) => containsVisionContent(item));
2566
+ };
2567
+ const resolveResponsesCompactThreshold = (maxPromptTokens) => {
2568
+ if (typeof maxPromptTokens === "number" && maxPromptTokens > 0) return Math.floor(maxPromptTokens * .9);
2569
+ return 5e4;
2570
+ };
2571
+ const createCompactionContextManagement = (compactThreshold) => [{
2572
+ type: "compaction",
2573
+ compact_threshold: compactThreshold
2574
+ }];
2575
+ const applyResponsesApiContextManagement = (payload, maxPromptTokens) => {
2576
+ if (payload.context_management !== void 0) return;
2577
+ if (!responsesUtilsDependencies.isResponsesApiContextManagementModel(payload.model)) return;
2578
+ payload.context_management = createCompactionContextManagement(resolveResponsesCompactThreshold(maxPromptTokens));
2579
+ };
2580
+ const compactInputByLatestCompaction = (payload) => {
2581
+ if (!Array.isArray(payload.input) || payload.input.length === 0) return;
2582
+ const latestCompactionMessageIndex = getLatestCompactionMessageIndex(payload.input);
2583
+ if (latestCompactionMessageIndex === void 0) return;
2584
+ payload.input = payload.input.slice(latestCompactionMessageIndex);
2585
+ };
2586
+ const getLatestCompactionMessageIndex = (input) => {
2587
+ for (let index = input.length - 1; index >= 0; index -= 1) if (isCompactionInputItem(input[index])) return index;
2588
+ };
2589
+ const isCompactionInputItem = (value) => {
2590
+ return "type" in value && typeof value.type === "string" && value.type === "compaction";
2591
+ };
2592
+ const getPayloadItems = (payload) => {
2593
+ const result = [];
2594
+ const { input } = payload;
2595
+ if (Array.isArray(input)) result.push(...input);
2596
+ return result;
2597
+ };
2598
+ const containsVisionContent = (value) => {
2599
+ if (!value) return false;
2600
+ if (Array.isArray(value)) return value.some((entry) => containsVisionContent(entry));
2601
+ if (typeof value !== "object") return false;
2602
+ const record = value;
2603
+ if ((typeof record.type === "string" ? record.type.toLowerCase() : void 0) === "input_image") return true;
2604
+ if (Array.isArray(record.content)) return record.content.some((entry) => containsVisionContent(entry));
2605
+ return false;
2606
+ };
2429
2607
  //#endregion
2430
2608
  //#region src/services/copilot/create-responses.ts
2431
- const createResponses = async (payload, { vision, initiator, subagentMarker, requestId, sessionId, compactType }) => {
2609
+ const RESPONSES_WEBSOCKET_IDLE_TIMEOUT_MS = 6e4;
2610
+ const createResponses = async (payload, { vision, initiator, subagentMarker, requestId, sessionId, compactType, transport = "http" }) => {
2432
2611
  if (!state.copilotToken) throw new Error("Copilot token not found");
2433
2612
  const headers = {
2434
2613
  ...copilotHeaders(state, requestId, vision),
@@ -2438,6 +2617,17 @@ const createResponses = async (payload, { vision, initiator, subagentMarker, req
2438
2617
  prepareForCompact(headers, compactType);
2439
2618
  payload.service_tier = void 0;
2440
2619
  consola.log(`<-- model: ${payload.model}`);
2620
+ if ((compactType === 1 ? "http" : transport) === "websocket") {
2621
+ const stream = createPooledResponsesWebSocketStream(prepareResponsesWebSocketRequest(payload, headers, {
2622
+ requestId,
2623
+ subagentMarker
2624
+ }));
2625
+ if (payload.stream) return stream;
2626
+ return await consumeResponsesWebSocketStream(stream);
2627
+ }
2628
+ return await createHttpResponses(payload, headers);
2629
+ };
2630
+ const createHttpResponses = async (payload, headers) => {
2441
2631
  const response = await fetch(`${copilotBaseUrl(state)}/responses`, {
2442
2632
  method: "POST",
2443
2633
  headers,
@@ -2451,6 +2641,264 @@ const createResponses = async (payload, { vision, initiator, subagentMarker, req
2451
2641
  if (payload.stream) return events(response);
2452
2642
  return await response.json();
2453
2643
  };
2644
+ const prepareResponsesWebSocketRequest = (payload, preparedHeaders, options) => {
2645
+ const initiator = getResponsesWebSocketInitiator(preparedHeaders);
2646
+ return {
2647
+ headers: copilotWebSocketHeaders(preparedHeaders),
2648
+ poolKey: buildResponsesWebSocketPoolKey(payload, options),
2649
+ payload: buildResponsesWebSocketPayload(payload, initiator)
2650
+ };
2651
+ };
2652
+ const buildResponsesWebSocketPoolKey = (payload, { requestId, subagentMarker }) => {
2653
+ const tokenFingerprint = state.copilotToken ? createHash("sha256").update(state.copilotToken).digest("hex").slice(0, 16) : "missing-token";
2654
+ const subagentKey = subagentMarker ? [
2655
+ subagentMarker.session_id,
2656
+ subagentMarker.agent_id,
2657
+ subagentMarker.agent_type
2658
+ ].join(":") : "main";
2659
+ return [
2660
+ tokenFingerprint,
2661
+ payload.model,
2662
+ requestId,
2663
+ subagentKey
2664
+ ].map(encodePoolKeyPart).join("|");
2665
+ };
2666
+ const getResponsesWebSocketInitiator = (preparedHeaders) => {
2667
+ return getHeaderValue(preparedHeaders, "x-initiator")?.toLowerCase() === "agent" ? "agent" : "user";
2668
+ };
2669
+ const createPooledResponsesWebSocketStream = (request) => runResponsesWebSocketPoolRequest(request);
2670
+ const buildResponsesWebSocketPayload = (payload, initiator) => {
2671
+ const websocketPayload = {
2672
+ ...payload,
2673
+ type: "response.create",
2674
+ initiator
2675
+ };
2676
+ delete websocketPayload.stream;
2677
+ delete websocketPayload["background"];
2678
+ delete websocketPayload.service_tier;
2679
+ return websocketPayload;
2680
+ };
2681
+ const buildResponsesWebSocketUrl = (baseUrl) => {
2682
+ const url = new URL(`${baseUrl.replace(/\/+$/u, "")}/responses`);
2683
+ if (url.protocol === "https:") url.protocol = "wss:";
2684
+ else if (url.protocol === "http:") url.protocol = "ws:";
2685
+ return url.toString();
2686
+ };
2687
+ const responsesWebSocketPool = /* @__PURE__ */ new Map();
2688
+ const runResponsesWebSocketPoolRequest = async function* (request) {
2689
+ const entry = getResponsesWebSocketPoolEntry(request);
2690
+ const release = await acquireResponsesWebSocketPoolEntry(request.poolKey, entry);
2691
+ try {
2692
+ const websocket = await entry.websocketPromise;
2693
+ websocket.send(JSON.stringify(request.payload));
2694
+ for await (const data of createWebSocketMessageStream(websocket)) {
2695
+ const chunk = createResponsesWebSocketStreamChunk(data);
2696
+ yield chunk;
2697
+ if (isTerminalResponsesStreamChunk(chunk)) return;
2698
+ }
2699
+ removeResponsesWebSocketPoolEntry(request.poolKey, entry);
2700
+ throw new Error("Responses websocket ended without a terminal response");
2701
+ } catch (error) {
2702
+ removeResponsesWebSocketPoolEntry(request.poolKey, entry);
2703
+ throw toError(error);
2704
+ } finally {
2705
+ release();
2706
+ }
2707
+ };
2708
+ const getResponsesWebSocketPoolEntry = (request) => {
2709
+ const existing = responsesWebSocketPool.get(request.poolKey);
2710
+ if (existing && !existing.closed) {
2711
+ clearResponsesWebSocketIdleTimer(existing);
2712
+ return existing;
2713
+ }
2714
+ const entry = createResponsesWebSocketPoolEntry(request);
2715
+ responsesWebSocketPool.set(request.poolKey, entry);
2716
+ return entry;
2717
+ };
2718
+ const createResponsesWebSocketPoolEntry = (request) => {
2719
+ const entry = {
2720
+ closed: false,
2721
+ idleTimer: null,
2722
+ lock: Promise.resolve(),
2723
+ websocketPromise: openResponsesWebSocket({
2724
+ headers: request.headers,
2725
+ url: buildResponsesWebSocketUrl(copilotBaseUrl(state))
2726
+ })
2727
+ };
2728
+ entry.websocketPromise.then((websocket) => {
2729
+ websocket.addEventListener("close", () => {
2730
+ removeResponsesWebSocketPoolEntry(request.poolKey, entry);
2731
+ });
2732
+ websocket.addEventListener("error", () => {
2733
+ removeResponsesWebSocketPoolEntry(request.poolKey, entry);
2734
+ });
2735
+ }).catch(() => {
2736
+ removeResponsesWebSocketPoolEntry(request.poolKey, entry);
2737
+ });
2738
+ return entry;
2739
+ };
2740
+ const acquireResponsesWebSocketPoolEntry = async (poolKey, entry) => {
2741
+ clearResponsesWebSocketIdleTimer(entry);
2742
+ let releaseCurrent;
2743
+ const previousLock = entry.lock;
2744
+ entry.lock = new Promise((resolve) => {
2745
+ releaseCurrent = resolve;
2746
+ });
2747
+ await previousLock;
2748
+ clearResponsesWebSocketIdleTimer(entry);
2749
+ let released = false;
2750
+ return () => {
2751
+ if (released) return;
2752
+ released = true;
2753
+ releaseCurrent();
2754
+ if (!entry.closed) scheduleResponsesWebSocketIdleClose(poolKey, entry);
2755
+ };
2756
+ };
2757
+ const scheduleResponsesWebSocketIdleClose = (poolKey, entry) => {
2758
+ clearResponsesWebSocketIdleTimer(entry);
2759
+ entry.idleTimer = setTimeout(() => {
2760
+ removeResponsesWebSocketPoolEntry(poolKey, entry);
2761
+ }, RESPONSES_WEBSOCKET_IDLE_TIMEOUT_MS);
2762
+ unrefTimer(entry.idleTimer);
2763
+ };
2764
+ const clearResponsesWebSocketIdleTimer = (entry) => {
2765
+ if (entry.idleTimer) {
2766
+ clearTimeout(entry.idleTimer);
2767
+ entry.idleTimer = null;
2768
+ }
2769
+ };
2770
+ const removeResponsesWebSocketPoolEntry = (poolKey, entry) => {
2771
+ if (responsesWebSocketPool.get(poolKey) !== entry) return;
2772
+ responsesWebSocketPool.delete(poolKey);
2773
+ entry.closed = true;
2774
+ clearResponsesWebSocketIdleTimer(entry);
2775
+ entry.websocketPromise.then(closeResponsesWebSocket).catch(() => {});
2776
+ };
2777
+ const unrefTimer = (timer) => {
2778
+ if (typeof timer === "object" && "unref" in timer && typeof timer.unref === "function") timer.unref();
2779
+ };
2780
+ const openResponsesWebSocket = async ({ headers, url }) => await new Promise((resolve, reject) => {
2781
+ const dispatcher = getProxyEnvDispatcher();
2782
+ const websocket = new WebSocket(url, dispatcher ? {
2783
+ dispatcher,
2784
+ headers
2785
+ } : { headers });
2786
+ const cleanup = () => {
2787
+ websocket.removeEventListener("open", onOpen);
2788
+ websocket.removeEventListener("error", onError);
2789
+ };
2790
+ const onOpen = () => {
2791
+ cleanup();
2792
+ resolve(websocket);
2793
+ };
2794
+ const onError = () => {
2795
+ cleanup();
2796
+ reject(/* @__PURE__ */ new Error("Failed to create responses websocket"));
2797
+ };
2798
+ websocket.addEventListener("open", onOpen);
2799
+ websocket.addEventListener("error", onError);
2800
+ });
2801
+ const createWebSocketMessageStream = async function* (websocket) {
2802
+ const queue = [];
2803
+ let closed = false;
2804
+ let error = null;
2805
+ let notify = null;
2806
+ const wake = () => {
2807
+ notify?.();
2808
+ notify = null;
2809
+ };
2810
+ const onMessage = (event) => {
2811
+ queue.push(normalizeWebSocketMessageData(event.data));
2812
+ wake();
2813
+ };
2814
+ const onClose = () => {
2815
+ closed = true;
2816
+ wake();
2817
+ };
2818
+ const onError = () => {
2819
+ error = /* @__PURE__ */ new Error("Responses websocket stream error");
2820
+ wake();
2821
+ };
2822
+ websocket.addEventListener("message", onMessage);
2823
+ websocket.addEventListener("close", onClose);
2824
+ websocket.addEventListener("error", onError);
2825
+ try {
2826
+ while (true) {
2827
+ const item = queue.shift();
2828
+ if (item) {
2829
+ yield await item;
2830
+ continue;
2831
+ }
2832
+ if (error) throw toError(error);
2833
+ if (closed) break;
2834
+ await new Promise((resolve) => {
2835
+ notify = resolve;
2836
+ });
2837
+ }
2838
+ } finally {
2839
+ websocket.removeEventListener("message", onMessage);
2840
+ websocket.removeEventListener("close", onClose);
2841
+ websocket.removeEventListener("error", onError);
2842
+ }
2843
+ };
2844
+ const normalizeWebSocketMessageData = async (data) => {
2845
+ if (typeof data === "string") return data;
2846
+ if (data instanceof ArrayBuffer) return new TextDecoder().decode(data);
2847
+ if (ArrayBuffer.isView(data)) {
2848
+ const view = data;
2849
+ return new TextDecoder().decode(new Uint8Array(view.buffer, view.byteOffset, view.byteLength));
2850
+ }
2851
+ if (isTextReadable(data)) return await data.text();
2852
+ return String(data);
2853
+ };
2854
+ const isTextReadable = (value) => {
2855
+ if (!value || typeof value !== "object" || !("text" in value)) return false;
2856
+ return typeof value.text === "function";
2857
+ };
2858
+ const toError = (value) => {
2859
+ if (value instanceof Error) return value;
2860
+ return new Error(String(value));
2861
+ };
2862
+ const getHeaderValue = (headers, headerName) => {
2863
+ const normalizedHeaderName = headerName.toLowerCase();
2864
+ return Object.entries(headers).find(([key]) => key.toLowerCase() === normalizedHeaderName)?.[1];
2865
+ };
2866
+ const encodePoolKeyPart = (value) => encodeURIComponent(value);
2867
+ const createResponsesWebSocketStreamChunk = (data) => {
2868
+ if (data === "[DONE]") return { data };
2869
+ try {
2870
+ const parsed = JSON.parse(data);
2871
+ if (parsed.type === "response.completed") logCopilotQuotaSnapshots(parsed.copilot_quota_snapshots);
2872
+ return {
2873
+ data: JSON.stringify(parsed),
2874
+ event: typeof parsed.type === "string" ? parsed.type : void 0,
2875
+ id: typeof parsed.id === "string" ? parsed.id : void 0
2876
+ };
2877
+ } catch {
2878
+ return { data };
2879
+ }
2880
+ };
2881
+ const isTerminalResponsesStreamChunk = (chunk) => {
2882
+ if (!chunk.data || chunk.data === "[DONE]") return false;
2883
+ try {
2884
+ const parsed = JSON.parse(chunk.data);
2885
+ return parsed.type === "response.completed" || parsed.type === "response.failed" || parsed.type === "response.incomplete" || parsed.type === "error";
2886
+ } catch {
2887
+ return false;
2888
+ }
2889
+ };
2890
+ const consumeResponsesWebSocketStream = async (stream) => {
2891
+ for await (const chunk of stream) {
2892
+ if (!chunk.data || chunk.data === "[DONE]") continue;
2893
+ const event = JSON.parse(chunk.data);
2894
+ if (event.type === "error") throw new Error(event.message);
2895
+ if (event.type === "response.completed" || event.type === "response.failed" || event.type === "response.incomplete") return event.response;
2896
+ }
2897
+ throw new Error("Responses websocket ended without a terminal response");
2898
+ };
2899
+ const closeResponsesWebSocket = (websocket) => {
2900
+ if (websocket.readyState === WebSocket.CONNECTING || websocket.readyState === WebSocket.OPEN) websocket.close();
2901
+ };
2454
2902
  //#endregion
2455
2903
  //#region src/routes/messages/responses-translation.ts
2456
2904
  const MESSAGE_TYPE = "message";
@@ -3307,63 +3755,6 @@ const extractFunctionCallDetails = (rawEvent) => {
3307
3755
  };
3308
3756
  };
3309
3757
  //#endregion
3310
- //#region src/routes/responses/utils.ts
3311
- const getResponsesRequestOptions = (payload) => {
3312
- return {
3313
- vision: hasVisionInput(payload),
3314
- initiator: hasAgentInitiator(payload) ? "agent" : "user"
3315
- };
3316
- };
3317
- const hasAgentInitiator = (payload) => {
3318
- const lastItem = getPayloadItems(payload).at(-1);
3319
- if (!lastItem) return false;
3320
- if (!("role" in lastItem) || !lastItem.role) return true;
3321
- return (typeof lastItem.role === "string" ? lastItem.role.toLowerCase() : "") === "assistant";
3322
- };
3323
- const hasVisionInput = (payload) => {
3324
- return getPayloadItems(payload).some((item) => containsVisionContent(item));
3325
- };
3326
- const resolveResponsesCompactThreshold = (maxPromptTokens) => {
3327
- if (typeof maxPromptTokens === "number" && maxPromptTokens > 0) return Math.floor(maxPromptTokens * .9);
3328
- return 5e4;
3329
- };
3330
- const createCompactionContextManagement = (compactThreshold) => [{
3331
- type: "compaction",
3332
- compact_threshold: compactThreshold
3333
- }];
3334
- const applyResponsesApiContextManagement = (payload, maxPromptTokens) => {
3335
- if (payload.context_management !== void 0) return;
3336
- if (!isResponsesApiContextManagementModel(payload.model)) return;
3337
- payload.context_management = createCompactionContextManagement(resolveResponsesCompactThreshold(maxPromptTokens));
3338
- };
3339
- const compactInputByLatestCompaction = (payload) => {
3340
- if (!Array.isArray(payload.input) || payload.input.length === 0) return;
3341
- const latestCompactionMessageIndex = getLatestCompactionMessageIndex(payload.input);
3342
- if (latestCompactionMessageIndex === void 0) return;
3343
- payload.input = payload.input.slice(latestCompactionMessageIndex);
3344
- };
3345
- const getLatestCompactionMessageIndex = (input) => {
3346
- for (let index = input.length - 1; index >= 0; index -= 1) if (isCompactionInputItem(input[index])) return index;
3347
- };
3348
- const isCompactionInputItem = (value) => {
3349
- return "type" in value && typeof value.type === "string" && value.type === "compaction";
3350
- };
3351
- const getPayloadItems = (payload) => {
3352
- const result = [];
3353
- const { input } = payload;
3354
- if (Array.isArray(input)) result.push(...input);
3355
- return result;
3356
- };
3357
- const containsVisionContent = (value) => {
3358
- if (!value) return false;
3359
- if (Array.isArray(value)) return value.some((entry) => containsVisionContent(entry));
3360
- if (typeof value !== "object") return false;
3361
- const record = value;
3362
- if ((typeof record.type === "string" ? record.type.toLowerCase() : void 0) === "input_image") return true;
3363
- if (Array.isArray(record.content)) return record.content.some((entry) => containsVisionContent(entry));
3364
- return false;
3365
- };
3366
- //#endregion
3367
3758
  //#region src/services/copilot/create-messages.ts
3368
3759
  const INTERLEAVED_THINKING_BETA = "interleaved-thinking-2025-05-14";
3369
3760
  const allowedAnthropicBetas = new Set([
@@ -3657,6 +4048,11 @@ const prepareMessagesApiPayload = (payload, selectedModel) => {
3657
4048
  const COPILOT_CONTEXT_CACHE_SYSTEM_MARKER_LIMIT = 2;
3658
4049
  const COPILOT_CONTEXT_CACHE_NON_SYSTEM_MARKER_LIMIT = 2;
3659
4050
  const COPILOT_CONTEXT_CACHE_CONTROL = { type: "ephemeral" };
4051
+ const messagesApiFlowDependencies = {
4052
+ createChatCompletions,
4053
+ createMessages,
4054
+ createResponses
4055
+ };
3660
4056
  const handleWithChatCompletions = async (c, anthropicPayload, options) => {
3661
4057
  const { logger, subagentMarker, requestId, sessionId, compactType } = options;
3662
4058
  const openAIPayload = translateToOpenAI(anthropicPayload);
@@ -3668,7 +4064,7 @@ const handleWithChatCompletions = async (c, anthropicPayload, options) => {
3668
4064
  payload: anthropicPayload
3669
4065
  });
3670
4066
  debugJson(logger, "Translated OpenAI request payload:", openAIPayload);
3671
- const response = await createChatCompletions(openAIPayload, {
4067
+ const response = await messagesApiFlowDependencies.createChatCompletions(openAIPayload, {
3672
4068
  subagentMarker,
3673
4069
  requestId,
3674
4070
  sessionId,
@@ -3731,9 +4127,11 @@ const handleWithResponsesApi = async (c, anthropicPayload, options) => {
3731
4127
  compactInputByLatestCompaction(responsesPayload);
3732
4128
  debugJson(logger, "Translated Responses payload:", responsesPayload);
3733
4129
  const { vision, initiator } = getResponsesRequestOptions(responsesPayload);
3734
- const response = await createResponses(responsesPayload, {
4130
+ const transport = getResponsesTransportForModel(selectedModel, { compactType: requestOptions.compactType }) ?? "http";
4131
+ const response = await messagesApiFlowDependencies.createResponses(responsesPayload, {
3735
4132
  vision,
3736
4133
  initiator,
4134
+ transport,
3737
4135
  ...requestOptions
3738
4136
  });
3739
4137
  if (responsesPayload.stream && isAsyncIterable$1(response)) {
@@ -3798,7 +4196,7 @@ const handleWithMessagesApi = async (c, anthropicPayload, options) => {
3798
4196
  payload: anthropicPayload
3799
4197
  });
3800
4198
  debugJson(logger, "Translated Messages payload:", anthropicPayload);
3801
- const response = await createMessages(anthropicPayload, anthropicBetaHeader, {
4199
+ const response = await messagesApiFlowDependencies.createMessages(anthropicPayload, anthropicBetaHeader, {
3802
4200
  subagentMarker,
3803
4201
  requestId,
3804
4202
  sessionId,
@@ -3961,7 +4359,7 @@ async function handleCompletion(c) {
3961
4359
  compactType,
3962
4360
  logger: logger$3
3963
4361
  });
3964
- if (shouldUseResponsesApi(selectedModel)) return await messagesFlowHandlers.handleWithResponsesApi(c, anthropicPayload, {
4362
+ if (shouldUseResponsesApi(selectedModel, compactType)) return await messagesFlowHandlers.handleWithResponsesApi(c, anthropicPayload, {
3965
4363
  subagentMarker,
3966
4364
  selectedModel,
3967
4365
  requestId,
@@ -3977,10 +4375,9 @@ async function handleCompletion(c) {
3977
4375
  logger: logger$3
3978
4376
  });
3979
4377
  }
3980
- const RESPONSES_ENDPOINT$1 = "/responses";
3981
4378
  const MESSAGES_ENDPOINT = "/v1/messages";
3982
- const shouldUseResponsesApi = (selectedModel) => {
3983
- return selectedModel?.supported_endpoints?.includes(RESPONSES_ENDPOINT$1) ?? false;
4379
+ const shouldUseResponsesApi = (selectedModel, compactType) => {
4380
+ return Boolean(getResponsesTransportForModel(selectedModel, { compactType }));
3984
4381
  };
3985
4382
  const shouldUseMessagesApi = (selectedModel) => {
3986
4383
  if (!isMessagesApiEnabled()) return false;
@@ -4113,9 +4510,14 @@ const handleItemId = (parsed, tracker) => {
4113
4510
  //#endregion
4114
4511
  //#region src/routes/responses/handler.ts
4115
4512
  const logger$1 = createHandlerLogger("responses-handler");
4116
- const RESPONSES_ENDPOINT = "/responses";
4513
+ const responsesHandlerDependencies = {
4514
+ checkRateLimit,
4515
+ createResponses,
4516
+ getConfig,
4517
+ isResponsesApiWebSearchEnabled
4518
+ };
4117
4519
  const handleResponses = async (c) => {
4118
- await checkRateLimit(state);
4520
+ await responsesHandlerDependencies.checkRateLimit(state);
4119
4521
  const payload = await c.req.json();
4120
4522
  debugJson(logger$1, "Responses request payload:", payload);
4121
4523
  const requestId = generateRequestIdFromPayload({ messages: payload.input });
@@ -4129,10 +4531,11 @@ const handleResponses = async (c) => {
4129
4531
  });
4130
4532
  useFunctionApplyPatch(payload);
4131
4533
  removeUnsupportedTools(payload);
4132
- if (!isResponsesApiWebSearchEnabled()) removeWebSearchTool(payload);
4534
+ if (!responsesHandlerDependencies.isResponsesApiWebSearchEnabled()) removeWebSearchTool(payload);
4133
4535
  compactInputByLatestCompaction(payload);
4134
4536
  const selectedModel = state.models?.data.find((model) => model.id === payload.model);
4135
- if (!(selectedModel?.supported_endpoints?.includes(RESPONSES_ENDPOINT) ?? false)) return c.json({ error: {
4537
+ const responsesTransport = getResponsesTransportForModel(selectedModel);
4538
+ if (!responsesTransport) return c.json({ error: {
4136
4539
  message: "This model does not support the responses endpoint. Please choose a different model.",
4137
4540
  type: "invalid_request_error"
4138
4541
  } }, 400);
@@ -4140,11 +4543,12 @@ const handleResponses = async (c) => {
4140
4543
  debugJson(logger$1, "Translated Responses payload:", payload);
4141
4544
  const { vision, initiator } = getResponsesRequestOptions(payload);
4142
4545
  if (state.manualApprove) await awaitApproval();
4143
- const response = await createResponses(payload, {
4546
+ const response = await responsesHandlerDependencies.createResponses(payload, {
4144
4547
  vision,
4145
4548
  initiator,
4146
4549
  requestId,
4147
- sessionId
4550
+ sessionId,
4551
+ transport: responsesTransport
4148
4552
  });
4149
4553
  if (isStreamingRequested(payload) && isAsyncIterable(response)) {
4150
4554
  logger$1.debug("Forwarding native Responses stream");
@@ -4184,7 +4588,7 @@ const parseResponsesStreamEvent = (chunk) => {
4184
4588
  }
4185
4589
  };
4186
4590
  const useFunctionApplyPatch = (payload) => {
4187
- if (getConfig().useFunctionApplyPatch ?? true) {
4591
+ if (responsesHandlerDependencies.getConfig().useFunctionApplyPatch ?? true) {
4188
4592
  logger$1.debug("Using function tool apply_patch for responses");
4189
4593
  if (Array.isArray(payload.tools)) {
4190
4594
  const toolsArr = payload.tools;
@@ -4258,6 +4662,10 @@ tokenUsageRoute.get("/", async (c) => {
4258
4662
  const summary = await getTokenUsageSummary(parsePeriod(c.req.query("period")));
4259
4663
  return c.json(summary);
4260
4664
  });
4665
+ tokenUsageRoute.get("/daily", async (c) => {
4666
+ const summary = await getTokenUsageDailySummary(parsePeriod(c.req.query("period")));
4667
+ return c.json(summary);
4668
+ });
4261
4669
  tokenUsageRoute.get("/events", async (c) => {
4262
4670
  const period = parsePeriod(c.req.query("period"));
4263
4671
  const eventsPage = await getTokenUsageEventsPage({
@@ -4327,4 +4735,4 @@ server.route("/:provider/v1/models", providerModelRoutes);
4327
4735
  //#endregion
4328
4736
  export { server };
4329
4737
 
4330
- //# sourceMappingURL=server-DkUKa2I6.js.map
4738
+ //# sourceMappingURL=server-DpVPS3zt.js.map