permission

package
v0.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 26, 2026 License: None detected not legal advice Imports: 0 Imported by: 0

Documentation

Overview

Package permission - composite_handler.go 实现多处理器叠加的 CompositeHandler.

宪法第 8 条「叠加而非替换」在权限层的实现. 多个权限处理器可以共存:CLI 弹框决策 + 审计日志记录 + WebHook 通知.

使用示例:

cliHandler := func(ctx context.Context, req *Request) (*Response, error) {
    // 终端弹框,等待用户输入
    return &Response{Decision: DecisionAllow, Reason: "user approved"}, nil
}
auditHandler := func(ctx context.Context, req *Request) (*Response, error) {
    // 记审计日志(不参与决策)
    log.Printf("permission request: %s", req.ToolName)
    return nil, nil
}
combined := NewCompositeHandler(
    NamedHandler{Name: "cli", Handler: cliHandler, IsDecisionMaker: true},
    NamedHandler{Name: "audit", Handler: auditHandler, IsDecisionMaker: false},
)
// 权限请求同时弹框给用户 + 记录审计日志

执行策略:

  • 所有 Handler 都会被调用(审计需要)
  • 第一个返回 Allow/Deny 的决策者 Handler 决定最终结果
  • 观察者 Handler 仍然执行,但不影响决策
  • 如果所有决策者都返回 nil/error,默认 Deny

升华改进(ELEVATED): 多渠道权限处理.CLI,HTTP,Hook,审计可以同时工作. 替代方案:单个 Handler(原始设计,只支持一种交互方式).

Package permission is the permission subsystem: the consumer-implementable Handler for Allow/Deny/Ask decisions, the Checker that orchestrates it with rules / classifiers / denial tracking, and the AI-based SecurityClassifier for automatic classification of tool calls.

Consumer integration surfaces fall into three shapes. For the full taxonomy see `docs/api-reference.md` section "API 消费形态 / API Consumption Patterns".

Synchronous callback — form 3:

  • Handler (func type): consumer-supplied callback for permission decisions; injected via engine.Config.PermissionHandler
  • SecurityClassifier.Classify: consumers can plug an alternate classifier backend; engine or a Handler calls Classify synchronously

Pull — form 2:

  • DenialTracker.Stats() DenialStats: cumulative denial snapshot for UI / audit / circuit-breaker decisions
  • Classifier.Classify returns *ClassifyResult as a pull-side payload (consumer reads Decision / Reason / Thinking / Stage / Usage / DurationMs to drive UX)

Package permission 是权限子系统: 消费者可实现的 Handler 用于 Allow/Deny/Ask 决策, Checker 负责编排规则 / 分类器 / 拒绝追踪, AI 的 SecurityClassifier 做工具调用的自动分类.

消费者接入面分三种形态. 完整分类见 `docs/api-reference.md` "API 消费形态 / API Consumption Patterns" 章节.

同步回调 (callback) —— 形态三:

  • Handler (函数类型): 消费者回调, 经 engine.Config.PermissionHandler 注入
  • SecurityClassifier.Classify: 消费者可换 classifier 后端; 引擎或 Handler 同步调 Classify

调取 (pull) —— 形态二:

  • DenialTracker.Stats() DenialStats: 累计拒绝快照, 供 UI / 审计 / 熔断决策消费
  • Classifier.Classify 返回的 *ClassifyResult 亦作 pull payload (消费者 读 Decision / Reason / Thinking / Stage / Usage / DurationMs 驱动 UX)

Package permission 定义权限引擎的接口和实现.

权限系统的核心实现. src/hooks/toolPermission/ 的功能.

原项目的问题:

  • 权限检查逻辑分散在工具内部(tool.checkPermissions)和外部(hasPermissionsToUseTool)两层
  • 规则匹配嵌套极深,compound 命令分解逻辑和主流程混在一起
  • 权限 UI(React 组件)和权限逻辑强耦合

Go 版本的设计:

  • 权限引擎是纯逻辑,不依赖任何 UI
  • Handler 接口由消费层实现(CLI 弹对话框,SDK 返回 JSON,HTTP 用 WebSocket)
  • 规则匹配是独立函数,可单元测试

Index

Constants

View Source
const DefaultPermissionTimeout = 5 * time.Minute

DefaultPermissionTimeout 是 Handler 调用的默认超时时间.

精妙之处(CLEVER): 5 分钟是"用户能够在终端做出响应"的经验上界-- 用户在 CLI 看到权限请求后,通常秒级响应;极少数场景(开会,步骤复杂)也不超过 5 分钟. 超过 5 分钟的 Handler 阻塞几乎必然是 bug(deadlock,网络挂死),而不是正常等待. 替代方案:不超时(早期设计)--Handler 永久挂死时 runLoop 无法退出,进程只能被 kill.

View Source
const MaxSubcommands = 50

MaxSubcommands 是单条 Bash 输入允许的最大子命令数量.

精妙之处(CLEVER): 50 子命令上限不是性能优化,是安全防线. 恶意注入可以构造 cmd1; cmd2; ... cmd1000 的超长命令串, 逐个检查时分类器调用次数爆炸.50 是"正常不会超过,恶意会超过"的经验值. 正常 shell one-liner 一般 5-15 个子命令.

Variables

View Source
var BuiltinDenyRules = []DenyRule{

	{Pattern: "rm -rf /", Reason: "destructive: removes root filesystem", Category: "command"},
	{Pattern: "rm -rf ~", Reason: "destructive: removes home directory", Category: "command"},
	{Pattern: "rm -rf /*", Reason: "destructive: removes all root entries", Category: "command"},
	{Pattern: "> /dev/sda", Reason: "destructive: writes to raw disk", Category: "command"},
	{Pattern: "> /dev/nvme", Reason: "destructive: writes to raw disk", Category: "command"},

	{Pattern: "mkfs.", Reason: "destructive: formats filesystem", Category: "command"},

	{Pattern: "dd if=", Reason: "potentially destructive: raw disk operation", Category: "command"},
	{Pattern: "dd of=/dev/", Reason: "destructive: writes to raw device", Category: "command"},

	{Pattern: ":(){ :|:& };:", Reason: "fork bomb", Category: "command"},
	{Pattern: ":(){:|:&};:", Reason: "fork bomb (no spaces)", Category: "command"},

	{Pattern: "curl -d @/etc/passwd", Reason: "data exfiltration: sends passwd file", Category: "network"},
	{Pattern: "curl -T /etc/shadow", Reason: "data exfiltration: uploads shadow file", Category: "network"},

	{Pattern: "chmod 4755", Reason: "setuid bit: potential privilege escalation", Category: "command"},
	{Pattern: "chmod u+s", Reason: "setuid bit: potential privilege escalation", Category: "command"},

	{Pattern: "> /etc/passwd", Reason: "destructive: overwrites passwd file", Category: "file"},
	{Pattern: "> /etc/shadow", Reason: "destructive: overwrites shadow file", Category: "file"},

	{Pattern: "system(", Reason: "code execution: awk/perl system() call", Category: "command", ToolName: "Bash"},

	{Pattern: "| getline", Reason: "code execution: awk pipe to getline", Category: "command", ToolName: "Bash"},

	{Pattern: "-exec ", Reason: "code execution: find -exec runs commands on matched files", Category: "command", ToolName: "Bash"},
	{Pattern: "-execdir ", Reason: "code execution: find -execdir runs commands in file's directory", Category: "command", ToolName: "Bash"},

	{Pattern: "| xargs ", Reason: "code execution: xargs builds and executes commands from input", Category: "command", ToolName: "Bash"},
}

BuiltinDenyRules 内置 deny 规则(编译进引擎).

历史包袱(LEGACY): 这些规则来源于多年的安全事件积累, 每一条背后都有一个"差点出事"的故事. 例如 `:(){:|:&};:` 是 fork bomb,曾经有 Agent 在尝试解释 shell 语法时意外执行了它.

View Source
var DefaultSafeTools = map[string]bool{

	"Read": true,

	"Grep": true,
	"Glob": true,

	"ToolSearch": true,

	"TaskCreate": true,
	"TaskGet":    true,
	"TaskList":   true,
	"TaskUpdate": true,

	"WebSearch": true,
}

DefaultSafeTools 是默认的安全工具白名单. 包含所有只读工具和不会修改系统状态的操作.

精妙之处(CLEVER): 白名单的工具选择标准是"即使被恶意调用也不会造成损害"-- 读文件,搜索代码,列任务这些操作最多泄露信息(这本来就是 Agent 需要的), 但不会修改或删除任何东西.信息泄露由对话隔离保证,不在分类器的职责范围内.

Functions

func CompactToolUse

func CompactToolUse(toolName string, input map[string]any) string

CompactToolUse 将工具调用压缩为一行.

格式:ToolName: <关键参数> 例如:

  • Bash: ls -la /home
  • Edit: /src/main.go (old_string -> new_string)
  • Read: /src/config.go
  • Grep: pattern="TODO" path=/src

func DetectProvider

func DetectProvider(modelID string) string

DetectProvider 根据模型 ID 推断提供商.

精妙之处(CLEVER): 通过模型 ID 前缀自动检测提供商, 用户不需要显式配置 provider 字段. claude-* → anthropic, gpt-* → openai, gemini-* → google.

func ExplainPermissionRequest

func ExplainPermissionRequest(toolName string, input map[string]any) string

ExplainPermissionRequest 为权限请求生成人类可读的说明.

根据工具类型和输入参数,生成清晰的操作描述,帮助用户理解:

  • 将要执行什么操作
  • 操作的具体内容(命令,文件路径,URL 等)
  • 为什么需要授权

func ExtractCommandName

func ExtractCommandName(cmd string) (command string, subcommand string)

ExtractCommandName 从命令字符串中提取主命令名和子命令.

使用 AST 解析器正确提取命令名,跳过 env/sudo 等前缀. 保持原有的函数签名,内部实现替换为 AST 解析.

会跳过常见的前缀:

  • env VAR=value ...
  • sudo ...
  • nohup ...
  • time ...
  • nice ...

示例:

  • "npm install" → ("npm", "install")
  • "env FOO=bar npm i" → ("npm", "i")
  • "sudo rm -rf /" → ("rm", "-rf")
  • "git push --force" → ("git", "push")

func ExtractDomainFromURL

func ExtractDomainFromURL(url string) string

ExtractDomainFromURL 从 URL 中提取域名.

示例:

func ExtractFilePath

func ExtractFilePath(input map[string]any) string

ExtractFilePath 从工具输入参数中提取文件路径.

支持的参数名:file_path, path, filepath, filename

func ExtractURL

func ExtractURL(input map[string]any) string

ExtractURL 从工具输入参数中提取 URL.

func GetCommandPrefixes

func GetCommandPrefixes(cmd string) []string

GetCommandPrefixes 从命令字符串中提取所有可能的前缀用于规则匹配.

对于 "npm install lodash",返回 ["npm", "npm install", "npm install lodash"]

func GlobMatch

func GlobMatch(pattern, path string) bool

GlobMatch 执行路径 glob 匹配.

支持的通配符:

  • * 匹配路径段内的任意字符(不跨越 /)
  • ** 匹配任意层级的路径(跨越 /)
  • ? 匹配单个字符

示例:

  • GlobMatch("/src/**", "/src/app/main.go") → true
  • GlobMatch("/src/*.go", "/src/main.go") → true
  • GlobMatch("/src/*.go", "/src/sub/main.go") → false

func IsDangerousCommand

func IsDangerousCommand(cmd string) bool

IsDangerousCommand 检查命令是否为危险命令.

检查维度:

  1. 命令名是否在危险命令列表中
  2. 命令+参数组合是否匹配危险模式
  3. 命令内容是否包含危险 SQL 语句
  4. 命令是否操作危险文件
  5. 命令替换 $(...) 或反引号内的嵌套命令(递归检查)

升华改进(ELEVATED): 使用 ExtractAllCommands 递归提取命令替换内的命令, 防止 `echo $(rm -rf /)` 这类嵌套危险命令绕过检测. 早期方案只检查顶层命令,命令替换内部命令不可见. 替代方案:只对字符串做全文匹配--否决:高误报率,且无法正确提取命令名/参数.

func IsDangerousCommandName

func IsDangerousCommandName(command string) bool

IsDangerousCommandName 检查命令名是否在已知危险命令列表中.

func IsDangerousPath

func IsDangerousPath(path string) bool

IsDangerousPath 检查路径是否为危险路径.

危险路径包括:

  • Shell 配置文件(.bashrc 等)
  • Git hooks
  • SSH 密钥和配置
  • 系统关键文件

func IsProtectedDir

func IsProtectedDir(path string) bool

IsProtectedDir 检查路径是否在受保护的目录中.

func MatchDomain

func MatchDomain(ruleDomain, actualDomain string) bool

MatchDomain 检查域名是否匹配规则域名.

支持子域名匹配:

  • "example.com" 匹配 "example.com" 和 "sub.example.com"
  • "api.github.com" 只匹配 "api.github.com"

精妙之处(CLEVER): 域名匹配支持子域名自动包含--规则 "example.com" 同时匹配 "example.com" 和 "sub.example.com",符合域名所有权的直觉. 但 "api.github.com" 不会匹配 "github.com"(子域名不能反向匹配父域名).

func MergeWhitelist

func MergeWhitelist(base map[string]bool, extra []string) map[string]bool

MergeWhitelist 合并用户自定义的安全工具到白名单. 返回合并后的新白名单(不修改原始 map).

func RegisterClassifierFactory

func RegisterClassifierFactory(provider string, factory ClassifierFactory)

RegisterClassifierFactory 注册自定义的分类器工厂. 并发安全,可在插件热加载场景中从多 goroutine 调用.

func RegisterToolClass

func RegisterToolClass(toolName string, class PermissionClass)

RegisterToolClass 注册工具的权限检查类型. 通常由 tools.Registry 在注册工具时自动调用,第三方工具也可手动调用.

精妙之处(CLEVER): 解耦 permission ↔ tools 双向依赖-- permission 包不 import tools,tools.Registry 调用此函数注入映射. 工具新增时只需在 Metadata.PermissionClass 声明一次,无需改权限核心代码.

func SerializeRule

func SerializeRule(rule Rule) string

SerializeRule 将规则序列化为字符串.

示例:

  • Rule{ToolName: "Bash", Content: "prefix:npm"} → "Bash(prefix:npm)"
  • Rule{ToolName: "*", Content: ""} → "*"

func SourcePriority

func SourcePriority(source RuleSource) int

SourcePriority 返回规则来源的优先级数值. 数值越大,优先级越高.

func SplitCompoundCommand

func SplitCompoundCommand(cmd string) []string

SplitCompoundCommand 将复合 Shell 命令分割为独立的子命令.

使用 AST 解析器正确处理复杂语法:heredoc,嵌套引号,命令替换等. 保持原有的函数签名,内部实现替换为 AST 解析.

支持的分隔符:

  • && 逻辑与
  • || 逻辑或
  • | 管道
  • ; 顺序执行

会正确处理引号内的分隔符(不分割).

示例:

  • "npm install && npm test" → ["npm install", "npm test"]
  • "echo 'a && b'" → ["echo 'a && b'"](引号内不分割)
  • "cat file | grep pattern | wc -l" → ["cat file", "grep pattern", "wc -l"]

精妙之处(CLEVER): 用完整的 AST 解析器做命令分割,而非简单的字符串 split-- 早期方案用正则和字符状态机分割,遇到 heredoc,嵌套引号,命令替换就出错. 例如 `echo "a && b" && rm -rf /` 中 echo 参数里的 && 不应被当作分隔符. 升级为 AST 解析后彻底解决了此类误判,安全检查不再有绕过风险.

func UnregisterToolClass

func UnregisterToolClass(toolName string)

UnregisterToolClass 从动态注册表中移除工具(测试清理用).

Types

type AIClassifier

type AIClassifier struct {
	// contains filtered or unexported fields
}

AIClassifier 是 AI 安全分类器. 通过调用 LLM API 判断工具调用是否安全. 精妙之处(CLEVER): 内置决策缓存--同一 toolName+input 组合在 TTL 内复用决策, 避免重复调用 LLM.典型场景:用户连续编辑同一文件,每次都触发相同的安全评估.

func NewAIClassifier

func NewAIClassifier(provider flyto.ModelProvider, stage1Model, stage2Model string) *AIClassifier

NewAIClassifier 创建 AI 分类器. stage1Model 用于快速判断(RoleFast),stage2Model 用于深度推理(RoleMain).

func (*AIClassifier) Classify

func (c *AIClassifier) Classify(ctx context.Context, req *ClassifyRequest) (*ClassifyResult, error)

Classify 执行两阶段 AI 分类.

type Checker

type Checker interface {
	// Check 检查是否有权限执行工具.
	// 根据规则和模式返回决策,如果是 Ask 则调用 Handler.
	Check(ctx context.Context, req *Request) (*Response, error)

	// Mode 返回当前权限模式.
	Mode() Mode

	// SetMode 设置权限模式.
	SetMode(mode Mode)

	// AddRule 添加权限规则.
	AddRule(rule Rule)
}

Checker 是权限引擎接口.

升华改进(ELEVATED): 原名 Engine 与 pkg/flyto.Engine(公共顶层接口)同名-- 在同时导入两个包的文件(如 engine.go)里,permission.Engine vs flyto.Engine 极易混淆. 改为 Checker 更准确地描述职责(核心方法是 Check),同时消除命名冲突. 替代方案:<保留 Engine 名> - 否决:同名不同语义是常见 bug 来源, 代码审查中肉眼难以区分是哪个包的 Engine.

func NewEngine

func NewEngine(mode Mode, handler Handler) Checker

NewEngine 创建权限引擎(使用默认 Handler 超时 DefaultPermissionTimeout).

func NewEngineWithTimeout

func NewEngineWithTimeout(mode Mode, handler Handler, timeout time.Duration) Checker

NewEngineWithTimeout 创建权限引擎,支持自定义 Handler 超时.

timeout 为 0 时使用 DefaultPermissionTimeout. 消费层可在需要定制超时的场景(自动化审批,企业审批流程)使用此函数.

type ClassifierFactory

type ClassifierFactory func(provider flyto.ModelProvider, stage1Model, stage2Model string) SecurityClassifier

ClassifierFactory 是 AI 分类器的工厂函数类型.

type ClassifierUsage

type ClassifierUsage struct {
	InputTokens     int
	OutputTokens    int
	CacheReadTokens int
}

ClassifierUsage 记录分类器的 token 使用情况.

type ClassifyRequest

type ClassifyRequest struct {
	ToolName   string            // 工具名称
	ToolInput  map[string]any    // 工具输入参数
	Transcript []TranscriptEntry // 对话历史投影
	UserIntent string            // FLYTO.md 内容(用户意图)
}

ClassifyRequest 是分类器的输入请求.

type ClassifyResult

type ClassifyResult struct {
	Decision   Decision         // Allow / Deny
	Reason     string           // 原因(Stage 2 时有)
	Thinking   string           // 推理过程
	Stage      string           // "whitelist" / "rule" / "ai_stage1" / "ai_stage2"
	Usage      *ClassifierUsage // token 使用统计
	DurationMs int64            // 耗时(毫秒)
}

ClassifyResult 是分类器的输出结果.

type CompositeHandler

type CompositeHandler struct {
	// contains filtered or unexported fields
}

CompositeHandler 将多个权限处理器叠加.

使用示例:

ch := NewCompositeHandler(
    NamedHandler{Name: "cli", Handler: cliHandler, IsDecisionMaker: true},
    NamedHandler{Name: "webhook", Handler: webhookHandler, IsDecisionMaker: false},
)

// 作为 permission.Handler 使用:
engine := permission.NewEngine(permission.ModeDefault, ch.Handle)

func NewCompositeHandler

func NewCompositeHandler(handlers ...NamedHandler) *CompositeHandler

NewCompositeHandler 创建叠加处理器.

使用示例:

// 单处理器退化--行为与直接使用该 Handler 一致
ch := NewCompositeHandler(NamedHandler{
    Name: "cli", Handler: myHandler, IsDecisionMaker: true,
})

// 多处理器叠加--CLI 决策 + 审计日志
ch := NewCompositeHandler(
    NamedHandler{Name: "cli", Handler: cliHandler, IsDecisionMaker: true},
    NamedHandler{Name: "audit", Handler: auditHandler, IsDecisionMaker: false},
)

func (*CompositeHandler) Add

func (ch *CompositeHandler) Add(h NamedHandler)

Add 动态添加处理器(运行时扩展).

使用示例:

ch.Add(NamedHandler{Name: "webhook", Handler: webhookFn, IsDecisionMaker: false})

Add 动态添加处理器. 如果已有同名处理器,替换(upsert 语义).

func (*CompositeHandler) Handle

func (ch *CompositeHandler) Handle(ctx context.Context, req *Request) (*Response, error)

Handle 执行所有处理器并返回决策结果. 实现 permission.Handler 签名,可直接传给 NewEngine.

精妙之处(CLEVER): 分离决策者和观察者. 决策者(CLI 弹框)的响应决定最终结果. 观察者(审计日志)的响应被忽略但仍然执行. 这样审计永远不会被跳过,即使决策已经做出.

func (*CompositeHandler) Len

func (ch *CompositeHandler) Len() int

Len 返回处理器数量.

func (*CompositeHandler) Remove

func (ch *CompositeHandler) Remove(name string) bool

Remove 按名称移除处理器.返回是否成功移除.

使用示例:

ch.Remove("webhook")

type ContentType

type ContentType string

ContentType 是规则内容的匹配类型.

const (
	ContentNone   ContentType = ""       // 无内容条件,匹配该工具的所有调用
	ContentPrefix ContentType = "prefix" // 前缀匹配(Bash 命令)
	ContentPath   ContentType = "path"   // 路径 glob 匹配(文件操作工具)
	ContentDomain ContentType = "domain" // 域名匹配(WebFetch 工具)
)

type DangerInfo

type DangerInfo struct {
	Reason           string // 人类可读的风险描述(英文,日志友好)
	Pattern          string // 匹配到的具体模式(如 "rm -rf" / "drop table" / ".ssh/authorized_keys")
	HeredocBodyStart int    // Heredoc body 在原文的起始字节 (仅 heredoc 归因时填). EN: origin-source start byte of offending heredoc body.
	HeredocBodyEnd   int    // Heredoc body 在原文的结束字节 (半开). EN: origin-source end byte (exclusive).
}

DangerInfo 结构化的危险分析结果. 供 checkpoint_suggested 事件携带,让消费层展示具体风险原因.

HeredocBodyStart / HeredocBodyEnd 仅当危险字面量归因到某个 heredoc body 时填非零值, 记录该 body 在**原文**中的字节半开区间 [Start, End). 审计 / TUI / 日志可据此精确指向用户输入的触发段 (例 "source bytes 42-67"), 而不只是 "heredoc 里有危险".

HeredocBodyStart / HeredocBodyEnd are non-zero only when the dangerous literal is attributed to a heredoc body; they carry the body's byte half-open interval [Start, End) within the ORIGINAL source. Lets audit / TUI / logs point precisely at the user-input segment that triggered the warning (e.g. "source bytes 42-67"), rather than just "there is danger in a heredoc".

func AnalyzeDanger

func AnalyzeDanger(cmd string) (bool, DangerInfo)

AnalyzeDanger 对单条命令进行结构化危险分析,返回 (危险, DangerInfo).

与 IsDangerousCommand 相比,额外返回匹配的具体模式和可读原因, 用于 checkpoint_suggested 事件中向消费层展示上下文.

升华改进(ELEVATED): 早期设计只返回 bool,消费层无法告知用户"为什么这个命令危险". 结构化分析让 TUI 可以展示 "detected: rm -rf recursive delete" 而非仅"高风险". 替代方案:<返回 string 原因> - 否决:Pattern 和 Reason 职责不同,分开字段更灵活.

升华改进(ELEVATED): 使用 ExtractAllCommands 递归提取命令替换内的嵌套命令, 对每条提取到的命令独立做危险分析,防止 $(rm -rf /) 嵌套绕过. 替代方案:只分析顶层命令--否决:命令替换是最常见的安全绕过手段.

type Decision

type Decision string

Decision 是权限决策结果.

const (
	DecisionAllow Decision = "allow" // 允许执行
	DecisionDeny  Decision = "deny"  // 拒绝执行
	DecisionAsk   Decision = "ask"   // 需要询问用户
)

type DenialStats

type DenialStats struct {
	ConsecutiveDenials int    // 连续拒绝同一工具的次数
	TotalDenials       int    // 总拒绝次数
	LastDeniedTool     string // 最后被拒绝的工具
	LastDeniedInput    string // 最后被拒绝的输入摘要
}

DenialStats 是拒绝追踪器的统计信息.

type DenialTracker

type DenialTracker struct {
	// contains filtered or unexported fields
}

DenialTracker 追踪权限拒绝事件.

func NewDenialTracker

func NewDenialTracker(consecutiveThreshold, totalThreshold int) *DenialTracker

NewDenialTracker 创建一个新的拒绝追踪器.

consecutiveThreshold 为连续拒绝警告阈值(<=0 使用默认值 3). totalThreshold 为总拒绝停止阈值(<=0 使用默认值 10).

func (*DenialTracker) RecordApproval

func (dt *DenialTracker) RecordApproval(toolName string)

RecordApproval 记录一次权限批准事件.

当用户批准了任何工具的调用时调用此方法. 批准后重置连续拒绝计数(表示用户意图已改变). 精妙之处(CLEVER): 任何工具被批准都重置连续拒绝计数--表示用户意图已经改变. 用户可能先拒绝了 3 次 rm,然后批准了 mkdir,说明用户仍在参与决策, 不应该继续认为用户在"连续拒绝"同类操作.

func (*DenialTracker) RecordDenial

func (dt *DenialTracker) RecordDenial(toolName, inputSummary string)

RecordDenial 记录一次权限拒绝事件.

toolName 是被拒绝的工具名称,inputSummary 是输入内容的简短摘要. 如果连续拒绝同一工具,连续计数递增;否则重置连续计数.

func (*DenialTracker) Reset

func (dt *DenialTracker) Reset()

Reset 重置所有追踪数据. 通常在会话开始或用户显式请求时调用.

func (*DenialTracker) ShouldStopAndAsk

func (dt *DenialTracker) ShouldStopAndAsk() bool

ShouldStopAndAsk 检查是否应该停下来询问用户.

当总拒绝次数达到阈值时,返回 true. Agent 应该停下来直接问用户想怎么做,而不是继续尝试.

func (*DenialTracker) ShouldWarnAgent

func (dt *DenialTracker) ShouldWarnAgent() (warn bool, message string)

ShouldWarnAgent 检查是否应该向 Agent 发出警告.

当连续拒绝同一工具达到阈值时,返回 true 和警告消息. Agent 应该在下次工具调用前收到此消息,换一种方式完成任务.

func (*DenialTracker) Stats

func (dt *DenialTracker) Stats() DenialStats

Stats returns a snapshot of the denial tracker's current state.

Shape: pull. Consumer calls Stats() anytime to read the current cumulative denial state; the four DenialStats fields (ConsecutiveDenials / TotalDenials / LastDeniedTool / LastDeniedInput) are the payload.

Stats 返回拒绝追踪器当前状态的快照.

形态: 调取 (pull). 消费者任意时刻调 Stats() 读当前累计拒绝状态, 四字段 (ConsecutiveDenials / TotalDenials / LastDeniedTool / LastDeniedInput) 即 payload.

type DenyRule

type DenyRule struct {
	Pattern  string // 匹配模式(在工具输入中搜索)
	Reason   string // 拒绝原因
	Category string // 分类(command / file / network)
	ToolName string // 限定工具名(空表示匹配所有工具)
}

DenyRule 是一条 deny 规则.

type Handler

type Handler func(ctx context.Context, req *Request) (*Response, error)

Handler 是权限处理器接口. 消费层实现此接口来处理权限请求.

CLI 消费层:弹出终端对话框,等待用户输入 SDK 消费层:通过 control_request JSON 协议转发 HTTP 消费层:通过 WebSocket 推送到前端 测试:直接返回 Allow

Shape: synchronous callback. Engine passes *Request and blocks for *Response (Decision = Allow / Deny / Ask + optional UpdatedInput rewrite).

形态: 同步回调. 引擎传 *Request 阻塞等 *Response (Decision = Allow / Deny / Ask + 可选 UpdatedInput 改写).

type HybridClassifier

type HybridClassifier struct {
	// contains filtered or unexported fields
}

HybridClassifier 混合分类器 - 白名单 + 规则 + AI.

升华改进(ELEVATED): 三层分类器各司其职,不互相越权. 白名单只 allow,规则只 deny,AI 做最终裁决. 这种分离确保每一层的逻辑都是单向的,不会出现冲突. 替代方案:单一 AI 分类器处理所有请求(延迟高,成本高,无法离线工作).

func NewHybridClassifier

func NewHybridClassifier(whitelist map[string]bool, rules []DenyRule, aiClassifier SecurityClassifier) *HybridClassifier

NewHybridClassifier 创建混合分类器. whitelist 为安全工具白名单,rules 为 deny 规则,factory 为 AI 分类器工厂. 如果 whitelist 为 nil,使用 DefaultSafeTools.

func (*HybridClassifier) Classify

Classify 执行混合分类.

流程:

  1. 白名单检查 → 命中直接 allow
  2. 规则引擎检查 → 命中直接 deny
  3. AI 分类器 → 两阶段判断

type LearningStats

type LearningStats struct {
	TotalDecisions int // 总决策次数
	TotalAllowed   int // 总允许次数
	TotalDenied    int // 总拒绝次数
}

LearningStats 是学习追踪器的统计信息.

type LearningTracker

type LearningTracker struct {
	// contains filtered or unexported fields
}

LearningTracker 追踪权限决策历史,用于建议永久规则.

func NewLearningTracker

func NewLearningTracker(threshold int) *LearningTracker

NewLearningTracker 创建一个新的权限学习追踪器.

threshold 指定建议阈值,即同类操作被允许多少次后建议添加永久规则. 传入 0 使用默认值(3 次).

func (*LearningTracker) Reset

func (lt *LearningTracker) Reset()

Reset 重置所有追踪数据. 通常在会话结束或用户显式请求时调用.

func (*LearningTracker) Stats

func (lt *LearningTracker) Stats() LearningStats

Stats 返回当前追踪统计信息.

func (*LearningTracker) SuggestPermanentRules

func (lt *LearningTracker) SuggestPermanentRules() []SuggestedRule

SuggestPermanentRules 基于历史决策建议永久规则.

返回所有满足建议阈值且未曾建议过的规则. 每条规则只会被建议一次,调用此方法后会标记已建议的规则.

func (*LearningTracker) TrackDecision

func (lt *LearningTracker) TrackDecision(toolName string, input map[string]any, decision Decision)

TrackDecision 记录一次权限决策.

在用户做出允许或拒绝决策后调用此方法,系统将提取操作特征并累计计数. 只记录用户主动做出的决策(Allow 和 Deny),自动决策不记录.

type Mode

type Mode string

Mode 是权限模式枚举. 对应原项目中的 PermissionMode 类型.

const (
	ModeDefault     Mode = "default"      // 所有操作询问用户
	ModeAcceptEdits Mode = "accept_edits" // 自动接受文件编辑
	ModeBypass      Mode = "bypass"       // 绕过所有权限检查
	ModePlan        Mode = "plan"         // 计划模式(暂停执行)
)

type NamedHandler

type NamedHandler struct {
	// Name 处理器名称(用于日志和动态移除)
	Name string

	// Handler 实际的权限处理函数
	Handler Handler

	// IsDecisionMaker 是否参与决策.
	// true: 该处理器的响应可以决定最终结果(如 CLI 弹框)
	// false: 仅观察/审计,响应被忽略但仍然执行
	IsDecisionMaker bool
}

NamedHandler 是带名称和角色标记的权限处理器.

使用示例:

nh := NamedHandler{
    Name:            "cli-prompt",
    Handler:         myCliHandler,
    IsDecisionMaker: true, // 参与决策
}

audit := NamedHandler{
    Name:            "audit-log",
    Handler:         myAuditHandler,
    IsDecisionMaker: false, // 仅观察,不参与决策
}

type ParsedContent

type ParsedContent struct {
	Type  ContentType // 内容类型
	Value string      // 匹配值(前缀字符串,glob 模式,域名)
}

ParsedContent 是解析后的规则内容.

func ParseContent

func ParseContent(content string) ParsedContent

ParseContent 解析规则的 Content 字段.

示例:

  • "" → {Type: ContentNone}
  • "prefix:npm" → {Type: ContentPrefix, Value: "npm"}
  • "/src/**" → {Type: ContentPath, Value: "/src/**"}
  • "domain:example.com" → {Type: ContentDomain, Value: "example.com"}

type PermissionClass

type PermissionClass = string

PermissionClass 是工具的权限检查类型. 工具在 Metadata.PermissionClass 中声明,权限引擎据此分派检查逻辑.

升华改进(ELEVATED): P0-2 修复--第三方工具通过 RegisterToolClass 声明权限类型, 所有工具必须在 Metadata.PermissionClass 中声明类型,注册时自动写入 toolClassRegistry. 替代方案:修改 CheckToolPermission 签名传入 ToolRegistry(破坏所有调用点).

const (
	// PermClassBash 命令执行类工具(Bash 语义:子命令分割,前缀匹配,危险命令检测).
	PermClassBash PermissionClass = "bash"
	// PermClassFile 文件操作类工具(路径权限检查,危险路径检测).
	PermClassFile PermissionClass = "file"
	// PermClassWebFetch Web 请求类工具(域名权限检查).
	PermClassWebFetch PermissionClass = "webfetch"
	// PermClassGeneric 通用工具(只读自动放行,其余默认 Ask).
	PermClassGeneric PermissionClass = "generic"
	// PermClassReadOnly 只读通用工具(等同 PermClassGeneric 但永远放行).
	// 用于明确声明工具无副作用,无需用户确认.
	PermClassReadOnly PermissionClass = "readonly"
)

type PermissionDedup

type PermissionDedup struct {
	// contains filtered or unexported fields
}

PermissionDedup 防止同一个 tool_use_id 的权限响应被重复处理.

精妙之处(CLEVER): 用 FIFO 而不是 LRU-- tool_use_id 是一次性的,不会被"再次访问",FIFO 就够了. 替代方案:LRU(更通用但多余,tool_use_id 不存在重复访问模式).

func NewPermissionDedup

func NewPermissionDedup(maxSize int) *PermissionDedup

NewPermissionDedup 创建去重器.

maxSize 指定最大容量,传入 0 使用默认值(1000 条).

func (*PermissionDedup) IsResolved

func (d *PermissionDedup) IsResolved(toolUseID string) bool

IsResolved 检查指定的 tool_use_id 是否已被处理.

func (*PermissionDedup) MarkResolved

func (d *PermissionDedup) MarkResolved(toolUseID string)

MarkResolved 标记指定的 tool_use_id 为已处理.

如果已标记过,不会重复添加. 如果超过 maxSize,按 FIFO 淘汰最早的条目.

func (*PermissionDedup) Size

func (d *PermissionDedup) Size() int

Size 返回当前已记录的 tool_use_id 数量.

type Request

type Request struct {
	ToolName string         // 工具名称
	ToolID   string         // 工具调用 ID
	Input    map[string]any // 工具输入参数
	Message  string         // 人类可读描述
}

Request 是权限检查请求.

func (*Request) MarshalJSON

func (r *Request) MarshalJSON() ([]byte, error)

MarshalJSON 支持序列化(用于 SDK 协议).

type Response

type Response struct {
	Decision       Decision        // 决策结果
	Reason         string          // 决策原因(用于日志和调试)
	RiskLevel      RiskLevel       // 操作风险等级(Ask 决策时填充)
	SuggestedRules []SuggestedRule // 建议的永久规则(Ask 决策时填充)

	// UpdatedInput, when non-nil on DecisionAllow, replaces the caller-supplied
	// tool input. Lets consumer-layer permission handlers sanitize/transform
	// tool arguments (e.g. rewrite Bash commands, restrict file paths, redact
	// secrets) before execution. Currently consumed by SubAgent.runLoop in the
	// Team permission-bubble path; engine main-thread tool exec ignores it (a
	// separate wiring item).
	//
	// UpdatedInput 非 nil 且 Decision=Allow 时, 替换调用方传入的 tool input.
	// 让消费层 permission handler 在执行前 sanitize/transform 工具参数 (改写 Bash
	// 命令, 限制文件路径, 脱敏 secret 等). 目前只有 SubAgent.runLoop 在 Team 权限
	// 冒泡路径消费; engine 主线程工具执行忽略 (独立 wire 项).
	UpdatedInput map[string]any
}

Response 是权限决策响应.

func CheckPathPermission

func CheckPathPermission(path string, rules []Rule) *Response

CheckPathPermission 检查文件路径是否被规则允许.

遍历所有规则,找到匹配的最高优先级规则. 如果没有匹配的规则,返回 nil(让调用者决定默认行为).

func CheckToolPermission

func CheckToolPermission(toolName string, input map[string]any, rules []Rule, mode Mode) *Response

CheckToolPermission 是完整的权限检查入口.

综合以下检查维度:

  1. 模式检查(bypass / plan 快速路径)
  2. 规则匹配(按优先级从低到高)
  3. 工具特定检查: - Bash:分割复合命令,逐个检查;检测危险命令 - 文件操作:检查路径权限和危险路径 - WebFetch:检查域名权限
  4. 危险检测(在规则匹配之后的额外安全层)

返回值说明:

  • DecisionAllow: 允许执行
  • DecisionDeny: 拒绝执行
  • DecisionAsk: 需要询问用户确认

type RiskLevel

type RiskLevel string

RiskLevel 表示操作的风险等级. 帮助用户快速判断是否需要仔细审查.

const (
	RiskLow    RiskLevel = "low"    // 只读操作,不会修改任何内容
	RiskMedium RiskLevel = "medium" // 文件修改或包管理操作
	RiskHigh   RiskLevel = "high"   // 系统命令,网络访问,危险操作
)

func AssessRisk

func AssessRisk(toolName string, input map[string]any) RiskLevel

AssessRisk 评估工具操作的风险等级.

风险等级分类:

  • Low: 只读操作,不会修改系统状态
  • Medium: 文件修改,包安装等可逆操作
  • High: 系统命令,网络访问,不可逆操作

type Rule

type Rule struct {
	Source   RuleSource // 规则来源
	Behavior Decision   // allow / deny / ask
	ToolName string     // 工具名称(支持通配符)
	Content  string     // 规则内容(路径,前缀,域名等)
}

Rule 是一条权限规则. 对应原项目中 PermissionRule 类型.

func LoadRulesFromSettings

func LoadRulesFromSettings(allowed []string, denied []string, source RuleSource) []Rule

LoadRulesFromSettings 从权限设置中加载规则列表.

AllowedTools 中的条目解析为 allow 规则, DeniedTools 中的条目解析为 deny 规则. source 参数指定规则的来源优先级.

func ParseRule

func ParseRule(ruleStr string, source RuleSource, behavior Decision) Rule

ParseRule 解析规则字符串为 Rule 结构体.

规则格式:ToolName 或 ToolName(content)

示例:

  • "Bash" → Rule{ToolName: "Bash", Content: ""}
  • "Bash(prefix:npm)" → Rule{ToolName: "Bash", Content: "prefix:npm"}
  • "Edit(/src/**)" → Rule{ToolName: "Edit", Content: "/src/**"}
  • "*" → Rule{ToolName: "*", Content: ""}

type RuleApplier

type RuleApplier struct {
	// contains filtered or unexported fields
}

RuleApplier 将权限学习的建议转化为实际生效的 session 规则.

升华改进(ELEVATED): 不缓存决策结果,而是将重复决策升级为规则. 规则比缓存更好:Bash(prefix:npm) 覆盖所有 npm 命令, 而缓存只记住 "npm install" 这一条. 替代方案:命令指纹缓存(hash(tool_name + input) → 决策)-- 更简单但有安全风险:同一个 rm -rf build/ 在不同 cwd 下含义不同.

func NewRuleApplier

func NewRuleApplier(engine Checker, learner *LearningTracker, maxRules int) *RuleApplier

NewRuleApplier 创建规则应用管道.

maxRules 指定 session 规则的最大条数,传入 0 使用默认值(100 条).

func (*RuleApplier) ApplyRule

func (a *RuleApplier) ApplyRule(rule Rule) bool

ApplyRule 手动应用一条 session 规则.

如果规则已经应用过(按序列化字符串去重),则返回 false. 如果达到 maxSessionRules 上限,也返回 false.

func (*RuleApplier) ApplySuggestions

func (a *RuleApplier) ApplySuggestions() []Rule

ApplySuggestions 检查学习追踪器的建议,自动应用为 session 规则.

返回本次新应用的规则列表.已经应用过的建议会被跳过. 达到 maxSessionRules 上限后停止应用新规则.

func (*RuleApplier) Reset

func (a *RuleApplier) Reset()

Reset 清除所有 session 规则的追踪状态. 注意:已添加到引擎的规则不会被移除(引擎没有 RemoveRule 接口). 通常在新会话开始时,引擎也会被重新创建,所以这不是问题.

func (*RuleApplier) SessionRuleCount

func (a *RuleApplier) SessionRuleCount() int

SessionRuleCount 返回当前 session 规则的数量.

type RuleDenyEngine

type RuleDenyEngine struct {
	// contains filtered or unexported fields
}

RuleDenyEngine 规则 deny 引擎. 只做一件事:检查工具调用是否匹配已知的危险模式.

func NewRuleDenyEngine

func NewRuleDenyEngine(rules []DenyRule) *RuleDenyEngine

NewRuleDenyEngine 创建规则 deny 引擎. 如果 rules 为 nil,使用 BuiltinDenyRules.

func (*RuleDenyEngine) AddRule

func (e *RuleDenyEngine) AddRule(rule DenyRule)

AddRule 向引擎追加一条 deny 规则.

func (*RuleDenyEngine) Check

func (e *RuleDenyEngine) Check(toolName string, input map[string]any) (bool, string)

Check 检查工具调用是否匹配 deny 规则. 返回 (是否被拒绝, 拒绝原因).

func (*RuleDenyEngine) Rules

func (e *RuleDenyEngine) Rules() []DenyRule

Rules 返回当前的规则列表(只读副本).

type RuleSource

type RuleSource string

RuleSource 是规则的来源. 决定优先级:后面的覆盖前面的.

const (
	SourceUser    RuleSource = "user"    // ~/.flyto/settings.json
	SourceProject RuleSource = "project" // .flyto/settings.json
	SourceLocal   RuleSource = "local"   // .flyto/settings.local.json
	SourceFlag    RuleSource = "flag"    // --settings CLI 参数
	SourcePolicy  RuleSource = "policy"  // 企业管理设置
	SourceCLI     RuleSource = "cli"     // 命令行参数
	SourceSession RuleSource = "session" // 会话临时规则
)

type SecurityClassifier

type SecurityClassifier interface {
	Classify(ctx context.Context, req *ClassifyRequest) (*ClassifyResult, error)
}

SecurityClassifier 安全分类器接口 - 模型无关. 任何实现此接口的分类器都可以被 HybridClassifier 使用.

Shape: pull. Consumer synchronously calls Classify(ctx, req) and receives a *ClassifyResult; the 6 fields (Decision / Reason / Thinking / Stage / Usage / DurationMs) are the pull payload.

形态: 调取 (pull). 消费者同步调 Classify(ctx, req) 拿 *ClassifyResult; 6 字段 (Decision / Reason / Thinking / Stage / Usage / DurationMs) 即 pull payload.

func NewAnthropicClassifier

func NewAnthropicClassifier(provider flyto.ModelProvider, stage1Model, stage2Model string) SecurityClassifier

NewAnthropicClassifier 创建 Anthropic 特化的分类器.

Anthropic 实现特点:

  • prompt caching(分类器系统提示跨调用复用)
  • 标准 AIClassifier(XML 输出天然适合 Anthropic 模型)
  • cache_control: ephemeral 标记可在上层配置

func NewClassifierForProvider

func NewClassifierForProvider(providerName string, provider flyto.ModelProvider, stage1Model, stage2Model string) SecurityClassifier

NewClassifierForProvider 根据提供商名称创建分类器. 如果提供商未注册,使用 generic 兜底.并发安全(读锁).

func NewClassifierFromRegistry

func NewClassifierFromRegistry(registry *config.ModelRegistry, provider flyto.ModelProvider) SecurityClassifier

NewClassifierFromRegistry 使用 ModelRegistry 和注入的 ModelProvider 创建分类器. 从注册表中获取 RoleFast 和 RoleMain 的模型 ID,通过模型 ID 自动检测提供商类型.

升华改进(ELEVATED): 不硬编码模型 ID,完全通过 ModelRegistry 解耦. 用户切换模型只需修改角色映射,分类器自动适配. 此前版本通过 legacyClientProvider 桥接 api.Client-- 那个路径将 Anthropic HTTP 客户端泄漏给所有分类器,OpenAI/Gemini 无法使用. 现在直接接受 flyto.ModelProvider 接口,provider 由调用方注入,分类器完全 Provider 无关. 替代方案(原方案): apiKey+baseURL 参数 + legacyClientProvider 桥接-- 调用方仍需提供 Anthropic 凭据,即使实际 provider 是 OpenAI.

func NewGenericClassifier

func NewGenericClassifier(provider flyto.ModelProvider, stage1Model, stage2Model string) SecurityClassifier

NewGenericClassifier 创建通用兜底分类器.

Generic 实现(兜底):

  • 纯文本 prompt
  • regex 解析 ALLOW/BLOCK
  • 不依赖任何提供商特有功能

func NewGoogleClassifier

func NewGoogleClassifier(provider flyto.ModelProvider, stage1Model, stage2Model string) SecurityClassifier

NewGoogleClassifier 创建 Google 特化的分类器.

⚠️ 占位实现(PLACEHOLDER):当前与 NewAnthropicClassifier 完全相同.

Google AI(Gemini)的 API 协议与 Anthropic 差异更大(REST-only,safety_settings 参数, grounding 功能,不同的 SSE 格式),通过 api.Client 调用时会产生请求/解析错误. 请勿在生产环境中将 provider 设为 "google",直到专用适配层完成.

技术债记录(P1-6):未来需实现 GoogleClassifier,差异点:

  • Endpoint: generativelanguage.googleapis.com
  • Auth: ?key=<API_KEY> 或 Bearer token
  • 请求格式: generateContent(与 /v1/messages 完全不同)
  • 响应格式: candidates[].content.parts[].text
  • 推荐利用 grounding 特性增强分类准确性

历史包袱(LEGACY): 与 NewOpenAIClassifier 同样的占位问题.

func NewOpenAIClassifier

func NewOpenAIClassifier(provider flyto.ModelProvider, stage1Model, stage2Model string) SecurityClassifier

NewOpenAIClassifier 创建 OpenAI 特化的分类器.

⚠️ 占位实现(PLACEHOLDER):当前与 NewAnthropicClassifier 完全相同.

OpenAI 的 API 协议(JSON body schema,响应格式,SSE 事件名)与 Anthropic 不兼容, 通过同一个 api.Client 调用 OpenAI 模型时 SSE 解析会失败或产生错误结果. 调用此工厂的代码将获得一个"表面可用但实际行为不正确"的分类器-- 请勿在生产环境中将 provider 设为 "openai",直到专用适配层完成.

技术债记录(P1-6):未来需实现 OpenAIClassifier,差异点:

  • API endpoint: /v1/chat/completions(非 /v1/messages)
  • Auth header: Authorization: Bearer(非 x-api-key)
  • 响应 JSON 结构: choices[].message.content(非 content[].text)
  • SSE event 格式: data: {delta: {content: ...}}(非 content_block_delta)
  • 推荐用 JSON mode(response_format: {type: "json_object"})取代 XML 解析

历史包袱(LEGACY): 占位实现返回了与 Anthropic 相同的 AIClassifier, 使调用方误以为切换 provider 字段即可无缝切换供应商.

type SedCheckResult

type SedCheckResult struct {
	Safe    bool   // 是否安全
	Reason  string // 原因说明
	Pattern string // 模式类型:"print" / "substitution" / "dangerous" / "unknown"
}

SedCheckResult 是 sed 命令安全检查的结果.

func CheckSedCommand

func CheckSedCommand(command string, allowFileWrites bool) SedCheckResult

CheckSedCommand 检查 sed 命令是否安全.

检查流程:

  1. 提取 sed 表达式(-e,--expression=,内联表达式)
  2. 检查每个表达式是否包含危险操作
  3. 如果全部安全,判断是打印模式还是替换模式

allowFileWrites: 是否允许文件写入(w 标志).一般为 false.

type SuggestedRule

type SuggestedRule struct {
	// RuleString 是规则字符串,如 "Bash(prefix:npm)"
	RuleString string
	// Description 是规则的人类可读描述
	Description string
}

SuggestedRule 是一条建议的永久权限规则. 当用户反复批准同类操作时,可以建议添加永久规则以减少打扰.

func SuggestRules

func SuggestRules(toolName string, input map[string]any) []SuggestedRule

SuggestRules 为权限请求建议可添加的永久规则.

返回的建议规则可以告知用户:"如果你信任这类操作,可以添加规则以自动放行."

type TranscriptEntry

type TranscriptEntry struct {
	Role    string // "user" / "assistant"
	Content string // 压缩后的内容
}

TranscriptEntry 对话历史的投影条目.

func BuildTranscript

func BuildTranscript(messages []query.Message, maxEntries int) []TranscriptEntry

BuildTranscript 从对话消息中构建分类器的对话历史投影.

投影规则:

  • 用户消息:保留文本内容(截断到 200 字符)
  • 助手消息:只保留 tool_use 调用(压缩为一行)
  • 系统消息:跳过
  • 最多保留最近 maxEntries 条(0 表示默认 20 条)

升华改进(ELEVATED): 排除助手的文本回复,只保留工具调用. 这是一个安全决策:如果保留助手文本,攻击者可以通过 prompt injection 让助手在文本回复中写入"用户已授权此操作"来影响分类器. 只保留 tool_use 记录是客观事实,无法被注入操纵. 替代方案:保留完整对话(包含助手文本回复,可能被 prompt injection 利用).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL