@dianshuv/copilot-api 0.11.0 → 0.11.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.
- package/dist/main.mjs +77 -33
- 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.11.
|
|
1351
|
+
var version = "0.11.1";
|
|
1352
1352
|
|
|
1353
1353
|
//#endregion
|
|
1354
1354
|
//#region src/lib/adaptive-rate-limiter.ts
|
|
@@ -8052,10 +8052,11 @@ function translateErrorToAnthropicErrorEvent(error) {
|
|
|
8052
8052
|
//#endregion
|
|
8053
8053
|
//#region src/routes/messages/tool-call-recovery.ts
|
|
8054
8054
|
const ENVELOPE = String.raw`(?:<(?:antml:)?function_calls>|call)`;
|
|
8055
|
-
const INVOKE_BODY = String.raw`<(?:antml:)?invoke\s+name="[^"]+">[\s\S]*?</(?:antml:)?invoke>`;
|
|
8056
|
-
const LEAKED_REGION_RE = new RegExp(String.raw`(?:^|\n)[ \t]
|
|
8057
|
-
const
|
|
8058
|
-
const
|
|
8055
|
+
const INVOKE_BODY = String.raw`<(?:antml:)?invoke\s+name="[^"]+">(?:(?!<(?:antml:)?invoke\b)[\s\S])*?</(?:antml:)?invoke>`;
|
|
8056
|
+
const LEAKED_REGION_RE = new RegExp(String.raw`(?:^|\n)[ \t]*(?:` + ENVELOPE + String.raw`[ \t\n]*(?:` + INVOKE_BODY + String.raw`\s*)+(?:</(?:antml:)?function_calls>)?|` + INVOKE_BODY + String.raw`)`, "g");
|
|
8057
|
+
const STARTS_WITH_ENVELOPE_RE = new RegExp(String.raw`^[ \t\n]*` + ENVELOPE);
|
|
8058
|
+
const INVOKE_OPENER_RE = new RegExp(String.raw`(?:^|\n)[ \t]*(?:` + ENVELOPE + String.raw`[ \t\n]*)?<(?:antml:)?invoke\b[^\n>]*>?`, "g");
|
|
8059
|
+
const LEAK_OPEN_RE = new RegExp(String.raw`(?:^|\n)[ \t]*(?:` + ENVELOPE + String.raw`[ \t\n]*)?<(?:antml:)?invoke\s+name="`, "g");
|
|
8059
8060
|
const INVOKE_RE = /<(?:antml:)?invoke\s+name="([^"]+)">([\s\S]*?)<\/(?:antml:)?invoke>/g;
|
|
8060
8061
|
const PARAMETER_RE = /<(?:antml:)?parameter\s+name="([^"]+)">([\s\S]*?)<\/(?:antml:)?parameter>/g;
|
|
8061
8062
|
function coerceParamValue(raw) {
|
|
@@ -8081,19 +8082,43 @@ function parseRegionInvokes(region, knownTools) {
|
|
|
8081
8082
|
}
|
|
8082
8083
|
return calls;
|
|
8083
8084
|
}
|
|
8085
|
+
const FENCE_DELIM_RE = /(?:^|\n)[ \t]*```/g;
|
|
8086
|
+
function insideFence(text, pos) {
|
|
8087
|
+
let openAt = -1;
|
|
8088
|
+
for (const m of text.matchAll(FENCE_DELIM_RE)) if (openAt === -1) {
|
|
8089
|
+
if (m.index >= pos) break;
|
|
8090
|
+
openAt = m.index;
|
|
8091
|
+
} else if (m.index > pos) return true;
|
|
8092
|
+
else openAt = -1;
|
|
8093
|
+
return false;
|
|
8094
|
+
}
|
|
8095
|
+
function regionIsLeak(region, offset, fullText, knownTools) {
|
|
8096
|
+
if (insideFence(fullText, offset)) return false;
|
|
8097
|
+
if (STARTS_WITH_ENVELOPE_RE.test(region)) return true;
|
|
8098
|
+
if (!knownTools) return false;
|
|
8099
|
+
return parseRegionInvokes(region, knownTools).length > 0;
|
|
8100
|
+
}
|
|
8084
8101
|
/**
|
|
8085
8102
|
* Split assistant text into ordered segments — dropping leaked envelope markup
|
|
8086
8103
|
* (and undeclared-tool invokes) while preserving the natural-language on either
|
|
8087
8104
|
* side and the pre/tool/post ordering. Returns a single text segment when there
|
|
8088
8105
|
* is no leak. Shared by both response recovery paths so they cannot diverge.
|
|
8106
|
+
*
|
|
8107
|
+
* `from` restricts the EMITTED window to `text.slice(from)` while still
|
|
8108
|
+
* classifying against the FULL `text` — so a streaming capture that started just
|
|
8109
|
+
* after an opening ``` fence still sees that fence (and the now-arrived closing
|
|
8110
|
+
* one) when deciding whether the region is documentation.
|
|
8089
8111
|
*/
|
|
8090
|
-
function recoverSegments(text, knownTools) {
|
|
8112
|
+
function recoverSegments(text, knownTools, from = 0) {
|
|
8091
8113
|
const segments = [];
|
|
8092
|
-
let cursor =
|
|
8114
|
+
let cursor = from;
|
|
8093
8115
|
let sawRegion = false;
|
|
8094
8116
|
for (const region of text.matchAll(LEAKED_REGION_RE)) {
|
|
8117
|
+
const idx = region.index;
|
|
8118
|
+
if (idx < from) continue;
|
|
8119
|
+
if (!regionIsLeak(region[0], idx, text, knownTools)) continue;
|
|
8095
8120
|
sawRegion = true;
|
|
8096
|
-
const pre = text.slice(cursor,
|
|
8121
|
+
const pre = text.slice(cursor, idx);
|
|
8097
8122
|
if (pre.trim() !== "") segments.push({
|
|
8098
8123
|
kind: "text",
|
|
8099
8124
|
text: pre
|
|
@@ -8102,11 +8127,11 @@ function recoverSegments(text, knownTools) {
|
|
|
8102
8127
|
kind: "tool",
|
|
8103
8128
|
call
|
|
8104
8129
|
});
|
|
8105
|
-
cursor =
|
|
8130
|
+
cursor = idx + region[0].length;
|
|
8106
8131
|
}
|
|
8107
8132
|
if (!sawRegion) return [{
|
|
8108
8133
|
kind: "text",
|
|
8109
|
-
text
|
|
8134
|
+
text: text.slice(from)
|
|
8110
8135
|
}];
|
|
8111
8136
|
const post = text.slice(cursor);
|
|
8112
8137
|
if (post.trim() !== "") segments.push({
|
|
@@ -8115,19 +8140,20 @@ function recoverSegments(text, knownTools) {
|
|
|
8115
8140
|
});
|
|
8116
8141
|
return segments;
|
|
8117
8142
|
}
|
|
8118
|
-
/** True when `text` contains at least one
|
|
8119
|
-
function containsLeakedToolCall(text) {
|
|
8143
|
+
/** True when `text` contains at least one real leak (see `regionIsLeak`). */
|
|
8144
|
+
function containsLeakedToolCall(text, knownTools) {
|
|
8120
8145
|
if (!text.includes("invoke")) return false;
|
|
8121
|
-
for (const
|
|
8146
|
+
for (const region of text.matchAll(LEAKED_REGION_RE)) if (regionIsLeak(region[0], region.index, text, knownTools)) return true;
|
|
8122
8147
|
return false;
|
|
8123
8148
|
}
|
|
8124
8149
|
/**
|
|
8125
|
-
* Remove
|
|
8126
|
-
* surrounding natural-language on both sides.
|
|
8150
|
+
* Remove real leaked tool-call regions from assistant text, preserving the
|
|
8151
|
+
* surrounding natural-language on both sides. Regions that are not leaks (an
|
|
8152
|
+
* envelope-less <invoke> that names no declared tool) round-trip untouched.
|
|
8127
8153
|
*/
|
|
8128
|
-
function stripLeakedToolCalls(text) {
|
|
8154
|
+
function stripLeakedToolCalls(text, knownTools) {
|
|
8129
8155
|
if (!text.includes("invoke")) return text;
|
|
8130
|
-
const stripped = text.replaceAll(LEAKED_REGION_RE, "");
|
|
8156
|
+
const stripped = text.replaceAll(LEAKED_REGION_RE, (match, offset) => regionIsLeak(match, offset, text, knownTools) ? "" : match);
|
|
8131
8157
|
if (stripped === text) return text;
|
|
8132
8158
|
return stripped.replace(/[ \t\n]+$/, "");
|
|
8133
8159
|
}
|
|
@@ -8141,14 +8167,27 @@ function toolNameSet(tools) {
|
|
|
8141
8167
|
return new Set((tools ?? []).filter((tool) => !isServerToolType(tool.type)).map((tool) => tool.name));
|
|
8142
8168
|
}
|
|
8143
8169
|
const EMPTIED_ASSISTANT_PLACEHOLDER = "[malformed tool call removed by proxy]";
|
|
8144
|
-
|
|
8170
|
+
const INVOKE_NAME_RE = /<(?:antml:)?invoke\s+name="([^"]+)"/;
|
|
8171
|
+
function findIncompleteLeakTail(text, knownTools) {
|
|
8172
|
+
let last;
|
|
8173
|
+
for (const m of text.matchAll(INVOKE_OPENER_RE)) last = m;
|
|
8174
|
+
if (!last || last.index === void 0) return -1;
|
|
8175
|
+
if (text.includes("</invoke>", last.index + last[0].length)) return -1;
|
|
8176
|
+
if (insideFence(text, last.index)) return -1;
|
|
8177
|
+
if (STARTS_WITH_ENVELOPE_RE.test(last[0])) return last.index;
|
|
8178
|
+
if (!knownTools) return -1;
|
|
8179
|
+
const name = INVOKE_NAME_RE.exec(last[0]);
|
|
8180
|
+
return name && knownTools.has(name[1]) ? last.index : -1;
|
|
8181
|
+
}
|
|
8182
|
+
function historyHasLeak(text, knownTools) {
|
|
8145
8183
|
if (!text.includes("invoke")) return false;
|
|
8146
|
-
return containsLeakedToolCall(text) ||
|
|
8184
|
+
return containsLeakedToolCall(text, knownTools) || findIncompleteLeakTail(text, knownTools) !== -1;
|
|
8147
8185
|
}
|
|
8148
|
-
function scrubHistoryText(text) {
|
|
8149
|
-
const stripped = stripLeakedToolCalls(text);
|
|
8150
|
-
const
|
|
8151
|
-
|
|
8186
|
+
function scrubHistoryText(text, knownTools) {
|
|
8187
|
+
const stripped = stripLeakedToolCalls(text, knownTools);
|
|
8188
|
+
const tailStart = findIncompleteLeakTail(stripped, knownTools);
|
|
8189
|
+
if (tailStart === -1) return stripped;
|
|
8190
|
+
return stripped.slice(0, tailStart).replace(/[ \t\n]+$/, "");
|
|
8152
8191
|
}
|
|
8153
8192
|
/**
|
|
8154
8193
|
* Request-side de-poison. Strips leaked tool-call markup (complete and truncated)
|
|
@@ -8159,6 +8198,7 @@ function scrubHistoryText(text) {
|
|
|
8159
8198
|
*/
|
|
8160
8199
|
function dePoisonAssistantMessages(payload) {
|
|
8161
8200
|
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;
|
|
8201
|
+
const knownTools = toolNameSet(payload.tools);
|
|
8162
8202
|
let changed = false;
|
|
8163
8203
|
const messages = [];
|
|
8164
8204
|
for (const msg of payload.messages) {
|
|
@@ -8167,26 +8207,26 @@ function dePoisonAssistantMessages(payload) {
|
|
|
8167
8207
|
continue;
|
|
8168
8208
|
}
|
|
8169
8209
|
if (typeof msg.content === "string") {
|
|
8170
|
-
if (!historyHasLeak(msg.content)) {
|
|
8210
|
+
if (!historyHasLeak(msg.content, knownTools)) {
|
|
8171
8211
|
messages.push(msg);
|
|
8172
8212
|
continue;
|
|
8173
8213
|
}
|
|
8174
8214
|
changed = true;
|
|
8175
|
-
const cleaned = scrubHistoryText(msg.content);
|
|
8215
|
+
const cleaned = scrubHistoryText(msg.content, knownTools);
|
|
8176
8216
|
messages.push({
|
|
8177
8217
|
...msg,
|
|
8178
8218
|
content: cleaned.trim() === "" ? EMPTIED_ASSISTANT_PLACEHOLDER : cleaned
|
|
8179
8219
|
});
|
|
8180
8220
|
continue;
|
|
8181
8221
|
}
|
|
8182
|
-
if (!msg.content.some((b) => b.type === "text" && historyHasLeak(b.text))) {
|
|
8222
|
+
if (!msg.content.some((b) => b.type === "text" && historyHasLeak(b.text, knownTools))) {
|
|
8183
8223
|
messages.push(msg);
|
|
8184
8224
|
continue;
|
|
8185
8225
|
}
|
|
8186
8226
|
changed = true;
|
|
8187
8227
|
const content = msg.content.flatMap((b) => {
|
|
8188
|
-
if (b.type !== "text" || !historyHasLeak(b.text)) return [b];
|
|
8189
|
-
const cleaned = scrubHistoryText(b.text);
|
|
8228
|
+
if (b.type !== "text" || !historyHasLeak(b.text, knownTools)) return [b];
|
|
8229
|
+
const cleaned = scrubHistoryText(b.text, knownTools);
|
|
8190
8230
|
return cleaned.trim() === "" ? [] : [{
|
|
8191
8231
|
...b,
|
|
8192
8232
|
text: cleaned
|
|
@@ -8222,7 +8262,7 @@ function recoverLeakedToolCallsInResponse(response, knownTools) {
|
|
|
8222
8262
|
let recoveredCall = false;
|
|
8223
8263
|
const content = [];
|
|
8224
8264
|
for (const block of response.content) {
|
|
8225
|
-
if (block.type !== "text" || !containsLeakedToolCall(block.text)) {
|
|
8265
|
+
if (block.type !== "text" || !containsLeakedToolCall(block.text, knownTools)) {
|
|
8226
8266
|
content.push(block);
|
|
8227
8267
|
continue;
|
|
8228
8268
|
}
|
|
@@ -8328,12 +8368,16 @@ var LeakedToolCallStreamRecovery = class {
|
|
|
8328
8368
|
if (t.buffer.length - t.captureStart > MAX_CAPTURE) return this.abandonCapture(t);
|
|
8329
8369
|
return [];
|
|
8330
8370
|
}
|
|
8331
|
-
if (!t.abandoned) {
|
|
8332
|
-
const open
|
|
8333
|
-
if (open) return this.beginCapture(t, open.index);
|
|
8371
|
+
if (!t.abandoned && t.buffer.includes("invoke")) {
|
|
8372
|
+
for (const open of t.buffer.matchAll(LEAK_OPEN_RE)) if (this.shouldArmCapture(open[0], t.buffer, open.index)) return this.beginCapture(t, open.index);
|
|
8334
8373
|
}
|
|
8335
8374
|
return this.flushSafe(t);
|
|
8336
8375
|
}
|
|
8376
|
+
shouldArmCapture(opener, buffer, openerIndex) {
|
|
8377
|
+
if (insideFence(buffer, openerIndex)) return false;
|
|
8378
|
+
if (STARTS_WITH_ENVELOPE_RE.test(opener)) return true;
|
|
8379
|
+
return this.knownTools !== void 0 && this.knownTools.size > 0;
|
|
8380
|
+
}
|
|
8337
8381
|
onBlockStop(event, rawData) {
|
|
8338
8382
|
const t = this.text;
|
|
8339
8383
|
if (!t || event.index !== t.upstreamIndex) return [this.reindexed(event, rawData)];
|
|
@@ -8479,7 +8523,7 @@ var LeakedToolCallStreamRecovery = class {
|
|
|
8479
8523
|
return events;
|
|
8480
8524
|
}
|
|
8481
8525
|
finishCapture(t) {
|
|
8482
|
-
const segments = recoverSegments(t.buffer
|
|
8526
|
+
const segments = recoverSegments(t.buffer, this.knownTools, t.captureStart);
|
|
8483
8527
|
const hasTool = segments.some((s) => s.kind === "tool");
|
|
8484
8528
|
const base = t.upstreamIndex + this.extraBlocks;
|
|
8485
8529
|
const events = [];
|