feat: 多层上下文压缩 + 多级权限管理系统

权限管理:
- PermissionTypes: 权限行为/模式/规则/决策/选择类型系统
- PermissionSettings: 用户级/项目级/会话级权限持久化(settings.json)
- PermissionRuleEngine: 多层规则引擎(deny→allow→readOnly→mode→dangerous→ASK)
- DangerousPatterns: 危险shell命令检测(rm -rf, eval, exec等)
- ReplSession: Y/A/N/D四选项权限确认UI
- ConfigCommand: permission-mode/list/reset子命令
- AgentLoop: 集成规则引擎,PermissionChoice回调

上下文压缩:
- TokenTracker: 上下文窗口监控(93%自动压缩/82%警告/98%阻塞)
- MicroCompact: 微压缩,裁剪旧tool_result(无API调用)
- SessionMemoryCompact: Session Memory压缩,AI摘要旧消息保留近期段
- FullCompact: 全量压缩+PTL重试(按API Round逐步丢弃)
- AutoCompactManager: 压缩编排器(micro→session→full),含熔断机制
- CompactCommand: 改造为委托FullCompact
- StatusLine: token使用百分比+颜色告警(绿→黄→红→闪烁)
- AppConfig: 注册PermissionSettings/RuleEngine/AutoCompactManager Bean

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
liuzh
2026-04-02 23:05:37 +08:00
co-authored by Copilot
parent c989d3b451
commit 730551cb3f
16 changed files with 1759 additions and 127 deletions
@@ -3,6 +3,9 @@ package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
import com.claudecode.command.SlashCommand;
import com.claudecode.console.AnsiStyle;
import com.claudecode.permission.PermissionRuleEngine;
import com.claudecode.permission.PermissionSettings;
import com.claudecode.permission.PermissionTypes.PermissionMode;
import java.util.List;
import java.util.Map;
@@ -10,8 +13,7 @@ import java.util.Map;
/**
* /config 命令 —— 查看和设置应用配置。
* <p>
* 支持查看当前配置、设置单个配置项。
* 配置变更仅在当前会话内生效。
* 支持查看当前配置、设置单个配置项,以及权限管理子命令
*/
public class ConfigCommand implements SlashCommand {
@@ -21,9 +23,24 @@ public class ConfigCommand implements SlashCommand {
"max-tokens", "Maximum output tokens per response",
"temperature", "Response randomness (0.0-1.0)",
"verbose", "Enable verbose logging (true/false)",
"auto-compact", "Auto compact when context is large (true/false)"
"auto-compact", "Auto compact when context is large (true/false)",
"permission-mode", "Permission mode: default/accept-edits/bypass/dont-ask",
"permission-list", "List all saved permission rules",
"permission-reset", "Clear all permission rules"
);
private PermissionSettings permissionSettings;
public ConfigCommand() {}
public ConfigCommand(PermissionSettings permissionSettings) {
this.permissionSettings = permissionSettings;
}
public void setPermissionSettings(PermissionSettings settings) {
this.permissionSettings = settings;
}
@Override
public String name() {
return "config";
@@ -90,17 +107,6 @@ public class ConfigCommand implements SlashCommand {
return sb.toString();
}
private String showConfig(String key, CommandContext context) {
if (!CONFIG_KEYS.containsKey(key)) {
return AnsiStyle.yellow(" ⚠ Unknown config key: " + key) + "\n"
+ AnsiStyle.dim(" Available: " + String.join(", ", CONFIG_KEYS.keySet()));
}
String desc = CONFIG_KEYS.get(key);
return " " + AnsiStyle.bold(key) + ": " + AnsiStyle.dim(desc) + "\n"
+ AnsiStyle.dim(" Set with: /config " + key + " <value>");
}
private String setConfig(String key, String value, CommandContext context) {
return switch (key) {
case "model" -> {
@@ -112,6 +118,9 @@ public class ConfigCommand implements SlashCommand {
boolean verbose = Boolean.parseBoolean(value);
yield AnsiStyle.green(" ✅ Verbose mode: " + (verbose ? "ON" : "OFF"));
}
case "permission-mode" -> setPermissionMode(value);
case "permission-list" -> listPermissionRules();
case "permission-reset" -> resetPermissionRules();
default -> {
if (!CONFIG_KEYS.containsKey(key)) {
yield AnsiStyle.yellow(" ⚠ Unknown config key: " + key);
@@ -121,4 +130,69 @@ public class ConfigCommand implements SlashCommand {
}
};
}
private String showConfig(String key, CommandContext context) {
// 无参数的权限子命令
if (key.equals("permission-list")) return listPermissionRules();
if (key.equals("permission-reset")) return resetPermissionRules();
if (!CONFIG_KEYS.containsKey(key)) {
return AnsiStyle.yellow(" ⚠ Unknown config key: " + key) + "\n"
+ AnsiStyle.dim(" Available: " + String.join(", ", CONFIG_KEYS.keySet()));
}
String desc = CONFIG_KEYS.get(key);
return " " + AnsiStyle.bold(key) + ": " + AnsiStyle.dim(desc) + "\n"
+ AnsiStyle.dim(" Set with: /config " + key + " <value>");
}
// ── 权限管理子命令 ──
private String setPermissionMode(String value) {
if (permissionSettings == null) {
return AnsiStyle.yellow(" ⚠ Permission settings not initialized");
}
try {
PermissionMode mode = switch (value.toLowerCase()) {
case "default" -> PermissionMode.DEFAULT;
case "accept-edits", "acceptedits" -> PermissionMode.ACCEPT_EDITS;
case "bypass" -> PermissionMode.BYPASS;
case "dont-ask", "dontask" -> PermissionMode.DONT_ASK;
default -> throw new IllegalArgumentException(value);
};
permissionSettings.setCurrentMode(mode);
return AnsiStyle.green(" ✅ Permission mode set to: " + mode);
} catch (IllegalArgumentException e) {
return AnsiStyle.yellow(" ⚠ Unknown mode: " + value) + "\n"
+ AnsiStyle.dim(" Available: default, accept-edits, bypass, dont-ask");
}
}
private String listPermissionRules() {
if (permissionSettings == null) {
return AnsiStyle.yellow(" ⚠ Permission settings not initialized");
}
var rules = permissionSettings.listRules();
if (rules.isEmpty()) {
return AnsiStyle.dim(" No saved permission rules") + "\n"
+ AnsiStyle.dim(" Mode: " + permissionSettings.getCurrentMode());
}
StringBuilder sb = new StringBuilder();
sb.append("\n");
sb.append(AnsiStyle.bold(" 🔒 Permission Rules")).append("\n");
sb.append(" ").append("".repeat(40)).append("\n");
sb.append(" ").append(AnsiStyle.bold("Mode: ")).append(permissionSettings.getCurrentMode()).append("\n\n");
for (String rule : rules) {
sb.append(" ").append(rule).append("\n");
}
return sb.toString();
}
private String resetPermissionRules() {
if (permissionSettings == null) {
return AnsiStyle.yellow(" ⚠ Permission settings not initialized");
}
permissionSettings.clearAll();
return AnsiStyle.green(" ✅ All permission rules cleared");
}
}