@dianshuv/copilot-api 0.6.2 → 0.6.3
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/README.md +2 -1
- package/dist/main.mjs +428 -180
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -66,7 +66,8 @@ copilot-api start
|
|
|
66
66
|
| `--no-auto-truncate` | Disable auto-truncate when exceeding token limits | false |
|
|
67
67
|
| `--compress-tool-results` | Compress old tool results before truncating | false |
|
|
68
68
|
| `--redirect-anthropic` | Force Anthropic through OpenAI translation | false |
|
|
69
|
-
| `--
|
|
69
|
+
| `--strip-server-tools` | Strip server-side tools from Anthropic requests | false |
|
|
70
|
+
| `--context-editing` | Context editing mode: off, clear-thinking, clear-tooluse, clear-both | off |
|
|
70
71
|
| `--timezone-offset` | Timezone offset in hours from UTC for log timestamps (e.g., +8, -5, 0) | +8 |
|
|
71
72
|
| `--posthog-key` | PostHog API key for token usage analytics (opt-in) | none |
|
|
72
73
|
|
package/dist/main.mjs
CHANGED
|
@@ -4,14 +4,14 @@ import consola from "consola";
|
|
|
4
4
|
import fs from "node:fs/promises";
|
|
5
5
|
import os from "node:os";
|
|
6
6
|
import path, { dirname, join } from "node:path";
|
|
7
|
+
import { getProxyForUrl } from "proxy-from-env";
|
|
8
|
+
import { Agent, ProxyAgent, setGlobalDispatcher } from "undici";
|
|
7
9
|
import { createHash, randomUUID } from "node:crypto";
|
|
8
10
|
import { existsSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
9
11
|
import clipboard from "clipboardy";
|
|
10
12
|
import { serve } from "srvx";
|
|
11
13
|
import invariant from "tiny-invariant";
|
|
12
14
|
import { PostHog } from "posthog-node";
|
|
13
|
-
import { getProxyForUrl } from "proxy-from-env";
|
|
14
|
-
import { Agent, ProxyAgent, setGlobalDispatcher } from "undici";
|
|
15
15
|
import { execSync } from "node:child_process";
|
|
16
16
|
import process$1 from "node:process";
|
|
17
17
|
import pc from "picocolors";
|
|
@@ -32,12 +32,90 @@ async function ensurePaths() {
|
|
|
32
32
|
await ensureFile(PATHS.GITHUB_TOKEN_PATH);
|
|
33
33
|
}
|
|
34
34
|
async function ensureFile(filePath) {
|
|
35
|
+
const isWindows = process.platform === "win32";
|
|
35
36
|
try {
|
|
36
37
|
await fs.access(filePath, fs.constants.W_OK);
|
|
37
|
-
if (
|
|
38
|
+
if (!isWindows) {
|
|
39
|
+
if (((await fs.stat(filePath)).mode & 511) !== 384) await fs.chmod(filePath, 384);
|
|
40
|
+
}
|
|
38
41
|
} catch {
|
|
39
42
|
await fs.writeFile(filePath, "");
|
|
40
|
-
await fs.chmod(filePath, 384);
|
|
43
|
+
if (!isWindows) await fs.chmod(filePath, 384);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
//#endregion
|
|
48
|
+
//#region src/lib/proxy.ts
|
|
49
|
+
/**
|
|
50
|
+
* Custom dispatcher that routes requests through proxies based on environment variables.
|
|
51
|
+
* Extends Agent to properly inherit the Dispatcher interface.
|
|
52
|
+
*/
|
|
53
|
+
var ProxyDispatcher = class extends Agent {
|
|
54
|
+
proxies = /* @__PURE__ */ new Map();
|
|
55
|
+
dispatch(options, handler) {
|
|
56
|
+
try {
|
|
57
|
+
const origin = this.getOriginUrl(options.origin);
|
|
58
|
+
const proxyUrl = this.getProxyUrl(origin);
|
|
59
|
+
if (!proxyUrl) {
|
|
60
|
+
consola.debug(`HTTP proxy bypass: ${origin.hostname}`);
|
|
61
|
+
return super.dispatch(options, handler);
|
|
62
|
+
}
|
|
63
|
+
const agent = this.getOrCreateProxyAgent(proxyUrl);
|
|
64
|
+
consola.debug(`HTTP proxy route: ${origin.hostname} via ${this.formatProxyLabel(proxyUrl)}`);
|
|
65
|
+
return agent.dispatch(options, handler);
|
|
66
|
+
} catch {
|
|
67
|
+
return super.dispatch(options, handler);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
getOriginUrl(origin) {
|
|
71
|
+
return typeof origin === "string" ? new URL(origin) : origin;
|
|
72
|
+
}
|
|
73
|
+
getProxyUrl(origin) {
|
|
74
|
+
const raw = getProxyForUrl(origin.toString());
|
|
75
|
+
return raw && raw.length > 0 ? raw : void 0;
|
|
76
|
+
}
|
|
77
|
+
getOrCreateProxyAgent(proxyUrl) {
|
|
78
|
+
let agent = this.proxies.get(proxyUrl);
|
|
79
|
+
if (!agent) {
|
|
80
|
+
agent = new ProxyAgent(proxyUrl);
|
|
81
|
+
this.proxies.set(proxyUrl, agent);
|
|
82
|
+
}
|
|
83
|
+
return agent;
|
|
84
|
+
}
|
|
85
|
+
formatProxyLabel(proxyUrl) {
|
|
86
|
+
try {
|
|
87
|
+
const u = new URL(proxyUrl);
|
|
88
|
+
return `${u.protocol}//${u.host}`;
|
|
89
|
+
} catch {
|
|
90
|
+
return proxyUrl;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
async close() {
|
|
94
|
+
await super.close();
|
|
95
|
+
await Promise.all([...this.proxies.values()].map((p) => p.close()));
|
|
96
|
+
this.proxies.clear();
|
|
97
|
+
}
|
|
98
|
+
destroy(errOrCallback, callback) {
|
|
99
|
+
for (const agent of this.proxies.values()) if (typeof errOrCallback === "function") agent.destroy(errOrCallback);
|
|
100
|
+
else if (callback) agent.destroy(errOrCallback ?? null, callback);
|
|
101
|
+
else agent.destroy(errOrCallback ?? null).catch(() => {});
|
|
102
|
+
this.proxies.clear();
|
|
103
|
+
if (typeof errOrCallback === "function") {
|
|
104
|
+
super.destroy(errOrCallback);
|
|
105
|
+
return;
|
|
106
|
+
} else if (callback) {
|
|
107
|
+
super.destroy(errOrCallback ?? null, callback);
|
|
108
|
+
return;
|
|
109
|
+
} else return super.destroy(errOrCallback ?? null);
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
function initProxyFromEnv() {
|
|
113
|
+
if (typeof Bun !== "undefined") return;
|
|
114
|
+
try {
|
|
115
|
+
setGlobalDispatcher(new ProxyDispatcher());
|
|
116
|
+
consola.debug("HTTP proxy configured from environment (per-URL)");
|
|
117
|
+
} catch (err) {
|
|
118
|
+
consola.debug("Proxy setup skipped:", err);
|
|
41
119
|
}
|
|
42
120
|
}
|
|
43
121
|
|
|
@@ -51,7 +129,10 @@ const state = {
|
|
|
51
129
|
autoTruncate: true,
|
|
52
130
|
compressToolResults: false,
|
|
53
131
|
redirectAnthropic: false,
|
|
54
|
-
|
|
132
|
+
stripServerTools: false,
|
|
133
|
+
contextEditingMode: "off",
|
|
134
|
+
normalizeResponsesCallIds: true,
|
|
135
|
+
historyMinEntries: 50,
|
|
55
136
|
staleRequestMaxAge: 600,
|
|
56
137
|
timezoneOffset: 8,
|
|
57
138
|
shutdownGracefulWait: 60,
|
|
@@ -482,6 +563,7 @@ async function runAuth(options) {
|
|
|
482
563
|
consola.info("Verbose logging enabled");
|
|
483
564
|
}
|
|
484
565
|
state.showToken = options.showToken;
|
|
566
|
+
initProxyFromEnv();
|
|
485
567
|
await ensurePaths();
|
|
486
568
|
await setupGitHubToken({ force: true });
|
|
487
569
|
consola.success("GitHub token written to", PATHS.GITHUB_TOKEN_PATH);
|
|
@@ -528,6 +610,7 @@ const checkUsage = defineCommand({
|
|
|
528
610
|
description: "Show current GitHub Copilot usage/quota information"
|
|
529
611
|
},
|
|
530
612
|
async run() {
|
|
613
|
+
initProxyFromEnv();
|
|
531
614
|
await ensurePaths();
|
|
532
615
|
await setupGitHubToken();
|
|
533
616
|
try {
|
|
@@ -585,6 +668,7 @@ async function checkTokenExists() {
|
|
|
585
668
|
}
|
|
586
669
|
async function getAccountInfo() {
|
|
587
670
|
try {
|
|
671
|
+
initProxyFromEnv();
|
|
588
672
|
await ensurePaths();
|
|
589
673
|
await setupGitHubToken();
|
|
590
674
|
if (!state.githubToken) return null;
|
|
@@ -673,6 +757,7 @@ const debugModels = defineCommand({
|
|
|
673
757
|
},
|
|
674
758
|
async run({ args }) {
|
|
675
759
|
state.accountType = args["account-type"];
|
|
760
|
+
initProxyFromEnv();
|
|
676
761
|
await ensurePaths();
|
|
677
762
|
if (args["github-token"]) {
|
|
678
763
|
state.githubToken = args["github-token"];
|
|
@@ -1036,7 +1121,7 @@ const patchClaude = defineCommand({
|
|
|
1036
1121
|
|
|
1037
1122
|
//#endregion
|
|
1038
1123
|
//#region package.json
|
|
1039
|
-
var version = "0.6.
|
|
1124
|
+
var version = "0.6.3";
|
|
1040
1125
|
|
|
1041
1126
|
//#endregion
|
|
1042
1127
|
//#region src/lib/adaptive-rate-limiter.ts
|
|
@@ -1908,6 +1993,26 @@ function getTokenStats() {
|
|
|
1908
1993
|
timeline
|
|
1909
1994
|
};
|
|
1910
1995
|
}
|
|
1996
|
+
function getHistoryEntryCount() {
|
|
1997
|
+
return historyState.entries.length;
|
|
1998
|
+
}
|
|
1999
|
+
function getHistoryMaxEntries() {
|
|
2000
|
+
return historyState.maxEntries;
|
|
2001
|
+
}
|
|
2002
|
+
function setHistoryMaxEntries(max) {
|
|
2003
|
+
historyState.maxEntries = max;
|
|
2004
|
+
}
|
|
2005
|
+
/**
|
|
2006
|
+
* Evict the oldest `count` entries from the history store.
|
|
2007
|
+
* Returns the actual number of entries evicted.
|
|
2008
|
+
*/
|
|
2009
|
+
function evictOldestEntries(count) {
|
|
2010
|
+
if (count <= 0) return 0;
|
|
2011
|
+
const actual = Math.min(count, historyState.entries.length);
|
|
2012
|
+
const removed = historyState.entries.splice(0, actual);
|
|
2013
|
+
for (const entry of removed) if (!historyState.entries.some((e) => e.sessionId === entry.sessionId)) historyState.sessions.delete(entry.sessionId);
|
|
2014
|
+
return actual;
|
|
2015
|
+
}
|
|
1911
2016
|
function exportHistory(format = "json") {
|
|
1912
2017
|
if (format === "json") return JSON.stringify({
|
|
1913
2018
|
sessions: Array.from(historyState.sessions.values()),
|
|
@@ -1948,6 +2053,91 @@ function exportHistory(format = "json") {
|
|
|
1948
2053
|
return [headers.join(","), ...rows.map((r) => r.join(","))].join("\n");
|
|
1949
2054
|
}
|
|
1950
2055
|
|
|
2056
|
+
//#endregion
|
|
2057
|
+
//#region src/lib/history-memory-pressure.ts
|
|
2058
|
+
/**
|
|
2059
|
+
* Memory pressure monitor — proactively evicts old history entries
|
|
2060
|
+
* when heap usage approaches the V8 heap limit, preventing OOM crashes.
|
|
2061
|
+
*
|
|
2062
|
+
* Graduated response:
|
|
2063
|
+
* 75–80% Warning: log only, no eviction
|
|
2064
|
+
* 80–90% High: evict entries, reduce maxEntries by 25%
|
|
2065
|
+
* 90%+ Critical: aggressive eviction, reduce maxEntries by 50%
|
|
2066
|
+
*/
|
|
2067
|
+
const CHECK_INTERVAL_MS = 3e4;
|
|
2068
|
+
const WARN_THRESHOLD = .75;
|
|
2069
|
+
const EVICT_THRESHOLD = .8;
|
|
2070
|
+
const CRITICAL_THRESHOLD = .9;
|
|
2071
|
+
const WARN_LOG_COOLDOWN_MS = 3e5;
|
|
2072
|
+
let resolvedHeapLimit = null;
|
|
2073
|
+
let timer = null;
|
|
2074
|
+
let lastWarningTime = 0;
|
|
2075
|
+
let totalEvictedCount = 0;
|
|
2076
|
+
async function resolveHeapLimit() {
|
|
2077
|
+
if (resolvedHeapLimit !== null) return resolvedHeapLimit;
|
|
2078
|
+
let limit;
|
|
2079
|
+
try {
|
|
2080
|
+
limit = (await import("node:v8")).getHeapStatistics().heap_size_limit;
|
|
2081
|
+
} catch {
|
|
2082
|
+
limit = 512 * 1024 * 1024;
|
|
2083
|
+
}
|
|
2084
|
+
resolvedHeapLimit = limit;
|
|
2085
|
+
return limit;
|
|
2086
|
+
}
|
|
2087
|
+
function formatMB(bytes) {
|
|
2088
|
+
return `${Math.round(bytes / 1024 / 1024)}MB`;
|
|
2089
|
+
}
|
|
2090
|
+
function formatPct(ratio) {
|
|
2091
|
+
return `${Math.round(ratio * 100)}%`;
|
|
2092
|
+
}
|
|
2093
|
+
async function checkMemoryPressure() {
|
|
2094
|
+
const heapLimit = await resolveHeapLimit();
|
|
2095
|
+
const { heapUsed } = process.memoryUsage();
|
|
2096
|
+
const ratio = heapUsed / heapLimit;
|
|
2097
|
+
if (ratio < WARN_THRESHOLD) return;
|
|
2098
|
+
const currentEntries = getHistoryEntryCount();
|
|
2099
|
+
if (currentEntries <= state.historyMinEntries) {
|
|
2100
|
+
if (ratio >= EVICT_THRESHOLD && Date.now() - lastWarningTime > WARN_LOG_COOLDOWN_MS) {
|
|
2101
|
+
lastWarningTime = Date.now();
|
|
2102
|
+
consola.warn(`[memory] Heap ${formatMB(heapUsed)}/${formatMB(heapLimit)} (${formatPct(ratio)}) — only ${currentEntries} history entries remain. Consider increasing --max-old-space-size`);
|
|
2103
|
+
}
|
|
2104
|
+
return;
|
|
2105
|
+
}
|
|
2106
|
+
if (ratio < EVICT_THRESHOLD) {
|
|
2107
|
+
if (Date.now() - lastWarningTime > WARN_LOG_COOLDOWN_MS) {
|
|
2108
|
+
lastWarningTime = Date.now();
|
|
2109
|
+
consola.warn(`[memory] Heap ${formatMB(heapUsed)}/${formatMB(heapLimit)} (${formatPct(ratio)}) — approaching limit, ${currentEntries} history entries in memory`);
|
|
2110
|
+
}
|
|
2111
|
+
return;
|
|
2112
|
+
}
|
|
2113
|
+
lastWarningTime = Date.now();
|
|
2114
|
+
const currentMax = getHistoryMaxEntries();
|
|
2115
|
+
const newMaxEntries = ratio >= CRITICAL_THRESHOLD ? Math.max(state.historyMinEntries, Math.floor(currentMax * .5)) : Math.max(state.historyMinEntries, Math.floor(currentMax * .75));
|
|
2116
|
+
const evictCount = Math.max(0, currentEntries - newMaxEntries);
|
|
2117
|
+
if (evictCount <= 0) return;
|
|
2118
|
+
const evicted = evictOldestEntries(evictCount);
|
|
2119
|
+
totalEvictedCount += evicted;
|
|
2120
|
+
if (newMaxEntries < currentMax) setHistoryMaxEntries(newMaxEntries);
|
|
2121
|
+
const afterHeapUsed = process.memoryUsage().heapUsed;
|
|
2122
|
+
consola.warn(`[memory] Evicted ${evicted} history entries due to memory pressure (heap: ${formatMB(heapUsed)} → ${formatMB(afterHeapUsed)}/${formatMB(heapLimit)}, entries: ${currentEntries} → ${currentEntries - evicted}, max: ${newMaxEntries})`);
|
|
2123
|
+
globalThis.gc?.();
|
|
2124
|
+
}
|
|
2125
|
+
function startMemoryPressureMonitor() {
|
|
2126
|
+
if (timer) return;
|
|
2127
|
+
timer = setInterval(() => {
|
|
2128
|
+
checkMemoryPressure().catch((error) => {
|
|
2129
|
+
consola.error("[memory] Error in memory pressure check:", error);
|
|
2130
|
+
});
|
|
2131
|
+
}, CHECK_INTERVAL_MS);
|
|
2132
|
+
if ("unref" in timer) timer.unref();
|
|
2133
|
+
}
|
|
2134
|
+
function stopMemoryPressureMonitor() {
|
|
2135
|
+
if (timer) {
|
|
2136
|
+
clearInterval(timer);
|
|
2137
|
+
timer = null;
|
|
2138
|
+
}
|
|
2139
|
+
}
|
|
2140
|
+
|
|
1951
2141
|
//#endregion
|
|
1952
2142
|
//#region src/lib/posthog.ts
|
|
1953
2143
|
let client = null;
|
|
@@ -1997,81 +2187,6 @@ async function shutdownPostHog() {
|
|
|
1997
2187
|
}
|
|
1998
2188
|
}
|
|
1999
2189
|
|
|
2000
|
-
//#endregion
|
|
2001
|
-
//#region src/lib/proxy.ts
|
|
2002
|
-
/**
|
|
2003
|
-
* Custom dispatcher that routes requests through proxies based on environment variables.
|
|
2004
|
-
* Extends Agent to properly inherit the Dispatcher interface.
|
|
2005
|
-
*/
|
|
2006
|
-
var ProxyDispatcher = class extends Agent {
|
|
2007
|
-
proxies = /* @__PURE__ */ new Map();
|
|
2008
|
-
dispatch(options, handler) {
|
|
2009
|
-
try {
|
|
2010
|
-
const origin = this.getOriginUrl(options.origin);
|
|
2011
|
-
const proxyUrl = this.getProxyUrl(origin);
|
|
2012
|
-
if (!proxyUrl) {
|
|
2013
|
-
consola.debug(`HTTP proxy bypass: ${origin.hostname}`);
|
|
2014
|
-
return super.dispatch(options, handler);
|
|
2015
|
-
}
|
|
2016
|
-
const agent = this.getOrCreateProxyAgent(proxyUrl);
|
|
2017
|
-
consola.debug(`HTTP proxy route: ${origin.hostname} via ${this.formatProxyLabel(proxyUrl)}`);
|
|
2018
|
-
return agent.dispatch(options, handler);
|
|
2019
|
-
} catch {
|
|
2020
|
-
return super.dispatch(options, handler);
|
|
2021
|
-
}
|
|
2022
|
-
}
|
|
2023
|
-
getOriginUrl(origin) {
|
|
2024
|
-
return typeof origin === "string" ? new URL(origin) : origin;
|
|
2025
|
-
}
|
|
2026
|
-
getProxyUrl(origin) {
|
|
2027
|
-
const raw = getProxyForUrl(origin.toString());
|
|
2028
|
-
return raw && raw.length > 0 ? raw : void 0;
|
|
2029
|
-
}
|
|
2030
|
-
getOrCreateProxyAgent(proxyUrl) {
|
|
2031
|
-
let agent = this.proxies.get(proxyUrl);
|
|
2032
|
-
if (!agent) {
|
|
2033
|
-
agent = new ProxyAgent(proxyUrl);
|
|
2034
|
-
this.proxies.set(proxyUrl, agent);
|
|
2035
|
-
}
|
|
2036
|
-
return agent;
|
|
2037
|
-
}
|
|
2038
|
-
formatProxyLabel(proxyUrl) {
|
|
2039
|
-
try {
|
|
2040
|
-
const u = new URL(proxyUrl);
|
|
2041
|
-
return `${u.protocol}//${u.host}`;
|
|
2042
|
-
} catch {
|
|
2043
|
-
return proxyUrl;
|
|
2044
|
-
}
|
|
2045
|
-
}
|
|
2046
|
-
async close() {
|
|
2047
|
-
await super.close();
|
|
2048
|
-
await Promise.all([...this.proxies.values()].map((p) => p.close()));
|
|
2049
|
-
this.proxies.clear();
|
|
2050
|
-
}
|
|
2051
|
-
destroy(errOrCallback, callback) {
|
|
2052
|
-
for (const agent of this.proxies.values()) if (typeof errOrCallback === "function") agent.destroy(errOrCallback);
|
|
2053
|
-
else if (callback) agent.destroy(errOrCallback ?? null, callback);
|
|
2054
|
-
else agent.destroy(errOrCallback ?? null).catch(() => {});
|
|
2055
|
-
this.proxies.clear();
|
|
2056
|
-
if (typeof errOrCallback === "function") {
|
|
2057
|
-
super.destroy(errOrCallback);
|
|
2058
|
-
return;
|
|
2059
|
-
} else if (callback) {
|
|
2060
|
-
super.destroy(errOrCallback ?? null, callback);
|
|
2061
|
-
return;
|
|
2062
|
-
} else return super.destroy(errOrCallback ?? null);
|
|
2063
|
-
}
|
|
2064
|
-
};
|
|
2065
|
-
function initProxyFromEnv() {
|
|
2066
|
-
if (typeof Bun !== "undefined") return;
|
|
2067
|
-
try {
|
|
2068
|
-
setGlobalDispatcher(new ProxyDispatcher());
|
|
2069
|
-
consola.debug("HTTP proxy configured from environment (per-URL)");
|
|
2070
|
-
} catch (err) {
|
|
2071
|
-
consola.debug("Proxy setup skipped:", err);
|
|
2072
|
-
}
|
|
2073
|
-
}
|
|
2074
|
-
|
|
2075
2190
|
//#endregion
|
|
2076
2191
|
//#region src/lib/shell.ts
|
|
2077
2192
|
function getShell() {
|
|
@@ -2184,6 +2299,7 @@ async function gracefulShutdown(signal, deps) {
|
|
|
2184
2299
|
try {
|
|
2185
2300
|
deps?.contextManager?.stopReaper();
|
|
2186
2301
|
} catch {}
|
|
2302
|
+
stopMemoryPressureMonitor();
|
|
2187
2303
|
stopRefresh();
|
|
2188
2304
|
const wsClients = getWsCount();
|
|
2189
2305
|
if (wsClients > 0) {
|
|
@@ -6550,6 +6666,156 @@ async function checkNeedsCompactionAnthropic(payload, model, config = {}) {
|
|
|
6550
6666
|
};
|
|
6551
6667
|
}
|
|
6552
6668
|
|
|
6669
|
+
//#endregion
|
|
6670
|
+
//#region src/lib/anthropic/features.ts
|
|
6671
|
+
function normalizeForMatching(modelId) {
|
|
6672
|
+
return modelId.toLowerCase().replaceAll(/[-_.]/g, "").replace(/\d{8}$/, "");
|
|
6673
|
+
}
|
|
6674
|
+
function modelSupportsContextEditing(modelId) {
|
|
6675
|
+
const n = normalizeForMatching(modelId);
|
|
6676
|
+
return n.includes("claude") && (n.includes("haiku45") || n.includes("sonnet4") || n.includes("sonnet45") || n.includes("sonnet46") || n.includes("opus4") || n.includes("opus41") || n.includes("opus45") || n.includes("opus46"));
|
|
6677
|
+
}
|
|
6678
|
+
function modelSupportsToolSearch(modelId) {
|
|
6679
|
+
const n = normalizeForMatching(modelId);
|
|
6680
|
+
return n.includes("claude") && (n.includes("opus45") || n.includes("opus46") || n.includes("sonnet45") || n.includes("sonnet46"));
|
|
6681
|
+
}
|
|
6682
|
+
function isContextEditingEnabled(modelId) {
|
|
6683
|
+
return modelSupportsContextEditing(modelId) && state.contextEditingMode !== "off";
|
|
6684
|
+
}
|
|
6685
|
+
function modelHasAdaptiveThinking(resolvedModel) {
|
|
6686
|
+
return resolvedModel?.capabilities?.supports?.adaptive_thinking === true;
|
|
6687
|
+
}
|
|
6688
|
+
function buildAnthropicBetaHeaders(modelId, resolvedModel) {
|
|
6689
|
+
const headers = {};
|
|
6690
|
+
const betaFeatures = [];
|
|
6691
|
+
if (!modelHasAdaptiveThinking(resolvedModel)) betaFeatures.push("interleaved-thinking-2025-05-14");
|
|
6692
|
+
if (isContextEditingEnabled(modelId)) betaFeatures.push("context-management-2025-06-27");
|
|
6693
|
+
if (modelSupportsToolSearch(modelId)) betaFeatures.push("advanced-tool-use-2025-11-20");
|
|
6694
|
+
if (betaFeatures.length > 0) headers["anthropic-beta"] = betaFeatures.join(",");
|
|
6695
|
+
return headers;
|
|
6696
|
+
}
|
|
6697
|
+
const THINKING_KEEP_TURNS = 2;
|
|
6698
|
+
const TOOL_USE_TRIGGER_TYPE = "input_tokens";
|
|
6699
|
+
const TOOL_USE_TRIGGER_VALUE = 8e4;
|
|
6700
|
+
const TOOL_USE_KEEP_COUNT = 10;
|
|
6701
|
+
function buildContextManagement(mode, hasThinking) {
|
|
6702
|
+
if (mode === "off") return void 0;
|
|
6703
|
+
const edits = [];
|
|
6704
|
+
if ((mode === "clear-thinking" || mode === "clear-both") && hasThinking) edits.push({
|
|
6705
|
+
type: "clear_thinking_20251015",
|
|
6706
|
+
keep: {
|
|
6707
|
+
type: "thinking_turns",
|
|
6708
|
+
value: Math.max(1, THINKING_KEEP_TURNS)
|
|
6709
|
+
}
|
|
6710
|
+
});
|
|
6711
|
+
if (mode === "clear-tooluse" || mode === "clear-both") edits.push({
|
|
6712
|
+
type: "clear_tool_uses_20250919",
|
|
6713
|
+
trigger: {
|
|
6714
|
+
type: TOOL_USE_TRIGGER_TYPE,
|
|
6715
|
+
value: TOOL_USE_TRIGGER_VALUE
|
|
6716
|
+
},
|
|
6717
|
+
keep: {
|
|
6718
|
+
type: "tool_uses",
|
|
6719
|
+
value: TOOL_USE_KEEP_COUNT
|
|
6720
|
+
}
|
|
6721
|
+
});
|
|
6722
|
+
return edits.length > 0 ? { edits } : void 0;
|
|
6723
|
+
}
|
|
6724
|
+
|
|
6725
|
+
//#endregion
|
|
6726
|
+
//#region src/lib/anthropic/server-tool-filter.ts
|
|
6727
|
+
const SERVER_TOOL_TYPE_PREFIXES = [
|
|
6728
|
+
"web_search_",
|
|
6729
|
+
"web_fetch_",
|
|
6730
|
+
"code_execution_",
|
|
6731
|
+
"text_editor_",
|
|
6732
|
+
"computer_",
|
|
6733
|
+
"bash_"
|
|
6734
|
+
];
|
|
6735
|
+
/** Check if a block type is a server-side tool result (ends with _tool_result, but not plain tool_result) */
|
|
6736
|
+
function isServerToolResultType(type) {
|
|
6737
|
+
return type !== "tool_result" && type.endsWith("_tool_result");
|
|
6738
|
+
}
|
|
6739
|
+
/** Check if a content block is a server-side tool block */
|
|
6740
|
+
function isServerToolBlock(block) {
|
|
6741
|
+
if (block.type === "server_tool_use") return true;
|
|
6742
|
+
return isServerToolResultType(block.type);
|
|
6743
|
+
}
|
|
6744
|
+
/** Check if a tool's type field matches a known server tool prefix */
|
|
6745
|
+
function isServerToolType(type) {
|
|
6746
|
+
if (!type) return false;
|
|
6747
|
+
return SERVER_TOOL_TYPE_PREFIXES.some((prefix) => type.startsWith(prefix));
|
|
6748
|
+
}
|
|
6749
|
+
/** Log a single server tool block */
|
|
6750
|
+
function logServerToolBlock(block) {
|
|
6751
|
+
if (block.type === "server_tool_use") {
|
|
6752
|
+
consola.debug(`[ServerTool] server_tool_use: ${block.name}`);
|
|
6753
|
+
return;
|
|
6754
|
+
}
|
|
6755
|
+
if (!isServerToolResultType(block.type)) return;
|
|
6756
|
+
consola.debug(`[ServerTool] ${block.type}`);
|
|
6757
|
+
}
|
|
6758
|
+
/** Log all server tool blocks from a non-streaming response */
|
|
6759
|
+
function logServerToolBlocks(content) {
|
|
6760
|
+
for (const block of content) logServerToolBlock(block);
|
|
6761
|
+
}
|
|
6762
|
+
/**
|
|
6763
|
+
* Creates a filter for server tool blocks in SSE streams.
|
|
6764
|
+
* Handles index remapping so block indices remain dense/sequential after filtering.
|
|
6765
|
+
* Always active — matching vscode-copilot-chat behavior.
|
|
6766
|
+
*/
|
|
6767
|
+
function createServerToolBlockFilter() {
|
|
6768
|
+
const filteredIndices = /* @__PURE__ */ new Set();
|
|
6769
|
+
const clientIndexMap = /* @__PURE__ */ new Map();
|
|
6770
|
+
let nextClientIndex = 0;
|
|
6771
|
+
function getClientIndex(apiIndex) {
|
|
6772
|
+
let idx = clientIndexMap.get(apiIndex);
|
|
6773
|
+
if (idx === void 0) {
|
|
6774
|
+
idx = nextClientIndex++;
|
|
6775
|
+
clientIndexMap.set(apiIndex, idx);
|
|
6776
|
+
}
|
|
6777
|
+
return idx;
|
|
6778
|
+
}
|
|
6779
|
+
return { rewriteEvent(parsed, rawData) {
|
|
6780
|
+
if (!parsed) return rawData;
|
|
6781
|
+
if (parsed.type === "content_block_start") {
|
|
6782
|
+
const block = parsed.content_block;
|
|
6783
|
+
if (isServerToolBlock(block)) {
|
|
6784
|
+
filteredIndices.add(parsed.index);
|
|
6785
|
+
return null;
|
|
6786
|
+
}
|
|
6787
|
+
if (filteredIndices.size === 0) {
|
|
6788
|
+
getClientIndex(parsed.index);
|
|
6789
|
+
return rawData;
|
|
6790
|
+
}
|
|
6791
|
+
const clientIndex = getClientIndex(parsed.index);
|
|
6792
|
+
if (clientIndex === parsed.index) return rawData;
|
|
6793
|
+
const obj = JSON.parse(rawData);
|
|
6794
|
+
obj.index = clientIndex;
|
|
6795
|
+
return JSON.stringify(obj);
|
|
6796
|
+
}
|
|
6797
|
+
if (parsed.type === "content_block_delta" || parsed.type === "content_block_stop") {
|
|
6798
|
+
if (filteredIndices.has(parsed.index)) return null;
|
|
6799
|
+
if (filteredIndices.size === 0) return rawData;
|
|
6800
|
+
const clientIndex = getClientIndex(parsed.index);
|
|
6801
|
+
if (clientIndex === parsed.index) return rawData;
|
|
6802
|
+
const obj = JSON.parse(rawData);
|
|
6803
|
+
obj.index = clientIndex;
|
|
6804
|
+
return JSON.stringify(obj);
|
|
6805
|
+
}
|
|
6806
|
+
return rawData;
|
|
6807
|
+
} };
|
|
6808
|
+
}
|
|
6809
|
+
/** Filter server tool blocks from a non-streaming response */
|
|
6810
|
+
function filterServerToolBlocksFromResponse(response) {
|
|
6811
|
+
const filtered = response.content.filter((block) => !isServerToolBlock(block));
|
|
6812
|
+
if (filtered.length === response.content.length) return response;
|
|
6813
|
+
return {
|
|
6814
|
+
...response,
|
|
6815
|
+
content: filtered
|
|
6816
|
+
};
|
|
6817
|
+
}
|
|
6818
|
+
|
|
6553
6819
|
//#endregion
|
|
6554
6820
|
//#region src/services/copilot/create-anthropic-messages.ts
|
|
6555
6821
|
/**
|
|
@@ -6574,14 +6840,15 @@ const COPILOT_SUPPORTED_FIELDS = new Set([
|
|
|
6574
6840
|
"tools",
|
|
6575
6841
|
"tool_choice",
|
|
6576
6842
|
"thinking",
|
|
6577
|
-
"service_tier"
|
|
6843
|
+
"service_tier",
|
|
6844
|
+
"context_management"
|
|
6578
6845
|
]);
|
|
6579
6846
|
/**
|
|
6580
6847
|
* Filter payload to only include fields supported by Copilot's Anthropic API.
|
|
6581
6848
|
* This prevents errors like "Extra inputs are not permitted" for unsupported
|
|
6582
6849
|
* fields like `output_config`.
|
|
6583
6850
|
*
|
|
6584
|
-
*
|
|
6851
|
+
* Optionally strips server-side tools when state.stripServerTools is enabled.
|
|
6585
6852
|
*/
|
|
6586
6853
|
function filterPayloadForCopilot(payload) {
|
|
6587
6854
|
const filtered = {};
|
|
@@ -6589,7 +6856,7 @@ function filterPayloadForCopilot(payload) {
|
|
|
6589
6856
|
for (const [key, value] of Object.entries(payload)) if (COPILOT_SUPPORTED_FIELDS.has(key)) filtered[key] = value;
|
|
6590
6857
|
else unsupportedFields.push(key);
|
|
6591
6858
|
if (unsupportedFields.length > 0) consola.debug(`[DirectAnthropic] Filtered unsupported fields: ${unsupportedFields.join(", ")}`);
|
|
6592
|
-
if (filtered.tools) filtered.tools =
|
|
6859
|
+
if (filtered.tools) filtered.tools = stripServerToolsFromPayload(filtered.tools);
|
|
6593
6860
|
return filtered;
|
|
6594
6861
|
}
|
|
6595
6862
|
/**
|
|
@@ -6620,6 +6887,7 @@ async function createAnthropicMessages(payload, options) {
|
|
|
6620
6887
|
if (!state.copilotToken) throw new Error("Copilot token not found");
|
|
6621
6888
|
let filteredPayload = filterPayloadForCopilot(payload);
|
|
6622
6889
|
filteredPayload = adjustMaxTokensForThinking(filteredPayload);
|
|
6890
|
+
const resolvedModel = state.models?.data.find((m) => m.id === filteredPayload.model);
|
|
6623
6891
|
const enableVision = filteredPayload.messages.some((msg) => {
|
|
6624
6892
|
if (typeof msg.content === "string") return false;
|
|
6625
6893
|
return msg.content.some((block) => block.type === "image");
|
|
@@ -6633,6 +6901,16 @@ async function createAnthropicMessages(payload, options) {
|
|
|
6633
6901
|
"X-Initiator": options?.initiator ?? (isAgentCall ? "agent" : "user"),
|
|
6634
6902
|
"anthropic-version": "2023-06-01"
|
|
6635
6903
|
};
|
|
6904
|
+
const betaHeaders = buildAnthropicBetaHeaders(filteredPayload.model, resolvedModel);
|
|
6905
|
+
Object.assign(headers, betaHeaders);
|
|
6906
|
+
if (isContextEditingEnabled(filteredPayload.model)) {
|
|
6907
|
+
const hasThinking = filteredPayload.thinking?.type === "enabled";
|
|
6908
|
+
const cm = buildContextManagement(state.contextEditingMode, hasThinking);
|
|
6909
|
+
if (cm) {
|
|
6910
|
+
filteredPayload.context_management = cm;
|
|
6911
|
+
consola.debug("[DirectAnthropic] Added context_management:", JSON.stringify(cm));
|
|
6912
|
+
}
|
|
6913
|
+
}
|
|
6636
6914
|
consola.debug("Sending direct Anthropic request to Copilot /v1/messages");
|
|
6637
6915
|
const response = await fetch(`${copilotBaseUrl(state)}/v1/messages`, {
|
|
6638
6916
|
method: "POST",
|
|
@@ -6656,96 +6934,21 @@ async function createAnthropicMessages(payload, options) {
|
|
|
6656
6934
|
if (payload.stream) return events(response);
|
|
6657
6935
|
return await response.json();
|
|
6658
6936
|
}
|
|
6659
|
-
const SERVER_TOOL_CONFIGS = {
|
|
6660
|
-
web_search: {
|
|
6661
|
-
description: "Search the web for current information. Returns web search results that can help answer questions about recent events, current data, or information that may have changed since your knowledge cutoff.",
|
|
6662
|
-
input_schema: {
|
|
6663
|
-
type: "object",
|
|
6664
|
-
properties: { query: {
|
|
6665
|
-
type: "string",
|
|
6666
|
-
description: "The search query"
|
|
6667
|
-
} },
|
|
6668
|
-
required: ["query"]
|
|
6669
|
-
}
|
|
6670
|
-
},
|
|
6671
|
-
web_fetch: {
|
|
6672
|
-
description: "Fetch content from a URL. NOTE: This is a client-side tool - the client must fetch the URL and return the content.",
|
|
6673
|
-
input_schema: {
|
|
6674
|
-
type: "object",
|
|
6675
|
-
properties: { url: {
|
|
6676
|
-
type: "string",
|
|
6677
|
-
description: "The URL to fetch"
|
|
6678
|
-
} },
|
|
6679
|
-
required: ["url"]
|
|
6680
|
-
}
|
|
6681
|
-
},
|
|
6682
|
-
code_execution: {
|
|
6683
|
-
description: "Execute code in a sandbox. NOTE: This is a client-side tool - the client must execute the code.",
|
|
6684
|
-
input_schema: {
|
|
6685
|
-
type: "object",
|
|
6686
|
-
properties: {
|
|
6687
|
-
code: {
|
|
6688
|
-
type: "string",
|
|
6689
|
-
description: "The code to execute"
|
|
6690
|
-
},
|
|
6691
|
-
language: {
|
|
6692
|
-
type: "string",
|
|
6693
|
-
description: "The programming language"
|
|
6694
|
-
}
|
|
6695
|
-
},
|
|
6696
|
-
required: ["code"]
|
|
6697
|
-
}
|
|
6698
|
-
},
|
|
6699
|
-
computer: {
|
|
6700
|
-
description: "Control computer desktop. NOTE: This is a client-side tool - the client must handle computer control.",
|
|
6701
|
-
input_schema: {
|
|
6702
|
-
type: "object",
|
|
6703
|
-
properties: { action: {
|
|
6704
|
-
type: "string",
|
|
6705
|
-
description: "The action to perform"
|
|
6706
|
-
} },
|
|
6707
|
-
required: ["action"]
|
|
6708
|
-
}
|
|
6709
|
-
}
|
|
6710
|
-
};
|
|
6711
|
-
/**
|
|
6712
|
-
* Check if a tool is a server-side tool that needs conversion.
|
|
6713
|
-
*/
|
|
6714
|
-
function getServerToolPrefix(tool) {
|
|
6715
|
-
if (tool.type) {
|
|
6716
|
-
for (const prefix of Object.keys(SERVER_TOOL_CONFIGS)) if (tool.type.startsWith(prefix)) return prefix;
|
|
6717
|
-
}
|
|
6718
|
-
return null;
|
|
6719
|
-
}
|
|
6720
6937
|
/**
|
|
6721
|
-
*
|
|
6722
|
-
*
|
|
6723
|
-
*
|
|
6724
|
-
* Note: Server-side tools are only converted if state.rewriteAnthropicTools is enabled.
|
|
6938
|
+
* Strip server-side tools from the tools array when configured.
|
|
6939
|
+
* When state.stripServerTools is enabled, server tools are removed from the request.
|
|
6940
|
+
* When disabled (default), server tools are passed through unchanged.
|
|
6725
6941
|
*/
|
|
6726
|
-
function
|
|
6727
|
-
if (!tools) return;
|
|
6942
|
+
function stripServerToolsFromPayload(tools) {
|
|
6943
|
+
if (!tools) return void 0;
|
|
6944
|
+
if (!state.stripServerTools) return tools;
|
|
6728
6945
|
const result = [];
|
|
6729
6946
|
for (const tool of tools) {
|
|
6730
|
-
|
|
6731
|
-
|
|
6732
|
-
|
|
6733
|
-
|
|
6734
|
-
|
|
6735
|
-
result.push(tool);
|
|
6736
|
-
continue;
|
|
6737
|
-
}
|
|
6738
|
-
if (config.remove) {
|
|
6739
|
-
consola.warn(`[DirectAnthropic] Removing unsupported server tool: ${tool.name}. Reason: ${config.removalReason}`);
|
|
6740
|
-
continue;
|
|
6741
|
-
}
|
|
6742
|
-
consola.debug(`[DirectAnthropic] Converting server tool to custom: ${tool.name} (type: ${tool.type})`);
|
|
6743
|
-
result.push({
|
|
6744
|
-
name: tool.name,
|
|
6745
|
-
description: config.description,
|
|
6746
|
-
input_schema: config.input_schema
|
|
6747
|
-
});
|
|
6748
|
-
} else result.push(tool);
|
|
6947
|
+
if (isServerToolType(tool.type)) {
|
|
6948
|
+
consola.warn(`[DirectAnthropic] Stripping server tool: ${tool.name} (type: ${tool.type})`);
|
|
6949
|
+
continue;
|
|
6950
|
+
}
|
|
6951
|
+
result.push(tool);
|
|
6749
6952
|
}
|
|
6750
6953
|
return result.length > 0 ? result : void 0;
|
|
6751
6954
|
}
|
|
@@ -6840,9 +7043,6 @@ function createAnthropicStreamAccumulator() {
|
|
|
6840
7043
|
currentToolCall: null
|
|
6841
7044
|
};
|
|
6842
7045
|
}
|
|
6843
|
-
function isServerToolResultType(type) {
|
|
6844
|
-
return type !== "tool_result" && type.endsWith("_tool_result");
|
|
6845
|
-
}
|
|
6846
7046
|
function processAnthropicEvent(event, acc) {
|
|
6847
7047
|
switch (event.type) {
|
|
6848
7048
|
case "content_block_delta":
|
|
@@ -7491,6 +7691,8 @@ function handleDirectAnthropicNonStreamingResponse(c, response, ctx, truncateRes
|
|
|
7491
7691
|
});
|
|
7492
7692
|
let finalResponse = response;
|
|
7493
7693
|
if (state.verbose && truncateResult?.wasCompacted) finalResponse = prependMarkerToAnthropicResponse$1(response, createTruncationMarker$1(truncateResult));
|
|
7694
|
+
logServerToolBlocks(finalResponse.content);
|
|
7695
|
+
finalResponse = filterServerToolBlocksFromResponse(finalResponse);
|
|
7494
7696
|
return c.json(finalResponse);
|
|
7495
7697
|
}
|
|
7496
7698
|
/**
|
|
@@ -7522,6 +7724,7 @@ async function handleDirectAnthropicStreamingResponse(opts) {
|
|
|
7522
7724
|
const { stream, response, anthropicPayload, ctx } = opts;
|
|
7523
7725
|
const acc = createAnthropicStreamAccumulator();
|
|
7524
7726
|
const checkRepetition = createStreamRepetitionChecker(`anthropic:${anthropicPayload.model}`);
|
|
7727
|
+
const serverToolFilter = createServerToolBlockFilter();
|
|
7525
7728
|
try {
|
|
7526
7729
|
for await (const rawEvent of response) {
|
|
7527
7730
|
consola.debug("Direct Anthropic raw stream event:", JSON.stringify(rawEvent));
|
|
@@ -7535,10 +7738,13 @@ async function handleDirectAnthropicStreamingResponse(opts) {
|
|
|
7535
7738
|
continue;
|
|
7536
7739
|
}
|
|
7537
7740
|
processAnthropicEvent(event, acc);
|
|
7741
|
+
if (event.type === "content_block_start") logServerToolBlock(event.content_block);
|
|
7538
7742
|
if (event.type === "content_block_delta" && event.delta.type === "text_delta") checkRepetition(event.delta.text);
|
|
7743
|
+
const forwardData = serverToolFilter.rewriteEvent(event, rawEvent.data);
|
|
7744
|
+
if (forwardData === null) continue;
|
|
7539
7745
|
await stream.writeSSE({
|
|
7540
7746
|
event: rawEvent.event || event.type,
|
|
7541
|
-
data:
|
|
7747
|
+
data: forwardData
|
|
7542
7748
|
});
|
|
7543
7749
|
}
|
|
7544
7750
|
recordAnthropicStreamingResponse(acc, anthropicPayload.model, ctx);
|
|
@@ -8056,6 +8262,38 @@ const handleItemId = (parsed, tracker) => {
|
|
|
8056
8262
|
|
|
8057
8263
|
//#endregion
|
|
8058
8264
|
//#region src/routes/responses/utils.ts
|
|
8265
|
+
const CALL_PREFIX = "call_";
|
|
8266
|
+
const FC_PREFIX = "fc_";
|
|
8267
|
+
/**
|
|
8268
|
+
* Normalize function call IDs in Responses API input.
|
|
8269
|
+
* Converts Chat Completions format `call_xxx` IDs to Responses format `fc_xxx` IDs
|
|
8270
|
+
* on function_call and function_call_output items.
|
|
8271
|
+
*/
|
|
8272
|
+
function normalizeCallIds(payload) {
|
|
8273
|
+
if (typeof payload.input === "string") return payload;
|
|
8274
|
+
let count = 0;
|
|
8275
|
+
const input = payload.input.map((item) => {
|
|
8276
|
+
if ("call_id" in item && typeof item.call_id === "string") {
|
|
8277
|
+
const callId = item.call_id;
|
|
8278
|
+
if (callId.startsWith(CALL_PREFIX)) {
|
|
8279
|
+
count++;
|
|
8280
|
+
return {
|
|
8281
|
+
...item,
|
|
8282
|
+
call_id: FC_PREFIX + callId.slice(5)
|
|
8283
|
+
};
|
|
8284
|
+
}
|
|
8285
|
+
}
|
|
8286
|
+
return item;
|
|
8287
|
+
});
|
|
8288
|
+
if (count > 0) {
|
|
8289
|
+
consola.debug(`[Responses] Normalized ${count} call IDs (call_ → fc_)`);
|
|
8290
|
+
return {
|
|
8291
|
+
...payload,
|
|
8292
|
+
input
|
|
8293
|
+
};
|
|
8294
|
+
}
|
|
8295
|
+
return payload;
|
|
8296
|
+
}
|
|
8059
8297
|
const getResponsesRequestOptions = (payload) => {
|
|
8060
8298
|
return {
|
|
8061
8299
|
vision: hasVisionInput(payload),
|
|
@@ -8206,7 +8444,8 @@ const TERMINAL_EVENTS = new Set([
|
|
|
8206
8444
|
"error"
|
|
8207
8445
|
]);
|
|
8208
8446
|
const handleResponses = async (c) => {
|
|
8209
|
-
|
|
8447
|
+
let payload = await c.req.json();
|
|
8448
|
+
if (state.normalizeResponsesCallIds) payload = normalizeCallIds(payload);
|
|
8210
8449
|
consola.debug("Responses request payload:", JSON.stringify(payload));
|
|
8211
8450
|
const trackingId = c.get("trackingId");
|
|
8212
8451
|
const startTime = (trackingId ? requestTracker.getRequest(trackingId) : void 0)?.startTime ?? Date.now();
|
|
@@ -8485,7 +8724,8 @@ async function runServer(options) {
|
|
|
8485
8724
|
state.autoTruncate = options.autoTruncate;
|
|
8486
8725
|
state.compressToolResults = options.compressToolResults;
|
|
8487
8726
|
state.redirectAnthropic = options.redirectAnthropic;
|
|
8488
|
-
state.
|
|
8727
|
+
state.stripServerTools = options.stripServerTools;
|
|
8728
|
+
state.contextEditingMode = options.contextEditing;
|
|
8489
8729
|
state.timezoneOffset = options.timezoneOffset;
|
|
8490
8730
|
if (options.rateLimit) initAdaptiveRateLimiter({
|
|
8491
8731
|
baseRetryIntervalSeconds: options.retryInterval,
|
|
@@ -8497,11 +8737,13 @@ async function runServer(options) {
|
|
|
8497
8737
|
if (!options.autoTruncate) consola.info("Auto-truncate disabled");
|
|
8498
8738
|
if (options.compressToolResults) consola.info("Tool result compression enabled");
|
|
8499
8739
|
if (options.redirectAnthropic) consola.info("Anthropic API redirect enabled (using OpenAI translation)");
|
|
8500
|
-
if (
|
|
8740
|
+
if (options.stripServerTools) consola.info("Server-side tools will be stripped from requests");
|
|
8741
|
+
if (options.contextEditing !== "off") consola.info(`Context editing mode: ${options.contextEditing}`);
|
|
8501
8742
|
initHistory(options.history, options.historyLimit);
|
|
8502
8743
|
if (options.history) {
|
|
8503
8744
|
const limitText = options.historyLimit === 0 ? "unlimited" : `max ${options.historyLimit}`;
|
|
8504
8745
|
consola.info(`History recording enabled (${limitText} entries)`);
|
|
8746
|
+
startMemoryPressureMonitor();
|
|
8505
8747
|
}
|
|
8506
8748
|
if (options.posthogKey) {
|
|
8507
8749
|
initPostHog(options.posthogKey);
|
|
@@ -8672,10 +8914,15 @@ const start = defineCommand({
|
|
|
8672
8914
|
default: false,
|
|
8673
8915
|
description: "Redirect Anthropic models through OpenAI translation (instead of direct API)"
|
|
8674
8916
|
},
|
|
8675
|
-
"
|
|
8917
|
+
"strip-server-tools": {
|
|
8676
8918
|
type: "boolean",
|
|
8677
8919
|
default: false,
|
|
8678
|
-
description: "
|
|
8920
|
+
description: "Strip Anthropic server-side tools (web_search, etc.) from requests"
|
|
8921
|
+
},
|
|
8922
|
+
"context-editing": {
|
|
8923
|
+
type: "string",
|
|
8924
|
+
default: "off",
|
|
8925
|
+
description: "Context editing mode: off, clear-thinking, clear-tooluse, clear-both"
|
|
8679
8926
|
},
|
|
8680
8927
|
"timezone-offset": {
|
|
8681
8928
|
type: "string",
|
|
@@ -8708,7 +8955,8 @@ const start = defineCommand({
|
|
|
8708
8955
|
autoTruncate: !args["no-auto-truncate"],
|
|
8709
8956
|
compressToolResults: args["compress-tool-results"],
|
|
8710
8957
|
redirectAnthropic: args["redirect-anthropic"],
|
|
8711
|
-
|
|
8958
|
+
stripServerTools: args["strip-server-tools"],
|
|
8959
|
+
contextEditing: args["context-editing"],
|
|
8712
8960
|
timezoneOffset: parseTimezoneOffset(args["timezone-offset"]),
|
|
8713
8961
|
posthogKey: args["posthog-key"]
|
|
8714
8962
|
});
|