@dianshuv/copilot-api 0.10.0 → 0.10.1

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 (2) hide show
  1. package/dist/main.mjs +488 -15
  2. package/package.json +1 -1
package/dist/main.mjs CHANGED
@@ -1348,7 +1348,7 @@ const patchClaude = defineCommand({
1348
1348
 
1349
1349
  //#endregion
1350
1350
  //#region package.json
1351
- var version = "0.10.0";
1351
+ var version = "0.10.1";
1352
1352
 
1353
1353
  //#endregion
1354
1354
  //#region src/lib/adaptive-rate-limiter.ts
@@ -8011,6 +8011,472 @@ function translateErrorToAnthropicErrorEvent(error) {
8011
8011
  };
8012
8012
  }
8013
8013
 
8014
+ //#endregion
8015
+ //#region src/routes/messages/tool-call-recovery.ts
8016
+ const ENVELOPE = String.raw`(?:<(?:antml:)?function_calls>|call)`;
8017
+ const INVOKE_BODY = String.raw`<(?:antml:)?invoke\s+name="[^"]+">[\s\S]*?</(?:antml:)?invoke>`;
8018
+ const LEAKED_REGION_RE = new RegExp(String.raw`(?:^|\n)[ \t]*` + ENVELOPE + String.raw`[ \t\n]*(?:` + INVOKE_BODY + String.raw`\s*)+(?:</(?:antml:)?function_calls>)?`, "g");
8019
+ const INCOMPLETE_LEAK_TAIL_RE = new RegExp(String.raw`(?:^|\n)[ \t]*` + ENVELOPE + String.raw`[ \t\n]*<(?:antml:)?invoke\b[\s\S]*$`);
8020
+ const LEAK_OPEN_RE = new RegExp(String.raw`(?:^|\n)[ \t]*` + ENVELOPE + String.raw`[ \t\n]*<(?:antml:)?invoke\s+name="`);
8021
+ const INVOKE_RE = /<(?:antml:)?invoke\s+name="([^"]+)">([\s\S]*?)<\/(?:antml:)?invoke>/g;
8022
+ const PARAMETER_RE = /<(?:antml:)?parameter\s+name="([^"]+)">([\s\S]*?)<\/(?:antml:)?parameter>/g;
8023
+ function coerceParamValue(raw) {
8024
+ const trimmed = raw.trim();
8025
+ if (trimmed.startsWith("{") && trimmed.endsWith("}") || trimmed.startsWith("[") && trimmed.endsWith("]")) try {
8026
+ return JSON.parse(trimmed);
8027
+ } catch {
8028
+ return raw;
8029
+ }
8030
+ return raw;
8031
+ }
8032
+ function parseRegionInvokes(region, knownTools) {
8033
+ const calls = [];
8034
+ for (const invokeMatch of region.matchAll(INVOKE_RE)) {
8035
+ const name = invokeMatch[1];
8036
+ if (knownTools && !knownTools.has(name)) continue;
8037
+ const input = {};
8038
+ for (const paramMatch of invokeMatch[2].matchAll(PARAMETER_RE)) input[paramMatch[1]] = coerceParamValue(paramMatch[2]);
8039
+ calls.push({
8040
+ name,
8041
+ input
8042
+ });
8043
+ }
8044
+ return calls;
8045
+ }
8046
+ /**
8047
+ * Split assistant text into ordered segments — dropping leaked envelope markup
8048
+ * (and undeclared-tool invokes) while preserving the natural-language on either
8049
+ * side and the pre/tool/post ordering. Returns a single text segment when there
8050
+ * is no leak. Shared by both response recovery paths so they cannot diverge.
8051
+ */
8052
+ function recoverSegments(text, knownTools) {
8053
+ const segments = [];
8054
+ let cursor = 0;
8055
+ let sawRegion = false;
8056
+ for (const region of text.matchAll(LEAKED_REGION_RE)) {
8057
+ sawRegion = true;
8058
+ const pre = text.slice(cursor, region.index);
8059
+ if (pre.trim() !== "") segments.push({
8060
+ kind: "text",
8061
+ text: pre
8062
+ });
8063
+ for (const call of parseRegionInvokes(region[0], knownTools)) segments.push({
8064
+ kind: "tool",
8065
+ call
8066
+ });
8067
+ cursor = region.index + region[0].length;
8068
+ }
8069
+ if (!sawRegion) return [{
8070
+ kind: "text",
8071
+ text
8072
+ }];
8073
+ const post = text.slice(cursor);
8074
+ if (post.trim() !== "") segments.push({
8075
+ kind: "text",
8076
+ text: post
8077
+ });
8078
+ return segments;
8079
+ }
8080
+ /** True when `text` contains at least one complete, envelope-wrapped leak. */
8081
+ function containsLeakedToolCall(text) {
8082
+ if (!text.includes("invoke")) return false;
8083
+ for (const _region of text.matchAll(LEAKED_REGION_RE)) return true;
8084
+ return false;
8085
+ }
8086
+ /**
8087
+ * Remove complete leaked tool-call regions from assistant text, preserving the
8088
+ * surrounding natural-language on both sides.
8089
+ */
8090
+ function stripLeakedToolCalls(text) {
8091
+ if (!text.includes("invoke")) return text;
8092
+ const stripped = text.replaceAll(LEAKED_REGION_RE, "");
8093
+ if (stripped === text) return text;
8094
+ return stripped.replace(/[ \t\n]+$/, "");
8095
+ }
8096
+ /**
8097
+ * Declared CLIENT tool names for a payload. Server-side tools are excluded (they
8098
+ * are not client-executable, so a leaked server-tool invoke must not become a
8099
+ * client tool_use). Always returns a set — an empty set means "no declared
8100
+ * tools", which correctly drops every leaked invoke rather than trusting it.
8101
+ */
8102
+ function toolNameSet(tools) {
8103
+ return new Set((tools ?? []).filter((tool) => !isServerToolType(tool.type)).map((tool) => tool.name));
8104
+ }
8105
+ const EMPTIED_ASSISTANT_PLACEHOLDER = "[malformed tool call removed by proxy]";
8106
+ function historyHasLeak(text) {
8107
+ if (!text.includes("invoke")) return false;
8108
+ return containsLeakedToolCall(text) || INCOMPLETE_LEAK_TAIL_RE.test(text);
8109
+ }
8110
+ function scrubHistoryText(text) {
8111
+ const stripped = stripLeakedToolCalls(text);
8112
+ const final = stripped.replace(INCOMPLETE_LEAK_TAIL_RE, "");
8113
+ return final === stripped ? stripped : final.replace(/[ \t\n]+$/, "");
8114
+ }
8115
+ /**
8116
+ * Request-side de-poison. Strips leaked tool-call markup (complete and truncated)
8117
+ * from assistant text in the inbound history so the model never sees a
8118
+ * text-format exemplar to imitate — breaking the self-reinforcing poisoning
8119
+ * loop. When stripping empties an assistant message, the message is kept with a
8120
+ * short placeholder rather than dropped, so role alternation is preserved.
8121
+ */
8122
+ function dePoisonAssistantMessages(payload) {
8123
+ if (!payload.messages.some((m) => m.role === "assistant" && (typeof m.content === "string" ? m.content.includes("invoke") : m.content.some((b) => b.type === "text" && b.text.includes("invoke"))))) return payload;
8124
+ let changed = false;
8125
+ const messages = [];
8126
+ for (const msg of payload.messages) {
8127
+ if (msg.role !== "assistant") {
8128
+ messages.push(msg);
8129
+ continue;
8130
+ }
8131
+ if (typeof msg.content === "string") {
8132
+ if (!historyHasLeak(msg.content)) {
8133
+ messages.push(msg);
8134
+ continue;
8135
+ }
8136
+ changed = true;
8137
+ const cleaned = scrubHistoryText(msg.content);
8138
+ messages.push({
8139
+ ...msg,
8140
+ content: cleaned.trim() === "" ? EMPTIED_ASSISTANT_PLACEHOLDER : cleaned
8141
+ });
8142
+ continue;
8143
+ }
8144
+ if (!msg.content.some((b) => b.type === "text" && historyHasLeak(b.text))) {
8145
+ messages.push(msg);
8146
+ continue;
8147
+ }
8148
+ changed = true;
8149
+ const content = msg.content.flatMap((b) => {
8150
+ if (b.type !== "text" || !historyHasLeak(b.text)) return [b];
8151
+ const cleaned = scrubHistoryText(b.text);
8152
+ return cleaned.trim() === "" ? [] : [{
8153
+ ...b,
8154
+ text: cleaned
8155
+ }];
8156
+ });
8157
+ messages.push(content.length > 0 ? {
8158
+ ...msg,
8159
+ content
8160
+ } : {
8161
+ ...msg,
8162
+ content: EMPTIED_ASSISTANT_PLACEHOLDER
8163
+ });
8164
+ }
8165
+ return changed ? {
8166
+ ...payload,
8167
+ messages
8168
+ } : payload;
8169
+ }
8170
+ let recoveryCounter = 0;
8171
+ function nextToolUseId() {
8172
+ recoveryCounter += 1;
8173
+ return `toolu_recovered_${Date.now().toString(36)}_${recoveryCounter}`;
8174
+ }
8175
+ /**
8176
+ * Non-streaming recovery: rewrites any assistant text block that contains a
8177
+ * leaked tool call into ordered [pre-text, tool_use(s), post-text] blocks,
8178
+ * dropping the leak markup and undeclared-tool invokes. stop_reason is flipped to
8179
+ * tool_use only when a real call was recovered AND upstream reported a plain
8180
+ * end-of-turn (so max_tokens / refusal / pause_turn survive).
8181
+ */
8182
+ function recoverLeakedToolCallsInResponse(response, knownTools) {
8183
+ let changed = false;
8184
+ let recoveredCall = false;
8185
+ const content = [];
8186
+ for (const block of response.content) {
8187
+ if (block.type !== "text" || !containsLeakedToolCall(block.text)) {
8188
+ content.push(block);
8189
+ continue;
8190
+ }
8191
+ changed = true;
8192
+ for (const segment of recoverSegments(block.text, knownTools)) if (segment.kind === "text") content.push({
8193
+ type: "text",
8194
+ text: segment.text
8195
+ });
8196
+ else {
8197
+ recoveredCall = true;
8198
+ content.push({
8199
+ type: "tool_use",
8200
+ id: nextToolUseId(),
8201
+ name: segment.call.name,
8202
+ input: segment.call.input
8203
+ });
8204
+ }
8205
+ }
8206
+ if (!changed) return response;
8207
+ const flip = recoveredCall && (response.stop_reason === "end_turn" || response.stop_reason === null);
8208
+ return {
8209
+ ...response,
8210
+ content,
8211
+ stop_reason: flip ? "tool_use" : response.stop_reason
8212
+ };
8213
+ }
8214
+ function out(event) {
8215
+ return {
8216
+ event,
8217
+ data: JSON.stringify(event)
8218
+ };
8219
+ }
8220
+ const TAIL_GUARD = 48;
8221
+ const MAX_CAPTURE = 65536;
8222
+ /**
8223
+ * Per-response streaming transformer. Feed it each parsed upstream Anthropic
8224
+ * event (plus the original `data` string); forward whatever it returns; call
8225
+ * `flush()` once the upstream stream ends.
8226
+ *
8227
+ * Identity passthrough until an envelope appears in a text block; from there it
8228
+ * suppresses the leaked markup, emits structured tool_use block(s) (declared
8229
+ * tools only) plus any trailing prose, shifts the indices of later blocks, and
8230
+ * flips a plain end-of-turn stop_reason to tool_use.
8231
+ */
8232
+ var LeakedToolCallStreamRecovery = class {
8233
+ extraBlocks = 0;
8234
+ text = null;
8235
+ converted = false;
8236
+ knownTools;
8237
+ constructor(knownTools) {
8238
+ this.knownTools = knownTools;
8239
+ }
8240
+ process(event, rawData) {
8241
+ switch (event.type) {
8242
+ case "content_block_start": return this.onBlockStart(event, rawData);
8243
+ case "content_block_delta": return this.onBlockDelta(event, rawData);
8244
+ case "content_block_stop": return this.onBlockStop(event, rawData);
8245
+ case "message_delta": return this.onMessageDelta(event, rawData);
8246
+ default: return [{
8247
+ event,
8248
+ data: rawData
8249
+ }];
8250
+ }
8251
+ }
8252
+ /** Flush any pending (buffered/capturing) text block at end of stream. */
8253
+ flush() {
8254
+ return this.flushPending();
8255
+ }
8256
+ reindexed(event, rawData) {
8257
+ if (this.extraBlocks === 0) return {
8258
+ event,
8259
+ data: rawData
8260
+ };
8261
+ const shifted = {
8262
+ ...event,
8263
+ index: event.index + this.extraBlocks
8264
+ };
8265
+ return {
8266
+ event: shifted,
8267
+ data: JSON.stringify(shifted)
8268
+ };
8269
+ }
8270
+ onBlockStart(event, rawData) {
8271
+ if (event.content_block.type === "text") {
8272
+ this.text = {
8273
+ upstreamIndex: event.index,
8274
+ opened: false,
8275
+ buffer: "",
8276
+ forwarded: 0,
8277
+ capturing: false,
8278
+ captureStart: 0,
8279
+ abandoned: false
8280
+ };
8281
+ return [];
8282
+ }
8283
+ return [this.reindexed(event, rawData)];
8284
+ }
8285
+ onBlockDelta(event, rawData) {
8286
+ const t = this.text;
8287
+ if (!t || event.index !== t.upstreamIndex || event.delta.type !== "text_delta") return [this.reindexed(event, rawData)];
8288
+ t.buffer += event.delta.text;
8289
+ if (t.capturing) {
8290
+ if (t.buffer.length - t.captureStart > MAX_CAPTURE) return this.abandonCapture(t);
8291
+ return [];
8292
+ }
8293
+ if (!t.abandoned) {
8294
+ const open = LEAK_OPEN_RE.exec(t.buffer);
8295
+ if (open) return this.beginCapture(t, open.index);
8296
+ }
8297
+ return this.flushSafe(t);
8298
+ }
8299
+ onBlockStop(event, rawData) {
8300
+ const t = this.text;
8301
+ if (!t || event.index !== t.upstreamIndex) return [this.reindexed(event, rawData)];
8302
+ return this.flushPending();
8303
+ }
8304
+ onMessageDelta(event, rawData) {
8305
+ const flushed = this.flushPending();
8306
+ const isNaturalEnd = event.delta.stop_reason === "end_turn" || event.delta.stop_reason === null;
8307
+ if (!this.converted || !isNaturalEnd) return [...flushed, {
8308
+ event,
8309
+ data: rawData
8310
+ }];
8311
+ const rewritten = {
8312
+ ...event,
8313
+ delta: {
8314
+ ...event.delta,
8315
+ stop_reason: "tool_use"
8316
+ }
8317
+ };
8318
+ return [...flushed, out(rewritten)];
8319
+ }
8320
+ emitText(t, chunk) {
8321
+ const idx = t.upstreamIndex + this.extraBlocks;
8322
+ const events = [];
8323
+ if (!t.opened) {
8324
+ events.push(out({
8325
+ type: "content_block_start",
8326
+ index: idx,
8327
+ content_block: {
8328
+ type: "text",
8329
+ text: ""
8330
+ }
8331
+ }));
8332
+ t.opened = true;
8333
+ }
8334
+ events.push(out({
8335
+ type: "content_block_delta",
8336
+ index: idx,
8337
+ delta: {
8338
+ type: "text_delta",
8339
+ text: chunk
8340
+ }
8341
+ }));
8342
+ return events;
8343
+ }
8344
+ flushSafe(t) {
8345
+ const safeEnd = t.buffer.length - TAIL_GUARD;
8346
+ if (safeEnd <= t.forwarded) return [];
8347
+ const chunk = t.buffer.slice(t.forwarded, safeEnd);
8348
+ t.forwarded = safeEnd;
8349
+ return this.emitText(t, chunk);
8350
+ }
8351
+ beginCapture(t, start) {
8352
+ t.captureStart = start;
8353
+ t.capturing = true;
8354
+ if (start > t.forwarded) {
8355
+ const preamble = t.buffer.slice(t.forwarded, start);
8356
+ t.forwarded = start;
8357
+ return this.emitText(t, preamble);
8358
+ }
8359
+ return [];
8360
+ }
8361
+ abandonCapture(t) {
8362
+ t.capturing = false;
8363
+ t.abandoned = true;
8364
+ const events = this.emitText(t, t.buffer.slice(t.forwarded));
8365
+ t.forwarded = t.buffer.length;
8366
+ return events;
8367
+ }
8368
+ emitTextBlock(index, text) {
8369
+ return [
8370
+ out({
8371
+ type: "content_block_start",
8372
+ index,
8373
+ content_block: {
8374
+ type: "text",
8375
+ text: ""
8376
+ }
8377
+ }),
8378
+ out({
8379
+ type: "content_block_delta",
8380
+ index,
8381
+ delta: {
8382
+ type: "text_delta",
8383
+ text
8384
+ }
8385
+ }),
8386
+ out({
8387
+ type: "content_block_stop",
8388
+ index
8389
+ })
8390
+ ];
8391
+ }
8392
+ emitToolBlock(index, call) {
8393
+ return [
8394
+ out({
8395
+ type: "content_block_start",
8396
+ index,
8397
+ content_block: {
8398
+ type: "tool_use",
8399
+ id: nextToolUseId(),
8400
+ name: call.name,
8401
+ input: {}
8402
+ }
8403
+ }),
8404
+ out({
8405
+ type: "content_block_delta",
8406
+ index,
8407
+ delta: {
8408
+ type: "input_json_delta",
8409
+ partial_json: JSON.stringify(call.input)
8410
+ }
8411
+ }),
8412
+ out({
8413
+ type: "content_block_stop",
8414
+ index
8415
+ })
8416
+ ];
8417
+ }
8418
+ flushPending() {
8419
+ const t = this.text;
8420
+ if (!t) return [];
8421
+ this.text = null;
8422
+ if (t.capturing) return this.finishCapture(t);
8423
+ const events = [];
8424
+ if (t.buffer.length > t.forwarded) events.push(...this.emitText(t, t.buffer.slice(t.forwarded)));
8425
+ const idx = t.upstreamIndex + this.extraBlocks;
8426
+ if (!t.opened) {
8427
+ events.push(out({
8428
+ type: "content_block_start",
8429
+ index: idx,
8430
+ content_block: {
8431
+ type: "text",
8432
+ text: ""
8433
+ }
8434
+ }));
8435
+ t.opened = true;
8436
+ }
8437
+ events.push(out({
8438
+ type: "content_block_stop",
8439
+ index: idx
8440
+ }));
8441
+ return events;
8442
+ }
8443
+ finishCapture(t) {
8444
+ const segments = recoverSegments(t.buffer.slice(t.captureStart), this.knownTools);
8445
+ const hasTool = segments.some((s) => s.kind === "tool");
8446
+ const base = t.upstreamIndex + this.extraBlocks;
8447
+ const events = [];
8448
+ if (!hasTool) {
8449
+ const text = segments.map((s) => s.kind === "text" ? s.text : "").join("");
8450
+ if (text !== "") events.push(...this.emitText(t, text), out({
8451
+ type: "content_block_stop",
8452
+ index: base
8453
+ }));
8454
+ else if (t.opened) events.push(out({
8455
+ type: "content_block_stop",
8456
+ index: base
8457
+ }));
8458
+ else this.extraBlocks -= 1;
8459
+ return events;
8460
+ }
8461
+ this.converted = true;
8462
+ let cursor = base;
8463
+ if (t.opened) {
8464
+ events.push(out({
8465
+ type: "content_block_stop",
8466
+ index: base
8467
+ }));
8468
+ cursor = base + 1;
8469
+ }
8470
+ for (const [i, segment] of segments.entries()) {
8471
+ const idx = cursor + i;
8472
+ events.push(...segment.kind === "text" ? this.emitTextBlock(idx, segment.text) : this.emitToolBlock(idx, segment.call));
8473
+ }
8474
+ const clientBlocks = (t.opened ? 1 : 0) + segments.length;
8475
+ this.extraBlocks += clientBlocks - 1;
8476
+ return events;
8477
+ }
8478
+ };
8479
+
8014
8480
  //#endregion
8015
8481
  //#region src/routes/messages/direct-anthropic-handler.ts
8016
8482
  /**
@@ -8069,7 +8535,7 @@ async function handleDirectAnthropicCompletion(c, anthropicPayload, ctx, initiat
8069
8535
  });
8070
8536
  });
8071
8537
  }
8072
- return handleDirectAnthropicNonStreamingResponse(c, response, ctx, truncateResult, effectivePayload);
8538
+ return handleDirectAnthropicNonStreamingResponse(c, recoverLeakedToolCallsInResponse(response, toolNameSet(effectivePayload.tools)), ctx, truncateResult, effectivePayload);
8073
8539
  } catch (error) {
8074
8540
  if (error instanceof HTTPError && error.status === 413) logPayloadSizeInfoAnthropic(effectivePayload, selectedModel);
8075
8541
  recordErrorResponse(ctx, anthropicPayload.model, error, "messages", anthropicPayload.stream ?? false);
@@ -8187,6 +8653,19 @@ async function handleDirectAnthropicStreamingResponse(opts) {
8187
8653
  const acc = createAnthropicStreamAccumulator();
8188
8654
  const checkRepetition = createStreamRepetitionChecker(`anthropic:${anthropicPayload.model}`);
8189
8655
  const serverToolFilter = createServerToolBlockFilter();
8656
+ const recovery = new LeakedToolCallStreamRecovery(toolNameSet(anthropicPayload.tools));
8657
+ const forward = async (recovered) => {
8658
+ const outEvent = recovered.event;
8659
+ processAnthropicEvent(outEvent, acc);
8660
+ if (outEvent.type === "content_block_start") logServerToolBlock(outEvent.content_block);
8661
+ const forwardData = serverToolFilter.rewriteEvent(outEvent, recovered.data);
8662
+ if (forwardData === null) return;
8663
+ const echoedData = echoForwardData(forwardData, outEvent.type, ctx);
8664
+ await stream.writeSSE({
8665
+ event: outEvent.type,
8666
+ data: echoedData
8667
+ });
8668
+ };
8190
8669
  try {
8191
8670
  for await (const rawEvent of response) {
8192
8671
  consola.debug("Direct Anthropic raw stream event:", JSON.stringify(rawEvent));
@@ -8199,17 +8678,10 @@ async function handleDirectAnthropicStreamingResponse(opts) {
8199
8678
  consola.error("Failed to parse Anthropic stream event:", parseError, rawEvent.data);
8200
8679
  continue;
8201
8680
  }
8202
- processAnthropicEvent(event, acc);
8203
- if (event.type === "content_block_start") logServerToolBlock(event.content_block);
8204
8681
  if (event.type === "content_block_delta" && event.delta.type === "text_delta") checkRepetition(event.delta.text);
8205
- const forwardData = serverToolFilter.rewriteEvent(event, rawEvent.data);
8206
- if (forwardData === null) continue;
8207
- const echoedData = echoForwardData(forwardData, event.type, ctx);
8208
- await stream.writeSSE({
8209
- event: rawEvent.event || event.type,
8210
- data: echoedData
8211
- });
8682
+ for (const recovered of recovery.process(event, rawEvent.data)) await forward(recovered);
8212
8683
  }
8684
+ for (const recovered of recovery.flush()) await forward(recovered);
8213
8685
  recordAnthropicStreamingResponse(acc, anthropicPayload.model, ctx);
8214
8686
  completeTracking(ctx.trackingId, acc.inputTokens, acc.outputTokens, ctx.queueWaitMs, void 0, {
8215
8687
  model: acc.model || anthropicPayload.model,
@@ -8536,12 +9008,13 @@ async function handleCompletion(c) {
8536
9008
  system: extractSystemPrompt(p.system)
8537
9009
  })
8538
9010
  });
8539
- logToolInfo(anthropicPayload);
8540
- const subagentMarker = parseSubagentMarkerFromFirstUser(anthropicPayload);
9011
+ const sanitizedPayload = dePoisonAssistantMessages(anthropicPayload);
9012
+ logToolInfo(sanitizedPayload);
9013
+ const subagentMarker = parseSubagentMarkerFromFirstUser(sanitizedPayload);
8541
9014
  const initiatorOverride = subagentMarker ? "agent" : void 0;
8542
9015
  if (subagentMarker) consola.debug("Detected Subagent marker:", JSON.stringify(subagentMarker));
8543
- if (supportsDirectAnthropicApi(anthropicPayload.model)) return handleDirectAnthropicCompletion(c, anthropicPayload, ctx, initiatorOverride);
8544
- return handleTranslatedCompletion(c, anthropicPayload, ctx, initiatorOverride);
9016
+ if (supportsDirectAnthropicApi(sanitizedPayload.model)) return handleDirectAnthropicCompletion(c, sanitizedPayload, ctx, initiatorOverride);
9017
+ return handleTranslatedCompletion(c, sanitizedPayload, ctx, initiatorOverride);
8545
9018
  }
8546
9019
  /**
8547
9020
  * Log tool-related information for debugging
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dianshuv/copilot-api",
3
- "version": "0.10.0",
3
+ "version": "0.10.1",
4
4
  "description": "Turn GitHub Copilot into OpenAI/Anthropic API compatible server. Usable with Claude Code!",
5
5
  "author": "dianshuv",
6
6
  "type": "module",