// Package tools 定义工具系统的核心接口和注册表.
//
// 工具系统的类型定义和接口.
// 原项目的工具系统有以下问题:
// - Tool 定义和 UI 渲染耦合(renderToolResultMessage 直接返回 JSX)
// - 并发安全性通过每个工具的 isConcurrencySafe() 方法判断,散落各处
// - 权限检查混在工具内部和外部两层
//
// Go 版本的设计:
// - Tool 接口只关心执行逻辑,不关心 UI
// - 并发安全性和只读性作为元数据声明,由编排器统一调度
// - 权限检查完全由 PermissionEngine 负责,工具不参与
package tools
import (
"context"
"encoding/json"
)
// Tool 是所有工具必须实现的接口.
// 对应原项目中 Tool 泛型类型.
//
// Go 版本简化为统一的 JSON 输入/输出,避免泛型复杂度.
// 类型安全由每个工具内部的 json.Unmarshal 保证.
//
// Shape: synchronous callback. Engine dispatches via Execute with a
// JSON input and blocks for *Result; custom tool authors inject via
// engine.Config.Tools. Description / InputSchema / Name are pull-side
// metadata read by engine at registration / prompt assembly time.
//
// 形态: 同步回调. 引擎经 Execute 传 JSON 输入阻塞等 *Result; 自定义工具
// 作者经 engine.Config.Tools 注入. Description / InputSchema / Name 是
// pull 侧元数据, 引擎在注册 / 拼 prompt 时读.
type Tool interface {
// Name 返回工具的唯一标识符.
Name() string
// Description 返回工具的描述(提供给模型的说明).
// 可以是动态的,根据当前上下文返回不同内容.
Description(ctx context.Context) string
// InputSchema 返回工具的 JSON Schema 输入定义.
// 模型根据此 schema 生成工具调用参数.
InputSchema() json.RawMessage
// Execute 执行工具逻辑.
// input 是模型生成的 JSON 参数(已通过 schema 验证).
// 返回 Result 和可能的错误.
//
// ctx 携带取消信号,对应原 TS 项目的 AbortController.
// progress 回调用于报告长时间任务的进度.
Execute(ctx context.Context, input json.RawMessage, progress ProgressFunc) (*Result, error)
}
// Metadata 是工具的元数据声明.
// 工具可以选择实现 MetadataProvider 接口来声明这些属性.
// 如果不实现,编排器使用保守默认值(不可并发,非只读,非破坏性).
//
// 对应原项目中 isConcurrencySafe(),isReadOnly(),isDestructive() 等方法.
type Metadata struct {
// ConcurrencySafe 表示该工具是否可以与其他工具并行执行.
// true: Glob,Grep,FileRead 等只读工具
// false: FileEdit,FileWrite,Bash(可能有副作用)等
ConcurrencySafe bool
// ReadOnly declares this tool has no side effects (pure queries /
// read-only FS access). Informational metadata: external SDK / TUI /
// audit consumers render a "read-only" label; evolve's tool generator
// may auto-set it.
//
// Not part of permission decisions -- permission path is driven by
// PermissionClass (above). No in-tree consumer reads this field for
// permission logic today.
//
// ReadOnly 声明该工具不产生副作用 (纯查询 / 只读 FS 访问).
// Informational 元数据: 外部 SDK / TUI / audit 消费层据此渲染 "只读"
// 标签, evolve 工具生成器也可据此自动设置.
//
// 不参与权限决策 -- 权限路径由 PermissionClass 驱动 (见上方).
// 本字段当前无 in-tree 消费者读取做权限判断.
ReadOnly bool
// Destructive declares the tool may cause irreversible damage (rm -rf,
// git push --force, DROP TABLE). Informational metadata: audit / UI
// render warning icons; reports count "N destructive calls this session".
//
// Not part of permission decisions -- same constraint as ReadOnly.
//
// Destructive 声明该工具可能造成不可逆破坏 (rm -rf / git push --force /
// DROP TABLE 等). Informational 元数据: audit / UI 据此渲染警告图标,
// 审计报表统计 "本 session 破坏性调用 N 次".
//
// 不参与权限决策 -- 约束同 ReadOnly.
Destructive bool
// Aliases 工具的别名列表(向后兼容用).
Aliases []string
// SearchHint 搜索提示词(3-10 字),用于 ToolSearch 延迟加载时的匹配.
SearchHint string
// PermissionClass 声明工具的权限检查类型.
// 权限引擎据此分派正确的安全检查逻辑,避免权限系统硬编码工具名.
//
// 升华改进(ELEVATED): P0-2 修复核心--工具自描述权限语义,
// 第三方工具注册时 tools.Registry 自动调用 permission.RegisterToolClass.
// 有效取值(见 permission 包中的 PermClass* 常量):
// "bash" - 命令执行类(子命令分割 + 危险命令检测)
// "file" - 文件操作类(路径权限 + 危险路径检测)
// "webfetch" - Web 请求类(域名权限检查)
// "readonly" - 只读类(直接放行)
// "generic" - 通用(默认,规则匹配后 Ask)
// "" - 未声明,权限系统使用内置 fallback(向后兼容)
PermissionClass string
// AuditOperation 声明工具的审计操作类型.
// 用于 AuditObserver 将工具事件翻译为 AuditEntry.Operation 字段.
//
// 升华改进(ELEVATED): P1 L1191 修复 - 与 PermissionClass 同源模式, 工具自描述
// 跨切面语义 (权限 + 审计), 消费者 (flysafe / flyto-platform-common / 新工具作者)
// 注册自定义工具时不需要 fork 引擎修改 audit_observer.operationFromTool 的
// switch.例如第三方 "DatabaseWrite" / "SlackSend" / "K8sApply" 工具通过
// Metadata{AuditOperation: "write"} 声明, 审计记录立即正确.
//
// 常用取值 (任意自定义字符串也合法, 由消费者与其审计后端约定):
// "write" - 写入 (文件写, DB 写, 消息发送)
// "edit" - 修改 (文件编辑, DB 更新)
// "read" - 读取 (文件读, DB 查询, glob, grep)
// "execute" - 命令执行 (bash, 子进程)
// "delete" - 删除 (文件删, DB 删)
// "invoke" - 通用调用 (默认值, 无副作用)
// "" - 未声明, AuditObserver 回退到启发式 (基于工具名匹配)
//
// 替代方案 A: <保留硬编码 switch, 不加 Metadata 字段> - 否决:
// 违反开闭原则, 消费者加新工具必须 fork 引擎.
// 替代方案 B: <新增全局 RegisterAuditOperation(name, op) 函数> - 否决:
// 引入平行于 Metadata 的第二种注册路径, 与既有 PermissionClass 风格不一致.
// 替代方案 C: - 否决:
// 消费者要事先知道所有工具名, 插件动态注册场景不适用.
AuditOperation string
// RequiresCheckpoint 表示该工具执行前需要等待外部确认.
//
// 升华改进(ELEVATED): 早期实现 中"Human checkpoint"只是提示词里的文字建议,
// 模型自己决定要不要暂停--不可靠(模型可能忽略).
// 我们在工具声明层强制暂停:RequiresCheckpoint=true 的工具,
// 引擎在执行前必须经过 CheckpointHandlerFn 放行,handler 未注册默认拒绝.
//
// 适用场景(不可逆操作):
// - 发送消息(邮件,IM,短信)
// - 删除数据(DROP TABLE,rm -rf)
// - 生产部署(kubectl apply,terraform apply)
// - 资金操作(转账,支付)
//
// 替代方案:<在 prompt 中写"执行前问用户">
// - 否决:依赖模型遵守,不可靠;且无法在 SDK 嵌入模式下触发程序级回调.
RequiresCheckpoint bool
// RequiresReverseThinking flags this tool as one whose call benefits
// from a reverse-thinking pass before execution: producing a
// counterfactual.Deliverable (hidden assumptions / failure scenarios /
// verdict / verdict_reason) that is persisted alongside the call for
// audit and evolve.Reflector replay.
//
// Boundary with RequiresCheckpoint: RequiresCheckpoint asks "should a
// human approve this call?"; RequiresReverseThinking asks "should the
// agent challenge its own recommendation before this call?". Both can
// be true simultaneously (high-stakes calls deserve both); core does
// not impose ordering -- the consuming hook chain (e.g.
// platform/common/safetychain) decides whether reverse-think runs
// before / after / instead-of the human checkpoint.
//
// Suitable when (non-trivial design space, not every irreversible op):
// - Tool selects between competing strategies (route planning,
// carrier choice, policy parameter)
// - Tool encodes a decision whose failure mode is non-obvious
// (settlement matching, fraud judgment)
// - Tool calls a downstream system whose schema may shift
// (third-party API, partner integration)
//
// Avoid when: the tool is a pure mechanical operation (file read,
// arithmetic, well-defined CRUD); reverse-think on these is overhead
// without insight.
//
// Default false. When false, no reverse-thinking pass is invoked
// regardless of any hooks layered on; opt-in is per-tool.
//
// ELEVATED rationale: rule of two with RequiresCheckpoint -- both are
// "self-describing tool flags consumed by hooks / handlers above core",
// adding one alongside the existing one is cheaper than introducing a
// new metadata category.
//
// 替代方案 (已否决):
// A. 引擎级 ReverseThinkingGate 接口 (TODO L569 原方案 8 模块完整版)
// -- 否决: 与 hooks / permission / validator / staging / RequiresCheckpoint
// 5 套现有 gate 重叠, 业界零先例 (调研: Claude Agent SDK / OpenAI
// Assistants / Vertex ADK / LangGraph / AutoGen / CrewAI / Semantic
// Kernel / Cline 全部走 prompt-level + tool desc + 开发者自填策略).
// B. 仅写 prompt 层引导, 不加字段
// -- 否决: prompt 易丢, 无法在 SDK 嵌入模式下程序级判定; 且
// Deliverable 持久化无锚点字段.
//
// RequiresReverseThinking 标记本工具调用前应走反向思维 pass: 产 counterfactual
// .Deliverable (hidden_assumptions / failure_scenarios / verdict /
// verdict_reason) 与调用一同持久化供审计和 evolve.Reflector 重放.
//
// 与 RequiresCheckpoint 边界: RequiresCheckpoint 问 "需要人确认吗?";
// RequiresReverseThinking 问 "agent 应不应该挑战自己的建议?". 两者可同时
// true (高风险调用值得两道); core 不规定顺序 -- 消费 hook 链
// (例如 platform/common/safetychain) 决定 reverse-think 在 checkpoint
// 之前 / 之后 / 替代.
//
// 适用场景 (非平凡设计空间, 不是所有不可逆操作):
// - 工具在多个候选策略中选择 (路由规划 / 承运商选择 / 策略参数)
// - 工具编码失败模式不直观的决策 (账单匹配 / 反欺诈判定)
// - 工具调下游 schema 可能漂移的系统 (三方 API / 合作方集成)
//
// 不适用: 纯机械操作 (文件读 / 算术 / 良定义 CRUD); 反向思维只是开销
// 没有洞察.
//
// 默认 false. false 时无论上层挂多少 hook 都不触发反向思维; 按工具 opt-in.
//
// 升华理由 (ELEVATED): 与 RequiresCheckpoint 走 rule of two -- 两者都是
// "core 之上 hook / handler 消费的自描述工具 flag", 在已有字段旁加同源字段
// 比新增 metadata 类别更便宜.
RequiresReverseThinking bool
}
// MetadataProvider 是工具的可选接口.
// 实现此接口的工具可以声明并发安全性等元数据.
//
// Shape: pull. Engine reads Metadata() at orchestration decisions
// (can run in parallel? is destructive? requires checkpoint?).
//
// 形态: 调取 (pull). 引擎在编排决策时读 Metadata() (能否并发? 是否破坏性?
// 要不要 checkpoint?).
type MetadataProvider interface {
Metadata() Metadata
}
// Result 是工具执行的结果.
type Result struct {
// Output 是工具的文本输出.
Output string
// IsError 标记输出是否为错误信息.
IsError bool
// Data 是结构化数据输出(可选).
// 用于工具间传递结构化信息.
Data any
// UndoInfo 撤销信息(由 Reversible 工具填充).
// 升华改进(ELEVATED): 撤销信息嵌入 Result 而非单独返回--
// 这样编排器在收集工具结果时自然就拿到了撤销信息,不需要额外调用.
// 替代方案:在 Orchestrator 中单独调用 GenerateUndo(多一次调用,时序更复杂).
UndoInfo *UndoInfo
}
// ProgressFunc 是进度回调函数类型.
type ProgressFunc func(progress float64, detail string)
// GetMetadata 安全获取工具的元数据.
// 如果工具未实现 MetadataProvider,返回保守默认值.
func GetMetadata(t Tool) Metadata {
if mp, ok := t.(MetadataProvider); ok {
return mp.Metadata()
}
// 保守默认值:假设不可并发,非只读,非破坏性
return Metadata{
ConcurrencySafe: false,
ReadOnly: false,
Destructive: false,
}
}