Commit Graph

130 Commits

Author SHA1 Message Date
Xu Kang
70fa4074e8 Merge pull request #93 from huangzt/feature/vue-logs-ui
feat: 新增 SQLite 日志持久化 + Vue UI 后端过滤分页(完全兼容,不影响主流程)
2026-03-23 09:05:18 +08:00
huangzhenting
a84dfd6d03 feat: 实现 db_enabled/db_path 热重载支持,修复文档格式
- logger-db.ts: 新增 closeDb(),修复 initDb() 支持安全重复调用
- logger.ts: 注册 onConfigReload 回调,db_enabled/db_path 变更后无需重启
- config.yaml.example: 删除「需重启」警告注释,补充热重载说明
- README.md: 修复环境变量表格被 blockquote 截断的格式问题,更新热重载说明
- vue-ui/README.md: 删除「需重启服务」错误说明
- ConfigDrawer.vue: 删除「需重启」提示
2026-03-23 08:45:21 +08:00
huangzhenting
1bc91cac24 feat: 新增 SQLite 持久化支持 + Vue UI 后端过滤与分页优化
- 新增 src/logger-db.ts:SQLite 封装层(WAL 模式,支持写入/分页/状态计数/按需 payload 查询)
- logger.ts:双写 SQLite+JSONL,启动时 db_enabled 模式跳过 JSONL 读取避免 OOM,新增游标分页和后端过滤函数
- config.ts/config-api.ts:新增 db_enabled/db_path 配置字段及 LOG_DB_ENABLED/LOG_DB_PATH 环境变量
- log-viewer.ts/index.ts:新增 /api/requests/more 支持 status/keyword/since 后端过滤
- Vue UI:搜索框 400ms 防抖,状态/时间筛选立即触发后端查询,statusCounts 不受状态筛选影响,SSE 实时推送时增量更新计数
- 新增迁移工具 test/migrate-jsonl-to-sqlite.mjs 和单元测试 test/unit-logger-db.mjs
- 完善 README.md、config.yaml.example、docker-compose.yml、vue-ui/README.md 文档
2026-03-22 21:10:26 +08:00
Xu Kang
9a69e66a7e Merge pull request #90 from huangzt/feature/vue-logs-ui
fix: 修复 thinking 截断时内容泄漏到正文的问题
2026-03-22 18:24:54 +08:00
huangzhenting
f317dc04b0 fix: 修复 thinking 截断时内容泄漏到正文的问题
问题:当模型 thinking 内容超出单次输出上限时,<thinking> 标签未闭合,
导致 thinking 内容被当作正文泄漏给客户端;续写请求中 assistantContext
含未闭合标签,模型不知道思考阶段已结束,继续输出 thinking 而非正文。

修复:
1. splitLeadingThinkingBlocks:未闭合时返回已积累的部分 thinkingContent
   而非空字符串,供调用方正确提取
2. handler.ts / openai-handler.ts:流结束 flush 新增 !complete 分支,
   提取截断的 thinkingContent,不将 thinking 内容 flush 为正文
3. 新增 closeUnclosedThinking:续写前补全缺失的 </thinking> 标签,
   应用于所有 4 处续写 assistantContext 构建,让模型正确从正文续写
4. shouldAutoContinueTruncatedToolResponse:json action 块未闭合时
   跳过 200-char 检查,修复 thinking 剥离后正文过短导致续写不触发的问题

测试:新增 unit-thinking-truncation.mjs(11个单元测试)、
e2e-thinking-truncation.mjs(3个实际 API 请求测试),全部通过
2026-03-22 14:10:58 +08:00
Xu Kang
bb86c1a66e Merge pull request #88 from huangzt/feature/vue-logs-ui
fix: 优化 token 预算精度,新增 TokenDiff 日志对比功能
2026-03-22 10:49:40 +08:00
huangzhenting
c4b81f33d1 fix: improve token budget accuracy and add TokenDiff logging
- converter: replace rough overhead formula (tools*70+350) with actual
  estimateTokens on built few-shot messages + Cursor hidden overhead
  (1300 base + perTool by schema_mode); remove 16000 output reservation
- cursor-client: sendCursorRequestFull now returns {text, usage?} to
  capture real Cursor inputTokens/outputTokens from messageMetadata
- handler: add estimateCursorReqTokens() and [TokenDiff] log to compare
  tiktoken estimate vs actual Cursor usage; fix non-stream retry paths
  to update usage from retry result; skip auto-continue when response < 200 chars
- openai-handler: update 4 call sites for new sendCursorRequestFull return type
- config: raise default maxHistoryTokens from 130000 to 150000
- docs/config: remove incorrect 'tiktoken underestimates 10~20%' claim;
  update overhead description and reference range to 130000~170000
2026-03-22 02:35:04 +08:00
Xu Kang
3e0ceb2227 Merge pull request #87 from huangzt/feature/vue-logs-ui
feat: 新增 max_history_tokens 按 token 数裁剪历史 + 记录 Cursor API 真实 token 用量
2026-03-21 19:09:55 +08:00
huangzhenting
a153dad4de feat: record and display real Cursor API token usage
Capture the actual input/output token counts from Cursor API's finish
event (messageMetadata.usage) and use them in place of tiktoken
estimates where available. Fall back to tiktoken if not present.

- src/types.ts: extend CursorSSEEvent with finishReason/messageMetadata
- src/handler.ts: capture finish event usage in streaming paths, pass
  real counts to updateSummary with tiktoken fallback
- src/logger.ts: add inputTokens/outputTokens fields to RequestSummary
- vue-ui: show ↑/↓ Cursor tokens in RequestList, DetailPanel, PayloadView
- public/logs.js: show ↑/↓ Cursor tokens in scard and prompts summary
2026-03-21 18:42:12 +08:00
huangzhenting
b542d554c6 feat: add max_history_tokens to trim context by token budget
Introduce js-tiktoken (cl100k_base) based token estimation to replace
the naive chars/3 approach. Add max_history_tokens config option that
trims oldest messages when the estimated token budget is exceeded.

- src/tokenizer.ts: new module wrapping js-tiktoken getEncoding
- src/config.ts/config-api.ts: YAML parse, env var, hot-reload, default 130000
- src/converter.ts: token budget trimming after max_history_messages pass
- src/handler.ts: replace estimateInputTokens with tiktoken-based version
- config.yaml.example/docker-compose.yml/README.md: docs and examples
- vue-ui: ConfigDrawer field, HotConfig type, README table row
2026-03-21 18:41:45 +08:00
Xu Kang
b70f787aef Merge pull request #84 from huangzt/feature/vue-logs-ui
fix: HTML token 重复检测 + CodeBlock 语法高亮修复
2026-03-21 01:19:20 +08:00
huangzhenting
a3bd993d15 fix: CodeBlock 仅对指定 lang 做语法高亮,markdown 类型非 MD 预览时显示原始文本
- 移除 highlightAuto,避免纯文本内容被误判染色
- lang=markdown 仅在 mdPreview 开启时渲染,否则显示原始文本
- toolCalls 区块补充 lang=json 以保留 JSON 高亮
2026-03-21 00:47:13 +08:00
huangzhenting
feab833fbd fix: replace line-repeat detection with HTML token repeat detection
- 用跨 delta 拼接的 HTML token 重复检测替换依赖 \n 的行级检测
- 新增 streaming-text warmup HTML guard,阻止纯 HTML token 在 warmup 阶段提前 unlock 推流
- 覆盖 <br>、</s>、&nbsp; 等被拆散发送或无换行的场景
2026-03-21 00:30:01 +08:00
Xu Kang
d519338ba1 Merge pull request #79 from huangzt/feature/vue-logs-ui
feat: Vue UI 配置抽屉、行级重复检测修复、Docker 部署文档完善
2026-03-20 18:11:40 +08:00
huangzhenting
35e2c56855 docs: update vue-ui README with Docker deploy notes, fix config.yaml ro mount
Remove :ro from config.yaml volume mount in docker-compose.yml so the
config drawer can write back changes at runtime. Add deployment section
to vue-ui/README.md covering: build-before-docker requirement, config.yaml
read-write mount, first-time setup steps, and config drawer field reference.
2026-03-20 17:54:51 +08:00
huangzhenting
45f192a381 feat: add Vue config drawer for hot-reloadable settings
Add /api/config GET+POST endpoints to read and write config.yaml fields
that support hot-reload. Frontend: config button in AppHeader opens a
650px side drawer with grouped fields, SegSelect/Toggle components,
YAML key names as labels, and descriptions sourced from config.yaml.example.
thinking.enabled supports a 3-way selector (auto/off/on) where auto
deletes the yaml key so the default kicks in.
2026-03-20 17:50:49 +08:00
huangzhenting
203de92228 fix: detect and abort line-level repetition loops in cursor stream
Adds line-by-line repeat detection alongside the existing delta-level
degenerate loop guard. When the same non-empty line appears 8+ consecutive
times, the stream is cancelled and a retryable error is thrown.
2026-03-20 17:50:38 +08:00
Xu Kang
3ad8be7d33 Merge pull request #78 from majorcheng/main
fix: complete OpenAI logs and default persisted logs to summary
2026-03-20 14:29:27 +08:00
majorcheng
310fd8672d fix: complete OpenAI logs and default persisted logs to summary 2026-03-20 14:06:46 +08:00
小海
d293d272ad release: v2.7.6 - tool passthrough/disabled modes, identity leak fix, tool_choice guidance
New features:
- tools.passthrough: bypass few-shot injection, embed raw tool defs (for Roo Code/Cline)
- tools.disabled: skip all tool injection for max context savings
- Enhanced Cursor identity leak sanitization (new regex patterns)
- Improved tool_choice=any guidance with collaborative tone
- TOOLS_PASSTHROUGH / TOOLS_DISABLED env var support
- Startup & request logs show tool mode (disabled/passthrough/standard)
v2.7.6
2026-03-20 09:28:49 +08:00
小海
db5d3fb1f7 feat: show tools mode (disabled/passthrough) in startup and request logs
- Startup log: Tools: disabled / passthrough / schema=full
- Request log: tools=98(跳过) / tools=98(透传) / tools=98
2026-03-20 09:26:23 +08:00
小海
4a026b6b98 feat: add tools.disabled mode to skip all tool injection for max context savings
- tools.disabled: true completely skips tool definitions + few-shot injection
- Response-side parseToolCalls still works if model outputs action blocks
- Env var: TOOLS_DISABLED=true|false
- Updated config.yaml.example and docker-compose.yml
2026-03-20 09:23:15 +08:00
小海
7c2422ce60 feat: add TOOLS_PASSTHROUGH env var override for Docker support 2026-03-20 09:18:13 +08:00
小海
70d077b27a docs: add passthrough option to config.yaml.example 2026-03-20 09:16:33 +08:00
小海
90be75ff9f feat: add tool passthrough mode, identity leak sanitization & enhanced tool_choice=any
1. Tool passthrough mode (config: tools.passthrough: true)
   - Bypasses multi-namespace few-shot injection
   - Embeds raw tool definitions in <tools> tags with minimal 1-shot example
   - Cleans conflicting client prompts (provider-native tool calling, XML markup)
   - Ideal for Roo Code / Cline clients

2. Enhanced Cursor identity leak sanitization
   - New refusal detection patterns for "currently in Cursor context" leaks
   - 4 new sanitizeResponse regexes targeting full context leak paragraphs
   - Covers "I apologize - it appears I'm in Cursor support assistant context"

3. Enhanced tool_choice=any force message
   - Lists available tool names (up to 15) with format example
   - Uses collaborative guidance tone to avoid triggering refusal
   - Stream and non-stream paths aligned
2026-03-20 09:14:28 +08:00
Xu Kang
127f635592 Merge pull request #73 from huangzt/feature/vue-logs-ui
feat: 添加 Vue3 日志 UI(/vuelogs 路由 + 前端全面优化)
2026-03-20 08:58:37 +08:00
huangzhenting
54cb89c11a feat: Vue UI 全面优化 — 暗色主题、搜索、交互、动效 2026-03-20 03:42:32 +08:00
huangzhenting
c868bf3074 feat: 添加 /vuelogs 路由(服务端) 2026-03-20 03:42:27 +08:00
Xu Kang
17a1633617 Merge pull request #72 from huangzt/feature/vue-logs-ui
feat: 新增 Vue3 日志查看界面 (/vuelogs)
2026-03-19 23:09:02 +08:00
huangzhenting
349c729d38 docs: 新增 vue-ui 开发说明文档
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 23:00:20 +08:00
huangzhenting
8e50c7bf9b Merge remote-tracking branch 'origin/main' into feature/vue-logs-ui 2026-03-19 22:43:18 +08:00
huangzhenting
69983e3996 feat: 添加 Vue3 日志 UI — 新增 /vuelogs 路由及 vue-ui 前端目录
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 22:42:33 +08:00
chinadoiphin
f17353e05b fix: 修复 Cursor 身份泄漏 — 模型声称只有 read_file/read_dir 工具 (#68)
问题:模型回复"在当前环境中我只有读取 Cursor 文档的工具
(read_file / read_dir),无法访问你的本地文件系统",
暴露了 Cursor 文档助手身份。

修复:
1. handleDirectTextStream 的 warmupChars 从 96 → 300,
   与工具模式对齐,确保拒绝检测窗口覆盖完整的中文拒绝句式
2. constants.ts 新增 9 条中文拒绝检测规则,覆盖
   "只有读取 Cursor/文档的工具"、"无法访问本地文件"、
   "无法执行命令"、"需要在 Claude Code CLI 环境" 等新措辞
3. sanitizeResponse 新增 5 条清洗规则作为最后防线
2026-03-19 22:12:14 +08:00
小海
f6ad4292f8 fix: 会话隔离 — 基于内容哈希派生确定性会话 ID (#56)
修复 CC 执行 /clear 或 /new 后旧会话上下文残留的问题。
将 CursorChatRequest.id 从随机 shortId() 改为基于
system prompt + 首条用户消息的 SHA-256 哈希派生,
确保不同逻辑会话获得不同的 Cursor 会话 ID。
v2.7.5
2026-03-19 10:53:11 +08:00
小海
14aa65349c feat: v2.7.5 — 常量集中管理 + 自定义拒绝规则 + 响应清洗开关
🏗️ 常量集中管理
- 新增 constants.ts,提取 REFUSAL_PATTERNS、IDENTITY_PROBE_PATTERNS、
  TOOL_CAPABILITY_PATTERNS、CLAUDE_IDENTITY_RESPONSE、CLAUDE_TOOLS_RESPONSE
- isRefusal() 统一导出,内置 + 自定义规则合并检测

🔧 自定义拒绝检测规则 (config.yaml: refusal_patterns)
- 用户可添加自定义正则匹配规则,追加到内置列表
- 无效正则自动退化为字面量匹配
- 缓存编译 + 热重载支持

🔀 响应内容清洗开关 (config.yaml: sanitize_response)
- 控制 sanitizeResponse() 是否替换 Cursor 身份引用为 Claude
- 默认关闭,关闭时零开销
- 支持环境变量 SANITIZE_RESPONSE 覆盖
2026-03-19 09:44:21 +08:00
小海
e15dd5af33 fix: 压缩上下文后首条消息返回"你有什么问题吗"的问题 (#68)
CC 压缩后,消息主体全是 XML 标签(压缩的上下文摘要),
剥离标签后 actualQuery 为空,回退保留了完整 XML 内容,
但追加的 suffix 是通用的"Respond with structured format",
模型看不到具体任务 → 回答"你有什么问题吗?"

修复:检测到压缩回退场景时,改用上下文感知的引导指令:
"Based on the context above, determine the next step and
proceed..." + "Do NOT ask the user what they want",
让模型直接根据压缩上下文继续工作,而非等待新指令。
2026-03-19 09:26:15 +08:00
小海
1dd24ca84d feat: 多类别 few-shot 示范,提升 MCP/Skills/Plugins 工具调用率 (#67)
问题:模型只模仿 few-shot 中见过的工具(Read/Bash),
导致 MCP 工具、Skills、Plugins 等第三方工具从不被调用。

修复:
- 按命名空间/来源自动分组第三方工具(MCP 双下划线、
  驼峰前缀、蛇形前缀等规则)
- 每个命名空间选一个代表性工具加入 few-shot 示范
- 核心工具(Read/Bash) + 最多 4 个第三方工具代表
- 在单个 assistant 回复中展示多工具调用格式
- 第三方工具的示例参数从 schema 中自动提取

示例:如果用户有 mcp__context7、SuperPowers、claude_mem
等不同来源的工具,few-shot 会各选一个代表进行示范,
让模型知道所有类别的工具都可以调用。
2026-03-19 09:23:38 +08:00
小海
4bd6066dda fix: 检测模型退化循环输出并自动中止流 (#66)
当 Cursor 后端模型陷入退化循环时(不断重复输出 </s>、</br> 等
短文本标记),会导致内容输出一点就停止、后台日志充斥大量
重复标记的现象。

修复方案:在 cursor-client.ts SSE 解析层加入退化重复检测器:
- 跟踪连续相同的 text-delta(仅检测 ≤20 字符的短 token)
- 同一 delta 连续出现 ≥8 次时判定为退化循环
- 立即中止流(reader.cancel),保留已收到的有效内容
- 外层 sendCursorRequest 识别 DEGENERATE_LOOP_ABORTED 后
  不再重试(重试也会重蹈覆辙)

所有流式路径(Anthropic handler、OpenAI handler、续写)
自动受保护,无需逐个修改。
2026-03-19 09:14:29 +08:00
小海
53335aeeab fix: 修复 SVG 图片导致 tesseract.js 崩溃的问题 (#69)
SVG 是基于 XML 的矢量图格式,tesseract.js 无法处理。
尝试对 SVG 进行 OCR 会在 tesseract Worker 内部通过 process.nextTick
抛出未捕获的 TypeError (fetch failed),导致整个进程异常终止。

修复方案:在三层防御点过滤 SVG 图片:
1. converter.ts preprocessImages(): 下载/读取后检测 content-type
   或文件扩展名为 SVG 时,替换为文本描述
2. vision.ts applyVisionInterceptor(): 在分发到 OCR/Vision API
   之前过滤 SVG 类型的 image block
3. vision.ts processWithLocalOCR(): 在调用 worker.recognize()
   之前检查 UNSUPPORTED_OCR_TYPES 集合(纵深防御)
2026-03-19 09:06:40 +08:00
chinadoiphin
23c9f16dff perf: 视觉拦截器仅处理最后一条 user 消息的图片
历史消息中的图片已在前几轮被转换为文本描述,无需重复送入
OCR/Vision API 处理。此优化避免多轮对话中重复消耗 API 配额
并减少每次请求的延迟。
2026-03-18 21:17:50 +08:00
chinadoiphin
c2dae870ca feat: config.yaml 热重载支持(修改后无需重启服务)
- 重构 config.ts:提取 parseYamlConfig / applyEnvOverrides 为可复用函数
- 新增 initConfigWatcher:fs.watch + 500ms 防抖监听 config.yaml
- 端口变更仅警告(需重启),其余字段下一次请求即生效
- 中间件改用 getConfig() 实时读取,确保 auth_tokens 等热更新生效
- 新增 onConfigReload 回调机制、detectChanges 变更检测
- 优雅关闭:SIGTERM/SIGINT 时停止文件监听
- 环境变量覆盖始终保持最高优先级
2026-03-18 21:16:11 +08:00
小海
f9bebeb4b5 fix: thinking 检测位置约束,防止正文字面量误触发 (Issue #64)
将所有 includes('<thinking>') 替换为 hasLeadingThinking(),
只在 <thinking> 出现在响应开头时才触发提取,
防止用户消息或模型正文中的字面量标签误触发 extractThinking 导致内容丢失。
v2.7.4
2026-03-18 14:06:53 +08:00
小海
8a5117bbb1 v2.7.4: 截断安全 + 代理续写禁用 + 日志提示词对比视图
- 截断时跳过工具解析,防止损坏的工具调用(写入半截文件)
- maxAutoContinue 默认 0,交由 Claude Code 原生续写
- 系统提示词身份声明清除(防 prompt injection 拒绝)
- 流式热身窗口 96→300 chars(拒绝检测前不释放文本)
- 日志查看器「提示词对比」视图:原始 vs Cursor 转换后
- 转换摘要面板:工具数/消息数/上下文大小一目了然
- 标题提取增强:通用 XML 标签清除 + 更多引导语过滤
2026-03-18 11:56:26 +08:00
小海
e6f3a06416 chore: 从版本控制中移除 .claude 本地配置 2026-03-18 10:37:04 +08:00
小海
59e9ef51c0 chore: 添加 CTF 测试脚本,更新 .gitignore
- 添加 e2e-ctf-bypass.mjs 和 e2e-ctf-proxy.mjs 测试脚本
- .gitignore: 忽略测试结果 JSON 文件 (test/*-results*.json)
- .gitignore: 忽略 .claude/ 本地配置目录
2026-03-18 10:36:08 +08:00
小海
8a6b11a4c7 perf: optimize prompt efficiency via A/B tested behaviorRules
- behaviorRules: append action-first directive reducing narration 37% (32%→20%)
  \"Each response must be maximally efficient: omit preamble and planning text
  when the next step is clear—go straight to the action block.\"
- Continuation suffix: shorten from ~180 chars to ~30 chars (83% token saving)
  \"Based on the output above, continue working...\" → \"Continue with the next action.\"
- Add A/B test harnesses (e2e-prompt-ab.mjs, e2e-prompt-ab2.mjs) for future prompt tuning

Tested 4 variants × 17 scenarios. Candidate B won with:
  - Narration ratio: 32% → 20%
  - Response latency: 2372ms → 1795ms (↓24%)
  - Zero over-action side effects
  - 9/9 continuation scenarios passed
2026-03-18 10:34:38 +08:00
Xu Kang
af6449300f Merge pull request #62 from majorcheng/main
Fix OpenAI stream usage in final chunk
2026-03-18 09:48:47 +08:00
小海
c06755c46c perf: 早期中止拒绝检测 + 降低重试/续写上限 + 续写上下文截断
性能优化:
- sendCursorRequest 支持外部 AbortSignal,允许调用方提前中止流
- 工具模式 executeStream 在前 200-400 字符即检测拒绝,立即中止流
  (原先等完整响应再检测,浪费 2-5s/次)
- MAX_REFUSAL_RETRIES 2→1,减少最差情况 API 调用次数
- maxAutoContinue 默认 3→2,减少续写开销
- 续写请求只发最后 2000 字符作为上下文(原先发完整响应,可能 10K+)

测试:
- 重写 perf-diag.mjs 公平对比:直连也使用相同 reframing 提示词
- 公平测试结果:代理平均 0.78x(比直连更快),无性能瓶颈
2026-03-18 09:46:36 +08:00
majorcheng
0768c52790 Merge branch '7836246:main' into main 2026-03-18 09:34:40 +08:00
majorcheng
ee8a7135dd Fix OpenAI stream usage in final chunk 2026-03-18 09:19:37 +08:00