@jeffreycao/copilot-api 1.14.23 → 1.15.0

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";
@@ -327,203 +327,6 @@ const createHandlerLogger = (name) => {
327
327
  return instance;
328
328
  };
329
329
  //#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
330
  //#region src/lib/provider-model.ts
528
331
  const parseProviderModelAlias = (model) => {
529
332
  const separatorIndex = model.indexOf("/");
@@ -554,6 +357,31 @@ const createFallbackModel = (modelId) => ({
554
357
  version: "unknown"
555
358
  });
556
359
  //#endregion
360
+ //#region src/lib/provider-resolver.ts
361
+ function isMissingCodexCredentialsError(error) {
362
+ return error instanceof Error && error.message === "Codex credentials not found. Run `copilot-api auth login --provider codex` first.";
363
+ }
364
+ async function resolveProviderConfig(providerName) {
365
+ const normalizedProviderName = providerName.trim();
366
+ if (!normalizedProviderName) return null;
367
+ if (normalizedProviderName === "codex") {
368
+ if (getRawProviderConfig(normalizedProviderName)?.enabled === false) return null;
369
+ try {
370
+ await setupCodexToken();
371
+ } catch (error) {
372
+ if (isMissingCodexCredentialsError(error)) return null;
373
+ throw error;
374
+ }
375
+ const providerConfig = getProviderConfig(normalizedProviderName);
376
+ if (!providerConfig) return null;
377
+ return {
378
+ ...providerConfig,
379
+ apiKey: state.codexAccessToken ?? providerConfig.apiKey
380
+ };
381
+ }
382
+ return getProviderConfig(normalizedProviderName);
383
+ }
384
+ //#endregion
557
385
  //#region src/lib/event-bus.ts
558
386
  var EventBus = class {
559
387
  handlers = /* @__PURE__ */ new Map();
@@ -1671,34 +1499,1161 @@ function normalizeOptionalCost(value) {
1671
1499
  return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
1672
1500
  }
1673
1501
  //#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]);
1502
+ //#region src/routes/alpha-search/alpha-search-types.ts
1503
+ const unsignedInteger = z.number().int().nonnegative();
1504
+ const passthroughRecord = z.record(z.string(), z.unknown());
1505
+ const searchQuerySchema = z.object({
1506
+ q: z.string(),
1507
+ recency: unsignedInteger.optional(),
1508
+ domains: z.array(z.string()).optional()
1509
+ }).loose();
1510
+ const openOperationSchema = z.object({
1511
+ ref_id: z.string(),
1512
+ lineno: unsignedInteger.optional()
1513
+ }).loose();
1514
+ const clickOperationSchema = z.object({
1515
+ ref_id: z.string(),
1516
+ id: unsignedInteger
1517
+ }).loose();
1518
+ const findOperationSchema = z.object({
1519
+ ref_id: z.string(),
1520
+ pattern: z.string()
1521
+ }).loose();
1522
+ const screenshotOperationSchema = z.object({
1523
+ ref_id: z.string(),
1524
+ pageno: unsignedInteger
1525
+ }).loose();
1526
+ const financeOperationSchema = z.object({
1527
+ ticker: z.string(),
1528
+ type: z.enum([
1529
+ "equity",
1530
+ "fund",
1531
+ "crypto",
1532
+ "index"
1533
+ ]),
1534
+ market: z.string().optional()
1535
+ }).loose();
1536
+ const weatherOperationSchema = z.object({
1537
+ location: z.string(),
1538
+ start: z.string().optional(),
1539
+ duration: unsignedInteger.optional()
1540
+ }).loose();
1541
+ const sportsOperationSchema = z.object({
1542
+ tool: z.literal("sports").optional(),
1543
+ fn: z.enum(["schedule", "standings"]),
1544
+ league: z.enum([
1545
+ "nba",
1546
+ "wnba",
1547
+ "nfl",
1548
+ "nhl",
1549
+ "mlb",
1550
+ "epl",
1551
+ "ncaamb",
1552
+ "ncaawb",
1553
+ "ipl"
1554
+ ]),
1555
+ team: z.string().optional(),
1556
+ opponent: z.string().optional(),
1557
+ date_from: z.string().optional(),
1558
+ date_to: z.string().optional(),
1559
+ num_games: unsignedInteger.optional(),
1560
+ locale: z.string().optional()
1561
+ }).loose();
1562
+ const timeOperationSchema = z.object({ utc_offset: z.string().regex(/^[+-](?:[01]\d|2[0-3]):[0-5]\d$/u) }).loose();
1563
+ const alphaSearchCommandsSchema = z.object({
1564
+ search_query: z.array(searchQuerySchema).optional(),
1565
+ image_query: z.array(searchQuerySchema).optional(),
1566
+ open: z.array(openOperationSchema).optional(),
1567
+ click: z.array(clickOperationSchema).optional(),
1568
+ find: z.array(findOperationSchema).optional(),
1569
+ screenshot: z.array(screenshotOperationSchema).optional(),
1570
+ finance: z.array(financeOperationSchema).optional(),
1571
+ weather: z.array(weatherOperationSchema).optional(),
1572
+ sports: z.array(sportsOperationSchema).optional(),
1573
+ time: z.array(timeOperationSchema).optional(),
1574
+ response_length: z.enum([
1575
+ "short",
1576
+ "medium",
1577
+ "long"
1578
+ ]).optional()
1579
+ }).loose();
1580
+ const reasoningSchema = z.object({
1581
+ effort: z.string().min(1).nullable().optional(),
1582
+ summary: z.enum([
1583
+ "auto",
1584
+ "concise",
1585
+ "detailed",
1586
+ "none"
1587
+ ]).nullable().optional(),
1588
+ context: z.enum([
1589
+ "auto",
1590
+ "current_turn",
1591
+ "all_turns"
1592
+ ]).nullable().optional()
1593
+ }).loose();
1594
+ const settingsSchema = z.object({
1595
+ user_location: z.object({
1596
+ type: z.literal("approximate"),
1597
+ country: z.string().optional(),
1598
+ region: z.string().optional(),
1599
+ city: z.string().optional(),
1600
+ timezone: z.string().optional()
1601
+ }).loose().optional(),
1602
+ search_context_size: z.enum([
1603
+ "low",
1604
+ "medium",
1605
+ "high"
1606
+ ]).optional(),
1607
+ filters: z.object({
1608
+ allowed_domains: z.array(z.string()).optional(),
1609
+ blocked_domains: z.array(z.string()).optional()
1610
+ }).loose().optional(),
1611
+ image_settings: z.object({
1612
+ max_results: unsignedInteger.optional(),
1613
+ caption: z.boolean().optional()
1614
+ }).loose().optional(),
1615
+ allowed_callers: z.array(z.enum([
1616
+ "direct",
1617
+ "shell",
1618
+ "code_interpreter"
1619
+ ])).optional(),
1620
+ external_web_access: z.union([z.boolean(), z.enum([
1621
+ "cached",
1622
+ "indexed",
1623
+ "live"
1624
+ ])]).optional()
1625
+ }).loose();
1626
+ const alphaSearchRequestSchema = z.object({
1627
+ id: z.string(),
1628
+ model: z.string(),
1629
+ reasoning: reasoningSchema.optional(),
1630
+ input: z.union([z.string(), z.array(passthroughRecord)]).optional(),
1631
+ commands: alphaSearchCommandsSchema.optional(),
1632
+ settings: settingsSchema.optional(),
1633
+ max_output_tokens: unsignedInteger.optional()
1634
+ }).loose();
1635
+ //#endregion
1636
+ //#region src/routes/messages/web-search/backend.ts
1637
+ /** Builds the Responses API web_search tool object from normalized config. */
1638
+ const buildResponsesWebSearchTool = (config) => {
1639
+ const tool = { type: "web_search" };
1640
+ const filters = {};
1641
+ if (config.allowedDomains?.length) filters.allowed_domains = config.allowedDomains;
1642
+ if (config.blockedDomains?.length) filters.blocked_domains = config.blockedDomains;
1643
+ if (Object.keys(filters).length > 0) tool.filters = filters;
1644
+ if (config.userLocation) tool.user_location = config.userLocation;
1645
+ if (config.searchContextSize) tool.search_context_size = config.searchContextSize;
1646
+ return tool;
1691
1647
  };
1692
- const selectContextCacheMessageIndexes = (messages) => {
1693
- const cacheableIndexes = messages.flatMap((message, index) => isContextCacheMarkerEligible(message) ? [index] : []);
1694
- const systemIndexes = cacheableIndexes.filter((index) => messages[index]?.role === "system").slice(0, 2);
1695
- const finalIndexes = cacheableIndexes.filter((index) => messages[index]?.role !== "system").slice(-1);
1696
- return uniqueIndexes$1([...systemIndexes, ...finalIndexes]).sort((a, b) => a - b);
1648
+ const isMessageItem = (item) => item.type === "message";
1649
+ const isValidUrlCitation = (annotation, seenUrls) => {
1650
+ const ann = annotation;
1651
+ return ann.type === "url_citation" && Boolean(ann.url) && !seenUrls.has(ann.url);
1697
1652
  };
1698
- const uniqueIndexes$1 = (indexes) => [...new Set(indexes)].slice(0, OPENAI_COMPATIBLE_CONTEXT_CACHE_MARKER_LIMIT);
1699
- const isContextCacheMarkerEligible = (message) => {
1700
- if (!OPENAI_COMPATIBLE_CONTEXT_CACHE_ROLES.has(message.role)) return false;
1701
- if (typeof message.content === "string") return message.content.length > 0;
1653
+ const collectTextParts = (blocks, seenUrls) => {
1654
+ const textParts = [];
1655
+ const sources = [];
1656
+ for (const block of blocks ?? []) {
1657
+ if (block.type !== "output_text") continue;
1658
+ if (block.text) textParts.push(block.text);
1659
+ for (const annotation of block.annotations ?? []) {
1660
+ if (!isValidUrlCitation(annotation, seenUrls)) continue;
1661
+ const ann = annotation;
1662
+ seenUrls.add(ann.url);
1663
+ const start = Math.max(0, (ann.start_index ?? 0) - 120);
1664
+ const end = Math.min(block.text?.length ?? 0, (ann.end_index ?? block.text?.length ?? 0) + 120);
1665
+ sources.push({
1666
+ url: ann.url,
1667
+ title: ann.title ?? ann.url,
1668
+ snippet: block.text?.slice(start, end).trim()
1669
+ });
1670
+ }
1671
+ }
1672
+ return {
1673
+ textParts,
1674
+ sources
1675
+ };
1676
+ };
1677
+ const collectQuery = (item, queries) => {
1678
+ if (item.action?.queries?.length) queries.push(...item.action.queries);
1679
+ else if (item.action?.query) queries.push(item.action.query);
1680
+ };
1681
+ /**
1682
+ * Extracts the answer text, deduped sources, and run queries from a GPT
1683
+ * /responses web_search result.
1684
+ */
1685
+ const extractWebSearchResult = (result) => {
1686
+ const textParts = [];
1687
+ const sources = [];
1688
+ const seenUrls = /* @__PURE__ */ new Set();
1689
+ const queries = [];
1690
+ for (const item of result.output) if (isMessageItem(item)) {
1691
+ const collected = collectTextParts(item.content, seenUrls);
1692
+ textParts.push(...collected.textParts);
1693
+ sources.push(...collected.sources);
1694
+ }
1695
+ for (const item of result.output) if (item.type === "web_search_call") {
1696
+ const action = item.action;
1697
+ collectQuery({ action }, queries);
1698
+ for (const source of action?.sources ?? []) {
1699
+ if (!source.url || seenUrls.has(source.url)) continue;
1700
+ seenUrls.add(source.url);
1701
+ sources.push({
1702
+ url: source.url,
1703
+ title: source.url
1704
+ });
1705
+ }
1706
+ }
1707
+ return {
1708
+ answerText: textParts.join("\n\n").trim() || (result.output_text ?? "").trim(),
1709
+ sources,
1710
+ queries
1711
+ };
1712
+ };
1713
+ //#endregion
1714
+ //#region src/lib/copilot-rate-limit.ts
1715
+ const copilotRateLimitTypes = ["session", "weekly"];
1716
+ const copilotRateLimitHeaders = {
1717
+ session: "x-usage-ratelimit-session",
1718
+ weekly: "x-usage-ratelimit-weekly"
1719
+ };
1720
+ const copilotQuotaSnapshotKeys = {
1721
+ session: "5Hour-Session-RateLimits",
1722
+ weekly: "Weekly-Session-RateLimits"
1723
+ };
1724
+ const hasGetMethod = (headers) => {
1725
+ return "get" in headers && typeof headers.get === "function";
1726
+ };
1727
+ const getHeaderValue$1 = (headers, headerName) => {
1728
+ if (hasGetMethod(headers)) return headers.get(headerName);
1729
+ const normalizedHeaderName = headerName.toLowerCase();
1730
+ return Object.entries(headers).find(([key]) => key.toLowerCase() === normalizedHeaderName)?.[1] ?? null;
1731
+ };
1732
+ const parseCopilotRateLimitHeader = (headerValue) => {
1733
+ const params = new URLSearchParams(headerValue);
1734
+ const remaining = params.get("rem");
1735
+ const resetAt = params.get("rst");
1736
+ if (!remaining || !resetAt) return null;
1737
+ return {
1738
+ remaining,
1739
+ resetAt
1740
+ };
1741
+ };
1742
+ const getCopilotRateLimitUsage = (headers, type) => {
1743
+ const headerName = copilotRateLimitHeaders[type];
1744
+ const headerValue = getHeaderValue$1(headers, headerName);
1745
+ if (!headerValue) return null;
1746
+ const parsed = parseCopilotRateLimitHeader(headerValue);
1747
+ if (!parsed) return null;
1748
+ return {
1749
+ type,
1750
+ ...parsed
1751
+ };
1752
+ };
1753
+ const getCopilotRateLimitUsageFromSnapshots = (snapshots, type) => {
1754
+ const snapshot = snapshots?.[copilotQuotaSnapshotKeys[type]];
1755
+ if (!isCopilotQuotaSnapshot(snapshot)) return null;
1756
+ return {
1757
+ remaining: String(snapshot.percent_remaining),
1758
+ resetAt: snapshot.reset_date,
1759
+ type
1760
+ };
1761
+ };
1762
+ const logCopilotRateLimits = (headers) => {
1763
+ for (const type of copilotRateLimitTypes) {
1764
+ const usage = getCopilotRateLimitUsage(headers, type);
1765
+ if (!usage) continue;
1766
+ logCopilotRateLimitUsage(usage);
1767
+ }
1768
+ };
1769
+ const logCopilotQuotaSnapshots = (snapshots) => {
1770
+ for (const type of copilotRateLimitTypes) {
1771
+ const usage = getCopilotRateLimitUsageFromSnapshots(snapshots, type);
1772
+ if (!usage) continue;
1773
+ logCopilotRateLimitUsage(usage);
1774
+ }
1775
+ };
1776
+ const logCopilotRateLimitUsage = (usage) => {
1777
+ const d = new Date(usage.resetAt);
1778
+ const dateStr = Number.isNaN(d.getTime()) ? usage.resetAt : d.toLocaleString();
1779
+ consola.log(`Copilot ${usage.type} quota remaining: ${usage.remaining}, resets at: ${dateStr}`);
1780
+ };
1781
+ const isCopilotQuotaSnapshot = (value) => {
1782
+ if (!value || typeof value !== "object") return false;
1783
+ const record = value;
1784
+ 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";
1785
+ };
1786
+ //#endregion
1787
+ //#region src/services/copilot/create-responses.ts
1788
+ const createResponses = async (payload, { vision, initiator, subagentMarker, requestId, sessionId, compactType, transport = "http" }) => {
1789
+ if (!state.copilotToken) throw new Error("Copilot token not found");
1790
+ const headers = {
1791
+ ...copilotHeaders(state, requestId, vision),
1792
+ "x-initiator": initiator
1793
+ };
1794
+ prepareInteractionHeaders(sessionId, Boolean(subagentMarker), headers);
1795
+ prepareForCompact(headers, compactType);
1796
+ payload.service_tier = void 0;
1797
+ consola.log(`<-- model: ${payload.model}`);
1798
+ const effectiveTransport = compactType === 1 ? "http" : transport;
1799
+ if (payload.stream === true && effectiveTransport === "websocket") return createPooledResponsesWebSocketStream(prepareResponsesWebSocketRequest(payload, headers, {
1800
+ requestId,
1801
+ subagentMarker
1802
+ }));
1803
+ return await createHttpResponses(payload, headers);
1804
+ };
1805
+ const createHttpResponses = async (payload, headers) => {
1806
+ const response = await fetch(`${copilotBaseUrl(state)}/responses`, {
1807
+ method: "POST",
1808
+ headers,
1809
+ body: JSON.stringify(payload)
1810
+ });
1811
+ logCopilotRateLimits(response.headers);
1812
+ if (!response.ok) {
1813
+ consola.error("Failed to create responses", response);
1814
+ throw new HTTPError("Failed to create responses", response);
1815
+ }
1816
+ if (payload.stream) return events(response);
1817
+ return await response.json();
1818
+ };
1819
+ const prepareResponsesWebSocketRequest = (payload, preparedHeaders, options) => {
1820
+ const initiator = getResponsesWebSocketInitiator(preparedHeaders);
1821
+ return {
1822
+ headers: copilotWebSocketHeaders(preparedHeaders),
1823
+ poolKey: buildResponsesWebSocketPoolKey(payload, options),
1824
+ payload: buildResponsesWebSocketPayload(payload, initiator),
1825
+ url: buildResponsesWebSocketUrl(copilotBaseUrl(state))
1826
+ };
1827
+ };
1828
+ const buildResponsesWebSocketPoolKey = (payload, { requestId, subagentMarker }) => {
1829
+ const tokenFingerprint = state.copilotToken ? createHash("sha256").update(state.copilotToken).digest("hex").slice(0, 16) : "missing-token";
1830
+ const subagentKey = subagentMarker ? [
1831
+ subagentMarker.session_id,
1832
+ subagentMarker.agent_id,
1833
+ subagentMarker.agent_type
1834
+ ].join(":") : "main";
1835
+ return [
1836
+ tokenFingerprint,
1837
+ payload.model,
1838
+ requestId,
1839
+ subagentKey
1840
+ ].map(encodePoolKeyPart).join("|");
1841
+ };
1842
+ const getResponsesWebSocketInitiator = (preparedHeaders) => {
1843
+ return getHeaderValue(preparedHeaders, "x-initiator")?.toLowerCase() === "agent" ? "agent" : "user";
1844
+ };
1845
+ const createPooledResponsesWebSocketStream = (request) => createResponsesSafeStream(createPooledWebSocketStream(request, {
1846
+ createChunk: createResponsesWebSocketStreamChunk,
1847
+ isTerminalChunk: isTerminalResponsesStreamChunk,
1848
+ openErrorMessage: "Failed to create responses websocket",
1849
+ streamErrorMessage: "Responses websocket stream error",
1850
+ terminalChunkMissingMessage: "Responses websocket ended without a terminal response"
1851
+ }));
1852
+ const createResponsesSafeStream = async function* (source) {
1853
+ try {
1854
+ yield* source;
1855
+ } catch (error) {
1856
+ yield createResponsesErrorServerSentEventChunk(getErrorMessage(error));
1857
+ }
1858
+ };
1859
+ const buildResponsesWebSocketPayload = (payload, initiator) => {
1860
+ const websocketPayload = {
1861
+ ...payload,
1862
+ type: "response.create",
1863
+ initiator
1864
+ };
1865
+ delete websocketPayload.stream;
1866
+ delete websocketPayload["background"];
1867
+ delete websocketPayload.service_tier;
1868
+ return websocketPayload;
1869
+ };
1870
+ const buildResponsesWebSocketUrl = (baseUrl) => {
1871
+ return createWebSocketUrl(`${baseUrl.replace(/\/+$/u, "")}/responses`);
1872
+ };
1873
+ const getHeaderValue = (headers, headerName) => {
1874
+ const normalizedHeaderName = headerName.toLowerCase();
1875
+ return Object.entries(headers).find(([key]) => key.toLowerCase() === normalizedHeaderName)?.[1];
1876
+ };
1877
+ const encodePoolKeyPart = (value) => encodeURIComponent(value);
1878
+ const createResponsesWebSocketStreamChunk = (data) => {
1879
+ if (data === "[DONE]") return { data };
1880
+ try {
1881
+ const parsed = JSON.parse(data);
1882
+ if (parsed.type === "response.completed") logCopilotQuotaSnapshots(parsed.copilot_quota_snapshots);
1883
+ if (parsed.type === "error" && parsed.error) {
1884
+ consola.warn("Copilot responses websocket stream error:", parsed.error);
1885
+ parsed.code = parsed.error.code;
1886
+ parsed.message = parsed.error.message;
1887
+ }
1888
+ return {
1889
+ event: typeof parsed.type === "string" ? parsed.type : void 0,
1890
+ data: JSON.stringify(parsed),
1891
+ id: typeof parsed.id === "string" ? parsed.id : void 0
1892
+ };
1893
+ } catch {
1894
+ return { data };
1895
+ }
1896
+ };
1897
+ const isTerminalResponsesStreamChunk = (chunk) => {
1898
+ if (!chunk.data || chunk.data === "[DONE]") return false;
1899
+ try {
1900
+ const parsed = JSON.parse(chunk.data);
1901
+ return parsed.type === "response.completed" || parsed.type === "response.failed" || parsed.type === "response.incomplete" || parsed.type === "error";
1902
+ } catch {
1903
+ return false;
1904
+ }
1905
+ };
1906
+ const createResponsesErrorServerSentEventChunk = (message) => {
1907
+ const errorEvent = {
1908
+ code: null,
1909
+ message,
1910
+ param: null,
1911
+ sequence_number: 0,
1912
+ type: "error"
1913
+ };
1914
+ return {
1915
+ event: errorEvent.type,
1916
+ data: JSON.stringify(errorEvent)
1917
+ };
1918
+ };
1919
+ const getErrorMessage = (error) => {
1920
+ if (error instanceof Error && error.message) return error.message;
1921
+ return String(error);
1922
+ };
1923
+ //#endregion
1924
+ //#region src/services/providers/provider-proxy.ts
1925
+ const SHARED_FORWARDABLE_HEADERS = ["accept", "user-agent"];
1926
+ const ANTHROPIC_FORWARDABLE_HEADERS = ["anthropic-version", "anthropic-beta"];
1927
+ const STRIPPED_RESPONSE_HEADERS = [
1928
+ "connection",
1929
+ "content-encoding",
1930
+ "content-length",
1931
+ "keep-alive",
1932
+ "proxy-authenticate",
1933
+ "proxy-authorization",
1934
+ "te",
1935
+ "trailer",
1936
+ "transfer-encoding",
1937
+ "upgrade"
1938
+ ];
1939
+ function buildProviderUpstreamHeaders(providerConfig, requestHeaders) {
1940
+ const authHeaders = {};
1941
+ if (providerConfig.authType === "x-api-key") authHeaders["x-api-key"] = providerConfig.apiKey;
1942
+ else authHeaders.authorization = `Bearer ${providerConfig.apiKey}`;
1943
+ const headers = {
1944
+ "content-type": "application/json",
1945
+ accept: "application/json",
1946
+ ...authHeaders
1947
+ };
1948
+ for (const headerName of SHARED_FORWARDABLE_HEADERS) {
1949
+ const headerValue = requestHeaders.get(headerName);
1950
+ if (headerValue) headers[headerName] = headerValue;
1951
+ }
1952
+ if (providerConfig.type !== "anthropic") return headers;
1953
+ for (const headerName of ANTHROPIC_FORWARDABLE_HEADERS) {
1954
+ const headerValue = requestHeaders.get(headerName);
1955
+ if (headerValue) headers[headerName] = headerValue;
1956
+ }
1957
+ return headers;
1958
+ }
1959
+ function createProviderProxyResponse(upstreamResponse, body) {
1960
+ const headers = new Headers(upstreamResponse.headers);
1961
+ for (const headerName of STRIPPED_RESPONSE_HEADERS) headers.delete(headerName);
1962
+ return new Response(body ?? upstreamResponse.body, {
1963
+ headers,
1964
+ status: upstreamResponse.status,
1965
+ statusText: upstreamResponse.statusText
1966
+ });
1967
+ }
1968
+ async function forwardProviderMessages(providerConfig, payload, requestHeaders) {
1969
+ consola.log(`<-- model: ${payload.model}`);
1970
+ return await fetch(`${providerConfig.baseUrl}/v1/messages`, {
1971
+ method: "POST",
1972
+ headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
1973
+ body: JSON.stringify(payload)
1974
+ });
1975
+ }
1976
+ async function forwardProviderChatCompletions(providerConfig, payload, requestHeaders) {
1977
+ consola.log(`<-- model: ${payload.model}`);
1978
+ return await fetch(`${providerConfig.baseUrl}/v1/chat/completions`, {
1979
+ method: "POST",
1980
+ headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
1981
+ body: JSON.stringify(payload)
1982
+ });
1983
+ }
1984
+ async function forwardProviderResponses(providerConfig, payload, requestHeaders) {
1985
+ consola.log(`<-- model: ${payload.model}`);
1986
+ return await fetch(`${providerConfig.baseUrl}/v1/responses`, {
1987
+ method: "POST",
1988
+ headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders),
1989
+ body: JSON.stringify(payload)
1990
+ });
1991
+ }
1992
+ async function forwardProviderModels(providerConfig, requestHeaders) {
1993
+ return await fetch(`${providerConfig.baseUrl}/v1/models`, {
1994
+ method: "GET",
1995
+ headers: buildProviderUpstreamHeaders(providerConfig, requestHeaders)
1996
+ });
1997
+ }
1998
+ /** Align with Codex images: long-running generation/edits need a generous cap. */
1999
+ const PROVIDER_IMAGES_TIMEOUT_MS = 900 * 1e3;
2000
+ const providerImagesDispatcher = { dispatch(options, handler) {
2001
+ return getGlobalDispatcher().dispatch({
2002
+ ...options,
2003
+ bodyTimeout: PROVIDER_IMAGES_TIMEOUT_MS,
2004
+ headersTimeout: PROVIDER_IMAGES_TIMEOUT_MS
2005
+ }, handler);
2006
+ } };
2007
+ function resolveProviderRequestUrl(providerConfig, requestUrl, path) {
2008
+ const upstreamUrl = new URL(`${providerConfig.baseUrl}${path}`);
2009
+ upstreamUrl.search = new URL(requestUrl, "http://localhost").search;
2010
+ return upstreamUrl.toString();
2011
+ }
2012
+ async function forwardProviderAlphaSearch(providerConfig, request) {
2013
+ const headers = buildProviderUpstreamHeaders(providerConfig, request.headers);
2014
+ const body = await request.arrayBuffer();
2015
+ return await fetch(resolveProviderRequestUrl(providerConfig, request.url, "/v1/alpha/search"), {
2016
+ method: "POST",
2017
+ headers,
2018
+ body
2019
+ });
2020
+ }
2021
+ async function forwardProviderImages(providerConfig, request, operation) {
2022
+ const headers = buildProviderUpstreamHeaders(providerConfig, request.headers);
2023
+ const contentType = request.headers.get("content-type");
2024
+ if (contentType) headers["content-type"] = contentType;
2025
+ else if (operation === "edits") delete headers["content-type"];
2026
+ const init = {
2027
+ method: "POST",
2028
+ headers,
2029
+ body: request.body,
2030
+ duplex: "half",
2031
+ signal: AbortSignal.timeout(PROVIDER_IMAGES_TIMEOUT_MS)
2032
+ };
2033
+ const upstreamUrl = resolveProviderRequestUrl(providerConfig, request.url, `/v1/images/${operation}`);
2034
+ if (typeof Bun !== "undefined") return await fetch(upstreamUrl, init);
2035
+ return await fetch$1(upstreamUrl, {
2036
+ ...init,
2037
+ dispatcher: providerImagesDispatcher
2038
+ });
2039
+ }
2040
+ //#endregion
2041
+ //#region src/routes/alpha-search/alpha-search-responses.ts
2042
+ const logger$15 = createHandlerLogger("alpha-search-responses-handler");
2043
+ const SESSION_TTL_MS = 3600 * 1e3;
2044
+ const MAX_SESSIONS = 128;
2045
+ const MAX_URL_REFERENCES = 256;
2046
+ const MAX_SNAPSHOTS = 16;
2047
+ const MAX_SNAPSHOT_CHARACTERS = 2e4;
2048
+ const IMAGE_UNSUPPORTED = "Unsupported by GitHub Copilot web search: image_query. Do not retry this operation; use search_query for image-source pages.";
2049
+ const SCREENSHOT_UNSUPPORTED = "Unsupported by GitHub Copilot web search: screenshot. Do not retry this operation; open the PDF for text content.";
2050
+ const KNOWN_COMMANDS = new Set([
2051
+ "search_query",
2052
+ "image_query",
2053
+ "open",
2054
+ "click",
2055
+ "find",
2056
+ "screenshot",
2057
+ "finance",
2058
+ "weather",
2059
+ "sports",
2060
+ "time",
2061
+ "response_length"
2062
+ ]);
2063
+ const sessions = /* @__PURE__ */ new Map();
2064
+ const alphaSearchResponsesDependencies = {
2065
+ createResponses,
2066
+ findEndpointModel,
2067
+ now: () => Date.now(),
2068
+ resolveMappedModel,
2069
+ createUsageRecorder: (model, sessionId) => createCopilotTokenUsageRecorder({
2070
+ endpoint: "responses",
2071
+ fallbackSessionId: sessionId,
2072
+ model
2073
+ })
2074
+ };
2075
+ function reserveSession(id, now) {
2076
+ for (const [sessionId, session] of sessions) if (now - session.touchedAt >= SESSION_TTL_MS) sessions.delete(sessionId);
2077
+ let session = sessions.get(id);
2078
+ if (!session) {
2079
+ if (sessions.size >= MAX_SESSIONS) {
2080
+ const oldestSession = sessions.keys().next();
2081
+ if (!oldestSession.done) sessions.delete(oldestSession.value);
2082
+ }
2083
+ session = {
2084
+ nextTurn: 0,
2085
+ touchedAt: now,
2086
+ referencesById: /* @__PURE__ */ new Map(),
2087
+ referencesByUrl: /* @__PURE__ */ new Map(),
2088
+ snapshots: /* @__PURE__ */ new Map()
2089
+ };
2090
+ } else {
2091
+ sessions.delete(id);
2092
+ session.touchedAt = now;
2093
+ }
2094
+ sessions.set(id, session);
2095
+ const turn = session.nextTurn;
2096
+ session.nextTurn += 1;
2097
+ return {
2098
+ session,
2099
+ turn
2100
+ };
2101
+ }
2102
+ function parseHttpUrl(value) {
2103
+ try {
2104
+ const url = new URL(value);
2105
+ return url.protocol === "http:" || url.protocol === "https:" ? url.toString() : null;
2106
+ } catch {
2107
+ return null;
2108
+ }
2109
+ }
2110
+ function extractMarkdownSources(text) {
2111
+ return Array.from(text.matchAll(/(?<!!)\[([^\]\n]+)\]\((https?:\/\/(?:[^\s<>()]|\([^\s<>()]*\))+)\)/gu), ([, title, url]) => ({
2112
+ title,
2113
+ url
2114
+ }));
2115
+ }
2116
+ function addUrlReference(session, source, turn) {
2117
+ const url = parseHttpUrl(source.url);
2118
+ if (!url) return null;
2119
+ const existing = session.referencesByUrl.get(url);
2120
+ if (existing) {
2121
+ if (existing.result.title === existing.result.url && source.title) existing.result.title = source.title;
2122
+ if (source.snippet) existing.result.snippet = source.snippet;
2123
+ return existing;
2124
+ }
2125
+ if (session.referencesById.size >= MAX_URL_REFERENCES) {
2126
+ const oldest = session.referencesById.entries().next().value;
2127
+ if (oldest) {
2128
+ const [refId, reference] = oldest;
2129
+ session.referencesById.delete(refId);
2130
+ session.referencesByUrl.delete(reference.result.url);
2131
+ }
2132
+ }
2133
+ const refId = `turn${turn.number}search${turn.nextReferenceIndex}`;
2134
+ turn.nextReferenceIndex += 1;
2135
+ const reference = { result: {
2136
+ type: "text_result",
2137
+ domain: new URL(url).hostname,
2138
+ ref_id: refId,
2139
+ snippet: source.snippet?.trim() || source.title || url,
2140
+ title: source.title || url,
2141
+ url
2142
+ } };
2143
+ session.referencesById.set(refId, reference);
2144
+ session.referencesByUrl.set(url, reference);
2145
+ return reference;
2146
+ }
2147
+ function resolveUrlReference(session, refId, turn) {
2148
+ const stored = session.referencesById.get(refId);
2149
+ if (stored) return stored;
2150
+ const url = parseHttpUrl(refId);
2151
+ return url ? addUrlReference(session, {
2152
+ title: url,
2153
+ url
2154
+ }, turn) : null;
2155
+ }
2156
+ function getSnapshot(session, refId) {
2157
+ const snapshot = session.snapshots.get(refId);
2158
+ if (!snapshot) return null;
2159
+ session.snapshots.delete(refId);
2160
+ session.snapshots.set(refId, snapshot);
2161
+ return snapshot;
2162
+ }
2163
+ function findSnapshotByUrl(session, url) {
2164
+ for (const snapshot of session.snapshots.values()) if (snapshot.source.result.url === url) return getSnapshot(session, snapshot.refId);
2165
+ return null;
2166
+ }
2167
+ function addSnapshot(session, source, turn, index, text, links) {
2168
+ const existing = findSnapshotByUrl(session, source.result.url);
2169
+ if (existing) {
2170
+ existing.text = text.slice(0, MAX_SNAPSHOT_CHARACTERS);
2171
+ existing.links = links;
2172
+ return existing;
2173
+ }
2174
+ if (session.snapshots.size >= MAX_SNAPSHOTS) {
2175
+ const oldestRefId = session.snapshots.keys().next().value;
2176
+ if (oldestRefId) session.snapshots.delete(oldestRefId);
2177
+ }
2178
+ const snapshot = {
2179
+ refId: `turn${turn}view${index}`,
2180
+ source,
2181
+ text: text.slice(0, MAX_SNAPSHOT_CHARACTERS),
2182
+ links
2183
+ };
2184
+ session.snapshots.set(snapshot.refId, snapshot);
2185
+ return snapshot;
2186
+ }
2187
+ function renderSnapshot(snapshot, lineno) {
2188
+ const lines = snapshot.text.split("\n");
2189
+ const start = lineno === void 0 ? 0 : Math.max(0, Math.min(lineno, lines.length - 1) - 10);
2190
+ const end = lineno === void 0 ? lines.length : Math.min(lines.length, start + 21);
2191
+ const numbered = lines.slice(start, end).map((line, index) => `L${start + index}: ${line}`).join("\n");
2192
+ const links = snapshot.links.map((link, index) => `[${index}] ${link.result.title} — ${link.result.url} (${link.result.ref_id})`).join("\n");
2193
+ return [
2194
+ `Open ${snapshot.refId} (${snapshot.source.result.url})`,
2195
+ numbered,
2196
+ links ? `Links:\n${links}` : ""
2197
+ ].filter(Boolean).join("\n");
2198
+ }
2199
+ function findInSnapshot(snapshot, pattern) {
2200
+ const normalizedPattern = pattern.toLowerCase();
2201
+ const matches = snapshot.text.split("\n").map((line, index) => ({
2202
+ line,
2203
+ index
2204
+ })).filter(({ line }) => line.toLowerCase().includes(normalizedPattern)).slice(0, 20);
2205
+ if (matches.length === 0) return null;
2206
+ return [`Find results for ${JSON.stringify(pattern)} in ${snapshot.refId}:`, ...matches.map(({ line, index }) => `L${index}: ${line}`)].join("\n");
2207
+ }
2208
+ function formatTime(utcOffset, now) {
2209
+ const offsetMinutes = (utcOffset.startsWith("+") ? 1 : -1) * (Number.parseInt(utcOffset.slice(1, 3), 10) * 60 + Number.parseInt(utcOffset.slice(4, 6), 10));
2210
+ return `Time at UTC${utcOffset}: ${new Date(now + offsetMinutes * 6e4).toISOString().slice(0, 19).replace("T", " ")}`;
2211
+ }
2212
+ function buildInstruction(operations, responseLength) {
2213
+ return [
2214
+ "The Operations JSON below is the complete and exclusive request for this response.",
2215
+ "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.",
2216
+ "Return only grounded results and citations for these operations. Do not discuss unrelated tasks or conversations.",
2217
+ "For search operations, honor every query, recency, domain, market, date, league, team, and locale constraint.",
2218
+ "For open operations, open the exact URL and return grounded page text plus cited links.",
2219
+ "For find operations, find the exact pattern in the specified URL and return matching context.",
2220
+ `Requested response length: ${responseLength ?? "medium"}.`,
2221
+ `Operations:\n${JSON.stringify(operations, null, 2)}`
2222
+ ].join("\n");
2223
+ }
2224
+ function unavailableReference(refId) {
2225
+ return `Reference ${JSON.stringify(refId)} is unavailable or expired. Search or open the URL again.`;
2226
+ }
2227
+ function invalidRequest$1(c, message) {
2228
+ return c.json({ error: {
2229
+ message,
2230
+ type: "invalid_request_error"
2231
+ } }, 400);
2232
+ }
2233
+ function createSearchOperationState(session, turn) {
2234
+ return {
2235
+ session,
2236
+ turn,
2237
+ output: [],
2238
+ warnings: [],
2239
+ resultReferences: /* @__PURE__ */ new Map(),
2240
+ remoteOperations: [],
2241
+ remotePages: []
2242
+ };
2243
+ }
2244
+ function includeResult(state, reference) {
2245
+ state.resultReferences.set(reference.result.url, reference);
2246
+ }
2247
+ function queuePage(state, kind, source, options = {}) {
2248
+ state.remoteOperations.push({
2249
+ operation: kind,
2250
+ url: source.result.url,
2251
+ ...options.lineno === void 0 ? {} : { lineno: options.lineno },
2252
+ ...options.pattern === void 0 ? {} : { pattern: options.pattern }
2253
+ });
2254
+ state.remotePages.push({
2255
+ kind,
2256
+ source,
2257
+ ...options
2258
+ });
2259
+ includeResult(state, source);
2260
+ }
2261
+ function processSearchCommands(commands, state) {
2262
+ for (const command of commands.search_query ?? []) state.remoteOperations.push({
2263
+ ...command,
2264
+ operation: "search_query"
2265
+ });
2266
+ }
2267
+ function processOpenCommands(commands, state) {
2268
+ for (const command of commands.open ?? []) {
2269
+ const directSnapshot = getSnapshot(state.session, command.ref_id);
2270
+ if (directSnapshot) {
2271
+ state.output.push(renderSnapshot(directSnapshot, command.lineno));
2272
+ includeResult(state, directSnapshot.source);
2273
+ continue;
2274
+ }
2275
+ const source = resolveUrlReference(state.session, command.ref_id, state.turn);
2276
+ if (!source) {
2277
+ state.output.push(unavailableReference(command.ref_id));
2278
+ continue;
2279
+ }
2280
+ const cached = findSnapshotByUrl(state.session, source.result.url);
2281
+ if (cached) {
2282
+ state.output.push(renderSnapshot(cached, command.lineno));
2283
+ includeResult(state, source);
2284
+ continue;
2285
+ }
2286
+ queuePage(state, "open", source, { lineno: command.lineno });
2287
+ }
2288
+ }
2289
+ function processClickCommands(commands, state) {
2290
+ for (const command of commands.click ?? []) {
2291
+ const source = getSnapshot(state.session, command.ref_id)?.links[command.id];
2292
+ if (!source) {
2293
+ state.output.push(unavailableReference(`${command.ref_id} link ${command.id}`));
2294
+ continue;
2295
+ }
2296
+ const cached = findSnapshotByUrl(state.session, source.result.url);
2297
+ if (cached) {
2298
+ state.output.push(renderSnapshot(cached));
2299
+ includeResult(state, source);
2300
+ continue;
2301
+ }
2302
+ queuePage(state, "open", source);
2303
+ }
2304
+ }
2305
+ function processFindCommands(commands, state) {
2306
+ for (const command of commands.find ?? []) {
2307
+ const directSnapshot = getSnapshot(state.session, command.ref_id);
2308
+ const source = directSnapshot?.source ?? resolveUrlReference(state.session, command.ref_id, state.turn);
2309
+ if (!source) {
2310
+ state.output.push(unavailableReference(command.ref_id));
2311
+ continue;
2312
+ }
2313
+ const snapshot = directSnapshot ?? findSnapshotByUrl(state.session, source.result.url);
2314
+ const localResult = snapshot ? findInSnapshot(snapshot, command.pattern) : null;
2315
+ if (localResult) {
2316
+ state.output.push(localResult);
2317
+ includeResult(state, source);
2318
+ continue;
2319
+ }
2320
+ queuePage(state, "find", source, { pattern: command.pattern });
2321
+ }
2322
+ }
2323
+ function processStructuredCommands(commands, state, now) {
2324
+ for (const command of commands.finance ?? []) state.remoteOperations.push({
2325
+ ...command,
2326
+ operation: "finance"
2327
+ });
2328
+ for (const command of commands.weather ?? []) state.remoteOperations.push({
2329
+ ...command,
2330
+ operation: "weather"
2331
+ });
2332
+ for (const command of commands.sports ?? []) state.remoteOperations.push({
2333
+ ...command,
2334
+ operation: "sports"
2335
+ });
2336
+ for (const command of commands.time ?? []) state.output.push(formatTime(command.utc_offset, now));
2337
+ }
2338
+ function processUnsupportedCommands(commands, state) {
2339
+ 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.`);
2340
+ }
2341
+ function processCommandOperations(commands, state, now) {
2342
+ processSearchCommands(commands, state);
2343
+ if ((commands.image_query?.length ?? 0) > 0) state.warnings.push(IMAGE_UNSUPPORTED);
2344
+ processOpenCommands(commands, state);
2345
+ processClickCommands(commands, state);
2346
+ processFindCommands(commands, state);
2347
+ if ((commands.screenshot?.length ?? 0) > 0) state.warnings.push(SCREENSHOT_UNSUPPORTED);
2348
+ processStructuredCommands(commands, state, now);
2349
+ processUnsupportedCommands(commands, state);
2350
+ }
2351
+ function supportsLiveAccess(request) {
2352
+ const externalWebAccess = request.settings?.external_web_access;
2353
+ return externalWebAccess === void 0 || externalWebAccess === true || externalWebAccess === "live";
2354
+ }
2355
+ function addLiveAccessWarning(request, state) {
2356
+ if (state.remoteOperations.length === 0 || supportsLiveAccess(request)) return;
2357
+ 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.`);
2358
+ }
2359
+ function shouldExecuteRemoteOperations(request, state) {
2360
+ return state.remoteOperations.length > 0 && supportsLiveAccess(request);
2361
+ }
2362
+ async function resolveRemoteModel(c, request, provider) {
2363
+ const model = provider ? request.model : alphaSearchResponsesDependencies.resolveMappedModel(request.model);
2364
+ if (provider) {
2365
+ const providerConfig = await resolveProviderConfig(provider);
2366
+ if (!providerConfig) return c.json({ error: {
2367
+ message: `Provider '${provider}' not found or disabled`,
2368
+ type: "invalid_request_error"
2369
+ } }, 404);
2370
+ if (resolveEffectiveProviderType(providerConfig, model) !== "openai-responses") return invalidRequest$1(c, `Provider '${provider}' does not support the /v1/responses endpoint required for alpha search`);
2371
+ return {
2372
+ model,
2373
+ providerConfig
2374
+ };
2375
+ }
2376
+ 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`);
2377
+ return { model };
2378
+ }
2379
+ function createRemoteResponsesPayload(request, model, instruction) {
2380
+ return {
2381
+ model,
2382
+ input: instruction,
2383
+ tools: [buildResponsesWebSearchTool({
2384
+ allowedDomains: request.settings?.filters?.allowed_domains,
2385
+ blockedDomains: request.settings?.filters?.blocked_domains,
2386
+ userLocation: request.settings?.user_location,
2387
+ searchContextSize: request.settings?.search_context_size
2388
+ })],
2389
+ tool_choice: "required",
2390
+ store: false,
2391
+ stream: false,
2392
+ include: ["web_search_call.action.sources"],
2393
+ reasoning: request.reasoning,
2394
+ max_output_tokens: request.max_output_tokens
2395
+ };
2396
+ }
2397
+ async function requestProviderSearch(c, providerConfig, payload, model, sessionId) {
2398
+ debugJson(logger$15, "Alpha search provider Responses request:", {
2399
+ payload,
2400
+ provider: providerConfig.name
2401
+ });
2402
+ const upstreamResponse = await forwardProviderResponses(providerConfig, payload, c.req.raw.headers);
2403
+ if (!upstreamResponse.ok) throw new HTTPError(`Failed to create ${providerConfig.name} responses for alpha search`, upstreamResponse);
2404
+ const result = await upstreamResponse.json();
2405
+ debugJson(logger$15, "Alpha search provider Responses result:", {
2406
+ provider: providerConfig.name,
2407
+ result
2408
+ });
2409
+ createProviderTokenUsageRecorder({
2410
+ endpoint: "responses",
2411
+ fallbackSessionId: sessionId,
2412
+ model,
2413
+ pricing: providerConfig.models?.[model]?.pricing,
2414
+ pricingCurrency: providerConfig.pricingCurrency,
2415
+ providerName: providerConfig.name,
2416
+ sessionId
2417
+ })(normalizeResponsesUsage(result.usage));
2418
+ return result;
2419
+ }
2420
+ async function requestCopilotSearch(payload, model, requestId, sessionId) {
2421
+ debugJson(logger$15, "Alpha search Copilot Responses request:", payload);
2422
+ const result = await alphaSearchResponsesDependencies.createResponses(payload, {
2423
+ vision: false,
2424
+ initiator: "agent",
2425
+ transport: "http",
2426
+ requestId,
2427
+ sessionId
2428
+ });
2429
+ debugJson(logger$15, "Alpha search Copilot Responses result:", result);
2430
+ alphaSearchResponsesDependencies.createUsageRecorder(model, sessionId)({
2431
+ ...normalizeResponsesUsage(result.usage),
2432
+ total_nano_aiu: normalizeOptionalToken(result.copilot_usage?.total_nano_aiu)
2433
+ });
2434
+ return result;
2435
+ }
2436
+ async function requestRemoteSearch(c, request, state, target) {
2437
+ const instruction = buildInstruction(state.remoteOperations, request.commands?.response_length);
2438
+ const sessionId = getUUID(request.id);
2439
+ const requestId = generateRequestIdFromPayload({ messages: `${state.turn.number}:${instruction}` }, sessionId);
2440
+ const payload = createRemoteResponsesPayload(request, target.model, instruction);
2441
+ if (target.providerConfig) return await requestProviderSearch(c, target.providerConfig, payload, target.model, sessionId);
2442
+ return await requestCopilotSearch(payload, target.model, requestId, sessionId);
2443
+ }
2444
+ function getActiveRemoteReferences(session, references) {
2445
+ return references.filter((reference) => session.referencesById.get(reference.result.ref_id) === reference);
2446
+ }
2447
+ function buildSnapshotLinks(state, target, markdownReferences, remoteReferences) {
2448
+ 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()];
2449
+ }
2450
+ function processRemotePages(state, answerText, markdownReferences, remoteReferences) {
2451
+ for (const [index, page] of state.remotePages.entries()) {
2452
+ const target = addUrlReference(state.session, {
2453
+ url: page.source.result.url,
2454
+ title: page.source.result.title,
2455
+ snippet: answerText
2456
+ }, state.turn) ?? page.source;
2457
+ includeResult(state, target);
2458
+ const snapshotLinks = buildSnapshotLinks(state, target, markdownReferences, remoteReferences);
2459
+ if (page.kind === "find") {
2460
+ state.output.push(`Find results for ${JSON.stringify(page.pattern ?? "")} in ${target.result.url}:\n${answerText}`);
2461
+ if (!findSnapshotByUrl(state.session, target.result.url)) addSnapshot(state.session, target, state.turn.number, index, answerText, snapshotLinks);
2462
+ continue;
2463
+ }
2464
+ const snapshot = addSnapshot(state.session, target, state.turn.number, index, answerText, snapshotLinks);
2465
+ state.output.push(renderSnapshot(snapshot, page.lineno));
2466
+ }
2467
+ }
2468
+ function appendActiveSources(state, references) {
2469
+ if (references.length === 0) return;
2470
+ state.output.push(["Sources:", ...references.map((reference) => `- [${reference.result.ref_id}] ${reference.result.title} — ${reference.result.url}`)].join("\n"));
2471
+ }
2472
+ function processRemoteResult(result, state) {
2473
+ const extracted = extractWebSearchResult(result);
2474
+ const citedSources = extracted.sources.filter((source) => source.snippet !== void 0);
2475
+ const relevantSources = citedSources.length > 0 ? citedSources : extracted.sources;
2476
+ consola.log(`--> web search: operations=${[...new Set(state.remoteOperations.map((remoteOperation) => remoteOperation.operation))].join(",")} queries=${JSON.stringify(extracted.queries)} sources=${relevantSources.length}`);
2477
+ const remoteReferences = relevantSources.map((source) => addUrlReference(state.session, source, state.turn)).filter((reference) => Boolean(reference));
2478
+ for (const reference of getActiveRemoteReferences(state.session, remoteReferences)) includeResult(state, reference);
2479
+ const answerText = extracted.answerText || "GitHub Copilot web search returned no text.";
2480
+ const markdownReferences = state.remotePages.length === 0 ? [] : extractMarkdownSources(answerText).map((source) => addUrlReference(state.session, source, state.turn)).filter((reference) => Boolean(reference));
2481
+ for (const reference of markdownReferences) includeResult(state, reference);
2482
+ if (state.remotePages.length === 0) state.output.push(answerText);
2483
+ processRemotePages(state, answerText, markdownReferences, remoteReferences);
2484
+ appendActiveSources(state, getActiveRemoteReferences(state.session, remoteReferences));
2485
+ }
2486
+ async function executeRemoteOperations(c, request, state, provider) {
2487
+ const target = await resolveRemoteModel(c, request, provider);
2488
+ if (target instanceof Response) return target;
2489
+ processRemoteResult(await requestRemoteSearch(c, request, state, target), state);
2490
+ return null;
2491
+ }
2492
+ function buildAlphaSearchResponse(state) {
2493
+ state.output.push(...state.warnings);
2494
+ if (state.output.length === 0) state.output.push("No supported search operations were requested.");
2495
+ return {
2496
+ encrypted_output: null,
2497
+ output: state.output.join("\n\n"),
2498
+ results: [...state.resultReferences.values()].filter((reference) => state.session.referencesById.get(reference.result.ref_id) === reference).map(({ result }) => result)
2499
+ };
2500
+ }
2501
+ async function handleAlphaSearchResponses(c, options) {
2502
+ const parsed = alphaSearchRequestSchema.safeParse(options.request);
2503
+ if (!parsed.success) {
2504
+ const issue = parsed.error.issues[0];
2505
+ return invalidRequest$1(c, `Invalid alpha search request at ${issue?.path.join(".") || "body"}: ${issue?.message ?? "invalid value"}`);
2506
+ }
2507
+ const request = parsed.data;
2508
+ const now = alphaSearchResponsesDependencies.now();
2509
+ const reservation = reserveSession(request.id, now);
2510
+ const session = reservation.session;
2511
+ const state = createSearchOperationState(session, {
2512
+ number: reservation.turn,
2513
+ nextReferenceIndex: 0
2514
+ });
2515
+ processCommandOperations(request.commands ?? {}, state, now);
2516
+ addLiveAccessWarning(request, state);
2517
+ if (shouldExecuteRemoteOperations(request, state)) {
2518
+ const remoteResponse = await executeRemoteOperations(c, request, state, options.provider);
2519
+ if (remoteResponse) return remoteResponse;
2520
+ }
2521
+ return c.json(buildAlphaSearchResponse(state));
2522
+ }
2523
+ //#endregion
2524
+ //#region src/services/codex/alpha-search.ts
2525
+ const CODEX_ALPHA_SEARCH_URL = `${CODEX_API_BASE_URL}/codex/alpha/search`;
2526
+ function resolveCodexAlphaSearchUrl(requestUrl) {
2527
+ const upstreamUrl = new URL(CODEX_ALPHA_SEARCH_URL);
2528
+ upstreamUrl.search = new URL(requestUrl, "http://localhost").search;
2529
+ return upstreamUrl.toString();
2530
+ }
2531
+ async function forwardCodexAlphaSearch(request) {
2532
+ const headers = buildCodexRequestHeaders(request.headers);
2533
+ if (!headers.has("accept")) headers.set("accept", "application/json");
2534
+ const body = await request.arrayBuffer();
2535
+ if (!headers.has("content-type")) headers.set("content-type", "application/json");
2536
+ return await fetch(resolveCodexAlphaSearchUrl(request.url), {
2537
+ method: "POST",
2538
+ headers,
2539
+ body
2540
+ });
2541
+ }
2542
+ //#endregion
2543
+ //#region src/routes/alpha-search/route.ts
2544
+ const logger$14 = createHandlerLogger("alpha-search-handler");
2545
+ const alphaSearchRoutes = new Hono();
2546
+ function parseDebugBody(body) {
2547
+ try {
2548
+ return JSON.parse(body);
2549
+ } catch {
2550
+ return body;
2551
+ }
2552
+ }
2553
+ async function forwardCodexAlphaSearchRequest(request) {
2554
+ await debugJsonAsync(logger$14, "alpha_search.codex.request", async () => ({ body: parseDebugBody(await request.clone().text()) }));
2555
+ const upstreamResponse = await forwardCodexAlphaSearch(request);
2556
+ await debugJsonAsync(logger$14, "alpha_search.codex.response", async () => ({
2557
+ body: parseDebugBody(await upstreamResponse.clone().text()),
2558
+ statusCode: upstreamResponse.status
2559
+ }));
2560
+ return createProviderProxyResponse(upstreamResponse);
2561
+ }
2562
+ function createAlphaSearchRequest(request, payload) {
2563
+ return new Request(request, {
2564
+ body: JSON.stringify(payload),
2565
+ method: "post"
2566
+ });
2567
+ }
2568
+ async function handleCodexRequest(c, request, resolvedProviderConfig) {
2569
+ if (!(resolvedProviderConfig ?? await resolveProviderConfig("codex"))) return c.json({ error: {
2570
+ message: "Provider 'codex' not found or disabled",
2571
+ type: "invalid_request_error"
2572
+ } }, 404);
2573
+ return await forwardCodexAlphaSearchRequest(request);
2574
+ }
2575
+ function invalidRequest(c, message) {
2576
+ return c.json({ error: {
2577
+ message,
2578
+ type: "invalid_request_error"
2579
+ } }, 400);
2580
+ }
2581
+ async function parseAlphaSearchBody(c) {
2582
+ let body;
2583
+ try {
2584
+ body = await c.req.raw.clone().json();
2585
+ } catch {
2586
+ return invalidRequest(c, "Invalid alpha search request: expected JSON body");
2587
+ }
2588
+ if (typeof body?.model !== "string") return invalidRequest(c, "Invalid alpha search request: model must be a string");
2589
+ return body;
2590
+ }
2591
+ /**
2592
+ * Handles top-level alpha-search dispatch. Pass `resolvedProviderConfig` when
2593
+ * the provider-scoped route has already resolved Codex. Codex is preferred
2594
+ * for every top-level request while alphaSearchCodexPriority stays enabled.
2595
+ */
2596
+ async function handleAlphaSearchRequest(c, resolvedProviderConfig) {
2597
+ if (resolvedProviderConfig) return await handleCodexRequest(c, c.req.raw, resolvedProviderConfig);
2598
+ const payload = await parseAlphaSearchBody(c);
2599
+ if (payload instanceof Response) return payload;
2600
+ const requestedModel = payload.model;
2601
+ payload.model = resolveMappedModel(requestedModel);
2602
+ if (payload.model !== requestedModel) consola.debug(`Resolved model mapping: ${requestedModel} -> ${payload.model}`);
2603
+ const providerModelAlias = parseProviderModelAlias(payload.model);
2604
+ if (providerModelAlias) {
2605
+ payload.model = providerModelAlias.model;
2606
+ if (providerModelAlias.provider === "codex") return await handleCodexRequest(c, createAlphaSearchRequest(c.req.raw, payload));
2607
+ }
2608
+ if (isAlphaSearchCodexPriorityEnabled()) {
2609
+ if (await resolveProviderConfig("codex")) return await forwardCodexAlphaSearchRequest(createAlphaSearchRequest(c.req.raw, payload));
2610
+ }
2611
+ if (providerModelAlias) return await handleAlphaSearchResponses(c, {
2612
+ provider: providerModelAlias.provider,
2613
+ request: payload
2614
+ });
2615
+ return await handleAlphaSearchResponses(c, { request: {
2616
+ ...payload,
2617
+ model: requestedModel
2618
+ } });
2619
+ }
2620
+ alphaSearchRoutes.post("/", async (c) => {
2621
+ try {
2622
+ return await handleAlphaSearchRequest(c);
2623
+ } catch (error) {
2624
+ logger$14.error("alpha_search.error", { error });
2625
+ return await forwardError(c, error);
2626
+ }
2627
+ });
2628
+ //#endregion
2629
+ //#region src/lib/dashscope.ts
2630
+ const OPENAI_COMPATIBLE_CONTEXT_CACHE_MARKER_LIMIT = 4;
2631
+ const OPENAI_COMPATIBLE_CONTEXT_CACHE_CONTROL = { type: "ephemeral" };
2632
+ const OPENAI_COMPATIBLE_CONTEXT_CACHE_ROLES = new Set([
2633
+ "system",
2634
+ "user",
2635
+ "assistant",
2636
+ "tool"
2637
+ ]);
2638
+ const isDashScopeAliyunProvider = (providerConfig) => providerConfig.name === "dashscope" || providerConfig.baseUrl.includes("aliyuncs.com");
2639
+ const applyDashScopePreserveThinkingDefault = (payload, providerConfig) => {
2640
+ if (!isDashScopeAliyunProvider(providerConfig)) return;
2641
+ if (!Object.hasOwn(payload, "preserve_thinking")) payload.preserve_thinking = true;
2642
+ };
2643
+ const applyOpenAICompatibleContextCache = (payload) => {
2644
+ const messageIndexes = selectContextCacheMessageIndexes(payload.messages);
2645
+ for (const messageIndex of messageIndexes) applyContextCacheControl(payload.messages[messageIndex]);
2646
+ };
2647
+ const selectContextCacheMessageIndexes = (messages) => {
2648
+ const cacheableIndexes = messages.flatMap((message, index) => isContextCacheMarkerEligible(message) ? [index] : []);
2649
+ const systemIndexes = cacheableIndexes.filter((index) => messages[index]?.role === "system").slice(0, 2);
2650
+ const finalIndexes = cacheableIndexes.filter((index) => messages[index]?.role !== "system").slice(-1);
2651
+ return uniqueIndexes$1([...systemIndexes, ...finalIndexes]).sort((a, b) => a - b);
2652
+ };
2653
+ const uniqueIndexes$1 = (indexes) => [...new Set(indexes)].slice(0, OPENAI_COMPATIBLE_CONTEXT_CACHE_MARKER_LIMIT);
2654
+ const isContextCacheMarkerEligible = (message) => {
2655
+ if (!OPENAI_COMPATIBLE_CONTEXT_CACHE_ROLES.has(message.role)) return false;
2656
+ if (typeof message.content === "string") return message.content.length > 0;
1702
2657
  return Array.isArray(message.content) && message.content.length > 0;
1703
2658
  };
1704
2659
  const applyContextCacheControl = (message) => {
@@ -1767,123 +2722,50 @@ const applyMissingExtraBody$1 = (payload, options) => {
1767
2722
  for (const [key, value] of Object.entries(options.extraBody ?? {})) if (!Object.hasOwn(payload, key)) payload[key] = value;
1768
2723
  };
1769
2724
  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
2725
+ if (!payload.stream) return;
2726
+ payload.stream_options = {
2727
+ ...payload.stream_options ?? {},
2728
+ include_usage: true
1862
2729
  };
1863
2730
  };
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
- }
2731
+ const applyProviderContextCache = (payload, modelConfig, providerConfig) => {
2732
+ const isDashScopeProvider = isDashScopeAliyunProvider(providerConfig);
2733
+ if (modelConfig?.contextCache ?? isDashScopeProvider) applyOpenAICompatibleContextCache(payload);
1877
2734
  };
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}`);
2735
+ const createProviderChatCompletionsUsageRecorder = (payload, provider, modelConfig, pricingCurrency) => createProviderTokenUsageRecorder({
2736
+ endpoint: "chat_completions",
2737
+ model: payload.model,
2738
+ pricing: modelConfig?.pricing,
2739
+ pricingCurrency,
2740
+ providerName: provider
2741
+ });
2742
+ const streamProviderChatCompletions = (c, upstreamResponse, options) => {
2743
+ logger$13.debug("provider.chat_completions.streaming", { provider: options.provider });
2744
+ return streamSSE(c, async (stream) => {
2745
+ let usage = {};
2746
+ try {
2747
+ for await (const chunk of events(upstreamResponse)) {
2748
+ debugJson(logger$13, "provider.chat_completions.stream_chunk", chunk);
2749
+ if (chunk.data && chunk.data !== "[DONE]") {
2750
+ const parsedChunk = parseChatCompletionChunkData(chunk.data);
2751
+ if (parsedChunk?.usage) usage = normalizeOpenAIUsage(parsedChunk.usage);
2752
+ }
2753
+ await stream.writeSSE({
2754
+ event: chunk.event,
2755
+ data: chunk.data ?? ""
2756
+ });
2757
+ }
2758
+ } finally {
2759
+ options.recordUsage(usage);
2760
+ }
2761
+ });
1882
2762
  };
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";
2763
+ const parseChatCompletionChunkData = (data) => {
2764
+ try {
2765
+ return JSON.parse(data);
2766
+ } catch {
2767
+ return null;
2768
+ }
1887
2769
  };
1888
2770
  //#endregion
1889
2771
  //#region src/services/copilot/create-chat-completions.ts
@@ -3579,143 +4461,6 @@ function closeThinkingBlockIfOpen(state, events) {
3579
4461
  }
3580
4462
  }
3581
4463
  //#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
4464
  //#region src/routes/messages/responses-translation.ts
3720
4465
  const MESSAGE_TYPE = "message";
3721
4466
  const COMPACTION_SIGNATURE_PREFIX = "cm1#";
@@ -5037,72 +5782,6 @@ const containsVisionContent = (value) => {
5037
5782
  return false;
5038
5783
  };
5039
5784
  //#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
5785
  //#region src/routes/messages/web-search/fulfill.ts
5107
5786
  const webSearchFlowDependencies = {
5108
5787
  createResponses,
@@ -6686,7 +7365,7 @@ providerAlphaSearchRoutes.post("/", async (c) => {
6686
7365
  message: `Provider '${provider}' not found or disabled`,
6687
7366
  type: "invalid_request_error"
6688
7367
  } }, 404);
6689
- if (providerConfig.name === "codex") return await handleCodexAlphaSearch(c, providerConfig);
7368
+ if (providerConfig.name === "codex") return await handleAlphaSearchRequest(c, providerConfig);
6690
7369
  await debugJsonAsync(logger$5, "provider.alpha_search.request", async () => ({
6691
7370
  body: await c.req.raw.clone().text(),
6692
7371
  provider
@@ -7259,4 +7938,4 @@ server.route("/:provider/images", providerImageRoutes);
7259
7938
  //#endregion
7260
7939
  export { server };
7261
7940
 
7262
- //# sourceMappingURL=server-D8An7dpg.js.map
7941
+ //# sourceMappingURL=server-DZtBoW6V.js.map