@dianshuv/copilot-api 0.4.1 → 0.4.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.
Files changed (3) hide show
  1. package/README.md +11 -1
  2. package/dist/main.mjs +21 -6
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -61,10 +61,11 @@ copilot-api start
61
61
  | `--proxy-env` | Use proxy from environment | false |
62
62
  | `--no-history` | Disable request history UI at `/history` | false |
63
63
  | `--history-limit` | Max history entries in memory | 1000 |
64
- | `--no-auto-truncate` | Disable auto-truncate when exceeding limits | false |
64
+ | `--no-auto-truncate` | Disable auto-truncate when exceeding token limits | false |
65
65
  | `--compress-tool-results` | Compress old tool results before truncating | false |
66
66
  | `--redirect-anthropic` | Force Anthropic through OpenAI translation | false |
67
67
  | `--no-rewrite-anthropic-tools` | Don't rewrite server-side tools | false |
68
+ | `--timezone-offset` | Timezone offset in hours from UTC for log timestamps (e.g., +8, -5, 0) | +8 |
68
69
 
69
70
  ### Patch-Claude Command Options
70
71
 
@@ -105,6 +106,15 @@ copilot-api start
105
106
  | `/history` | GET | Request history Web UI with token analytics (enabled by default) |
106
107
  | `/history/api/*` | GET/DELETE | History API endpoints |
107
108
 
109
+ ## Auto-Truncate
110
+
111
+ When enabled (default), auto-truncate automatically compacts conversation history when it exceeds the model's token limit. This prevents request failures due to context overflow.
112
+
113
+ - **Token-based truncation**: Uses the model's `max_context_window_tokens` from the Copilot API to determine when truncation is needed. A 2% safety margin is applied.
114
+ - **No preset byte limit**: There is no hardcoded request body size limit. If the Copilot API returns a 413 (Request Entity Too Large), the proxy dynamically learns the byte limit and applies it to subsequent requests.
115
+ - **Smart compression**: With `--compress-tool-results`, old tool results are compressed before removing messages, preserving more conversation context.
116
+ - **Orphan filtering**: After truncation, orphaned tool results (without matching tool calls) are automatically removed.
117
+
108
118
  ## Using with Claude Code
109
119
 
110
120
  Create `.claude/settings.json` in your project:
package/dist/main.mjs CHANGED
@@ -52,6 +52,7 @@ const state = {
52
52
  redirectAnthropic: false,
53
53
  rewriteAnthropicTools: true,
54
54
  staleRequestMaxAge: 600,
55
+ timezoneOffset: 8,
55
56
  shutdownGracefulWait: 60,
56
57
  shutdownAbortWait: 120
57
58
  };
@@ -105,7 +106,7 @@ const GITHUB_APP_SCOPES = ["read:user"].join(" ");
105
106
  */
106
107
  const DEFAULT_AUTO_TRUNCATE_CONFIG = {
107
108
  safetyMarginPercent: 2,
108
- maxRequestBodyBytes: 510 * 1024,
109
+ maxRequestBodyBytes: Infinity,
109
110
  preserveRecentPercent: .7
110
111
  };
111
112
  /** Dynamic byte limit that adjusts based on 413 errors */
@@ -1020,7 +1021,7 @@ const patchClaude = defineCommand({
1020
1021
 
1021
1022
  //#endregion
1022
1023
  //#region package.json
1023
- var version = "0.4.1";
1024
+ var version = "0.4.3";
1024
1025
 
1025
1026
  //#endregion
1026
1027
  //#region src/lib/adaptive-rate-limiter.ts
@@ -2199,7 +2200,8 @@ function setupShutdownHandlers() {
2199
2200
  //#region src/lib/tui/console-renderer.ts
2200
2201
  const CLEAR_LINE = "\x1B[2K\r";
2201
2202
  function formatTime(date = /* @__PURE__ */ new Date()) {
2202
- return `${String(date.getHours()).padStart(2, "0")}:${String(date.getMinutes()).padStart(2, "0")}:${String(date.getSeconds()).padStart(2, "0")}`;
2203
+ const adjusted = new Date(date.getTime() + state.timezoneOffset * 60 * 60 * 1e3);
2204
+ return `${String(adjusted.getUTCHours()).padStart(2, "0")}:${String(adjusted.getUTCMinutes()).padStart(2, "0")}:${String(adjusted.getUTCSeconds()).padStart(2, "0")}`;
2203
2205
  }
2204
2206
  function formatDuration(ms) {
2205
2207
  if (ms < 1e3) return `${ms}ms`;
@@ -3519,7 +3521,7 @@ async function buildFinalPayload(payload, model) {
3519
3521
  }
3520
3522
  try {
3521
3523
  const check = await checkNeedsCompactionOpenAI(payload, model);
3522
- consola.debug(`Auto-truncate check: ${check.currentTokens} tokens (limit ${check.tokenLimit}), ${Math.round(check.currentBytes / 1024)}KB (limit ${Math.round(check.byteLimit / 1024)}KB), needed: ${check.needed}${check.reason ? ` (${check.reason})` : ""}`);
3524
+ consola.debug(`Auto-truncate check: ${check.currentTokens} tokens (limit ${check.tokenLimit}), ${Math.round(check.currentBytes / 1024)}KB (limit ${check.byteLimit === Infinity ? "unlimited" : `${Math.round(check.byteLimit / 1024)}KB`}), needed: ${check.needed}${check.reason ? ` (${check.reason})` : ""}`);
3523
3525
  if (!check.needed) return {
3524
3526
  finalPayload: payload,
3525
3527
  truncateResult: null
@@ -6736,7 +6738,7 @@ async function handleDirectAnthropicCompletion(c, anthropicPayload, ctx, initiat
6736
6738
  let truncateResult;
6737
6739
  if (state.autoTruncate && selectedModel) {
6738
6740
  const check = await checkNeedsCompactionAnthropic(anthropicPayload, selectedModel);
6739
- consola.debug(`[Anthropic] Auto-truncate check: ${check.currentTokens} tokens (limit ${check.tokenLimit}), ${Math.round(check.currentBytes / 1024)}KB (limit ${Math.round(check.byteLimit / 1024)}KB), needed: ${check.needed}${check.reason ? ` (${check.reason})` : ""}`);
6741
+ consola.debug(`[Anthropic] Auto-truncate check: ${check.currentTokens} tokens (limit ${check.tokenLimit}), ${Math.round(check.currentBytes / 1024)}KB (limit ${check.byteLimit === Infinity ? "unlimited" : `${Math.round(check.byteLimit / 1024)}KB`}), needed: ${check.needed}${check.reason ? ` (${check.reason})` : ""}`);
6740
6742
  if (check.needed) try {
6741
6743
  truncateResult = await autoTruncateAnthropic(anthropicPayload, selectedModel);
6742
6744
  if (truncateResult.wasCompacted) effectivePayload = truncateResult.payload;
@@ -7815,6 +7817,7 @@ async function runServer(options) {
7815
7817
  state.compressToolResults = options.compressToolResults;
7816
7818
  state.redirectAnthropic = options.redirectAnthropic;
7817
7819
  state.rewriteAnthropicTools = options.rewriteAnthropicTools;
7820
+ state.timezoneOffset = options.timezoneOffset;
7818
7821
  if (options.rateLimit) initAdaptiveRateLimiter({
7819
7822
  baseRetryIntervalSeconds: options.retryInterval,
7820
7823
  requestIntervalSeconds: options.requestInterval,
@@ -7879,6 +7882,12 @@ async function runServer(options) {
7879
7882
  hostname: options.host
7880
7883
  }));
7881
7884
  }
7885
+ function parseTimezoneOffset(value) {
7886
+ if (typeof value !== "string") return 8;
7887
+ const n = Number(value);
7888
+ if (!Number.isFinite(n)) return 8;
7889
+ return n;
7890
+ }
7882
7891
  const start = defineCommand({
7883
7892
  meta: {
7884
7893
  name: "start",
@@ -7988,6 +7997,11 @@ const start = defineCommand({
7988
7997
  type: "boolean",
7989
7998
  default: false,
7990
7999
  description: "Don't rewrite Anthropic server-side tools (web_search, etc.) to custom tool format"
8000
+ },
8001
+ "timezone-offset": {
8002
+ type: "string",
8003
+ default: "+8",
8004
+ description: "Timezone offset in hours from UTC for log timestamps (e.g., +8, -5, 0)"
7991
8005
  }
7992
8006
  },
7993
8007
  run({ args }) {
@@ -8011,7 +8025,8 @@ const start = defineCommand({
8011
8025
  autoTruncate: !args["no-auto-truncate"],
8012
8026
  compressToolResults: args["compress-tool-results"],
8013
8027
  redirectAnthropic: args["redirect-anthropic"],
8014
- rewriteAnthropicTools: !args["no-rewrite-anthropic-tools"]
8028
+ rewriteAnthropicTools: !args["no-rewrite-anthropic-tools"],
8029
+ timezoneOffset: parseTimezoneOffset(args["timezone-offset"])
8015
8030
  });
8016
8031
  }
8017
8032
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dianshuv/copilot-api",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "Turn GitHub Copilot into OpenAI/Anthropic API compatible server. Usable with Claude Code!",
5
5
  "author": "dianshuv",
6
6
  "type": "module",