Documentation
¶
Overview ¶
Package tools 的 ToolCapability 协议 -- Agent Tool Safety Protocol.
升华改进(ELEVATED): 定义 Agent Tool Safety Protocol. 文件/数据库/API 三种操作统一为同一套能力声明. 支持 DryRun 的工具会被 Agent 优先选择(因为更安全). 替代方案:所有工具一视同仁,不声明能力(无法区分安全等级).
三个层次:
Level 0: 不支持 DryRun 也不可逆 → Agent 调用前警告 Level 1: Reversible(可回滚)→ Saga 补偿模式 Level 2: DryRun(可预览)→ 先试再做,最安全
Package tools defines the Tool interface (what the engine dispatches) and the capability hooks (DryRunnable / Reversible / MetadataProvider / CapabilityProvider) that let consumers describe tool-level properties to the engine and to UIs/auditors.
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:
- Tool.Execute: engine dispatches the tool with a Request and blocks for *Result. Custom tools implement this — SDK consumers inject via engine.Config.Tools.
Pull — form 2 (metadata / capability description):
- MetadataProvider.Metadata() Metadata — engine reads to classify tool safety (destructive / read-only)
- CapabilityProvider.Capability() ToolCapability — lists AffectedResources / UndoMethod / MinConfidence so UIs can render preview UX
- DryRunnable.DryRun(input) (*DryRunResult, error) — consumer-provided preview of tool effects without executing
- Reversible.Undo(result) / UndoInfo — enables operation history / undo
Package tools 定义 Tool 接口 (引擎分发工具的入口) 和能力钩子 (DryRunnable / Reversible / MetadataProvider / CapabilityProvider), 让消费者向引擎和 UI / 审计面声明工具级属性.
消费者接入面分三种形态. 完整分类见 `docs/api-reference.md` "API 消费形态 / API Consumption Patterns" 章节.
同步回调 (callback) —— 形态三:
- Tool.Execute: 引擎传 Request 阻塞等 *Result. 自定义工具实现此接口, SDK 消费者经 engine.Config.Tools 注入.
调取 (pull) —— 形态二 (元数据 / 能力描述):
- MetadataProvider.Metadata() Metadata —— 引擎据此分类工具安全性 (破坏性 / 只读)
- CapabilityProvider.Capability() ToolCapability —— 列出 AffectedResources / UndoMethod / MinConfidence, 供 UI 渲染预览 UX
- DryRunnable.DryRun(input) —— 消费者提供的工具效果预览, 不真正执行
- Reversible.Undo(result) / UndoInfo —— 支持操作历史 / 撤销
Package tools 定义工具系统的核心接口和注册表.
工具系统的类型定义和接口. 原项目的工具系统有以下问题:
- Tool 定义和 UI 渲染耦合(renderToolResultMessage 直接返回 JSX)
- 并发安全性通过每个工具的 isConcurrencySafe() 方法判断,散落各处
- 权限检查混在工具内部和外部两层
Go 版本的设计:
- Tool 接口只关心执行逻辑,不关心 UI
- 并发安全性和只读性作为元数据声明,由编排器统一调度
- 权限检查完全由 PermissionEngine 负责,工具不参与
Index ¶
- Constants
- func CheckConfidenceGate(cap ToolCapability, input json.RawMessage) (pass bool, errMsg string, stripped json.RawMessage)
- func IsDryRunnable(t Tool) bool
- func IsReversible(t Tool) bool
- func MessageIDFromContext(ctx context.Context) string
- func RegisterDeferredTool(name string)
- func WithMessageID(ctx context.Context, id string) context.Context
- func WithWorkdir(ctx context.Context, dir string) context.Context
- func WorkdirFromContext(ctx context.Context) string
- type CapabilityProvider
- type DeferredRegistry
- func (dr *DeferredRegistry) ActivateTool(name string)
- func (dr *DeferredRegistry) ActiveTools() []Tool
- func (dr *DeferredRegistry) IsDeferred(name string) bool
- func (dr *DeferredRegistry) Registry() *Registry
- func (dr *DeferredRegistry) ResetActivations()
- func (dr *DeferredRegistry) SearchTools(query string) []Tool
- func (dr *DeferredRegistry) Threshold() int
- func (dr *DeferredRegistry) WithAlwaysLoad(names ...string) *DeferredRegistry
- type DryRunResult
- type DryRunnable
- type MessageIDAware
- type Metadata
- type MetadataProvider
- type Orchestrator
- type ProgressFunc
- type Registry
- func (r *Registry) All() []Tool
- func (r *Registry) Count() int
- func (r *Registry) Filter(names []string) []Tool
- func (r *Registry) Get(name string) (Tool, bool)
- func (r *Registry) MetadataFor(name string) (Metadata, bool)
- func (r *Registry) Names() []string
- func (r *Registry) Register(t Tool) error
- func (r *Registry) Unregister(name string) bool
- type Result
- type ResultProcessor
- type Reversible
- type SocketAware
- type Tool
- type ToolCall
- type ToolCallResult
- type ToolCapability
- type ToolPolicy
- type UndoInfo
Constants ¶
const ConfidenceInputField = "_flyto_confidence"
ConfidenceInputField is the reserved JSON key the LLM uses to self-report tool-call confidence (0-100). Prefix underscore marks it as a framework field, not a tool business parameter; the orchestrator strips it before Execute so tool InputSchema contracts stay intact.
ConfidenceInputField 是 LLM 自报工具调用置信度 (0-100) 的 JSON 保留字段. 下划线前缀表示这是框架字段而非工具业务参数; orchestrator 在 Execute 前 剥除, 工具 InputSchema 契约不被侵入.
Variables ¶
This section is empty.
Functions ¶
func CheckConfidenceGate ¶
func CheckConfidenceGate(cap ToolCapability, input json.RawMessage) (pass bool, errMsg string, stripped json.RawMessage)
CheckConfidenceGate enforces ToolCapability.MinConfidence against the `_flyto_confidence` reserved field in the input JSON. Returns:
- pass=true, errMsg="", stripped: input with the reserved field removed, safe to hand to the tool.
- pass=false, errMsg=reason: caller returns a tool-level error.
Gate semantics:
- cap.MinConfidence == 0: always pass, input returned unchanged (no parse, no allocation).
- cap.MinConfidence > 0 and field missing: fail with "requires _flyto_confidence (0-100) in input, min=N".
- cap.MinConfidence > 0 and field < threshold: fail with "confidence M below required N".
- cap.MinConfidence > 0 and field >= threshold: pass, field stripped.
Input must be a JSON object. Non-object / invalid JSON returns pass=true unchanged when gate disabled; when gate enabled, returns fail (cannot locate the reserved field).
CheckConfidenceGate 按 ToolCapability.MinConfidence 校验 input JSON 中的 `_flyto_confidence` 保留字段. 返回:
- pass=true, errMsg="", stripped: 剥除保留字段后的 input, 可直接给工具.
- pass=false, errMsg=理由: 调用方返工具级错误.
Gate 语义:
- cap.MinConfidence == 0: 永远放行, input 原样返回 (不解析不分配).
- cap.MinConfidence > 0 且字段缺失: 失败 "requires _flyto_confidence (0-100) in input, min=N".
- cap.MinConfidence > 0 且字段 < 阈值: 失败 "confidence M below required N".
- cap.MinConfidence > 0 且字段 >= 阈值: 放行, 字段剥除.
input 必须是 JSON 对象. 非对象 / 无效 JSON: gate disabled 时原样放行; gate enabled 时视为失败 (无法定位保留字段).
func MessageIDFromContext ¶
MessageIDFromContext returns the turn id set by WithMessageID, or "" if none. Tools implementing MessageIDAware should prefer this over their stored field.
MessageIDFromContext 返回 WithMessageID 设的轮次 id, 未设则 "". 实现 MessageIDAware 的工具应优先用它而非存储的字段.
func RegisterDeferredTool ¶
func RegisterDeferredTool(name string)
RegisterDeferredTool 在全局默认核心工具列表中注册新工具名.
升华改进(ELEVATED): 可注册的动态白名单-- 第三方工具库可以将自己的核心工具(如仓储场景的 ScanBarcode)注册到始终加载列表, 无需 fork 源码修改 defaultAlwaysLoadTools. 替代方案(原方案):硬编码 map 字面量,第三方只能 fork 改源码.
注意:此函数修改全局状态.如需按 Engine 实例定制,请使用 NewDeferredRegistry + WithAlwaysLoad.
func WithMessageID ¶
WithMessageID derives a context carrying the current turn id for tools that key per-turn side effects (file-history snapshots). The engine sets it once per turn on the context it passes to tool execution, so concurrent Runs on one engine each carry their OWN id with no shared-instance race -- the race-free, correct replacement for the shared SetMessageID field. An empty id is a no-op (the consumer falls back to its SetMessageID field, or the zero key "").
WithMessageID 派生一个携带当前轮次 id 的 context, 供按轮次打键副作用 (file-history 快照) 的工具用. engine 每轮在传给工具执行的 context 上设一次, 故 一个 engine 上的并发 Run 各带自己的 id, 无共享实例 race -- 是共享 SetMessageID 字段的 race-free 且正确的替代. 空 id 为 no-op (消费者回落到 SetMessageID 字段, 或 零值键 "").
func WithWorkdir ¶
WithWorkdir 派生一个携带 dir 的 context, 供 cwd 感知工具在 Execute 开头 读取. dir 空串视为 "无覆盖" — 直接返回原 ctx 不污染调用链.
WithWorkdir derives a context carrying dir, to be read by cwd-aware tools at Execute start. An empty dir is treated as "no override" — the original ctx is returned unchanged to keep the call chain clean.
func WorkdirFromContext ¶
WorkdirFromContext 返回 WithWorkdir 设置的 cwd, 未设置返回 "". 工具实现 模式: cwd := WorkdirFromContext(ctx); if cwd == "" { cwd = t.cwd }.
WorkdirFromContext returns the cwd set by WithWorkdir, or "" if absent. Tool implementation pattern: cwd := WorkdirFromContext(ctx); if cwd == "" { cwd = t.cwd }.
Types ¶
type CapabilityProvider ¶
type CapabilityProvider interface {
Capability() ToolCapability
}
CapabilityProvider 工具能力声明(可选接口). 精妙之处(CLEVER): 和 MetadataProvider 对称设计-- Metadata 描述"工具是什么",Capability 描述"工具能做什么安全措施".
Shape: pull. Engine / audit UI reads Capability() to learn which resources the tool affects, whether undo is supported, and the minimum confidence required.
形态: 调取 (pull). 引擎 / 审计 UI 读 Capability() 得知工具影响哪些资源 / 是否支持撤销 / 最低置信度要求.
type DeferredRegistry ¶
type DeferredRegistry struct {
// contains filtered or unexported fields
}
DeferredRegistry 包装 Registry,支持延迟加载. 当注册的工具数量超过阈值时,非核心工具会被延迟加载.
func NewDeferredRegistry ¶
func NewDeferredRegistry(registry *Registry, threshold int) *DeferredRegistry
NewDeferredRegistry 创建一个支持延迟加载的注册表包装器.
registry 是底层工具注册表,threshold 为启用延迟加载的工具数量阈值. threshold <= 0 使用默认值(15).
func (*DeferredRegistry) ActivateTool ¶
func (dr *DeferredRegistry) ActivateTool(name string)
ActivateTool 将一个延迟工具激活. 激活后的工具在后续的 ActiveTools 调用中会被包含.
func (*DeferredRegistry) ActiveTools ¶
func (dr *DeferredRegistry) ActiveTools() []Tool
ActiveTools 返回当前活跃的工具列表.
如果工具总数不超过阈值,返回所有工具. 否则只返回始终加载的工具 + 已激活的延迟工具.
func (*DeferredRegistry) IsDeferred ¶
func (dr *DeferredRegistry) IsDeferred(name string) bool
IsDeferred 检查指定工具是否处于延迟加载状态.
返回 true 表示该工具存在但未激活(即延迟中). 如果工具不存在或已激活,返回 false.
func (*DeferredRegistry) Registry ¶
func (dr *DeferredRegistry) Registry() *Registry
Registry 返回底层注册表.
func (*DeferredRegistry) ResetActivations ¶
func (dr *DeferredRegistry) ResetActivations()
ResetActivations 重置所有已激活的延迟工具. 通常在新会话开始时调用.
func (*DeferredRegistry) SearchTools ¶
func (dr *DeferredRegistry) SearchTools(query string) []Tool
SearchTools 搜索延迟加载的工具.
根据查询字符串匹配工具名称,描述和搜索提示词. 返回匹配的工具列表(不包含已激活和始终加载的工具).
func (*DeferredRegistry) Threshold ¶
func (dr *DeferredRegistry) Threshold() int
Threshold 返回当前延迟加载阈值.
func (*DeferredRegistry) WithAlwaysLoad ¶
func (dr *DeferredRegistry) WithAlwaysLoad(names ...string) *DeferredRegistry
WithAlwaysLoad 将额外的工具名加入当前 DeferredRegistry 实例的始终加载列表.
升华改进(ELEVATED): 实例级核心工具定制-- 不同租户(仓储/医疗/编程)各自的"核心工具"不同,通过此方法按实例注入, 不污染其他实例也不修改全局状态. 替代方案(原方案):只有包级 RegisterDeferredTool(影响全局,不支持多租户隔离).
返回 *DeferredRegistry 自身,支持链式调用.
type DryRunResult ¶
type DryRunResult struct {
// WouldAffect is a human-readable description of what will be
// affected (e.g. file path, table name, endpoint URL). Consumed by
// preview UI to show "this will touch X" before approval.
//
// WouldAffect 是会影响什么的人类可读描述 (如文件路径 / 表名 /
// endpoint URL). 预览 UI 消费用于批准前展示 "即将影响 X".
WouldAffect string
// Preview is the detailed preview payload (e.g. unified diff, change
// summary, SQL plan). Shown to the human operator / presented to the
// LLM in an approval loop.
//
// Preview 是详细预览载荷 (如 unified diff / 变更摘要 / SQL plan).
// 展示给人类操作员 / 在审批循环中展示给 LLM.
Preview string
// EstimatedImpact is a structured extension slot (map[string]any) for
// consumer-specific impact metrics -- row counts, byte deltas,
// estimated cost, etc. Schema is consumer-defined; engine does not
// interpret it. Similar extension shape as ShadowResult.Meta and
// ReplayEvent.Meta.
//
// EstimatedImpact 是结构化扩展槽 (map[string]any), 给消费端特定的
// 影响指标 -- 行数 / byte 增量 / 预估成本等. schema 由消费端定义,
// 引擎不解读. 扩展形状同 ShadowResult.Meta / ReplayEvent.Meta.
EstimatedImpact map[string]any
}
DryRunResult is the return shape of DryRunnable.DryRun. All three fields are external pull-API surface: engine / preview UI / safety dashboard call DryRun and read them directly. Engine does not internally read these fields (no in-tree consumer), which is expected -- DryRun is the pre-execution preview contract, not an internal wire.
DryRunResult 是 DryRunnable.DryRun 的返回形状. 三个字段均为外部调取 (pull) API 表面: 引擎 / 预览 UI / 安全面板调 DryRun 后直接读. 引擎内部 不读这些字段 (无 in-tree consumer), 属预期行为 -- DryRun 是预执行 预览契约, 不走内部 wire.
type DryRunnable ¶
type DryRunnable interface {
// DryRun 模拟执行,返回"会发生什么"但不真正执行
DryRun(ctx context.Context, input json.RawMessage) (*DryRunResult, error)
}
DryRunnable 支持模拟执行的工具(可选接口). 精妙之处(CLEVER): 用可选接口而非修改 Tool 接口,不破坏现有实现. 现有的 10+ 个工具都不需要改动,只有需要 DryRun 能力的工具才实现这个接口.
Shape: synchronous callback. Engine / preview UI calls DryRun to get a preview of effects without executing; tool author implements to compute diff / impact without side effects.
形态: 同步回调. 引擎 / 预览 UI 调 DryRun 得到效果预览而不执行; 工具作者 实现此方法计算 diff / 影响范围而不产生副作用.
type MessageIDAware ¶
type MessageIDAware interface {
// SetMessageID sets the current turn id used to key per-turn side
// effects. Called by the engine at the start of every turn.
//
// LEGACY / fallback only: storing the turn id on a SHARED tool instance
// races when one engine runs concurrent Runs (e.g. platform serves
// /agent/run + the plan queue on one engine), because each turn's
// SetMessageID WRITE overlaps another Run's Execute READ of the same field
// -- a data race that also corrupts file-history snapshot keys. The engine
// now threads the turn id through the per-call context (WithMessageID); a
// tool should prefer MessageIDFromContext(ctx) and treat the field set here
// as a fallback for direct callers that do not thread a context. Implementing
// this interface stays optional and backward compatible.
//
// 仅 LEGACY / fallback: 把轮次 id 存在 SHARED 工具实例上, 当一个 engine 跑并发
// Run 时会 race (如平台在一个 engine 上同时服务 /agent/run + 计划队列), 因为每轮
// SetMessageID 的 WRITE 与另一个 Run 的 Execute 对同一字段的 READ 重叠 -- 既是
// data race 又破坏 file-history 快照键. engine 现在经 per-call context 透传轮次
// id (WithMessageID); 工具应优先 MessageIDFromContext(ctx), 把这里设的字段当作
// 不透传 context 的直接调用者的 fallback. 实现本接口仍是可选且向后兼容.
SetMessageID(id string)
}
MessageIDAware is an optional interface implemented by tools that key their per-turn side effects (notably file-history snapshots) by the current turn id. The engine threads the SAME id the OperationLog records (turnMessageID, "turn-N") at the start of every turn, so a later Engine.Rollback(id) addresses the file snapshot AND the operation-log entry under one consistent key. Without this wiring the snapshot key stays the zero value "" and rollback silently restores nothing.
Engine code locates implementors by iterating the tool registry and type-asserting (mirrors SocketAware), so the wiring is decoupled from the tool names "Edit" / "Write" (ADR-0005 § Bug Q discipline).
MessageIDAware 是可选接口, 由 "按当前轮次 id 给每轮副作用 (主要是 file-history 快照) 打键" 的工具实现. 引擎在每轮开始把与 OperationLog 记录相同的 id (turnMessageID, "turn-N") 传入, 使随后的 Engine.Rollback(id) 用同一个键既找到文件快照又找到操作日志条目. 不接这条线时快照键停留在 零值 "" -> 回滚静默什么都不还原. engine 通过遍历注册表 + 类型断言定位 实现者 (对位 SocketAware), 把接线从工具名 "Edit" / "Write" 解耦 (ADR-0005 § Bug Q 纪律).
type Metadata ¶
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: <AuditObserver 构造时注入 operationMapper func> - 否决:
// 消费者要事先知道所有工具名, 插件动态注册场景不适用.
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
}
Metadata 是工具的元数据声明. 工具可以选择实现 MetadataProvider 接口来声明这些属性. 如果不实现,编排器使用保守默认值(不可并发,非只读,非破坏性).
对应原项目中 isConcurrencySafe(),isReadOnly(),isDestructive() 等方法.
func GetMetadata ¶
GetMetadata 安全获取工具的元数据. 如果工具未实现 MetadataProvider,返回保守默认值.
type MetadataProvider ¶
type MetadataProvider interface {
Metadata() Metadata
}
MetadataProvider 是工具的可选接口. 实现此接口的工具可以声明并发安全性等元数据.
Shape: pull. Engine reads Metadata() at orchestration decisions (can run in parallel? is destructive? requires checkpoint?).
形态: 调取 (pull). 引擎在编排决策时读 Metadata() (能否并发? 是否破坏性? 要不要 checkpoint?).
type Orchestrator ¶
type Orchestrator struct {
// contains filtered or unexported fields
}
Orchestrator 负责工具调用的并发调度.
func NewOrchestrator ¶
func NewOrchestrator(registry *Registry, maxConcurrency int) *Orchestrator
NewOrchestrator 创建一个工具编排器.
func NewOrchestratorWithResultProcessor ¶
func NewOrchestratorWithResultProcessor(registry *Registry, maxConcurrency int, processor ResultProcessor) *Orchestrator
NewOrchestratorWithResultProcessor 创建一个带大结果处理器的工具编排器. processor 为 nil 时退化为无处理行为,向后兼容.
func (*Orchestrator) ExecuteBatch ¶
func (o *Orchestrator) ExecuteBatch( ctx context.Context, calls []ToolCall, results chan<- ToolCallResult, )
ExecuteBatch 执行一批工具调用. 自动根据工具的并发安全性分组,安全的并行执行,不安全的串行执行.
对应原项目中 runTools() 异步生成器. 原项目用 yield 逐个返回结果,Go 版本通过 results channel 推送.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry 是工具注册表. 对应原项目中 assembleToolPool() 和 tools 数组的功能, 但作为独立的,线程安全的数据结构存在.
原项目的工具池在每次 getCommands() 时重新组装(带 memoize 缓存), 且内置工具和 MCP 工具的合并逻辑散落在多处. Go 版本统一用 Registry 管理,支持运行时动态注册/注销.
func (*Registry) MetadataFor ¶
MetadataFor 按名称查询工具的 Metadata (支持别名). 返回 (Metadata, true) 表示工具存在; (Metadata{}, false) 表示未注册.
工具未实现 MetadataProvider 时 GetMetadata 返回零值 Metadata, 调用方据此判断"已注册但未声明元数据".
升华改进(ELEVATED): 与 Get() 对称, 但只暴露元数据不暴露 Tool 实例. 用于跨切面关注 (audit / permission / policy) 查询语义标签 - AuditObserver 据此决定 operationFromTool, 不再硬编码 switch.
type Result ¶
type Result struct {
// Output 是工具的文本输出.
Output string
// IsError 标记输出是否为错误信息.
IsError bool
// Data 是结构化数据输出(可选).
// 用于工具间传递结构化信息.
Data any
// UndoInfo 撤销信息(由 Reversible 工具填充).
// 升华改进(ELEVATED): 撤销信息嵌入 Result 而非单独返回--
// 这样编排器在收集工具结果时自然就拿到了撤销信息,不需要额外调用.
// 替代方案:在 Orchestrator 中单独调用 GenerateUndo(多一次调用,时序更复杂).
UndoInfo *UndoInfo
}
Result 是工具执行的结果.
type ResultProcessor ¶
type ResultProcessor interface {
ProcessResult(toolUseID, toolName, output string) (processedOutput string, storedPath string)
}
ResultProcessor 是大结果处理的接口. Orchestrator 通过此接口处理工具输出,避免直接依赖 engine 包.
type Reversible ¶
type Reversible interface {
// GenerateUndo 基于执行结果生成撤销信息
GenerateUndo(ctx context.Context, input json.RawMessage, result *Result) (*UndoInfo, error)
}
Reversible 可撤销的工具(可选接口). 精妙之处(CLEVER): Saga 补偿模式--每个正向操作生成对应的补偿操作. 回滚时倒序执行所有补偿操作,和分布式事务的 Saga 模式一样.
Shape: synchronous callback. Engine calls GenerateUndo synchronously after execution to capture an undo record; later the engine can replay the undo to revert.
形态: 同步回调. 引擎在执行后同步调 GenerateUndo 拿撤销记录; 之后引擎 可回放撤销做回退.
type SocketAware ¶
type SocketAware interface {
// SetSessionSockPath sets the path to the session UDS socket. Called by
// engine when the Inbox UDS server starts.
// 设 session UDS socket 路径. engine 启动 Inbox UDS server 时调用.
SetSessionSockPath(path string)
// SetPlanSockPath sets the path to the plan-queue UDS socket. Called by
// engine when PlanCommandServer starts.
// 设 plan-queue UDS socket 路径. engine 启动 PlanCommandServer 时调用.
SetPlanSockPath(path string)
}
SocketAware is an optional interface implemented by tools that need the engine to wire UDS (Unix Domain Socket) paths — e.g. BashTool receives session/plan socket paths via SetSessionSockPath / SetPlanSockPath. Engine code locates implementors by iterating the tool registry and type-asserting, instead of hardcoding the tool name "Bash".
SocketAware 是可选接口, 由需要引擎接 UDS (Unix Domain Socket) 路径的工具 实现 — 例如 BashTool 通过 SetSessionSockPath / SetPlanSockPath 接收 session/plan socket 路径. engine 代码通过遍历工具注册表 + 类型断言定位 实现者, 不再硬编工具名 "Bash".
See ADR-0005 § Bug Q. Pre-ADR-0005 the engine called `e.tools.Get("Bash")` then type-asserted *builtin.BashTool. That blocked any cfg.Toolset configuration like "I want only my custom tool, no Bash" — the UDS init paths would silently no-op or panic depending on assertion result. SocketAware decouples the requirement from the tool name string.
ADR-0005 § Bug Q. ADR-0005 之前 engine 调 `e.tools.Get("Bash")` 然后 类型断言 *builtin.BashTool. 这阻塞了 "我只要自定义工具不要 Bash" 这种 cfg.Toolset 配置 — UDS init 路径会因断言结果静默 no-op 或 panic. SocketAware 把"需求"从"工具名字符串"解耦.
type Tool ¶
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)
}
Tool 是所有工具必须实现的接口. 对应原项目中 Tool<Input, Output, P> 泛型类型.
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 ToolCall ¶
type ToolCall struct {
ID string // API 生成的工具调用 ID
Name string // 工具名称
Input json.RawMessage // 工具输入参数(JSON)
}
ToolCall 表示模型发起的一次工具调用请求.
type ToolCallResult ¶
type ToolCallResult struct {
ID string // 对应的工具调用 ID
Name string // 工具名称
Output string // 工具输出(可能被截断, 见 Truncated)
IsError bool // 是否出错
// Truncated marks that Output is a short summary and the full result
// has been persisted to StoredPath.
//
// Truncated 标记 Output 是短摘要, 完整结果已落盘到 StoredPath.
Truncated bool
// StoredPath is where the ResultProcessor persisted the full output.
// Empty when Truncated is false. Path shape is consumer-defined
// (local path / sandbox path / object-storage key).
//
// SECURITY: path may carry sandbox-internal structure. Downstream
// observers / SSE bridges treat it as caller-specific, untrusted data
// (e.g. do not log to shared destinations without review). Same
// constraint applies in flyto.ToolResultEvent.StoredPath and
// OperationEntry.StoredPath; all three share one trust boundary.
//
// StoredPath 是 ResultProcessor 落盘完整输出的位置. Truncated=false 时
// 为空串. Path shape 由消费层定义 (本地路径 / sandbox 路径 / 对象存储
// key).
//
// 安全: path 可能带 sandbox 内部结构. 下游 observer / SSE bridge 当作
// 调用方特定的不可信数据对待 (未审查前不写入共享目的地). 此约束同样
// 适用于 flyto.ToolResultEvent.StoredPath 和 OperationEntry.StoredPath,
// 三者共享同一条信任边界.
StoredPath string
UndoInfo *UndoInfo // 撤销信息(由 Reversible 工具生成)
// Data is the structured payload that the tool's Result.Data carries
// through to the engine. Primary consumer (2026-04): engine wraps an
// *tools/builtin.ImageResult into an array-form tool_result so vision
// models see the real image bytes (see engine.go tool_result
// construction). Future non-image structured payloads (e.g. document,
// chart) can plug in without another field.
//
// Orchestrator preserves the reference verbatim -- no copy, no
// serialization. Consumers MUST type-assert (`if img, ok := res.Data.(*ImageResult); ok`)
// and handle the nil case.
//
// Data 是工具 Result.Data 透传到引擎的结构化载荷. 主消费者 (2026-04):
// 引擎把 *tools/builtin.ImageResult 包成 array-form tool_result 让 vision
// 模型真读到图 (见 engine.go tool_result 构造点). 未来非图结构化载荷
// (文档 / 图表 / 结构化表格) 可直接沿用, 不用再加字段.
//
// Orchestrator 原样透传引用 -- 不拷贝不序列化. 消费方必须 type-assert
// (`if img, ok := res.Data.(*ImageResult); ok`) 并处理 nil 情况.
Data any
}
ToolCallResult 表示一次工具调用的结果.
Truncated / StoredPath surface the large-result handling decision so downstream consumers (engine → flyto.ToolResultEvent / OperationEntry / SSE bridge → UI / audit sink) can honor the "click to open full content" affordance and keep audit logs pointing at the authoritative artifact. When the orchestrator's pluggable ResultProcessor rewrites Output to a short summary, these fields are the only breadcrumb back to the full result.
Truncated / StoredPath 把大结果处理决策透传给下游消费者 (engine → flyto.ToolResultEvent / OperationEntry / SSE bridge → UI / 审计 sink), 让消费者能兑现 "点击查看完整内容" 入口并让审计日志指向权威产物. 当 orchestrator 的可插拔 ResultProcessor 把 Output 重写成短摘要时, 这两个 字段是回溯完整结果的唯一线索.
type ToolCapability ¶
type ToolCapability struct {
// DryRun 是否支持模拟执行(不产生副作用)
DryRun bool
// Reversible 执行后是否可撤销
Reversible bool
// UndoMethod is an informational static declaration of how this tool
// can be undone: "tool" / "manual" / "" (irreversible). This is a
// surface for external audit UI / tool panels to show rollback
// category before execution; the engine's actual rollback path reads
// UndoInfo.ToolName (dynamic, captured by Reversible.GenerateUndo at
// execute time), not this field. Static vs dynamic are complementary,
// not redundant: static lets the UI render "this tool is reversible"
// without running; dynamic carries the concrete undo input per call.
//
// UndoMethod 是工具如何被撤销的 informational 静态声明: "tool" / "manual"
// / "" (不可逆). 此字段面向外部审计 UI / 工具面板, 让其在执行前就能
// 展示回滚分类; 引擎真实 rollback 路径读 UndoInfo.ToolName (动态, 由
// Reversible.GenerateUndo 执行时捕获), 不读本字段. 静态和动态互补非
// 冗余: 静态让 UI 不跑也能渲染 "此工具可撤销"; 动态携带每次调用的
// 具体撤销参数.
UndoMethod string
// UndoToolName is the informational static name of the undo tool
// when UndoMethod="tool". Same static-vs-dynamic split as UndoMethod:
// external UI / audit consumers read this field to show "undo is
// handled by tool X" pre-execution; the engine dispatches undo via
// UndoInfo.ToolName (dynamic, same value in practice but sourced
// per-call by GenerateUndo).
//
// UndoToolName 是 UndoMethod="tool" 时撤销工具的 informational 静态名.
// 和 UndoMethod 同构的静态-动态分层: 外部 UI / 审计消费者读本字段
// 在执行前展示 "由工具 X 撤销"; 引擎执行 undo 时派发 UndoInfo.ToolName
// (动态值, 通常同值但按次由 GenerateUndo 提供).
UndoToolName string
// MinConfidence is the minimum self-reported confidence the engine
// requires before dispatching this tool (0-100). Zero disables the
// gate entirely (all calls pass).
//
// Wire (2026-04-21):
// - engine.buildToolDefs appends a "[Safety] requires _flyto_confidence
// in input, min=N" hint to Description for tools with MinConfidence>0,
// so the LLM sees the contract in the tool definition itself.
// - orchestrator.executeSingle calls CheckConfidenceGate before
// Execute. The gate parses the `_flyto_confidence` reserved field
// from input JSON, rejects calls below the threshold with a
// tool-level error (letting the LLM retry with a reconsidered
// value), and strips the field before handing input to the tool
// (so tool InputSchema stays clean).
// - Missing _flyto_confidence when MinConfidence>0 is treated as
// gate-fail (not implicit 100) -- LLM must explicitly self-assess
// for high-risk tools.
//
// MinConfidence 是引擎派发本工具前要求的最低自评置信度 (0-100). 零值
// 完全 disable gate (所有调用放行).
//
// Wire (2026-04-21):
// - engine.buildToolDefs 对 MinConfidence>0 的工具, 在 Description 末
// 尾追加 "[Safety] 需要在 input 中附 _flyto_confidence, 最低 N" 提示,
// 让 LLM 在工具定义层就看到契约.
// - orchestrator.executeSingle 在 Execute 前调 CheckConfidenceGate.
// Gate 从 input JSON 解析保留字段 `_flyto_confidence`, 低于阈值返工具
// 级错误 (让 LLM 重新评估), 放行前剥除该字段 (保持工具 InputSchema
// 干净).
// - MinConfidence>0 而 _flyto_confidence 缺失视为 gate-fail (非隐式 100)
// -- LLM 必须为高风险工具显式自评.
MinConfidence int
// AffectedResources is a human-readable list of resource categories
// the tool touches (e.g. ["file"], ["database","network"]). Pure
// informational surface for tool panels / audit UI to classify tools
// by blast radius. Engine does not read this field; consumers walk
// CapabilityProvider externally.
//
// AffectedResources 是工具触及的资源类别人类可读列表 (如 ["file"] /
// ["database","network"]). 纯 informational, 给工具面板 / 审计 UI 按
// 影响范围分类. 引擎不读本字段, 外部消费者走 CapabilityProvider 调取.
AffectedResources []string
}
ToolCapability 声明工具的安全能力.
func GetCapability ¶
func GetCapability(t Tool) ToolCapability
GetCapability 安全获取工具能力. 如果工具未实现 CapabilityProvider,返回零值(不支持 DryRun,不可逆).
func (ToolCapability) SafetyLevel ¶
func (c ToolCapability) SafetyLevel() int
SafetyLevel 返回工具的安全等级. Level 2: DryRun(最安全),Level 1: Reversible,Level 0: 两者都不支持.
type ToolPolicy ¶
type ToolPolicy interface {
// Resolve returns the tools the engine should register, in the order
// the model should see them. Empty slice or nil = no tools.
//
// Resolve 返回 engine 该注册的工具, 按模型看到的顺序. 空切片或 nil = 无工具.
Resolve() []Tool
}
ToolPolicy declares which tools the engine should register. Replaces the legacy `cfg.Tools []string` (allowlist-by-name) + `cfg.ExtraTools []Tool` (always-additive on top of builtins) pair, which together had no way to express "only this one tool, no builtins" — see ADR-0005 § Bug A + B.
ToolPolicy 声明引擎该注册哪些工具. 替代旧 `cfg.Tools []string` (按名字白名单) + `cfg.ExtraTools []Tool` (在 builtin 之上始终叠加) 这对 字段 — 二者合起来无法表达 "只要这一个工具不要 builtin" — 见 ADR-0005 § Bug A + B.
Three constructors cover the design space:
None() — empty registry; the model gets zero tools.
Allowlist(t...) — exactly these tools, in this order; no builtins
auto-injected. Caller imports core/pkg/tools/builtin
to get individual builtins, or core/extra/preset-coding
AllBuiltins helper (C12+) for the cc 23-tool slice.
(future) PolicyFunc — programmatic decision; reserved for cases where
the toolset depends on Run-time context. Not in C6.
三个构造函数覆盖设计空间:
None() — 空注册表; 模型零工具. Allowlist(t...) — 仅这些工具按此顺序, 不自动注入 builtin. caller 通过 core/pkg/tools/builtin 拿单个 builtin, 或 (C12+) core/extra/preset-coding 的 AllBuiltins helper 拿 cc 23 工具切片. (未来) PolicyFunc — 程序化决策; 给"toolset 依赖 Run-time 上下文"的 场景预留. C6 不做.
Aligns with Go database/sql (driver must Register explicitly, no default driver) and Express middleware (no default body parser, opt in body parser/cors/etc.). LangChain core does the same: BaseChatModel has no default tools=.
对齐 Go database/sql (driver 必须显式 Register, 无默认 driver) 和 Express middleware (无默认 body parser, body parser/cors 等显式开启). LangChain core 同样: BaseChatModel 无默认 tools=.
See ADR-0005 § 1.2 for the survey and § 2.3 for the design rationale.
业界对照见 ADR-0005 § 1.2, 设计依据见 § 2.3.
func Allowlist ¶
func Allowlist(t ...Tool) ToolPolicy
Allowlist returns a ToolPolicy that resolves to exactly the given tools, in the given order. Pass zero arguments for the same effect as None().
Allowlist 返回的 ToolPolicy 解析为给定工具按给定顺序. 零参数等价 None().
CLEVER: order matters — the model sees tools in this order in the system prompt's tool list (when it lives there) and in the API tools field. Some models bias toward earlier-listed tools.
CLEVER: 顺序很关键 — 模型在 system prompt 的工具列表 (放那时) 和 API tools 字段中按此顺序看到工具. 某些模型偏好排前面的工具.
func None ¶
func None() ToolPolicy
None returns a ToolPolicy that resolves to an empty tool list.
None 返回空工具列表的 ToolPolicy.
type UndoInfo ¶
type UndoInfo struct {
// ToolName 用哪个工具撤销
ToolName string
// Input 撤销工具的输入
Input map[string]any
// Description is a human-readable summary of the undo action shown to
// external operators (rollback UI / audit logs). The engine does not
// read this field during RollbackMessage -- it is purely a consumer
// surface.
//
// Description 是向外部操作员展示的撤销动作人类可读摘要 (rollback UI /
// 审计日志). 引擎在 RollbackMessage 过程中不读本字段 -- 纯消费者表面.
Description string
// Irreversible 标记为不可逆(某些操作执行后无法撤销)
Irreversible bool
// ManualGuide 不可逆时的人工处理指南
ManualGuide string
}
UndoInfo is the dynamic undo record captured by Reversible.GenerateUndo at execute time. ToolName / Input / Irreversible / ManualGuide are read by the engine's rollback path (OperationLog.RollbackMessage → UndoExecutor.ExecuteUndo). Description is an external pull-API field: external rollback UI / audit sinks render it to the operator; no internal assert site in the engine itself.
UndoInfo 是 Reversible.GenerateUndo 在执行时捕获的动态撤销记录. ToolName / Input / Irreversible / ManualGuide 由引擎 rollback 路径 (OperationLog.RollbackMessage → UndoExecutor.ExecuteUndo) 读取. Description 是外部调取 (pull) API 字段: 外部 rollback UI / 审计 sink 渲染给操作员, 引擎内部无断言点.