From 65864ba20aa17734f3c14085773b38f9e3349b42 Mon Sep 17 00:00:00 2001 From: limityan Date: Mon, 18 May 2026 17:42:36 +0800 Subject: [PATCH 1/2] refactor(agent-tools): add static tool provider assembly --- AGENTS.md | 12 +- docs/architecture/core-decomposition.md | 7 +- docs/plans/core-decomposition-plan.md | 23 ++-- scripts/check-core-boundaries.mjs | 81 ++++++++++++- src/crates/agent-tools/src/framework.rs | 15 +++ src/crates/agent-tools/src/lib.rs | 2 +- .../agent-tools/tests/tool_contracts.rs | 67 ++++++++++- src/crates/core/AGENTS.md | 5 +- src/crates/core/src/agentic/tools/mod.rs | 1 + src/crates/core/src/agentic/tools/registry.rs | 107 +++++++----------- .../src/agentic/tools/static_providers.rs | 80 +++++++++++++ 11 files changed, 304 insertions(+), 96 deletions(-) create mode 100644 src/crates/core/src/agentic/tools/static_providers.rs diff --git a/AGENTS.md b/AGENTS.md index 1fb07a749..151781129 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -121,12 +121,12 @@ and milestone verification gates. ### Tool ownership guardrails - `src/crates/agent-tools` owns lightweight tool contracts, pure - manifest/exposure contracts, and the generic registry / dynamic-provider - container. -- `src/crates/core/src/agentic/tools` owns product tool assembly, `dyn Tool` - adaptation, snapshot decoration, runtime manifest assembly / context - filtering, and on-demand tool spec discovery execution (`GetToolSpec`) for - now. + manifest/exposure contracts, and generic registry / static-provider / + dynamic-provider container contracts. +- `src/crates/core/src/agentic/tools` owns product tool provider assembly + (`static_providers.rs`), `dyn Tool` adaptation, snapshot decoration, runtime + manifest assembly / context filtering, and on-demand tool spec discovery + execution (`GetToolSpec`) for now. - Keep `ToolUseContext` and concrete tool implementations in core until a reviewed port/provider design and equivalence tests exist. - Tool migrations must preserve expanded/collapsed exposure, prompt-visible diff --git a/docs/architecture/core-decomposition.md b/docs/architecture/core-decomposition.md index e9b78529b..c514f6209 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、runtime restriction、pure manifest/exposure contract、generic registry / provider container | partial:runtime manifest assembly / context filtering、`ToolUseContext`、`GetToolSpec` 执行和 concrete tools 仍在 core | +| `bitfun-agent-tools` | 轻量 tool DTO / contract、runtime restriction、pure manifest/exposure contract、generic registry / static-provider / dynamic-provider container | partial:runtime manifest assembly / context filtering、`ToolUseContext`、`GetToolSpec` 执行和 concrete tools 仍在 core;core 当前仅把内置工具列表收敛为 core-owned static provider group | | `bitfun-tool-packs` | 由 feature group 隔离的具体工具实现 | target/scaffold:不得声明 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 | @@ -141,6 +141,11 @@ owner 边界,否则不要把一个 feature group 继续拆成更小的 crate tool schema/description、`GetToolSpec` 执行和 `ToolUseContext.unlocked_collapsed_tools` 暂时仍属于 `bitfun-core` product tool runtime。继续迁移前必须证明 prompt-visible manifest、expanded/collapsed exposure、unlock state 与 desktop/MCP/ACP tool catalog 等价。 +- 当前 tool runtime 外移的低风险入口是 `StaticToolProvider` / `install_static_provider` + 合约归属 `bitfun-agent-tools`,并让 core 在 `static_providers.rs` 中将内置工具列表收敛为 + `core.basic`、`core.agent`、`core.session`、`core.integration` provider group。 + 这不代表 concrete tools、`ToolUseContext`、runtime manifest resolver 或 + `GetToolSpecTool` 执行已经迁移。 - 最新主干的 remote workspace guard 和 search fallback/context 修复提高了 workspace/search 迁移门槛。后续迁移 workspace 或 search runtime 时,必须保留 remote workspace metadata、 startup runtime ensure、remote flashgrep fallback、preview mapping 和 local/remote fallback 语义。 diff --git a/docs/plans/core-decomposition-plan.md b/docs/plans/core-decomposition-plan.md index caaa35275..6fb1d2efb 100644 --- a/docs/plans/core-decomposition-plan.md +++ b/docs/plans/core-decomposition-plan.md @@ -1019,6 +1019,7 @@ cargo check -p bitfun-cli - [x] 抽出 tool result、validation、dynamic metadata、runtime restriction、path resolution DTO,以及 generic registry / dynamic provider container 到 `agent-tools`。 - [x] 抽出纯 manifest/exposure 契约到 `agent-tools`:`ToolExposure`、`GetToolSpec` 名称、纯 manifest policy、collapsed prompt stub 与 prompt-visible ordering;core 继续拥有 runtime assembly。 +- [x] 抽出 static tool provider 安装合约到 `agent-tools`,并将 core 内置工具列表收敛到 `static_providers.rs` 的 core-owned provider groups;不迁移 concrete tool implementation。 - [ ] 抽出 `Tool` trait 与 `ToolUseContext` 前,先补可移植 tool context / service port 设计;当前不做无端口支撑的行为迁移。 - [x] `agent-tools` 不依赖任何 concrete service。 - [ ] 将工具实现迁移到 `tool-packs` crate,并按 feature group 分模块: @@ -1050,12 +1051,12 @@ pub fn create_tool_registry() -> ToolRegistry { - [ ] 迁移 runtime manifest assembly / `GetToolSpec` 执行前,补 expanded/collapsed manifest、 prompt-visible stub、unlock state 和 desktop/MCP/ACP catalog 等价测试。 -**当前安全迁移状态(2026-05-14):** +**当前安全迁移状态(2026-05-18):** -- 已迁移到 `bitfun-agent-tools`:`ToolResult`、`ValidationResult`、`InputValidator`、dynamic tool metadata、tool render options、runtime restriction DTO、path resolution DTO,以及不依赖 core service 的 `ToolRegistry` / `ToolRegistryItem` generic registry container。dynamic tool provider / decorator contract 已通过 `agent-tools` 提供兼容 re-export,原 `runtime-ports` 路径保持可用;core 旧路径继续 re-export,并只保留 `BitFunError` 映射与路径 containment helper。 -- `bitfun-core::agentic::tools` 现在保留产品完整工具列表、snapshot decorator 组装、旧构造函数、`dyn Tool` 到 generic registry 的适配,以及最新主干新增的 runtime manifest assembly / context filtering / `GetToolSpec` 执行;dynamic metadata map、tool map、dynamic descriptor assembly 和纯 manifest/exposure 契约由 `bitfun-agent-tools` 拥有。 +- 已迁移到 `bitfun-agent-tools`:`ToolResult`、`ValidationResult`、`InputValidator`、dynamic tool metadata、tool render options、runtime restriction DTO、path resolution DTO、不依赖 core service 的 `ToolRegistry` / `ToolRegistryItem` generic registry container,以及 `StaticToolProvider` / `install_static_provider` 安装合约。dynamic tool provider / decorator contract 已通过 `agent-tools` 提供兼容 re-export,原 `runtime-ports` 路径保持可用;core 旧路径继续 re-export,并只保留 `BitFunError` 映射与路径 containment helper。 +- `bitfun-core::agentic::tools` 现在保留 core-owned product provider groups、snapshot decorator 组装、旧构造函数、`dyn Tool` 到 generic registry 的适配,以及最新主干新增的 runtime manifest assembly / context filtering / `GetToolSpec` 执行;dynamic metadata map、tool map、dynamic descriptor assembly、static provider 安装合约和纯 manifest/exposure 契约由 `bitfun-agent-tools` 拥有。 - 已新增 `bitfun-tool-packs` feature scaffold,默认 feature 为空,`product-full` 只聚合 feature,不注册或迁移任何工具实现。 -- 已通过 boundary check 锁定 `agent-tools` / `tool-packs` 暂不拥有 product tool runtime assembly、`GetToolSpecTool` 执行或 collapsed-tool unlock state;`tool-packs` 也不得拥有 manifest/exposure 契约。`agent-tools` 只允许拥有纯 manifest/exposure helper,core product tool runtime 继续负责产品 registry snapshot、context-aware schema/description、unlock state 和执行路径。 +- 已通过 boundary check 锁定 `agent-tools` / `tool-packs` 暂不拥有 product tool runtime assembly、`GetToolSpecTool` 执行或 collapsed-tool unlock state;`tool-packs` 也不得拥有 manifest/exposure 契约。`agent-tools` 只允许拥有纯 manifest/exposure helper 和不依赖具体工具的 provider 安装合约,core product tool runtime 继续负责产品 registry snapshot、context-aware schema/description、unlock state 和执行路径。 - 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,继续迁移前必须先确认可移植 tool context / provider port 方案,并补工具清单等价性测试。 - 最新主干新增的 Deep Review shared-context / evidence-ledger checkpoint hook 仍保留在 core 的 `ToolUseContext` 中;在设计独立 tool context / event port 前,不应把 `ToolUseContext` 或 concrete tool implementation 继续外移。 @@ -1175,7 +1176,7 @@ default = [] **当前收敛状态(2026-05-13):** -- 本轮不把 `remote-ssh` runtime、`remote-connect`、announcement runtime、concrete tool implementations、`ToolUseContext`、product registry / manifest / exposure assembly、miniapp runtime/compiler/builtin、function-agent 运行逻辑声明为已迁移;它们继续作为 `bitfun-core` 的 product runtime assembly 或后续 owner PR 拥有路径。`git` feature group 已外移;`remote-ssh` 目前只外移 contract/type、workspace path/identity helper 与 unresolved-session-key helper;MCP PR2 已外移 config service orchestration、server process / transport lifecycle、adapter 和 dynamic tool/resource/prompt provider;generic tool registry / dynamic descriptor assembly 已由 `bitfun-agent-tools` 拥有,core 只保留 ConfigService store adapter、OAuth data-dir 注入、BitFunError 映射、legacy facade、产品工具列表、tool manifest/exposure 和 snapshot decorator assembly;`announcement` 目前只外移 types contract。 +- 本轮不把 `remote-ssh` runtime、`remote-connect`、announcement runtime、concrete tool implementations、`ToolUseContext`、product registry / manifest / exposure assembly、miniapp runtime/compiler/builtin、function-agent 运行逻辑声明为已迁移;它们继续作为 `bitfun-core` 的 product runtime assembly 或后续 owner PR 拥有路径。`git` feature group 已外移;`remote-ssh` 目前只外移 contract/type、workspace path/identity helper 与 unresolved-session-key helper;MCP PR2 已外移 config service orchestration、server process / transport lifecycle、adapter 和 dynamic tool/resource/prompt provider;generic tool registry / static provider installation / dynamic descriptor assembly 已由 `bitfun-agent-tools` 拥有,core 只保留 ConfigService store adapter、OAuth data-dir 注入、BitFunError 映射、legacy facade、core-owned product provider groups、tool manifest/exposure 和 snapshot decorator assembly;`announcement` 目前只外移 types contract。 - 新增 `scripts/check-core-boundaries.mjs`,用于阻止已拆出的 owner crate 反向依赖 `bitfun-core`。该脚本只证明 crate graph 方向,不替代产品等价性测试。 - `default = []` 仍保持为后续独立评估项,本轮不调整默认 feature、构建脚本或 release 脚本。 @@ -1535,7 +1536,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 契约。core 继续负责产品工具列表、snapshot decorator、`dyn Tool` 适配、runtime manifest assembly / context filtering 与 `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。core 继续负责 concrete tools、snapshot decorator、`dyn Tool` 适配、runtime manifest assembly / context filtering 与 `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 收口混合。 @@ -1561,8 +1562,8 @@ cargo check --workspace - 已将 `product-domains` 的 `dirs` 依赖限制到 `miniapp` feature,默认 profile 保持轻量。 - 已为 `product-domains` 增加 runtime-owner 静态保护,禁止在未确认 port/provider 迁移方案前引入进程启动、具体 Git/AI 服务、网络客户端或平台 API;也已锁定 `agent-tools` / `tool-packs` 暂不拥有 product tool runtime assembly、`GetToolSpecTool` 执行或 collapsed-tool unlock state。 - 已为 core 侧高风险 owner 增加 required-content anchor,覆盖 product tool registry / manifest / `GetToolSpec` / collapsed-tool unlock 流,以及 MiniApp storage/runtime adapter 与 function-agent Git adapter;该检查用于避免“轻量 crate 已抽出”被误解为 runtime owner 已迁移。 -- 已补充 `ToolResult` image attachment、dynamic provider metadata、dynamic descriptor wire shape、runtime restrictions、path resolution contract、generic tool registry descriptor/stale metadata 测试,以及 core 内置 tool registry 清单快照测试;后续迁移 `ToolUseContext`、product registry / manifest assembly 或 concrete tool implementation 前必须保持这些基线。 -- 已将 generic tool registry / dynamic provider descriptor assembly 迁入 `bitfun-agent-tools`;core tool runtime 保留产品完整工具列表、manifest/exposure、snapshot decorator 和 `dyn Tool` 适配,并通过 boundary check 禁止重新拥有 `IndexMap` 工具容器或 dynamic metadata map。 +- 已补充 `ToolResult` image attachment、dynamic provider metadata、dynamic descriptor wire shape、runtime restrictions、path resolution contract、generic tool registry descriptor/stale metadata、static provider 安装,以及 core provider-based 内置 tool registry 清单快照测试;后续迁移 `ToolUseContext`、product registry / manifest assembly 或 concrete tool implementation 前必须保持这些基线。 +- 已将 generic tool registry / static provider installation / dynamic provider descriptor assembly 迁入 `bitfun-agent-tools`;core tool runtime 保留 core-owned product provider groups、manifest/exposure、snapshot decorator 和 `dyn Tool` 适配,并通过 boundary check 禁止重新拥有 `IndexMap` 工具容器、dynamic metadata map,或绕过 provider contract 回到散落手工注册。 - PR 1 已开始执行:remote-SSH workspace registry / ambiguous root resolution / legacy state snapshot 已迁入 `bitfun-services-integrations::remote_ssh::RemoteWorkspaceRegistry`,core 仅保留 local assistant path guard 与 SSH manager / file service / terminal manager 组装;announcement state persistence 已迁入 `bitfun-services-integrations::announcement::AnnouncementStateStore`,core 旧 `PathManager` 构造 API 继续委托并映射原错误类型。 - 本批 dependency profile 基线已验证: - `cargo tree -p bitfun-core-types --depth 1 --edges features` 运行时依赖仅显示 `serde`,测试依赖显示 `serde_json`。 @@ -1622,7 +1623,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 / dynamic provider container,以及纯 manifest/exposure 契约已迁入 `bitfun-agent-tools`,并为 dynamic provider contract 提供 `agent-tools` 兼容 re-export;core tool runtime 保留产品完整工具列表、snapshot decorator、`dyn Tool` 适配、runtime manifest assembly / context filtering 和 `GetToolSpec` 执行。`ToolUseContext`、runtime manifest assembly / `GetToolSpec` 执行与 concrete tool implementation 按 feature group 外移需要新的 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,以及纯 manifest/exposure 契约已迁入 `bitfun-agent-tools`,并为 dynamic provider contract 提供 `agent-tools` 兼容 re-export;core tool runtime 保留 core-owned product provider groups、snapshot decorator、`dyn Tool` 适配、runtime manifest assembly / context filtering 和 `GetToolSpec` 执行。`ToolUseContext`、runtime manifest assembly / `GetToolSpec` 执行与 concrete tool implementation 按 feature group 外移需要新的 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。 @@ -1653,7 +1654,7 @@ P2 后产品表面契约轨道(contract-only): **P3 进入条件与最新主干补充(2026-05-18):** -- P3 只能在 P2 剩余迁移闭环后启动:重 service 迁移、`ToolUseContext` / runtime manifest assembly / `GetToolSpec` 执行 / concrete tool implementation 迁移、product registry / provider assembly、miniapp/function-agent 运行逻辑迁移都必须先完成或显式保留为 core-owned runtime;generic registry/provider container 和纯 manifest/exposure 契约已在 agent-tools 低风险外移中完成。 +- P3 只能在 P2 剩余迁移闭环后启动:重 service 迁移、`ToolUseContext` / runtime manifest assembly / `GetToolSpec` 执行 / concrete tool implementation 迁移、product registry / provider assembly、miniapp/function-agent 运行逻辑迁移都必须先完成或显式保留为 core-owned runtime;generic registry / static-provider / dynamic-provider container 和纯 manifest/exposure 契约已在 agent-tools 低风险外移中完成。 - 最近 `origin/main` 的 Deep Review 变更增加了 context profile、evidence ledger、capacity/cost/queue 控制、`deep_review_run_manifest` / `deep_review_cache`、以及 review-team UI orchestration;最新主干还补充了 agent-stream tool-call dedupe、search remote/fallback、session rollback persistence、remote workspace compatibility guard、ACP startup timeout / operation diff fallback 和 companion typewriter。P3 facade 收敛前必须确认这些行为要么仍由 core product runtime assembly 或对应 product surface 拥有,要么已有对应 owner crate + port/provider 合约和等价测试。 - 最新主干的 mode-scoped subagent visibility 将 `agentic::agents` 重组为 definitions / registry / visibility 边界,并扩展了 desktop subagent API、CLI `/subagents` mode-aware list/config 与 Review Team 可见性测试;后续又加入 `Multitask` mode、内置 `GeneralPurpose` subagent 和后台 subagent result delivery。后续若迁移 agent registry / subagent definitions / scheduler,不能只做路径 re-export,必须保留 mode 可见性过滤、hidden/custom/review 分组语义、CLI availability override 路径、前后端 API contract、`Task.run_in_background` 的 parent metadata / workspace routing、running-turn injection 与 idle-session follow-up turn 语义。 - 最新主干的 DeepResearch citation renumber hook 是 deterministic post-turn runtime 行为,不是普通 prompt 文案;后续若迁移 agent runtime / report finalization,必须保留 `report.md`、`citations.md`、`display_map.json` 与 REJECTED citation 过滤语义。 @@ -1731,7 +1732,7 @@ git diff -- package.json scripts/dev.cjs scripts/desktop-tauri-build.mjs scripts 12. 历史已完成:MCP runtime 与 dynamic tools;已迁移 config service orchestration、server process / transport lifecycle、adapter、dynamic tool/resource/prompt provider,core 保留 ConfigService store adapter、OAuth data-dir 注入、BitFunError 映射、legacy facade 和 product registry / manifest assembly。 13. P2 后前置轨道:产品表面 contract-only 补强,可在后续 PR 第一组提交中处理;只允许 DTO/port、round-trip/no-op tests 和 boundary check,不实现 CLI/Desktop/Remote/ACP UI 或命令变更。 14. 已完成:remote-connect tracker / wire / pure policy owner slice:产品表面 DTO 已以 contract-only 方式进入 `bitfun-core-types`;`bitfun-services-integrations` 的 `remote-connect` feature 拥有 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、tracker state / registry lifecycle、tracker event reduction、legacy image context fallback / preference、restore target decision、cancel decision 与 remote file transfer size/chunk/name policy;relay/bot 创建 session 通过 `AgentSubmissionPort`,取消、远程状态读取和事件事实已有 `runtime-ports` 契约。远程消息执行、`ImageContextData` adapter、file IO/path resolution、terminal pre-warm 与 workspace/session restore 执行仍保留在 `bitfun-core` product runtime assembly。 -15. 已完成:agent tools + `tool-packs` owner 化低风险闭环;tool contract / DTO、runtime restriction、path resolution、generic registry / dynamic provider container 已归属 `bitfun-agent-tools`,core 保留产品工具列表、snapshot decorator、`ToolUseContext` 和 concrete tool implementation,后续外移需单独 port/provider 设计。 +15. 已完成:agent tools + `tool-packs` owner 化低风险闭环;tool contract / DTO、runtime restriction、path resolution、generic registry / static provider installation / dynamic provider container 已归属 `bitfun-agent-tools`,core 保留 core-owned product provider groups、snapshot decorator、`ToolUseContext` 和 concrete tool implementation,后续外移需单独 port/provider 设计。 16. 已完成:关键语义回归 baseline,不移动 runtime owner。覆盖 MCP config failure / catalog invalidation / 既有 list-changed helper / dynamic manifest、tool manifest / `GetToolSpec`、product-domains adapter equivalence、remote workspace search fallback 的 focused tests 或 snapshots。 17. 已完成:remote-connect runtime 当前批次收口。已基于当前 port baseline 记录 remote command/response、remote model catalog、poll response、model catalog delta、session restore、active turn、cancel、image context、tracker event、queue/event fanout 的输入输出和验证命令;tracker state / registry lifecycle、legacy image context fallback / preference、restore target decision、cancel decision 与 remote file transfer size/chunk/name policy 已迁入 `bitfun-services-integrations`。dispatcher / product execution、`ImageContextData` adapter、file IO/path resolution、terminal pre-warm 与 workspace/session restore 执行显式保留在 core-owned runtime;后续只有在另起 port/provider 设计且 focused regression 继续通过时才允许继续移动这些 runtime owner,不能把 generic attachment guard 当作已接入多模态行为。 18. 后续高风险单独审视:`product-domains` runtime + core facade finalization 的剩余 PathManager、process execution、Git/AI service、prompt template、host dispatch 执行与 worker/storage IO owner 迁移;不得与既有 PR2/PR3 范围混合。 diff --git a/scripts/check-core-boundaries.mjs b/scripts/check-core-boundaries.mjs index 07cc8a454..22d67c4c2 100644 --- a/scripts/check-core-boundaries.mjs +++ b/scripts/check-core-boundaries.mjs @@ -1138,15 +1138,15 @@ const requiredContentRules = [ { path: 'src/crates/core/src/agentic/tools/registry.rs', reason: - 'core must continue owning product tool registry assembly until an approved product-provider migration exists', + 'core must continue installing product tool providers until portable tool context and concrete tool-pack migration exist', patterns: [ { - regex: /\bfn register_all_tools\b/, - message: 'missing product tool registration owner', + regex: /\binstall_static_provider\b/, + message: 'missing provider-based registry installation', }, { - regex: /\bGetToolSpecTool::new\(\)/, - message: 'missing GetToolSpec registration anchor', + regex: /\bfn register_all_tools\b/, + message: 'missing product tool registration owner', }, { regex: /\bget_collapsed_tool_names\b/, @@ -1158,6 +1158,55 @@ const requiredContentRules = [ }, ], }, + { + path: 'src/crates/core/src/agentic/tools/static_providers.rs', + reason: + 'core owns builtin product tool provider groups until concrete tool-pack migration exists', + patterns: [ + { + regex: /\bbuiltin_static_tool_providers\b/, + message: 'missing builtin static tool provider owner', + }, + { + regex: /\bStaticToolProvider\b/, + message: 'missing static provider contract use', + }, + { + regex: /core\.basic/, + message: 'missing core basic tool provider group', + }, + { + regex: /core\.agent/, + message: 'missing core agent tool provider group', + }, + { + regex: /core\.session/, + message: 'missing core session tool provider group', + }, + { + regex: /core\.integration/, + message: 'missing core integration tool provider group', + }, + { + regex: /\bGetToolSpecTool::new\(\)/, + message: 'missing GetToolSpec registration anchor', + }, + ], + }, + { + path: 'src/crates/agent-tools/src/framework.rs', + reason: 'agent-tools owns generic registry and static provider installation contracts', + patterns: [ + { + regex: /\bpub trait StaticToolProvider\b/, + message: 'missing static tool provider contract', + }, + { + regex: /\bpub fn install_static_provider\b/, + message: 'missing static provider registry installer', + }, + ], + }, { path: 'src/crates/core/src/agentic/tools/manifest_resolver.rs', reason: @@ -2662,7 +2711,27 @@ function runManifestParserSelfTest() { }, { path: 'src/crates/core/src/agentic/tools/registry.rs', - contracts: ['register_all_tools', 'GetToolSpecTool', 'get_collapsed_tool_names'], + contracts: [ + 'install_static_provider', + 'register_all_tools', + 'get_collapsed_tool_names', + ], + }, + { + path: 'src/crates/core/src/agentic/tools/static_providers.rs', + contracts: [ + 'builtin_static_tool_providers', + 'StaticToolProvider', + 'core.basic', + 'core.agent', + 'core.session', + 'core.integration', + 'GetToolSpecTool', + ], + }, + { + path: 'src/crates/agent-tools/src/framework.rs', + contracts: ['StaticToolProvider', 'install_static_provider'], }, { path: 'src/crates/core/src/agentic/tools/manifest_resolver.rs', diff --git a/src/crates/agent-tools/src/framework.rs b/src/crates/agent-tools/src/framework.rs index 27d88400f..15196b1fe 100644 --- a/src/crates/agent-tools/src/framework.rs +++ b/src/crates/agent-tools/src/framework.rs @@ -217,6 +217,12 @@ impl ToolDecorator for IdentityToolDecorator { pub type ToolRef = Arc; pub type ToolDecoratorRef = Arc>>; +pub trait StaticToolProvider: Send + Sync { + fn provider_id(&self) -> &'static str; + + fn tools(&self) -> Vec>; +} + pub struct ToolRegistry { tools: IndexMap>, dynamic_tools: IndexMap, @@ -267,6 +273,15 @@ impl ToolRegistry { self.tools.insert(name, tool); } + pub fn install_static_provider(&mut self, provider: &Provider) + where + Provider: StaticToolProvider + ?Sized, + { + for tool in provider.tools() { + self.register_tool(tool); + } + } + pub fn unregister_mcp_server_tools(&mut self, server_id: &str) { let to_remove = self .dynamic_tools diff --git a/src/crates/agent-tools/src/lib.rs b/src/crates/agent-tools/src/lib.rs index da071a74a..8b333e977 100644 --- a/src/crates/agent-tools/src/lib.rs +++ b/src/crates/agent-tools/src/lib.rs @@ -11,7 +11,7 @@ pub use bitfun_runtime_ports::{ DynamicToolDescriptor, DynamicToolProvider, PortError, PortErrorKind, PortResult, ToolDecorator, }; pub use framework::{ - DynamicMcpToolInfo, DynamicToolInfo, GET_TOOL_SPEC_TOOL_NAME, ToolExposure, + DynamicMcpToolInfo, DynamicToolInfo, GET_TOOL_SPEC_TOOL_NAME, StaticToolProvider, ToolExposure, ToolManifestDefinition, ToolManifestPolicyResolution, ToolManifestPolicyTool, ToolPathBackend, ToolPathOperation, ToolPathPolicy, ToolPathResolution, ToolRef, ToolRegistry, ToolRegistryItem, ToolRenderOptions, ToolRestrictionError, ToolResult, ToolRuntimeRestrictions, ValidationResult, diff --git a/src/crates/agent-tools/tests/tool_contracts.rs b/src/crates/agent-tools/tests/tool_contracts.rs index a48b91cb8..ff6ca038d 100644 --- a/src/crates/agent-tools/tests/tool_contracts.rs +++ b/src/crates/agent-tools/tests/tool_contracts.rs @@ -6,8 +6,8 @@ use bitfun_agent_tools::{ sort_tool_manifest_definitions, }; use bitfun_agent_tools::{ - DynamicToolDescriptor, DynamicToolProvider, PortResult, ToolDecorator, ToolRegistry, - ToolRegistryItem, + DynamicToolDescriptor, DynamicToolProvider, PortResult, StaticToolProvider, ToolDecorator, + ToolRegistry, ToolRegistryItem, }; use serde_json::json; use std::path::PathBuf; @@ -402,6 +402,7 @@ fn tool_manifest_sorting_preserves_prompt_visible_order() { ); } +#[derive(Clone)] struct RegistryMarkerTool { name: String, provider_id: Option, @@ -443,6 +444,68 @@ fn registry_marker_tool(name: &str, provider_id: Option<&str>) -> Arc>, +} + +impl StaticToolProvider for RegistryMarkerProvider { + fn provider_id(&self) -> &'static str { + self.provider_id + } + + fn tools(&self) -> Vec> { + self.tools.clone() + } +} + +struct RegistryMarkerDecorator; + +impl ToolDecorator> for RegistryMarkerDecorator { + fn decorate(&self, tool: Arc) -> Arc { + Arc::new(RegistryMarkerTool { + name: format!("decorated_{}", tool.name), + provider_id: tool.provider_id.clone(), + }) + } +} + +#[test] +fn generic_tool_registry_installs_static_provider_in_order() { + let mut registry = ToolRegistry::new(); + let provider = RegistryMarkerProvider { + provider_id: "core-basic", + tools: vec![ + registry_marker_tool("Read", None), + registry_marker_tool("Write", None), + ], + }; + + registry.install_static_provider(&provider); + + assert_eq!(provider.provider_id(), "core-basic"); + assert_eq!( + registry.get_tool_names(), + vec!["Read".to_string(), "Write".to_string()] + ); +} + +#[test] +fn generic_tool_registry_applies_decorator_to_static_provider_tools() { + let mut registry = ToolRegistry::with_tool_decorator(Arc::new(RegistryMarkerDecorator)); + let provider = RegistryMarkerProvider { + provider_id: "decorated-provider", + tools: vec![registry_marker_tool("Read", None)], + }; + + registry.install_static_provider(&provider); + + assert_eq!( + registry.get_tool_names(), + vec!["decorated_Read".to_string()] + ); +} + #[tokio::test] async fn generic_tool_registry_preserves_dynamic_descriptor_contract() { let mut registry = ToolRegistry::new(); diff --git a/src/crates/core/AGENTS.md b/src/crates/core/AGENTS.md index a3babbdcb..20368e58a 100644 --- a/src/crates/core/AGENTS.md +++ b/src/crates/core/AGENTS.md @@ -32,8 +32,9 @@ SessionManager → Session → DialogTurn → ModelRound product runtime assembly point. New modules should prefer the extracted owner crate listed in `docs/architecture/core-decomposition.md`. - For tools, keep lightweight contracts, pure manifest/exposure contracts, and - generic registry/provider container logic in `bitfun-agent-tools`. Core tool - runtime should assemble product tools, adapt `dyn Tool`, apply snapshot + generic registry / static-provider / dynamic-provider container contracts in + `bitfun-agent-tools`. Core tool runtime should assemble product tool + providers in `static_providers.rs`, adapt `dyn Tool`, apply snapshot decoration, and own runtime manifest assembly / context filtering plus on-demand spec discovery execution (`GetToolSpec`) for now. - Keep `ToolUseContext` and concrete tool implementations in core unless a diff --git a/src/crates/core/src/agentic/tools/mod.rs b/src/crates/core/src/agentic/tools/mod.rs index 7b568b62f..3b373c01e 100644 --- a/src/crates/core/src/agentic/tools/mod.rs +++ b/src/crates/core/src/agentic/tools/mod.rs @@ -13,6 +13,7 @@ pub mod pipeline; pub(crate) mod post_call_hooks; pub mod registry; pub mod restrictions; +pub(crate) mod static_providers; pub mod user_input_manager; pub mod workspace_paths; pub use bitfun_agent_tools::input_validator; diff --git a/src/crates/core/src/agentic/tools/registry.rs b/src/crates/core/src/agentic/tools/registry.rs index 314252fb3..6abd38b94 100644 --- a/src/crates/core/src/agentic/tools/registry.rs +++ b/src/crates/core/src/agentic/tools/registry.rs @@ -1,7 +1,7 @@ //! Tool registry use crate::agentic::tools::framework::{DynamicToolInfo, Tool, ToolExposure}; -use crate::agentic::tools::implementations::*; +use crate::agentic::tools::static_providers::builtin_static_tool_providers; use crate::util::errors::BitFunResult; use bitfun_agent_tools::{ DynamicToolDescriptor, DynamicToolProvider, PortResult, ToolDecorator, @@ -132,71 +132,9 @@ impl ToolRegistry { /// Register all tools fn register_all_tools(&mut self) { - // Basic tool set - self.register_tool(Arc::new(LSTool::new())); - self.register_tool(Arc::new(FileReadTool::new())); - self.register_tool(Arc::new(GlobTool::new())); - self.register_tool(Arc::new(GrepTool::new())); - self.register_tool(Arc::new(FileWriteTool::new())); - self.register_tool(Arc::new(FileEditTool::new())); - self.register_tool(Arc::new(DeleteFileTool::new())); - self.register_tool(Arc::new(BashTool::new())); - // TaskTool, execute subagent - self.register_tool(Arc::new(TaskTool::new())); - // Skill tool - self.register_tool(Arc::new(SkillTool::new())); - // AskUserQuestion tool - self.register_tool(Arc::new(AskUserQuestionTool::new())); - // TodoWrite tool - self.register_tool(Arc::new(TodoWriteTool::new())); - // CreatePlan tool - self.register_tool(Arc::new(CreatePlanTool::new())); - // Code review submit tool - self.register_tool(Arc::new(CodeReviewTool::new())); - - // GetToolSpec — the discovery entry point for collapsed tools. - self.register_tool(Arc::new(GetToolSpecTool::new())); - - // GetFileDiff tool - self.register_tool(Arc::new(GetFileDiffTool::new())); - // Log tool (debug mode only) - self.register_tool(Arc::new(LogTool::new())); - - // TerminalControl is now accessible via ControlHub's "terminal" domain, - // but we keep it registered separately for backward compatibility. - self.register_tool(Arc::new(TerminalControlTool::new())); - - self.register_tool(Arc::new(SessionControlTool::new())); - self.register_tool(Arc::new(SessionMessageTool::new())); - self.register_tool(Arc::new(SessionHistoryTool::new())); - - // Cron scheduled jobs tool - self.register_tool(Arc::new(CronTool::new())); - - // Web tool - self.register_tool(Arc::new(WebSearchTool::new())); - self.register_tool(Arc::new(WebFetchTool::new())); - - self.register_tool(Arc::new(ListMCPResourcesTool::new())); - self.register_tool(Arc::new(ReadMCPResourceTool::new())); - self.register_tool(Arc::new(ListMCPPromptsTool::new())); - self.register_tool(Arc::new(GetMCPPromptTool::new())); - - self.register_tool(Arc::new(GenerativeUITool::new())); - - // Git version control tool - self.register_tool(Arc::new(GitTool::new())); - - // MiniApp Agent tool (single InitMiniApp) - self.register_tool(Arc::new(InitMiniAppTool::new())); - - // ControlHub — unified browser/terminal/meta control entry point. - // Local desktop and OS/system Computer Use is exposed as a dedicated tool. - self.register_tool(Arc::new(ControlHubTool::new())); - self.register_tool(Arc::new(ComputerUseTool::new())); - - // Playbook — predefined step-by-step operation guides for common tasks. - self.register_tool(Arc::new(PlaybookTool::new())); + for provider in builtin_static_tool_providers() { + self.inner.install_static_provider(&provider); + } } /// Register a single tool @@ -285,8 +223,9 @@ mod tests { use crate::agentic::tools::framework::{ DynamicMcpToolInfo, DynamicToolInfo, Tool, ToolResult, ToolUseContext, ValidationResult, }; + use crate::agentic::tools::static_providers::builtin_static_tool_providers; use async_trait::async_trait; - use bitfun_agent_tools::DynamicToolProvider; + use bitfun_agent_tools::{DynamicToolProvider, StaticToolProvider}; use serde_json::Value; use serde_json::json; use std::sync::Arc; @@ -447,6 +386,40 @@ mod tests { ); } + #[test] + fn builtin_static_tool_providers_cover_registry_manifest_in_order() { + let provider_tools = builtin_static_tool_providers() + .into_iter() + .flat_map(|provider| provider.tools()) + .map(|tool| tool.name().to_string()) + .collect::>(); + + assert_eq!( + provider_tools, + create_tool_registry().get_tool_names(), + "provider-based assembly must preserve the existing builtin registry order" + ); + } + + #[test] + fn builtin_static_tool_providers_keep_owner_group_order() { + let provider_ids = builtin_static_tool_providers() + .into_iter() + .map(|provider| provider.provider_id()) + .collect::>(); + + assert_eq!( + provider_ids, + vec![ + "core.basic", + "core.agent", + "core.session", + "core.integration" + ], + "provider groups must stay stable until concrete tool-pack owners exist" + ); + } + #[test] fn registry_marks_collapsed_tools_for_get_tool_spec() { let registry = create_tool_registry(); diff --git a/src/crates/core/src/agentic/tools/static_providers.rs b/src/crates/core/src/agentic/tools/static_providers.rs new file mode 100644 index 000000000..2b9d2f26b --- /dev/null +++ b/src/crates/core/src/agentic/tools/static_providers.rs @@ -0,0 +1,80 @@ +//! Core-owned static tool provider assembly. + +use crate::agentic::tools::framework::Tool; +use crate::agentic::tools::implementations::*; +use bitfun_agent_tools::{StaticToolProvider, ToolRef}; +use std::sync::Arc; + +pub(crate) struct CoreStaticToolProvider { + provider_id: &'static str, + tools: Vec>, +} + +impl StaticToolProvider for CoreStaticToolProvider { + fn provider_id(&self) -> &'static str { + self.provider_id + } + + fn tools(&self) -> Vec> { + self.tools.clone() + } +} + +pub(crate) fn builtin_static_tool_providers() -> Vec { + vec![ + CoreStaticToolProvider { + provider_id: "core.basic", + tools: vec![ + Arc::new(LSTool::new()), + Arc::new(FileReadTool::new()), + Arc::new(GlobTool::new()), + Arc::new(GrepTool::new()), + Arc::new(FileWriteTool::new()), + Arc::new(FileEditTool::new()), + Arc::new(DeleteFileTool::new()), + Arc::new(BashTool::new()), + ], + }, + CoreStaticToolProvider { + provider_id: "core.agent", + tools: vec![ + Arc::new(TaskTool::new()), + Arc::new(SkillTool::new()), + Arc::new(AskUserQuestionTool::new()), + Arc::new(TodoWriteTool::new()), + Arc::new(CreatePlanTool::new()), + Arc::new(CodeReviewTool::new()), + Arc::new(GetToolSpecTool::new()), + Arc::new(GetFileDiffTool::new()), + Arc::new(LogTool::new()), + ], + }, + CoreStaticToolProvider { + provider_id: "core.session", + tools: vec![ + Arc::new(TerminalControlTool::new()), + Arc::new(SessionControlTool::new()), + Arc::new(SessionMessageTool::new()), + Arc::new(SessionHistoryTool::new()), + Arc::new(CronTool::new()), + ], + }, + CoreStaticToolProvider { + provider_id: "core.integration", + tools: vec![ + Arc::new(WebSearchTool::new()), + Arc::new(WebFetchTool::new()), + Arc::new(ListMCPResourcesTool::new()), + Arc::new(ReadMCPResourceTool::new()), + Arc::new(ListMCPPromptsTool::new()), + Arc::new(GetMCPPromptTool::new()), + Arc::new(GenerativeUITool::new()), + Arc::new(GitTool::new()), + Arc::new(InitMiniAppTool::new()), + Arc::new(ControlHubTool::new()), + Arc::new(ComputerUseTool::new()), + Arc::new(PlaybookTool::new()), + ], + }, + ] +} From b6d44ac489d0005503663d21ededfe3220b2b301 Mon Sep 17 00:00:00 2001 From: limityan Date: Mon, 18 May 2026 17:56:09 +0800 Subject: [PATCH 2/2] refactor(agent-tools): add portable tool context facts --- AGENTS.md | 13 +- docs/architecture/core-decomposition.md | 10 +- docs/plans/core-decomposition-plan.md | 16 +- scripts/check-core-boundaries.mjs | 68 +++++++- src/crates/agent-tools/src/framework.rs | 30 ++++ src/crates/agent-tools/src/lib.rs | 9 +- .../agent-tools/tests/tool_contracts.rs | 82 ++++++++-- src/crates/core/AGENTS.md | 16 +- .../core/src/agentic/tools/framework.rs | 146 +++++++++++++++-- src/crates/core/src/agentic/tools/mod.rs | 5 +- src/crates/tool-packs/Cargo.toml | 6 +- src/crates/tool-packs/src/lib.rs | 147 ++++++++++++++++++ 12 files changed, 501 insertions(+), 47 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 151781129..9cf420bd2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -120,15 +120,20 @@ and milestone verification gates. ### Tool ownership guardrails -- `src/crates/agent-tools` owns lightweight tool contracts, pure - manifest/exposure contracts, and generic registry / static-provider / - dynamic-provider container contracts. +- `src/crates/agent-tools` owns lightweight tool contracts, portable tool + context facts/provider contracts, pure manifest/exposure contracts, and + generic registry / static-provider / dynamic-provider container contracts. +- `src/crates/tool-packs` may expose planned tool-pack feature-group scaffold + metadata, but it must not own concrete tool implementations or product + manifest runtime until a reviewed provider migration exists. - `src/crates/core/src/agentic/tools` owns product tool provider assembly (`static_providers.rs`), `dyn Tool` adaptation, snapshot decoration, runtime manifest assembly / context filtering, and on-demand tool spec discovery execution (`GetToolSpec`) for now. - Keep `ToolUseContext` and concrete tool implementations in core until a - reviewed port/provider design and equivalence tests exist. + reviewed port/provider design and equivalence tests exist. A portable + `ToolContextFacts` projection via `PortableToolContextProvider` may cross + crate boundaries, but runtime handles and service objects must stay in core. - Tool migrations must preserve expanded/collapsed exposure, prompt-visible manifests, `ToolUseContext.unlocked_collapsed_tools`, and desktop/MCP/ACP tool catalog behavior. diff --git a/docs/architecture/core-decomposition.md b/docs/architecture/core-decomposition.md index c514f6209..b32a83915 100644 --- a/docs/architecture/core-decomposition.md +++ b/docs/architecture/core-decomposition.md @@ -66,8 +66,8 @@ 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、runtime restriction、pure manifest/exposure contract、generic registry / static-provider / dynamic-provider container | partial:runtime manifest assembly / context filtering、`ToolUseContext`、`GetToolSpec` 执行和 concrete tools 仍在 core;core 当前仅把内置工具列表收敛为 core-owned static provider group | -| `bitfun-tool-packs` | 由 feature group 隔离的具体工具实现 | target/scaffold:不得声明 concrete tools 已迁移 | +| `bitfun-agent-tools` | 轻量 tool DTO / contract、portable tool context facts / provider、runtime restriction、pure manifest/exposure contract、generic registry / static-provider / dynamic-provider container | partial:runtime manifest assembly / context filtering、`ToolUseContext`、`GetToolSpec` 执行和 concrete tools 仍在 core;core 当前仅把内置工具列表收敛为 core-owned static provider group,并只通过 `PortableToolContextProvider` 提供 `ToolContextFacts` 只读投影 | +| `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 | | `bitfun-product-domains` | Miniapp 和 function-agent 产品子域 | partial:pure decision、port、storage layout 可迁入;IO、worker、Git/AI service runtime 仍在 core | @@ -146,6 +146,12 @@ owner 边界,否则不要把一个 feature group 继续拆成更小的 crate `core.basic`、`core.agent`、`core.session`、`core.integration` provider group。 这不代表 concrete tools、`ToolUseContext`、runtime manifest resolver 或 `GetToolSpecTool` 执行已经迁移。 +- `ToolContextFacts` 只记录 tool call、agent/session/turn、workspace kind/root + 与 runtime restriction 等可移植事实。它不携带 collapsed-tool unlock state、 + `computer_use_host`、workspace services、cancellation token、custom data 或任何 + 可执行 service handle;workspace root 使用 session identity 的 logical path + (remote 为 normalized remote root)。`PortableToolContextProvider` 只是只读 facts + provider 合约,当前由 core `ToolUseContext` 实现;`ToolUseContext` 本体仍归 core 拥有。 - 最新主干的 remote workspace guard 和 search fallback/context 修复提高了 workspace/search 迁移门槛。后续迁移 workspace 或 search runtime 时,必须保留 remote workspace metadata、 startup runtime ensure、remote flashgrep fallback、preview mapping 和 local/remote fallback 语义。 diff --git a/docs/plans/core-decomposition-plan.md b/docs/plans/core-decomposition-plan.md index 6fb1d2efb..d5f429ebc 100644 --- a/docs/plans/core-decomposition-plan.md +++ b/docs/plans/core-decomposition-plan.md @@ -1020,6 +1020,8 @@ cargo check -p bitfun-cli - [x] 抽出 tool result、validation、dynamic metadata、runtime restriction、path resolution DTO,以及 generic registry / dynamic provider container 到 `agent-tools`。 - [x] 抽出纯 manifest/exposure 契约到 `agent-tools`:`ToolExposure`、`GetToolSpec` 名称、纯 manifest policy、collapsed prompt stub 与 prompt-visible ordering;core 继续拥有 runtime assembly。 - [x] 抽出 static tool provider 安装合约到 `agent-tools`,并将 core 内置工具列表收敛到 `static_providers.rs` 的 core-owned provider groups;不迁移 concrete tool implementation。 +- [x] 抽出 `ToolContextFacts` / `ToolWorkspaceKind` 轻量上下文事实契约,并由 core `ToolUseContext` 提供只读投影;workspace root fact 使用 session identity 的 logical path,remote 场景输出 normalized remote root;不迁移 collapsed unlock state、runtime handles、workspace services 或 cancellation token。 +- [x] 增加 `PortableToolContextProvider` 只读 facts provider 合约,并由 core `ToolUseContext` 兼容实现;该合约不暴露 workspace services、cancellation token、computer-use host 或 collapsed unlock state。 - [ ] 抽出 `Tool` trait 与 `ToolUseContext` 前,先补可移植 tool context / service port 设计;当前不做无端口支撑的行为迁移。 - [x] `agent-tools` 不依赖任何 concrete service。 - [ ] 将工具实现迁移到 `tool-packs` crate,并按 feature group 分模块: @@ -1030,7 +1032,7 @@ cargo check -p bitfun-cli - computer use - miniapp - cron/task/agent control -- [x] `tool-packs` 默认 feature 为空,产品完整 runtime 启用 `product-full`。 +- [x] `tool-packs` 默认 feature 为空,产品完整 runtime 启用 `product-full`;当前仅提供 basic / git / mcp / browser-web / computer-use / image-analysis / miniapp / agent-control feature-group 元数据,不注册或迁移任何具体工具。 - [ ] 产品 runtime assembly 注册所有 provider: ```rust @@ -1053,12 +1055,12 @@ pub fn create_tool_registry() -> ToolRegistry { **当前安全迁移状态(2026-05-18):** -- 已迁移到 `bitfun-agent-tools`:`ToolResult`、`ValidationResult`、`InputValidator`、dynamic tool metadata、tool render options、runtime restriction DTO、path resolution DTO、不依赖 core service 的 `ToolRegistry` / `ToolRegistryItem` generic registry container,以及 `StaticToolProvider` / `install_static_provider` 安装合约。dynamic tool provider / decorator contract 已通过 `agent-tools` 提供兼容 re-export,原 `runtime-ports` 路径保持可用;core 旧路径继续 re-export,并只保留 `BitFunError` 映射与路径 containment helper。 -- `bitfun-core::agentic::tools` 现在保留 core-owned product provider groups、snapshot decorator 组装、旧构造函数、`dyn Tool` 到 generic registry 的适配,以及最新主干新增的 runtime manifest assembly / context filtering / `GetToolSpec` 执行;dynamic metadata map、tool map、dynamic descriptor assembly、static provider 安装合约和纯 manifest/exposure 契约由 `bitfun-agent-tools` 拥有。 -- 已新增 `bitfun-tool-packs` feature scaffold,默认 feature 为空,`product-full` 只聚合 feature,不注册或迁移任何工具实现。 +- 已迁移到 `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` 安装合约。dynamic tool provider / decorator contract 已通过 `agent-tools` 提供兼容 re-export,原 `runtime-ports` 路径保持可用;core 旧路径继续 re-export,并只保留 `BitFunError` 映射、路径 containment helper 与 `ToolUseContext` 到 facts 的只读投影。 +- `bitfun-core::agentic::tools` 现在保留 core-owned product provider groups、snapshot decorator 组装、旧构造函数、`dyn Tool` 到 generic registry 的适配、`ToolUseContext` runtime handle / service owner,以及最新主干新增的 runtime manifest assembly / context filtering / `GetToolSpec` 执行;dynamic metadata map、tool map、dynamic descriptor assembly、static provider 安装合约、portable context facts 和纯 manifest/exposure 契约由 `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` 暂不拥有 product tool runtime assembly、`GetToolSpecTool` 执行或 collapsed-tool unlock state;`tool-packs` 也不得拥有 manifest/exposure 契约。`agent-tools` 只允许拥有纯 manifest/exposure helper 和不依赖具体工具的 provider 安装合约,core product tool runtime 继续负责产品 registry snapshot、context-aware schema/description、unlock state 和执行路径。 - 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,继续迁移前必须先确认可移植 tool context / provider port 方案,并补工具清单等价性测试。 +- `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 继续外移。 - 最新主干新增 on-demand tool spec discovery:`ToolExposure`、`GetToolSpec` 名称、collapsed prompt stub 与 manifest ordering 的纯契约已可由 `bitfun-agent-tools` 承载;`manifest_resolver`、collapsed-tool catalog、context-aware `description_with_context` / `input_schema_for_model_with_context`、`GetToolSpecTool` 执行以及 `ToolUseContext.unlocked_collapsed_tools` 仍会影响模型可见工具集合。该变化不推翻 PR4 的低风险结论,但把后续 tool/provider 迁移提升为高风险项,不能在 product-domain runtime 收尾中顺带执行。 @@ -1623,7 +1625,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,以及纯 manifest/exposure 契约已迁入 `bitfun-agent-tools`,并为 dynamic provider contract 提供 `agent-tools` 兼容 re-export;core tool runtime 保留 core-owned product provider groups、snapshot decorator、`dyn Tool` 适配、runtime manifest assembly / context filtering 和 `GetToolSpec` 执行。`ToolUseContext`、runtime manifest assembly / `GetToolSpec` 执行与 concrete tool implementation 按 feature group 外移需要新的 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、`PortableToolContextProvider` 只读 facts provider,以及纯 manifest/exposure 契约已迁入 `bitfun-agent-tools`,并为 dynamic provider contract 提供 `agent-tools` 兼容 re-export;core tool runtime 保留 core-owned product provider groups、snapshot decorator、`dyn Tool` 适配、runtime manifest assembly / context filtering 和 `GetToolSpec` 执行。`tool-packs` 当前只提供计划内 feature-group 元数据,不注册或迁移具体工具。`ToolUseContext`、runtime manifest assembly / `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。 @@ -1732,7 +1734,7 @@ git diff -- package.json scripts/dev.cjs scripts/desktop-tauri-build.mjs scripts 12. 历史已完成:MCP runtime 与 dynamic tools;已迁移 config service orchestration、server process / transport lifecycle、adapter、dynamic tool/resource/prompt provider,core 保留 ConfigService store adapter、OAuth data-dir 注入、BitFunError 映射、legacy facade 和 product registry / manifest assembly。 13. P2 后前置轨道:产品表面 contract-only 补强,可在后续 PR 第一组提交中处理;只允许 DTO/port、round-trip/no-op tests 和 boundary check,不实现 CLI/Desktop/Remote/ACP UI 或命令变更。 14. 已完成:remote-connect tracker / wire / pure policy owner slice:产品表面 DTO 已以 contract-only 方式进入 `bitfun-core-types`;`bitfun-services-integrations` 的 `remote-connect` feature 拥有 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、tracker state / registry lifecycle、tracker event reduction、legacy image context fallback / preference、restore target decision、cancel decision 与 remote file transfer size/chunk/name policy;relay/bot 创建 session 通过 `AgentSubmissionPort`,取消、远程状态读取和事件事实已有 `runtime-ports` 契约。远程消息执行、`ImageContextData` adapter、file IO/path resolution、terminal pre-warm 与 workspace/session restore 执行仍保留在 `bitfun-core` product runtime assembly。 -15. 已完成:agent tools + `tool-packs` owner 化低风险闭环;tool contract / DTO、runtime restriction、path resolution、generic registry / static provider installation / dynamic provider container 已归属 `bitfun-agent-tools`,core 保留 core-owned product provider groups、snapshot decorator、`ToolUseContext` 和 concrete tool implementation,后续外移需单独 port/provider 设计。 +15. 已完成:agent tools + `tool-packs` owner 化低风险闭环;tool contract / DTO、runtime restriction、path resolution、portable context facts/provider、generic registry / static provider installation / dynamic provider container 已归属 `bitfun-agent-tools`,`tool-packs` 只提供计划内 feature-group scaffold,core 保留 core-owned product provider groups、snapshot decorator、`ToolUseContext` 和 concrete tool implementation,后续外移需单独 service port/provider 设计。 16. 已完成:关键语义回归 baseline,不移动 runtime owner。覆盖 MCP config failure / catalog invalidation / 既有 list-changed helper / dynamic manifest、tool manifest / `GetToolSpec`、product-domains adapter equivalence、remote workspace search fallback 的 focused tests 或 snapshots。 17. 已完成:remote-connect runtime 当前批次收口。已基于当前 port baseline 记录 remote command/response、remote model catalog、poll response、model catalog delta、session restore、active turn、cancel、image context、tracker event、queue/event fanout 的输入输出和验证命令;tracker state / registry lifecycle、legacy image context fallback / preference、restore target decision、cancel decision 与 remote file transfer size/chunk/name policy 已迁入 `bitfun-services-integrations`。dispatcher / product execution、`ImageContextData` adapter、file IO/path resolution、terminal pre-warm 与 workspace/session restore 执行显式保留在 core-owned runtime;后续只有在另起 port/provider 设计且 focused regression 继续通过时才允许继续移动这些 runtime owner,不能把 generic attachment guard 当作已接入多模态行为。 18. 后续高风险单独审视:`product-domains` runtime + core facade finalization 的剩余 PathManager、process execution、Git/AI service、prompt template、host dispatch 执行与 worker/storage IO owner 迁移;不得与既有 PR2/PR3 范围混合。 diff --git a/scripts/check-core-boundaries.mjs b/scripts/check-core-boundaries.mjs index 22d67c4c2..d4a04ecbf 100644 --- a/scripts/check-core-boundaries.mjs +++ b/scripts/check-core-boundaries.mjs @@ -341,6 +341,14 @@ const forbiddenContentRules = [ regex: /\bpub struct ToolPathResolution\b/, message: 'core tool framework must not redefine ToolPathResolution; use bitfun-agent-tools', }, + { + regex: /\bpub struct ToolContextFacts\b/, + message: 'core tool framework must not redefine ToolContextFacts; use bitfun-agent-tools', + }, + { + regex: /\bpub enum ToolWorkspaceKind\b/, + message: 'core tool framework must not redefine ToolWorkspaceKind; use bitfun-agent-tools', + }, ], }, { @@ -1195,8 +1203,20 @@ const requiredContentRules = [ }, { path: 'src/crates/agent-tools/src/framework.rs', - reason: 'agent-tools owns generic registry and static provider installation contracts', + reason: 'agent-tools owns portable tool facts plus generic registry and provider contracts', patterns: [ + { + regex: /\bpub struct ToolContextFacts\b/, + message: 'missing portable tool context facts contract', + }, + { + regex: /\bpub trait PortableToolContextProvider\b/, + message: 'missing portable tool context provider contract', + }, + { + regex: /\bpub enum ToolWorkspaceKind\b/, + message: 'missing portable workspace kind contract', + }, { regex: /\bpub trait StaticToolProvider\b/, message: 'missing static tool provider contract', @@ -1207,6 +1227,25 @@ const requiredContentRules = [ }, ], }, + { + path: 'src/crates/tool-packs/src/lib.rs', + reason: + 'tool-packs must keep its feature-group scaffold explicit without owning concrete tools yet', + patterns: [ + { + regex: /\bpub enum ToolPackFeatureGroup\b/, + message: 'missing tool-pack feature group scaffold', + }, + { + regex: /\bpub fn all_feature_groups\b/, + message: 'missing tool-pack full feature group metadata helper', + }, + { + regex: /\bpub fn enabled_feature_groups\b/, + message: 'missing tool-pack compile-time feature metadata helper', + }, + ], + }, { path: 'src/crates/core/src/agentic/tools/manifest_resolver.rs', reason: @@ -1266,6 +1305,10 @@ const requiredContentRules = [ regex: /\bpub struct ToolUseContext\b/, message: 'missing ToolUseContext owner type', }, + { + regex: /\bto_tool_context_facts\b/, + message: 'missing portable ToolUseContext facts projection', + }, { regex: /\bunlocked_collapsed_tools\b/, message: 'missing collapsed-tool unlock state', @@ -2731,7 +2774,21 @@ function runManifestParserSelfTest() { }, { path: 'src/crates/agent-tools/src/framework.rs', - contracts: ['StaticToolProvider', 'install_static_provider'], + contracts: [ + 'ToolContextFacts', + 'PortableToolContextProvider', + 'ToolWorkspaceKind', + 'StaticToolProvider', + 'install_static_provider', + ], + }, + { + path: 'src/crates/tool-packs/src/lib.rs', + contracts: [ + 'ToolPackFeatureGroup', + 'all_feature_groups', + 'enabled_feature_groups', + ], }, { path: 'src/crates/core/src/agentic/tools/manifest_resolver.rs', @@ -2749,7 +2806,12 @@ function runManifestParserSelfTest() { }, { path: 'src/crates/core/src/agentic/tools/framework.rs', - contracts: ['ToolExposure', 'ToolUseContext', 'unlocked_collapsed_tools'], + contracts: [ + 'ToolExposure', + 'ToolUseContext', + 'to_tool_context_facts', + 'unlocked_collapsed_tools', + ], }, { path: 'src/crates/core/src/agentic/tools/pipeline/tool_pipeline.rs', diff --git a/src/crates/agent-tools/src/framework.rs b/src/crates/agent-tools/src/framework.rs index 15196b1fe..a2c04cf32 100644 --- a/src/crates/agent-tools/src/framework.rs +++ b/src/crates/agent-tools/src/framework.rs @@ -31,6 +31,36 @@ pub struct DynamicToolInfo { pub mcp: Option, } +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +pub enum ToolWorkspaceKind { + Local, + Remote, +} + +#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "camelCase")] +pub struct ToolContextFacts { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub tool_call_id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub agent_type: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub session_id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub dialog_turn_id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub workspace_kind: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub workspace_root: Option, + #[serde(default)] + pub runtime_tool_restrictions: ToolRuntimeRestrictions, +} + +pub trait PortableToolContextProvider: Send + Sync { + fn tool_context_facts(&self) -> ToolContextFacts; +} + pub const GET_TOOL_SPEC_TOOL_NAME: &str = "GetToolSpec"; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] diff --git a/src/crates/agent-tools/src/lib.rs b/src/crates/agent-tools/src/lib.rs index 8b333e977..67eab1384 100644 --- a/src/crates/agent-tools/src/lib.rs +++ b/src/crates/agent-tools/src/lib.rs @@ -11,11 +11,12 @@ pub use bitfun_runtime_ports::{ DynamicToolDescriptor, DynamicToolProvider, PortError, PortErrorKind, PortResult, ToolDecorator, }; pub use framework::{ - DynamicMcpToolInfo, DynamicToolInfo, GET_TOOL_SPEC_TOOL_NAME, StaticToolProvider, ToolExposure, + build_collapsed_tool_stub_definition, resolve_tool_manifest_policy, + sort_tool_manifest_definitions, tool_manifest_sort_rank, DynamicMcpToolInfo, DynamicToolInfo, + PortableToolContextProvider, StaticToolProvider, ToolContextFacts, ToolExposure, ToolManifestDefinition, ToolManifestPolicyResolution, ToolManifestPolicyTool, ToolPathBackend, ToolPathOperation, ToolPathPolicy, ToolPathResolution, ToolRef, ToolRegistry, ToolRegistryItem, - ToolRenderOptions, ToolRestrictionError, ToolResult, ToolRuntimeRestrictions, ValidationResult, - build_collapsed_tool_stub_definition, resolve_tool_manifest_policy, - sort_tool_manifest_definitions, tool_manifest_sort_rank, + ToolRenderOptions, ToolRestrictionError, ToolResult, 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 ff6ca038d..1bb7b099d 100644 --- a/src/crates/agent-tools/tests/tool_contracts.rs +++ b/src/crates/agent-tools/tests/tool_contracts.rs @@ -1,13 +1,13 @@ use bitfun_agent_tools::{ - DynamicMcpToolInfo, DynamicToolInfo, GET_TOOL_SPEC_TOOL_NAME, InputValidator, ToolExposure, - ToolImageAttachment, ToolManifestDefinition, ToolManifestPolicyTool, ToolPathBackend, - ToolPathResolution, ToolRenderOptions, ToolResult, ToolRuntimeRestrictions, ValidationResult, build_collapsed_tool_stub_definition, resolve_tool_manifest_policy, - sort_tool_manifest_definitions, + sort_tool_manifest_definitions, DynamicMcpToolInfo, DynamicToolInfo, InputValidator, + ToolContextFacts, ToolExposure, ToolImageAttachment, ToolManifestDefinition, + ToolManifestPolicyTool, ToolPathBackend, ToolPathResolution, ToolRenderOptions, ToolResult, + ToolRuntimeRestrictions, ToolWorkspaceKind, ValidationResult, GET_TOOL_SPEC_TOOL_NAME, }; use bitfun_agent_tools::{ - DynamicToolDescriptor, DynamicToolProvider, PortResult, StaticToolProvider, ToolDecorator, - ToolRegistry, ToolRegistryItem, + DynamicToolDescriptor, DynamicToolProvider, PortResult, PortableToolContextProvider, + StaticToolProvider, ToolDecorator, ToolRegistry, ToolRegistryItem, }; use serde_json::json; use std::path::PathBuf; @@ -137,6 +137,69 @@ fn runtime_restrictions_keep_allow_deny_semantics_without_core_dependency() { ); } +#[test] +fn tool_context_facts_keep_portable_wire_shape_without_runtime_handles() { + let facts = ToolContextFacts { + tool_call_id: Some("call-1".to_string()), + agent_type: Some("Agentic".to_string()), + session_id: Some("session-1".to_string()), + dialog_turn_id: Some("turn-1".to_string()), + workspace_kind: Some(ToolWorkspaceKind::Remote), + workspace_root: Some("/remote/workspace".to_string()), + runtime_tool_restrictions: ToolRuntimeRestrictions::default(), + }; + + let value = serde_json::to_value(&facts).expect("serialize context facts"); + + assert_eq!(value["toolCallId"], "call-1"); + assert_eq!(value["agentType"], "Agentic"); + assert_eq!(value["sessionId"], "session-1"); + assert_eq!(value["dialogTurnId"], "turn-1"); + assert_eq!(value["workspaceKind"], "remote"); + assert_eq!(value["workspaceRoot"], "/remote/workspace"); + assert!(value.get("unlockedCollapsedTools").is_none()); + assert!(value.get("computer_use_host").is_none()); + assert!(value.get("workspace_services").is_none()); + assert!(value.get("cancellation_token").is_none()); + + let round_trip: ToolContextFacts = + serde_json::from_value(value).expect("deserialize context facts"); + assert_eq!(round_trip.workspace_kind, Some(ToolWorkspaceKind::Remote)); +} + +#[test] +fn portable_tool_context_provider_exposes_facts_only() { + struct FactsOnlyProvider { + facts: ToolContextFacts, + } + + impl PortableToolContextProvider for FactsOnlyProvider { + fn tool_context_facts(&self) -> ToolContextFacts { + self.facts.clone() + } + } + + let provider = FactsOnlyProvider { + facts: ToolContextFacts { + tool_call_id: Some("call-2".to_string()), + agent_type: Some("Agentic".to_string()), + session_id: Some("session-2".to_string()), + dialog_turn_id: None, + workspace_kind: Some(ToolWorkspaceKind::Local), + workspace_root: Some("/repo/project".to_string()), + runtime_tool_restrictions: ToolRuntimeRestrictions::default(), + }, + }; + + let value = + serde_json::to_value(provider.tool_context_facts()).expect("serialize context facts"); + + assert_eq!(value["toolCallId"], "call-2"); + assert_eq!(value["workspaceKind"], "local"); + assert!(value.get("workspace_services").is_none()); + assert!(value.get("unlockedCollapsedTools").is_none()); +} + #[test] fn runtime_restrictions_keep_current_snake_case_wire_shape() { let value = json!({ @@ -362,10 +425,9 @@ fn collapsed_tool_stub_definition_preserves_prompt_visible_guardrail() { assert_eq!(stub.name, "WebFetch"); assert!(stub.description.contains("Fetch a URL")); - assert!( - stub.description - .contains("First call `GetToolSpec` with {\"tool_name\":\"WebFetch\"}") - ); + assert!(stub + .description + .contains("First call `GetToolSpec` with {\"tool_name\":\"WebFetch\"}")); assert_eq!( stub.parameters, json!({ diff --git a/src/crates/core/AGENTS.md b/src/crates/core/AGENTS.md index 20368e58a..f2826b2e7 100644 --- a/src/crates/core/AGENTS.md +++ b/src/crates/core/AGENTS.md @@ -32,13 +32,17 @@ SessionManager → Session → DialogTurn → ModelRound product runtime assembly point. New modules should prefer the extracted owner crate listed in `docs/architecture/core-decomposition.md`. - For tools, keep lightweight contracts, pure manifest/exposure contracts, and - generic registry / static-provider / dynamic-provider container contracts in - `bitfun-agent-tools`. Core tool runtime should assemble product tool - providers in `static_providers.rs`, adapt `dyn Tool`, apply snapshot - decoration, and own runtime manifest assembly / context filtering plus - on-demand spec discovery execution (`GetToolSpec`) for now. + portable tool context facts/provider plus generic registry / static-provider + / dynamic-provider container contracts in `bitfun-agent-tools`. Core tool + runtime should assemble product tool providers in `static_providers.rs`, + adapt `dyn Tool`, apply snapshot decoration, and own runtime manifest + assembly / context filtering plus on-demand spec discovery execution + (`GetToolSpec`) for now. `bitfun-tool-packs` may expose planned + feature-group scaffold metadata, but it must not own concrete tools yet. - Keep `ToolUseContext` and concrete tool implementations in core unless a - reviewed port/provider plan and equivalence tests exist. + reviewed port/provider plan and equivalence tests exist. `ToolContextFacts` + / `PortableToolContextProvider` are only portable projections; they must not + carry runtime handles, workspace services, or cancellation tokens. - Any tool migration must preserve expanded/collapsed exposure, prompt-visible manifests, `ToolUseContext.unlocked_collapsed_tools`, and desktop/MCP/ACP tool catalog behavior. diff --git a/src/crates/core/src/agentic/tools/framework.rs b/src/crates/core/src/agentic/tools/framework.rs index 3f45227d4..81279b1d3 100644 --- a/src/crates/core/src/agentic/tools/framework.rs +++ b/src/crates/core/src/agentic/tools/framework.rs @@ -1,26 +1,27 @@ //! Tool framework - Tool interface definition and execution context -use crate::agentic::WorkspaceBinding; use crate::agentic::coordination::get_global_coordinator; use crate::agentic::session::EvidenceLedgerCheckpoint; use crate::agentic::tools::post_call_hooks; use crate::agentic::tools::restrictions::{ - ToolPathOperation, ToolRuntimeRestrictions, is_local_path_within_root, - is_remote_posix_path_within_root, + is_local_path_within_root, is_remote_posix_path_within_root, ToolPathOperation, + ToolRuntimeRestrictions, }; use crate::agentic::tools::workspace_paths::{ build_bitfun_runtime_uri, is_bitfun_runtime_uri, normalize_runtime_relative_path, parse_bitfun_runtime_uri, }; use crate::agentic::workspace::WorkspaceServices; +use crate::agentic::WorkspaceBinding; use crate::infrastructure::get_path_manager_arc; use crate::service::git::{GitDiffParams, GitService}; use crate::service::remote_ssh::workspace_state::remote_workspace_runtime_root; -use crate::service::{WorkspaceRuntimeContext, get_workspace_runtime_service_arc}; +use crate::service::{get_workspace_runtime_service_arc, WorkspaceRuntimeContext}; use crate::util::errors::BitFunResult; use async_trait::async_trait; pub use bitfun_agent_tools::{ - DynamicMcpToolInfo, DynamicToolInfo, ToolExposure, ToolPathBackend, ToolPathResolution, - ToolRenderOptions, ToolResult, ValidationResult, + DynamicMcpToolInfo, DynamicToolInfo, PortableToolContextProvider, ToolContextFacts, + ToolExposure, ToolPathBackend, ToolPathResolution, ToolRenderOptions, ToolResult, + ToolWorkspaceKind, ValidationResult, }; use log::warn; use serde_json::Value; @@ -62,6 +63,31 @@ impl ToolUseContext { .unwrap_or(false) } + pub fn to_tool_context_facts(&self) -> ToolContextFacts { + let workspace_kind = self.workspace.as_ref().map(|workspace| { + if workspace.is_remote() { + ToolWorkspaceKind::Remote + } else { + ToolWorkspaceKind::Local + } + }); + + ToolContextFacts { + tool_call_id: self.tool_call_id.clone(), + agent_type: self.agent_type.clone(), + session_id: self.session_id.clone(), + dialog_turn_id: self.dialog_turn_id.clone(), + workspace_kind, + workspace_root: self.workspace.as_ref().map(|workspace| { + workspace + .session_identity + .logical_workspace_path() + .to_string() + }), + runtime_tool_restrictions: self.runtime_tool_restrictions.clone(), + } + } + pub fn ws_fs(&self) -> Option<&dyn crate::agentic::workspace::WorkspaceFileSystem> { self.workspace_services.as_ref().map(|s| s.fs.as_ref()) } @@ -434,12 +460,21 @@ impl ToolUseContext { } } +impl PortableToolContextProvider for ToolUseContext { + fn tool_context_facts(&self) -> ToolContextFacts { + self.to_tool_context_facts() + } +} + #[cfg(test)] mod path_resolution_tests { use super::ToolUseContext; + use crate::agentic::tools::{ + PortableToolContextProvider, ToolRuntimeRestrictions, ToolWorkspaceKind, + }; use crate::agentic::WorkspaceBinding; - use crate::agentic::tools::ToolRuntimeRestrictions; - use std::collections::HashMap; + use crate::service::remote_ssh::workspace_state::workspace_session_identity; + use std::collections::{BTreeSet, HashMap}; use std::path::PathBuf; fn local_context(root: &str) -> ToolUseContext { @@ -474,6 +509,99 @@ mod path_resolution_tests { } } + #[test] + fn tool_context_facts_preserve_portable_fields_without_runtime_handles() { + let context = ToolUseContext { + tool_call_id: Some("call-1".to_string()), + agent_type: Some("Agentic".to_string()), + session_id: Some("session-1".to_string()), + dialog_turn_id: Some("turn-1".to_string()), + workspace: Some(WorkspaceBinding::new(None, PathBuf::from("/repo/project"))), + unlocked_collapsed_tools: vec!["WebFetch".to_string()], + custom_data: HashMap::new(), + computer_use_host: None, + cancellation_token: None, + runtime_tool_restrictions: ToolRuntimeRestrictions { + allowed_tool_names: BTreeSet::from(["Read".to_string()]), + denied_tool_names: BTreeSet::from(["Bash".to_string()]), + path_policy: Default::default(), + }, + workspace_services: None, + }; + + let facts = context.to_tool_context_facts(); + + assert_eq!(facts.tool_call_id.as_deref(), Some("call-1")); + assert_eq!(facts.agent_type.as_deref(), Some("Agentic")); + assert_eq!(facts.session_id.as_deref(), Some("session-1")); + assert_eq!(facts.dialog_turn_id.as_deref(), Some("turn-1")); + assert_eq!(facts.workspace_kind, Some(ToolWorkspaceKind::Local)); + assert_eq!(facts.workspace_root.as_deref(), Some("/repo/project")); + assert!(facts.runtime_tool_restrictions.is_tool_allowed("Read")); + assert!(!facts.runtime_tool_restrictions.is_tool_allowed("Bash")); + + let value = serde_json::to_value(&facts).expect("serialize context facts"); + assert!(value.get("unlockedCollapsedTools").is_none()); + assert!(value.get("computer_use_host").is_none()); + assert!(value.get("workspace_services").is_none()); + assert!(value.get("cancellation_token").is_none()); + } + + #[test] + fn tool_context_facts_use_normalized_remote_workspace_identity() { + let session_identity = workspace_session_identity( + "/home/wsp//projects/test/", + Some("conn-1"), + Some("ssh.dev"), + ) + .expect("remote identity"); + let context = ToolUseContext { + tool_call_id: None, + agent_type: None, + session_id: Some("session-remote".to_string()), + dialog_turn_id: None, + workspace: Some(WorkspaceBinding::new_remote( + Some("workspace-remote".to_string()), + PathBuf::from("/home/wsp//projects/test/"), + "conn-1".to_string(), + "Dev SSH".to_string(), + session_identity, + )), + unlocked_collapsed_tools: Vec::new(), + custom_data: HashMap::new(), + computer_use_host: None, + cancellation_token: None, + runtime_tool_restrictions: ToolRuntimeRestrictions::default(), + workspace_services: None, + }; + + let facts = context.to_tool_context_facts(); + + assert_eq!(facts.workspace_kind, Some(ToolWorkspaceKind::Remote)); + assert_eq!( + facts.workspace_root.as_deref(), + Some("/home/wsp/projects/test") + ); + + let value = serde_json::to_value(&facts).expect("serialize remote context facts"); + assert!(value.get("connectionId").is_none()); + assert!(value.get("connectionName").is_none()); + assert!(value.get("workspace_services").is_none()); + } + + #[test] + fn tool_use_context_implements_portable_context_provider() { + fn assert_provider() {} + assert_provider::(); + + let context = local_context("/repo/project"); + + let facts = PortableToolContextProvider::tool_context_facts(&context); + + assert_eq!(facts.workspace_kind, Some(ToolWorkspaceKind::Local)); + assert_eq!(facts.workspace_root.as_deref(), Some("/repo/project")); + } + #[test] fn workspace_path_resolution_rejects_absolute_paths_outside_local_workspace() { let context = local_context("/repo/project"); @@ -710,7 +838,7 @@ mod shared_context_tests { use crate::agentic::tools::ToolRuntimeRestrictions; use crate::util::errors::BitFunResult; use async_trait::async_trait; - use serde_json::{Value, json}; + use serde_json::{json, Value}; use std::collections::HashMap; struct MeasurementReadTool; diff --git a/src/crates/core/src/agentic/tools/mod.rs b/src/crates/core/src/agentic/tools/mod.rs index 3b373c01e..0d659cef1 100644 --- a/src/crates/core/src/agentic/tools/mod.rs +++ b/src/crates/core/src/agentic/tools/mod.rs @@ -18,7 +18,10 @@ pub mod user_input_manager; pub mod workspace_paths; pub use bitfun_agent_tools::input_validator; -pub use framework::{Tool, ToolResult, ToolUseContext, ValidationResult}; +pub use framework::{ + PortableToolContextProvider, Tool, ToolContextFacts, ToolResult, ToolUseContext, + ToolWorkspaceKind, ValidationResult, +}; pub use image_context::{ImageContextData, ImageContextProvider, ImageContextProviderRef}; pub use input_validator::InputValidator; pub use manifest_resolver::{ diff --git a/src/crates/tool-packs/Cargo.toml b/src/crates/tool-packs/Cargo.toml index 8e91d0842..d0c85f846 100644 --- a/src/crates/tool-packs/Cargo.toml +++ b/src/crates/tool-packs/Cargo.toml @@ -14,5 +14,9 @@ default = [] basic = [] git = [] mcp = [] +browser-web = [] computer-use = [] -product-full = ["basic", "git", "mcp", "computer-use"] +image-analysis = [] +miniapp = [] +agent-control = [] +product-full = ["basic", "git", "mcp", "browser-web", "computer-use", "image-analysis", "miniapp", "agent-control"] diff --git a/src/crates/tool-packs/src/lib.rs b/src/crates/tool-packs/src/lib.rs index 797ea08d2..9dea9329b 100644 --- a/src/crates/tool-packs/src/lib.rs +++ b/src/crates/tool-packs/src/lib.rs @@ -2,3 +2,150 @@ //! //! The feature scaffold is intentionally behavior-neutral until the core //! `ToolUseContext` and registry boundaries are split into portable ports. + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum ToolPackFeatureGroup { + Basic, + Git, + Mcp, + BrowserWeb, + ComputerUse, + ImageAnalysis, + MiniApp, + AgentControl, +} + +impl ToolPackFeatureGroup { + pub const fn id(self) -> &'static str { + match self { + Self::Basic => "basic", + Self::Git => "git", + Self::Mcp => "mcp", + Self::BrowserWeb => "browser-web", + Self::ComputerUse => "computer-use", + Self::ImageAnalysis => "image-analysis", + Self::MiniApp => "miniapp", + Self::AgentControl => "agent-control", + } + } +} + +pub const ALL_FEATURE_GROUPS: &[ToolPackFeatureGroup] = &[ + ToolPackFeatureGroup::Basic, + ToolPackFeatureGroup::Git, + ToolPackFeatureGroup::Mcp, + ToolPackFeatureGroup::BrowserWeb, + ToolPackFeatureGroup::ComputerUse, + ToolPackFeatureGroup::ImageAnalysis, + ToolPackFeatureGroup::MiniApp, + ToolPackFeatureGroup::AgentControl, +]; + +pub fn all_feature_groups() -> &'static [ToolPackFeatureGroup] { + ALL_FEATURE_GROUPS +} + +pub fn enabled_feature_groups() -> Vec { + [ + (cfg!(feature = "basic"), ToolPackFeatureGroup::Basic), + (cfg!(feature = "git"), ToolPackFeatureGroup::Git), + (cfg!(feature = "mcp"), ToolPackFeatureGroup::Mcp), + ( + cfg!(feature = "browser-web"), + ToolPackFeatureGroup::BrowserWeb, + ), + ( + cfg!(feature = "computer-use"), + ToolPackFeatureGroup::ComputerUse, + ), + ( + cfg!(feature = "image-analysis"), + ToolPackFeatureGroup::ImageAnalysis, + ), + (cfg!(feature = "miniapp"), ToolPackFeatureGroup::MiniApp), + ( + cfg!(feature = "agent-control"), + ToolPackFeatureGroup::AgentControl, + ), + ] + .into_iter() + .filter_map(|(enabled, group)| enabled.then_some(group)) + .collect() +} + +#[cfg(test)] +mod tests { + use super::{all_feature_groups, enabled_feature_groups, ToolPackFeatureGroup}; + + #[test] + fn all_feature_groups_cover_planned_tool_pack_scaffold() { + let feature_ids = all_feature_groups() + .iter() + .map(|group| group.id()) + .collect::>(); + + assert_eq!( + feature_ids, + vec![ + "basic", + "git", + "mcp", + "browser-web", + "computer-use", + "image-analysis", + "miniapp", + "agent-control" + ] + ); + } + + #[test] + fn enabled_feature_groups_reflect_compile_time_features() { + let groups = enabled_feature_groups(); + + assert_eq!( + groups.contains(&ToolPackFeatureGroup::Basic), + cfg!(feature = "basic") + ); + assert_eq!( + groups.contains(&ToolPackFeatureGroup::Git), + cfg!(feature = "git") + ); + assert_eq!( + groups.contains(&ToolPackFeatureGroup::Mcp), + cfg!(feature = "mcp") + ); + assert_eq!( + groups.contains(&ToolPackFeatureGroup::BrowserWeb), + cfg!(feature = "browser-web") + ); + assert_eq!( + groups.contains(&ToolPackFeatureGroup::ComputerUse), + cfg!(feature = "computer-use") + ); + assert_eq!( + groups.contains(&ToolPackFeatureGroup::ImageAnalysis), + cfg!(feature = "image-analysis") + ); + assert_eq!( + groups.contains(&ToolPackFeatureGroup::MiniApp), + cfg!(feature = "miniapp") + ); + assert_eq!( + groups.contains(&ToolPackFeatureGroup::AgentControl), + cfg!(feature = "agent-control") + ); + } + + #[test] + fn feature_group_ids_match_cargo_feature_names() { + assert_eq!(ToolPackFeatureGroup::Basic.id(), "basic"); + assert_eq!(ToolPackFeatureGroup::Git.id(), "git"); + assert_eq!(ToolPackFeatureGroup::Mcp.id(), "mcp"); + assert_eq!(ToolPackFeatureGroup::BrowserWeb.id(), "browser-web"); + assert_eq!(ToolPackFeatureGroup::ComputerUse.id(), "computer-use"); + assert_eq!(ToolPackFeatureGroup::ImageAnalysis.id(), "image-analysis"); + assert_eq!(ToolPackFeatureGroup::MiniApp.id(), "miniapp"); + assert_eq!(ToolPackFeatureGroup::AgentControl.id(), "agent-control"); + } +}