diff --git a/docs/architecture/core-decomposition.md b/docs/architecture/core-decomposition.md index 4baed4b2d..a7c91f750 100644 --- a/docs/architecture/core-decomposition.md +++ b/docs/architecture/core-decomposition.md @@ -66,7 +66,7 @@ Rust 编译和链接面。 | `bitfun-agent-stream` | Stream 聚合和 stream-focused 测试 | done:stream 聚合已独立 | | `bitfun-runtime-ports` | 面向 service/agent 边界的轻量跨层 DTO 和 trait | partial:DTO/trait-only 边界已建立,包含 agent submission/transcript/cancel、remote state、runtime event 与 remote image attachment 契约;不拥有 runtime 实现 | | `bitfun-agent-runtime` | Sessions、execution、coordination、agent system | target:crate 尚不存在,agent runtime 仍在 core | -| `bitfun-agent-tools` | 轻量 tool DTO / contract、portable tool context facts / provider、runtime restriction、pure manifest/exposure and GetToolSpec presentation/schema/static metadata/detail/result assembly / execution-plan contract、provider-backed GetToolSpec execution result helper、generic contextual manifest resolver、generic catalog snapshot provider / GetToolSpec catalog provider、generic registry / static-provider / dynamic-provider / decorator-ref / snapshot-decorator adapter / runtime assembly container、generic readonly/enabled snapshot filter | partial:product registry snapshot access、`ToolUseContext` adapter、`GetToolSpec` Tool impl 和 concrete tools 仍在 core;core 当前把内置工具列表保留为 core-owned static provider group,但 static-provider 安装 assembly、decorator reference、generic snapshot decorator adapter 与 readonly/enabled 过滤规则已委托给 `bitfun-agent-tools` | +| `bitfun-agent-tools` | 轻量 tool DTO / contract、portable tool context facts / provider、runtime restriction、pure manifest/exposure and GetToolSpec presentation/schema/static metadata/detail/result assembly / execution-plan contract、provider-backed tool catalog / GetToolSpec runtime facade、provider-backed GetToolSpec execution result helper、generic contextual manifest resolver、generic catalog snapshot provider / GetToolSpec catalog provider、generic registry / static-provider / dynamic-provider / decorator-ref / snapshot-decorator adapter / runtime assembly container、generic readonly/enabled snapshot filter | partial:product registry snapshot access、`ToolUseContext` adapter、`GetToolSpec` Tool impl 和 concrete tools 仍在 core;core 当前把内置工具列表保留为 core-owned static provider group,但 static-provider 安装 assembly、decorator reference、generic snapshot decorator adapter、provider-backed catalog runtime facade 与 readonly/enabled 过滤规则已委托给 `bitfun-agent-tools` | | `bitfun-tool-packs` | 由 feature group 隔离的具体工具实现 | target/scaffold:仅提供 basic / git / mcp / browser-web / computer-use / image-analysis / miniapp / agent-control feature-group 元数据,不得声明 concrete tools 已迁移 | | `bitfun-services-core` | Config、session、workspace、storage、filesystem、system services | partial:部分 pure helper 已迁出;config/workspace/filesystem runtime 多数仍在 core | | `bitfun-services-integrations` | Git、MCP、remote SSH、remote connect、file watch integrations | partial:MCP runtime 已迁入;remote SSH 仍只迁移低风险 contracts/helpers;remote-connect 已拥有 wire DTO、request builder、tracker state / registry lifecycle 与 tracker event reduction,dispatcher/product execution 仍在 core | @@ -137,7 +137,7 @@ owner 边界,否则不要把一个 feature group 继续拆成更小的 crate 在此之前该 hook 仍属于 `bitfun-core` agent runtime assembly。 - 最新主干新增 on-demand tool spec discovery。`ToolExposure`、`GetToolSpec` 名称、 collapsed stub、manifest ordering、generic collapsed exposure query、generic contextual - prompt-manifest resolver、generic catalog snapshot provider、GetToolSpec catalog provider / + prompt-manifest resolver、generic catalog snapshot provider、ToolCatalogRuntime / GetToolSpec catalog provider / prompt / schema / assistant-detail rendering / detail JSON 等 provider-neutral 契约可由 `bitfun-agent-tools` 拥有;但产品 registry snapshot、`dyn Tool` / `ToolUseContext` adapter、product collapsed-tool catalog、context-aware tool @@ -230,20 +230,25 @@ owner 边界,否则不要把一个 feature group 继续拆成更小的 crate 迁移;后续高风险队列只允许按 H1-H5 的单一 owner 主题推进: - H1:tool runtime owner 迁移。当前只完成迁移前/迁移中边界收敛:`bitfun-agent-tools` 承载 provider-neutral tool contract、generic registry/static/dynamic provider、 - contextual manifest resolver、GetToolSpec presentation/schema/static metadata/detail/ - result assembly / execution-plan helper、provider-backed execution result helper, + contextual manifest resolver、provider-backed tool catalog runtime facade、 + GetToolSpec presentation/schema/static tool surface/detail/ + result assembly / execution-plan helper、provider-backed runtime facade / execution result helper, generic decorator reference / snapshot-decorator adapter / static-provider runtime assembly container,以及 generic readonly/enabled registry snapshot filter; core 仍持有 `ToolUseContext`、runtime manifest assembly、`GetToolSpecTool` Tool impl、collapsed unlock state source、product snapshot wrapper adapter 与 concrete tools。 已合入 PR #803 把 core `Tool` 到 provider-neutral contract 的 adapter 收敛到 `tool_adapter.rs`,把 product catalog / manifest / GetToolSpec catalog-detail provider - 收敛到 `catalog_provider.rs`;本阶段把 provider-neutral GetToolSpec static metadata / - tool-use message / execution plan / result assembly 与 provider-backed execution result helper 收敛到 - `bitfun-agent-tools`;本阶段继续把 static-provider 安装 assembly 委托到 + 收敛到 `catalog_provider.rs`;本阶段把 provider-neutral GetToolSpec static tool surface + (name / description / schema / readonly / concurrency / permission / validation / tool-use message)、 + execution plan / result assembly 与 provider-backed execution result helper 收敛到 + `bitfun-agent-tools`;本阶段继续把 provider-backed visible-tools / manifest / readonly + catalog 查询收敛到 `ToolCatalogRuntime`,core 只保留 product registry snapshot、agent + policy、`dyn Tool` / `ToolUseContext` adapter;本阶段继续把 static-provider 安装 assembly 委托到 `ToolRuntimeAssembly`,core 只保留 concrete provider groups、product snapshot wrapper - adapter、product provider/context 注入和错误映射;本阶段也把 decorator reference - contract、generic snapshot decorator adapter 与 readonly enabled filtering 的通用规则委托给 + adapter、product provider/context 注入、`GetToolSpecTool` Tool impl、unlock state source + 和错误映射;本阶段也把 decorator reference contract、generic snapshot decorator adapter、 + GetToolSpec runtime facade 与 readonly enabled filtering 的通用规则委托给 `bitfun-agent-tools`,不改变工具行为。 - H2:product-domain runtime owner 迁移。MiniApp / function-agent 的纯 DTO/helper/port facade 已外移,filesystem IO、worker process、host dispatch、built-in asset seeding / diff --git a/docs/plans/core-decomposition-plan.md b/docs/plans/core-decomposition-plan.md index 2d2363ba2..b03723fc9 100644 --- a/docs/plans/core-decomposition-plan.md +++ b/docs/plans/core-decomposition-plan.md @@ -1088,10 +1088,10 @@ pub fn create_tool_registry() -> ToolRegistry { **当前安全迁移状态(2026-05-20):** -- 已迁移到 `bitfun-agent-tools`:`ToolResult`、`ValidationResult`、`InputValidator`、dynamic tool metadata、tool render options、runtime restriction DTO、path resolution DTO、`ToolContextFacts` / `ToolWorkspaceKind` 轻量上下文事实、`PortableToolContextProvider` 只读 facts provider、不依赖 core service 的 `ToolRegistry` / `ToolRegistryItem` generic registry container、`StaticToolProvider` / `install_static_provider` 安装合约、generic decorator reference / snapshot decorator adapter / static-provider `ToolRuntimeAssembly` container、generic readonly/enabled registry snapshot filter、generic catalog snapshot provider、generic GetToolSpec catalog provider、registry snapshot 到 manifest policy input 的纯 helper、generic collapsed exposure 查询、`GetToolSpec` load observation 到 collapsed 工具名集合的纯收集 helper、prompt-visible manifest definition 的纯组装 helper、generic contextual prompt-manifest resolver,以及 `GetToolSpec` catalog / detail / static metadata / tool-use message / execution-plan / result assembly 的 provider-neutral 组装 helper 和 provider-backed execution result helper。dynamic tool provider / decorator contract 已通过 `agent-tools` 提供兼容 re-export,原 `runtime-ports` 路径保持可用;core 旧路径继续 re-export,并只保留 `BitFunError` 映射、路径 containment helper、`ToolUseContext` 到 facts 的只读投影和 runtime unlock state。 -- `bitfun-core::agentic::tools` 现在保留 core-owned product provider groups、`ProductToolRuntimeAssembly` product snapshot wrapper 注入、旧构造函数、`dyn Tool` 到 generic registry / contextual manifest / catalog snapshot provider / GetToolSpec catalog provider 的适配、`ToolUseContext` runtime handle / service owner,以及 product registry snapshot access / `GetToolSpecTool` Tool impl;dynamic metadata map、tool map、dynamic descriptor assembly、static provider 安装合约、generic decorator reference / snapshot decorator adapter / provider-install assembly、generic readonly/enabled filtering、portable context facts、纯 manifest/exposure 契约、generic catalog snapshot provider、generic contextual prompt-manifest resolver、GetToolSpec presentation/schema/detail/static metadata/tool-use message 纯 helper、provider-neutral execution-plan、provider-backed execution result helper 和 result assembly helper 由 `bitfun-agent-tools` 拥有。 +- 已迁移到 `bitfun-agent-tools`:`ToolResult`、`ValidationResult`、`InputValidator`、dynamic tool metadata、tool render options、runtime restriction DTO、path resolution DTO、`ToolContextFacts` / `ToolWorkspaceKind` 轻量上下文事实、`PortableToolContextProvider` 只读 facts provider、不依赖 core service 的 `ToolRegistry` / `ToolRegistryItem` generic registry container、`StaticToolProvider` / `install_static_provider` 安装合约、generic decorator reference / snapshot decorator adapter / static-provider `ToolRuntimeAssembly` container、generic readonly/enabled registry snapshot filter、generic catalog snapshot provider、generic GetToolSpec catalog provider、provider-backed `ToolCatalogRuntime`、registry snapshot 到 manifest policy input 的纯 helper、generic collapsed exposure 查询、`GetToolSpec` load observation 到 collapsed 工具名集合的纯收集 helper、prompt-visible manifest definition 的纯组装 helper、generic contextual prompt-manifest resolver,以及 `GetToolSpec` catalog / detail / static metadata / tool-use message / execution-plan / result assembly 的 provider-neutral 组装 helper、provider-backed execution result helper 和 runtime facade。dynamic tool provider / decorator contract 已通过 `agent-tools` 提供兼容 re-export,原 `runtime-ports` 路径保持可用;core 旧路径继续 re-export,并只保留 `BitFunError` 映射、路径 containment helper、`ToolUseContext` 到 facts 的只读投影和 runtime unlock state。 +- `bitfun-core::agentic::tools` 现在保留 core-owned product provider groups、`ProductToolRuntimeAssembly` product snapshot wrapper 注入、旧构造函数、`dyn Tool` 到 generic registry / catalog snapshot provider / GetToolSpec catalog provider 的适配、`ToolUseContext` runtime handle / service owner,以及 product registry snapshot access / agent policy / `GetToolSpecTool` Tool impl;dynamic metadata map、tool map、dynamic descriptor assembly、static provider 安装合约、generic decorator reference / snapshot decorator adapter / provider-install assembly、generic readonly/enabled filtering、portable context facts、纯 manifest/exposure 契约、generic catalog snapshot provider、generic contextual prompt-manifest resolver、provider-backed catalog runtime facade、GetToolSpec presentation/schema/detail/static metadata/tool-use message 纯 helper、provider-neutral execution-plan、provider-backed execution result helper、runtime facade 和 result assembly helper 由 `bitfun-agent-tools` 拥有。 - 已新增 `bitfun-tool-packs` feature scaffold,默认 feature 为空,`product-full` 只聚合 feature;当前只提供 `ToolPackFeatureGroup` / `all_feature_groups` / `enabled_feature_groups` 元数据,不注册或迁移任何工具实现。 -- 已通过 boundary check 锁定 `agent-tools` / `tool-packs` 暂不拥有 full product tool runtime assembly、`GetToolSpecTool` Tool impl、collapsed-tool unlock state owner 或 concrete tools;`tool-packs` 也不得拥有 manifest/exposure 契约。`agent-tools` 只允许拥有纯 manifest/exposure helper、generic catalog snapshot provider、generic GetToolSpec catalog provider、generic contextual manifest resolver、generic readonly/enabled filter、generic decorator reference / snapshot decorator adapter、GetToolSpec presentation/schema/detail/static metadata/tool-use message/execution-plan/result assembly helper、provider-backed execution result helper 和不依赖具体工具的 provider 安装 / runtime assembly 合约,core product tool runtime 继续负责产品 registry snapshot、concrete provider groups、product snapshot wrapper adapter、`dyn Tool` / `ToolUseContext` adapter、unlock state source 和执行路径。 +- 已通过 boundary check 锁定 `agent-tools` / `tool-packs` 暂不拥有 full product tool runtime assembly、`GetToolSpecTool` Tool impl、collapsed-tool unlock state owner 或 concrete tools;`tool-packs` 也不得拥有 manifest/exposure 契约。`agent-tools` 只允许拥有纯 manifest/exposure helper、generic catalog snapshot provider、generic GetToolSpec catalog provider、provider-backed `ToolCatalogRuntime`、generic contextual manifest resolver、generic readonly/enabled filter、generic decorator reference / snapshot decorator adapter、GetToolSpec presentation/schema/detail/static metadata/tool-use message/execution-plan/result assembly helper、provider-backed execution result helper / runtime facade 和不依赖具体工具的 provider 安装 / runtime assembly 合约,core product tool runtime 继续负责产品 registry snapshot、agent policy、concrete provider groups、product snapshot wrapper adapter、`dyn Tool` / `ToolUseContext` adapter、unlock state source 和执行路径。 - boundary check 也已补充 core owner anchor:要求产品工具注册、expanded/collapsed manifest、`GetToolSpec` duplicate-load guard、`ToolUseContext.unlocked_collapsed_tools`、执行管线 gating 与 execution unlock collector 仍保留在 core。后续若迁移这些 owner,必须先更新 port/provider 设计、等价测试与该脚本,而不能只删除 core 侧实现。 - `Tool` trait、`ToolUseContext` 和具体工具实现仍在 core;它们直接连接 workspace service、snapshot wrapper、computer-use host、cancellation token 与 Deep Review checkpoint hook。`ToolContextFacts` / `PortableToolContextProvider` 只能作为只读事实投影,继续迁移前必须先确认 service port 方案,并补工具清单等价性测试。 - 最新主干新增的 Deep Review shared-context / evidence-ledger checkpoint hook 仍保留在 core 的 `ToolUseContext` 中;在设计独立 tool context / event port 前,不应把 `ToolUseContext` 或 concrete tool implementation 继续外移。 @@ -1165,6 +1165,18 @@ pub fn create_tool_registry() -> ToolRegistry { `wrap_tool_for_snapshot_tracking`。该切片不迁移 snapshot runtime、修改类工具实现、 `ToolUseContext`、runtime manifest facade、collapsed unlock state 或 `GetToolSpecTool` Tool impl。 +- H1 GetToolSpec runtime facade slice(2026-05-20):将 provider-backed catalog + description 与 execution result 入口收敛为 `bitfun-agent-tools::GetToolSpecRuntime`; + core `catalog_provider.rs` 只负责 product provider / context / unlock-state 注入, + `GetToolSpecTool` 仍保留 Tool impl、`BitFunError` 映射和 assistant rendering 旧路径兼容。 + 该切片不迁移 `ToolUseContext`、runtime manifest facade、collapsed unlock state owner 或 + concrete tools。 +- H1 tool catalog runtime facade slice(2026-05-20):将 provider-backed visible-tools、 + prompt-visible manifest 和 readonly enabled catalog 查询收敛为 + `bitfun-agent-tools::ToolCatalogRuntime`;core `catalog_provider.rs` 只负责 product registry + snapshot、agent policy、`dyn Tool` / `ToolUseContext` adapter 和 product facade,`ToolRegistry` + 只通过 product facade 查询 readonly tools。该切片不迁移 `ToolUseContext`、`GetToolSpecTool` + Tool impl、collapsed unlock state、snapshot wrapper implementation 或 concrete tools。 **验证:** @@ -1654,7 +1666,7 @@ cargo check --workspace - 已完成中等粒度 owner crate 成型的安全部分:`bitfun-services-core`、`bitfun-services-integrations`、`bitfun-agent-tools`、`bitfun-tool-packs`、`bitfun-product-domains` 均已加入 workspace。 - 已迁移的模块均由 core facade re-export,未改变产品默认 feature、构建脚本或 release 脚本。 -- Git feature group 已闭环迁移到 `bitfun-services-integrations` 的 `git` feature:DTO/params/graph/raw command output/text parser/arg builder、`GitError`、`GitService` runtime implementation 与 git utils 均由 integrations owner crate 拥有,并通过 `bitfun-core::service::git::*` 保留旧路径兼容。`GitService` 所需的 Windows `libgit2` system-link 边界挂在该 crate 的 `git` feature 上;`bitfun-core` 仍因未迁移的 remote-connect runtime 保留其它 `git2` 使用。remote-ssh 本轮进一步外移 workspace path/identity 与 unresolved-session-key helper,并用 owner crate contract test 锁定 normalized path、mirror subpath、hostname sanitization、stable id 和 unresolved key 输出;PathManager-backed mirror root、global workspace registry、SSH manager/fs/terminal/runtime 仍留在 core。MCP PR2 已进一步外移 config service orchestration、server process / local-remote transport lifecycle、dynamic tool provider 与 context resource selection helper,core 旧路径继续做兼容 facade、core config store adapter、OAuth 数据目录注入与 `BitFunError` 映射。PR4 已将 generic tool registry / dynamic descriptor assembly 迁入 `bitfun-agent-tools`;后续进一步迁入纯 tool manifest/exposure 契约,本轮再迁入 static provider 安装合约,并把 core 内置工具列表收敛为 core-owned provider groups。当前 tool-runtime H6 已将 generic contextual manifest resolver、generic catalog snapshot provider、generic GetToolSpec catalog provider 与 GetToolSpec summary/detail helper 迁入 `bitfun-agent-tools`;core 继续负责 concrete tools、product snapshot wrapper adapter、`dyn Tool` / `ToolUseContext` 适配、product registry snapshot access 与 `GetToolSpec` 执行。 +- Git feature group 已闭环迁移到 `bitfun-services-integrations` 的 `git` feature:DTO/params/graph/raw command output/text parser/arg builder、`GitError`、`GitService` runtime implementation 与 git utils 均由 integrations owner crate 拥有,并通过 `bitfun-core::service::git::*` 保留旧路径兼容。`GitService` 所需的 Windows `libgit2` system-link 边界挂在该 crate 的 `git` feature 上;`bitfun-core` 仍因未迁移的 remote-connect runtime 保留其它 `git2` 使用。remote-ssh 本轮进一步外移 workspace path/identity 与 unresolved-session-key helper,并用 owner crate contract test 锁定 normalized path、mirror subpath、hostname sanitization、stable id 和 unresolved key 输出;PathManager-backed mirror root、global workspace registry、SSH manager/fs/terminal/runtime 仍留在 core。MCP PR2 已进一步外移 config service orchestration、server process / local-remote transport lifecycle、dynamic tool provider 与 context resource selection helper,core 旧路径继续做兼容 facade、core config store adapter、OAuth 数据目录注入与 `BitFunError` 映射。PR4 已将 generic tool registry / dynamic descriptor assembly 迁入 `bitfun-agent-tools`;后续进一步迁入纯 tool manifest/exposure 契约,本轮再迁入 static provider 安装合约,并把 core 内置工具列表收敛为 core-owned provider groups。当前 tool-runtime H6 已将 generic contextual manifest resolver、generic catalog snapshot provider、generic GetToolSpec catalog provider、provider-backed `ToolCatalogRuntime` 与 GetToolSpec summary/detail/runtime facade 迁入 `bitfun-agent-tools`;core 继续负责 concrete tools、product snapshot wrapper adapter、`dyn Tool` / `ToolUseContext` 适配、product registry snapshot access、agent policy 与 `GetToolSpec` 执行。 - 未声明完成的 P2/后续剩余部分:remote-ssh runtime、remote-connect 等重 service 迁移、`ToolUseContext` 外移、runtime manifest assembly / `GetToolSpec` 执行 owner 化、concrete tool implementation 迁移、product registry / provider assembly、miniapp/function-agent 运行逻辑迁移。这些会触碰 `PathManager`、`ToolUseContext`、workspace service、snapshot wrapper、prompt-visible tool catalog、`AgentSubmissionPort` 或 AI service 边界,需要在继续前显式确认。 - 本次 rebase 后重新核对最新主干 Deep Review capacity/cost/queue、context profile、evidence ledger 与 session manifest 变更:当前 PR 已完成 Git feature group 的 owner crate 归属迁移,但未改动这些 Deep Review 行为路径;后续迁移必须补端口设计和等价测试后再推进。 - 本次 rebase 后重新核对最新主干 tool 变更:on-demand tool spec discovery 新增 collapsed/expanded manifest、`GetToolSpec`、context-aware schema/description 与 unlock state。这不要求回退当前 P2 已完成内容,但要求后续 tool/provider 迁移先补 manifest / catalog / unlock 等价保护,且不得和 PR5 product-domain runtime 收口混合。 @@ -1741,7 +1753,7 @@ P2 后产品表面契约轨道(contract-only): - 文档校正:P2 后补充文档中的 MCP runtime step 已由本 PR2 闭环;后续 MCP 相关工作只保留 concrete tool implementation 迁移或 product registry / manifest assembly,不再重复迁移 config/process/transport lifecycle。 3. 已完成:remote-connect tracker / wire / pure policy owner slice:产品表面 DTO、remote command/response wire DTO、remote model catalog DTO、poll response assembly / model catalog poll delta、remote chat/image/tool/session wire DTO、relay/bot session/submission request builder、remote image attachment/request DTO、`AgentTurnCancellationPort`、`RemoteControlStatePort`、`RuntimeEventSink`、`RemoteSessionStateTracker`、`RemoteSessionTrackerRegistry`、`TrackerEvent`、legacy image context fallback / preference、restore target decision、cancel decision 与 remote file transfer size/chunk/name policy 已具备 owner/port 契约;core 仍保留 tracker host adapter、`ImageContextData` adapter、file IO/path resolution、dispatcher/product execution。 - 本轮收口:remote-connect 在当前批次以 tracker / wire / pure policy / registry lifecycle 归 owner crate、dispatcher / product execution 显式保留 core-owned 闭环;若未来继续迁移完整 dialog submission、terminal pre-warm、file IO/path resolution 或 `ImageContextData` adapter,必须另起 port/provider 设计与行为等价评审,不得混入 tool/provider owner 化。 -4. 已完成本轮可提交闭环:agent tools + `tool-packs` owner 化低风险部分。纯 tool contract/provider metadata、runtime restriction DTO、path resolution DTO、generic tool registry / static-provider / dynamic-provider container、generic catalog snapshot provider、generic GetToolSpec catalog provider、`PortableToolContextProvider` 只读 facts provider、纯 manifest/exposure 契约、generic contextual manifest resolver,以及 GetToolSpec presentation/schema/detail helper 已迁入 `bitfun-agent-tools`,并为 dynamic provider contract 提供 `agent-tools` 兼容 re-export;core tool runtime 保留 core-owned product provider groups、product snapshot wrapper adapter、`dyn Tool` / `ToolUseContext` 适配、product registry snapshot access 和 `GetToolSpec` 执行。`tool-packs` 当前只提供计划内 feature-group 元数据,不注册或迁移具体工具。`ToolUseContext`、`GetToolSpec` 执行与 concrete tool implementation 按 feature group 外移需要新的 service port/provider 设计,必须保持 builtin/readonly/dynamic manifest、expanded/collapsed exposure、prompt stub、unlock state、snapshot wrapping、runtime restrictions、cancellation 与 Deep Review tool flow 等价,作为后续高风险迁移单独审视。 +4. 已完成本轮可提交闭环:agent tools + `tool-packs` owner 化低风险部分。纯 tool contract/provider metadata、runtime restriction DTO、path resolution DTO、generic tool registry / static-provider / dynamic-provider container、generic catalog snapshot provider、generic GetToolSpec catalog provider、provider-backed `ToolCatalogRuntime`、`PortableToolContextProvider` 只读 facts provider、纯 manifest/exposure 契约、generic contextual manifest resolver,以及 GetToolSpec presentation/schema/detail/runtime facade helper 已迁入 `bitfun-agent-tools`,并为 dynamic provider contract 提供 `agent-tools` 兼容 re-export;core tool runtime 保留 core-owned product provider groups、product snapshot wrapper adapter、`dyn Tool` / `ToolUseContext` 适配、product registry snapshot access、agent policy 和 `GetToolSpec` 执行。`tool-packs` 当前只提供计划内 feature-group 元数据,不注册或迁移具体工具。`ToolUseContext`、`GetToolSpec` 执行与 concrete tool implementation 按 feature group 外移需要新的 service port/provider 设计,必须保持 builtin/readonly/dynamic manifest、expanded/collapsed exposure、prompt stub、unlock state、snapshot wrapping、runtime restrictions、cancellation 与 Deep Review tool flow 等价,作为后续高风险迁移单独审视。 5. `product-domains` runtime + core facade finalization:迁移 miniapp runtime/compiler/builtin 与 function-agent 运行逻辑,最后把 `bitfun-core` 收敛为 facade + product runtime assembly;不在本 PR 中修改 `bitfun-core default = []` 或 per-product feature matrix。 `bitfun-core default = []`、per-product feature set、构建矩阵和 release 能力调整仍作为重构完成后的独立评估,不计入上述 5 个 PR。 @@ -1902,11 +1914,12 @@ git diff -- package.json scripts/dev.cjs scripts/desktop-tauri-build.mjs scripts 23. 已合入:`GetToolSpec` unlock state guard。execution 侧只接受成功的 `GetToolSpec` 结果、只输出 collapsed 白名单工具,并保持去重和过滤语义;`GetToolSpecTool` 执行、runtime manifest assembly、`ToolUseContext` 和 concrete tools 不迁移。 24. 已合入:contextual manifest owner migration。context-aware prompt manifest / visible-tools resolution 通用算法、generic catalog snapshot provider、generic GetToolSpec catalog provider 和 `GetToolSpec` collapsed summary/detail helper 已迁入 `bitfun-agent-tools`;core 仍保留 product registry snapshot access、product collapsed catalog source、core `Tool` / `ToolUseContext` adapter 与旧路径返回类型。 25. 已合入:product tool adapter/catalog facade closure。`tool_adapter.rs` 承接 core `Tool` 到 provider-neutral contract 的 adapter,`catalog_provider.rs` 承接 product registry snapshot、contextual catalog、manifest、GetToolSpec catalog-description/detail provider facade;`manifest_resolver` 和 `GetToolSpecTool` 只保留旧路径 result type 转换、execution wrapper、duplicate-load guard 与 assistant result rendering。 -26. 已完成:H1 GetToolSpec execution-plan/result owner slice。`bitfun-agent-tools` 接管 static metadata、tool-use message、input extraction、duplicate-load planning、duplicate-load result、provider-backed detail lookup 到 result assembly 的组装规则和 typed execution error;core 继续持有 `GetToolSpecTool` Tool impl、`ToolUseContext.unlocked_collapsed_tools` 状态来源、product provider/context 注入和 `BitFunError` 映射;该阶段不迁移 runtime manifest assembly、unlock state owner、snapshot decorator 或 concrete tools。 +26. 已完成:H1 GetToolSpec runtime facade owner slice。`bitfun-agent-tools` 接管 static tool surface(name / description / schema / readonly / concurrency / permission / validation / tool-use message)、input extraction、duplicate-load planning、duplicate-load result、provider-backed detail lookup 到 result assembly 的组装规则和 typed execution error;core 继续持有 `GetToolSpecTool` Tool impl、`ToolUseContext.unlocked_collapsed_tools` 状态来源、product provider/context 注入和 `BitFunError` 映射;该阶段不迁移 runtime manifest assembly、unlock state owner、snapshot decorator 或 concrete tools。 27. 已完成:H1 generic runtime assembly owner slice。`bitfun-agent-tools` 接管 static-provider 安装 assembly 的通用顺序、decorator reference contract、generic snapshot decorator adapter 与 decorator 应用规则;core `ProductToolRuntimeAssembly` 继续持有 concrete provider group 来源、product snapshot wrapper adapter 注入、旧路径 `ToolRegistry` wrapper、product registry snapshot access 和 dynamic MCP tool entry,不迁移 `ToolUseContext`、`GetToolSpecTool` Tool impl、runtime manifest facade、unlock state owner、snapshot wrapper implementation 或 concrete tools。 28. 已完成:H1 readonly filter owner slice。`bitfun-agent-tools` 接管 registry snapshot 上 readonly + enabled 过滤的通用规则;core 继续持有 product snapshot access、`dyn Tool` adapter 和各 concrete tool 的 readonly/enabled 判定,不迁移 `ToolUseContext`、runtime manifest facade、`GetToolSpecTool`、snapshot wrapper implementation 或 concrete tools。 -29. 当前下一步:H1 必须继续真正 runtime owner 迁移设计/实施,而不是插入 baseline-only PR:只能选择 `ToolUseContext`、runtime manifest assembly / `GetToolSpec` Tool impl 或单一 concrete tool feature group 中的一个 owner,并补 port/provider 设计、旧路径兼容和等价测试。 -30. 后续独立评估:`bitfun-core default = []`、per-product feature set、依赖版本收敛或构建收益优化;任何收益声明都需要记录 `cargo check -p bitfun-core`、workspace check 和目标 crate check 的前后数据。 +29. 已完成:H1 tool catalog runtime facade slice。`bitfun-agent-tools::ToolCatalogRuntime` 接管 provider-backed visible-tools、prompt-visible manifest 与 readonly enabled catalog 查询入口;core 继续持有 product registry snapshot、agent policy、`dyn Tool` / `ToolUseContext` adapter 和 product facade,不迁移 `ToolUseContext`、`GetToolSpecTool` Tool impl、collapsed unlock state、snapshot wrapper implementation 或 concrete tools。 +30. 当前下一步:H1 仍需继续真正 runtime owner 迁移设计/实施,而不是插入 baseline-only PR:只能选择 `ToolUseContext`、runtime manifest assembly / `GetToolSpecTool` Tool impl 最后一层适配,或单一 concrete tool feature group 中的一个 owner,并补 port/provider 设计、旧路径兼容和等价测试。 +31. 后续独立评估:`bitfun-core default = []`、per-product feature set、依赖版本收敛或构建收益优化;任何收益声明都需要记录 `cargo check -p bitfun-core`、workspace check 和目标 crate check 的前后数据。 冗余清理 PR 不进入上述主线序号。只有在满足 `0A.6` 的绝对等价要求时,才可以插入到相邻里程碑之间,并且不得与主线拆分 PR 混合。 diff --git a/scripts/check-core-boundaries.mjs b/scripts/check-core-boundaries.mjs index 367e50c19..9a833642d 100644 --- a/scripts/check-core-boundaries.mjs +++ b/scripts/check-core-boundaries.mjs @@ -1285,6 +1285,10 @@ const requiredContentRules = [ regex: /\bpub async fn resolve_get_tool_spec_execution_result_from_provider\b/, message: 'missing provider-backed GetToolSpec execution result resolver', }, + { + regex: /\bpub struct GetToolSpecRuntime\b/, + message: 'missing provider-backed GetToolSpec runtime facade', + }, { regex: /\bpub struct GetToolSpecLoadObservation\b/, message: 'missing pure GetToolSpec load observation contract', @@ -1333,6 +1337,10 @@ const requiredContentRules = [ regex: /\bpub async fn resolve_readonly_enabled_tools\b/, message: 'missing generic readonly enabled tool filter', }, + { + regex: /\bpub struct ToolCatalogRuntime\b/, + message: 'missing provider-backed tool catalog runtime facade', + }, ], }, { @@ -1384,8 +1392,8 @@ const requiredContentRules = [ message: 'missing collapsed-tool catalog owner', }, { - regex: /\bresolve_readonly_enabled_tools\b/, - message: 'missing agent-tools readonly enabled filter delegation', + regex: /\bresolve_product_readonly_enabled_tools\b/, + message: 'missing product tool catalog readonly facade delegation', }, { regex: /\bregistry_preserves_collapsed_tool_manifest_for_owner_migration\b/, @@ -1528,25 +1536,29 @@ const requiredContentRules = [ message: 'missing core agent policy source for contextual catalog', }, { - regex: /\bresolve_contextual_visible_tools_from_provider\b/, - message: 'missing agent-tools provider-backed visible-tools resolver delegation', + regex: /\bToolCatalogRuntime\b/, + message: 'missing agent-tools product catalog runtime facade delegation', }, { - regex: /\bresolve_contextual_tool_manifest_from_provider\b/, - message: 'missing agent-tools provider-backed manifest resolver delegation', + regex: /\bproduct_tool_catalog_runtime\b/, + message: 'missing product catalog runtime factory', }, { - regex: /\bbuild_get_tool_spec_catalog_description_from_provider\b/, - message: 'missing agent-tools provider-backed GetToolSpec catalog delegation', + regex: /\bGetToolSpecRuntime\b/, + message: 'missing agent-tools GetToolSpec runtime facade delegation', }, { - regex: /\bresolve_get_tool_spec_execution_result_from_provider\b/, - message: 'missing agent-tools provider-backed GetToolSpec execution result delegation', + regex: /\bproduct_get_tool_spec_runtime\b/, + message: 'missing product GetToolSpec runtime factory', }, { regex: /\bresolve_product_tool_manifest\b/, message: 'missing product manifest facade', }, + { + regex: /\bresolve_product_readonly_enabled_tools\b/, + message: 'missing product readonly enabled tools facade', + }, { regex: /\bresolve_product_get_tool_spec_execution_result\b/, message: 'missing product GetToolSpec execution result facade', @@ -1625,6 +1637,10 @@ const requiredContentRules = [ regex: /\bpub async fn resolve_get_tool_spec_execution_result_from_provider\b/, message: 'missing provider-backed GetToolSpec execution result helper', }, + { + regex: /\bpub struct GetToolSpecRuntime\b/, + message: 'missing provider-backed GetToolSpec runtime facade', + }, ], }, { @@ -1680,7 +1696,7 @@ const requiredContentRules = [ { path: 'src/crates/core/src/agentic/tools/implementations/get_tool_spec_tool.rs', reason: - 'core must continue owning GetToolSpec runtime until an approved provider migration exists', + 'core must continue owning the GetToolSpec Tool adapter and product boundary while delegating generic runtime surface to agent-tools', patterns: [ { regex: /\bpub struct GetToolSpecTool\b/, @@ -1699,16 +1715,12 @@ const requiredContentRules = [ message: 'missing core product GetToolSpec catalog facade delegation', }, { - regex: /\bget_tool_spec_short_description\b/, - message: 'missing agent-tools GetToolSpec static metadata delegation', - }, - { - regex: /\brender_get_tool_spec_tool_use_message\b/, - message: 'missing agent-tools GetToolSpec use-message delegation', + regex: /\bproduct_get_tool_spec_runtime\b/, + message: 'missing product GetToolSpec runtime facade delegation', }, { - regex: /\bvalidate_get_tool_spec_input\b/, - message: 'missing agent-tools GetToolSpec validation helper delegation', + regex: /\bwith_runtime\b/, + message: 'missing core GetToolSpec static surface facade boundary', }, ], }, @@ -3399,6 +3411,7 @@ function runManifestParserSelfTest() { 'GetToolSpecExecutionError', 'resolve_get_tool_spec_execution_plan', 'resolve_get_tool_spec_execution_result_from_provider', + 'GetToolSpecRuntime', 'GetToolSpecLoadObservation', 'collect_loaded_collapsed_tool_names', 'sort_tool_manifest_definitions', @@ -3488,7 +3501,7 @@ function runManifestParserSelfTest() { 'ProductToolDecoratorRef', 'ProductToolRuntimeAssembly', 'get_collapsed_tool_names', - 'resolve_readonly_enabled_tools', + 'resolve_product_readonly_enabled_tools', ], }, { @@ -3524,6 +3537,7 @@ function runManifestParserSelfTest() { 'StaticToolProvider', 'StaticToolProviderGroup', 'ToolRuntimeAssembly', + 'ToolCatalogRuntime', 'ToolDecoratorRef', 'SnapshotToolWrapper', 'SnapshotToolDecorator', @@ -3534,6 +3548,7 @@ function runManifestParserSelfTest() { 'build_get_tool_spec_detail_result', 'resolve_get_tool_spec_execution_plan', 'resolve_get_tool_spec_execution_result_from_provider', + 'GetToolSpecRuntime', ], }, { @@ -3564,11 +3579,12 @@ function runManifestParserSelfTest() { 'GetToolSpecCatalogProvider', 'get_global_tool_registry', 'get_agent_registry', - 'resolve_contextual_visible_tools_from_provider', - 'resolve_contextual_tool_manifest_from_provider', - 'build_get_tool_spec_catalog_description_from_provider', - 'resolve_get_tool_spec_execution_result_from_provider', + 'ToolCatalogRuntime', + 'product_tool_catalog_runtime', + 'GetToolSpecRuntime', + 'product_get_tool_spec_runtime', 'resolve_product_tool_manifest', + 'resolve_product_readonly_enabled_tools', 'resolve_product_get_tool_spec_execution_result', 'unlocked_collapsed_tools', 'product_catalog_provider_default_get_tool_spec_catalog_matches_registry', @@ -3590,11 +3606,10 @@ function runManifestParserSelfTest() { contracts: [ 'GetToolSpecTool', 'build_product_get_tool_spec_catalog_description', + 'product_get_tool_spec_runtime', + 'with_runtime', 'resolve_product_get_tool_spec_execution_result', 'map_get_tool_spec_execution_error', - 'get_tool_spec_short_description', - 'render_get_tool_spec_tool_use_message', - 'validate_get_tool_spec_input', ], }, { diff --git a/src/crates/agent-tools/src/framework.rs b/src/crates/agent-tools/src/framework.rs index 11cf50b03..fdc9c70dd 100644 --- a/src/crates/agent-tools/src/framework.rs +++ b/src/crates/agent-tools/src/framework.rs @@ -8,6 +8,7 @@ use serde::{Deserialize, Serialize}; use serde_json::Value; use std::collections::{BTreeSet, HashSet}; use std::fmt; +use std::marker::PhantomData; use std::path::{Path, PathBuf}; use std::sync::Arc; @@ -640,6 +641,72 @@ pub async fn resolve_readonly_enabled_tools( readonly_tools } +pub struct ToolCatalogRuntime<'a, Tool: ?Sized, Context, Provider: ?Sized> { + provider: &'a Provider, + get_tool_spec_tool_name: &'a str, + _marker: PhantomData, +} + +impl<'a, Tool: ?Sized, Context, Provider: ?Sized> ToolCatalogRuntime<'a, Tool, Context, Provider> { + pub fn new(provider: &'a Provider, get_tool_spec_tool_name: &'a str) -> Self { + Self { + provider, + get_tool_spec_tool_name, + _marker: PhantomData, + } + } +} + +impl<'a, Tool, Context, Provider> ToolCatalogRuntime<'a, Tool, Context, Provider> +where + Tool: ToolRegistryItem + ?Sized, + Provider: ToolCatalogSnapshotProvider + ?Sized, +{ + pub async fn readonly_enabled_tools(&self) -> Vec> { + let tool_snapshot = self.provider.tool_snapshot().await; + resolve_readonly_enabled_tools(&tool_snapshot).await + } +} + +impl<'a, Tool, Context, Provider> ToolCatalogRuntime<'a, Tool, Context, Provider> +where + Tool: ContextualToolManifestItem + ?Sized, + Context: Sync, + Provider: ToolCatalogSnapshotProvider + ?Sized, +{ + pub async fn visible_tools( + &self, + allowed_tools: &[String], + exposure_overrides: &IndexMap, + context: &Context, + ) -> ContextualVisibleTools { + resolve_contextual_visible_tools_from_provider( + self.provider, + allowed_tools, + exposure_overrides, + context, + self.get_tool_spec_tool_name, + ) + .await + } + + pub async fn tool_manifest( + &self, + allowed_tools: &[String], + exposure_overrides: &IndexMap, + context: &Context, + ) -> ContextualToolManifest { + resolve_contextual_tool_manifest_from_provider( + self.provider, + allowed_tools, + exposure_overrides, + context, + self.get_tool_spec_tool_name, + ) + .await + } +} + pub async fn resolve_get_tool_spec_detail( collapsed_tools: &[ToolRef], tool_name: &str, @@ -743,6 +810,88 @@ where } } +pub struct GetToolSpecRuntime<'a, Tool: ?Sized, Context, Provider: ?Sized> { + provider: &'a Provider, + tool_name: &'a str, + _marker: PhantomData, +} + +impl<'a, Tool: ?Sized, Context, Provider: ?Sized> GetToolSpecRuntime<'a, Tool, Context, Provider> { + pub fn new(provider: &'a Provider, tool_name: &'a str) -> Self { + Self { + provider, + tool_name, + _marker: PhantomData, + } + } + + pub fn name(&self) -> &str { + self.tool_name + } + + pub fn short_description(&self) -> String { + get_tool_spec_short_description() + } + + pub fn input_schema(&self) -> Value { + get_tool_spec_input_schema() + } + + pub fn is_readonly(&self) -> bool { + get_tool_spec_is_readonly() + } + + pub fn is_concurrency_safe(&self, input: Option<&Value>) -> bool { + get_tool_spec_is_concurrency_safe(input) + } + + pub fn needs_permissions(&self, input: Option<&Value>) -> bool { + get_tool_spec_needs_permissions(input) + } + + pub fn render_tool_use_message(&self, input: &Value) -> String { + render_get_tool_spec_tool_use_message(input) + } + + pub fn validate_input(&self, input: &Value) -> ValidationResult { + validate_get_tool_spec_input(input) + } +} + +impl<'a, Tool, Context, Provider> GetToolSpecRuntime<'a, Tool, Context, Provider> +where + Tool: ToolRegistryItem + ?Sized, + Context: Sync, + Provider: GetToolSpecCatalogProvider + ?Sized, +{ + pub async fn catalog_description(&self, context: Option<&Context>) -> String { + build_get_tool_spec_catalog_description_from_provider(self.provider, context).await + } +} + +impl<'a, Tool, Context, Provider> GetToolSpecRuntime<'a, Tool, Context, Provider> +where + Tool: ContextualToolManifestItem + ?Sized, + Context: Sync, + Provider: GetToolSpecCatalogProvider + ?Sized, +{ + pub async fn execute( + &self, + input: &Value, + loaded_collapsed_tools: &[String], + context: &Context, + ) -> Result { + resolve_get_tool_spec_execution_result_from_provider( + self.provider, + input, + loaded_collapsed_tools, + context, + self.tool_name, + ) + .await + } +} + pub async fn resolve_contextual_visible_tools_from_provider( provider: &Provider, allowed_tools: &[String], diff --git a/src/crates/agent-tools/src/lib.rs b/src/crates/agent-tools/src/lib.rs index 5712b88c8..10c46fd22 100644 --- a/src/crates/agent-tools/src/lib.rs +++ b/src/crates/agent-tools/src/lib.rs @@ -29,12 +29,13 @@ pub use framework::{ ContextualToolManifest, ContextualToolManifestItem, ContextualVisibleTools, DynamicMcpToolInfo, DynamicToolInfo, GetToolSpecCatalogProvider, GetToolSpecCollapsedToolSummary, GetToolSpecDetail, GetToolSpecExecutionError, GetToolSpecExecutionPlan, - GetToolSpecLoadObservation, PortableToolContextProvider, PromptVisibleToolManifestItem, - SnapshotToolDecorator, SnapshotToolWrapper, SnapshotToolWrapperRef, StaticToolProvider, - StaticToolProviderGroup, ToolCatalogSnapshotProvider, ToolContextFacts, ToolDecoratorRef, - ToolExposure, ToolManifestDefinition, ToolManifestPolicyResolution, ToolManifestPolicyTool, - ToolPathBackend, ToolPathOperation, ToolPathPolicy, ToolPathResolution, ToolRef, ToolRegistry, - ToolRegistryItem, ToolRenderOptions, ToolRestrictionError, ToolResult, ToolRuntimeAssembly, + GetToolSpecLoadObservation, GetToolSpecRuntime, PortableToolContextProvider, + PromptVisibleToolManifestItem, SnapshotToolDecorator, SnapshotToolWrapper, + SnapshotToolWrapperRef, StaticToolProvider, StaticToolProviderGroup, ToolCatalogRuntime, + ToolCatalogSnapshotProvider, ToolContextFacts, ToolDecoratorRef, ToolExposure, + ToolManifestDefinition, ToolManifestPolicyResolution, ToolManifestPolicyTool, ToolPathBackend, + ToolPathOperation, ToolPathPolicy, ToolPathResolution, ToolRef, ToolRegistry, ToolRegistryItem, + ToolRenderOptions, ToolRestrictionError, ToolResult, ToolRuntimeAssembly, ToolRuntimeRestrictions, ToolWorkspaceKind, ValidationResult, GET_TOOL_SPEC_TOOL_NAME, }; pub use input_validator::InputValidator; diff --git a/src/crates/agent-tools/tests/tool_contracts.rs b/src/crates/agent-tools/tests/tool_contracts.rs index 6609d5bb6..2d3ebff82 100644 --- a/src/crates/agent-tools/tests/tool_contracts.rs +++ b/src/crates/agent-tools/tests/tool_contracts.rs @@ -13,7 +13,7 @@ use bitfun_agent_tools::{ resolve_tool_manifest_policy, sort_tool_manifest_definitions, summarize_get_tool_spec_collapsed_tools, validate_get_tool_spec_input, DynamicMcpToolInfo, DynamicToolInfo, GetToolSpecCollapsedToolSummary, GetToolSpecExecutionError, - GetToolSpecExecutionPlan, GetToolSpecLoadObservation, InputValidator, + GetToolSpecExecutionPlan, GetToolSpecLoadObservation, GetToolSpecRuntime, InputValidator, PromptVisibleToolManifestItem, ToolContextFacts, ToolExposure, ToolImageAttachment, ToolManifestDefinition, ToolManifestPolicyTool, ToolPathBackend, ToolPathResolution, ToolRenderOptions, ToolResult, ToolRuntimeRestrictions, ToolWorkspaceKind, ValidationResult, @@ -22,8 +22,8 @@ use bitfun_agent_tools::{ use bitfun_agent_tools::{ ContextualToolManifestItem, DynamicToolDescriptor, DynamicToolProvider, GetToolSpecCatalogProvider, PortResult, PortableToolContextProvider, StaticToolProvider, - StaticToolProviderGroup, ToolCatalogSnapshotProvider, ToolDecorator, ToolDecoratorRef, - ToolRegistry, ToolRegistryItem, ToolRuntimeAssembly, + StaticToolProviderGroup, ToolCatalogRuntime, ToolCatalogSnapshotProvider, ToolDecorator, + ToolDecoratorRef, ToolRegistry, ToolRegistryItem, ToolRuntimeAssembly, }; use serde_json::json; use std::path::PathBuf; @@ -943,6 +943,10 @@ struct ContextualManifestSnapshotProvider { tools: Vec>, } +struct RegistryMarkerSnapshotProvider { + tools: Vec>, +} + struct ErroringGetToolSpecProvider; #[async_trait::async_trait] @@ -952,6 +956,13 @@ impl ToolCatalogSnapshotProvider for ContextualManifestS } } +#[async_trait::async_trait] +impl ToolCatalogSnapshotProvider for RegistryMarkerSnapshotProvider { + async fn tool_snapshot(&self) -> Vec> { + self.tools.clone() + } +} + #[async_trait::async_trait] impl GetToolSpecCatalogProvider for ContextualManifestSnapshotProvider @@ -1370,6 +1381,104 @@ async fn contextual_manifest_resolver_accepts_snapshot_provider_boundary() { ); } +#[tokio::test] +async fn tool_catalog_runtime_facade_owns_manifest_and_readonly_paths() { + let manifest_provider = ContextualManifestSnapshotProvider { + tools: vec![ + contextual_manifest_tool("Read", ToolExposure::Expanded, None), + contextual_manifest_tool("WebFetch", ToolExposure::Collapsed, None), + contextual_manifest_tool("Git", ToolExposure::Collapsed, Some("other-agent")), + contextual_manifest_tool(GET_TOOL_SPEC_TOOL_NAME, ToolExposure::Expanded, None), + ], + }; + let runtime = ToolCatalogRuntime::::new( + &manifest_provider, + GET_TOOL_SPEC_TOOL_NAME, + ); + + let visible_tools = runtime + .visible_tools( + &[ + "Read".to_string(), + "WebFetch".to_string(), + "Git".to_string(), + ], + &Default::default(), + &ManifestTestContext { agent: "agentic" }, + ) + .await; + assert_eq!( + visible_tools.allowed_tool_names, + vec![ + "Read".to_string(), + "WebFetch".to_string(), + "Git".to_string(), + GET_TOOL_SPEC_TOOL_NAME.to_string(), + ], + "runtime facade must preserve allowed-list insertion" + ); + assert_eq!( + visible_tools + .expanded_tools + .iter() + .map(|tool| tool.name.as_str()) + .collect::>(), + vec!["Read", GET_TOOL_SPEC_TOOL_NAME], + "runtime facade must preserve expanded handle order" + ); + assert_eq!( + visible_tools.collapsed_tool_names, + vec!["WebFetch".to_string()], + "runtime facade must preserve context-aware collapsed filtering" + ); + + let manifest = runtime + .tool_manifest( + &[ + "Read".to_string(), + "WebFetch".to_string(), + "Git".to_string(), + ], + &Default::default(), + &ManifestTestContext { agent: "agentic" }, + ) + .await; + assert_eq!( + manifest + .tool_definitions + .iter() + .map(|tool| tool.name.as_str()) + .collect::>(), + vec!["Read", "WebFetch", GET_TOOL_SPEC_TOOL_NAME], + "runtime facade must preserve prompt-visible manifest order" + ); + + let readonly_provider = RegistryMarkerSnapshotProvider { + tools: vec![ + registry_marker_tool_with_access("Read", None, ToolExposure::Expanded, true, true), + registry_marker_tool_with_access("Write", None, ToolExposure::Expanded, false, true), + registry_marker_tool_with_access("Disabled", None, ToolExposure::Expanded, true, false), + registry_marker_tool_with_access("WebFetch", None, ToolExposure::Collapsed, true, true), + ], + }; + let readonly_runtime = ToolCatalogRuntime::::new( + &readonly_provider, + GET_TOOL_SPEC_TOOL_NAME, + ); + let readonly_names = readonly_runtime + .readonly_enabled_tools() + .await + .into_iter() + .map(|tool| tool.name().to_string()) + .collect::>(); + + assert_eq!( + readonly_names, + vec!["Read".to_string(), "WebFetch".to_string()], + "runtime facade must preserve readonly enabled filtering order" + ); +} + #[tokio::test] async fn get_tool_spec_detail_resolver_preserves_contextual_detail_contract() { let collapsed_tools = vec![ @@ -1559,6 +1668,70 @@ async fn get_tool_spec_provider_execution_returns_detail_result_from_provider() assert_eq!(image_attachments, None); } +#[tokio::test] +async fn get_tool_spec_runtime_facade_owns_catalog_and_execution_paths() { + let provider = ContextualManifestSnapshotProvider { + tools: vec![contextual_manifest_tool( + "WebFetch", + ToolExposure::Collapsed, + None, + )], + }; + let context = ManifestTestContext { agent: "agentic" }; + let input = json!({ "tool_name": "WebFetch" }); + let runtime = GetToolSpecRuntime::::new( + &provider, + GET_TOOL_SPEC_TOOL_NAME, + ); + + let description = runtime.catalog_description(Some(&context)).await; + assert!(description.contains("- WebFetch: WebFetch short description")); + + let result = runtime + .execute(&input, &[], &context) + .await + .expect("collapsed tool detail should resolve through runtime facade"); + + let ToolResult::Result { data, .. } = result else { + panic!("expected normal tool result"); + }; + assert_eq!(data["tool_name"], "WebFetch"); + assert_eq!(data["description"], "WebFetch description for agentic"); + assert_eq!( + data["input_schema"]["properties"]["agent"]["const"], + "agentic" + ); +} + +#[test] +fn get_tool_spec_runtime_facade_owns_static_tool_surface() { + let provider = ContextualManifestSnapshotProvider { tools: Vec::new() }; + let runtime = GetToolSpecRuntime::::new( + &provider, + GET_TOOL_SPEC_TOOL_NAME, + ); + + assert_eq!(runtime.name(), GET_TOOL_SPEC_TOOL_NAME); + assert_eq!( + runtime.short_description(), + get_tool_spec_short_description() + ); + assert_eq!(runtime.input_schema(), get_tool_spec_input_schema()); + assert!(runtime.is_readonly()); + assert!(runtime.is_concurrency_safe(None)); + assert!(!runtime.needs_permissions(None)); + assert_eq!( + runtime.render_tool_use_message(&json!({ "tool_name": "WebFetch" })), + "Reading tool spec for 'WebFetch'." + ); + assert!( + runtime + .validate_input(&json!({ "tool_name": "WebFetch" })) + .result + ); + assert!(!runtime.validate_input(&json!({})).result); +} + #[tokio::test] async fn get_tool_spec_provider_execution_classifies_detail_errors() { let provider = ContextualManifestSnapshotProvider { diff --git a/src/crates/core/AGENTS.md b/src/crates/core/AGENTS.md index c4dec3a18..5315f3d5d 100644 --- a/src/crates/core/AGENTS.md +++ b/src/crates/core/AGENTS.md @@ -34,9 +34,10 @@ SessionManager → Session → DialogTurn → ModelRound - For tools, keep lightweight contracts, pure manifest/exposure contracts, generic contextual prompt-manifest resolver contracts, generic catalog snapshot provider contracts, generic GetToolSpec catalog provider/detail/ - summary/static metadata/tool-use message/execution-plan/provider-backed execution-result/ + summary/static tool surface/execution-plan/provider-backed runtime facade / execution-result/ result-assembly helpers, and portable tool context facts/provider plus generic registry / static-provider / dynamic-provider container - contracts in `bitfun-agent-tools`. Generic decorator references, snapshot decorator adapters, static-provider runtime assembly, and readonly/enabled + contracts in `bitfun-agent-tools`. Provider-backed visible-tools / prompt-visible manifest / readonly catalog runtime facades, + generic decorator references, snapshot decorator adapters, static-provider runtime assembly, and readonly/enabled registry-snapshot filtering belong in `bitfun-agent-tools`; core tool runtime should keep concrete provider groups and product snapshot wrapper adapter injection in `runtime_assembly.rs` + `static_providers.rs`, adapt core `Tool` into diff --git a/src/crates/core/src/agentic/tools/catalog_provider.rs b/src/crates/core/src/agentic/tools/catalog_provider.rs index 78ee8bd56..3d9e52960 100644 --- a/src/crates/core/src/agentic/tools/catalog_provider.rs +++ b/src/crates/core/src/agentic/tools/catalog_provider.rs @@ -3,11 +3,8 @@ use crate::agentic::tools::framework::{Tool, ToolExposure, ToolResult, ToolUseCo use crate::agentic::tools::registry::{get_global_tool_registry, GET_TOOL_SPEC_TOOL_NAME}; use crate::util::errors::{BitFunError, BitFunResult}; use bitfun_agent_tools::{ - build_get_tool_spec_catalog_description_from_provider, - resolve_contextual_tool_manifest_from_provider, resolve_contextual_visible_tools_from_provider, - resolve_get_tool_spec_execution_result_from_provider, ContextualToolManifest, - ContextualVisibleTools, GetToolSpecCatalogProvider, GetToolSpecExecutionError, - ToolCatalogSnapshotProvider, + ContextualToolManifest, ContextualVisibleTools, GetToolSpecCatalogProvider, + GetToolSpecExecutionError, GetToolSpecRuntime, ToolCatalogRuntime, ToolCatalogSnapshotProvider, }; use serde_json::Value; use std::sync::Arc; @@ -15,6 +12,12 @@ use std::sync::Arc; #[derive(Debug, Clone, Copy, Default)] pub(crate) struct ProductToolCatalogProvider; +pub(crate) type ProductGetToolSpecRuntime<'a> = + GetToolSpecRuntime<'a, dyn Tool, ToolUseContext, ProductToolCatalogProvider>; + +pub(crate) type ProductToolCatalogRuntime<'a> = + ToolCatalogRuntime<'a, dyn Tool, ToolUseContext, ProductToolCatalogProvider>; + #[async_trait::async_trait] impl ToolCatalogSnapshotProvider for ProductToolCatalogProvider { async fn tool_snapshot(&self) -> Vec> { @@ -63,31 +66,34 @@ impl ProductToolCatalogProvider { let policy = agent_registry .get_agent_tool_policy(agent_type, workspace_root) .await; - let visible_tools = resolve_contextual_visible_tools_from_provider( - self, - &policy.allowed_tools, - &policy.exposure_overrides, - context, - GET_TOOL_SPEC_TOOL_NAME, - ) - .await; + let visible_tools = product_tool_catalog_runtime(self) + .visible_tools(&policy.allowed_tools, &policy.exposure_overrides, context) + .await; Ok(visible_tools.collapsed_tools) } } +pub(crate) fn product_get_tool_spec_runtime( + provider: &ProductToolCatalogProvider, +) -> ProductGetToolSpecRuntime<'_> { + GetToolSpecRuntime::new(provider, GET_TOOL_SPEC_TOOL_NAME) +} + +pub(crate) fn product_tool_catalog_runtime( + provider: &ProductToolCatalogProvider, +) -> ProductToolCatalogRuntime<'_> { + ToolCatalogRuntime::new(provider, GET_TOOL_SPEC_TOOL_NAME) +} + pub(crate) async fn resolve_product_visible_tools( allowed_tools: &[String], exposure_overrides: &AgentToolPolicyOverrides, context: &ToolUseContext, ) -> ContextualVisibleTools { - resolve_contextual_visible_tools_from_provider( - &ProductToolCatalogProvider, - allowed_tools, - exposure_overrides, - context, - GET_TOOL_SPEC_TOOL_NAME, - ) - .await + let provider = ProductToolCatalogProvider; + product_tool_catalog_runtime(&provider) + .visible_tools(allowed_tools, exposure_overrides, context) + .await } pub(crate) async fn resolve_product_tool_manifest( @@ -95,20 +101,25 @@ pub(crate) async fn resolve_product_tool_manifest( exposure_overrides: &AgentToolPolicyOverrides, context: &ToolUseContext, ) -> ContextualToolManifest { - resolve_contextual_tool_manifest_from_provider( - &ProductToolCatalogProvider, - allowed_tools, - exposure_overrides, - context, - GET_TOOL_SPEC_TOOL_NAME, - ) - .await + let provider = ProductToolCatalogProvider; + product_tool_catalog_runtime(&provider) + .tool_manifest(allowed_tools, exposure_overrides, context) + .await +} + +pub(crate) async fn resolve_product_readonly_enabled_tools() -> Vec> { + let provider = ProductToolCatalogProvider; + product_tool_catalog_runtime(&provider) + .readonly_enabled_tools() + .await } pub(crate) async fn build_product_get_tool_spec_catalog_description( context: Option<&ToolUseContext>, ) -> String { - build_get_tool_spec_catalog_description_from_provider(&ProductToolCatalogProvider, context) + let provider = ProductToolCatalogProvider; + product_get_tool_spec_runtime(&provider) + .catalog_description(context) .await } @@ -117,21 +128,17 @@ pub(crate) async fn resolve_product_get_tool_spec_execution_result( context: &ToolUseContext, get_tool_spec_tool_name: &str, ) -> Result { - resolve_get_tool_spec_execution_result_from_provider( - &ProductToolCatalogProvider, - input, - &context.unlocked_collapsed_tools, - context, - get_tool_spec_tool_name, - ) - .await + let provider = ProductToolCatalogProvider; + GetToolSpecRuntime::new(&provider, get_tool_spec_tool_name) + .execute(input, &context.unlocked_collapsed_tools, context) + .await } #[cfg(test)] mod tests { use super::{ - resolve_product_get_tool_spec_execution_result, resolve_product_tool_manifest, - ProductToolCatalogProvider, + resolve_product_get_tool_spec_execution_result, resolve_product_readonly_enabled_tools, + resolve_product_tool_manifest, ProductToolCatalogProvider, }; use crate::agentic::agents::AgentToolPolicyOverrides; use crate::agentic::tools::framework::ToolUseContext; @@ -264,4 +271,24 @@ mod tests { .is_empty()); assert_eq!(data["input_schema"]["type"], "object"); } + + #[tokio::test] + async fn product_catalog_facade_resolves_readonly_enabled_tools_from_same_provider_owner() { + let readonly_names = resolve_product_readonly_enabled_tools() + .await + .into_iter() + .map(|tool| tool.name().to_string()) + .collect::>(); + let mut expected_readonly_names = Vec::new(); + for tool in create_tool_registry().get_all_tools() { + if tool.is_readonly() && tool.is_enabled().await { + expected_readonly_names.push(tool.name().to_string()); + } + } + + assert_eq!( + readonly_names, expected_readonly_names, + "product readonly catalog facade must preserve registry snapshot order" + ); + } } diff --git a/src/crates/core/src/agentic/tools/implementations/get_tool_spec_tool.rs b/src/crates/core/src/agentic/tools/implementations/get_tool_spec_tool.rs index f0d3c4b5f..774f63d02 100644 --- a/src/crates/core/src/agentic/tools/implementations/get_tool_spec_tool.rs +++ b/src/crates/core/src/agentic/tools/implementations/get_tool_spec_tool.rs @@ -1,19 +1,16 @@ //! GetToolSpec tool implementation use crate::agentic::tools::catalog_provider::{ - build_product_get_tool_spec_catalog_description, resolve_product_get_tool_spec_execution_result, + build_product_get_tool_spec_catalog_description, product_get_tool_spec_runtime, + resolve_product_get_tool_spec_execution_result, ProductGetToolSpecRuntime, + ProductToolCatalogProvider, }; use crate::agentic::tools::framework::{ Tool, ToolRenderOptions, ToolResult, ToolUseContext, ValidationResult, }; use crate::util::errors::{BitFunError, BitFunResult}; use async_trait::async_trait; -use bitfun_agent_tools::{ - get_tool_spec_input_schema, get_tool_spec_is_concurrency_safe, get_tool_spec_is_readonly, - get_tool_spec_needs_permissions, get_tool_spec_short_description, - render_get_tool_spec_tool_use_message, validate_get_tool_spec_input, GetToolSpecExecutionError, - GET_TOOL_SPEC_TOOL_NAME, -}; +use bitfun_agent_tools::{GetToolSpecExecutionError, GET_TOOL_SPEC_TOOL_NAME}; use serde_json::Value; pub struct GetToolSpecTool; @@ -34,6 +31,11 @@ impl Default for GetToolSpecTool { } } +fn with_runtime(operation: impl FnOnce(ProductGetToolSpecRuntime<'_>) -> Result) -> Result { + let provider = ProductToolCatalogProvider; + operation(product_get_tool_spec_runtime(&provider)) +} + #[async_trait] impl Tool for GetToolSpecTool { fn name(&self) -> &str { @@ -45,7 +47,7 @@ impl Tool for GetToolSpecTool { } fn short_description(&self) -> String { - get_tool_spec_short_description() + with_runtime(|runtime| runtime.short_description()) } async fn description_with_context( @@ -56,23 +58,23 @@ impl Tool for GetToolSpecTool { } fn input_schema(&self) -> Value { - get_tool_spec_input_schema() + with_runtime(|runtime| runtime.input_schema()) } fn is_readonly(&self) -> bool { - get_tool_spec_is_readonly() + with_runtime(|runtime| runtime.is_readonly()) } fn is_concurrency_safe(&self, input: Option<&Value>) -> bool { - get_tool_spec_is_concurrency_safe(input) + with_runtime(|runtime| runtime.is_concurrency_safe(input)) } fn needs_permissions(&self, input: Option<&Value>) -> bool { - get_tool_spec_needs_permissions(input) + with_runtime(|runtime| runtime.needs_permissions(input)) } fn render_tool_use_message(&self, input: &Value, _options: &ToolRenderOptions) -> String { - render_get_tool_spec_tool_use_message(input) + with_runtime(|runtime| runtime.render_tool_use_message(input)) } async fn validate_input( @@ -80,7 +82,7 @@ impl Tool for GetToolSpecTool { input: &Value, _context: Option<&ToolUseContext>, ) -> ValidationResult { - validate_get_tool_spec_input(input) + with_runtime(|runtime| runtime.validate_input(input)) } async fn call_impl( diff --git a/src/crates/core/src/agentic/tools/registry.rs b/src/crates/core/src/agentic/tools/registry.rs index c7040bc16..37752fd51 100644 --- a/src/crates/core/src/agentic/tools/registry.rs +++ b/src/crates/core/src/agentic/tools/registry.rs @@ -1,10 +1,11 @@ //! Tool registry +use crate::agentic::tools::catalog_provider::resolve_product_readonly_enabled_tools; use crate::agentic::tools::framework::{DynamicToolInfo, Tool}; use crate::util::errors::BitFunResult; use bitfun_agent_tools::{ - resolve_readonly_enabled_tools, DynamicToolDescriptor, DynamicToolProvider, PortResult, - ToolDecoratorRef, ToolRegistry as AgentToolRegistry, + DynamicToolDescriptor, DynamicToolProvider, PortResult, ToolDecoratorRef, + ToolRegistry as AgentToolRegistry, }; use log::{debug, info, trace, warn}; use std::sync::Arc; @@ -769,8 +770,7 @@ pub async fn get_all_tools() -> Vec> { /// Get readonly tools pub async fn get_readonly_tools() -> BitFunResult>> { - let all_tools = get_all_tools().await; - Ok(resolve_readonly_enabled_tools(&all_tools).await) + Ok(resolve_product_readonly_enabled_tools().await) } /// Create default tool registry - factory function