aicodeswitch 5.2.2 → 5.2.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.
@@ -122,10 +122,14 @@ function extractMessageContent(content) {
122
122
  /**
123
123
  * 检测消息是否为 Claude Code 的 compact 命令请求。
124
124
  *
125
+ * 采用两级检测策略:
126
+ * - 严格匹配:四个关键词全部命中(适配已知格式)
127
+ * - 宽松匹配:"TEXT ONLY" + "<summary>" 组合(覆盖 prompt 格式变化)
128
+ *
125
129
  * Compact 命令触发时,Claude Code 会在 messages 末尾插入一条特殊指令:
126
130
  * - role 为 "user"
127
131
  * - content 为数组,包含一个 text 块
128
- * - text 内容以 "CRITICAL: Respond with TEXT ONLY" 开头
132
+ * - text 内容包含 "CRITICAL: Respond with TEXT ONLY" 等标识
129
133
  * - 包含对话摘要生成指令,要求输出 <analysis> 和 <summary> 结构
130
134
  */
131
135
  function isClaudeCompactRequest(message) {
@@ -139,10 +143,21 @@ function isClaudeCompactRequest(message) {
139
143
  for (const block of content) {
140
144
  if ((block === null || block === void 0 ? void 0 : block.type) === 'text' && typeof block.text === 'string') {
141
145
  const text = block.text;
142
- if (text.includes('CRITICAL: Respond with TEXT ONLY') &&
143
- text.includes('create a detailed summary of the conversation') &&
144
- text.includes('<analysis>') &&
145
- text.includes('<summary>')) {
146
+ const hasTextOnly = text.includes('TEXT ONLY');
147
+ const hasSummary = text.includes('<summary>');
148
+ const hasCritical = text.includes('CRITICAL: Respond with TEXT ONLY');
149
+ const hasDetailedSummary = text.includes('create a detailed summary of the conversation');
150
+ const hasAnalysis = text.includes('<analysis>');
151
+ // 严格匹配:Claude Code 标准格式(四个关键词)
152
+ if (hasCritical && hasDetailedSummary && hasAnalysis && hasSummary) {
153
+ console.log('[COMPACT] Strict match: all 4 markers found');
154
+ return true;
155
+ }
156
+ // 宽松匹配:覆盖 Claude Code prompt 格式变化
157
+ // "TEXT ONLY" 是紧凑指令的核心标识,<summary> 是输出格式标签
158
+ // 两者组合在非 compact 请求中几乎不可能同时出现
159
+ if (hasTextOnly && hasSummary) {
160
+ console.log(`[COMPACT] Loose match: CRITICAL=${hasCritical}, detailed_summary=${hasDetailedSummary}, <analysis>=${hasAnalysis}`);
146
161
  return true;
147
162
  }
148
163
  }
@@ -150,13 +165,22 @@ function isClaudeCompactRequest(message) {
150
165
  return false;
151
166
  }
152
167
  /**
153
- * 检测消息列表中的最后一条消息是否为 Claude Code compact 请求。
168
+ * 检测消息列表中是否包含 Claude Code compact 请求。
169
+ *
170
+ * 从最后一条消息开始往前搜索,最多检查 3 条,
171
+ * 处理 compact 指令后面可能跟了 assistant 占位消息的边缘情况。
154
172
  */
155
173
  function isLastClaudeMessageCompact(messages) {
156
174
  if (!Array.isArray(messages) || messages.length === 0) {
157
175
  return false;
158
176
  }
159
- return isClaudeCompactRequest(messages[messages.length - 1]);
177
+ const checkCount = Math.min(messages.length, 3);
178
+ for (let i = messages.length - 1; i >= messages.length - checkCount; i--) {
179
+ if (isClaudeCompactRequest(messages[i])) {
180
+ return true;
181
+ }
182
+ }
183
+ return false;
160
184
  }
161
185
  /**
162
186
  * 检测请求是否为 Codex 的 compact(压缩)请求。
@@ -1779,6 +1779,28 @@ class ProxyServer {
1779
1779
  return rule;
1780
1780
  }
1781
1781
  }
1782
+ // compact 规则同样拥有最高优先级,确保压缩请求不被其他规则覆盖
1783
+ if (contentType === 'compact') {
1784
+ const compactRules = enabledRules.filter(rule => rule.contentType === 'compact');
1785
+ for (const rule of compactRules) {
1786
+ const isBlacklisted = yield this.dbManager.isServiceBlacklisted(rule.targetServiceId, routeId, rule.contentType);
1787
+ if (isBlacklisted) {
1788
+ continue;
1789
+ }
1790
+ this.dbManager.checkAndResetRuleIfNeeded(rule.id);
1791
+ this.dbManager.checkAndResetRequestCountIfNeeded(rule.id);
1792
+ if (rule.tokenLimit && rule.totalTokensUsed !== undefined && rule.totalTokensUsed >= rule.tokenLimit * 1000) {
1793
+ continue;
1794
+ }
1795
+ if (rule.requestCountLimit && rule.totalRequestsUsed !== undefined && rule.totalRequestsUsed >= rule.requestCountLimit) {
1796
+ continue;
1797
+ }
1798
+ if (this.isFrequencyLimitExceeded(rule)) {
1799
+ continue;
1800
+ }
1801
+ return rule;
1802
+ }
1803
+ }
1782
1804
  // 1. 查找其他内容类型的规则
1783
1805
  const contentTypeRules = enabledRules.filter(rule => rule.contentType === contentType);
1784
1806
  // 过滤黑名单和token限制
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aicodeswitch",
3
- "version": "5.2.2",
3
+ "version": "5.2.3",
4
4
  "description": "A tool to help you manage AI programming tools to access large language models locally. It allows your Claude Code, Codex and other tools to no longer be limited to official models.",
5
5
  "author": "tangshuang",
6
6
  "license": "GPL-3.0",