@jeffreycao/copilot-api 1.14.23 → 1.15.2

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,7 +1,7 @@
1
- import { D as setModelMappings, E as resolveProviderAuthType, T as resolveMappedModel, _ as isMessagesApiEnabled, a as getConfig, c as getModelMappings, d as getRawProviderConfig, f as getReasoningEffortForModel, g as isGpt56OrAbove, h as isContextManagementEnabledForResponses, i as getClaudeTokenMultiplier, k as PATHS, l as getModelResponsesApiCompactThreshold$1, m as isContextManagementEnabledForMessages, n as getAnthropicApiKey, o as getExtraPromptForModel, p as getSmallModel, r as getClaudeAutoModel, s as getMessageApiWebSearchModel, u as getProviderConfig, v as isResponsesApiWebSearchEnabled, w as resolveEffectiveProviderType, x as listEnabledProviders, y as isResponsesApiWebSocketEnabled } from "./config-NTX23mAe.js";
2
- import { B as HTTPError, C as prepareMessageProxyHeaders, E as compactMessageSections, F as requestContext, I as resolveTraceId$1, L as createPooledWebSocketStream, M as buildCodexRequestHeaders, N as forwardCodexResponses, O as compactSystemPromptStarts, P as generateTraceId, R as createWebSocketUrl, S as prepareInteractionHeaders, T as compactAutoContinuePromptStarts, V as forwardError, b as copilotWebSocketHeaders, d as generateRequestIdFromPayload, f as getRootSessionId, g as getCopilotUsage, h as parseUserIdMetadata, j as CODEX_API_BASE_URL, m as isNullish, p as getUUID, r as setupCodexToken, v as copilotBaseUrl, x as prepareForCompact, y as copilotHeaders, z as state } from "./token-BjW645tq.js";
1
+ import { A as PATHS, D as resolveProviderAuthType, E as resolveMappedModel, O as setModelMappings, S as listEnabledProviders, T as resolveEffectiveProviderType, _ as isGpt56OrAbove, a as getConfig, b as isResponsesApiWebSocketEnabled, c as getModelMappings, d as getRawProviderConfig, f as getReasoningEffortForModel, g as isContextManagementEnabledForResponses, h as isContextManagementEnabledForMessages, i as getClaudeTokenMultiplier, l as getModelResponsesApiCompactThreshold$1, m as isAlphaSearchCodexPriorityEnabled, n as getAnthropicApiKey, o as getExtraPromptForModel, p as getSmallModel, r as getClaudeAutoModel, s as getMessageApiWebSearchModel, u as getProviderConfig, v as isMessagesApiEnabled, y as isResponsesApiWebSearchEnabled } from "./config-B3hsNlmB.js";
2
+ import { B as HTTPError, C as prepareMessageProxyHeaders, E as compactMessageSections, F as requestContext, I as resolveTraceId$1, L as createPooledWebSocketStream, M as buildCodexRequestHeaders, N as forwardCodexResponses, O as compactSystemPromptStarts, P as generateTraceId, R as createWebSocketUrl, S as prepareInteractionHeaders, T as compactAutoContinuePromptStarts, V as forwardError, b as copilotWebSocketHeaders, d as generateRequestIdFromPayload, f as getRootSessionId, g as getCopilotUsage, h as parseUserIdMetadata, j as CODEX_API_BASE_URL, m as isNullish, p as getUUID, r as setupCodexToken, v as copilotBaseUrl, x as prepareForCompact, y as copilotHeaders, z as state } from "./token-DArgO8vi.js";
3
3
  import { a as isDeferredToolName, c as parseMcpToolSearchSentinel, d as shouldEnableResponsesToolSearch, i as isBridgeToolSearchName, l as resolveBridgeToolSearchName, o as listDeferredToolNames, r as formatToolSearchBridgeArguments, s as normalizeToolSearchBridgeArguments, t as BRIDGE_TOOL_SEARCH_NAME, u as selectDeferredToolsByNames } from "./tool-search-Ds1vbmGG.js";
4
- import { i as toClientModelId, r as normalizeSdkModelId, t as findEndpointModel } from "./models-D-Rwtv0H.js";
4
+ import { i as toClientModelId, r as normalizeSdkModelId, t as findEndpointModel } from "./models-_FSj4UOe.js";
5
5
  import consola from "consola";
6
6
  import { createHash } from "node:crypto";
7
7
  import fs, { readFileSync } from "node:fs";
@@ -193,22 +193,26 @@ function registerProcessCleanup(handler) {
193
193
  //#region src/lib/logger.ts
194
194
  const LOG_RETENTION_MS = 10080 * 60 * 1e3;
195
195
  const CLEANUP_INTERVAL_MS = 1440 * 60 * 1e3;
196
- const LOG_DIR = path.join(PATHS.APP_DIR, "logs");
196
+ const LOG_DIR_ENV = "COPILOT_API_LOG_DIR";
197
+ const DEFAULT_LOG_DIR = path.join(PATHS.APP_DIR, "logs");
197
198
  const FLUSH_INTERVAL_MS = 1e3;
198
199
  const MAX_BUFFER_SIZE = 100;
200
+ const getLogDir = () => process.env[LOG_DIR_ENV]?.trim() || DEFAULT_LOG_DIR;
199
201
  const logStreams = /* @__PURE__ */ new Map();
200
202
  const logBuffers = /* @__PURE__ */ new Map();
201
203
  let runtimeInitialized = false;
202
204
  let flushInterval;
203
205
  let cleanupInterval;
204
206
  const ensureLogDirectory = () => {
205
- if (!fs.existsSync(LOG_DIR)) fs.mkdirSync(LOG_DIR, { recursive: true });
207
+ const logDir = getLogDir();
208
+ if (!fs.existsSync(logDir)) fs.mkdirSync(logDir, { recursive: true });
206
209
  };
207
210
  const cleanupOldLogs = () => {
208
- if (!fs.existsSync(LOG_DIR)) return;
211
+ const logDir = getLogDir();
212
+ if (!fs.existsSync(logDir)) return;
209
213
  const now = Date.now();
210
- for (const entry of fs.readdirSync(LOG_DIR)) {
211
- const filePath = path.join(LOG_DIR, entry);
214
+ for (const entry of fs.readdirSync(logDir)) {
215
+ const filePath = path.join(logDir, entry);
212
216
  let stats;
213
217
  try {
214
218
  stats = fs.statSync(filePath);
@@ -247,7 +251,13 @@ const flushBuffer = (filePath) => {
247
251
  const flushAllBuffers = () => {
248
252
  for (const filePath of logBuffers.keys()) flushBuffer(filePath);
249
253
  };
250
- const cleanup = () => {
254
+ const flushAndCloseLogStreams = () => {
255
+ flushAllBuffers();
256
+ for (const stream of logStreams.values()) stream.end();
257
+ logStreams.clear();
258
+ logBuffers.clear();
259
+ };
260
+ const shutdownLoggerRuntime = () => {
251
261
  if (flushInterval) {
252
262
  clearInterval(flushInterval);
253
263
  flushInterval = void 0;
@@ -256,10 +266,8 @@ const cleanup = () => {
256
266
  clearInterval(cleanupInterval);
257
267
  cleanupInterval = void 0;
258
268
  }
259
- flushAllBuffers();
260
- for (const stream of logStreams.values()) stream.end();
261
- logStreams.clear();
262
- logBuffers.clear();
269
+ flushAndCloseLogStreams();
270
+ runtimeInitialized = false;
263
271
  };
264
272
  const initializeLoggerRuntime = () => {
265
273
  if (runtimeInitialized) return;
@@ -270,7 +278,7 @@ const initializeLoggerRuntime = () => {
270
278
  maybeUnref(flushInterval);
271
279
  cleanupInterval = setInterval(cleanupOldLogs, CLEANUP_INTERVAL_MS);
272
280
  maybeUnref(cleanupInterval);
273
- registerProcessCleanup(cleanup);
281
+ registerProcessCleanup(shutdownLoggerRuntime);
274
282
  };
275
283
  const getLogStream = (filePath) => {
276
284
  initializeLoggerRuntime();
@@ -319,7 +327,7 @@ const createHandlerLogger = (name) => {
319
327
  const date = logObj.date;
320
328
  const dateKey = date.toLocaleDateString("sv-SE");
321
329
  const timestamp = date.toLocaleString("sv-SE", { hour12: false });
322
- const filePath = path.join(LOG_DIR, `${sanitizedName}-${dateKey}.log`);
330
+ const filePath = path.join(getLogDir(), `${sanitizedName}-${dateKey}.log`);
323
331
  const message = formatArgs(logObj.args);
324
332
  const traceIdStr = traceId ? ` [${traceId}]` : "";
325
333
  appendLine(filePath, `[${timestamp}] [${logObj.type}] [${logObj.tag || name}]${traceIdStr}${message ? ` ${message}` : ""}`);
@@ -327,203 +335,6 @@ const createHandlerLogger = (name) => {
327
335
  return instance;
328
336
  };
329
337
  //#endregion
330
- //#region src/lib/provider-resolver.ts
331
- function isMissingCodexCredentialsError(error) {
332
- return error instanceof Error && error.message === "Codex credentials not found. Run `copilot-api auth login --provider codex` first.";
333
- }
334
- async function resolveProviderConfig(providerName) {
335
- const normalizedProviderName = providerName.trim();
336
- if (!normalizedProviderName) return null;
337
- if (normalizedProviderName === "codex") {
338
- if (getRawProviderConfig(normalizedProviderName)?.enabled === false) return null;
339
- try {
340
- await setupCodexToken();
341
- } catch (error) {
342
- if (isMissingCodexCredentialsError(error)) return null;
343
- throw error;
344
- }
345
- const providerConfig = getProviderConfig(normalizedProviderName);
346
- if (!providerConfig) return null;
347
- return {
348
- ...providerConfig,
349
- apiKey: state.codexAccessToken ?? providerConfig.apiKey
350
- };
351
- }
352
- return getProviderConfig(normalizedProviderName);
353
- }
354
- //#endregion
355
- //#region src/services/codex/alpha-search.ts
356
- const CODEX_ALPHA_SEARCH_URL = `${CODEX_API_BASE_URL}/codex/alpha/search`;
357
- function resolveCodexAlphaSearchUrl(requestUrl) {
358
- const upstreamUrl = new URL(CODEX_ALPHA_SEARCH_URL);
359
- upstreamUrl.search = new URL(requestUrl, "http://localhost").search;
360
- return upstreamUrl.toString();
361
- }
362
- async function forwardCodexAlphaSearch(request) {
363
- const headers = buildCodexRequestHeaders(request.headers);
364
- if (!headers.has("accept")) headers.set("accept", "application/json");
365
- const body = await request.arrayBuffer();
366
- if (!headers.has("content-type")) headers.set("content-type", "application/json");
367
- return await fetch(resolveCodexAlphaSearchUrl(request.url), {
368
- method: "POST",
369
- headers,
370
- body
371
- });
372
- }
373
- //#endregion
374
- //#region src/services/providers/provider-proxy.ts
375
- const SHARED_FORWARDABLE_HEADERS = ["accept", "user-agent"];
376
- const ANTHROPIC_FORWARDABLE_HEADERS = ["anthropic-version", "anthropic-beta"];
377
- const STRIPPED_RESPONSE_HEADERS = [
378
- "connection",
379
- "content-encoding",
380
- "content-length",
381
- "keep-alive",
382
- "proxy-authenticate",
383
- "proxy-authorization",
384
- "te",
385
- "trailer",
386
- "transfer-encoding",
387
- "upgrade"
388
- ];
389
- function buildProviderUpstreamHeaders(providerConfig, requestHeaders) {
390
- const authHeaders = {};
391
- if (providerConfig.authType === "x-api-key") authHeaders["x-api-key"] = providerConfig.apiKey;
392
- else authHeaders.authorization = `Bearer ${providerConfig.apiKey}`;
393
- const headers = {
394
- "content-type": "application/json",
395
- accept: "application/json",
396
- ...authHeaders
397
- };
398
- for (const headerName of SHARED_FORWARDABLE_HEADERS) {
399
- const headerValue = requestHeaders.get(headerName);
400
- if (headerValue) headers[headerName] = headerValue;
401
- }
402
- if (providerConfig.type !== "anthropic") return headers;
403
- for (const headerName of ANTHROPIC_FORWARDABLE_HEADERS) {
404
- const headerValue = requestHeaders.get(headerName);
405
- if (headerValue) headers[headerName] = headerValue;
406
- }
407
- return headers;
408
- }
409
- function createProviderProxyResponse(upstreamResponse, body) {
410
- const headers = new Headers(upstreamResponse.headers);
411
- for (const headerName of STRIPPED_RESPONSE_HEADERS) headers.delete(headerName);
412
- return new Response(body ?? upstreamResponse.body, {
413
- headers,
414
- status: upstreamResponse.status,
415
- statusText: upstreamResponse.statusText
416
- });
417
- }
418
- async function forwardProviderMessages(providerConfig, payload, requestHeaders) {
419
- consola.log(`<-- model: ${payload.model}`);
420
- return await fetch(`${providerConfig.baseUrl}/v1/messages`, {
421
- method: "POST",
422
- headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
423
- body: JSON.stringify(payload)
424
- });
425
- }
426
- async function forwardProviderChatCompletions(providerConfig, payload, requestHeaders) {
427
- consola.log(`<-- model: ${payload.model}`);
428
- return await fetch(`${providerConfig.baseUrl}/v1/chat/completions`, {
429
- method: "POST",
430
- headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
431
- body: JSON.stringify(payload)
432
- });
433
- }
434
- async function forwardProviderResponses(providerConfig, payload, requestHeaders) {
435
- consola.log(`<-- model: ${payload.model}`);
436
- return await fetch(`${providerConfig.baseUrl}/v1/responses`, {
437
- method: "POST",
438
- headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
439
- body: JSON.stringify(payload)
440
- });
441
- }
442
- async function forwardProviderModels(providerConfig, requestHeaders) {
443
- return await fetch(`${providerConfig.baseUrl}/v1/models`, {
444
- method: "GET",
445
- headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders)
446
- });
447
- }
448
- /** Align with Codex images: long-running generation/edits need a generous cap. */
449
- const PROVIDER_IMAGES_TIMEOUT_MS = 900 * 1e3;
450
- const providerImagesDispatcher = { dispatch(options, handler) {
451
- return getGlobalDispatcher().dispatch({
452
- ...options,
453
- bodyTimeout: PROVIDER_IMAGES_TIMEOUT_MS,
454
- headersTimeout: PROVIDER_IMAGES_TIMEOUT_MS
455
- }, handler);
456
- } };
457
- function resolveProviderRequestUrl(providerConfig, requestUrl, path) {
458
- const upstreamUrl = new URL(`${providerConfig.baseUrl}${path}`);
459
- upstreamUrl.search = new URL(requestUrl, "http://localhost").search;
460
- return upstreamUrl.toString();
461
- }
462
- async function forwardProviderAlphaSearch(providerConfig, request) {
463
- const headers = buildProviderUpstreamHeaders(providerConfig, request.headers);
464
- const body = await request.arrayBuffer();
465
- return await fetch(resolveProviderRequestUrl(providerConfig, request.url, "/v1/alpha/search"), {
466
- method: "POST",
467
- headers,
468
- body
469
- });
470
- }
471
- async function forwardProviderImages(providerConfig, request, operation) {
472
- const headers = buildProviderUpstreamHeaders(providerConfig, request.headers);
473
- const contentType = request.headers.get("content-type");
474
- if (contentType) headers["content-type"] = contentType;
475
- else if (operation === "edits") delete headers["content-type"];
476
- const init = {
477
- method: "POST",
478
- headers,
479
- body: request.body,
480
- duplex: "half",
481
- signal: AbortSignal.timeout(PROVIDER_IMAGES_TIMEOUT_MS)
482
- };
483
- const upstreamUrl = resolveProviderRequestUrl(providerConfig, request.url, `/v1/images/${operation}`);
484
- if (typeof Bun !== "undefined") return await fetch(upstreamUrl, init);
485
- return await fetch$1(upstreamUrl, {
486
- ...init,
487
- dispatcher: providerImagesDispatcher
488
- });
489
- }
490
- //#endregion
491
- //#region src/routes/alpha-search/route.ts
492
- const logger$14 = createHandlerLogger("alpha-search-handler");
493
- const alphaSearchRoutes = new Hono();
494
- function parseDebugBody(body) {
495
- try {
496
- return JSON.parse(body);
497
- } catch {
498
- return body;
499
- }
500
- }
501
- /**
502
- * Handles Codex alpha-search proxying. Pass `resolvedProviderConfig` when the
503
- * caller already resolved the codex provider to avoid a second resolve.
504
- */
505
- async function handleCodexAlphaSearch(c, resolvedProviderConfig) {
506
- if (!(resolvedProviderConfig ?? await resolveProviderConfig("codex"))) return c.json({ error: {
507
- message: "Provider 'codex' not found or disabled",
508
- type: "invalid_request_error"
509
- } }, 404);
510
- await debugJsonAsync(logger$14, "alpha_search.codex.request", async () => ({ body: parseDebugBody(await c.req.raw.clone().text()) }));
511
- const upstreamResponse = await forwardCodexAlphaSearch(c.req.raw);
512
- await debugJsonAsync(logger$14, "alpha_search.codex.response", async () => ({
513
- body: parseDebugBody(await upstreamResponse.clone().text()),
514
- statusCode: upstreamResponse.status
515
- }));
516
- return createProviderProxyResponse(upstreamResponse);
517
- }
518
- alphaSearchRoutes.post("/", async (c) => {
519
- try {
520
- return await handleCodexAlphaSearch(c);
521
- } catch (error) {
522
- logger$14.error("alpha_search.codex.error", { error });
523
- return await forwardError(c, error);
524
- }
525
- });
526
- //#endregion
527
338
  //#region src/lib/provider-model.ts
528
339
  const parseProviderModelAlias = (model) => {
529
340
  const separatorIndex = model.indexOf("/");
@@ -554,6 +365,31 @@ const createFallbackModel = (modelId) => ({
554
365
  version: "unknown"
555
366
  });
556
367
  //#endregion
368
+ //#region src/lib/provider-resolver.ts
369
+ function isMissingCodexCredentialsError(error) {
370
+ return error instanceof Error && error.message === "Codex credentials not found. Run `copilot-api auth login --provider codex` first.";
371
+ }
372
+ async function resolveProviderConfig(providerName) {
373
+ const normalizedProviderName = providerName.trim();
374
+ if (!normalizedProviderName) return null;
375
+ if (normalizedProviderName === "codex") {
376
+ if (getRawProviderConfig(normalizedProviderName)?.enabled === false) return null;
377
+ try {
378
+ await setupCodexToken();
379
+ } catch (error) {
380
+ if (isMissingCodexCredentialsError(error)) return null;
381
+ throw error;
382
+ }
383
+ const providerConfig = getProviderConfig(normalizedProviderName);
384
+ if (!providerConfig) return null;
385
+ return {
386
+ ...providerConfig,
387
+ apiKey: state.codexAccessToken ?? providerConfig.apiKey
388
+ };
389
+ }
390
+ return getProviderConfig(normalizedProviderName);
391
+ }
392
+ //#endregion
557
393
  //#region src/lib/event-bus.ts
558
394
  var EventBus = class {
559
395
  handlers = /* @__PURE__ */ new Map();
@@ -1203,6 +1039,7 @@ const BUILTIN_PROVIDER_CURRENCIES = {
1203
1039
  codex: "USD",
1204
1040
  dashscope: "CNY",
1205
1041
  deepseek: "CNY",
1042
+ kimi: "USD",
1206
1043
  "opencode-go": "USD"
1207
1044
  };
1208
1045
  const BUILTIN_PROVIDER_PRICING = {
@@ -1335,7 +1172,12 @@ const BUILTIN_PROVIDER_PRICING = {
1335
1172
  input: 6,
1336
1173
  maxInputTokens: 1e6,
1337
1174
  output: 24
1338
- }] }
1175
+ }] },
1176
+ "kimi/kimi-k3": {
1177
+ cachedInput: 2,
1178
+ input: 20,
1179
+ output: 100
1180
+ }
1339
1181
  },
1340
1182
  deepseek: {
1341
1183
  "deepseek-v4-flash": {
@@ -1453,6 +1295,18 @@ const BUILTIN_PROVIDER_PRICING = {
1453
1295
  maxInputTokens: 512e3,
1454
1296
  output: 2.4
1455
1297
  }] }
1298
+ },
1299
+ kimi: {
1300
+ k3: {
1301
+ cachedInput: .3,
1302
+ input: 3,
1303
+ output: 15
1304
+ },
1305
+ "k3-256k": {
1306
+ cachedInput: .3,
1307
+ input: 3,
1308
+ output: 15
1309
+ }
1456
1310
  }
1457
1311
  };
1458
1312
  function resolveTokenUsageCost(input) {
@@ -1671,24 +1525,1151 @@ function normalizeOptionalCost(value) {
1671
1525
  return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
1672
1526
  }
1673
1527
  //#endregion
1674
- //#region src/lib/dashscope.ts
1675
- const OPENAI_COMPATIBLE_CONTEXT_CACHE_MARKER_LIMIT = 4;
1676
- const OPENAI_COMPATIBLE_CONTEXT_CACHE_CONTROL = { type: "ephemeral" };
1677
- const OPENAI_COMPATIBLE_CONTEXT_CACHE_ROLES = new Set([
1678
- "system",
1679
- "user",
1680
- "assistant",
1681
- "tool"
1682
- ]);
1683
- const isDashScopeAliyunProvider = (providerConfig) => providerConfig.name === "dashscope" || providerConfig.baseUrl.includes("aliyuncs.com");
1684
- const applyDashScopePreserveThinkingDefault = (payload, providerConfig) => {
1685
- if (!isDashScopeAliyunProvider(providerConfig)) return;
1686
- if (!Object.hasOwn(payload, "preserve_thinking")) payload.preserve_thinking = true;
1687
- };
1688
- const applyOpenAICompatibleContextCache = (payload) => {
1689
- const messageIndexes = selectContextCacheMessageIndexes(payload.messages);
1690
- for (const messageIndex of messageIndexes) applyContextCacheControl(payload.messages[messageIndex]);
1691
- };
1528
+ //#region src/routes/alpha-search/alpha-search-types.ts
1529
+ const unsignedInteger = z.number().int().nonnegative();
1530
+ const passthroughRecord = z.record(z.string(), z.unknown());
1531
+ const searchQuerySchema = z.object({
1532
+ q: z.string(),
1533
+ recency: unsignedInteger.optional(),
1534
+ domains: z.array(z.string()).optional()
1535
+ }).loose();
1536
+ const openOperationSchema = z.object({
1537
+ ref_id: z.string(),
1538
+ lineno: unsignedInteger.optional()
1539
+ }).loose();
1540
+ const clickOperationSchema = z.object({
1541
+ ref_id: z.string(),
1542
+ id: unsignedInteger
1543
+ }).loose();
1544
+ const findOperationSchema = z.object({
1545
+ ref_id: z.string(),
1546
+ pattern: z.string()
1547
+ }).loose();
1548
+ const screenshotOperationSchema = z.object({
1549
+ ref_id: z.string(),
1550
+ pageno: unsignedInteger
1551
+ }).loose();
1552
+ const financeOperationSchema = z.object({
1553
+ ticker: z.string(),
1554
+ type: z.enum([
1555
+ "equity",
1556
+ "fund",
1557
+ "crypto",
1558
+ "index"
1559
+ ]),
1560
+ market: z.string().optional()
1561
+ }).loose();
1562
+ const weatherOperationSchema = z.object({
1563
+ location: z.string(),
1564
+ start: z.string().optional(),
1565
+ duration: unsignedInteger.optional()
1566
+ }).loose();
1567
+ const sportsOperationSchema = z.object({
1568
+ tool: z.literal("sports").optional(),
1569
+ fn: z.enum(["schedule", "standings"]),
1570
+ league: z.enum([
1571
+ "nba",
1572
+ "wnba",
1573
+ "nfl",
1574
+ "nhl",
1575
+ "mlb",
1576
+ "epl",
1577
+ "ncaamb",
1578
+ "ncaawb",
1579
+ "ipl"
1580
+ ]),
1581
+ team: z.string().optional(),
1582
+ opponent: z.string().optional(),
1583
+ date_from: z.string().optional(),
1584
+ date_to: z.string().optional(),
1585
+ num_games: unsignedInteger.optional(),
1586
+ locale: z.string().optional()
1587
+ }).loose();
1588
+ const timeOperationSchema = z.object({ utc_offset: z.string().regex(/^[+-](?:[01]\d|2[0-3]):[0-5]\d$/u) }).loose();
1589
+ const alphaSearchCommandsSchema = z.object({
1590
+ search_query: z.array(searchQuerySchema).optional(),
1591
+ image_query: z.array(searchQuerySchema).optional(),
1592
+ open: z.array(openOperationSchema).optional(),
1593
+ click: z.array(clickOperationSchema).optional(),
1594
+ find: z.array(findOperationSchema).optional(),
1595
+ screenshot: z.array(screenshotOperationSchema).optional(),
1596
+ finance: z.array(financeOperationSchema).optional(),
1597
+ weather: z.array(weatherOperationSchema).optional(),
1598
+ sports: z.array(sportsOperationSchema).optional(),
1599
+ time: z.array(timeOperationSchema).optional(),
1600
+ response_length: z.enum([
1601
+ "short",
1602
+ "medium",
1603
+ "long"
1604
+ ]).optional()
1605
+ }).loose();
1606
+ const reasoningSchema = z.object({
1607
+ effort: z.string().min(1).nullable().optional(),
1608
+ summary: z.enum([
1609
+ "auto",
1610
+ "concise",
1611
+ "detailed",
1612
+ "none"
1613
+ ]).nullable().optional(),
1614
+ context: z.enum([
1615
+ "auto",
1616
+ "current_turn",
1617
+ "all_turns"
1618
+ ]).nullable().optional()
1619
+ }).loose();
1620
+ const settingsSchema = z.object({
1621
+ user_location: z.object({
1622
+ type: z.literal("approximate"),
1623
+ country: z.string().optional(),
1624
+ region: z.string().optional(),
1625
+ city: z.string().optional(),
1626
+ timezone: z.string().optional()
1627
+ }).loose().optional(),
1628
+ search_context_size: z.enum([
1629
+ "low",
1630
+ "medium",
1631
+ "high"
1632
+ ]).optional(),
1633
+ filters: z.object({
1634
+ allowed_domains: z.array(z.string()).optional(),
1635
+ blocked_domains: z.array(z.string()).optional()
1636
+ }).loose().optional(),
1637
+ image_settings: z.object({
1638
+ max_results: unsignedInteger.optional(),
1639
+ caption: z.boolean().optional()
1640
+ }).loose().optional(),
1641
+ allowed_callers: z.array(z.enum([
1642
+ "direct",
1643
+ "shell",
1644
+ "code_interpreter"
1645
+ ])).optional(),
1646
+ external_web_access: z.union([z.boolean(), z.enum([
1647
+ "cached",
1648
+ "indexed",
1649
+ "live"
1650
+ ])]).optional()
1651
+ }).loose();
1652
+ const alphaSearchRequestSchema = z.object({
1653
+ id: z.string(),
1654
+ model: z.string(),
1655
+ reasoning: reasoningSchema.optional(),
1656
+ input: z.union([z.string(), z.array(passthroughRecord)]).optional(),
1657
+ commands: alphaSearchCommandsSchema.optional(),
1658
+ settings: settingsSchema.optional(),
1659
+ max_output_tokens: unsignedInteger.optional()
1660
+ }).loose();
1661
+ //#endregion
1662
+ //#region src/routes/messages/web-search/backend.ts
1663
+ /** Builds the Responses API web_search tool object from normalized config. */
1664
+ const buildResponsesWebSearchTool = (config) => {
1665
+ const tool = { type: "web_search" };
1666
+ const filters = {};
1667
+ if (config.allowedDomains?.length) filters.allowed_domains = config.allowedDomains;
1668
+ if (config.blockedDomains?.length) filters.blocked_domains = config.blockedDomains;
1669
+ if (Object.keys(filters).length > 0) tool.filters = filters;
1670
+ if (config.userLocation) tool.user_location = config.userLocation;
1671
+ if (config.searchContextSize) tool.search_context_size = config.searchContextSize;
1672
+ return tool;
1673
+ };
1674
+ const isMessageItem = (item) => item.type === "message";
1675
+ const isValidUrlCitation = (annotation, seenUrls) => {
1676
+ const ann = annotation;
1677
+ return ann.type === "url_citation" && Boolean(ann.url) && !seenUrls.has(ann.url);
1678
+ };
1679
+ const collectTextParts = (blocks, seenUrls) => {
1680
+ const textParts = [];
1681
+ const sources = [];
1682
+ for (const block of blocks ?? []) {
1683
+ if (block.type !== "output_text") continue;
1684
+ if (block.text) textParts.push(block.text);
1685
+ for (const annotation of block.annotations ?? []) {
1686
+ if (!isValidUrlCitation(annotation, seenUrls)) continue;
1687
+ const ann = annotation;
1688
+ seenUrls.add(ann.url);
1689
+ const start = Math.max(0, (ann.start_index ?? 0) - 120);
1690
+ const end = Math.min(block.text?.length ?? 0, (ann.end_index ?? block.text?.length ?? 0) + 120);
1691
+ sources.push({
1692
+ url: ann.url,
1693
+ title: ann.title ?? ann.url,
1694
+ snippet: block.text?.slice(start, end).trim()
1695
+ });
1696
+ }
1697
+ }
1698
+ return {
1699
+ textParts,
1700
+ sources
1701
+ };
1702
+ };
1703
+ const collectQuery = (item, queries) => {
1704
+ if (item.action?.queries?.length) queries.push(...item.action.queries);
1705
+ else if (item.action?.query) queries.push(item.action.query);
1706
+ };
1707
+ /**
1708
+ * Extracts the answer text, deduped sources, and run queries from a GPT
1709
+ * /responses web_search result.
1710
+ */
1711
+ const extractWebSearchResult = (result) => {
1712
+ const textParts = [];
1713
+ const sources = [];
1714
+ const seenUrls = /* @__PURE__ */ new Set();
1715
+ const queries = [];
1716
+ for (const item of result.output) if (isMessageItem(item)) {
1717
+ const collected = collectTextParts(item.content, seenUrls);
1718
+ textParts.push(...collected.textParts);
1719
+ sources.push(...collected.sources);
1720
+ }
1721
+ for (const item of result.output) if (item.type === "web_search_call") {
1722
+ const action = item.action;
1723
+ collectQuery({ action }, queries);
1724
+ for (const source of action?.sources ?? []) {
1725
+ if (!source.url || seenUrls.has(source.url)) continue;
1726
+ seenUrls.add(source.url);
1727
+ sources.push({
1728
+ url: source.url,
1729
+ title: source.url
1730
+ });
1731
+ }
1732
+ }
1733
+ return {
1734
+ answerText: textParts.join("\n\n").trim() || (result.output_text ?? "").trim(),
1735
+ sources,
1736
+ queries
1737
+ };
1738
+ };
1739
+ //#endregion
1740
+ //#region src/lib/copilot-rate-limit.ts
1741
+ const copilotRateLimitTypes = ["session", "weekly"];
1742
+ const copilotRateLimitHeaders = {
1743
+ session: "x-usage-ratelimit-session",
1744
+ weekly: "x-usage-ratelimit-weekly"
1745
+ };
1746
+ const copilotQuotaSnapshotKeys = {
1747
+ session: "5Hour-Session-RateLimits",
1748
+ weekly: "Weekly-Session-RateLimits"
1749
+ };
1750
+ const hasGetMethod = (headers) => {
1751
+ return "get" in headers && typeof headers.get === "function";
1752
+ };
1753
+ const getHeaderValue$1 = (headers, headerName) => {
1754
+ if (hasGetMethod(headers)) return headers.get(headerName);
1755
+ const normalizedHeaderName = headerName.toLowerCase();
1756
+ return Object.entries(headers).find(([key]) => key.toLowerCase() === normalizedHeaderName)?.[1] ?? null;
1757
+ };
1758
+ const parseCopilotRateLimitHeader = (headerValue) => {
1759
+ const params = new URLSearchParams(headerValue);
1760
+ const remaining = params.get("rem");
1761
+ const resetAt = params.get("rst");
1762
+ if (!remaining || !resetAt) return null;
1763
+ return {
1764
+ remaining,
1765
+ resetAt
1766
+ };
1767
+ };
1768
+ const getCopilotRateLimitUsage = (headers, type) => {
1769
+ const headerName = copilotRateLimitHeaders[type];
1770
+ const headerValue = getHeaderValue$1(headers, headerName);
1771
+ if (!headerValue) return null;
1772
+ const parsed = parseCopilotRateLimitHeader(headerValue);
1773
+ if (!parsed) return null;
1774
+ return {
1775
+ type,
1776
+ ...parsed
1777
+ };
1778
+ };
1779
+ const getCopilotRateLimitUsageFromSnapshots = (snapshots, type) => {
1780
+ const snapshot = snapshots?.[copilotQuotaSnapshotKeys[type]];
1781
+ if (!isCopilotQuotaSnapshot(snapshot)) return null;
1782
+ return {
1783
+ remaining: String(snapshot.percent_remaining),
1784
+ resetAt: snapshot.reset_date,
1785
+ type
1786
+ };
1787
+ };
1788
+ const logCopilotRateLimits = (headers) => {
1789
+ for (const type of copilotRateLimitTypes) {
1790
+ const usage = getCopilotRateLimitUsage(headers, type);
1791
+ if (!usage) continue;
1792
+ logCopilotRateLimitUsage(usage);
1793
+ }
1794
+ };
1795
+ const logCopilotQuotaSnapshots = (snapshots) => {
1796
+ for (const type of copilotRateLimitTypes) {
1797
+ const usage = getCopilotRateLimitUsageFromSnapshots(snapshots, type);
1798
+ if (!usage) continue;
1799
+ logCopilotRateLimitUsage(usage);
1800
+ }
1801
+ };
1802
+ const logCopilotRateLimitUsage = (usage) => {
1803
+ const d = new Date(usage.resetAt);
1804
+ const dateStr = Number.isNaN(d.getTime()) ? usage.resetAt : d.toLocaleString();
1805
+ consola.log(`Copilot ${usage.type} quota remaining: ${usage.remaining}, resets at: ${dateStr}`);
1806
+ };
1807
+ const isCopilotQuotaSnapshot = (value) => {
1808
+ if (!value || typeof value !== "object") return false;
1809
+ const record = value;
1810
+ 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";
1811
+ };
1812
+ //#endregion
1813
+ //#region src/services/copilot/create-responses.ts
1814
+ const createResponses = async (payload, { vision, initiator, subagentMarker, requestId, sessionId, compactType, transport = "http" }) => {
1815
+ if (!state.copilotToken) throw new Error("Copilot token not found");
1816
+ const headers = {
1817
+ ...copilotHeaders(state, requestId, vision),
1818
+ "x-initiator": initiator
1819
+ };
1820
+ prepareInteractionHeaders(sessionId, Boolean(subagentMarker), headers);
1821
+ prepareForCompact(headers, compactType);
1822
+ payload.service_tier = void 0;
1823
+ consola.log(`<-- model: ${payload.model}`);
1824
+ const effectiveTransport = compactType === 1 ? "http" : transport;
1825
+ if (payload.stream === true && effectiveTransport === "websocket") return createPooledResponsesWebSocketStream(prepareResponsesWebSocketRequest(payload, headers, {
1826
+ requestId,
1827
+ subagentMarker
1828
+ }));
1829
+ return await createHttpResponses(payload, headers);
1830
+ };
1831
+ const createHttpResponses = async (payload, headers) => {
1832
+ const response = await fetch(`${copilotBaseUrl(state)}/responses`, {
1833
+ method: "POST",
1834
+ headers,
1835
+ body: JSON.stringify(payload)
1836
+ });
1837
+ logCopilotRateLimits(response.headers);
1838
+ if (!response.ok) {
1839
+ consola.error("Failed to create responses", response);
1840
+ throw new HTTPError("Failed to create responses", response);
1841
+ }
1842
+ if (payload.stream) return events(response);
1843
+ return await response.json();
1844
+ };
1845
+ const prepareResponsesWebSocketRequest = (payload, preparedHeaders, options) => {
1846
+ const initiator = getResponsesWebSocketInitiator(preparedHeaders);
1847
+ return {
1848
+ headers: copilotWebSocketHeaders(preparedHeaders),
1849
+ poolKey: buildResponsesWebSocketPoolKey(payload, options),
1850
+ payload: buildResponsesWebSocketPayload(payload, initiator),
1851
+ url: buildResponsesWebSocketUrl(copilotBaseUrl(state))
1852
+ };
1853
+ };
1854
+ const buildResponsesWebSocketPoolKey = (payload, { requestId, subagentMarker }) => {
1855
+ const tokenFingerprint = state.copilotToken ? createHash("sha256").update(state.copilotToken).digest("hex").slice(0, 16) : "missing-token";
1856
+ const subagentKey = subagentMarker ? [
1857
+ subagentMarker.session_id,
1858
+ subagentMarker.agent_id,
1859
+ subagentMarker.agent_type
1860
+ ].join(":") : "main";
1861
+ return [
1862
+ tokenFingerprint,
1863
+ payload.model,
1864
+ requestId,
1865
+ subagentKey
1866
+ ].map(encodePoolKeyPart).join("|");
1867
+ };
1868
+ const getResponsesWebSocketInitiator = (preparedHeaders) => {
1869
+ return getHeaderValue(preparedHeaders, "x-initiator")?.toLowerCase() === "agent" ? "agent" : "user";
1870
+ };
1871
+ const createPooledResponsesWebSocketStream = (request) => createResponsesSafeStream(createPooledWebSocketStream(request, {
1872
+ createChunk: createResponsesWebSocketStreamChunk,
1873
+ isTerminalChunk: isTerminalResponsesStreamChunk,
1874
+ openErrorMessage: "Failed to create responses websocket",
1875
+ streamErrorMessage: "Responses websocket stream error",
1876
+ terminalChunkMissingMessage: "Responses websocket ended without a terminal response"
1877
+ }));
1878
+ const createResponsesSafeStream = async function* (source) {
1879
+ try {
1880
+ yield* source;
1881
+ } catch (error) {
1882
+ yield createResponsesErrorServerSentEventChunk(getErrorMessage(error));
1883
+ }
1884
+ };
1885
+ const buildResponsesWebSocketPayload = (payload, initiator) => {
1886
+ const websocketPayload = {
1887
+ ...payload,
1888
+ type: "response.create",
1889
+ initiator
1890
+ };
1891
+ delete websocketPayload.stream;
1892
+ delete websocketPayload["background"];
1893
+ delete websocketPayload.service_tier;
1894
+ return websocketPayload;
1895
+ };
1896
+ const buildResponsesWebSocketUrl = (baseUrl) => {
1897
+ return createWebSocketUrl(`${baseUrl.replace(/\/+$/u, "")}/responses`);
1898
+ };
1899
+ const getHeaderValue = (headers, headerName) => {
1900
+ const normalizedHeaderName = headerName.toLowerCase();
1901
+ return Object.entries(headers).find(([key]) => key.toLowerCase() === normalizedHeaderName)?.[1];
1902
+ };
1903
+ const encodePoolKeyPart = (value) => encodeURIComponent(value);
1904
+ const createResponsesWebSocketStreamChunk = (data) => {
1905
+ if (data === "[DONE]") return { data };
1906
+ try {
1907
+ const parsed = JSON.parse(data);
1908
+ if (parsed.type === "response.completed") logCopilotQuotaSnapshots(parsed.copilot_quota_snapshots);
1909
+ if (parsed.type === "error" && parsed.error) {
1910
+ consola.warn("Copilot responses websocket stream error:", parsed.error);
1911
+ parsed.code = parsed.error.code;
1912
+ parsed.message = parsed.error.message;
1913
+ }
1914
+ return {
1915
+ event: typeof parsed.type === "string" ? parsed.type : void 0,
1916
+ data: JSON.stringify(parsed),
1917
+ id: typeof parsed.id === "string" ? parsed.id : void 0
1918
+ };
1919
+ } catch {
1920
+ return { data };
1921
+ }
1922
+ };
1923
+ const isTerminalResponsesStreamChunk = (chunk) => {
1924
+ if (!chunk.data || chunk.data === "[DONE]") return false;
1925
+ try {
1926
+ const parsed = JSON.parse(chunk.data);
1927
+ return parsed.type === "response.completed" || parsed.type === "response.failed" || parsed.type === "response.incomplete" || parsed.type === "error";
1928
+ } catch {
1929
+ return false;
1930
+ }
1931
+ };
1932
+ const createResponsesErrorServerSentEventChunk = (message) => {
1933
+ const errorEvent = {
1934
+ code: null,
1935
+ message,
1936
+ param: null,
1937
+ sequence_number: 0,
1938
+ type: "error"
1939
+ };
1940
+ return {
1941
+ event: errorEvent.type,
1942
+ data: JSON.stringify(errorEvent)
1943
+ };
1944
+ };
1945
+ const getErrorMessage = (error) => {
1946
+ if (error instanceof Error && error.message) return error.message;
1947
+ return String(error);
1948
+ };
1949
+ //#endregion
1950
+ //#region src/services/providers/provider-proxy.ts
1951
+ const SHARED_FORWARDABLE_HEADERS = ["accept", "user-agent"];
1952
+ const ANTHROPIC_FORWARDABLE_HEADERS = ["anthropic-version", "anthropic-beta"];
1953
+ const STRIPPED_RESPONSE_HEADERS = [
1954
+ "connection",
1955
+ "content-encoding",
1956
+ "content-length",
1957
+ "keep-alive",
1958
+ "proxy-authenticate",
1959
+ "proxy-authorization",
1960
+ "te",
1961
+ "trailer",
1962
+ "transfer-encoding",
1963
+ "upgrade"
1964
+ ];
1965
+ function buildProviderUpstreamHeaders(providerConfig, requestHeaders) {
1966
+ const authHeaders = {};
1967
+ if (providerConfig.authType === "x-api-key") authHeaders["x-api-key"] = providerConfig.apiKey;
1968
+ else authHeaders.authorization = `Bearer ${providerConfig.apiKey}`;
1969
+ const headers = {
1970
+ "content-type": "application/json",
1971
+ accept: "application/json",
1972
+ ...authHeaders
1973
+ };
1974
+ for (const headerName of SHARED_FORWARDABLE_HEADERS) {
1975
+ const headerValue = requestHeaders.get(headerName);
1976
+ if (headerValue) headers[headerName] = headerValue;
1977
+ }
1978
+ if (providerConfig.type !== "anthropic") return headers;
1979
+ for (const headerName of ANTHROPIC_FORWARDABLE_HEADERS) {
1980
+ const headerValue = requestHeaders.get(headerName);
1981
+ if (headerValue) headers[headerName] = headerValue;
1982
+ }
1983
+ return headers;
1984
+ }
1985
+ function createProviderProxyResponse(upstreamResponse, body) {
1986
+ const headers = new Headers(upstreamResponse.headers);
1987
+ for (const headerName of STRIPPED_RESPONSE_HEADERS) headers.delete(headerName);
1988
+ return new Response(body ?? upstreamResponse.body, {
1989
+ headers,
1990
+ status: upstreamResponse.status,
1991
+ statusText: upstreamResponse.statusText
1992
+ });
1993
+ }
1994
+ async function forwardProviderMessages(providerConfig, payload, requestHeaders) {
1995
+ consola.log(`<-- model: ${payload.model}`);
1996
+ return await fetch(`${providerConfig.baseUrl}/v1/messages`, {
1997
+ method: "POST",
1998
+ headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
1999
+ body: JSON.stringify(payload)
2000
+ });
2001
+ }
2002
+ async function forwardProviderChatCompletions(providerConfig, payload, requestHeaders) {
2003
+ consola.log(`<-- model: ${payload.model}`);
2004
+ return await fetch(`${providerConfig.baseUrl}/v1/chat/completions`, {
2005
+ method: "POST",
2006
+ headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
2007
+ body: JSON.stringify(payload)
2008
+ });
2009
+ }
2010
+ async function forwardProviderResponses(providerConfig, payload, requestHeaders) {
2011
+ consola.log(`<-- model: ${payload.model}`);
2012
+ return await fetch(`${providerConfig.baseUrl}/v1/responses`, {
2013
+ method: "POST",
2014
+ headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
2015
+ body: JSON.stringify(payload)
2016
+ });
2017
+ }
2018
+ async function forwardProviderModels(providerConfig, requestHeaders) {
2019
+ return await fetch(`${providerConfig.baseUrl}/v1/models`, {
2020
+ method: "GET",
2021
+ headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders)
2022
+ });
2023
+ }
2024
+ /** Align with Codex images: long-running generation/edits need a generous cap. */
2025
+ const PROVIDER_IMAGES_TIMEOUT_MS = 900 * 1e3;
2026
+ const providerImagesDispatcher = { dispatch(options, handler) {
2027
+ return getGlobalDispatcher().dispatch({
2028
+ ...options,
2029
+ bodyTimeout: PROVIDER_IMAGES_TIMEOUT_MS,
2030
+ headersTimeout: PROVIDER_IMAGES_TIMEOUT_MS
2031
+ }, handler);
2032
+ } };
2033
+ function resolveProviderRequestUrl(providerConfig, requestUrl, path) {
2034
+ const upstreamUrl = new URL(`${providerConfig.baseUrl}${path}`);
2035
+ upstreamUrl.search = new URL(requestUrl, "http://localhost").search;
2036
+ return upstreamUrl.toString();
2037
+ }
2038
+ async function forwardProviderAlphaSearch(providerConfig, request) {
2039
+ const headers = buildProviderUpstreamHeaders(providerConfig, request.headers);
2040
+ const body = await request.arrayBuffer();
2041
+ return await fetch(resolveProviderRequestUrl(providerConfig, request.url, "/v1/alpha/search"), {
2042
+ method: "POST",
2043
+ headers,
2044
+ body
2045
+ });
2046
+ }
2047
+ async function forwardProviderImages(providerConfig, request, operation) {
2048
+ const headers = buildProviderUpstreamHeaders(providerConfig, request.headers);
2049
+ const contentType = request.headers.get("content-type");
2050
+ if (contentType) headers["content-type"] = contentType;
2051
+ else if (operation === "edits") delete headers["content-type"];
2052
+ const init = {
2053
+ method: "POST",
2054
+ headers,
2055
+ body: request.body,
2056
+ duplex: "half",
2057
+ signal: AbortSignal.timeout(PROVIDER_IMAGES_TIMEOUT_MS)
2058
+ };
2059
+ const upstreamUrl = resolveProviderRequestUrl(providerConfig, request.url, `/v1/images/${operation}`);
2060
+ if (typeof Bun !== "undefined") return await fetch(upstreamUrl, init);
2061
+ return await fetch$1(upstreamUrl, {
2062
+ ...init,
2063
+ dispatcher: providerImagesDispatcher
2064
+ });
2065
+ }
2066
+ //#endregion
2067
+ //#region src/routes/alpha-search/alpha-search-responses.ts
2068
+ const logger$15 = createHandlerLogger("alpha-search-responses-handler");
2069
+ const SESSION_TTL_MS = 3600 * 1e3;
2070
+ const MAX_SESSIONS = 128;
2071
+ const MAX_URL_REFERENCES = 256;
2072
+ const MAX_SNAPSHOTS = 16;
2073
+ const MAX_SNAPSHOT_CHARACTERS = 2e4;
2074
+ const IMAGE_UNSUPPORTED = "Unsupported by GitHub Copilot web search: image_query. Do not retry this operation; use search_query for image-source pages.";
2075
+ const SCREENSHOT_UNSUPPORTED = "Unsupported by GitHub Copilot web search: screenshot. Do not retry this operation; open the PDF for text content.";
2076
+ const KNOWN_COMMANDS = new Set([
2077
+ "search_query",
2078
+ "image_query",
2079
+ "open",
2080
+ "click",
2081
+ "find",
2082
+ "screenshot",
2083
+ "finance",
2084
+ "weather",
2085
+ "sports",
2086
+ "time",
2087
+ "response_length"
2088
+ ]);
2089
+ const sessions = /* @__PURE__ */ new Map();
2090
+ const alphaSearchResponsesDependencies = {
2091
+ createResponses,
2092
+ findEndpointModel,
2093
+ now: () => Date.now(),
2094
+ resolveMappedModel,
2095
+ createUsageRecorder: (model, sessionId) => createCopilotTokenUsageRecorder({
2096
+ endpoint: "responses",
2097
+ fallbackSessionId: sessionId,
2098
+ model
2099
+ })
2100
+ };
2101
+ function reserveSession(id, now) {
2102
+ for (const [sessionId, session] of sessions) if (now - session.touchedAt >= SESSION_TTL_MS) sessions.delete(sessionId);
2103
+ let session = sessions.get(id);
2104
+ if (!session) {
2105
+ if (sessions.size >= MAX_SESSIONS) {
2106
+ const oldestSession = sessions.keys().next();
2107
+ if (!oldestSession.done) sessions.delete(oldestSession.value);
2108
+ }
2109
+ session = {
2110
+ nextTurn: 0,
2111
+ touchedAt: now,
2112
+ referencesById: /* @__PURE__ */ new Map(),
2113
+ referencesByUrl: /* @__PURE__ */ new Map(),
2114
+ snapshots: /* @__PURE__ */ new Map()
2115
+ };
2116
+ } else {
2117
+ sessions.delete(id);
2118
+ session.touchedAt = now;
2119
+ }
2120
+ sessions.set(id, session);
2121
+ const turn = session.nextTurn;
2122
+ session.nextTurn += 1;
2123
+ return {
2124
+ session,
2125
+ turn
2126
+ };
2127
+ }
2128
+ function parseHttpUrl(value) {
2129
+ try {
2130
+ const url = new URL(value);
2131
+ return url.protocol === "http:" || url.protocol === "https:" ? url.toString() : null;
2132
+ } catch {
2133
+ return null;
2134
+ }
2135
+ }
2136
+ function extractMarkdownSources(text) {
2137
+ return Array.from(text.matchAll(/(?<!!)\[([^\]\n]+)\]\((https?:\/\/(?:[^\s<>()]|\([^\s<>()]*\))+)\)/gu), ([, title, url]) => ({
2138
+ title,
2139
+ url
2140
+ }));
2141
+ }
2142
+ function addUrlReference(session, source, turn) {
2143
+ const url = parseHttpUrl(source.url);
2144
+ if (!url) return null;
2145
+ const existing = session.referencesByUrl.get(url);
2146
+ if (existing) {
2147
+ if (existing.result.title === existing.result.url && source.title) existing.result.title = source.title;
2148
+ if (source.snippet) existing.result.snippet = source.snippet;
2149
+ return existing;
2150
+ }
2151
+ if (session.referencesById.size >= MAX_URL_REFERENCES) {
2152
+ const oldest = session.referencesById.entries().next().value;
2153
+ if (oldest) {
2154
+ const [refId, reference] = oldest;
2155
+ session.referencesById.delete(refId);
2156
+ session.referencesByUrl.delete(reference.result.url);
2157
+ }
2158
+ }
2159
+ const refId = `turn${turn.number}search${turn.nextReferenceIndex}`;
2160
+ turn.nextReferenceIndex += 1;
2161
+ const reference = { result: {
2162
+ type: "text_result",
2163
+ domain: new URL(url).hostname,
2164
+ ref_id: refId,
2165
+ snippet: source.snippet?.trim() || source.title || url,
2166
+ title: source.title || url,
2167
+ url
2168
+ } };
2169
+ session.referencesById.set(refId, reference);
2170
+ session.referencesByUrl.set(url, reference);
2171
+ return reference;
2172
+ }
2173
+ function resolveUrlReference(session, refId, turn) {
2174
+ const stored = session.referencesById.get(refId);
2175
+ if (stored) return stored;
2176
+ const url = parseHttpUrl(refId);
2177
+ return url ? addUrlReference(session, {
2178
+ title: url,
2179
+ url
2180
+ }, turn) : null;
2181
+ }
2182
+ function getSnapshot(session, refId) {
2183
+ const snapshot = session.snapshots.get(refId);
2184
+ if (!snapshot) return null;
2185
+ session.snapshots.delete(refId);
2186
+ session.snapshots.set(refId, snapshot);
2187
+ return snapshot;
2188
+ }
2189
+ function findSnapshotByUrl(session, url) {
2190
+ for (const snapshot of session.snapshots.values()) if (snapshot.source.result.url === url) return getSnapshot(session, snapshot.refId);
2191
+ return null;
2192
+ }
2193
+ function addSnapshot(session, source, turn, index, text, links) {
2194
+ const existing = findSnapshotByUrl(session, source.result.url);
2195
+ if (existing) {
2196
+ existing.text = text.slice(0, MAX_SNAPSHOT_CHARACTERS);
2197
+ existing.links = links;
2198
+ return existing;
2199
+ }
2200
+ if (session.snapshots.size >= MAX_SNAPSHOTS) {
2201
+ const oldestRefId = session.snapshots.keys().next().value;
2202
+ if (oldestRefId) session.snapshots.delete(oldestRefId);
2203
+ }
2204
+ const snapshot = {
2205
+ refId: `turn${turn}view${index}`,
2206
+ source,
2207
+ text: text.slice(0, MAX_SNAPSHOT_CHARACTERS),
2208
+ links
2209
+ };
2210
+ session.snapshots.set(snapshot.refId, snapshot);
2211
+ return snapshot;
2212
+ }
2213
+ function renderSnapshot(snapshot, lineno) {
2214
+ const lines = snapshot.text.split("\n");
2215
+ const start = lineno === void 0 ? 0 : Math.max(0, Math.min(lineno, lines.length - 1) - 10);
2216
+ const end = lineno === void 0 ? lines.length : Math.min(lines.length, start + 21);
2217
+ const numbered = lines.slice(start, end).map((line, index) => `L${start + index}: ${line}`).join("\n");
2218
+ const links = snapshot.links.map((link, index) => `[${index}] ${link.result.title} — ${link.result.url} (${link.result.ref_id})`).join("\n");
2219
+ return [
2220
+ `Open ${snapshot.refId} (${snapshot.source.result.url})`,
2221
+ numbered,
2222
+ links ? `Links:\n${links}` : ""
2223
+ ].filter(Boolean).join("\n");
2224
+ }
2225
+ function findInSnapshot(snapshot, pattern) {
2226
+ const normalizedPattern = pattern.toLowerCase();
2227
+ const matches = snapshot.text.split("\n").map((line, index) => ({
2228
+ line,
2229
+ index
2230
+ })).filter(({ line }) => line.toLowerCase().includes(normalizedPattern)).slice(0, 20);
2231
+ if (matches.length === 0) return null;
2232
+ return [`Find results for ${JSON.stringify(pattern)} in ${snapshot.refId}:`, ...matches.map(({ line, index }) => `L${index}: ${line}`)].join("\n");
2233
+ }
2234
+ function formatTime(utcOffset, now) {
2235
+ const offsetMinutes = (utcOffset.startsWith("+") ? 1 : -1) * (Number.parseInt(utcOffset.slice(1, 3), 10) * 60 + Number.parseInt(utcOffset.slice(4, 6), 10));
2236
+ return `Time at UTC${utcOffset}: ${new Date(now + offsetMinutes * 6e4).toISOString().slice(0, 19).replace("T", " ")}`;
2237
+ }
2238
+ function buildInstruction(operations, responseLength) {
2239
+ return [
2240
+ "The Operations JSON below is the complete and exclusive request for this response.",
2241
+ "Use web_search to execute every listed operation and only those operations. Do not validate, infer, or mention operations absent from the JSON; the adapter already handled them.",
2242
+ "Return only grounded results and citations for these operations. Do not discuss unrelated tasks or conversations.",
2243
+ "For search operations, honor every query, recency, domain, market, date, league, team, and locale constraint.",
2244
+ "For open operations, open the exact URL and return grounded page text plus cited links.",
2245
+ "For find operations, find the exact pattern in the specified URL and return matching context.",
2246
+ `Requested response length: ${responseLength ?? "medium"}.`,
2247
+ `Operations:\n${JSON.stringify(operations, null, 2)}`
2248
+ ].join("\n");
2249
+ }
2250
+ function unavailableReference(refId) {
2251
+ return `Reference ${JSON.stringify(refId)} is unavailable or expired. Search or open the URL again.`;
2252
+ }
2253
+ function invalidRequest$1(c, message) {
2254
+ return c.json({ error: {
2255
+ message,
2256
+ type: "invalid_request_error"
2257
+ } }, 400);
2258
+ }
2259
+ function createSearchOperationState(session, turn) {
2260
+ return {
2261
+ session,
2262
+ turn,
2263
+ output: [],
2264
+ warnings: [],
2265
+ resultReferences: /* @__PURE__ */ new Map(),
2266
+ remoteOperations: [],
2267
+ remotePages: []
2268
+ };
2269
+ }
2270
+ function includeResult(state, reference) {
2271
+ state.resultReferences.set(reference.result.url, reference);
2272
+ }
2273
+ function queuePage(state, kind, source, options = {}) {
2274
+ state.remoteOperations.push({
2275
+ operation: kind,
2276
+ url: source.result.url,
2277
+ ...options.lineno === void 0 ? {} : { lineno: options.lineno },
2278
+ ...options.pattern === void 0 ? {} : { pattern: options.pattern }
2279
+ });
2280
+ state.remotePages.push({
2281
+ kind,
2282
+ source,
2283
+ ...options
2284
+ });
2285
+ includeResult(state, source);
2286
+ }
2287
+ function processSearchCommands(commands, state) {
2288
+ for (const command of commands.search_query ?? []) state.remoteOperations.push({
2289
+ ...command,
2290
+ operation: "search_query"
2291
+ });
2292
+ }
2293
+ function processOpenCommands(commands, state) {
2294
+ for (const command of commands.open ?? []) {
2295
+ const directSnapshot = getSnapshot(state.session, command.ref_id);
2296
+ if (directSnapshot) {
2297
+ state.output.push(renderSnapshot(directSnapshot, command.lineno));
2298
+ includeResult(state, directSnapshot.source);
2299
+ continue;
2300
+ }
2301
+ const source = resolveUrlReference(state.session, command.ref_id, state.turn);
2302
+ if (!source) {
2303
+ state.output.push(unavailableReference(command.ref_id));
2304
+ continue;
2305
+ }
2306
+ const cached = findSnapshotByUrl(state.session, source.result.url);
2307
+ if (cached) {
2308
+ state.output.push(renderSnapshot(cached, command.lineno));
2309
+ includeResult(state, source);
2310
+ continue;
2311
+ }
2312
+ queuePage(state, "open", source, { lineno: command.lineno });
2313
+ }
2314
+ }
2315
+ function processClickCommands(commands, state) {
2316
+ for (const command of commands.click ?? []) {
2317
+ const source = getSnapshot(state.session, command.ref_id)?.links[command.id];
2318
+ if (!source) {
2319
+ state.output.push(unavailableReference(`${command.ref_id} link ${command.id}`));
2320
+ continue;
2321
+ }
2322
+ const cached = findSnapshotByUrl(state.session, source.result.url);
2323
+ if (cached) {
2324
+ state.output.push(renderSnapshot(cached));
2325
+ includeResult(state, source);
2326
+ continue;
2327
+ }
2328
+ queuePage(state, "open", source);
2329
+ }
2330
+ }
2331
+ function processFindCommands(commands, state) {
2332
+ for (const command of commands.find ?? []) {
2333
+ const directSnapshot = getSnapshot(state.session, command.ref_id);
2334
+ const source = directSnapshot?.source ?? resolveUrlReference(state.session, command.ref_id, state.turn);
2335
+ if (!source) {
2336
+ state.output.push(unavailableReference(command.ref_id));
2337
+ continue;
2338
+ }
2339
+ const snapshot = directSnapshot ?? findSnapshotByUrl(state.session, source.result.url);
2340
+ const localResult = snapshot ? findInSnapshot(snapshot, command.pattern) : null;
2341
+ if (localResult) {
2342
+ state.output.push(localResult);
2343
+ includeResult(state, source);
2344
+ continue;
2345
+ }
2346
+ queuePage(state, "find", source, { pattern: command.pattern });
2347
+ }
2348
+ }
2349
+ function processStructuredCommands(commands, state, now) {
2350
+ for (const command of commands.finance ?? []) state.remoteOperations.push({
2351
+ ...command,
2352
+ operation: "finance"
2353
+ });
2354
+ for (const command of commands.weather ?? []) state.remoteOperations.push({
2355
+ ...command,
2356
+ operation: "weather"
2357
+ });
2358
+ for (const command of commands.sports ?? []) state.remoteOperations.push({
2359
+ ...command,
2360
+ operation: "sports"
2361
+ });
2362
+ for (const command of commands.time ?? []) state.output.push(formatTime(command.utc_offset, now));
2363
+ }
2364
+ function processUnsupportedCommands(commands, state) {
2365
+ for (const commandName of Object.keys(commands)) if (!KNOWN_COMMANDS.has(commandName)) state.warnings.push(`Unsupported by GitHub Copilot web search: ${commandName}. Do not retry this operation.`);
2366
+ }
2367
+ function processCommandOperations(commands, state, now) {
2368
+ processSearchCommands(commands, state);
2369
+ if ((commands.image_query?.length ?? 0) > 0) state.warnings.push(IMAGE_UNSUPPORTED);
2370
+ processOpenCommands(commands, state);
2371
+ processClickCommands(commands, state);
2372
+ processFindCommands(commands, state);
2373
+ if ((commands.screenshot?.length ?? 0) > 0) state.warnings.push(SCREENSHOT_UNSUPPORTED);
2374
+ processStructuredCommands(commands, state, now);
2375
+ processUnsupportedCommands(commands, state);
2376
+ }
2377
+ function supportsLiveAccess(request) {
2378
+ const externalWebAccess = request.settings?.external_web_access;
2379
+ return externalWebAccess === void 0 || externalWebAccess === true || externalWebAccess === "live";
2380
+ }
2381
+ function addLiveAccessWarning(request, state) {
2382
+ if (state.remoteOperations.length === 0 || supportsLiveAccess(request)) return;
2383
+ state.warnings.push(`GitHub Copilot alpha search supports live retrieval only; external_web_access=${JSON.stringify(request.settings?.external_web_access)} is unsupported. Do not retry this request in this mode.`);
2384
+ }
2385
+ function shouldExecuteRemoteOperations(request, state) {
2386
+ return state.remoteOperations.length > 0 && supportsLiveAccess(request);
2387
+ }
2388
+ async function resolveRemoteModel(c, request, provider) {
2389
+ const model = provider ? request.model : alphaSearchResponsesDependencies.resolveMappedModel(request.model);
2390
+ if (provider) {
2391
+ const providerConfig = await resolveProviderConfig(provider);
2392
+ if (!providerConfig) return c.json({ error: {
2393
+ message: `Provider '${provider}' not found or disabled`,
2394
+ type: "invalid_request_error"
2395
+ } }, 404);
2396
+ if (resolveEffectiveProviderType(providerConfig, model) !== "openai-responses") return invalidRequest$1(c, `Provider '${provider}' does not support the /v1/responses endpoint required for alpha search`);
2397
+ return {
2398
+ model,
2399
+ providerConfig
2400
+ };
2401
+ }
2402
+ if (!alphaSearchResponsesDependencies.findEndpointModel(model)?.supported_endpoints?.includes("/responses")) return invalidRequest$1(c, `Model '${model}' does not support the Copilot Responses endpoint required for alpha search`);
2403
+ return { model };
2404
+ }
2405
+ function createRemoteResponsesPayload(request, model, instruction) {
2406
+ return {
2407
+ model,
2408
+ input: instruction,
2409
+ tools: [buildResponsesWebSearchTool({
2410
+ allowedDomains: request.settings?.filters?.allowed_domains,
2411
+ blockedDomains: request.settings?.filters?.blocked_domains,
2412
+ userLocation: request.settings?.user_location,
2413
+ searchContextSize: request.settings?.search_context_size
2414
+ })],
2415
+ tool_choice: "required",
2416
+ store: false,
2417
+ stream: false,
2418
+ include: ["web_search_call.action.sources"],
2419
+ reasoning: request.reasoning,
2420
+ max_output_tokens: request.max_output_tokens
2421
+ };
2422
+ }
2423
+ async function requestProviderSearch(c, providerConfig, payload, model, sessionId) {
2424
+ debugJson(logger$15, "Alpha search provider Responses request:", {
2425
+ payload,
2426
+ provider: providerConfig.name
2427
+ });
2428
+ const upstreamResponse = await forwardProviderResponses(providerConfig, payload, c.req.raw.headers);
2429
+ if (!upstreamResponse.ok) throw new HTTPError(`Failed to create ${providerConfig.name} responses for alpha search`, upstreamResponse);
2430
+ const result = await upstreamResponse.json();
2431
+ debugJson(logger$15, "Alpha search provider Responses result:", {
2432
+ provider: providerConfig.name,
2433
+ result
2434
+ });
2435
+ createProviderTokenUsageRecorder({
2436
+ endpoint: "responses",
2437
+ fallbackSessionId: sessionId,
2438
+ model,
2439
+ pricing: providerConfig.models?.[model]?.pricing,
2440
+ pricingCurrency: providerConfig.pricingCurrency,
2441
+ providerName: providerConfig.name,
2442
+ sessionId
2443
+ })(normalizeResponsesUsage(result.usage));
2444
+ return result;
2445
+ }
2446
+ async function requestCopilotSearch(payload, model, requestId, sessionId) {
2447
+ debugJson(logger$15, "Alpha search Copilot Responses request:", payload);
2448
+ const result = await alphaSearchResponsesDependencies.createResponses(payload, {
2449
+ vision: false,
2450
+ initiator: "agent",
2451
+ transport: "http",
2452
+ requestId,
2453
+ sessionId
2454
+ });
2455
+ debugJson(logger$15, "Alpha search Copilot Responses result:", result);
2456
+ alphaSearchResponsesDependencies.createUsageRecorder(model, sessionId)({
2457
+ ...normalizeResponsesUsage(result.usage),
2458
+ total_nano_aiu: normalizeOptionalToken(result.copilot_usage?.total_nano_aiu)
2459
+ });
2460
+ return result;
2461
+ }
2462
+ async function requestRemoteSearch(c, request, state, target) {
2463
+ const instruction = buildInstruction(state.remoteOperations, request.commands?.response_length);
2464
+ const sessionId = getUUID(request.id);
2465
+ const requestId = generateRequestIdFromPayload({ messages: `${state.turn.number}:${instruction}` }, sessionId);
2466
+ const payload = createRemoteResponsesPayload(request, target.model, instruction);
2467
+ if (target.providerConfig) return await requestProviderSearch(c, target.providerConfig, payload, target.model, sessionId);
2468
+ return await requestCopilotSearch(payload, target.model, requestId, sessionId);
2469
+ }
2470
+ function getActiveRemoteReferences(session, references) {
2471
+ return references.filter((reference) => session.referencesById.get(reference.result.ref_id) === reference);
2472
+ }
2473
+ function buildSnapshotLinks(state, target, markdownReferences, remoteReferences) {
2474
+ return [...new Map([...markdownReferences, ...getActiveRemoteReferences(state.session, remoteReferences)].filter((reference) => reference.result.url !== target.result.url && state.session.referencesById.get(reference.result.ref_id) === reference).map((reference) => [reference.result.url, reference])).values()];
2475
+ }
2476
+ function processRemotePages(state, answerText, markdownReferences, remoteReferences) {
2477
+ for (const [index, page] of state.remotePages.entries()) {
2478
+ const target = addUrlReference(state.session, {
2479
+ url: page.source.result.url,
2480
+ title: page.source.result.title,
2481
+ snippet: answerText
2482
+ }, state.turn) ?? page.source;
2483
+ includeResult(state, target);
2484
+ const snapshotLinks = buildSnapshotLinks(state, target, markdownReferences, remoteReferences);
2485
+ if (page.kind === "find") {
2486
+ state.output.push(`Find results for ${JSON.stringify(page.pattern ?? "")} in ${target.result.url}:\n${answerText}`);
2487
+ if (!findSnapshotByUrl(state.session, target.result.url)) addSnapshot(state.session, target, state.turn.number, index, answerText, snapshotLinks);
2488
+ continue;
2489
+ }
2490
+ const snapshot = addSnapshot(state.session, target, state.turn.number, index, answerText, snapshotLinks);
2491
+ state.output.push(renderSnapshot(snapshot, page.lineno));
2492
+ }
2493
+ }
2494
+ function appendActiveSources(state, references) {
2495
+ if (references.length === 0) return;
2496
+ state.output.push(["Sources:", ...references.map((reference) => `- [${reference.result.ref_id}] ${reference.result.title} — ${reference.result.url}`)].join("\n"));
2497
+ }
2498
+ function processRemoteResult(result, state) {
2499
+ const extracted = extractWebSearchResult(result);
2500
+ const citedSources = extracted.sources.filter((source) => source.snippet !== void 0);
2501
+ const relevantSources = citedSources.length > 0 ? citedSources : extracted.sources;
2502
+ consola.log(`--> web search: operations=${[...new Set(state.remoteOperations.map((remoteOperation) => remoteOperation.operation))].join(",")} queries=${JSON.stringify(extracted.queries)} sources=${relevantSources.length}`);
2503
+ const remoteReferences = relevantSources.map((source) => addUrlReference(state.session, source, state.turn)).filter((reference) => Boolean(reference));
2504
+ for (const reference of getActiveRemoteReferences(state.session, remoteReferences)) includeResult(state, reference);
2505
+ const answerText = extracted.answerText || "GitHub Copilot web search returned no text.";
2506
+ const markdownReferences = state.remotePages.length === 0 ? [] : extractMarkdownSources(answerText).map((source) => addUrlReference(state.session, source, state.turn)).filter((reference) => Boolean(reference));
2507
+ for (const reference of markdownReferences) includeResult(state, reference);
2508
+ if (state.remotePages.length === 0) state.output.push(answerText);
2509
+ processRemotePages(state, answerText, markdownReferences, remoteReferences);
2510
+ appendActiveSources(state, getActiveRemoteReferences(state.session, remoteReferences));
2511
+ }
2512
+ async function executeRemoteOperations(c, request, state, provider) {
2513
+ const target = await resolveRemoteModel(c, request, provider);
2514
+ if (target instanceof Response) return target;
2515
+ processRemoteResult(await requestRemoteSearch(c, request, state, target), state);
2516
+ return null;
2517
+ }
2518
+ function buildAlphaSearchResponse(state) {
2519
+ state.output.push(...state.warnings);
2520
+ if (state.output.length === 0) state.output.push("No supported search operations were requested.");
2521
+ return {
2522
+ encrypted_output: null,
2523
+ output: state.output.join("\n\n"),
2524
+ results: [...state.resultReferences.values()].filter((reference) => state.session.referencesById.get(reference.result.ref_id) === reference).map(({ result }) => result)
2525
+ };
2526
+ }
2527
+ async function handleAlphaSearchResponses(c, options) {
2528
+ const parsed = alphaSearchRequestSchema.safeParse(options.request);
2529
+ if (!parsed.success) {
2530
+ const issue = parsed.error.issues[0];
2531
+ return invalidRequest$1(c, `Invalid alpha search request at ${issue?.path.join(".") || "body"}: ${issue?.message ?? "invalid value"}`);
2532
+ }
2533
+ const request = parsed.data;
2534
+ const now = alphaSearchResponsesDependencies.now();
2535
+ const reservation = reserveSession(request.id, now);
2536
+ const session = reservation.session;
2537
+ const state = createSearchOperationState(session, {
2538
+ number: reservation.turn,
2539
+ nextReferenceIndex: 0
2540
+ });
2541
+ processCommandOperations(request.commands ?? {}, state, now);
2542
+ addLiveAccessWarning(request, state);
2543
+ if (shouldExecuteRemoteOperations(request, state)) {
2544
+ const remoteResponse = await executeRemoteOperations(c, request, state, options.provider);
2545
+ if (remoteResponse) return remoteResponse;
2546
+ }
2547
+ return c.json(buildAlphaSearchResponse(state));
2548
+ }
2549
+ //#endregion
2550
+ //#region src/services/codex/alpha-search.ts
2551
+ const CODEX_ALPHA_SEARCH_URL = `${CODEX_API_BASE_URL}/codex/alpha/search`;
2552
+ function resolveCodexAlphaSearchUrl(requestUrl) {
2553
+ const upstreamUrl = new URL(CODEX_ALPHA_SEARCH_URL);
2554
+ upstreamUrl.search = new URL(requestUrl, "http://localhost").search;
2555
+ return upstreamUrl.toString();
2556
+ }
2557
+ async function forwardCodexAlphaSearch(request) {
2558
+ const headers = buildCodexRequestHeaders(request.headers);
2559
+ if (!headers.has("accept")) headers.set("accept", "application/json");
2560
+ const body = await request.arrayBuffer();
2561
+ if (!headers.has("content-type")) headers.set("content-type", "application/json");
2562
+ return await fetch(resolveCodexAlphaSearchUrl(request.url), {
2563
+ method: "POST",
2564
+ headers,
2565
+ body
2566
+ });
2567
+ }
2568
+ //#endregion
2569
+ //#region src/routes/alpha-search/route.ts
2570
+ const logger$14 = createHandlerLogger("alpha-search-handler");
2571
+ const alphaSearchRoutes = new Hono();
2572
+ function parseDebugBody(body) {
2573
+ try {
2574
+ return JSON.parse(body);
2575
+ } catch {
2576
+ return body;
2577
+ }
2578
+ }
2579
+ async function forwardCodexAlphaSearchRequest(request) {
2580
+ await debugJsonAsync(logger$14, "alpha_search.codex.request", async () => ({ body: parseDebugBody(await request.clone().text()) }));
2581
+ const upstreamResponse = await forwardCodexAlphaSearch(request);
2582
+ await debugJsonAsync(logger$14, "alpha_search.codex.response", async () => ({
2583
+ body: parseDebugBody(await upstreamResponse.clone().text()),
2584
+ statusCode: upstreamResponse.status
2585
+ }));
2586
+ return createProviderProxyResponse(upstreamResponse);
2587
+ }
2588
+ function createAlphaSearchRequest(request, payload) {
2589
+ return new Request(request, {
2590
+ body: JSON.stringify(payload),
2591
+ method: "post"
2592
+ });
2593
+ }
2594
+ async function handleCodexRequest(c, request, resolvedProviderConfig) {
2595
+ if (!(resolvedProviderConfig ?? await resolveProviderConfig("codex"))) return c.json({ error: {
2596
+ message: "Provider 'codex' not found or disabled",
2597
+ type: "invalid_request_error"
2598
+ } }, 404);
2599
+ return await forwardCodexAlphaSearchRequest(request);
2600
+ }
2601
+ function invalidRequest(c, message) {
2602
+ return c.json({ error: {
2603
+ message,
2604
+ type: "invalid_request_error"
2605
+ } }, 400);
2606
+ }
2607
+ async function parseAlphaSearchBody(c) {
2608
+ let body;
2609
+ try {
2610
+ body = await c.req.raw.clone().json();
2611
+ } catch {
2612
+ return invalidRequest(c, "Invalid alpha search request: expected JSON body");
2613
+ }
2614
+ if (typeof body?.model !== "string") return invalidRequest(c, "Invalid alpha search request: model must be a string");
2615
+ return body;
2616
+ }
2617
+ /**
2618
+ * Handles top-level alpha-search dispatch. Pass `resolvedProviderConfig` when
2619
+ * the provider-scoped route has already resolved Codex. Codex is preferred
2620
+ * for every top-level request while alphaSearchCodexPriority stays enabled.
2621
+ */
2622
+ async function handleAlphaSearchRequest(c, resolvedProviderConfig) {
2623
+ if (resolvedProviderConfig) return await handleCodexRequest(c, c.req.raw, resolvedProviderConfig);
2624
+ const payload = await parseAlphaSearchBody(c);
2625
+ if (payload instanceof Response) return payload;
2626
+ const requestedModel = payload.model;
2627
+ payload.model = resolveMappedModel(requestedModel);
2628
+ if (payload.model !== requestedModel) consola.debug(`Resolved model mapping: ${requestedModel} -> ${payload.model}`);
2629
+ const providerModelAlias = parseProviderModelAlias(payload.model);
2630
+ if (providerModelAlias) {
2631
+ payload.model = providerModelAlias.model;
2632
+ if (providerModelAlias.provider === "codex") return await handleCodexRequest(c, createAlphaSearchRequest(c.req.raw, payload));
2633
+ }
2634
+ if (isAlphaSearchCodexPriorityEnabled()) {
2635
+ if (await resolveProviderConfig("codex")) return await forwardCodexAlphaSearchRequest(createAlphaSearchRequest(c.req.raw, payload));
2636
+ }
2637
+ if (providerModelAlias) return await handleAlphaSearchResponses(c, {
2638
+ provider: providerModelAlias.provider,
2639
+ request: payload
2640
+ });
2641
+ return await handleAlphaSearchResponses(c, { request: {
2642
+ ...payload,
2643
+ model: requestedModel
2644
+ } });
2645
+ }
2646
+ alphaSearchRoutes.post("/", async (c) => {
2647
+ try {
2648
+ return await handleAlphaSearchRequest(c);
2649
+ } catch (error) {
2650
+ logger$14.error("alpha_search.error", { error });
2651
+ return await forwardError(c, error);
2652
+ }
2653
+ });
2654
+ //#endregion
2655
+ //#region src/lib/dashscope.ts
2656
+ const OPENAI_COMPATIBLE_CONTEXT_CACHE_MARKER_LIMIT = 4;
2657
+ const OPENAI_COMPATIBLE_CONTEXT_CACHE_CONTROL = { type: "ephemeral" };
2658
+ const OPENAI_COMPATIBLE_CONTEXT_CACHE_ROLES = new Set([
2659
+ "system",
2660
+ "user",
2661
+ "assistant",
2662
+ "tool"
2663
+ ]);
2664
+ const isDashScopeAliyunProvider = (providerConfig) => providerConfig.name === "dashscope" || providerConfig.baseUrl.includes("aliyuncs.com");
2665
+ const applyDashScopePreserveThinkingDefault = (payload, providerConfig) => {
2666
+ if (!isDashScopeAliyunProvider(providerConfig)) return;
2667
+ if (!Object.hasOwn(payload, "preserve_thinking")) payload.preserve_thinking = true;
2668
+ };
2669
+ const applyOpenAICompatibleContextCache = (payload) => {
2670
+ const messageIndexes = selectContextCacheMessageIndexes(payload.messages);
2671
+ for (const messageIndex of messageIndexes) applyContextCacheControl(payload.messages[messageIndex]);
2672
+ };
1692
2673
  const selectContextCacheMessageIndexes = (messages) => {
1693
2674
  const cacheableIndexes = messages.flatMap((message, index) => isContextCacheMarkerEligible(message) ? [index] : []);
1694
2675
  const systemIndexes = cacheableIndexes.filter((index) => messages[index]?.role === "system").slice(0, 2);
@@ -1767,123 +2748,50 @@ const applyMissingExtraBody$1 = (payload, options) => {
1767
2748
  for (const [key, value] of Object.entries(options.extraBody ?? {})) if (!Object.hasOwn(payload, key)) payload[key] = value;
1768
2749
  };
1769
2750
  const applyProviderStreamOptions = (payload) => {
1770
- if (!payload.stream) return;
1771
- payload.stream_options = {
1772
- ...payload.stream_options ?? {},
1773
- include_usage: true
1774
- };
1775
- };
1776
- const applyProviderContextCache = (payload, modelConfig, providerConfig) => {
1777
- const isDashScopeProvider = isDashScopeAliyunProvider(providerConfig);
1778
- if (modelConfig?.contextCache ?? isDashScopeProvider) applyOpenAICompatibleContextCache(payload);
1779
- };
1780
- const createProviderChatCompletionsUsageRecorder = (payload, provider, modelConfig, pricingCurrency) => createProviderTokenUsageRecorder({
1781
- endpoint: "chat_completions",
1782
- model: payload.model,
1783
- pricing: modelConfig?.pricing,
1784
- pricingCurrency,
1785
- providerName: provider
1786
- });
1787
- const streamProviderChatCompletions = (c, upstreamResponse, options) => {
1788
- logger$13.debug("provider.chat_completions.streaming", { provider: options.provider });
1789
- return streamSSE(c, async (stream) => {
1790
- let usage = {};
1791
- try {
1792
- for await (const chunk of events(upstreamResponse)) {
1793
- debugJson(logger$13, "provider.chat_completions.stream_chunk", chunk);
1794
- if (chunk.data && chunk.data !== "[DONE]") {
1795
- const parsedChunk = parseChatCompletionChunkData(chunk.data);
1796
- if (parsedChunk?.usage) usage = normalizeOpenAIUsage(parsedChunk.usage);
1797
- }
1798
- await stream.writeSSE({
1799
- event: chunk.event,
1800
- data: chunk.data ?? ""
1801
- });
1802
- }
1803
- } finally {
1804
- options.recordUsage(usage);
1805
- }
1806
- });
1807
- };
1808
- const parseChatCompletionChunkData = (data) => {
1809
- try {
1810
- return JSON.parse(data);
1811
- } catch {
1812
- return null;
1813
- }
1814
- };
1815
- //#endregion
1816
- //#region src/lib/copilot-rate-limit.ts
1817
- const copilotRateLimitTypes = ["session", "weekly"];
1818
- const copilotRateLimitHeaders = {
1819
- session: "x-usage-ratelimit-session",
1820
- weekly: "x-usage-ratelimit-weekly"
1821
- };
1822
- const copilotQuotaSnapshotKeys = {
1823
- session: "5Hour-Session-RateLimits",
1824
- weekly: "Weekly-Session-RateLimits"
1825
- };
1826
- const hasGetMethod = (headers) => {
1827
- return "get" in headers && typeof headers.get === "function";
1828
- };
1829
- const getHeaderValue$1 = (headers, headerName) => {
1830
- if (hasGetMethod(headers)) return headers.get(headerName);
1831
- const normalizedHeaderName = headerName.toLowerCase();
1832
- return Object.entries(headers).find(([key]) => key.toLowerCase() === normalizedHeaderName)?.[1] ?? null;
1833
- };
1834
- const parseCopilotRateLimitHeader = (headerValue) => {
1835
- const params = new URLSearchParams(headerValue);
1836
- const remaining = params.get("rem");
1837
- const resetAt = params.get("rst");
1838
- if (!remaining || !resetAt) return null;
1839
- return {
1840
- remaining,
1841
- resetAt
1842
- };
1843
- };
1844
- const getCopilotRateLimitUsage = (headers, type) => {
1845
- const headerName = copilotRateLimitHeaders[type];
1846
- const headerValue = getHeaderValue$1(headers, headerName);
1847
- if (!headerValue) return null;
1848
- const parsed = parseCopilotRateLimitHeader(headerValue);
1849
- if (!parsed) return null;
1850
- return {
1851
- type,
1852
- ...parsed
1853
- };
1854
- };
1855
- const getCopilotRateLimitUsageFromSnapshots = (snapshots, type) => {
1856
- const snapshot = snapshots?.[copilotQuotaSnapshotKeys[type]];
1857
- if (!isCopilotQuotaSnapshot(snapshot)) return null;
1858
- return {
1859
- remaining: String(snapshot.percent_remaining),
1860
- resetAt: snapshot.reset_date,
1861
- type
2751
+ if (!payload.stream) return;
2752
+ payload.stream_options = {
2753
+ ...payload.stream_options ?? {},
2754
+ include_usage: true
1862
2755
  };
1863
2756
  };
1864
- const logCopilotRateLimits = (headers) => {
1865
- for (const type of copilotRateLimitTypes) {
1866
- const usage = getCopilotRateLimitUsage(headers, type);
1867
- if (!usage) continue;
1868
- logCopilotRateLimitUsage(usage);
1869
- }
1870
- };
1871
- const logCopilotQuotaSnapshots = (snapshots) => {
1872
- for (const type of copilotRateLimitTypes) {
1873
- const usage = getCopilotRateLimitUsageFromSnapshots(snapshots, type);
1874
- if (!usage) continue;
1875
- logCopilotRateLimitUsage(usage);
1876
- }
2757
+ const applyProviderContextCache = (payload, modelConfig, providerConfig) => {
2758
+ const isDashScopeProvider = isDashScopeAliyunProvider(providerConfig);
2759
+ if (modelConfig?.contextCache ?? isDashScopeProvider) applyOpenAICompatibleContextCache(payload);
1877
2760
  };
1878
- const logCopilotRateLimitUsage = (usage) => {
1879
- const d = new Date(usage.resetAt);
1880
- const dateStr = Number.isNaN(d.getTime()) ? usage.resetAt : d.toLocaleString();
1881
- consola.log(`Copilot ${usage.type} quota remaining: ${usage.remaining}, resets at: ${dateStr}`);
2761
+ const createProviderChatCompletionsUsageRecorder = (payload, provider, modelConfig, pricingCurrency) => createProviderTokenUsageRecorder({
2762
+ endpoint: "chat_completions",
2763
+ model: payload.model,
2764
+ pricing: modelConfig?.pricing,
2765
+ pricingCurrency,
2766
+ providerName: provider
2767
+ });
2768
+ const streamProviderChatCompletions = (c, upstreamResponse, options) => {
2769
+ logger$13.debug("provider.chat_completions.streaming", { provider: options.provider });
2770
+ return streamSSE(c, async (stream) => {
2771
+ let usage = {};
2772
+ try {
2773
+ for await (const chunk of events(upstreamResponse)) {
2774
+ debugJson(logger$13, "provider.chat_completions.stream_chunk", chunk);
2775
+ if (chunk.data && chunk.data !== "[DONE]") {
2776
+ const parsedChunk = parseChatCompletionChunkData(chunk.data);
2777
+ if (parsedChunk?.usage) usage = normalizeOpenAIUsage(parsedChunk.usage);
2778
+ }
2779
+ await stream.writeSSE({
2780
+ event: chunk.event,
2781
+ data: chunk.data ?? ""
2782
+ });
2783
+ }
2784
+ } finally {
2785
+ options.recordUsage(usage);
2786
+ }
2787
+ });
1882
2788
  };
1883
- const isCopilotQuotaSnapshot = (value) => {
1884
- if (!value || typeof value !== "object") return false;
1885
- const record = value;
1886
- 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";
2789
+ const parseChatCompletionChunkData = (data) => {
2790
+ try {
2791
+ return JSON.parse(data);
2792
+ } catch {
2793
+ return null;
2794
+ }
1887
2795
  };
1888
2796
  //#endregion
1889
2797
  //#region src/services/copilot/create-chat-completions.ts
@@ -3579,143 +4487,6 @@ function closeThinkingBlockIfOpen(state, events) {
3579
4487
  }
3580
4488
  }
3581
4489
  //#endregion
3582
- //#region src/services/copilot/create-responses.ts
3583
- const createResponses = async (payload, { vision, initiator, subagentMarker, requestId, sessionId, compactType, transport = "http" }) => {
3584
- if (!state.copilotToken) throw new Error("Copilot token not found");
3585
- const headers = {
3586
- ...copilotHeaders(state, requestId, vision),
3587
- "x-initiator": initiator
3588
- };
3589
- prepareInteractionHeaders(sessionId, Boolean(subagentMarker), headers);
3590
- prepareForCompact(headers, compactType);
3591
- payload.service_tier = void 0;
3592
- consola.log(`<-- model: ${payload.model}`);
3593
- const effectiveTransport = compactType === 1 ? "http" : transport;
3594
- if (payload.stream === true && effectiveTransport === "websocket") return createPooledResponsesWebSocketStream(prepareResponsesWebSocketRequest(payload, headers, {
3595
- requestId,
3596
- subagentMarker
3597
- }));
3598
- return await createHttpResponses(payload, headers);
3599
- };
3600
- const createHttpResponses = async (payload, headers) => {
3601
- const response = await fetch(`${copilotBaseUrl(state)}/responses`, {
3602
- method: "POST",
3603
- headers,
3604
- body: JSON.stringify(payload)
3605
- });
3606
- logCopilotRateLimits(response.headers);
3607
- if (!response.ok) {
3608
- consola.error("Failed to create responses", response);
3609
- throw new HTTPError("Failed to create responses", response);
3610
- }
3611
- if (payload.stream) return events(response);
3612
- return await response.json();
3613
- };
3614
- const prepareResponsesWebSocketRequest = (payload, preparedHeaders, options) => {
3615
- const initiator = getResponsesWebSocketInitiator(preparedHeaders);
3616
- return {
3617
- headers: copilotWebSocketHeaders(preparedHeaders),
3618
- poolKey: buildResponsesWebSocketPoolKey(payload, options),
3619
- payload: buildResponsesWebSocketPayload(payload, initiator),
3620
- url: buildResponsesWebSocketUrl(copilotBaseUrl(state))
3621
- };
3622
- };
3623
- const buildResponsesWebSocketPoolKey = (payload, { requestId, subagentMarker }) => {
3624
- const tokenFingerprint = state.copilotToken ? createHash("sha256").update(state.copilotToken).digest("hex").slice(0, 16) : "missing-token";
3625
- const subagentKey = subagentMarker ? [
3626
- subagentMarker.session_id,
3627
- subagentMarker.agent_id,
3628
- subagentMarker.agent_type
3629
- ].join(":") : "main";
3630
- return [
3631
- tokenFingerprint,
3632
- payload.model,
3633
- requestId,
3634
- subagentKey
3635
- ].map(encodePoolKeyPart).join("|");
3636
- };
3637
- const getResponsesWebSocketInitiator = (preparedHeaders) => {
3638
- return getHeaderValue(preparedHeaders, "x-initiator")?.toLowerCase() === "agent" ? "agent" : "user";
3639
- };
3640
- const createPooledResponsesWebSocketStream = (request) => createResponsesSafeStream(createPooledWebSocketStream(request, {
3641
- createChunk: createResponsesWebSocketStreamChunk,
3642
- isTerminalChunk: isTerminalResponsesStreamChunk,
3643
- openErrorMessage: "Failed to create responses websocket",
3644
- streamErrorMessage: "Responses websocket stream error",
3645
- terminalChunkMissingMessage: "Responses websocket ended without a terminal response"
3646
- }));
3647
- const createResponsesSafeStream = async function* (source) {
3648
- try {
3649
- yield* source;
3650
- } catch (error) {
3651
- yield createResponsesErrorServerSentEventChunk(getErrorMessage(error));
3652
- }
3653
- };
3654
- const buildResponsesWebSocketPayload = (payload, initiator) => {
3655
- const websocketPayload = {
3656
- ...payload,
3657
- type: "response.create",
3658
- initiator
3659
- };
3660
- delete websocketPayload.stream;
3661
- delete websocketPayload["background"];
3662
- delete websocketPayload.service_tier;
3663
- return websocketPayload;
3664
- };
3665
- const buildResponsesWebSocketUrl = (baseUrl) => {
3666
- return createWebSocketUrl(`${baseUrl.replace(/\/+$/u, "")}/responses`);
3667
- };
3668
- const getHeaderValue = (headers, headerName) => {
3669
- const normalizedHeaderName = headerName.toLowerCase();
3670
- return Object.entries(headers).find(([key]) => key.toLowerCase() === normalizedHeaderName)?.[1];
3671
- };
3672
- const encodePoolKeyPart = (value) => encodeURIComponent(value);
3673
- const createResponsesWebSocketStreamChunk = (data) => {
3674
- if (data === "[DONE]") return { data };
3675
- try {
3676
- const parsed = JSON.parse(data);
3677
- if (parsed.type === "response.completed") logCopilotQuotaSnapshots(parsed.copilot_quota_snapshots);
3678
- if (parsed.type === "error" && parsed.error) {
3679
- consola.warn("Copilot responses websocket stream error:", parsed.error);
3680
- parsed.code = parsed.error.code;
3681
- parsed.message = parsed.error.message;
3682
- }
3683
- return {
3684
- event: typeof parsed.type === "string" ? parsed.type : void 0,
3685
- data: JSON.stringify(parsed),
3686
- id: typeof parsed.id === "string" ? parsed.id : void 0
3687
- };
3688
- } catch {
3689
- return { data };
3690
- }
3691
- };
3692
- const isTerminalResponsesStreamChunk = (chunk) => {
3693
- if (!chunk.data || chunk.data === "[DONE]") return false;
3694
- try {
3695
- const parsed = JSON.parse(chunk.data);
3696
- return parsed.type === "response.completed" || parsed.type === "response.failed" || parsed.type === "response.incomplete" || parsed.type === "error";
3697
- } catch {
3698
- return false;
3699
- }
3700
- };
3701
- const createResponsesErrorServerSentEventChunk = (message) => {
3702
- const errorEvent = {
3703
- code: null,
3704
- message,
3705
- param: null,
3706
- sequence_number: 0,
3707
- type: "error"
3708
- };
3709
- return {
3710
- event: errorEvent.type,
3711
- data: JSON.stringify(errorEvent)
3712
- };
3713
- };
3714
- const getErrorMessage = (error) => {
3715
- if (error instanceof Error && error.message) return error.message;
3716
- return String(error);
3717
- };
3718
- //#endregion
3719
4490
  //#region src/routes/messages/responses-translation.ts
3720
4491
  const MESSAGE_TYPE = "message";
3721
4492
  const COMPACTION_SIGNATURE_PREFIX = "cm1#";
@@ -5037,72 +5808,6 @@ const containsVisionContent = (value) => {
5037
5808
  return false;
5038
5809
  };
5039
5810
  //#endregion
5040
- //#region src/routes/messages/web-search/backend.ts
5041
- /** Builds the Responses API web_search tool object from the Anthropic config. */
5042
- const buildResponsesWebSearchTool = (config) => {
5043
- const tool = { type: "web_search" };
5044
- const filters = {};
5045
- if (config.allowedDomains?.length) filters.allowed_domains = config.allowedDomains;
5046
- if (config.blockedDomains?.length) filters.blocked_domains = config.blockedDomains;
5047
- if (Object.keys(filters).length > 0) tool.filters = filters;
5048
- if (config.userLocation) tool.user_location = config.userLocation;
5049
- return tool;
5050
- };
5051
- const isMessageItem = (item) => item.type === "message";
5052
- const isValidUrlCitation = (annotation, seenUrls) => {
5053
- const ann = annotation;
5054
- return ann.type === "url_citation" && Boolean(ann.url) && !seenUrls.has(ann.url);
5055
- };
5056
- const collectTextParts = (blocks, seenUrls) => {
5057
- const textParts = [];
5058
- const sources = [];
5059
- for (const block of blocks ?? []) {
5060
- if (block.type !== "output_text") continue;
5061
- if (block.text) textParts.push(block.text);
5062
- for (const annotation of block.annotations ?? []) {
5063
- if (!isValidUrlCitation(annotation, seenUrls)) continue;
5064
- const ann = annotation;
5065
- seenUrls.add(ann.url);
5066
- sources.push({
5067
- url: ann.url,
5068
- title: ann.title ?? ann.url
5069
- });
5070
- }
5071
- }
5072
- return {
5073
- textParts,
5074
- sources
5075
- };
5076
- };
5077
- const collectQuery = (item, queries) => {
5078
- if (item.action?.queries?.length) queries.push(...item.action.queries);
5079
- else if (item.action?.query) queries.push(item.action.query);
5080
- };
5081
- /**
5082
- * Extracts the answer text, deduped sources, and run queries from a GPT
5083
- * /responses web_search result.
5084
- */
5085
- const extractWebSearchResult = (result) => {
5086
- const textParts = [];
5087
- const sources = [];
5088
- const seenUrls = /* @__PURE__ */ new Set();
5089
- const queries = [];
5090
- for (const item of result.output) {
5091
- if (isMessageItem(item)) {
5092
- const collected = collectTextParts(item.content, seenUrls);
5093
- textParts.push(...collected.textParts);
5094
- sources.push(...collected.sources);
5095
- continue;
5096
- }
5097
- if (item.type === "web_search_call") collectQuery(item, queries);
5098
- }
5099
- return {
5100
- answerText: textParts.join("\n\n").trim() || (result.output_text ?? "").trim(),
5101
- sources,
5102
- queries
5103
- };
5104
- };
5105
- //#endregion
5106
5811
  //#region src/routes/messages/web-search/fulfill.ts
5107
5812
  const webSearchFlowDependencies = {
5108
5813
  createResponses,
@@ -6686,7 +7391,7 @@ providerAlphaSearchRoutes.post("/", async (c) => {
6686
7391
  message: `Provider '${provider}' not found or disabled`,
6687
7392
  type: "invalid_request_error"
6688
7393
  } }, 404);
6689
- if (providerConfig.name === "codex") return await handleCodexAlphaSearch(c, providerConfig);
7394
+ if (providerConfig.name === "codex") return await handleAlphaSearchRequest(c, providerConfig);
6690
7395
  await debugJsonAsync(logger$5, "provider.alpha_search.request", async () => ({
6691
7396
  body: await c.req.raw.clone().text(),
6692
7397
  provider
@@ -7259,4 +7964,4 @@ server.route("/:provider/images", providerImageRoutes);
7259
7964
  //#endregion
7260
7965
  export { server };
7261
7966
 
7262
- //# sourceMappingURL=server-D8An7dpg.js.map
7967
+ //# sourceMappingURL=server-DWY7PmxG.js.map