@ashsec/copilot-api 0.7.13 → 0.9.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.
Files changed (3) hide show
  1. package/dist/main.js +1622 -387
  2. package/dist/main.js.map +1 -1
  3. package/package.json +69 -68
package/dist/main.js CHANGED
@@ -5,10 +5,10 @@ import fs from "node:fs/promises";
5
5
  import os from "node:os";
6
6
  import path from "node:path";
7
7
  import { randomUUID } from "node:crypto";
8
- import { events } from "fetch-event-stream";
9
8
  import clipboard from "clipboardy";
10
9
  import { serve } from "srvx";
11
10
  import invariant from "tiny-invariant";
11
+ import fs$1 from "node:fs";
12
12
  import { getProxyForUrl } from "proxy-from-env";
13
13
  import { Agent, ProxyAgent, setGlobalDispatcher } from "undici";
14
14
  import { execSync } from "node:child_process";
@@ -16,10 +16,12 @@ import process$1 from "node:process";
16
16
  import { Hono } from "hono";
17
17
  import { cors } from "hono/cors";
18
18
  import { streamSSE } from "hono/streaming";
19
+ import { events } from "fetch-event-stream";
20
+ import util from "node:util";
19
21
 
20
22
  //#region package.json
21
23
  var name = "@ashsec/copilot-api";
22
- var version = "0.7.13";
24
+ var version = "0.9.0";
23
25
  var description = "Turn GitHub Copilot into OpenAI/Anthropic API compatible server. Usable with Claude Code!";
24
26
  var keywords = [
25
27
  "proxy",
@@ -57,6 +59,7 @@ var dependencies = {
57
59
  "fetch-event-stream": "^0.1.5",
58
60
  "gpt-tokenizer": "^3.0.1",
59
61
  "hono": "^4.9.9",
62
+ "ms": "^2.1.3",
60
63
  "proxy-from-env": "^1.1.0",
61
64
  "srvx": "^0.8.9",
62
65
  "tiny-invariant": "^1.3.3",
@@ -99,18 +102,16 @@ var package_default = {
99
102
  //#region src/lib/paths.ts
100
103
  const APP_DIR = path.join(os.homedir(), ".local", "share", "copilot-api");
101
104
  const GITHUB_TOKEN_PATH = path.join(APP_DIR, "github_token");
102
- const AZURE_OPENAI_CONFIG_PATH = path.join(APP_DIR, "azure_openai_config");
103
105
  const REPLACEMENTS_CONFIG_PATH = path.join(APP_DIR, "replacements.json");
104
106
  const PATHS = {
105
107
  APP_DIR,
108
+ CONFIG_PATH: path.join(APP_DIR, "config.json"),
106
109
  GITHUB_TOKEN_PATH,
107
- AZURE_OPENAI_CONFIG_PATH,
108
110
  REPLACEMENTS_CONFIG_PATH
109
111
  };
110
112
  async function ensurePaths() {
111
113
  await fs.mkdir(PATHS.APP_DIR, { recursive: true });
112
114
  await ensureFile(PATHS.GITHUB_TOKEN_PATH);
113
- await ensureFile(PATHS.AZURE_OPENAI_CONFIG_PATH);
114
115
  }
115
116
  async function ensureFile(filePath) {
116
117
  try {
@@ -128,7 +129,8 @@ const state = {
128
129
  manualApprove: false,
129
130
  rateLimitWait: false,
130
131
  showToken: false,
131
- debug: false
132
+ debug: false,
133
+ verbose: false
132
134
  };
133
135
 
134
136
  //#endregion
@@ -137,10 +139,10 @@ const standardHeaders = () => ({
137
139
  "content-type": "application/json",
138
140
  accept: "application/json"
139
141
  });
140
- const COPILOT_VERSION = "0.26.7";
142
+ const COPILOT_VERSION = "0.37.6";
141
143
  const EDITOR_PLUGIN_VERSION = `copilot-chat/${COPILOT_VERSION}`;
142
144
  const USER_AGENT = `GitHubCopilotChat/${COPILOT_VERSION}`;
143
- const API_VERSION = "2025-04-01";
145
+ const API_VERSION = "2025-10-01";
144
146
  const copilotBaseUrl = (state$1) => state$1.accountType === "individual" ? "https://api.githubcopilot.com" : `https://api.${state$1.accountType}.githubcopilot.com`;
145
147
  const copilotHeaders = (state$1, vision = false) => {
146
148
  const headers = {
@@ -150,7 +152,7 @@ const copilotHeaders = (state$1, vision = false) => {
150
152
  "editor-version": `vscode/${state$1.vsCodeVersion}`,
151
153
  "editor-plugin-version": EDITOR_PLUGIN_VERSION,
152
154
  "user-agent": USER_AGENT,
153
- "openai-intent": "conversation-panel",
155
+ "openai-intent": "conversation-agent",
154
156
  "x-github-api-version": API_VERSION,
155
157
  "x-request-id": randomUUID(),
156
158
  "x-vscode-user-agent-library-version": "electron-fetch"
@@ -251,56 +253,6 @@ async function getGitHubUser() {
251
253
  return await response.json();
252
254
  }
253
255
 
254
- //#endregion
255
- //#region src/services/azure-openai/config.ts
256
- const AZURE_OPENAI_MODEL_PREFIX = "azure_openai_";
257
- async function loadAzureOpenAIConfig() {
258
- try {
259
- const content = await fs.readFile(PATHS.AZURE_OPENAI_CONFIG_PATH, "utf8");
260
- if (!content.trim()) return null;
261
- const decoded = Buffer.from(content.trim(), "base64").toString("utf8");
262
- const config$1 = JSON.parse(decoded);
263
- if (!config$1.endpoint || !config$1.apiKey) return null;
264
- return config$1;
265
- } catch {
266
- return null;
267
- }
268
- }
269
- async function saveAzureOpenAIConfig(config$1) {
270
- const encoded = Buffer.from(JSON.stringify(config$1)).toString("base64");
271
- await fs.writeFile(PATHS.AZURE_OPENAI_CONFIG_PATH, encoded, "utf8");
272
- await fs.chmod(PATHS.AZURE_OPENAI_CONFIG_PATH, 384);
273
- consola.success("Azure OpenAI configuration saved");
274
- }
275
- async function promptAzureOpenAISetup() {
276
- if (!await consola.prompt("Would you like to add a custom Azure OpenAI endpoint?", {
277
- type: "confirm",
278
- initial: false
279
- })) return null;
280
- const endpoint = await consola.prompt("Enter your Azure OpenAI endpoint URL (e.g., https://your-resource.openai.azure.com):", { type: "text" });
281
- if (!endpoint || typeof endpoint !== "string" || !endpoint.trim()) {
282
- consola.warn("No endpoint provided, skipping Azure OpenAI setup");
283
- return null;
284
- }
285
- const apiKey = await consola.prompt("Enter your Azure OpenAI API key:", { type: "text" });
286
- if (!apiKey || typeof apiKey !== "string" || !apiKey.trim()) {
287
- consola.warn("No API key provided, skipping Azure OpenAI setup");
288
- return null;
289
- }
290
- const config$1 = {
291
- endpoint: endpoint.trim().replace(/\/$/, ""),
292
- apiKey: apiKey.trim()
293
- };
294
- await saveAzureOpenAIConfig(config$1);
295
- return config$1;
296
- }
297
- function isAzureOpenAIModel(modelId) {
298
- return modelId.startsWith(AZURE_OPENAI_MODEL_PREFIX);
299
- }
300
- function getAzureDeploymentName(modelId) {
301
- return modelId.slice(13);
302
- }
303
-
304
256
  //#endregion
305
257
  //#region src/lib/retry-fetch.ts
306
258
  const RETRY_DELAYS_MS = [
@@ -370,62 +322,6 @@ async function fetchWithRetry(input, init) {
370
322
  throw lastError;
371
323
  }
372
324
 
373
- //#endregion
374
- //#region src/services/azure-openai/create-chat-completions.ts
375
- const AZURE_API_VERSION = "2024-10-21";
376
- async function createAzureOpenAIChatCompletions(config$1, payload) {
377
- const deploymentName = getAzureDeploymentName(payload.model);
378
- const { max_tokens,...restPayload } = payload;
379
- const azurePayload = {
380
- ...restPayload,
381
- model: deploymentName,
382
- ...max_tokens != null && { max_completion_tokens: max_tokens }
383
- };
384
- const response = await fetchWithRetry(`${config$1.endpoint}/openai/deployments/${deploymentName}/chat/completions?api-version=${AZURE_API_VERSION}`, {
385
- method: "POST",
386
- headers: {
387
- "api-key": config$1.apiKey,
388
- "Content-Type": "application/json"
389
- },
390
- body: JSON.stringify(azurePayload)
391
- });
392
- if (!response.ok) {
393
- consola.error("Failed to create Azure OpenAI chat completions:", response);
394
- throw new HTTPError("Failed to create Azure OpenAI chat completions", response, payload);
395
- }
396
- if (payload.stream) return events(response);
397
- return await response.json();
398
- }
399
-
400
- //#endregion
401
- //#region src/services/azure-openai/get-models.ts
402
- const AZURE_DEPLOYMENTS_API_VERSION = "2022-12-01";
403
- async function getAzureOpenAIDeployments(config$1) {
404
- try {
405
- const response = await fetchWithRetry(`${config$1.endpoint}/openai/deployments?api-version=${AZURE_DEPLOYMENTS_API_VERSION}`, { headers: {
406
- "api-key": config$1.apiKey,
407
- "Content-Type": "application/json"
408
- } });
409
- if (!response.ok) {
410
- const errorText = await response.text().catch(() => "");
411
- consola.error(`Failed to fetch Azure OpenAI deployments: ${response.status}`, errorText);
412
- throw new HTTPError("Failed to fetch Azure OpenAI deployments", response);
413
- }
414
- return (await response.json()).data.filter((deployment) => deployment.status === "succeeded").map((deployment) => ({
415
- id: `${AZURE_OPENAI_MODEL_PREFIX}${deployment.id}`,
416
- deploymentName: deployment.id,
417
- model: deployment.model,
418
- created: deployment.created_at,
419
- object: "deployment",
420
- owned_by: deployment.owner || "azure-openai"
421
- }));
422
- } catch (error) {
423
- if (error instanceof HTTPError) throw error;
424
- consola.error("Failed to fetch Azure OpenAI deployments:", error);
425
- return [];
426
- }
427
- }
428
-
429
325
  //#endregion
430
326
  //#region src/services/copilot/get-models.ts
431
327
  const getModels = async () => {
@@ -489,24 +385,6 @@ const cacheVSCodeVersion = async () => {
489
385
  state.vsCodeVersion = response;
490
386
  consola.info(`Using VSCode version: ${response}`);
491
387
  };
492
- async function setupAzureOpenAI() {
493
- let config$1 = await loadAzureOpenAIConfig();
494
- if (!config$1) config$1 = await promptAzureOpenAISetup();
495
- if (!config$1) {
496
- consola.info("Azure OpenAI not configured");
497
- return;
498
- }
499
- state.azureOpenAIConfig = config$1;
500
- consola.info("Azure OpenAI configuration loaded");
501
- try {
502
- const deployments = await getAzureOpenAIDeployments(config$1);
503
- state.azureOpenAIDeployments = deployments;
504
- if (deployments.length > 0) consola.info(`Loaded ${deployments.length} Azure OpenAI deployment(s):\n${deployments.map((d) => `- ${d.id} (${d.model})`).join("\n")}`);
505
- else consola.warn("No Azure OpenAI deployments found");
506
- } catch (error) {
507
- consola.warn("Failed to fetch Azure OpenAI deployments:", error);
508
- }
509
- }
510
388
 
511
389
  //#endregion
512
390
  //#region src/services/github/poll-access-token.ts
@@ -1254,6 +1132,119 @@ const debug = defineCommand({
1254
1132
  }
1255
1133
  });
1256
1134
 
1135
+ //#endregion
1136
+ //#region src/lib/config.ts
1137
+ const gpt5ExplorationPrompt = `## Exploration and reading files
1138
+ - **Think first.** Before any tool call, decide ALL files/resources you will need.
1139
+ - **Batch everything.** If you need multiple files (even from different places), read them together.
1140
+ - **multi_tool_use.parallel** Use multi_tool_use.parallel to parallelize tool calls and only this.
1141
+ - **Only make sequential calls if you truly cannot know the next file without seeing a result first.**
1142
+ - **Workflow:** (a) plan all needed reads → (b) issue one parallel batch → (c) analyze results → (d) repeat if new, unpredictable reads arise.`;
1143
+ const gpt5CommentaryPrompt = `# Working with the user
1144
+
1145
+ You interact with the user through a terminal. You have 2 ways of communicating with the users:
1146
+ - Share intermediary updates in \`commentary\` channel.
1147
+ - After you have completed all your work, send a message to the \`final\` channel.
1148
+
1149
+ ## Intermediary updates
1150
+
1151
+ - Intermediary updates go to the \`commentary\` channel.
1152
+ - User updates are short updates while you are working, they are NOT final answers.
1153
+ - You use 1-2 sentence user updates to communicate progress and new information to the user as you are doing work.
1154
+ - Do not begin responses with conversational interjections or meta commentary. Avoid openers such as acknowledgements ("Done —", "Got it", "Great question, ") or framing phrases.
1155
+ - You provide user updates frequently, every 20s.
1156
+ - Before exploring or doing substantial work, you start with a user update acknowledging the request and explaining your first step. You should include your understanding of the user request and explain what you will do. Avoid commenting on the request or using starters such as "Got it -" or "Understood -" etc.
1157
+ - When exploring, e.g. searching, reading files, you provide user updates as you go, every 20s, explaining what context you are gathering and what you've learned. Vary your sentence structure when providing these updates to avoid sounding repetitive - in particular, don't start each sentence the same way.
1158
+ - After you have sufficient context, and the work is substantial, you provide a longer plan (this is the only user update that may be longer than 2 sentences and can contain formatting).
1159
+ - Before performing file edits of any kind, you provide updates explaining what edits you are making.
1160
+ - As you are thinking, you very frequently provide updates even if not taking any actions, informing the user of your progress. You interrupt your thinking and send multiple updates in a row if thinking for more than 100 words.
1161
+ - Tone of your updates MUST match your personality.`;
1162
+ const defaultConfig = {
1163
+ auth: { apiKeys: [] },
1164
+ extraPrompts: {
1165
+ "gpt-5-mini": gpt5ExplorationPrompt,
1166
+ "gpt-5.1-codex-max": gpt5ExplorationPrompt,
1167
+ "gpt-5.3-codex": gpt5CommentaryPrompt
1168
+ },
1169
+ smallModel: "gpt-5-mini",
1170
+ modelReasoningEfforts: { "gpt-5-mini": "low" },
1171
+ useFunctionApplyPatch: true,
1172
+ compactUseSmallModel: true
1173
+ };
1174
+ let cachedConfig = null;
1175
+ function ensureConfigFile() {
1176
+ try {
1177
+ fs$1.accessSync(PATHS.CONFIG_PATH, fs$1.constants.R_OK | fs$1.constants.W_OK);
1178
+ } catch {
1179
+ fs$1.mkdirSync(PATHS.APP_DIR, { recursive: true });
1180
+ fs$1.writeFileSync(PATHS.CONFIG_PATH, `${JSON.stringify(defaultConfig, null, 2)}\n`, "utf8");
1181
+ try {
1182
+ fs$1.chmodSync(PATHS.CONFIG_PATH, 384);
1183
+ } catch {
1184
+ return;
1185
+ }
1186
+ }
1187
+ }
1188
+ function readConfigFromDisk() {
1189
+ ensureConfigFile();
1190
+ try {
1191
+ const raw = fs$1.readFileSync(PATHS.CONFIG_PATH, "utf8");
1192
+ if (!raw.trim()) {
1193
+ fs$1.writeFileSync(PATHS.CONFIG_PATH, `${JSON.stringify(defaultConfig, null, 2)}\n`, "utf8");
1194
+ return defaultConfig;
1195
+ }
1196
+ return JSON.parse(raw);
1197
+ } catch (error) {
1198
+ consola.error("Failed to read config file, using default config", error);
1199
+ return defaultConfig;
1200
+ }
1201
+ }
1202
+ function mergeDefaultExtraPrompts(config$1) {
1203
+ const extraPrompts = config$1.extraPrompts ?? {};
1204
+ const defaultExtraPrompts = defaultConfig.extraPrompts ?? {};
1205
+ if (Object.keys(defaultExtraPrompts).filter((model) => !Object.hasOwn(extraPrompts, model)).length === 0) return {
1206
+ mergedConfig: config$1,
1207
+ changed: false
1208
+ };
1209
+ return {
1210
+ mergedConfig: {
1211
+ ...config$1,
1212
+ extraPrompts: {
1213
+ ...defaultExtraPrompts,
1214
+ ...extraPrompts
1215
+ }
1216
+ },
1217
+ changed: true
1218
+ };
1219
+ }
1220
+ function mergeConfigWithDefaults() {
1221
+ const config$1 = readConfigFromDisk();
1222
+ const { mergedConfig, changed } = mergeDefaultExtraPrompts(config$1);
1223
+ if (changed) try {
1224
+ fs$1.writeFileSync(PATHS.CONFIG_PATH, `${JSON.stringify(mergedConfig, null, 2)}\n`, "utf8");
1225
+ } catch (writeError) {
1226
+ consola.warn("Failed to write merged extraPrompts to config file", writeError);
1227
+ }
1228
+ cachedConfig = mergedConfig;
1229
+ return mergedConfig;
1230
+ }
1231
+ function getConfig() {
1232
+ cachedConfig ??= readConfigFromDisk();
1233
+ return cachedConfig;
1234
+ }
1235
+ function getExtraPromptForModel(model) {
1236
+ return getConfig().extraPrompts?.[model] ?? "";
1237
+ }
1238
+ function getSmallModel() {
1239
+ return getConfig().smallModel ?? "gpt-5-mini";
1240
+ }
1241
+ function getReasoningEffortForModel(model) {
1242
+ return getConfig().modelReasoningEfforts?.[model] ?? "high";
1243
+ }
1244
+ function shouldCompactUseSmallModel() {
1245
+ return getConfig().compactUseSmallModel ?? true;
1246
+ }
1247
+
1257
1248
  //#endregion
1258
1249
  //#region src/lib/proxy.ts
1259
1250
  function initProxyFromEnv() {
@@ -1353,6 +1344,52 @@ function generateEnvScript(envVars, commandToRun = "") {
1353
1344
  return commandBlock || commandToRun;
1354
1345
  }
1355
1346
 
1347
+ //#endregion
1348
+ //#region src/lib/request-auth.ts
1349
+ function normalizeApiKeys(apiKeys) {
1350
+ if (!Array.isArray(apiKeys)) {
1351
+ if (apiKeys !== void 0) consola.warn("Invalid auth.apiKeys config. Expected an array of strings.");
1352
+ return [];
1353
+ }
1354
+ const normalizedKeys = apiKeys.filter((key) => typeof key === "string").map((key) => key.trim()).filter((key) => key.length > 0);
1355
+ if (normalizedKeys.length !== apiKeys.length) consola.warn("Invalid auth.apiKeys entries found. Only non-empty strings are allowed.");
1356
+ return [...new Set(normalizedKeys)];
1357
+ }
1358
+ function getConfiguredApiKeys() {
1359
+ const config$1 = getConfig();
1360
+ return normalizeApiKeys(config$1.auth?.apiKeys);
1361
+ }
1362
+ function extractRequestApiKey(c) {
1363
+ const xApiKey = c.req.header("x-api-key")?.trim();
1364
+ if (xApiKey) return xApiKey;
1365
+ const authorization = c.req.header("authorization");
1366
+ if (!authorization) return null;
1367
+ const [scheme, ...rest] = authorization.trim().split(/\s+/);
1368
+ if (scheme.toLowerCase() !== "bearer") return null;
1369
+ return rest.join(" ").trim() || null;
1370
+ }
1371
+ function createUnauthorizedResponse(c) {
1372
+ c.header("WWW-Authenticate", "Bearer realm=\"copilot-api\"");
1373
+ return c.json({ error: {
1374
+ message: "Unauthorized",
1375
+ type: "authentication_error"
1376
+ } }, 401);
1377
+ }
1378
+ function createAuthMiddleware(options = {}) {
1379
+ const getApiKeys = options.getApiKeys ?? getConfiguredApiKeys;
1380
+ const allowUnauthenticatedPaths = options.allowUnauthenticatedPaths ?? ["/"];
1381
+ const allowOptionsBypass = options.allowOptionsBypass ?? true;
1382
+ return async (c, next) => {
1383
+ if (allowOptionsBypass && c.req.method === "OPTIONS") return next();
1384
+ if (allowUnauthenticatedPaths.includes(c.req.path)) return next();
1385
+ const apiKeys = getApiKeys();
1386
+ if (apiKeys.length === 0) return next();
1387
+ const requestApiKey = extractRequestApiKey(c);
1388
+ if (!requestApiKey || !apiKeys.includes(requestApiKey)) return createUnauthorizedResponse(c);
1389
+ return next();
1390
+ };
1391
+ }
1392
+
1356
1393
  //#endregion
1357
1394
  //#region src/lib/request-logger.ts
1358
1395
  const REQUEST_CONTEXT_KEY = "requestContext";
@@ -1455,7 +1492,7 @@ async function requestLogger(c, next) {
1455
1492
  const durationStr = `${colors.cyan}${duration}s${colors.reset}`;
1456
1493
  lines.push(`${colors.bold}${method}${colors.reset} ${path$1} ${statusBadge} ${durationStr}`);
1457
1494
  if (ctx?.provider && ctx.model) {
1458
- const providerColor = ctx.provider === "Azure OpenAI" ? colors.blue : colors.magenta;
1495
+ const providerColor = colors.magenta;
1459
1496
  lines.push(` ${colors.gray}Provider:${colors.reset} ${providerColor}${ctx.provider}${colors.reset} ${colors.gray}->${colors.reset} ${colors.white}${ctx.model}${colors.reset}`);
1460
1497
  }
1461
1498
  if (ctx?.inputTokens !== void 0 || ctx?.outputTokens !== void 0) {
@@ -1467,14 +1504,6 @@ async function requestLogger(c, next) {
1467
1504
  lines.push(` ${colors.dim}${getTimeString()}${colors.reset}`);
1468
1505
  console.log(lines.join("\n"));
1469
1506
  }
1470
- /**
1471
- * Log token usage (for streaming responses where tokens are known after stream completes)
1472
- */
1473
- function logTokenUsage(inputTokens, outputTokens) {
1474
- const parts = [];
1475
- parts.push(` ${colors.gray}Tokens:${colors.reset} ${colors.yellow}${inputTokens.toLocaleString()} in${colors.reset} ${colors.gray}/${colors.reset} ${colors.green}${outputTokens.toLocaleString()} out${colors.reset}`);
1476
- console.log(parts.join(""));
1477
- }
1478
1507
 
1479
1508
  //#endregion
1480
1509
  //#region src/lib/approval.ts
@@ -1719,13 +1748,17 @@ const getTokenCount = async (payload, model) => {
1719
1748
 
1720
1749
  //#endregion
1721
1750
  //#region src/services/copilot/create-chat-completions.ts
1722
- const createChatCompletions = async (payload) => {
1751
+ const createChatCompletions = async (payload, options) => {
1723
1752
  if (!state.copilotToken) throw new Error("Copilot token not found");
1724
1753
  const enableVision = payload.messages.some((x) => typeof x.content !== "string" && x.content?.some((x$1) => x$1.type === "image_url"));
1725
- const isAgentCall = payload.messages.some((msg) => ["assistant", "tool"].includes(msg.role));
1754
+ let isAgentCall = false;
1755
+ if (payload.messages.length > 0) {
1756
+ const lastMessage = payload.messages.at(-1);
1757
+ if (lastMessage) isAgentCall = ["assistant", "tool"].includes(lastMessage.role);
1758
+ }
1726
1759
  const headers = {
1727
1760
  ...copilotHeaders(state, enableVision),
1728
- "X-Initiator": isAgentCall ? "agent" : "user"
1761
+ "X-Initiator": options?.initiator ?? (isAgentCall ? "agent" : "user")
1729
1762
  };
1730
1763
  const response = await fetchWithRetry(`${copilotBaseUrl(state)}/chat/completions`, {
1731
1764
  method: "POST",
@@ -1751,37 +1784,6 @@ async function handleCompletion$1(c) {
1751
1784
  model: normalizeModelName(payload.model)
1752
1785
  };
1753
1786
  consola.debug("Request payload:", JSON.stringify(payload).slice(-400));
1754
- if (isAzureOpenAIModel(payload.model)) {
1755
- if (!state.azureOpenAIConfig) return c.json({ error: "Azure OpenAI not configured" }, 500);
1756
- setRequestContext(c, {
1757
- provider: "Azure OpenAI",
1758
- model: payload.model
1759
- });
1760
- if (state.manualApprove) await awaitApproval();
1761
- const response$1 = await createAzureOpenAIChatCompletions(state.azureOpenAIConfig, payload);
1762
- if (isNonStreaming(response$1)) {
1763
- consola.debug("Non-streaming response:", JSON.stringify(response$1));
1764
- if (response$1.usage) setRequestContext(c, {
1765
- inputTokens: response$1.usage.prompt_tokens,
1766
- outputTokens: response$1.usage.completion_tokens
1767
- });
1768
- return c.json(response$1);
1769
- }
1770
- consola.debug("Streaming response");
1771
- return streamSSE(c, async (stream) => {
1772
- for await (const chunk of response$1) {
1773
- consola.debug("Streaming chunk:", JSON.stringify(chunk));
1774
- if (chunk.data && chunk.data !== "[DONE]") {
1775
- const parsed = JSON.parse(chunk.data);
1776
- if (parsed.usage) setRequestContext(c, {
1777
- inputTokens: parsed.usage.prompt_tokens,
1778
- outputTokens: parsed.usage.completion_tokens
1779
- });
1780
- }
1781
- await stream.writeSSE(chunk);
1782
- }
1783
- });
1784
- }
1785
1787
  setRequestContext(c, {
1786
1788
  provider: "Copilot",
1787
1789
  model: payload.model
@@ -1804,7 +1806,7 @@ async function handleCompletion$1(c) {
1804
1806
  consola.debug("Set max_tokens to:", JSON.stringify(payload.max_tokens));
1805
1807
  }
1806
1808
  const response = await createChatCompletions(payload);
1807
- if (isNonStreaming(response)) {
1809
+ if (isNonStreaming$1(response)) {
1808
1810
  consola.debug("Non-streaming response:", JSON.stringify(response));
1809
1811
  if (response.usage) setRequestContext(c, {
1810
1812
  inputTokens: response.usage.prompt_tokens,
@@ -1827,7 +1829,7 @@ async function handleCompletion$1(c) {
1827
1829
  }
1828
1830
  });
1829
1831
  }
1830
- const isNonStreaming = (response) => Object.hasOwn(response, "choices");
1832
+ const isNonStreaming$1 = (response) => Object.hasOwn(response, "choices");
1831
1833
 
1832
1834
  //#endregion
1833
1835
  //#region src/routes/chat-completions/route.ts
@@ -2111,126 +2113,1088 @@ async function handleCountTokens(c) {
2111
2113
  }
2112
2114
 
2113
2115
  //#endregion
2114
- //#region src/routes/messages/stream-translation.ts
2115
- function isToolBlockOpen(state$1) {
2116
- if (!state$1.contentBlockOpen) return false;
2117
- return Object.values(state$1.toolCalls).some((tc) => tc.anthropicBlockIndex === state$1.contentBlockIndex);
2118
- }
2119
- function createMessageDeltaEvents(finishReason, usage) {
2120
- const stopReason = mapOpenAIStopReasonToAnthropic(finishReason);
2121
- const inputTokens = usage.prompt_tokens - usage.cached_tokens;
2122
- return [{
2123
- type: "message_delta",
2124
- delta: {
2125
- stop_reason: stopReason,
2126
- stop_sequence: null
2127
- },
2128
- usage: {
2129
- input_tokens: inputTokens,
2130
- output_tokens: usage.completion_tokens,
2131
- cache_creation_input_tokens: 0,
2132
- cache_read_input_tokens: usage.cached_tokens
2116
+ //#region src/lib/logger.ts
2117
+ const LOG_RETENTION_MS = 10080 * 60 * 1e3;
2118
+ const CLEANUP_INTERVAL_MS = 1440 * 60 * 1e3;
2119
+ const LOG_DIR = path.join(PATHS.APP_DIR, "logs");
2120
+ const FLUSH_INTERVAL_MS = 1e3;
2121
+ const MAX_BUFFER_SIZE = 100;
2122
+ const logStreams = /* @__PURE__ */ new Map();
2123
+ const logBuffers = /* @__PURE__ */ new Map();
2124
+ const ensureLogDirectory = () => {
2125
+ if (!fs$1.existsSync(LOG_DIR)) fs$1.mkdirSync(LOG_DIR, { recursive: true });
2126
+ };
2127
+ const cleanupOldLogs = () => {
2128
+ if (!fs$1.existsSync(LOG_DIR)) return;
2129
+ const now = Date.now();
2130
+ for (const entry of fs$1.readdirSync(LOG_DIR)) {
2131
+ const filePath = path.join(LOG_DIR, entry);
2132
+ let stats;
2133
+ try {
2134
+ stats = fs$1.statSync(filePath);
2135
+ } catch {
2136
+ continue;
2133
2137
  }
2134
- }, { type: "message_stop" }];
2135
- }
2136
- function createFallbackMessageDeltaEvents(state$1) {
2137
- if (state$1.messageDeltaSent) return [];
2138
- if (state$1.pendingFinishReason) {
2139
- const usage = state$1.pendingUsage ?? {
2140
- prompt_tokens: 0,
2141
- completion_tokens: 0,
2142
- cached_tokens: 0
2143
- };
2144
- return createMessageDeltaEvents(state$1.pendingFinishReason, usage);
2145
- }
2146
- return [];
2147
- }
2148
- function translateChunkToAnthropicEvents(chunk, state$1, originalModel) {
2149
- const events$1 = [];
2150
- if (chunk.usage) {
2151
- state$1.pendingUsage = {
2152
- prompt_tokens: chunk.usage.prompt_tokens,
2153
- completion_tokens: chunk.usage.completion_tokens,
2154
- cached_tokens: chunk.usage.prompt_tokens_details?.cached_tokens ?? 0
2155
- };
2156
- if (state$1.pendingFinishReason && !state$1.messageDeltaSent) {
2157
- events$1.push(...createMessageDeltaEvents(state$1.pendingFinishReason, state$1.pendingUsage));
2158
- state$1.messageDeltaSent = true;
2138
+ if (!stats.isFile()) continue;
2139
+ if (now - stats.mtimeMs > LOG_RETENTION_MS) try {
2140
+ fs$1.rmSync(filePath);
2141
+ } catch {
2142
+ continue;
2159
2143
  }
2160
2144
  }
2161
- if (chunk.choices.length === 0) return events$1;
2162
- const choice = chunk.choices[0];
2163
- const { delta } = choice;
2164
- if (!state$1.messageStartSent) {
2165
- const usage = state$1.pendingUsage ?? {
2166
- prompt_tokens: 0,
2167
- completion_tokens: 0,
2168
- cached_tokens: 0
2169
- };
2170
- const inputTokens = usage.prompt_tokens - usage.cached_tokens;
2171
- events$1.push({
2172
- type: "message_start",
2173
- message: {
2174
- id: chunk.id,
2175
- type: "message",
2176
- role: "assistant",
2177
- content: [],
2178
- model: originalModel ?? chunk.model,
2179
- stop_reason: null,
2180
- stop_sequence: null,
2181
- usage: {
2182
- input_tokens: inputTokens,
2183
- output_tokens: 0,
2184
- cache_creation_input_tokens: 0,
2185
- cache_read_input_tokens: usage.cached_tokens
2186
- }
2187
- }
2145
+ };
2146
+ const formatArgs = (args) => args.map((arg) => typeof arg === "string" ? arg : util.inspect(arg, {
2147
+ depth: null,
2148
+ colors: false
2149
+ })).join(" ");
2150
+ const sanitizeName = (name$1) => {
2151
+ const normalized = name$1.toLowerCase().replaceAll(/[^a-z0-9]+/g, "-").replaceAll(/^-+|-+$/g, "");
2152
+ return normalized === "" ? "handler" : normalized;
2153
+ };
2154
+ const getLogStream = (filePath) => {
2155
+ let stream = logStreams.get(filePath);
2156
+ if (!stream || stream.destroyed) {
2157
+ stream = fs$1.createWriteStream(filePath, { flags: "a" });
2158
+ logStreams.set(filePath, stream);
2159
+ stream.on("error", (error) => {
2160
+ console.warn("Log stream error", error);
2161
+ logStreams.delete(filePath);
2188
2162
  });
2189
- state$1.messageStartSent = true;
2190
2163
  }
2191
- if (delta.content) {
2192
- if (isToolBlockOpen(state$1)) {
2193
- events$1.push({
2194
- type: "content_block_stop",
2195
- index: state$1.contentBlockIndex
2164
+ return stream;
2165
+ };
2166
+ const flushBuffer = (filePath) => {
2167
+ const buffer = logBuffers.get(filePath);
2168
+ if (!buffer || buffer.length === 0) return;
2169
+ const stream = getLogStream(filePath);
2170
+ const content = buffer.join("\n") + "\n";
2171
+ stream.write(content, (error) => {
2172
+ if (error) console.warn("Failed to write handler log", error);
2173
+ });
2174
+ logBuffers.set(filePath, []);
2175
+ };
2176
+ const flushAllBuffers = () => {
2177
+ for (const filePath of logBuffers.keys()) flushBuffer(filePath);
2178
+ };
2179
+ const appendLine = (filePath, line) => {
2180
+ let buffer = logBuffers.get(filePath);
2181
+ if (!buffer) {
2182
+ buffer = [];
2183
+ logBuffers.set(filePath, buffer);
2184
+ }
2185
+ buffer.push(line);
2186
+ if (buffer.length >= MAX_BUFFER_SIZE) flushBuffer(filePath);
2187
+ };
2188
+ setInterval(flushAllBuffers, FLUSH_INTERVAL_MS);
2189
+ const cleanup = () => {
2190
+ flushAllBuffers();
2191
+ for (const stream of logStreams.values()) stream.end();
2192
+ logStreams.clear();
2193
+ logBuffers.clear();
2194
+ };
2195
+ process.on("exit", cleanup);
2196
+ process.on("SIGINT", () => {
2197
+ cleanup();
2198
+ process.exit(0);
2199
+ });
2200
+ process.on("SIGTERM", () => {
2201
+ cleanup();
2202
+ process.exit(0);
2203
+ });
2204
+ let lastCleanup = 0;
2205
+ const createHandlerLogger = (name$1) => {
2206
+ ensureLogDirectory();
2207
+ const sanitizedName = sanitizeName(name$1);
2208
+ const instance = consola.withTag(name$1);
2209
+ if (state.verbose) instance.level = 5;
2210
+ instance.setReporters([]);
2211
+ instance.addReporter({ log(logObj) {
2212
+ ensureLogDirectory();
2213
+ if (Date.now() - lastCleanup > CLEANUP_INTERVAL_MS) {
2214
+ cleanupOldLogs();
2215
+ lastCleanup = Date.now();
2216
+ }
2217
+ const date = logObj.date;
2218
+ const dateKey = date.toLocaleDateString("sv-SE");
2219
+ const timestamp = date.toLocaleString("sv-SE", { hour12: false });
2220
+ const filePath = path.join(LOG_DIR, `${sanitizedName}-${dateKey}.log`);
2221
+ const message = formatArgs(logObj.args);
2222
+ const line = `[${timestamp}] [${logObj.type}] [${logObj.tag || name$1}]${message ? ` ${message}` : ""}`;
2223
+ appendLine(filePath, line);
2224
+ } });
2225
+ return instance;
2226
+ };
2227
+
2228
+ //#endregion
2229
+ //#region src/services/copilot/create-responses.ts
2230
+ const createResponses = async (payload, { vision, initiator }) => {
2231
+ if (!state.copilotToken) throw new Error("Copilot token not found");
2232
+ const headers = {
2233
+ ...copilotHeaders(state, vision),
2234
+ "X-Initiator": initiator
2235
+ };
2236
+ payload.service_tier = null;
2237
+ const response = await fetch(`${copilotBaseUrl(state)}/responses`, {
2238
+ method: "POST",
2239
+ headers,
2240
+ body: JSON.stringify(payload)
2241
+ });
2242
+ if (!response.ok) {
2243
+ consola.error("Failed to create responses", response);
2244
+ throw new HTTPError("Failed to create responses", response);
2245
+ }
2246
+ if (payload.stream) return events(response);
2247
+ return await response.json();
2248
+ };
2249
+
2250
+ //#endregion
2251
+ //#region src/routes/messages/responses-translation.ts
2252
+ const MESSAGE_TYPE = "message";
2253
+ const CODEX_PHASE_MODEL = "gpt-5.3-codex";
2254
+ const THINKING_TEXT = "Thinking...";
2255
+ const translateAnthropicMessagesToResponsesPayload = (payload) => {
2256
+ const input = [];
2257
+ for (const message of payload.messages) input.push(...translateMessage(message, payload.model));
2258
+ const translatedTools = convertAnthropicTools(payload.tools);
2259
+ const toolChoice = convertAnthropicToolChoice(payload.tool_choice);
2260
+ const { safetyIdentifier, promptCacheKey } = parseUserId(payload.metadata?.user_id);
2261
+ return {
2262
+ model: payload.model,
2263
+ input,
2264
+ instructions: translateSystemPrompt(payload.system, payload.model),
2265
+ temperature: 1,
2266
+ top_p: payload.top_p ?? null,
2267
+ max_output_tokens: Math.max(payload.max_tokens, 12800),
2268
+ tools: translatedTools,
2269
+ tool_choice: toolChoice,
2270
+ metadata: payload.metadata ? { ...payload.metadata } : null,
2271
+ safety_identifier: safetyIdentifier,
2272
+ prompt_cache_key: promptCacheKey,
2273
+ stream: payload.stream ?? null,
2274
+ store: false,
2275
+ parallel_tool_calls: true,
2276
+ reasoning: {
2277
+ effort: getReasoningEffortForModel(payload.model),
2278
+ summary: "detailed"
2279
+ },
2280
+ include: ["reasoning.encrypted_content"]
2281
+ };
2282
+ };
2283
+ const translateMessage = (message, model) => {
2284
+ if (message.role === "user") return translateUserMessage(message);
2285
+ return translateAssistantMessage(message, model);
2286
+ };
2287
+ const translateUserMessage = (message) => {
2288
+ if (typeof message.content === "string") return [createMessage("user", message.content)];
2289
+ if (!Array.isArray(message.content)) return [];
2290
+ const items = [];
2291
+ const pendingContent = [];
2292
+ for (const block of message.content) {
2293
+ if (block.type === "tool_result") {
2294
+ flushPendingContent(pendingContent, items, { role: "user" });
2295
+ items.push(createFunctionCallOutput(block));
2296
+ continue;
2297
+ }
2298
+ const converted = translateUserContentBlock(block);
2299
+ if (converted) pendingContent.push(converted);
2300
+ }
2301
+ flushPendingContent(pendingContent, items, { role: "user" });
2302
+ return items;
2303
+ };
2304
+ const translateAssistantMessage = (message, model) => {
2305
+ const assistantPhase = resolveAssistantPhase(model, message.content);
2306
+ if (typeof message.content === "string") return [createMessage("assistant", message.content, assistantPhase)];
2307
+ if (!Array.isArray(message.content)) return [];
2308
+ const items = [];
2309
+ const pendingContent = [];
2310
+ for (const block of message.content) {
2311
+ if (block.type === "tool_use") {
2312
+ flushPendingContent(pendingContent, items, {
2313
+ role: "assistant",
2314
+ phase: assistantPhase
2196
2315
  });
2197
- state$1.contentBlockIndex++;
2198
- state$1.contentBlockOpen = false;
2316
+ items.push(createFunctionToolCall(block));
2317
+ continue;
2199
2318
  }
2200
- if (!state$1.contentBlockOpen) {
2201
- events$1.push({
2202
- type: "content_block_start",
2203
- index: state$1.contentBlockIndex,
2204
- content_block: {
2205
- type: "text",
2206
- text: ""
2207
- }
2319
+ if (block.type === "thinking" && block.signature && block.signature.includes("@")) {
2320
+ flushPendingContent(pendingContent, items, {
2321
+ role: "assistant",
2322
+ phase: assistantPhase
2208
2323
  });
2209
- state$1.contentBlockOpen = true;
2324
+ items.push(createReasoningContent(block));
2325
+ continue;
2210
2326
  }
2211
- events$1.push({
2212
- type: "content_block_delta",
2213
- index: state$1.contentBlockIndex,
2214
- delta: {
2215
- type: "text_delta",
2216
- text: delta.content
2217
- }
2218
- });
2327
+ const converted = translateAssistantContentBlock(block);
2328
+ if (converted) pendingContent.push(converted);
2219
2329
  }
2220
- if (delta.tool_calls) for (const toolCall of delta.tool_calls) {
2221
- if (toolCall.id && toolCall.function?.name) {
2222
- if (state$1.contentBlockOpen) {
2223
- events$1.push({
2224
- type: "content_block_stop",
2225
- index: state$1.contentBlockIndex
2226
- });
2227
- state$1.contentBlockIndex++;
2228
- state$1.contentBlockOpen = false;
2229
- }
2230
- const anthropicBlockIndex = state$1.contentBlockIndex;
2231
- state$1.toolCalls[toolCall.index] = {
2232
- id: toolCall.id,
2233
- name: toolCall.function.name,
2330
+ flushPendingContent(pendingContent, items, {
2331
+ role: "assistant",
2332
+ phase: assistantPhase
2333
+ });
2334
+ return items;
2335
+ };
2336
+ const translateUserContentBlock = (block) => {
2337
+ switch (block.type) {
2338
+ case "text": return createTextContent(block.text);
2339
+ case "image": return createImageContent(block);
2340
+ default: return;
2341
+ }
2342
+ };
2343
+ const translateAssistantContentBlock = (block) => {
2344
+ switch (block.type) {
2345
+ case "text": return createOutPutTextContent(block.text);
2346
+ default: return;
2347
+ }
2348
+ };
2349
+ const flushPendingContent = (pendingContent, target, message) => {
2350
+ if (pendingContent.length === 0) return;
2351
+ const messageContent = [...pendingContent];
2352
+ target.push(createMessage(message.role, messageContent, message.phase));
2353
+ pendingContent.length = 0;
2354
+ };
2355
+ const createMessage = (role, content, phase) => ({
2356
+ type: MESSAGE_TYPE,
2357
+ role,
2358
+ content,
2359
+ ...role === "assistant" && phase ? { phase } : {}
2360
+ });
2361
+ const resolveAssistantPhase = (model, content) => {
2362
+ if (!shouldApplyCodexPhase(model)) return;
2363
+ if (typeof content === "string") return "final_answer";
2364
+ if (!Array.isArray(content)) return;
2365
+ if (!content.some((block) => block.type === "text")) return;
2366
+ return content.some((block) => block.type === "tool_use") ? "commentary" : "final_answer";
2367
+ };
2368
+ const shouldApplyCodexPhase = (model) => model === CODEX_PHASE_MODEL;
2369
+ const createTextContent = (text) => ({
2370
+ type: "input_text",
2371
+ text
2372
+ });
2373
+ const createOutPutTextContent = (text) => ({
2374
+ type: "output_text",
2375
+ text
2376
+ });
2377
+ const createImageContent = (block) => ({
2378
+ type: "input_image",
2379
+ image_url: `data:${block.source.media_type};base64,${block.source.data}`,
2380
+ detail: "auto"
2381
+ });
2382
+ const createReasoningContent = (block) => {
2383
+ const array = (block.signature ?? "").split("@");
2384
+ const signature = array[0];
2385
+ const id = array[1];
2386
+ const thinking = block.thinking === THINKING_TEXT ? "" : block.thinking;
2387
+ return {
2388
+ id,
2389
+ type: "reasoning",
2390
+ summary: thinking ? [{
2391
+ type: "summary_text",
2392
+ text: thinking
2393
+ }] : [],
2394
+ encrypted_content: signature
2395
+ };
2396
+ };
2397
+ const createFunctionToolCall = (block) => ({
2398
+ type: "function_call",
2399
+ call_id: block.id,
2400
+ name: block.name,
2401
+ arguments: JSON.stringify(block.input),
2402
+ status: "completed"
2403
+ });
2404
+ const createFunctionCallOutput = (block) => ({
2405
+ type: "function_call_output",
2406
+ call_id: block.tool_use_id,
2407
+ output: convertToolResultContent(block.content),
2408
+ status: block.is_error ? "incomplete" : "completed"
2409
+ });
2410
+ const translateSystemPrompt = (system, model) => {
2411
+ if (!system) return null;
2412
+ const extraPrompt = getExtraPromptForModel(model);
2413
+ if (typeof system === "string") return system + extraPrompt;
2414
+ const text = system.map((block, index) => {
2415
+ if (index === 0) return block.text + extraPrompt;
2416
+ return block.text;
2417
+ }).join(" ");
2418
+ return text.length > 0 ? text : null;
2419
+ };
2420
+ const convertAnthropicTools = (tools) => {
2421
+ if (!tools || tools.length === 0) return null;
2422
+ return tools.map((tool) => ({
2423
+ type: "function",
2424
+ name: tool.name,
2425
+ parameters: tool.input_schema,
2426
+ strict: false,
2427
+ ...tool.description ? { description: tool.description } : {}
2428
+ }));
2429
+ };
2430
+ const convertAnthropicToolChoice = (choice) => {
2431
+ if (!choice) return "auto";
2432
+ switch (choice.type) {
2433
+ case "auto": return "auto";
2434
+ case "any": return "required";
2435
+ case "tool": return choice.name ? {
2436
+ type: "function",
2437
+ name: choice.name
2438
+ } : "auto";
2439
+ case "none": return "none";
2440
+ default: return "auto";
2441
+ }
2442
+ };
2443
+ const translateResponsesResultToAnthropic = (response) => {
2444
+ const contentBlocks = mapOutputToAnthropicContent(response.output);
2445
+ const usage = mapResponsesUsage(response);
2446
+ let anthropicContent = fallbackContentBlocks(response.output_text);
2447
+ if (contentBlocks.length > 0) anthropicContent = contentBlocks;
2448
+ const stopReason = mapResponsesStopReason(response);
2449
+ return {
2450
+ id: response.id,
2451
+ type: "message",
2452
+ role: "assistant",
2453
+ content: anthropicContent,
2454
+ model: response.model,
2455
+ stop_reason: stopReason,
2456
+ stop_sequence: null,
2457
+ usage
2458
+ };
2459
+ };
2460
+ const mapOutputToAnthropicContent = (output) => {
2461
+ const contentBlocks = [];
2462
+ for (const item of output) switch (item.type) {
2463
+ case "reasoning": {
2464
+ const thinkingText = extractReasoningText(item);
2465
+ if (thinkingText.length > 0) contentBlocks.push({
2466
+ type: "thinking",
2467
+ thinking: thinkingText,
2468
+ signature: (item.encrypted_content ?? "") + "@" + item.id
2469
+ });
2470
+ break;
2471
+ }
2472
+ case "function_call": {
2473
+ const toolUseBlock = createToolUseContentBlock(item);
2474
+ if (toolUseBlock) contentBlocks.push(toolUseBlock);
2475
+ break;
2476
+ }
2477
+ case "message": {
2478
+ const combinedText = combineMessageTextContent(item.content);
2479
+ if (combinedText.length > 0) contentBlocks.push({
2480
+ type: "text",
2481
+ text: combinedText
2482
+ });
2483
+ break;
2484
+ }
2485
+ default: {
2486
+ const combinedText = combineMessageTextContent(item.content);
2487
+ if (combinedText.length > 0) contentBlocks.push({
2488
+ type: "text",
2489
+ text: combinedText
2490
+ });
2491
+ }
2492
+ }
2493
+ return contentBlocks;
2494
+ };
2495
+ const combineMessageTextContent = (content) => {
2496
+ if (!Array.isArray(content)) return "";
2497
+ let aggregated = "";
2498
+ for (const block of content) {
2499
+ if (isResponseOutputText(block)) {
2500
+ aggregated += block.text;
2501
+ continue;
2502
+ }
2503
+ if (isResponseOutputRefusal(block)) {
2504
+ aggregated += block.refusal;
2505
+ continue;
2506
+ }
2507
+ if (typeof block.text === "string") {
2508
+ aggregated += block.text;
2509
+ continue;
2510
+ }
2511
+ if (typeof block.reasoning === "string") {
2512
+ aggregated += block.reasoning;
2513
+ continue;
2514
+ }
2515
+ }
2516
+ return aggregated;
2517
+ };
2518
+ const extractReasoningText = (item) => {
2519
+ const segments = [];
2520
+ const collectFromBlocks = (blocks) => {
2521
+ if (!Array.isArray(blocks)) return;
2522
+ for (const block of blocks) if (typeof block.text === "string") {
2523
+ segments.push(block.text);
2524
+ continue;
2525
+ }
2526
+ };
2527
+ if (!item.summary || item.summary.length === 0) return THINKING_TEXT;
2528
+ collectFromBlocks(item.summary);
2529
+ return segments.join("").trim();
2530
+ };
2531
+ const createToolUseContentBlock = (call) => {
2532
+ const toolId = call.call_id;
2533
+ if (!call.name || !toolId) return null;
2534
+ const input = parseFunctionCallArguments(call.arguments);
2535
+ return {
2536
+ type: "tool_use",
2537
+ id: toolId,
2538
+ name: call.name,
2539
+ input
2540
+ };
2541
+ };
2542
+ const parseFunctionCallArguments = (rawArguments) => {
2543
+ if (typeof rawArguments !== "string" || rawArguments.trim().length === 0) return {};
2544
+ try {
2545
+ const parsed = JSON.parse(rawArguments);
2546
+ if (Array.isArray(parsed)) return { arguments: parsed };
2547
+ if (parsed && typeof parsed === "object") return parsed;
2548
+ } catch (error) {
2549
+ consola.warn("Failed to parse function call arguments", {
2550
+ error,
2551
+ rawArguments
2552
+ });
2553
+ }
2554
+ return { raw_arguments: rawArguments };
2555
+ };
2556
+ const fallbackContentBlocks = (outputText) => {
2557
+ if (!outputText) return [];
2558
+ return [{
2559
+ type: "text",
2560
+ text: outputText
2561
+ }];
2562
+ };
2563
+ const mapResponsesStopReason = (response) => {
2564
+ const { status, incomplete_details: incompleteDetails } = response;
2565
+ if (status === "completed") {
2566
+ if (response.output.some((item) => item.type === "function_call")) return "tool_use";
2567
+ return "end_turn";
2568
+ }
2569
+ if (status === "incomplete") {
2570
+ if (incompleteDetails?.reason === "max_output_tokens") return "max_tokens";
2571
+ if (incompleteDetails?.reason === "content_filter") return "end_turn";
2572
+ }
2573
+ return null;
2574
+ };
2575
+ const mapResponsesUsage = (response) => {
2576
+ const inputTokens = response.usage?.input_tokens ?? 0;
2577
+ const outputTokens = response.usage?.output_tokens ?? 0;
2578
+ const inputCachedTokens = response.usage?.input_tokens_details?.cached_tokens;
2579
+ return {
2580
+ input_tokens: inputTokens - (inputCachedTokens ?? 0),
2581
+ output_tokens: outputTokens,
2582
+ ...response.usage?.input_tokens_details?.cached_tokens !== void 0 && { cache_read_input_tokens: response.usage.input_tokens_details.cached_tokens }
2583
+ };
2584
+ };
2585
+ const isRecord = (value) => typeof value === "object" && value !== null;
2586
+ const isResponseOutputText = (block) => isRecord(block) && "type" in block && block.type === "output_text";
2587
+ const isResponseOutputRefusal = (block) => isRecord(block) && "type" in block && block.type === "refusal";
2588
+ const parseUserId = (userId) => {
2589
+ if (!userId || typeof userId !== "string") return {
2590
+ safetyIdentifier: null,
2591
+ promptCacheKey: null
2592
+ };
2593
+ const userMatch = userId.match(/user_([^_]+)_account/);
2594
+ const safetyIdentifier = userMatch ? userMatch[1] : null;
2595
+ const sessionMatch = userId.match(/_session_(.+)$/);
2596
+ const promptCacheKey = sessionMatch ? sessionMatch[1] : null;
2597
+ return {
2598
+ safetyIdentifier,
2599
+ promptCacheKey
2600
+ };
2601
+ };
2602
+ const convertToolResultContent = (content) => {
2603
+ if (typeof content === "string") return content;
2604
+ if (Array.isArray(content)) {
2605
+ const result = [];
2606
+ for (const block of content) switch (block.type) {
2607
+ case "text":
2608
+ result.push(createTextContent(block.text));
2609
+ break;
2610
+ case "image":
2611
+ result.push(createImageContent(block));
2612
+ break;
2613
+ default: break;
2614
+ }
2615
+ return result;
2616
+ }
2617
+ return "";
2618
+ };
2619
+
2620
+ //#endregion
2621
+ //#region src/routes/messages/responses-stream-translation.ts
2622
+ const MAX_CONSECUTIVE_FUNCTION_CALL_WHITESPACE = 20;
2623
+ var FunctionCallArgumentsValidationError = class extends Error {
2624
+ constructor(message) {
2625
+ super(message);
2626
+ this.name = "FunctionCallArgumentsValidationError";
2627
+ }
2628
+ };
2629
+ const updateWhitespaceRunState = (previousCount, chunk) => {
2630
+ let count = previousCount;
2631
+ for (const char of chunk) {
2632
+ if (char === "\r" || char === "\n" || char === " ") {
2633
+ count += 1;
2634
+ if (count > MAX_CONSECUTIVE_FUNCTION_CALL_WHITESPACE) return {
2635
+ nextCount: count,
2636
+ exceeded: true
2637
+ };
2638
+ continue;
2639
+ }
2640
+ if (char !== " ") count = 0;
2641
+ }
2642
+ return {
2643
+ nextCount: count,
2644
+ exceeded: false
2645
+ };
2646
+ };
2647
+ const createResponsesStreamState = () => ({
2648
+ messageStartSent: false,
2649
+ messageCompleted: false,
2650
+ nextContentBlockIndex: 0,
2651
+ blockIndexByKey: /* @__PURE__ */ new Map(),
2652
+ openBlocks: /* @__PURE__ */ new Set(),
2653
+ blockHasDelta: /* @__PURE__ */ new Set(),
2654
+ functionCallStateByOutputIndex: /* @__PURE__ */ new Map()
2655
+ });
2656
+ const translateResponsesStreamEvent = (rawEvent, state$1) => {
2657
+ switch (rawEvent.type) {
2658
+ case "response.created": return handleResponseCreated(rawEvent, state$1);
2659
+ case "response.output_item.added": return handleOutputItemAdded$1(rawEvent, state$1);
2660
+ case "response.reasoning_summary_text.delta": return handleReasoningSummaryTextDelta(rawEvent, state$1);
2661
+ case "response.output_text.delta": return handleOutputTextDelta(rawEvent, state$1);
2662
+ case "response.reasoning_summary_text.done": return handleReasoningSummaryTextDone(rawEvent, state$1);
2663
+ case "response.output_text.done": return handleOutputTextDone(rawEvent, state$1);
2664
+ case "response.output_item.done": return handleOutputItemDone$1(rawEvent, state$1);
2665
+ case "response.function_call_arguments.delta": return handleFunctionCallArgumentsDelta(rawEvent, state$1);
2666
+ case "response.function_call_arguments.done": return handleFunctionCallArgumentsDone(rawEvent, state$1);
2667
+ case "response.completed":
2668
+ case "response.incomplete": return handleResponseCompleted(rawEvent, state$1);
2669
+ case "response.failed": return handleResponseFailed(rawEvent, state$1);
2670
+ case "error": return handleErrorEvent(rawEvent, state$1);
2671
+ default: return [];
2672
+ }
2673
+ };
2674
+ const handleResponseCreated = (rawEvent, state$1) => {
2675
+ return messageStart(state$1, rawEvent.response);
2676
+ };
2677
+ const handleOutputItemAdded$1 = (rawEvent, state$1) => {
2678
+ const events$1 = new Array();
2679
+ const functionCallDetails = extractFunctionCallDetails(rawEvent);
2680
+ if (!functionCallDetails) return events$1;
2681
+ const { outputIndex, toolCallId, name: name$1, initialArguments } = functionCallDetails;
2682
+ const blockIndex = openFunctionCallBlock(state$1, {
2683
+ outputIndex,
2684
+ toolCallId,
2685
+ name: name$1,
2686
+ events: events$1
2687
+ });
2688
+ if (initialArguments !== void 0 && initialArguments.length > 0) {
2689
+ events$1.push({
2690
+ type: "content_block_delta",
2691
+ index: blockIndex,
2692
+ delta: {
2693
+ type: "input_json_delta",
2694
+ partial_json: initialArguments
2695
+ }
2696
+ });
2697
+ state$1.blockHasDelta.add(blockIndex);
2698
+ }
2699
+ return events$1;
2700
+ };
2701
+ const handleOutputItemDone$1 = (rawEvent, state$1) => {
2702
+ const events$1 = new Array();
2703
+ const item = rawEvent.item;
2704
+ if (item.type !== "reasoning") return events$1;
2705
+ const outputIndex = rawEvent.output_index;
2706
+ const blockIndex = openThinkingBlockIfNeeded(state$1, outputIndex, events$1);
2707
+ const signature = (item.encrypted_content ?? "") + "@" + item.id;
2708
+ if (signature) {
2709
+ if (!item.summary || item.summary.length === 0) events$1.push({
2710
+ type: "content_block_delta",
2711
+ index: blockIndex,
2712
+ delta: {
2713
+ type: "thinking_delta",
2714
+ thinking: THINKING_TEXT
2715
+ }
2716
+ });
2717
+ events$1.push({
2718
+ type: "content_block_delta",
2719
+ index: blockIndex,
2720
+ delta: {
2721
+ type: "signature_delta",
2722
+ signature
2723
+ }
2724
+ });
2725
+ state$1.blockHasDelta.add(blockIndex);
2726
+ }
2727
+ return events$1;
2728
+ };
2729
+ const handleFunctionCallArgumentsDelta = (rawEvent, state$1) => {
2730
+ const events$1 = new Array();
2731
+ const outputIndex = rawEvent.output_index;
2732
+ const deltaText = rawEvent.delta;
2733
+ if (!deltaText) return events$1;
2734
+ const blockIndex = openFunctionCallBlock(state$1, {
2735
+ outputIndex,
2736
+ events: events$1
2737
+ });
2738
+ const functionCallState = state$1.functionCallStateByOutputIndex.get(outputIndex);
2739
+ if (!functionCallState) return handleFunctionCallArgumentsValidationError(new FunctionCallArgumentsValidationError("Received function call arguments delta without an open tool call block."), state$1, events$1);
2740
+ const { nextCount, exceeded } = updateWhitespaceRunState(functionCallState.consecutiveWhitespaceCount, deltaText);
2741
+ if (exceeded) return handleFunctionCallArgumentsValidationError(new FunctionCallArgumentsValidationError("Received function call arguments delta containing more than 20 consecutive whitespace characters."), state$1, events$1);
2742
+ functionCallState.consecutiveWhitespaceCount = nextCount;
2743
+ events$1.push({
2744
+ type: "content_block_delta",
2745
+ index: blockIndex,
2746
+ delta: {
2747
+ type: "input_json_delta",
2748
+ partial_json: deltaText
2749
+ }
2750
+ });
2751
+ state$1.blockHasDelta.add(blockIndex);
2752
+ return events$1;
2753
+ };
2754
+ const handleFunctionCallArgumentsDone = (rawEvent, state$1) => {
2755
+ const events$1 = new Array();
2756
+ const outputIndex = rawEvent.output_index;
2757
+ const blockIndex = openFunctionCallBlock(state$1, {
2758
+ outputIndex,
2759
+ events: events$1
2760
+ });
2761
+ const finalArguments = typeof rawEvent.arguments === "string" ? rawEvent.arguments : void 0;
2762
+ if (!state$1.blockHasDelta.has(blockIndex) && finalArguments) {
2763
+ events$1.push({
2764
+ type: "content_block_delta",
2765
+ index: blockIndex,
2766
+ delta: {
2767
+ type: "input_json_delta",
2768
+ partial_json: finalArguments
2769
+ }
2770
+ });
2771
+ state$1.blockHasDelta.add(blockIndex);
2772
+ }
2773
+ state$1.functionCallStateByOutputIndex.delete(outputIndex);
2774
+ return events$1;
2775
+ };
2776
+ const handleOutputTextDelta = (rawEvent, state$1) => {
2777
+ const events$1 = new Array();
2778
+ const outputIndex = rawEvent.output_index;
2779
+ const contentIndex = rawEvent.content_index;
2780
+ const deltaText = rawEvent.delta;
2781
+ if (!deltaText) return events$1;
2782
+ const blockIndex = openTextBlockIfNeeded(state$1, {
2783
+ outputIndex,
2784
+ contentIndex,
2785
+ events: events$1
2786
+ });
2787
+ events$1.push({
2788
+ type: "content_block_delta",
2789
+ index: blockIndex,
2790
+ delta: {
2791
+ type: "text_delta",
2792
+ text: deltaText
2793
+ }
2794
+ });
2795
+ state$1.blockHasDelta.add(blockIndex);
2796
+ return events$1;
2797
+ };
2798
+ const handleReasoningSummaryTextDelta = (rawEvent, state$1) => {
2799
+ const outputIndex = rawEvent.output_index;
2800
+ const deltaText = rawEvent.delta;
2801
+ const events$1 = new Array();
2802
+ const blockIndex = openThinkingBlockIfNeeded(state$1, outputIndex, events$1);
2803
+ events$1.push({
2804
+ type: "content_block_delta",
2805
+ index: blockIndex,
2806
+ delta: {
2807
+ type: "thinking_delta",
2808
+ thinking: deltaText
2809
+ }
2810
+ });
2811
+ state$1.blockHasDelta.add(blockIndex);
2812
+ return events$1;
2813
+ };
2814
+ const handleReasoningSummaryTextDone = (rawEvent, state$1) => {
2815
+ const outputIndex = rawEvent.output_index;
2816
+ const text = rawEvent.text;
2817
+ const events$1 = new Array();
2818
+ const blockIndex = openThinkingBlockIfNeeded(state$1, outputIndex, events$1);
2819
+ if (text && !state$1.blockHasDelta.has(blockIndex)) events$1.push({
2820
+ type: "content_block_delta",
2821
+ index: blockIndex,
2822
+ delta: {
2823
+ type: "thinking_delta",
2824
+ thinking: text
2825
+ }
2826
+ });
2827
+ return events$1;
2828
+ };
2829
+ const handleOutputTextDone = (rawEvent, state$1) => {
2830
+ const events$1 = new Array();
2831
+ const outputIndex = rawEvent.output_index;
2832
+ const contentIndex = rawEvent.content_index;
2833
+ const text = rawEvent.text;
2834
+ const blockIndex = openTextBlockIfNeeded(state$1, {
2835
+ outputIndex,
2836
+ contentIndex,
2837
+ events: events$1
2838
+ });
2839
+ if (text && !state$1.blockHasDelta.has(blockIndex)) events$1.push({
2840
+ type: "content_block_delta",
2841
+ index: blockIndex,
2842
+ delta: {
2843
+ type: "text_delta",
2844
+ text
2845
+ }
2846
+ });
2847
+ return events$1;
2848
+ };
2849
+ const handleResponseCompleted = (rawEvent, state$1) => {
2850
+ const response = rawEvent.response;
2851
+ const events$1 = new Array();
2852
+ closeAllOpenBlocks(state$1, events$1);
2853
+ const anthropic = translateResponsesResultToAnthropic(response);
2854
+ events$1.push({
2855
+ type: "message_delta",
2856
+ delta: {
2857
+ stop_reason: anthropic.stop_reason,
2858
+ stop_sequence: anthropic.stop_sequence
2859
+ },
2860
+ usage: anthropic.usage
2861
+ }, { type: "message_stop" });
2862
+ state$1.messageCompleted = true;
2863
+ return events$1;
2864
+ };
2865
+ const handleResponseFailed = (rawEvent, state$1) => {
2866
+ const response = rawEvent.response;
2867
+ const events$1 = new Array();
2868
+ closeAllOpenBlocks(state$1, events$1);
2869
+ const message = response.error?.message ?? "The response failed due to an unknown error.";
2870
+ events$1.push(buildErrorEvent(message));
2871
+ state$1.messageCompleted = true;
2872
+ return events$1;
2873
+ };
2874
+ const handleErrorEvent = (rawEvent, state$1) => {
2875
+ const message = typeof rawEvent.message === "string" ? rawEvent.message : "An unexpected error occurred during streaming.";
2876
+ state$1.messageCompleted = true;
2877
+ return [buildErrorEvent(message)];
2878
+ };
2879
+ const handleFunctionCallArgumentsValidationError = (error, state$1, events$1 = []) => {
2880
+ const reason = error.message;
2881
+ closeAllOpenBlocks(state$1, events$1);
2882
+ state$1.messageCompleted = true;
2883
+ events$1.push(buildErrorEvent(reason));
2884
+ return events$1;
2885
+ };
2886
+ const messageStart = (state$1, response) => {
2887
+ state$1.messageStartSent = true;
2888
+ const inputCachedTokens = response.usage?.input_tokens_details?.cached_tokens;
2889
+ const inputTokens = (response.usage?.input_tokens ?? 0) - (inputCachedTokens ?? 0);
2890
+ return [{
2891
+ type: "message_start",
2892
+ message: {
2893
+ id: response.id,
2894
+ type: "message",
2895
+ role: "assistant",
2896
+ content: [],
2897
+ model: response.model,
2898
+ stop_reason: null,
2899
+ stop_sequence: null,
2900
+ usage: {
2901
+ input_tokens: inputTokens,
2902
+ output_tokens: 0,
2903
+ cache_read_input_tokens: inputCachedTokens ?? 0
2904
+ }
2905
+ }
2906
+ }];
2907
+ };
2908
+ const openTextBlockIfNeeded = (state$1, params) => {
2909
+ const { outputIndex, contentIndex, events: events$1 } = params;
2910
+ const key = getBlockKey(outputIndex, contentIndex);
2911
+ let blockIndex = state$1.blockIndexByKey.get(key);
2912
+ if (blockIndex === void 0) {
2913
+ blockIndex = state$1.nextContentBlockIndex;
2914
+ state$1.nextContentBlockIndex += 1;
2915
+ state$1.blockIndexByKey.set(key, blockIndex);
2916
+ }
2917
+ if (!state$1.openBlocks.has(blockIndex)) {
2918
+ closeOpenBlocks(state$1, events$1);
2919
+ events$1.push({
2920
+ type: "content_block_start",
2921
+ index: blockIndex,
2922
+ content_block: {
2923
+ type: "text",
2924
+ text: ""
2925
+ }
2926
+ });
2927
+ state$1.openBlocks.add(blockIndex);
2928
+ }
2929
+ return blockIndex;
2930
+ };
2931
+ const openThinkingBlockIfNeeded = (state$1, outputIndex, events$1) => {
2932
+ const key = getBlockKey(outputIndex, 0);
2933
+ let blockIndex = state$1.blockIndexByKey.get(key);
2934
+ if (blockIndex === void 0) {
2935
+ blockIndex = state$1.nextContentBlockIndex;
2936
+ state$1.nextContentBlockIndex += 1;
2937
+ state$1.blockIndexByKey.set(key, blockIndex);
2938
+ }
2939
+ if (!state$1.openBlocks.has(blockIndex)) {
2940
+ closeOpenBlocks(state$1, events$1);
2941
+ events$1.push({
2942
+ type: "content_block_start",
2943
+ index: blockIndex,
2944
+ content_block: {
2945
+ type: "thinking",
2946
+ thinking: ""
2947
+ }
2948
+ });
2949
+ state$1.openBlocks.add(blockIndex);
2950
+ }
2951
+ return blockIndex;
2952
+ };
2953
+ const closeBlockIfOpen = (state$1, blockIndex, events$1) => {
2954
+ if (!state$1.openBlocks.has(blockIndex)) return;
2955
+ events$1.push({
2956
+ type: "content_block_stop",
2957
+ index: blockIndex
2958
+ });
2959
+ state$1.openBlocks.delete(blockIndex);
2960
+ state$1.blockHasDelta.delete(blockIndex);
2961
+ };
2962
+ const closeOpenBlocks = (state$1, events$1) => {
2963
+ for (const blockIndex of state$1.openBlocks) closeBlockIfOpen(state$1, blockIndex, events$1);
2964
+ };
2965
+ const closeAllOpenBlocks = (state$1, events$1) => {
2966
+ closeOpenBlocks(state$1, events$1);
2967
+ state$1.functionCallStateByOutputIndex.clear();
2968
+ };
2969
+ const buildErrorEvent = (message) => ({
2970
+ type: "error",
2971
+ error: {
2972
+ type: "api_error",
2973
+ message
2974
+ }
2975
+ });
2976
+ const getBlockKey = (outputIndex, contentIndex) => `${outputIndex}:${contentIndex}`;
2977
+ const openFunctionCallBlock = (state$1, params) => {
2978
+ const { outputIndex, toolCallId, name: name$1, events: events$1 } = params;
2979
+ let functionCallState = state$1.functionCallStateByOutputIndex.get(outputIndex);
2980
+ if (!functionCallState) {
2981
+ const blockIndex$1 = state$1.nextContentBlockIndex;
2982
+ state$1.nextContentBlockIndex += 1;
2983
+ const resolvedToolCallId = toolCallId ?? `tool_call_${blockIndex$1}`;
2984
+ functionCallState = {
2985
+ blockIndex: blockIndex$1,
2986
+ toolCallId: resolvedToolCallId,
2987
+ name: name$1 ?? "function",
2988
+ consecutiveWhitespaceCount: 0
2989
+ };
2990
+ state$1.functionCallStateByOutputIndex.set(outputIndex, functionCallState);
2991
+ }
2992
+ const { blockIndex } = functionCallState;
2993
+ if (!state$1.openBlocks.has(blockIndex)) {
2994
+ closeOpenBlocks(state$1, events$1);
2995
+ events$1.push({
2996
+ type: "content_block_start",
2997
+ index: blockIndex,
2998
+ content_block: {
2999
+ type: "tool_use",
3000
+ id: functionCallState.toolCallId,
3001
+ name: functionCallState.name,
3002
+ input: {}
3003
+ }
3004
+ });
3005
+ state$1.openBlocks.add(blockIndex);
3006
+ }
3007
+ return blockIndex;
3008
+ };
3009
+ const extractFunctionCallDetails = (rawEvent) => {
3010
+ const item = rawEvent.item;
3011
+ if (item.type !== "function_call") return;
3012
+ const outputIndex = rawEvent.output_index;
3013
+ const toolCallId = item.call_id;
3014
+ const name$1 = item.name;
3015
+ const initialArguments = item.arguments;
3016
+ return {
3017
+ outputIndex,
3018
+ toolCallId,
3019
+ name: name$1,
3020
+ initialArguments
3021
+ };
3022
+ };
3023
+
3024
+ //#endregion
3025
+ //#region src/routes/responses/utils.ts
3026
+ const getResponsesRequestOptions = (payload) => {
3027
+ const vision = hasVisionInput(payload);
3028
+ const initiator = hasAgentInitiator(payload) ? "agent" : "user";
3029
+ return {
3030
+ vision,
3031
+ initiator
3032
+ };
3033
+ };
3034
+ const hasAgentInitiator = (payload) => {
3035
+ const lastItem = getPayloadItems(payload).at(-1);
3036
+ if (!lastItem) return false;
3037
+ if (!("role" in lastItem) || !lastItem.role) return true;
3038
+ return (typeof lastItem.role === "string" ? lastItem.role.toLowerCase() : "") === "assistant";
3039
+ };
3040
+ const hasVisionInput = (payload) => {
3041
+ return getPayloadItems(payload).some((item) => containsVisionContent(item));
3042
+ };
3043
+ const getPayloadItems = (payload) => {
3044
+ const result = [];
3045
+ const { input } = payload;
3046
+ if (Array.isArray(input)) result.push(...input);
3047
+ return result;
3048
+ };
3049
+ const containsVisionContent = (value) => {
3050
+ if (!value) return false;
3051
+ if (Array.isArray(value)) return value.some((entry) => containsVisionContent(entry));
3052
+ if (typeof value !== "object") return false;
3053
+ const record = value;
3054
+ if ((typeof record.type === "string" ? record.type.toLowerCase() : void 0) === "input_image") return true;
3055
+ if (Array.isArray(record.content)) return record.content.some((entry) => containsVisionContent(entry));
3056
+ return false;
3057
+ };
3058
+
3059
+ //#endregion
3060
+ //#region src/services/copilot/create-messages.ts
3061
+ const createMessages = async (payload, anthropicBetaHeader, options) => {
3062
+ if (!state.copilotToken) throw new Error("Copilot token not found");
3063
+ const enableVision = payload.messages.some((message) => Array.isArray(message.content) && message.content.some((block) => block.type === "image"));
3064
+ let isInitiateRequest = false;
3065
+ const lastMessage = payload.messages.at(-1);
3066
+ if (lastMessage?.role === "user") isInitiateRequest = Array.isArray(lastMessage.content) ? lastMessage.content.some((block) => block.type !== "tool_result") : true;
3067
+ const initiator = options?.initiator ?? (isInitiateRequest ? "user" : "agent");
3068
+ const headers = {
3069
+ ...copilotHeaders(state, enableVision),
3070
+ "X-Initiator": initiator
3071
+ };
3072
+ if (anthropicBetaHeader) {
3073
+ const filteredBeta = anthropicBetaHeader.split(",").map((item) => item.trim()).filter((item) => item !== "claude-code-20250219").join(",");
3074
+ if (filteredBeta) headers["anthropic-beta"] = filteredBeta;
3075
+ } else if (payload.thinking?.budget_tokens) headers["anthropic-beta"] = "interleaved-thinking-2025-05-14";
3076
+ const response = await fetch(`${copilotBaseUrl(state)}/v1/messages`, {
3077
+ method: "POST",
3078
+ headers,
3079
+ body: JSON.stringify(payload)
3080
+ });
3081
+ if (!response.ok) {
3082
+ consola.error("Failed to create messages", response);
3083
+ throw new HTTPError("Failed to create messages", response);
3084
+ }
3085
+ if (payload.stream) return events(response);
3086
+ return await response.json();
3087
+ };
3088
+
3089
+ //#endregion
3090
+ //#region src/routes/messages/stream-translation.ts
3091
+ function isToolBlockOpen(state$1) {
3092
+ if (!state$1.contentBlockOpen) return false;
3093
+ return Object.values(state$1.toolCalls).some((tc) => tc.anthropicBlockIndex === state$1.contentBlockIndex);
3094
+ }
3095
+ function createMessageDeltaEvents(finishReason, usage) {
3096
+ const stopReason = mapOpenAIStopReasonToAnthropic(finishReason);
3097
+ const inputTokens = usage.prompt_tokens - usage.cached_tokens;
3098
+ return [{
3099
+ type: "message_delta",
3100
+ delta: {
3101
+ stop_reason: stopReason,
3102
+ stop_sequence: null
3103
+ },
3104
+ usage: {
3105
+ input_tokens: inputTokens,
3106
+ output_tokens: usage.completion_tokens,
3107
+ cache_creation_input_tokens: 0,
3108
+ cache_read_input_tokens: usage.cached_tokens
3109
+ }
3110
+ }, { type: "message_stop" }];
3111
+ }
3112
+ function translateChunkToAnthropicEvents(chunk, state$1, originalModel) {
3113
+ const events$1 = [];
3114
+ if (chunk.usage) {
3115
+ state$1.pendingUsage = {
3116
+ prompt_tokens: chunk.usage.prompt_tokens,
3117
+ completion_tokens: chunk.usage.completion_tokens,
3118
+ cached_tokens: chunk.usage.prompt_tokens_details?.cached_tokens ?? 0
3119
+ };
3120
+ if (state$1.pendingFinishReason && !state$1.messageDeltaSent) {
3121
+ events$1.push(...createMessageDeltaEvents(state$1.pendingFinishReason, state$1.pendingUsage));
3122
+ state$1.messageDeltaSent = true;
3123
+ }
3124
+ }
3125
+ if (chunk.choices.length === 0) return events$1;
3126
+ const choice = chunk.choices[0];
3127
+ const { delta } = choice;
3128
+ if (!state$1.messageStartSent) {
3129
+ const usage = state$1.pendingUsage ?? {
3130
+ prompt_tokens: 0,
3131
+ completion_tokens: 0,
3132
+ cached_tokens: 0
3133
+ };
3134
+ const inputTokens = usage.prompt_tokens - usage.cached_tokens;
3135
+ events$1.push({
3136
+ type: "message_start",
3137
+ message: {
3138
+ id: chunk.id,
3139
+ type: "message",
3140
+ role: "assistant",
3141
+ content: [],
3142
+ model: originalModel ?? chunk.model,
3143
+ stop_reason: null,
3144
+ stop_sequence: null,
3145
+ usage: {
3146
+ input_tokens: inputTokens,
3147
+ output_tokens: 0,
3148
+ cache_creation_input_tokens: 0,
3149
+ cache_read_input_tokens: usage.cached_tokens
3150
+ }
3151
+ }
3152
+ });
3153
+ state$1.messageStartSent = true;
3154
+ }
3155
+ if (delta.content) {
3156
+ if (isToolBlockOpen(state$1)) {
3157
+ events$1.push({
3158
+ type: "content_block_stop",
3159
+ index: state$1.contentBlockIndex
3160
+ });
3161
+ state$1.contentBlockIndex++;
3162
+ state$1.contentBlockOpen = false;
3163
+ }
3164
+ if (!state$1.contentBlockOpen) {
3165
+ events$1.push({
3166
+ type: "content_block_start",
3167
+ index: state$1.contentBlockIndex,
3168
+ content_block: {
3169
+ type: "text",
3170
+ text: ""
3171
+ }
3172
+ });
3173
+ state$1.contentBlockOpen = true;
3174
+ }
3175
+ events$1.push({
3176
+ type: "content_block_delta",
3177
+ index: state$1.contentBlockIndex,
3178
+ delta: {
3179
+ type: "text_delta",
3180
+ text: delta.content
3181
+ }
3182
+ });
3183
+ }
3184
+ if (delta.tool_calls) for (const toolCall of delta.tool_calls) {
3185
+ if (toolCall.id && toolCall.function?.name) {
3186
+ if (state$1.contentBlockOpen) {
3187
+ events$1.push({
3188
+ type: "content_block_stop",
3189
+ index: state$1.contentBlockIndex
3190
+ });
3191
+ state$1.contentBlockIndex++;
3192
+ state$1.contentBlockOpen = false;
3193
+ }
3194
+ const anthropicBlockIndex = state$1.contentBlockIndex;
3195
+ state$1.toolCalls[toolCall.index] = {
3196
+ id: toolCall.id,
3197
+ name: toolCall.function.name,
2234
3198
  anthropicBlockIndex
2235
3199
  };
2236
3200
  events$1.push({
@@ -2279,111 +3243,266 @@ function translateChunkToAnthropicEvents(chunk, state$1, originalModel) {
2279
3243
  }
2280
3244
 
2281
3245
  //#endregion
2282
- //#region src/routes/messages/handler.ts
2283
- /** Collect all chunks and extract usage data */
2284
- async function collectChunksWithUsage(eventStream) {
2285
- const chunks = [];
2286
- let usage = null;
2287
- for await (const event of eventStream) {
2288
- if (!event.data || event.data === "[DONE]") continue;
3246
+ //#region src/routes/messages/subagent-marker.ts
3247
+ const subagentMarkerPrefix = "__SUBAGENT_MARKER__";
3248
+ const parseSubagentMarkerFromFirstUser = (payload) => {
3249
+ const firstUserMessage = payload.messages.find((msg) => msg.role === "user");
3250
+ if (!firstUserMessage || !Array.isArray(firstUserMessage.content)) return null;
3251
+ for (const block of firstUserMessage.content) {
3252
+ if (block.type !== "text") continue;
3253
+ const marker = parseSubagentMarkerFromSystemReminder(block.text);
3254
+ if (marker) return marker;
3255
+ }
3256
+ return null;
3257
+ };
3258
+ const parseSubagentMarkerFromSystemReminder = (text) => {
3259
+ const startTag = "<system-reminder>";
3260
+ const endTag = "</system-reminder>";
3261
+ let searchFrom = 0;
3262
+ while (true) {
3263
+ const reminderStart = text.indexOf(startTag, searchFrom);
3264
+ if (reminderStart === -1) break;
3265
+ const contentStart = reminderStart + 17;
3266
+ const reminderEnd = text.indexOf(endTag, contentStart);
3267
+ if (reminderEnd === -1) break;
3268
+ const reminderContent = text.slice(contentStart, reminderEnd);
3269
+ const markerIndex = reminderContent.indexOf(subagentMarkerPrefix);
3270
+ if (markerIndex === -1) {
3271
+ searchFrom = reminderEnd + 18;
3272
+ continue;
3273
+ }
3274
+ const markerJson = reminderContent.slice(markerIndex + 19).trim();
2289
3275
  try {
2290
- const chunk = JSON.parse(event.data);
2291
- chunks.push(chunk);
2292
- if (chunk.usage) usage = {
2293
- prompt_tokens: chunk.usage.prompt_tokens,
2294
- completion_tokens: chunk.usage.completion_tokens,
2295
- cached_tokens: chunk.usage.prompt_tokens_details?.cached_tokens ?? 0
2296
- };
2297
- } catch (error) {
2298
- consola.error("Failed to parse chunk:", error, event.data);
3276
+ const parsed = JSON.parse(markerJson);
3277
+ if (!parsed.session_id || !parsed.agent_id || !parsed.agent_type) {
3278
+ searchFrom = reminderEnd + 18;
3279
+ continue;
3280
+ }
3281
+ return parsed;
3282
+ } catch {
3283
+ searchFrom = reminderEnd + 18;
3284
+ continue;
2299
3285
  }
2300
3286
  }
2301
- return {
2302
- chunks,
2303
- usage
2304
- };
2305
- }
3287
+ return null;
3288
+ };
3289
+
3290
+ //#endregion
3291
+ //#region src/routes/messages/handler.ts
3292
+ const logger$1 = createHandlerLogger("messages-handler");
3293
+ const compactSystemPromptStart = "You are a helpful AI assistant tasked with summarizing conversations";
2306
3294
  async function handleCompletion(c) {
2307
3295
  await checkRateLimit(state);
2308
3296
  const anthropicPayload = await c.req.json();
2309
- consola.debug("Anthropic request payload:", JSON.stringify(anthropicPayload));
2310
- const translatedPayload = translateToOpenAI(anthropicPayload);
2311
- let openAIPayload = await applyReplacementsToPayload(translatedPayload);
2312
- openAIPayload = {
2313
- ...openAIPayload,
2314
- model: normalizeModelName(openAIPayload.model)
2315
- };
3297
+ logger$1.debug("Anthropic request payload:", JSON.stringify(anthropicPayload));
3298
+ const subagentMarker = parseSubagentMarkerFromFirstUser(anthropicPayload);
3299
+ const initiatorOverride = subagentMarker ? "agent" : void 0;
3300
+ if (subagentMarker) logger$1.debug("Detected Subagent marker:", JSON.stringify(subagentMarker));
3301
+ const isCompact = isCompactRequest(anthropicPayload);
3302
+ const anthropicBeta = c.req.header("anthropic-beta");
3303
+ logger$1.debug("Anthropic Beta header:", anthropicBeta);
3304
+ const noTools = !anthropicPayload.tools || anthropicPayload.tools.length === 0;
3305
+ if (anthropicBeta && noTools && !isCompact) anthropicPayload.model = getSmallModel();
3306
+ if (isCompact) {
3307
+ logger$1.debug("Is compact request:", isCompact);
3308
+ if (shouldCompactUseSmallModel()) anthropicPayload.model = getSmallModel();
3309
+ } else mergeToolResultForClaude(anthropicPayload);
2316
3310
  if (state.manualApprove) await awaitApproval();
2317
- const isAzureModel = isAzureOpenAIModel(openAIPayload.model);
2318
- if (isAzureModel) {
2319
- if (!state.azureOpenAIConfig) return c.json({ error: "Azure OpenAI not configured" }, 500);
2320
- setRequestContext(c, {
2321
- provider: "Azure OpenAI",
2322
- model: openAIPayload.model
2323
- });
2324
- } else setRequestContext(c, {
2325
- provider: "Copilot",
2326
- model: openAIPayload.model
3311
+ const selectedModel = state.models?.data.find((m) => m.id === anthropicPayload.model);
3312
+ if (shouldUseMessagesApi(selectedModel)) return await handleWithMessagesApi(c, anthropicPayload, {
3313
+ anthropicBetaHeader: anthropicBeta,
3314
+ initiatorOverride,
3315
+ selectedModel
2327
3316
  });
2328
- if (anthropicPayload.stream) {
2329
- const streamPayload = {
2330
- ...openAIPayload,
2331
- stream: true,
2332
- stream_options: { include_usage: true }
3317
+ if (shouldUseResponsesApi(selectedModel)) return await handleWithResponsesApi(c, anthropicPayload, initiatorOverride);
3318
+ return await handleWithChatCompletions(c, anthropicPayload, initiatorOverride);
3319
+ }
3320
+ const RESPONSES_ENDPOINT$1 = "/responses";
3321
+ const MESSAGES_ENDPOINT = "/v1/messages";
3322
+ const handleWithChatCompletions = async (c, anthropicPayload, initiatorOverride) => {
3323
+ const openAIPayload = translateToOpenAI(anthropicPayload);
3324
+ let finalPayload = await applyReplacementsToPayload(openAIPayload);
3325
+ finalPayload = {
3326
+ ...finalPayload,
3327
+ model: normalizeModelName(finalPayload.model)
3328
+ };
3329
+ logger$1.debug("Translated OpenAI request payload:", JSON.stringify(finalPayload));
3330
+ const response = await createChatCompletions(finalPayload, { initiator: initiatorOverride });
3331
+ if (isNonStreaming(response)) {
3332
+ logger$1.debug("Non-streaming response from Copilot:", JSON.stringify(response).slice(-400));
3333
+ const anthropicResponse = translateToAnthropic(response);
3334
+ logger$1.debug("Translated Anthropic response:", JSON.stringify(anthropicResponse));
3335
+ return c.json(anthropicResponse);
3336
+ }
3337
+ logger$1.debug("Streaming response from Copilot");
3338
+ return streamSSE(c, async (stream) => {
3339
+ const streamState = {
3340
+ messageStartSent: false,
3341
+ contentBlockIndex: 0,
3342
+ contentBlockOpen: false,
3343
+ toolCalls: {}
2333
3344
  };
2334
- const azureConfig = state.azureOpenAIConfig;
2335
- const eventStream = isAzureModel && azureConfig ? await createAzureOpenAIChatCompletions(azureConfig, streamPayload) : await createChatCompletions(streamPayload);
2336
- return streamSSE(c, async (stream) => {
2337
- const { chunks, usage } = await collectChunksWithUsage(eventStream);
2338
- consola.debug(`[stream] Collected ${chunks.length} chunks, usage:`, usage);
2339
- if (usage) {
2340
- setRequestContext(c, {
2341
- inputTokens: usage.prompt_tokens,
2342
- outputTokens: usage.completion_tokens
3345
+ for await (const rawEvent of response) {
3346
+ logger$1.debug("Copilot raw stream event:", JSON.stringify(rawEvent));
3347
+ if (rawEvent.data === "[DONE]") break;
3348
+ if (!rawEvent.data) continue;
3349
+ const chunk = JSON.parse(rawEvent.data);
3350
+ const events$1 = translateChunkToAnthropicEvents(chunk, streamState);
3351
+ for (const event of events$1) {
3352
+ logger$1.debug("Translated Anthropic event:", JSON.stringify(event));
3353
+ await stream.writeSSE({
3354
+ event: event.type,
3355
+ data: JSON.stringify(event)
2343
3356
  });
2344
- logTokenUsage(usage.prompt_tokens, usage.completion_tokens);
2345
3357
  }
2346
- const streamState = {
2347
- messageStartSent: false,
2348
- contentBlockOpen: false,
2349
- contentBlockIndex: 0,
2350
- toolCalls: {},
2351
- pendingUsage: usage ?? void 0
2352
- };
2353
- for (const chunk of chunks) {
2354
- const events$1 = translateChunkToAnthropicEvents(chunk, streamState, anthropicPayload.model);
2355
- for (const evt of events$1) {
2356
- consola.debug(`[stream] Emitting event: ${evt.type}`);
3358
+ }
3359
+ });
3360
+ };
3361
+ const handleWithResponsesApi = async (c, anthropicPayload, initiatorOverride) => {
3362
+ const responsesPayload = translateAnthropicMessagesToResponsesPayload(anthropicPayload);
3363
+ logger$1.debug("Translated Responses payload:", JSON.stringify(responsesPayload));
3364
+ const { vision, initiator } = getResponsesRequestOptions(responsesPayload);
3365
+ const response = await createResponses(responsesPayload, {
3366
+ vision,
3367
+ initiator: initiatorOverride ?? initiator
3368
+ });
3369
+ if (responsesPayload.stream && isAsyncIterable$1(response)) {
3370
+ logger$1.debug("Streaming response from Copilot (Responses API)");
3371
+ return streamSSE(c, async (stream) => {
3372
+ const streamState = createResponsesStreamState();
3373
+ for await (const chunk of response) {
3374
+ if (chunk.event === "ping") {
2357
3375
  await stream.writeSSE({
2358
- event: evt.type,
2359
- data: JSON.stringify(evt)
3376
+ event: "ping",
3377
+ data: "{\"type\":\"ping\"}"
2360
3378
  });
3379
+ continue;
3380
+ }
3381
+ const data = chunk.data;
3382
+ if (!data) continue;
3383
+ logger$1.debug("Responses raw stream event:", data);
3384
+ const events$1 = translateResponsesStreamEvent(JSON.parse(data), streamState);
3385
+ for (const event of events$1) {
3386
+ const eventData = JSON.stringify(event);
3387
+ logger$1.debug("Translated Anthropic event:", eventData);
3388
+ await stream.writeSSE({
3389
+ event: event.type,
3390
+ data: eventData
3391
+ });
3392
+ }
3393
+ if (streamState.messageCompleted) {
3394
+ logger$1.debug("Message completed, ending stream");
3395
+ break;
2361
3396
  }
2362
3397
  }
2363
- const fallbackEvents = createFallbackMessageDeltaEvents(streamState);
2364
- consola.debug(`[stream] Fallback events: ${fallbackEvents.length}, messageDeltaSent: ${streamState.messageDeltaSent}`);
2365
- for (const evt of fallbackEvents) {
2366
- consola.debug(`[stream] Emitting fallback event: ${evt.type}`);
3398
+ if (!streamState.messageCompleted) {
3399
+ logger$1.warn("Responses stream ended without completion; sending error event");
3400
+ const errorEvent = buildErrorEvent("Responses stream ended without completion");
2367
3401
  await stream.writeSSE({
2368
- event: evt.type,
2369
- data: JSON.stringify(evt)
3402
+ event: errorEvent.type,
3403
+ data: JSON.stringify(errorEvent)
2370
3404
  });
2371
3405
  }
2372
3406
  });
2373
3407
  }
2374
- const nonStreamPayload = {
2375
- ...openAIPayload,
2376
- stream: false
2377
- };
2378
- const azureConfigNonStream = state.azureOpenAIConfig;
2379
- const response = isAzureModel && azureConfigNonStream ? await createAzureOpenAIChatCompletions(azureConfigNonStream, nonStreamPayload) : await createChatCompletions(nonStreamPayload);
2380
- if (response.usage) setRequestContext(c, {
2381
- inputTokens: response.usage.prompt_tokens,
2382
- outputTokens: response.usage.completion_tokens
2383
- });
2384
- const anthropicResponse = translateToAnthropic(response, anthropicPayload.model);
3408
+ logger$1.debug("Non-streaming Responses result:", JSON.stringify(response).slice(-400));
3409
+ const anthropicResponse = translateResponsesResultToAnthropic(response);
3410
+ logger$1.debug("Translated Anthropic response:", JSON.stringify(anthropicResponse));
2385
3411
  return c.json(anthropicResponse);
2386
- }
3412
+ };
3413
+ const handleWithMessagesApi = async (c, anthropicPayload, options) => {
3414
+ const { anthropicBetaHeader, initiatorOverride, selectedModel } = options ?? {};
3415
+ for (const msg of anthropicPayload.messages) if (msg.role === "assistant" && Array.isArray(msg.content)) msg.content = msg.content.filter((block) => {
3416
+ if (block.type !== "thinking") return true;
3417
+ return block.thinking && block.thinking !== "Thinking..." && block.signature && !block.signature.includes("@");
3418
+ });
3419
+ if (selectedModel?.capabilities.supports.adaptive_thinking) {
3420
+ anthropicPayload.thinking = { type: "adaptive" };
3421
+ anthropicPayload.output_config = { effort: getAnthropicEffortForModel(anthropicPayload.model) };
3422
+ }
3423
+ logger$1.debug("Translated Messages payload:", JSON.stringify(anthropicPayload));
3424
+ const response = await createMessages(anthropicPayload, anthropicBetaHeader, { initiator: initiatorOverride });
3425
+ if (isAsyncIterable$1(response)) {
3426
+ logger$1.debug("Streaming response from Copilot (Messages API)");
3427
+ return streamSSE(c, async (stream) => {
3428
+ for await (const event of response) {
3429
+ const eventName = event.event;
3430
+ const data = event.data ?? "";
3431
+ logger$1.debug("Messages raw stream event:", data);
3432
+ await stream.writeSSE({
3433
+ event: eventName,
3434
+ data
3435
+ });
3436
+ }
3437
+ });
3438
+ }
3439
+ logger$1.debug("Non-streaming Messages result:", JSON.stringify(response).slice(-400));
3440
+ return c.json(response);
3441
+ };
3442
+ const shouldUseResponsesApi = (selectedModel) => {
3443
+ return selectedModel?.supported_endpoints?.includes(RESPONSES_ENDPOINT$1) ?? false;
3444
+ };
3445
+ const shouldUseMessagesApi = (selectedModel) => {
3446
+ return selectedModel?.supported_endpoints?.includes(MESSAGES_ENDPOINT) ?? false;
3447
+ };
3448
+ const isNonStreaming = (response) => Object.hasOwn(response, "choices");
3449
+ const isAsyncIterable$1 = (value) => Boolean(value) && typeof value[Symbol.asyncIterator] === "function";
3450
+ const getAnthropicEffortForModel = (model) => {
3451
+ const reasoningEffort = getReasoningEffortForModel(model);
3452
+ if (reasoningEffort === "xhigh") return "max";
3453
+ if (reasoningEffort === "none" || reasoningEffort === "minimal") return "low";
3454
+ return reasoningEffort;
3455
+ };
3456
+ const isCompactRequest = (anthropicPayload) => {
3457
+ const system = anthropicPayload.system;
3458
+ if (typeof system === "string") return system.startsWith(compactSystemPromptStart);
3459
+ if (!Array.isArray(system)) return false;
3460
+ return system.some((msg) => typeof msg.text === "string" && msg.text.startsWith(compactSystemPromptStart));
3461
+ };
3462
+ const mergeContentWithText = (tr, textBlock) => {
3463
+ if (typeof tr.content === "string") return {
3464
+ ...tr,
3465
+ content: `${tr.content}\n\n${textBlock.text}`
3466
+ };
3467
+ return {
3468
+ ...tr,
3469
+ content: [...tr.content, textBlock]
3470
+ };
3471
+ };
3472
+ const mergeContentWithTexts = (tr, textBlocks) => {
3473
+ if (typeof tr.content === "string") {
3474
+ const appendedTexts = textBlocks.map((tb) => tb.text).join("\n\n");
3475
+ return {
3476
+ ...tr,
3477
+ content: `${tr.content}\n\n${appendedTexts}`
3478
+ };
3479
+ }
3480
+ return {
3481
+ ...tr,
3482
+ content: [...tr.content, ...textBlocks]
3483
+ };
3484
+ };
3485
+ const mergeToolResultForClaude = (anthropicPayload) => {
3486
+ for (const msg of anthropicPayload.messages) {
3487
+ if (msg.role !== "user" || !Array.isArray(msg.content)) continue;
3488
+ const toolResults = [];
3489
+ const textBlocks = [];
3490
+ let valid = true;
3491
+ for (const block of msg.content) if (block.type === "tool_result") toolResults.push(block);
3492
+ else if (block.type === "text") textBlocks.push(block);
3493
+ else {
3494
+ valid = false;
3495
+ break;
3496
+ }
3497
+ if (!valid || toolResults.length === 0 || textBlocks.length === 0) continue;
3498
+ msg.content = mergeToolResult(toolResults, textBlocks);
3499
+ }
3500
+ };
3501
+ const mergeToolResult = (toolResults, textBlocks) => {
3502
+ if (toolResults.length === textBlocks.length) return toolResults.map((tr, i) => mergeContentWithText(tr, textBlocks[i]));
3503
+ const lastIndex = toolResults.length - 1;
3504
+ return toolResults.map((tr, i) => i === lastIndex ? mergeContentWithTexts(tr, textBlocks) : tr);
3505
+ };
2387
3506
 
2388
3507
  //#endregion
2389
3508
  //#region src/routes/messages/route.ts
@@ -2418,19 +3537,9 @@ modelRoutes.get("/", async (c) => {
2418
3537
  owned_by: model.vendor,
2419
3538
  display_name: model.name
2420
3539
  })) ?? [];
2421
- const azureModels = state.azureOpenAIDeployments?.map((deployment) => ({
2422
- id: deployment.id,
2423
- object: "model",
2424
- type: "model",
2425
- created: deployment.created,
2426
- created_at: (/* @__PURE__ */ new Date(deployment.created * 1e3)).toISOString(),
2427
- owned_by: deployment.owned_by,
2428
- display_name: `${deployment.deploymentName} (${deployment.model})`
2429
- })) ?? [];
2430
- const allModels = [...copilotModels, ...azureModels];
2431
3540
  return c.json({
2432
3541
  object: "list",
2433
- data: allModels,
3542
+ data: copilotModels,
2434
3543
  has_more: false
2435
3544
  });
2436
3545
  } catch (error) {
@@ -2476,6 +3585,130 @@ replacementsRoute.delete("/", async (c) => {
2476
3585
  return c.json({ success: true });
2477
3586
  });
2478
3587
 
3588
+ //#endregion
3589
+ //#region src/routes/responses/stream-id-sync.ts
3590
+ const createStreamIdTracker = () => ({ outputItems: /* @__PURE__ */ new Map() });
3591
+ const fixStreamIds = (data, event, tracker) => {
3592
+ if (!data) return data;
3593
+ const parsed = JSON.parse(data);
3594
+ switch (event) {
3595
+ case "response.output_item.added": return handleOutputItemAdded(parsed, tracker);
3596
+ case "response.output_item.done": return handleOutputItemDone(parsed, tracker);
3597
+ default: return handleItemId(parsed, tracker);
3598
+ }
3599
+ };
3600
+ const handleOutputItemAdded = (parsed, tracker) => {
3601
+ if (!parsed.item.id) {
3602
+ let randomSuffix = "";
3603
+ while (randomSuffix.length < 16) randomSuffix += Math.random().toString(36).slice(2);
3604
+ parsed.item.id = `oi_${parsed.output_index}_${randomSuffix.slice(0, 16)}`;
3605
+ }
3606
+ const outputIndex = parsed.output_index;
3607
+ tracker.outputItems.set(outputIndex, parsed.item.id);
3608
+ return JSON.stringify(parsed);
3609
+ };
3610
+ const handleOutputItemDone = (parsed, tracker) => {
3611
+ const outputIndex = parsed.output_index;
3612
+ const originalId = tracker.outputItems.get(outputIndex);
3613
+ if (originalId) parsed.item.id = originalId;
3614
+ return JSON.stringify(parsed);
3615
+ };
3616
+ const handleItemId = (parsed, tracker) => {
3617
+ const outputIndex = parsed.output_index;
3618
+ if (outputIndex !== void 0) {
3619
+ const itemId = tracker.outputItems.get(outputIndex);
3620
+ if (itemId) parsed.item_id = itemId;
3621
+ }
3622
+ return JSON.stringify(parsed);
3623
+ };
3624
+
3625
+ //#endregion
3626
+ //#region src/routes/responses/handler.ts
3627
+ const logger = createHandlerLogger("responses-handler");
3628
+ const RESPONSES_ENDPOINT = "/responses";
3629
+ const handleResponses = async (c) => {
3630
+ await checkRateLimit(state);
3631
+ const payload = await c.req.json();
3632
+ setRequestContext(c, {
3633
+ provider: "Copilot (Responses)",
3634
+ model: payload.model
3635
+ });
3636
+ logger.debug("Responses request payload:", JSON.stringify(payload));
3637
+ useFunctionApplyPatch(payload);
3638
+ removeWebSearchTool(payload);
3639
+ if (!((state.models?.data.find((model) => model.id === payload.model))?.supported_endpoints?.includes(RESPONSES_ENDPOINT) ?? false)) return c.json({ error: {
3640
+ message: "This model does not support the responses endpoint. Please choose a different model.",
3641
+ type: "invalid_request_error"
3642
+ } }, 400);
3643
+ const { vision, initiator } = getResponsesRequestOptions(payload);
3644
+ if (state.manualApprove) await awaitApproval();
3645
+ const response = await createResponses(payload, {
3646
+ vision,
3647
+ initiator
3648
+ });
3649
+ if (isStreamingRequested(payload) && isAsyncIterable(response)) {
3650
+ logger.debug("Forwarding native Responses stream");
3651
+ return streamSSE(c, async (stream) => {
3652
+ const idTracker = createStreamIdTracker();
3653
+ for await (const chunk of response) {
3654
+ logger.debug("Responses stream chunk:", JSON.stringify(chunk));
3655
+ const processedData = fixStreamIds(chunk.data ?? "", chunk.event, idTracker);
3656
+ await stream.writeSSE({
3657
+ id: chunk.id,
3658
+ event: chunk.event,
3659
+ data: processedData
3660
+ });
3661
+ }
3662
+ });
3663
+ }
3664
+ logger.debug("Forwarding native Responses result:", JSON.stringify(response).slice(-400));
3665
+ return c.json(response);
3666
+ };
3667
+ const isAsyncIterable = (value) => Boolean(value) && typeof value[Symbol.asyncIterator] === "function";
3668
+ const isStreamingRequested = (payload) => Boolean(payload.stream);
3669
+ const useFunctionApplyPatch = (payload) => {
3670
+ if (getConfig().useFunctionApplyPatch ?? true) {
3671
+ logger.debug("Using function tool apply_patch for responses");
3672
+ if (Array.isArray(payload.tools)) {
3673
+ const toolsArr = payload.tools;
3674
+ for (let i = 0; i < toolsArr.length; i++) {
3675
+ const t = toolsArr[i];
3676
+ if (t.type === "custom" && t.name === "apply_patch") toolsArr[i] = {
3677
+ type: "function",
3678
+ name: t.name,
3679
+ description: "Use the `apply_patch` tool to edit files",
3680
+ parameters: {
3681
+ type: "object",
3682
+ properties: { input: {
3683
+ type: "string",
3684
+ description: "The entire contents of the apply_patch command"
3685
+ } },
3686
+ required: ["input"]
3687
+ },
3688
+ strict: false
3689
+ };
3690
+ }
3691
+ }
3692
+ }
3693
+ };
3694
+ const removeWebSearchTool = (payload) => {
3695
+ if (!Array.isArray(payload.tools) || payload.tools.length === 0) return;
3696
+ payload.tools = payload.tools.filter((t) => {
3697
+ return t.type !== "web_search";
3698
+ });
3699
+ };
3700
+
3701
+ //#endregion
3702
+ //#region src/routes/responses/route.ts
3703
+ const responsesRoutes = new Hono();
3704
+ responsesRoutes.post("/", async (c) => {
3705
+ try {
3706
+ return await handleResponses(c);
3707
+ } catch (error) {
3708
+ return await forwardError(c, error);
3709
+ }
3710
+ });
3711
+
2479
3712
  //#endregion
2480
3713
  //#region src/routes/token/route.ts
2481
3714
  const tokenRoute = new Hono();
@@ -2509,6 +3742,7 @@ usageRoute.get("/", async (c) => {
2509
3742
  const server = new Hono();
2510
3743
  server.use(requestLogger);
2511
3744
  server.use(cors());
3745
+ server.use("*", createAuthMiddleware());
2512
3746
  server.get("/", (c) => c.text("Server running"));
2513
3747
  server.route("/chat/completions", completionRoutes);
2514
3748
  server.route("/models", modelRoutes);
@@ -2516,9 +3750,11 @@ server.route("/embeddings", embeddingRoutes);
2516
3750
  server.route("/usage", usageRoute);
2517
3751
  server.route("/token", tokenRoute);
2518
3752
  server.route("/replacements", replacementsRoute);
3753
+ server.route("/responses", responsesRoutes);
2519
3754
  server.route("/v1/chat/completions", completionRoutes);
2520
3755
  server.route("/v1/models", modelRoutes);
2521
3756
  server.route("/v1/embeddings", embeddingRoutes);
3757
+ server.route("/v1/responses", responsesRoutes);
2522
3758
  server.route("/v1/messages", messageRoutes);
2523
3759
 
2524
3760
  //#endregion
@@ -2541,8 +3777,10 @@ async function runServer(options) {
2541
3777
  state.rateLimitWait = options.rateLimitWait;
2542
3778
  state.showToken = options.showToken;
2543
3779
  state.debug = options.debug;
3780
+ state.verbose = options.verbose;
2544
3781
  if (options.debug) consola.info("Debug mode enabled - raw HTTP requests will be logged");
2545
3782
  await ensurePaths();
3783
+ mergeConfigWithDefaults();
2546
3784
  await cacheVSCodeVersion();
2547
3785
  if (options.githubToken) {
2548
3786
  state.githubToken = options.githubToken;
@@ -2550,10 +3788,7 @@ async function runServer(options) {
2550
3788
  } else await setupGitHubToken();
2551
3789
  await setupCopilotToken();
2552
3790
  await cacheModels();
2553
- await setupAzureOpenAI();
2554
- const copilotModelIds = state.models?.data.map((model) => model.id) ?? [];
2555
- const azureModelIds = state.azureOpenAIDeployments?.map((deployment) => deployment.id) ?? [];
2556
- const allModelIds = [...copilotModelIds, ...azureModelIds];
3791
+ const allModelIds = state.models?.data.map((model) => model.id) ?? [];
2557
3792
  consola.info(`Available models: \n${allModelIds.map((id) => `- ${id}`).join("\n")}`);
2558
3793
  const serverUrl = `http://localhost:${options.port}`;
2559
3794
  if (options.claudeCode) {