@hsupu/copilot-api 0.7.18 → 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/config.example.yaml +1 -1
- package/dist/main.mjs +176 -63
- package/dist/main.mjs.map +1 -1
- package/package.json +1 -1
package/config.example.yaml
CHANGED
|
@@ -56,7 +56,7 @@ model_overrides:
|
|
|
56
56
|
stream_idle_timeout: 300 # Max seconds between SSE events (0 = no timeout).
|
|
57
57
|
# Applies to all streaming paths (Anthropic, Chat Completions, Responses).
|
|
58
58
|
|
|
59
|
-
fetch_timeout:
|
|
59
|
+
fetch_timeout: 300 # Seconds: request start → HTTP response headers (0 = no timeout).
|
|
60
60
|
# Applies to all upstream API clients.
|
|
61
61
|
|
|
62
62
|
stale_request_max_age: 600 # Max seconds an active request can live before the stale reaper
|
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
|
-
|
|
76
|
-
|
|
77
|
-
systemPromptOverrides: [],
|
|
70
|
+
fetchTimeout: 300,
|
|
71
|
+
filterToolSearchBlocks: false,
|
|
78
72
|
historyLimit: 200,
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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) {
|
|
@@ -4268,7 +4281,7 @@ const setupClaudeCode = defineCommand({
|
|
|
4268
4281
|
|
|
4269
4282
|
//#endregion
|
|
4270
4283
|
//#region package.json
|
|
4271
|
-
var version = "0.7.
|
|
4284
|
+
var version = "0.7.19";
|
|
4272
4285
|
|
|
4273
4286
|
//#endregion
|
|
4274
4287
|
//#region src/lib/config/config.ts
|
|
@@ -4367,6 +4380,7 @@ async function applyConfigToState() {
|
|
|
4367
4380
|
if (a.convert_server_tools_to_custom !== void 0) state.convertServerToolsToCustom = a.convert_server_tools_to_custom;
|
|
4368
4381
|
if (a.dedup_tool_calls !== void 0) state.dedupToolCalls = a.dedup_tool_calls === true ? "input" : a.dedup_tool_calls;
|
|
4369
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;
|
|
4370
4384
|
if (a.rewrite_system_reminders !== void 0) {
|
|
4371
4385
|
if (typeof a.rewrite_system_reminders === "boolean") state.rewriteSystemReminders = a.rewrite_system_reminders;
|
|
4372
4386
|
else if (Array.isArray(a.rewrite_system_reminders)) state.rewriteSystemReminders = compileRewriteRules(a.rewrite_system_reminders);
|
|
@@ -4578,7 +4592,8 @@ function toHistoryResponse(entryData) {
|
|
|
4578
4592
|
input_tokens: r.usage.input_tokens,
|
|
4579
4593
|
output_tokens: r.usage.output_tokens,
|
|
4580
4594
|
cache_read_input_tokens: r.usage.cache_read_input_tokens,
|
|
4581
|
-
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
|
|
4582
4597
|
},
|
|
4583
4598
|
stop_reason: r.stop_reason,
|
|
4584
4599
|
error: r.error,
|
|
@@ -5343,30 +5358,31 @@ function mapAnthropicContentBlocks(acc) {
|
|
|
5343
5358
|
const { _generic: _, ...rest } = block;
|
|
5344
5359
|
return rest;
|
|
5345
5360
|
}
|
|
5346
|
-
|
|
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) {
|
|
5347
5368
|
case "text": return {
|
|
5348
5369
|
type: "text",
|
|
5349
|
-
text:
|
|
5370
|
+
text: narrowed.text
|
|
5350
5371
|
};
|
|
5351
5372
|
case "thinking": return {
|
|
5352
5373
|
type: "thinking",
|
|
5353
|
-
thinking:
|
|
5374
|
+
thinking: narrowed.thinking
|
|
5354
5375
|
};
|
|
5355
5376
|
case "redacted_thinking": return { type: "redacted_thinking" };
|
|
5356
5377
|
case "tool_use":
|
|
5357
5378
|
case "server_tool_use": return {
|
|
5358
|
-
type:
|
|
5359
|
-
id:
|
|
5360
|
-
name:
|
|
5361
|
-
input: safeParseJson(
|
|
5362
|
-
};
|
|
5363
|
-
case "web_search_tool_result": return {
|
|
5364
|
-
type: "web_search_tool_result",
|
|
5365
|
-
tool_use_id: block.tool_use_id,
|
|
5366
|
-
content: block.content
|
|
5379
|
+
type: narrowed.type,
|
|
5380
|
+
id: narrowed.id,
|
|
5381
|
+
name: narrowed.name,
|
|
5382
|
+
input: safeParseJson(narrowed.input)
|
|
5367
5383
|
};
|
|
5368
5384
|
default: {
|
|
5369
|
-
const unknown =
|
|
5385
|
+
const unknown = narrowed;
|
|
5370
5386
|
consola.warn(`[recording] Unhandled content block type in stream result: ${unknown.type}`);
|
|
5371
5387
|
return { type: unknown.type };
|
|
5372
5388
|
}
|
|
@@ -5419,6 +5435,7 @@ function buildOpenAIResponseData(acc, fallbackModel) {
|
|
|
5419
5435
|
usage: {
|
|
5420
5436
|
input_tokens: acc.inputTokens,
|
|
5421
5437
|
output_tokens: acc.outputTokens,
|
|
5438
|
+
...acc.reasoningTokens > 0 && { output_tokens_details: { reasoning_tokens: acc.reasoningTokens } },
|
|
5422
5439
|
...acc.cachedTokens > 0 && { cache_read_input_tokens: acc.cachedTokens }
|
|
5423
5440
|
},
|
|
5424
5441
|
stop_reason: acc.finishReason || void 0,
|
|
@@ -5640,12 +5657,17 @@ async function processResponsesInstructions(instructions, model) {
|
|
|
5640
5657
|
//#endregion
|
|
5641
5658
|
//#region src/lib/openai/responses-client.ts
|
|
5642
5659
|
/** Call Copilot /responses endpoint */
|
|
5643
|
-
const createResponses = async (payload) => {
|
|
5660
|
+
const createResponses = async (payload, opts) => {
|
|
5644
5661
|
if (!state.copilotToken) throw new Error("Copilot token not found");
|
|
5645
5662
|
const enableVision = hasVisionContent(payload.input);
|
|
5646
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;
|
|
5647
5665
|
const headers = {
|
|
5648
|
-
...copilotHeaders(state,
|
|
5666
|
+
...copilotHeaders(state, {
|
|
5667
|
+
vision: enableVision && modelSupportsVision,
|
|
5668
|
+
modelRequestHeaders: opts?.resolvedModel?.request_headers,
|
|
5669
|
+
intent: isAgentCall ? "conversation-agent" : "conversation-panel"
|
|
5670
|
+
}),
|
|
5649
5671
|
"X-Initiator": isAgentCall ? "agent" : "user"
|
|
5650
5672
|
};
|
|
5651
5673
|
const fetchSignal = createFetchSignal();
|
|
@@ -5771,7 +5793,7 @@ function createTokenRefreshStrategy() {
|
|
|
5771
5793
|
* centralizes that configuration to avoid duplication.
|
|
5772
5794
|
*/
|
|
5773
5795
|
/** Create the FormatAdapter for Responses API pipeline execution */
|
|
5774
|
-
function createResponsesAdapter() {
|
|
5796
|
+
function createResponsesAdapter(selectedModel) {
|
|
5775
5797
|
return {
|
|
5776
5798
|
format: "openai-responses",
|
|
5777
5799
|
sanitize: (p) => ({
|
|
@@ -5779,7 +5801,7 @@ function createResponsesAdapter() {
|
|
|
5779
5801
|
removedCount: 0,
|
|
5780
5802
|
systemReminderRemovals: 0
|
|
5781
5803
|
}),
|
|
5782
|
-
execute: (p) => executeWithAdaptiveRateLimit(() => createResponses(p)),
|
|
5804
|
+
execute: (p) => executeWithAdaptiveRateLimit(() => createResponses(p, { resolvedModel: selectedModel })),
|
|
5783
5805
|
logPayloadSize: (p) => {
|
|
5784
5806
|
const count = typeof p.input === "string" ? 1 : p.input.length;
|
|
5785
5807
|
consola.debug(`Responses payload: ${count} input item(s), model: ${p.model}`);
|
|
@@ -5871,7 +5893,7 @@ async function handleResponseCreate(ws, payload) {
|
|
|
5871
5893
|
model: resolvedModel,
|
|
5872
5894
|
clientModel: requestedModel
|
|
5873
5895
|
});
|
|
5874
|
-
const adapter = createResponsesAdapter();
|
|
5896
|
+
const adapter = createResponsesAdapter(selectedModel);
|
|
5875
5897
|
const strategies = createResponsesStrategies();
|
|
5876
5898
|
try {
|
|
5877
5899
|
const iterator = (await executeRequestPipeline({
|
|
@@ -6689,12 +6711,17 @@ function createTruncationResponseMarkerOpenAI(result) {
|
|
|
6689
6711
|
|
|
6690
6712
|
//#endregion
|
|
6691
6713
|
//#region src/lib/openai/client.ts
|
|
6692
|
-
const createChatCompletions = async (payload) => {
|
|
6714
|
+
const createChatCompletions = async (payload, opts) => {
|
|
6693
6715
|
if (!state.copilotToken) throw new Error("Copilot token not found");
|
|
6694
6716
|
const enableVision = payload.messages.some((x) => typeof x.content !== "string" && x.content?.some((x) => x.type === "image_url"));
|
|
6695
6717
|
const isAgentCall = payload.messages.some((msg) => ["assistant", "tool"].includes(msg.role));
|
|
6718
|
+
const modelSupportsVision = opts?.resolvedModel?.capabilities?.supports?.vision !== false;
|
|
6696
6719
|
const headers = {
|
|
6697
|
-
...copilotHeaders(state,
|
|
6720
|
+
...copilotHeaders(state, {
|
|
6721
|
+
vision: enableVision && modelSupportsVision,
|
|
6722
|
+
modelRequestHeaders: opts?.resolvedModel?.request_headers,
|
|
6723
|
+
intent: isAgentCall ? "conversation-agent" : "conversation-panel"
|
|
6724
|
+
}),
|
|
6698
6725
|
"X-Initiator": isAgentCall ? "agent" : "user"
|
|
6699
6726
|
};
|
|
6700
6727
|
const fetchSignal = createFetchSignal();
|
|
@@ -6823,6 +6850,7 @@ function createOpenAIStreamAccumulator() {
|
|
|
6823
6850
|
inputTokens: 0,
|
|
6824
6851
|
outputTokens: 0,
|
|
6825
6852
|
cachedTokens: 0,
|
|
6853
|
+
reasoningTokens: 0,
|
|
6826
6854
|
finishReason: "",
|
|
6827
6855
|
content: "",
|
|
6828
6856
|
toolCalls: [],
|
|
@@ -6836,6 +6864,7 @@ function accumulateOpenAIStreamEvent(parsed, acc) {
|
|
|
6836
6864
|
acc.inputTokens = parsed.usage.prompt_tokens;
|
|
6837
6865
|
acc.outputTokens = parsed.usage.completion_tokens;
|
|
6838
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;
|
|
6839
6868
|
}
|
|
6840
6869
|
const choice = parsed.choices[0];
|
|
6841
6870
|
if (choice) {
|
|
@@ -7088,7 +7117,7 @@ async function executeRequest(opts) {
|
|
|
7088
7117
|
const adapter = {
|
|
7089
7118
|
format: "openai-chat-completions",
|
|
7090
7119
|
sanitize: (p) => sanitizeOpenAIMessages(p),
|
|
7091
|
-
execute: (p) => executeWithAdaptiveRateLimit(() => createChatCompletions(p)),
|
|
7120
|
+
execute: (p) => executeWithAdaptiveRateLimit(() => createChatCompletions(p, { resolvedModel: selectedModel })),
|
|
7092
7121
|
logPayloadSize: (p) => logPayloadSizeInfo(p, selectedModel)
|
|
7093
7122
|
};
|
|
7094
7123
|
const strategies = [
|
|
@@ -7978,9 +8007,6 @@ function contentToText(content, options) {
|
|
|
7978
8007
|
case "server_tool_use":
|
|
7979
8008
|
parts.push(`[server_tool_use: ${block.name}]`, JSON.stringify(block.input));
|
|
7980
8009
|
break;
|
|
7981
|
-
case "web_search_tool_result":
|
|
7982
|
-
parts.push(`[web_search_tool_result]`);
|
|
7983
|
-
break;
|
|
7984
8010
|
default: {
|
|
7985
8011
|
const genericBlock = block;
|
|
7986
8012
|
if ("tool_use_id" in genericBlock && genericBlock.type !== "image") {
|
|
@@ -8679,7 +8705,7 @@ const NON_DEFERRED_TOOL_NAMES = new Set([
|
|
|
8679
8705
|
"switch_agent",
|
|
8680
8706
|
...CLAUDE_CODE_OFFICIAL_TOOLS
|
|
8681
8707
|
]);
|
|
8682
|
-
const TOOL_SEARCH_TOOL_NAME = "tool_search_tool_regex";
|
|
8708
|
+
const TOOL_SEARCH_TOOL_NAME$1 = "tool_search_tool_regex";
|
|
8683
8709
|
const TOOL_SEARCH_TOOL_TYPE = "tool_search_tool_regex_20251119";
|
|
8684
8710
|
const EMPTY_INPUT_SCHEMA = {
|
|
8685
8711
|
type: "object",
|
|
@@ -8743,7 +8769,7 @@ function processToolPipeline(tools, modelId, messages) {
|
|
|
8743
8769
|
const historyToolNames = toolSearchEnabled ? collectHistoryToolNames(messages) : void 0;
|
|
8744
8770
|
const result = [];
|
|
8745
8771
|
if (toolSearchEnabled) result.push({
|
|
8746
|
-
name: TOOL_SEARCH_TOOL_NAME,
|
|
8772
|
+
name: TOOL_SEARCH_TOOL_NAME$1,
|
|
8747
8773
|
type: TOOL_SEARCH_TOOL_TYPE,
|
|
8748
8774
|
defer_loading: false
|
|
8749
8775
|
});
|
|
@@ -8946,7 +8972,7 @@ function adjustThinkingBudget(wire) {
|
|
|
8946
8972
|
* Create messages using Anthropic-style API directly.
|
|
8947
8973
|
* Calls Copilot's native Anthropic endpoint for Anthropic-vendor models.
|
|
8948
8974
|
*/
|
|
8949
|
-
async function createAnthropicMessages(payload) {
|
|
8975
|
+
async function createAnthropicMessages(payload, opts) {
|
|
8950
8976
|
if (!state.copilotToken) throw new Error("Copilot token not found");
|
|
8951
8977
|
const wire = buildWirePayload(payload);
|
|
8952
8978
|
adjustThinkingBudget(wire);
|
|
@@ -8959,8 +8985,13 @@ async function createAnthropicMessages(payload) {
|
|
|
8959
8985
|
return msg.content.some((block) => block.type === "image");
|
|
8960
8986
|
});
|
|
8961
8987
|
const isAgentCall = messages.some((msg) => msg.role === "assistant");
|
|
8988
|
+
const modelSupportsVision = opts?.resolvedModel?.capabilities?.supports?.vision !== false;
|
|
8962
8989
|
const headers = {
|
|
8963
|
-
...copilotHeaders(state,
|
|
8990
|
+
...copilotHeaders(state, {
|
|
8991
|
+
vision: enableVision && modelSupportsVision,
|
|
8992
|
+
modelRequestHeaders: opts?.resolvedModel?.request_headers,
|
|
8993
|
+
intent: isAgentCall ? "conversation-agent" : "conversation-panel"
|
|
8994
|
+
}),
|
|
8964
8995
|
"X-Initiator": isAgentCall ? "agent" : "user",
|
|
8965
8996
|
"anthropic-version": "2023-06-01",
|
|
8966
8997
|
...buildAnthropicBetaHeaders(model)
|
|
@@ -9095,25 +9126,29 @@ function handleContentBlockStart(index, block, acc) {
|
|
|
9095
9126
|
input: ""
|
|
9096
9127
|
};
|
|
9097
9128
|
break;
|
|
9098
|
-
|
|
9099
|
-
|
|
9100
|
-
|
|
9101
|
-
|
|
9102
|
-
|
|
9103
|
-
|
|
9104
|
-
|
|
9105
|
-
|
|
9106
|
-
|
|
9107
|
-
|
|
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}`);
|
|
9108
9140
|
newBlock = {
|
|
9109
|
-
...
|
|
9141
|
+
...block,
|
|
9110
9142
|
_generic: true
|
|
9111
9143
|
};
|
|
9112
9144
|
break;
|
|
9113
|
-
}
|
|
9114
9145
|
}
|
|
9115
9146
|
acc.contentBlocks[index] = newBlock;
|
|
9116
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
|
+
}
|
|
9117
9152
|
function handleContentBlockDelta(index, delta, acc, copilotAnnotations) {
|
|
9118
9153
|
const block = acc.contentBlocks[index];
|
|
9119
9154
|
if (!block) return;
|
|
@@ -9556,7 +9591,7 @@ async function handleDirectAnthropicCompletion(c, anthropicPayload, reqCtx) {
|
|
|
9556
9591
|
const adapter = {
|
|
9557
9592
|
format: "anthropic-messages",
|
|
9558
9593
|
sanitize: (p) => sanitizeAnthropicMessages(preprocessTools(p)),
|
|
9559
|
-
execute: (p) => executeWithAdaptiveRateLimit(() => createAnthropicMessages(p)),
|
|
9594
|
+
execute: (p) => executeWithAdaptiveRateLimit(() => createAnthropicMessages(p, { resolvedModel: selectedModel })),
|
|
9560
9595
|
logPayloadSize: (p) => logPayloadSizeInfoAnthropic(p, selectedModel)
|
|
9561
9596
|
};
|
|
9562
9597
|
const strategies = [
|
|
@@ -9638,6 +9673,7 @@ async function handleDirectAnthropicStreamingResponse(opts) {
|
|
|
9638
9673
|
let eventsIn = 0;
|
|
9639
9674
|
let currentBlockType = "";
|
|
9640
9675
|
let firstEventLogged = false;
|
|
9676
|
+
const toolSearchFilter = state.filterToolSearchBlocks ? createToolSearchBlockFilter() : null;
|
|
9641
9677
|
try {
|
|
9642
9678
|
for await (const { raw: rawEvent, parsed } of processAnthropicStream(response, acc, clientAbortSignal)) {
|
|
9643
9679
|
const dataLen = rawEvent.data?.length ?? 0;
|
|
@@ -9670,8 +9706,10 @@ async function handleDirectAnthropicStreamingResponse(opts) {
|
|
|
9670
9706
|
const delta = parsed.delta;
|
|
9671
9707
|
if (delta.type === "text_delta" && delta.text) checkRepetition(delta.text);
|
|
9672
9708
|
}
|
|
9709
|
+
const forwardData = toolSearchFilter ? toolSearchFilter.rewriteEvent(parsed, rawEvent.data ?? "") : rawEvent.data ?? "";
|
|
9710
|
+
if (forwardData === null) continue;
|
|
9673
9711
|
await stream.writeSSE({
|
|
9674
|
-
data:
|
|
9712
|
+
data: forwardData,
|
|
9675
9713
|
event: rawEvent.event,
|
|
9676
9714
|
id: rawEvent.id !== void 0 ? String(rawEvent.id) : void 0,
|
|
9677
9715
|
retry: rawEvent.retry
|
|
@@ -9720,6 +9758,7 @@ function handleDirectAnthropicNonStreamingResponse(c, response, reqCtx, truncate
|
|
|
9720
9758
|
});
|
|
9721
9759
|
let finalResponse = response;
|
|
9722
9760
|
if (state.verbose && truncateResult?.wasTruncated) finalResponse = prependMarkerToResponse(response, createTruncationMarker$1(truncateResult));
|
|
9761
|
+
if (state.filterToolSearchBlocks) finalResponse = filterToolSearchBlocksFromResponse(finalResponse);
|
|
9723
9762
|
return c.json(finalResponse);
|
|
9724
9763
|
}
|
|
9725
9764
|
/** Convert SanitizationStats to the format expected by rewrites */
|
|
@@ -9733,6 +9772,69 @@ function toSanitizationInfo(stats) {
|
|
|
9733
9772
|
systemReminderRemovals: stats.systemReminderRemovals
|
|
9734
9773
|
};
|
|
9735
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
|
+
}
|
|
9736
9838
|
|
|
9737
9839
|
//#endregion
|
|
9738
9840
|
//#region src/routes/messages/route.ts
|
|
@@ -9840,9 +9942,9 @@ async function handleResponses(c) {
|
|
|
9840
9942
|
/** Pass through to Copilot /responses endpoint directly */
|
|
9841
9943
|
async function handleDirectResponses(opts) {
|
|
9842
9944
|
const { c, payload, reqCtx } = opts;
|
|
9843
|
-
const adapter = createResponsesAdapter();
|
|
9844
|
-
const strategies = createResponsesStrategies();
|
|
9845
9945
|
const selectedModel = state.modelIndex.get(payload.model);
|
|
9946
|
+
const adapter = createResponsesAdapter(selectedModel);
|
|
9947
|
+
const strategies = createResponsesStrategies();
|
|
9846
9948
|
try {
|
|
9847
9949
|
const pipelineResult = await executeRequestPipeline({
|
|
9848
9950
|
adapter,
|
|
@@ -10072,7 +10174,16 @@ function parseIntOrDefault(value, defaultValue) {
|
|
|
10072
10174
|
const parsed = Number.parseInt(value, 10);
|
|
10073
10175
|
return Number.isFinite(parsed) ? parsed : defaultValue;
|
|
10074
10176
|
}
|
|
10177
|
+
const VALID_ACCOUNT_TYPES = [
|
|
10178
|
+
"individual",
|
|
10179
|
+
"business",
|
|
10180
|
+
"enterprise"
|
|
10181
|
+
];
|
|
10075
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
|
+
}
|
|
10076
10187
|
if (options.verbose) {
|
|
10077
10188
|
consola.level = 5;
|
|
10078
10189
|
state.verbose = true;
|
|
@@ -10146,7 +10257,9 @@ async function runServer(options) {
|
|
|
10146
10257
|
try {
|
|
10147
10258
|
await cacheModels();
|
|
10148
10259
|
} catch (error) {
|
|
10149
|
-
consola.
|
|
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);
|
|
10150
10263
|
}
|
|
10151
10264
|
consola.info(`Available models:\n${state.models?.data.map((m) => formatModelInfo(m)).join("\n")}`);
|
|
10152
10265
|
await loadPersistedLimits();
|