copilot-api-plus 1.2.47 → 1.2.49

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.en.md CHANGED
@@ -51,6 +51,7 @@ English | [简体中文](README.md)
51
51
  | ✂️ **Context Passthrough** | Full context passthrough to upstream API; clients (e.g. Claude Code) manage compression |
52
52
  | 🔍 **Smart Model Matching** | Handles model name format differences (date suffixes, dash/dot versions, etc.) |
53
53
  | 🧠 **Thinking Chain** | Automatically enables deep thinking for supported models, improving code quality |
54
+ | 🧹 **Reminder Stripping** | Strips client-injected reminder blocks before forwarding, avoiding upstream false-positive rejections |
54
55
 
55
56
  ---
56
57
 
package/README.md CHANGED
@@ -52,6 +52,7 @@
52
52
  | ✂️ **上下文透传** | 全量透传上下文至上游 API,由客户端(如 Claude Code)自行管理压缩 |
53
53
  | 🔍 **智能模型匹配** | 自动处理模型名格式差异(日期后缀、dash/dot 版本号等) |
54
54
  | 🧠 **Thinking 思维链** | 自动为支持的模型启用深度思考,提升代码质量 |
55
+ | 🧹 **Reminder 剥离** | 自动剥离客户端注入的提醒块,避免上游误判拒绝服务 |
55
56
 
56
57
  ---
57
58
 
package/dist/main.js CHANGED
@@ -1728,6 +1728,168 @@ async function checkRateLimit(state) {
1728
1728
  }
1729
1729
  }
1730
1730
  //#endregion
1731
+ //#region src/routes/messages/strip-reminders.ts
1732
+ /** Matches `<system-reminder>…</system-reminder>` non-greedy, across lines. */
1733
+ const REMINDER_RE = /<system-reminder>[\s\S]*?<\/system-reminder>/g;
1734
+ /** Cheap sentinel that lets callers skip the regex scan. */
1735
+ const REMINDER_OPEN_TAG = "<system-reminder>";
1736
+ /**
1737
+ * Strip every `<system-reminder>` block from a plain string.
1738
+ *
1739
+ * Collapses runs of 3+ newlines left behind by the removal into a
1740
+ * single blank line, then trims leading/trailing whitespace. Returns
1741
+ * the same reference if no reminder was present (zero allocation).
1742
+ */
1743
+ function stripText(s) {
1744
+ if (!s.includes(REMINDER_OPEN_TAG)) return s;
1745
+ return s.replaceAll(REMINDER_RE, "").replaceAll(/\n{3,}/g, "\n\n").trim();
1746
+ }
1747
+ /**
1748
+ * Strip reminders from a block array. Non-text blocks pass through.
1749
+ * Text blocks that become empty after stripping are filtered out.
1750
+ * Returns the same reference if nothing changed.
1751
+ */
1752
+ function stripBlocks(content) {
1753
+ if (!content.some((b) => b.type === "text" && b.text.includes(REMINDER_OPEN_TAG) || b.type === "tool_result" && b.content.includes(REMINDER_OPEN_TAG))) return content;
1754
+ const out = [];
1755
+ for (const b of content) {
1756
+ if (b.type === "tool_result") {
1757
+ const orig = b.content;
1758
+ const stripped = stripText(orig);
1759
+ if (stripped === orig) out.push(b);
1760
+ else if (stripped.length === 0) out.push({
1761
+ ...b,
1762
+ content: " "
1763
+ });
1764
+ else out.push({
1765
+ ...b,
1766
+ content: stripped
1767
+ });
1768
+ continue;
1769
+ }
1770
+ if (b.type !== "text") {
1771
+ out.push(b);
1772
+ continue;
1773
+ }
1774
+ const t = stripText(b.text);
1775
+ if (t.length === 0) continue;
1776
+ out.push(t === b.text ? b : {
1777
+ ...b,
1778
+ text: t
1779
+ });
1780
+ }
1781
+ return out;
1782
+ }
1783
+ /**
1784
+ * Strip reminders from a single message. Returns the same reference
1785
+ * if nothing changed.
1786
+ */
1787
+ function stripMessage(message) {
1788
+ if (typeof message.content === "string") {
1789
+ const next = stripText(message.content);
1790
+ if (next === message.content) return message;
1791
+ return {
1792
+ ...message,
1793
+ content: next
1794
+ };
1795
+ }
1796
+ const next = stripBlocks(message.content);
1797
+ if (next === message.content) return message;
1798
+ return {
1799
+ ...message,
1800
+ content: next
1801
+ };
1802
+ }
1803
+ /**
1804
+ * Strip reminders from the `system` field (string OR text-block array).
1805
+ * Returns the same reference if nothing changed.
1806
+ */
1807
+ function stripSystem(system) {
1808
+ if (system === void 0) return system;
1809
+ if (typeof system === "string") {
1810
+ const next = stripText(system);
1811
+ return next === system ? system : next;
1812
+ }
1813
+ if (!system.some((b) => b.text.includes(REMINDER_OPEN_TAG))) return system;
1814
+ const out = [];
1815
+ for (const b of system) {
1816
+ const t = stripText(b.text);
1817
+ if (t.length === 0) continue;
1818
+ out.push(t === b.text ? b : {
1819
+ ...b,
1820
+ text: t
1821
+ });
1822
+ }
1823
+ return out;
1824
+ }
1825
+ /**
1826
+ * Return a shallow-cloned payload with `<system-reminder>` blocks
1827
+ * removed from every message's text content AND the system field.
1828
+ * The input payload is NOT mutated; if no reminders are present
1829
+ * anywhere, the original payload reference is returned unchanged.
1830
+ */
1831
+ function stripSystemReminders(payload) {
1832
+ let changed = false;
1833
+ const newSystem = stripSystem(payload.system);
1834
+ if (newSystem !== payload.system) changed = true;
1835
+ const newMessages = payload.messages.map((m) => {
1836
+ const next = stripMessage(m);
1837
+ if (next !== m) changed = true;
1838
+ return next;
1839
+ });
1840
+ if (!changed) return payload;
1841
+ return {
1842
+ ...payload,
1843
+ system: newSystem,
1844
+ messages: newMessages
1845
+ };
1846
+ }
1847
+ /**
1848
+ * Strip reminders from an OpenAI-style chat completions payload.
1849
+ * Walks every message's content (string OR ContentPart[]). Filters
1850
+ * out text parts that become empty. Returns the same reference if
1851
+ * nothing changed.
1852
+ */
1853
+ function stripOpenAIReminders(payload) {
1854
+ let changed = false;
1855
+ const newMessages = payload.messages.map((m) => {
1856
+ if (m.content === null) return m;
1857
+ if (typeof m.content === "string") {
1858
+ const next = stripText(m.content);
1859
+ if (next === m.content) return m;
1860
+ changed = true;
1861
+ return {
1862
+ ...m,
1863
+ content: next
1864
+ };
1865
+ }
1866
+ if (!m.content.some((p) => p.type === "text" && typeof p.text === "string" && p.text.includes(REMINDER_OPEN_TAG))) return m;
1867
+ const out = [];
1868
+ for (const p of m.content) {
1869
+ if (p.type !== "text" || typeof p.text !== "string") {
1870
+ out.push(p);
1871
+ continue;
1872
+ }
1873
+ const t = stripText(p.text);
1874
+ if (t.length === 0) continue;
1875
+ out.push(t === p.text ? p : {
1876
+ ...p,
1877
+ text: t
1878
+ });
1879
+ }
1880
+ changed = true;
1881
+ return {
1882
+ ...m,
1883
+ content: out
1884
+ };
1885
+ });
1886
+ if (!changed) return payload;
1887
+ return {
1888
+ ...payload,
1889
+ messages: newMessages
1890
+ };
1891
+ }
1892
+ //#endregion
1731
1893
  //#region src/services/copilot/create-chat-completions.ts
1732
1894
  /**
1733
1895
  * Timeout for the initial HTTP connection + headers (not the body/stream).
@@ -2249,7 +2411,7 @@ async function handleCompletion$1(c) {
2249
2411
  await checkRateLimit(state);
2250
2412
  const rawPayload = await c.req.json();
2251
2413
  consola.debug("Request payload:", JSON.stringify(rawPayload).slice(-400));
2252
- const payload = applyMaxTokens(rawPayload);
2414
+ const payload = applyMaxTokens(stripOpenAIReminders(rawPayload));
2253
2415
  if (state.manualApprove) await awaitApproval();
2254
2416
  const response = await createChatCompletions(payload);
2255
2417
  if (isNonStreaming$1(response)) {
@@ -2760,63 +2922,6 @@ function getAnthropicToolUseBlocks(toolCalls) {
2760
2922
  }));
2761
2923
  }
2762
2924
  //#endregion
2763
- //#region src/routes/messages/strip-reminders.ts
2764
- const REMINDER_RE = /<system-reminder>[\s\S]*?<\/system-reminder>/g;
2765
- function stripFromText(text) {
2766
- if (!text.includes("<system-reminder>")) return text;
2767
- return text.replaceAll(REMINDER_RE, "").replaceAll(/\n{3,}/g, "\n\n").trim();
2768
- }
2769
- function stripFromBlock(block) {
2770
- if (block.type !== "text") return block;
2771
- const text = block.text;
2772
- const stripped = stripFromText(text);
2773
- if (stripped === text) return block;
2774
- return {
2775
- ...block,
2776
- text: stripped
2777
- };
2778
- }
2779
- function stripFromMessage(message) {
2780
- if (typeof message.content === "string") {
2781
- const stripped = stripFromText(message.content);
2782
- if (stripped === message.content) return message;
2783
- return {
2784
- ...message,
2785
- content: stripped
2786
- };
2787
- }
2788
- let changed = false;
2789
- const newBlocks = message.content.map((block) => {
2790
- const next = stripFromBlock(block);
2791
- if (next !== block) changed = true;
2792
- return next;
2793
- });
2794
- if (!changed) return message;
2795
- return {
2796
- ...message,
2797
- content: newBlocks
2798
- };
2799
- }
2800
- /**
2801
- * Return a shallow-cloned payload with `<system-reminder>` blocks removed
2802
- * from every message's text content. The input payload is NOT mutated.
2803
- * If no reminders are present anywhere, the original payload is returned
2804
- * unchanged (no allocation).
2805
- */
2806
- function stripSystemReminders(payload) {
2807
- let changed = false;
2808
- const newMessages = payload.messages.map((m) => {
2809
- const next = stripFromMessage(m);
2810
- if (next !== m) changed = true;
2811
- return next;
2812
- });
2813
- if (!changed) return payload;
2814
- return {
2815
- ...payload,
2816
- messages: newMessages
2817
- };
2818
- }
2819
- //#endregion
2820
2925
  //#region src/routes/messages/count-tokens-handler.ts
2821
2926
  /**
2822
2927
  * Handles token counting for Anthropic messages.