@hsupu/copilot-api 0.7.18-beta.3 → 0.7.19

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.
package/dist/main.mjs CHANGED
@@ -63,24 +63,25 @@ const DEFAULT_MODEL_OVERRIDES = {
63
63
  };
64
64
  const state = {
65
65
  accountType: "individual",
66
- modelIndex: /* @__PURE__ */ new Map(),
67
- modelIds: /* @__PURE__ */ new Set(),
68
- showGitHubToken: false,
69
- verbose: false,
70
66
  autoTruncate: true,
71
67
  compressToolResultsBeforeTruncate: true,
72
68
  convertServerToolsToCustom: true,
73
- modelOverrides: { ...DEFAULT_MODEL_OVERRIDES },
74
69
  dedupToolCalls: false,
75
- rewriteSystemReminders: false,
76
- truncateReadToolResult: false,
77
- systemPromptOverrides: [],
70
+ fetchTimeout: 300,
71
+ filterToolSearchBlocks: false,
78
72
  historyLimit: 200,
79
- fetchTimeout: 60,
80
- streamIdleTimeout: 300,
81
- shutdownGracefulWait: 60,
73
+ modelIds: /* @__PURE__ */ new Set(),
74
+ modelIndex: /* @__PURE__ */ new Map(),
75
+ modelOverrides: { ...DEFAULT_MODEL_OVERRIDES },
76
+ rewriteSystemReminders: false,
77
+ showGitHubToken: false,
82
78
  shutdownAbortWait: 120,
83
- staleRequestMaxAge: 600
79
+ shutdownGracefulWait: 60,
80
+ staleRequestMaxAge: 600,
81
+ streamIdleTimeout: 300,
82
+ systemPromptOverrides: [],
83
+ truncateReadToolResult: false,
84
+ verbose: false
84
85
  };
85
86
 
86
87
  //#endregion
@@ -105,7 +106,7 @@ const GITHUB_API_VERSION = "2022-11-28";
105
106
  */
106
107
  const INTERACTION_ID = randomUUID();
107
108
  const copilotBaseUrl = (state) => state.accountType === "individual" ? "https://api.githubcopilot.com" : `https://api.${state.accountType}.githubcopilot.com`;
108
- const copilotHeaders = (state, vision = false) => {
109
+ const copilotHeaders = (state, opts) => {
109
110
  const headers = {
110
111
  Authorization: `Bearer ${state.copilotToken}`,
111
112
  "content-type": standardHeaders()["content-type"],
@@ -113,13 +114,17 @@ const copilotHeaders = (state, vision = false) => {
113
114
  "editor-version": `vscode/${state.vsCodeVersion}`,
114
115
  "editor-plugin-version": EDITOR_PLUGIN_VERSION,
115
116
  "user-agent": USER_AGENT,
116
- "openai-intent": "conversation-panel",
117
+ "openai-intent": opts?.intent ?? "conversation-panel",
117
118
  "x-github-api-version": COPILOT_API_VERSION,
118
119
  "x-request-id": randomUUID(),
119
120
  "X-Interaction-Id": INTERACTION_ID,
120
121
  "x-vscode-user-agent-library-version": "electron-fetch"
121
122
  };
122
- if (vision) headers["copilot-vision-request"] = "true";
123
+ if (opts?.vision) headers["copilot-vision-request"] = "true";
124
+ if (opts?.modelRequestHeaders) {
125
+ const coreKeysLower = new Set(Object.keys(headers).map((k) => k.toLowerCase()));
126
+ for (const [key, value] of Object.entries(opts.modelRequestHeaders)) if (!coreKeysLower.has(key.toLowerCase())) headers[key] = value;
127
+ }
123
128
  return headers;
124
129
  };
125
130
  const GITHUB_API_BASE_URL = "https://api.github.com";
@@ -744,7 +749,10 @@ function forwardError(c, error) {
744
749
  consola.warn(`HTTP 429: Rate limit exceeded`);
745
750
  return c.json(formattedError, 429);
746
751
  }
747
- consola.error(`HTTP ${error.status}:`, errorJson);
752
+ if (typeof errorJson === "string") {
753
+ const preview = errorJson.trimStart().startsWith("<") ? `[HTML ${errorJson.length} bytes]` : truncateForLog(errorJson, 200);
754
+ consola.error(`HTTP ${error.status}: ${preview}`);
755
+ } else consola.error(`HTTP ${error.status}:`, errorJson);
748
756
  return c.json({ error: {
749
757
  message: error.responseText,
750
758
  type: "error"
@@ -984,6 +992,11 @@ function formatErrorWithCause(error) {
984
992
  if (error.cause instanceof Error && error.cause.message && error.cause.message !== error.message) msg += ` (cause: ${stripBunVerboseHint(error.cause.message)})`;
985
993
  return msg;
986
994
  }
995
+ /** Truncate a string for log display, adding ellipsis if truncated */
996
+ function truncateForLog(text, maxLen) {
997
+ if (text.length <= maxLen) return text;
998
+ return `${text.slice(0, maxLen)}… (${text.length} bytes total)`;
999
+ }
987
1000
  /** Extract error message with fallback. For HTTPError, extracts the actual API error response. */
988
1001
  function getErrorMessage(error, fallback = "Unknown error") {
989
1002
  if (error instanceof Error) {
@@ -1203,7 +1216,10 @@ function generateId(randomLength = 7) {
1203
1216
  //#region src/lib/token/github-client.ts
1204
1217
  /** GitHub OAuth API client — device code flow and user info */
1205
1218
  const getGitHubUser = async () => {
1206
- const response = await fetch(`${GITHUB_API_BASE_URL}/user`, { headers: githubHeaders(state) });
1219
+ const response = await fetch(`${GITHUB_API_BASE_URL}/user`, {
1220
+ headers: githubHeaders(state),
1221
+ signal: AbortSignal.timeout(15e3)
1222
+ });
1207
1223
  if (!response.ok) throw await HTTPError.fromResponse("Failed to get GitHub user", response);
1208
1224
  return await response.json();
1209
1225
  };
@@ -1214,7 +1230,8 @@ const getDeviceCode = async () => {
1214
1230
  body: JSON.stringify({
1215
1231
  client_id: GITHUB_CLIENT_ID,
1216
1232
  scope: "read:user"
1217
- })
1233
+ }),
1234
+ signal: AbortSignal.timeout(15e3)
1218
1235
  });
1219
1236
  if (!response.ok) throw await HTTPError.fromResponse("Failed to get device code", response);
1220
1237
  return await response.json();
@@ -1231,7 +1248,8 @@ async function pollAccessToken(deviceCode) {
1231
1248
  client_id: GITHUB_CLIENT_ID,
1232
1249
  device_code: deviceCode.device_code,
1233
1250
  grant_type: "urn:ietf:params:oauth:grant-type:device_code"
1234
- })
1251
+ }),
1252
+ signal: AbortSignal.timeout(15e3)
1235
1253
  });
1236
1254
  if (!response.ok) {
1237
1255
  await sleep(sleepDuration);
@@ -1405,7 +1423,9 @@ var DeviceAuthProvider = class extends GitHubTokenProvider {
1405
1423
  refreshable: true
1406
1424
  };
1407
1425
  } catch (error) {
1426
+ const cause = error instanceof TypeError && error.cause ? error.cause : void 0;
1408
1427
  consola.error("Device authorization failed:", error);
1428
+ if (cause) consola.error("Caused by:", cause);
1409
1429
  return null;
1410
1430
  }
1411
1431
  }
@@ -1735,6 +1755,17 @@ const checkUsage = defineCommand({
1735
1755
  }
1736
1756
  });
1737
1757
 
1758
+ //#endregion
1759
+ //#region src/lib/fetch-utils.ts
1760
+ /**
1761
+ * Create an AbortSignal for fetch timeout if configured.
1762
+ * Controls the time from request start to receiving response headers.
1763
+ * Returns undefined if fetchTimeout is 0 (disabled).
1764
+ */
1765
+ function createFetchSignal() {
1766
+ return state.fetchTimeout > 0 ? AbortSignal.timeout(state.fetchTimeout * 1e3) : void 0;
1767
+ }
1768
+
1738
1769
  //#endregion
1739
1770
  //#region src/lib/models/client.ts
1740
1771
  /** Fetch models from Copilot API and cache in global state */
@@ -1743,7 +1774,10 @@ async function cacheModels() {
1743
1774
  rebuildModelIndex();
1744
1775
  }
1745
1776
  const getModels = async () => {
1746
- const response = await fetch(`${copilotBaseUrl(state)}/models`, { headers: copilotHeaders(state) });
1777
+ const response = await fetch(`${copilotBaseUrl(state)}/models`, {
1778
+ headers: copilotHeaders(state),
1779
+ signal: createFetchSignal()
1780
+ });
1747
1781
  if (!response.ok) throw await HTTPError.fromResponse("Failed to get models", response);
1748
1782
  return await response.json();
1749
1783
  };
@@ -4247,7 +4281,7 @@ const setupClaudeCode = defineCommand({
4247
4281
 
4248
4282
  //#endregion
4249
4283
  //#region package.json
4250
- var version = "0.7.18-beta.3";
4284
+ var version = "0.7.19";
4251
4285
 
4252
4286
  //#endregion
4253
4287
  //#region src/lib/config/config.ts
@@ -4346,6 +4380,7 @@ async function applyConfigToState() {
4346
4380
  if (a.convert_server_tools_to_custom !== void 0) state.convertServerToolsToCustom = a.convert_server_tools_to_custom;
4347
4381
  if (a.dedup_tool_calls !== void 0) state.dedupToolCalls = a.dedup_tool_calls === true ? "input" : a.dedup_tool_calls;
4348
4382
  if (a.truncate_read_tool_result !== void 0) state.truncateReadToolResult = a.truncate_read_tool_result;
4383
+ if (a.filter_tool_search_blocks !== void 0) state.filterToolSearchBlocks = a.filter_tool_search_blocks;
4349
4384
  if (a.rewrite_system_reminders !== void 0) {
4350
4385
  if (typeof a.rewrite_system_reminders === "boolean") state.rewriteSystemReminders = a.rewrite_system_reminders;
4351
4386
  else if (Array.isArray(a.rewrite_system_reminders)) state.rewriteSystemReminders = compileRewriteRules(a.rewrite_system_reminders);
@@ -4557,7 +4592,8 @@ function toHistoryResponse(entryData) {
4557
4592
  input_tokens: r.usage.input_tokens,
4558
4593
  output_tokens: r.usage.output_tokens,
4559
4594
  cache_read_input_tokens: r.usage.cache_read_input_tokens,
4560
- cache_creation_input_tokens: r.usage.cache_creation_input_tokens
4595
+ cache_creation_input_tokens: r.usage.cache_creation_input_tokens,
4596
+ output_tokens_details: r.usage.output_tokens_details
4561
4597
  },
4562
4598
  stop_reason: r.stop_reason,
4563
4599
  error: r.error,
@@ -5059,7 +5095,18 @@ function responsesInputToMessages(input) {
5059
5095
  content: `[item_reference: ${item.id ?? "unknown"}]`
5060
5096
  });
5061
5097
  break;
5062
- default: break;
5098
+ case "reasoning":
5099
+ messages.push({
5100
+ role: "assistant",
5101
+ content: `[reasoning: ${item.id ?? "unknown"}]`
5102
+ });
5103
+ break;
5104
+ default:
5105
+ if (item.type && item.id) messages.push({
5106
+ role: "system",
5107
+ content: `[${item.type}: ${item.id}]`
5108
+ });
5109
+ break;
5063
5110
  }
5064
5111
  return messages;
5065
5112
  }
@@ -5083,6 +5130,10 @@ function responsesOutputToContent(output) {
5083
5130
  arguments: item.arguments
5084
5131
  }
5085
5132
  });
5133
+ if (item.type === "reasoning") {
5134
+ const summaryText = item.summary.map((s) => s.text).filter(Boolean).join("\n");
5135
+ if (summaryText) textParts.push(`[Reasoning: ${summaryText}]`);
5136
+ }
5086
5137
  }
5087
5138
  if (textParts.length === 0 && toolCalls.length === 0) return null;
5088
5139
  return {
@@ -5104,7 +5155,9 @@ function createResponsesStreamAccumulator() {
5104
5155
  responseId: "",
5105
5156
  toolCalls: [],
5106
5157
  toolCallMap: /* @__PURE__ */ new Map(),
5107
- contentParts: []
5158
+ contentParts: [],
5159
+ reasoningTokens: 0,
5160
+ cachedInputTokens: 0
5108
5161
  };
5109
5162
  }
5110
5163
  /** Get the final accumulated content string */
@@ -5129,6 +5182,8 @@ function accumulateResponsesStreamEvent(event, acc) {
5129
5182
  if (event.response.usage) {
5130
5183
  acc.inputTokens = event.response.usage.input_tokens;
5131
5184
  acc.outputTokens = event.response.usage.output_tokens;
5185
+ acc.reasoningTokens = event.response.usage.output_tokens_details?.reasoning_tokens ?? 0;
5186
+ acc.cachedInputTokens = event.response.usage.input_tokens_details?.cached_tokens ?? 0;
5132
5187
  }
5133
5188
  break;
5134
5189
  case "response.failed":
@@ -5303,30 +5358,31 @@ function mapAnthropicContentBlocks(acc) {
5303
5358
  const { _generic: _, ...rest } = block;
5304
5359
  return rest;
5305
5360
  }
5306
- switch (block.type) {
5361
+ if ("_brand" in block) return {
5362
+ type: block.type,
5363
+ tool_use_id: block.tool_use_id,
5364
+ content: block.content
5365
+ };
5366
+ const narrowed = block;
5367
+ switch (narrowed.type) {
5307
5368
  case "text": return {
5308
5369
  type: "text",
5309
- text: block.text
5370
+ text: narrowed.text
5310
5371
  };
5311
5372
  case "thinking": return {
5312
5373
  type: "thinking",
5313
- thinking: block.thinking
5374
+ thinking: narrowed.thinking
5314
5375
  };
5315
5376
  case "redacted_thinking": return { type: "redacted_thinking" };
5316
5377
  case "tool_use":
5317
5378
  case "server_tool_use": return {
5318
- type: block.type,
5319
- id: block.id,
5320
- name: block.name,
5321
- input: safeParseJson(block.input)
5322
- };
5323
- case "web_search_tool_result": return {
5324
- type: "web_search_tool_result",
5325
- tool_use_id: block.tool_use_id,
5326
- content: block.content
5379
+ type: narrowed.type,
5380
+ id: narrowed.id,
5381
+ name: narrowed.name,
5382
+ input: safeParseJson(narrowed.input)
5327
5383
  };
5328
5384
  default: {
5329
- const unknown = block;
5385
+ const unknown = narrowed;
5330
5386
  consola.warn(`[recording] Unhandled content block type in stream result: ${unknown.type}`);
5331
5387
  return { type: unknown.type };
5332
5388
  }
@@ -5379,6 +5435,7 @@ function buildOpenAIResponseData(acc, fallbackModel) {
5379
5435
  usage: {
5380
5436
  input_tokens: acc.inputTokens,
5381
5437
  output_tokens: acc.outputTokens,
5438
+ ...acc.reasoningTokens > 0 && { output_tokens_details: { reasoning_tokens: acc.reasoningTokens } },
5382
5439
  ...acc.cachedTokens > 0 && { cache_read_input_tokens: acc.cachedTokens }
5383
5440
  },
5384
5441
  stop_reason: acc.finishReason || void 0,
@@ -5414,7 +5471,9 @@ function buildResponsesResponseData(acc, fallbackModel) {
5414
5471
  model: acc.model || fallbackModel,
5415
5472
  usage: {
5416
5473
  input_tokens: acc.inputTokens,
5417
- output_tokens: acc.outputTokens
5474
+ output_tokens: acc.outputTokens,
5475
+ ...acc.reasoningTokens > 0 && { output_tokens_details: { reasoning_tokens: acc.reasoningTokens } },
5476
+ ...acc.cachedInputTokens > 0 && { cache_read_input_tokens: acc.cachedInputTokens }
5418
5477
  },
5419
5478
  stop_reason: acc.status || void 0,
5420
5479
  content: finalContent || toolCalls.length > 0 ? {
@@ -5595,26 +5654,20 @@ async function processResponsesInstructions(instructions, model) {
5595
5654
  return processSystemPromptText(instructions, model);
5596
5655
  }
5597
5656
 
5598
- //#endregion
5599
- //#region src/lib/fetch-utils.ts
5600
- /**
5601
- * Create an AbortSignal for fetch timeout if configured.
5602
- * Controls the time from request start to receiving response headers.
5603
- * Returns undefined if fetchTimeout is 0 (disabled).
5604
- */
5605
- function createFetchSignal() {
5606
- return state.fetchTimeout > 0 ? AbortSignal.timeout(state.fetchTimeout * 1e3) : void 0;
5607
- }
5608
-
5609
5657
  //#endregion
5610
5658
  //#region src/lib/openai/responses-client.ts
5611
5659
  /** Call Copilot /responses endpoint */
5612
- const createResponses = async (payload) => {
5660
+ const createResponses = async (payload, opts) => {
5613
5661
  if (!state.copilotToken) throw new Error("Copilot token not found");
5614
5662
  const enableVision = hasVisionContent(payload.input);
5615
5663
  const isAgentCall = Array.isArray(payload.input) && payload.input.some((item) => item.role === "assistant" || item.type === "function_call" || item.type === "function_call_output");
5664
+ const modelSupportsVision = opts?.resolvedModel?.capabilities?.supports?.vision !== false;
5616
5665
  const headers = {
5617
- ...copilotHeaders(state, enableVision),
5666
+ ...copilotHeaders(state, {
5667
+ vision: enableVision && modelSupportsVision,
5668
+ modelRequestHeaders: opts?.resolvedModel?.request_headers,
5669
+ intent: isAgentCall ? "conversation-agent" : "conversation-panel"
5670
+ }),
5618
5671
  "X-Initiator": isAgentCall ? "agent" : "user"
5619
5672
  };
5620
5673
  const fetchSignal = createFetchSignal();
@@ -5740,7 +5793,7 @@ function createTokenRefreshStrategy() {
5740
5793
  * centralizes that configuration to avoid duplication.
5741
5794
  */
5742
5795
  /** Create the FormatAdapter for Responses API pipeline execution */
5743
- function createResponsesAdapter() {
5796
+ function createResponsesAdapter(selectedModel) {
5744
5797
  return {
5745
5798
  format: "openai-responses",
5746
5799
  sanitize: (p) => ({
@@ -5748,7 +5801,7 @@ function createResponsesAdapter() {
5748
5801
  removedCount: 0,
5749
5802
  systemReminderRemovals: 0
5750
5803
  }),
5751
- execute: (p) => executeWithAdaptiveRateLimit(() => createResponses(p)),
5804
+ execute: (p) => executeWithAdaptiveRateLimit(() => createResponses(p, { resolvedModel: selectedModel })),
5752
5805
  logPayloadSize: (p) => {
5753
5806
  const count = typeof p.input === "string" ? 1 : p.input.length;
5754
5807
  consola.debug(`Responses payload: ${count} input item(s), model: ${p.model}`);
@@ -5840,7 +5893,7 @@ async function handleResponseCreate(ws, payload) {
5840
5893
  model: resolvedModel,
5841
5894
  clientModel: requestedModel
5842
5895
  });
5843
- const adapter = createResponsesAdapter();
5896
+ const adapter = createResponsesAdapter(selectedModel);
5844
5897
  const strategies = createResponsesStrategies();
5845
5898
  try {
5846
5899
  const iterator = (await executeRequestPipeline({
@@ -6658,12 +6711,17 @@ function createTruncationResponseMarkerOpenAI(result) {
6658
6711
 
6659
6712
  //#endregion
6660
6713
  //#region src/lib/openai/client.ts
6661
- const createChatCompletions = async (payload) => {
6714
+ const createChatCompletions = async (payload, opts) => {
6662
6715
  if (!state.copilotToken) throw new Error("Copilot token not found");
6663
6716
  const enableVision = payload.messages.some((x) => typeof x.content !== "string" && x.content?.some((x) => x.type === "image_url"));
6664
6717
  const isAgentCall = payload.messages.some((msg) => ["assistant", "tool"].includes(msg.role));
6718
+ const modelSupportsVision = opts?.resolvedModel?.capabilities?.supports?.vision !== false;
6665
6719
  const headers = {
6666
- ...copilotHeaders(state, enableVision),
6720
+ ...copilotHeaders(state, {
6721
+ vision: enableVision && modelSupportsVision,
6722
+ modelRequestHeaders: opts?.resolvedModel?.request_headers,
6723
+ intent: isAgentCall ? "conversation-agent" : "conversation-panel"
6724
+ }),
6667
6725
  "X-Initiator": isAgentCall ? "agent" : "user"
6668
6726
  };
6669
6727
  const fetchSignal = createFetchSignal();
@@ -6792,6 +6850,7 @@ function createOpenAIStreamAccumulator() {
6792
6850
  inputTokens: 0,
6793
6851
  outputTokens: 0,
6794
6852
  cachedTokens: 0,
6853
+ reasoningTokens: 0,
6795
6854
  finishReason: "",
6796
6855
  content: "",
6797
6856
  toolCalls: [],
@@ -6805,6 +6864,7 @@ function accumulateOpenAIStreamEvent(parsed, acc) {
6805
6864
  acc.inputTokens = parsed.usage.prompt_tokens;
6806
6865
  acc.outputTokens = parsed.usage.completion_tokens;
6807
6866
  if (parsed.usage.prompt_tokens_details?.cached_tokens !== void 0) acc.cachedTokens = parsed.usage.prompt_tokens_details.cached_tokens;
6867
+ if (parsed.usage.completion_tokens_details?.reasoning_tokens !== void 0) acc.reasoningTokens = parsed.usage.completion_tokens_details.reasoning_tokens;
6808
6868
  }
6809
6869
  const choice = parsed.choices[0];
6810
6870
  if (choice) {
@@ -7057,7 +7117,7 @@ async function executeRequest(opts) {
7057
7117
  const adapter = {
7058
7118
  format: "openai-chat-completions",
7059
7119
  sanitize: (p) => sanitizeOpenAIMessages(p),
7060
- execute: (p) => executeWithAdaptiveRateLimit(() => createChatCompletions(p)),
7120
+ execute: (p) => executeWithAdaptiveRateLimit(() => createChatCompletions(p, { resolvedModel: selectedModel })),
7061
7121
  logPayloadSize: (p) => logPayloadSizeInfo(p, selectedModel)
7062
7122
  };
7063
7123
  const strategies = [
@@ -7228,7 +7288,8 @@ const createEmbeddings = async (payload) => {
7228
7288
  const response = await fetch(`${copilotBaseUrl(state)}/embeddings`, {
7229
7289
  method: "POST",
7230
7290
  headers: copilotHeaders(state),
7231
- body: JSON.stringify(normalizedPayload)
7291
+ body: JSON.stringify(normalizedPayload),
7292
+ signal: createFetchSignal()
7232
7293
  });
7233
7294
  if (!response.ok) throw await HTTPError.fromResponse("Failed to create embeddings", response);
7234
7295
  return await response.json();
@@ -7946,9 +8007,6 @@ function contentToText(content, options) {
7946
8007
  case "server_tool_use":
7947
8008
  parts.push(`[server_tool_use: ${block.name}]`, JSON.stringify(block.input));
7948
8009
  break;
7949
- case "web_search_tool_result":
7950
- parts.push(`[web_search_tool_result]`);
7951
- break;
7952
8010
  default: {
7953
8011
  const genericBlock = block;
7954
8012
  if ("tool_use_id" in genericBlock && genericBlock.type !== "image") {
@@ -8647,7 +8705,7 @@ const NON_DEFERRED_TOOL_NAMES = new Set([
8647
8705
  "switch_agent",
8648
8706
  ...CLAUDE_CODE_OFFICIAL_TOOLS
8649
8707
  ]);
8650
- const TOOL_SEARCH_TOOL_NAME = "tool_search_tool_regex";
8708
+ const TOOL_SEARCH_TOOL_NAME$1 = "tool_search_tool_regex";
8651
8709
  const TOOL_SEARCH_TOOL_TYPE = "tool_search_tool_regex_20251119";
8652
8710
  const EMPTY_INPUT_SCHEMA = {
8653
8711
  type: "object",
@@ -8706,12 +8764,12 @@ function buildHistoryToolStubs(historyToolNames) {
8706
8764
  * Returns a new array — never mutates the input.
8707
8765
  */
8708
8766
  function processToolPipeline(tools, modelId, messages) {
8709
- const existingNames = new Set(tools.map((t) => t.name));
8767
+ const existingNamesLower = new Set(tools.map((t) => t.name.toLowerCase()));
8710
8768
  const toolSearchEnabled = modelSupportsToolSearch(modelId);
8711
8769
  const historyToolNames = toolSearchEnabled ? collectHistoryToolNames(messages) : void 0;
8712
8770
  const result = [];
8713
8771
  if (toolSearchEnabled) result.push({
8714
- name: TOOL_SEARCH_TOOL_NAME,
8772
+ name: TOOL_SEARCH_TOOL_NAME$1,
8715
8773
  type: TOOL_SEARCH_TOOL_TYPE,
8716
8774
  defer_loading: false
8717
8775
  });
@@ -8723,7 +8781,7 @@ function processToolPipeline(tools, modelId, messages) {
8723
8781
  defer_loading: true
8724
8782
  } : normalized);
8725
8783
  }
8726
- for (const name of CLAUDE_CODE_OFFICIAL_TOOLS) if (!existingNames.has(name)) {
8784
+ for (const name of CLAUDE_CODE_OFFICIAL_TOOLS) if (!existingNamesLower.has(name.toLowerCase())) {
8727
8785
  const stub = {
8728
8786
  name,
8729
8787
  description: `Claude Code ${name} tool`,
@@ -8914,7 +8972,7 @@ function adjustThinkingBudget(wire) {
8914
8972
  * Create messages using Anthropic-style API directly.
8915
8973
  * Calls Copilot's native Anthropic endpoint for Anthropic-vendor models.
8916
8974
  */
8917
- async function createAnthropicMessages(payload) {
8975
+ async function createAnthropicMessages(payload, opts) {
8918
8976
  if (!state.copilotToken) throw new Error("Copilot token not found");
8919
8977
  const wire = buildWirePayload(payload);
8920
8978
  adjustThinkingBudget(wire);
@@ -8927,8 +8985,13 @@ async function createAnthropicMessages(payload) {
8927
8985
  return msg.content.some((block) => block.type === "image");
8928
8986
  });
8929
8987
  const isAgentCall = messages.some((msg) => msg.role === "assistant");
8988
+ const modelSupportsVision = opts?.resolvedModel?.capabilities?.supports?.vision !== false;
8930
8989
  const headers = {
8931
- ...copilotHeaders(state, enableVision),
8990
+ ...copilotHeaders(state, {
8991
+ vision: enableVision && modelSupportsVision,
8992
+ modelRequestHeaders: opts?.resolvedModel?.request_headers,
8993
+ intent: isAgentCall ? "conversation-agent" : "conversation-panel"
8994
+ }),
8932
8995
  "X-Initiator": isAgentCall ? "agent" : "user",
8933
8996
  "anthropic-version": "2023-06-01",
8934
8997
  ...buildAnthropicBetaHeaders(model)
@@ -9063,25 +9126,29 @@ function handleContentBlockStart(index, block, acc) {
9063
9126
  input: ""
9064
9127
  };
9065
9128
  break;
9066
- case "web_search_tool_result":
9067
- newBlock = {
9068
- type: "web_search_tool_result",
9069
- tool_use_id: block.tool_use_id,
9070
- content: block.content
9071
- };
9072
- break;
9073
- default: {
9074
- const unknownBlock = block;
9075
- consola.warn(`[stream-accumulator] Unknown content block type: ${String(unknownBlock.type)}`);
9129
+ default:
9130
+ if (isServerToolResultType(block.type) && "tool_use_id" in block) {
9131
+ newBlock = {
9132
+ _brand: "server_tool_result",
9133
+ type: block.type,
9134
+ tool_use_id: block.tool_use_id,
9135
+ content: block.content
9136
+ };
9137
+ break;
9138
+ }
9139
+ consola.warn(`[stream-accumulator] Unknown content block type: ${block.type}`);
9076
9140
  newBlock = {
9077
- ...unknownBlock,
9141
+ ...block,
9078
9142
  _generic: true
9079
9143
  };
9080
9144
  break;
9081
- }
9082
9145
  }
9083
9146
  acc.contentBlocks[index] = newBlock;
9084
9147
  }
9148
+ /** Check if a block type is a server-side tool result (ends with _tool_result, but not plain tool_result) */
9149
+ function isServerToolResultType(type) {
9150
+ return type !== "tool_result" && type.endsWith("_tool_result");
9151
+ }
9085
9152
  function handleContentBlockDelta(index, delta, acc, copilotAnnotations) {
9086
9153
  const block = acc.contentBlocks[index];
9087
9154
  if (!block) return;
@@ -9524,7 +9591,7 @@ async function handleDirectAnthropicCompletion(c, anthropicPayload, reqCtx) {
9524
9591
  const adapter = {
9525
9592
  format: "anthropic-messages",
9526
9593
  sanitize: (p) => sanitizeAnthropicMessages(preprocessTools(p)),
9527
- execute: (p) => executeWithAdaptiveRateLimit(() => createAnthropicMessages(p)),
9594
+ execute: (p) => executeWithAdaptiveRateLimit(() => createAnthropicMessages(p, { resolvedModel: selectedModel })),
9528
9595
  logPayloadSize: (p) => logPayloadSizeInfoAnthropic(p, selectedModel)
9529
9596
  };
9530
9597
  const strategies = [
@@ -9606,6 +9673,7 @@ async function handleDirectAnthropicStreamingResponse(opts) {
9606
9673
  let eventsIn = 0;
9607
9674
  let currentBlockType = "";
9608
9675
  let firstEventLogged = false;
9676
+ const toolSearchFilter = state.filterToolSearchBlocks ? createToolSearchBlockFilter() : null;
9609
9677
  try {
9610
9678
  for await (const { raw: rawEvent, parsed } of processAnthropicStream(response, acc, clientAbortSignal)) {
9611
9679
  const dataLen = rawEvent.data?.length ?? 0;
@@ -9638,8 +9706,10 @@ async function handleDirectAnthropicStreamingResponse(opts) {
9638
9706
  const delta = parsed.delta;
9639
9707
  if (delta.type === "text_delta" && delta.text) checkRepetition(delta.text);
9640
9708
  }
9709
+ const forwardData = toolSearchFilter ? toolSearchFilter.rewriteEvent(parsed, rawEvent.data ?? "") : rawEvent.data ?? "";
9710
+ if (forwardData === null) continue;
9641
9711
  await stream.writeSSE({
9642
- data: rawEvent.data ?? "",
9712
+ data: forwardData,
9643
9713
  event: rawEvent.event,
9644
9714
  id: rawEvent.id !== void 0 ? String(rawEvent.id) : void 0,
9645
9715
  retry: rawEvent.retry
@@ -9688,6 +9758,7 @@ function handleDirectAnthropicNonStreamingResponse(c, response, reqCtx, truncate
9688
9758
  });
9689
9759
  let finalResponse = response;
9690
9760
  if (state.verbose && truncateResult?.wasTruncated) finalResponse = prependMarkerToResponse(response, createTruncationMarker$1(truncateResult));
9761
+ if (state.filterToolSearchBlocks) finalResponse = filterToolSearchBlocksFromResponse(finalResponse);
9691
9762
  return c.json(finalResponse);
9692
9763
  }
9693
9764
  /** Convert SanitizationStats to the format expected by rewrites */
@@ -9701,6 +9772,69 @@ function toSanitizationInfo(stats) {
9701
9772
  systemReminderRemovals: stats.systemReminderRemovals
9702
9773
  };
9703
9774
  }
9775
+ const TOOL_SEARCH_TOOL_NAME = "tool_search_tool_regex";
9776
+ const TOOL_SEARCH_RESULT_TYPE = "tool_search_tool_result";
9777
+ /** Check if a content block is an internal tool_search block */
9778
+ function isToolSearchBlock(block) {
9779
+ if (block.type === "server_tool_use" && block.name === TOOL_SEARCH_TOOL_NAME) return true;
9780
+ if (block.type === TOOL_SEARCH_RESULT_TYPE) return true;
9781
+ return false;
9782
+ }
9783
+ /**
9784
+ * Filters tool_search blocks from the SSE stream before forwarding to the client.
9785
+ * Handles index remapping so block indices remain dense/sequential after filtering.
9786
+ */
9787
+ function createToolSearchBlockFilter() {
9788
+ const filteredIndices = /* @__PURE__ */ new Set();
9789
+ const clientIndexMap = /* @__PURE__ */ new Map();
9790
+ let nextClientIndex = 0;
9791
+ function getClientIndex(apiIndex) {
9792
+ let idx = clientIndexMap.get(apiIndex);
9793
+ if (idx === void 0) {
9794
+ idx = nextClientIndex++;
9795
+ clientIndexMap.set(apiIndex, idx);
9796
+ }
9797
+ return idx;
9798
+ }
9799
+ return { rewriteEvent(parsed, rawData) {
9800
+ if (!parsed) return rawData;
9801
+ if (parsed.type === "content_block_start") {
9802
+ const block = parsed.content_block;
9803
+ if (isToolSearchBlock(block)) {
9804
+ filteredIndices.add(parsed.index);
9805
+ return null;
9806
+ }
9807
+ if (filteredIndices.size === 0) {
9808
+ getClientIndex(parsed.index);
9809
+ return rawData;
9810
+ }
9811
+ const clientIndex = getClientIndex(parsed.index);
9812
+ if (clientIndex === parsed.index) return rawData;
9813
+ const obj = JSON.parse(rawData);
9814
+ obj.index = clientIndex;
9815
+ return JSON.stringify(obj);
9816
+ }
9817
+ if (parsed.type === "content_block_delta" || parsed.type === "content_block_stop") {
9818
+ if (filteredIndices.has(parsed.index)) return null;
9819
+ if (filteredIndices.size === 0) return rawData;
9820
+ const clientIndex = getClientIndex(parsed.index);
9821
+ if (clientIndex === parsed.index) return rawData;
9822
+ const obj = JSON.parse(rawData);
9823
+ obj.index = clientIndex;
9824
+ return JSON.stringify(obj);
9825
+ }
9826
+ return rawData;
9827
+ } };
9828
+ }
9829
+ /** Filter tool_search blocks from a non-streaming response */
9830
+ function filterToolSearchBlocksFromResponse(response) {
9831
+ const filtered = response.content.filter((block) => !isToolSearchBlock(block));
9832
+ if (filtered.length === response.content.length) return response;
9833
+ return {
9834
+ ...response,
9835
+ content: filtered
9836
+ };
9837
+ }
9704
9838
 
9705
9839
  //#endregion
9706
9840
  //#region src/routes/messages/route.ts
@@ -9808,9 +9942,9 @@ async function handleResponses(c) {
9808
9942
  /** Pass through to Copilot /responses endpoint directly */
9809
9943
  async function handleDirectResponses(opts) {
9810
9944
  const { c, payload, reqCtx } = opts;
9811
- const adapter = createResponsesAdapter();
9812
- const strategies = createResponsesStrategies();
9813
9945
  const selectedModel = state.modelIndex.get(payload.model);
9946
+ const adapter = createResponsesAdapter(selectedModel);
9947
+ const strategies = createResponsesStrategies();
9814
9948
  try {
9815
9949
  const pipelineResult = await executeRequestPipeline({
9816
9950
  adapter,
@@ -9832,7 +9966,8 @@ async function handleDirectResponses(opts) {
9832
9966
  usage: {
9833
9967
  input_tokens: responsesResponse.usage?.input_tokens ?? 0,
9834
9968
  output_tokens: responsesResponse.usage?.output_tokens ?? 0,
9835
- ...responsesResponse.usage?.input_tokens_details?.cached_tokens && { cache_read_input_tokens: responsesResponse.usage.input_tokens_details.cached_tokens }
9969
+ ...responsesResponse.usage?.input_tokens_details?.cached_tokens && { cache_read_input_tokens: responsesResponse.usage.input_tokens_details.cached_tokens },
9970
+ ...responsesResponse.usage?.output_tokens_details?.reasoning_tokens && { output_tokens_details: { reasoning_tokens: responsesResponse.usage.output_tokens_details.reasoning_tokens } }
9836
9971
  },
9837
9972
  stop_reason: responsesResponse.status,
9838
9973
  content
@@ -9867,8 +10002,12 @@ async function handleDirectResponses(opts) {
9867
10002
  streamEventsIn: eventsIn
9868
10003
  });
9869
10004
  try {
9870
- accumulateResponsesStreamEvent(JSON.parse(rawEvent.data), acc);
9871
- await stream.writeSSE({ data: rawEvent.data });
10005
+ const event = JSON.parse(rawEvent.data);
10006
+ accumulateResponsesStreamEvent(event, acc);
10007
+ await stream.writeSSE({
10008
+ event: rawEvent.event ?? event.type,
10009
+ data: rawEvent.data
10010
+ });
9872
10011
  } catch {}
9873
10012
  }
9874
10013
  }
@@ -9878,10 +10017,13 @@ async function handleDirectResponses(opts) {
9878
10017
  consola.error("[Responses] Stream error:", error);
9879
10018
  reqCtx.fail(acc.model || payload.model, error);
9880
10019
  const errorMessage = error instanceof Error ? error.message : String(error);
9881
- await stream.writeSSE({ data: JSON.stringify({ error: {
9882
- message: errorMessage,
9883
- type: error instanceof StreamIdleTimeoutError ? "timeout_error" : "server_error"
9884
- } }) });
10020
+ await stream.writeSSE({
10021
+ event: "error",
10022
+ data: JSON.stringify({ error: {
10023
+ message: errorMessage,
10024
+ type: error instanceof StreamIdleTimeoutError ? "timeout_error" : "server_error"
10025
+ } })
10026
+ });
9885
10027
  }
9886
10028
  });
9887
10029
  } catch (error) {
@@ -10032,7 +10174,16 @@ function parseIntOrDefault(value, defaultValue) {
10032
10174
  const parsed = Number.parseInt(value, 10);
10033
10175
  return Number.isFinite(parsed) ? parsed : defaultValue;
10034
10176
  }
10177
+ const VALID_ACCOUNT_TYPES = [
10178
+ "individual",
10179
+ "business",
10180
+ "enterprise"
10181
+ ];
10035
10182
  async function runServer(options) {
10183
+ if (!VALID_ACCOUNT_TYPES.includes(options.accountType)) {
10184
+ consola.error(`Invalid account type: "${options.accountType}". Must be one of: ${VALID_ACCOUNT_TYPES.join(", ")}`);
10185
+ process.exit(1);
10186
+ }
10036
10187
  if (options.verbose) {
10037
10188
  consola.level = 5;
10038
10189
  state.verbose = true;
@@ -10085,6 +10236,10 @@ async function runServer(options) {
10085
10236
  on("[timeouts]", "Timeouts", parts.join(", "));
10086
10237
  }
10087
10238
  on("[history_limit]", "History", state.historyLimit === 0 ? "unlimited" : `max=${state.historyLimit}`);
10239
+ on("[shutdown]", "Shutdown", `graceful=${state.shutdownGracefulWait}s, abort=${state.shutdownAbortWait}s`);
10240
+ if (state.systemPromptOverrides.length > 0) on("[system_prompt_overrides]", "System prompt overrides", `${state.systemPromptOverrides.length} rules`);
10241
+ if (config.system_prompt_prepend) on("[system_prompt_prepend]", "System prompt prepend", `${config.system_prompt_prepend.length} chars`);
10242
+ if (config.system_prompt_append) on("[system_prompt_append]", "System prompt append", `${config.system_prompt_append.length} chars`);
10088
10243
  consola.info(`Configuration:\n${configLines.join("\n")}`);
10089
10244
  if (options.rateLimit) initAdaptiveRateLimiter({
10090
10245
  baseRetryIntervalSeconds: rlRetryInterval,
@@ -10102,7 +10257,9 @@ async function runServer(options) {
10102
10257
  try {
10103
10258
  await cacheModels();
10104
10259
  } catch (error) {
10105
- consola.warn("Failed to fetch models from Copilot API:", error instanceof Error ? error.message : error);
10260
+ consola.error("Failed to fetch models from Copilot API:", error instanceof Error ? error.message : error);
10261
+ consola.error(`Verify that --account-type "${state.accountType}" is correct. Available types: ${VALID_ACCOUNT_TYPES.join(", ")}`);
10262
+ process.exit(1);
10106
10263
  }
10107
10264
  consola.info(`Available models:\n${state.models?.data.map((m) => formatModelInfo(m)).join("\n")}`);
10108
10265
  await loadPersistedLimits();