diff --git a/AGENTS.md b/AGENTS.md index dad1120bc..1fb07a749 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -120,11 +120,13 @@ and milestone verification gates. ### Tool ownership guardrails -- `src/crates/agent-tools` owns lightweight tool contracts and the generic - registry / dynamic-provider container. +- `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, tool exposure / manifest resolution, and - on-demand tool spec discovery (`GetToolSpec`) for now. + 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 5f57272d7..e9b78529b 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、generic registry / provider container | partial:product manifest、`ToolUseContext`、`GetToolSpec` 和 concrete tools 仍在 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-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 | @@ -135,11 +135,12 @@ owner 边界,否则不要把一个 feature group 继续拆成更小的 crate - DeepResearch 现在包含 citation renumber post-turn hook。迁移 agent runtime 或 prompt/report 处理前,必须保留 `report.md` / `citations.md` / `display_map.json` 的 deterministic post-processing 行为; 在此之前该 hook 仍属于 `bitfun-core` agent runtime assembly。 -- 最新主干新增 on-demand tool spec discovery。`ToolExposure`、`GetToolSpec`、 - `manifest_resolver`、collapsed-tool catalog、context-aware tool schema/description - 和 `ToolUseContext.unlocked_collapsed_tools` 暂时属于 `bitfun-core` product tool runtime; - 迁移前必须证明 prompt-visible manifest、expanded/collapsed exposure、unlock state 与 - desktop/MCP/ACP tool catalog 等价。 +- 最新主干新增 on-demand tool spec discovery。`ToolExposure`、`GetToolSpec` 名称、 + collapsed stub 和 manifest ordering 等纯契约可由 `bitfun-agent-tools` 拥有;但 + `manifest_resolver`、产品 registry snapshot、collapsed-tool catalog、context-aware + 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 等价。 - 最新主干的 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 语义。 @@ -183,7 +184,7 @@ owner 边界,否则不要把一个 feature group 继续拆成更小的 crate built-in asset seeding/source-hash lookup, host-dispatch execution, `PathManager` integration, function-agent Git/AI calls, prompt templates, JSON extraction, and error mapping. - - 高风险:`ToolUseContext`、product tool registry / manifest / exposure / `GetToolSpec` owner 化、 + - 高风险:`ToolUseContext`、product tool registry / runtime manifest assembly / `GetToolSpec` 执行 owner 化、 MCP concrete tool integration、remote-connect、remote SSH runtime、miniapp / function-agent runtime、 agent registry、`bitfun-core default = []` 或任何产品 crate feature set 调整。 @@ -193,7 +194,7 @@ owner 边界,否则不要把一个 feature group 继续拆成更小的 crate owner 主题:`services-integrations` runtime 收口、MCP runtime/dynamic tools、 remote-connect runtime、agent tools + `tool-packs` owner 化、`product-domains` runtime + core facade finalization。PR 2 的 MCP runtime/dynamic tools 已完成;后续不得把 - remote-connect、product tool manifest/exposure owner 化或 product-domain runtime 顺带混入已完成的 MCP PR。 + remote-connect、product tool runtime manifest / `GetToolSpec` 执行 owner 化或 product-domain runtime 顺带混入已完成的 MCP PR。 `bitfun-core default = []` 和 per-product feature matrix 仍是上述 runtime 队列之后的独立评估。 - 当前批次的 remote-connect runtime 收口以“tracker / wire / pure policy / registry lifecycle 归 `bitfun-services-integrations`,dispatcher / product execution 显式保留 core-owned”作为可合入闭环。 @@ -203,7 +204,7 @@ owner 边界,否则不要把一个 feature group 继续拆成更小的 crate transport lifecycle、resource/prompt adapter、catalog cache、list-changed / reconnect policy、 dynamic tool descriptor、dynamic tool provider 与 result rendering。`bitfun-core` 保留 core `ConfigService` store adapter、OAuth data-dir 注入、`BitFunError` 映射、旧路径 facade - 和全局 tool registry / manifest 组装;product tool manifest/exposure owner 化仍归后续 tool/provider PR。 + 和全局 tool registry / manifest 组装;product tool runtime manifest / `GetToolSpec` 执行 owner 化仍归后续 tool/provider PR。 - core MCP facade 当前允许保留窄 adapter 语义:data-dir injection、credential/config store adapter、 `BitFunError` 映射、legacy facade、product tool wrapper 和 global registry / manifest 接入。 如果继续收敛 MCP manager 行为,必须先补 config failure、catalog invalidation、list-changed @@ -211,6 +212,12 @@ owner 边界,否则不要把一个 feature group 继续拆成更小的 crate - 已合入的 semantic baseline 已补 config failure、catalog replacement invalidation、沿用既有 list-changed helper baseline、dynamic manifest order/metadata、tool manifest / `GetToolSpec`、MiniApp storage layout adapter 等价和 remote search fallback gate;这些都是 behavior-locking tests,不移动 runtime owner。 +- 当前 tool manifest contract closure PR 只把 `ToolExposure`、`GetToolSpec` 名称、纯 manifest + policy、collapsed prompt stub 与 prompt-visible ordering 归入 `bitfun-agent-tools`,并让 core + runtime manifest resolver 委托这些纯 helper;不迁移 `ToolUseContext`、registry snapshot、 + context-aware schema/description、`GetToolSpecTool` 执行、unlock state 或 concrete tool implementation。 + 该 PR 闭环后,后续不应再插入 baseline-only PR 才开始 runtime owner 迁移;下一组 PR 应直接以 + 单一 owner 为单位移动实际 runtime,并沿用本节等价测试和边界脚本。 - 已合入的 `Services/Product Runtime Owner Closure` 只收口已经有 port/contract 保护的低风险 owner: remote-SSH session identity / mirror path / unresolved-session layout 归属 `bitfun-services-integrations`,MiniApp storage/import file layout、fallback payload diff --git a/docs/plans/core-decomposition-plan.md b/docs/plans/core-decomposition-plan.md index 51a9e81b6..caaa35275 100644 --- a/docs/plans/core-decomposition-plan.md +++ b/docs/plans/core-decomposition-plan.md @@ -971,7 +971,7 @@ product-full = ["git", "mcp", "remote-ssh", "remote-connect", "announcement", "f - [ ] 再迁移 `remote-ssh`,保留 `ssh-remote` 语义。 - [x] 先迁移 `remote-ssh` 的纯 contract/type、workspace path/identity helper 与 unresolved-session-key helper,runtime manager / fs / terminal 仍保留在 core。 - [x] 迁移 `mcp` 的 PR2 runtime 与 dynamic provider:config service orchestration、server process / transport lifecycle、resource/prompt adapter、catalog cache、list-changed/reconnect policy、dynamic descriptor / provider / result rendering 均归属 `bitfun-services-integrations`。 -- [x] `bitfun-core` 保留 core `ConfigService` store adapter、OAuth data-dir 注入、`BitFunError` 映射、旧路径 facade 和全局 tool registry / manifest 组装;product tool manifest/exposure owner 化不混入本 PR。 +- [x] `bitfun-core` 保留 core `ConfigService` store adapter、OAuth data-dir 注入、`BitFunError` 映射、旧路径 facade 和全局 tool registry / manifest 组装;product tool runtime manifest / `GetToolSpec` 执行 owner 化不混入本 PR。 - [x] 先迁移 `announcement` 的纯 types contract,scheduler / state store / content loader / remote fetch 仍保留在 core。 - [x] 先完成 `remote-connect` contract slice:remote chat/image/tool/session wire DTO 与 relay/bot session/submission request builder 由 `bitfun-services-integrations` 拥有,relay/bot session 创建通过 `AgentSubmissionPort`。 - [x] 已补齐 remote runtime 迁移前的第一层 port baseline:`SessionTranscriptReader`、`AgentTurnCancellationPort`、`RemoteControlStatePort`、`RuntimeEventSink` 与 remote image attachment/request DTO;完整 `remote-connect` runtime 仍需后续单独迁移并补 queue/event/image 行为等价测试。 @@ -984,7 +984,7 @@ product-full = ["git", "mcp", "remote-ssh", "remote-connect", "announcement", "f **当前安全迁移状态(2026-05-15):** - 已迁移到 `bitfun-services-integrations`:`service::file_watch`,通过 `file-watch` / `product-full` feature 启用,并保持 `core::service::file_watch` 旧路径。 -- `git` 已完成 DTO/params/graph/raw command output/text parser/arg builder、`GitError`、`GitService` runtime implementation 与 git utils 迁移;`bitfun-core::service::git::*` 仅保留 legacy facade re-export。`remote-ssh` 已迁移纯 contract/type、workspace path/identity helper 与 unresolved-session-key helper;SSH runtime manager / fs / terminal、password vault 与 PathManager-backed session mirror assembly 仍保留在 core。`mcp` 已迁移 tool-name / tool-info / protocol types / config location / server type-status、server config、cursor-format、JSON-RPC request builder、JSON config format/validation helper、config merge / remote authorization helper、OAuth credential vault / authorization bootstrap contract、remote auth error classifier、legacy remote header fallback helper、transport Authorization 归一化 helper、remote client capability helper、rmcp 到 BitFun protocol 的纯映射 helper、resource/prompt adapter、catalog cache、list-changed/reconnect policy、config service save-load orchestration、server process / local-remote transport lifecycle、dynamic tool descriptor / provider / result rendering helper,并用 owner crate contract test 锁定 wire shape、transport default、validation message、Cursor 兼容格式、config precedence / dedup 语义、OAuth vault 存储路径注入、NeedsAuth 分类、旧 env Authorization fallback、remote client capabilities、remote result metadata / structured content 映射、config load/save/delete contract、unsupported remote transport contract、context resource selection 和 dynamic manifest;`bitfun-core` 继续负责 core `ConfigService` store adapter、OAuth data-dir 注入、`BitFunError` 映射、legacy facade 和全局 tool registry / manifest 组装。`announcement` 仅迁移了纯 types contract,scheduler / state store / content loader / remote fetch 仍保留在 core;`remote-connect` 已完成 contract/request-builder slice,补齐 cancellation/state/event/image 第一层 port baseline,迁出 command/response wire DTO、remote model catalog DTO、poll response assembly / model catalog poll delta、tracker state / registry lifecycle / tracker event reduction / remote tool preview slimming helper、legacy image context fallback / preference、restore target decision、cancel decision 与 remote file transfer size/chunk/name policy,并补齐 remote command/response、restore、active turn、cancel、image context、tracker fanout 与 queue policy 迁移前快照;但远程消息执行、`ImageContextData` adapter、file IO/path resolution、terminal pre-warm 与 workspace/session restore 执行仍保留在 core。它们涉及 SSH runtime、remote agent submission runtime、product tool manifest/exposure owner 化与 announcement config/path 边界,继续前需要单独确认端口方案与等价性测试。 +- `git` 已完成 DTO/params/graph/raw command output/text parser/arg builder、`GitError`、`GitService` runtime implementation 与 git utils 迁移;`bitfun-core::service::git::*` 仅保留 legacy facade re-export。`remote-ssh` 已迁移纯 contract/type、workspace path/identity helper 与 unresolved-session-key helper;SSH runtime manager / fs / terminal、password vault 与 PathManager-backed session mirror assembly 仍保留在 core。`mcp` 已迁移 tool-name / tool-info / protocol types / config location / server type-status、server config、cursor-format、JSON-RPC request builder、JSON config format/validation helper、config merge / remote authorization helper、OAuth credential vault / authorization bootstrap contract、remote auth error classifier、legacy remote header fallback helper、transport Authorization 归一化 helper、remote client capability helper、rmcp 到 BitFun protocol 的纯映射 helper、resource/prompt adapter、catalog cache、list-changed/reconnect policy、config service save-load orchestration、server process / local-remote transport lifecycle、dynamic tool descriptor / provider / result rendering helper,并用 owner crate contract test 锁定 wire shape、transport default、validation message、Cursor 兼容格式、config precedence / dedup 语义、OAuth vault 存储路径注入、NeedsAuth 分类、旧 env Authorization fallback、remote client capabilities、remote result metadata / structured content 映射、config load/save/delete contract、unsupported remote transport contract、context resource selection 和 dynamic manifest;`bitfun-core` 继续负责 core `ConfigService` store adapter、OAuth data-dir 注入、`BitFunError` 映射、legacy facade 和全局 tool registry / manifest 组装。`announcement` 仅迁移了纯 types contract,scheduler / state store / content loader / remote fetch 仍保留在 core;`remote-connect` 已完成 contract/request-builder slice,补齐 cancellation/state/event/image 第一层 port baseline,迁出 command/response wire DTO、remote model catalog DTO、poll response assembly / model catalog poll delta、tracker state / registry lifecycle / tracker event reduction / remote tool preview slimming helper、legacy image context fallback / preference、restore target decision、cancel decision 与 remote file transfer size/chunk/name policy,并补齐 remote command/response、restore、active turn、cancel、image context、tracker fanout 与 queue policy 迁移前快照;但远程消息执行、`ImageContextData` adapter、file IO/path resolution、terminal pre-warm 与 workspace/session restore 执行仍保留在 core。它们涉及 SSH runtime、remote agent submission runtime、product tool runtime manifest / `GetToolSpec` 执行 owner 化与 announcement config/path 边界,继续前需要单独确认端口方案与等价性测试。 - 最新主干的 Deep Review capacity / cost / queue、context profile、evidence ledger、session manifest、stream dedupe、search remote/fallback 与 session rollback persistence 仍属于 core runtime 或对应产品 runtime,不在本轮 `services-integrations` 迁移范围内;如果后续迁移 remote-connect / MCP / search / session,需要先定义运行状态 port 合约和等价测试。 **验证:** @@ -1018,6 +1018,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。 - [ ] 抽出 `Tool` trait 与 `ToolUseContext` 前,先补可移植 tool context / service port 设计;当前不做无端口支撑的行为迁移。 - [x] `agent-tools` 不依赖任何 concrete service。 - [ ] 将工具实现迁移到 `tool-packs` crate,并按 feature group 分模块: @@ -1046,19 +1047,19 @@ pub fn create_tool_registry() -> ToolRegistry { ``` - [ ] 增加 registry / manifest 等价性测试:完整产品 registry、expanded/collapsed exposure 与 prompt-visible manifest 和拆分前一致。 -- [ ] 迁移 tool exposure / manifest / `GetToolSpec` 前,补 expanded/collapsed manifest、 +- [ ] 迁移 runtime manifest assembly / `GetToolSpec` 执行前,补 expanded/collapsed manifest、 prompt-visible stub、unlock state 和 desktop/MCP/ACP catalog 等价测试。 **当前安全迁移状态(2026-05-14):** - 已迁移到 `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 的适配,以及最新主干新增的 tool exposure / manifest resolution / `GetToolSpec` 按需工具说明发现;dynamic metadata map、tool map、dynamic descriptor assembly 由 `bitfun-agent-tools` 拥有。 +- `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-tool-packs` feature scaffold,默认 feature 为空,`product-full` 只聚合 feature,不注册或迁移任何工具实现。 -- 已通过 boundary check 锁定 `agent-tools` / `tool-packs` 暂不拥有 product tool manifest、`ToolExposure`、`GetToolSpec` 或 collapsed-tool unlock state;这些仍由 core product tool runtime 负责。 +- 已通过 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 也已补充 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 继续外移。 -- 最新主干新增 on-demand tool spec discovery:`ToolExposure`、`GetToolSpec`、`manifest_resolver`、collapsed-tool catalog、context-aware `description_with_context` / `input_schema_for_model_with_context`,以及 `ToolUseContext.unlocked_collapsed_tools` 均会影响模型可见工具集合。该变化不推翻 PR4 的低风险结论,但把后续 tool/provider 迁移提升为高风险项,不能在 PR5 product-domain 收尾中顺带执行。 +- 最新主干新增 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 收尾中顺带执行。 **验证:** @@ -1534,8 +1535,8 @@ 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`;core 继续负责产品工具列表、snapshot decorator、`dyn Tool` 适配、tool exposure / manifest resolution 与 `GetToolSpec` 按需工具说明发现。 -- 未声明完成的 P2/后续剩余部分:remote-ssh runtime、remote-connect 等重 service 迁移、`ToolUseContext` 外移、tool exposure / manifest / `GetToolSpec` owner 化、concrete tool implementation 迁移、product registry / manifest / provider assembly、miniapp/function-agent 运行逻辑迁移。这些会触碰 `PathManager`、`ToolUseContext`、workspace service、snapshot wrapper、prompt-visible tool catalog、`AgentSubmissionPort` 或 AI service 边界,需要在继续前显式确认。 +- 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` 执行。 +- 未声明完成的 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 收口混合。 - PR5 已先推进低风险 product-domain slice:MiniApp 纯 compiler、export/runtime/worker DTO、runtime search plan、worker install 命令选择、package.json storage-shape helper、import layout / fallback payload contract、lifecycle / revision helper、manager 纯状态转换 helper、host routing string / allowlist policy helper、customization metadata / permission diff、runtime/storage port contract,以及 git/startchat function-agent 纯 utils / commit summary / message assembly / prompt format / commit prompt preparation / AI response parsing policy / action normalization / git porcelain / diff combine / time-of-day / Git/AI port contract / project context analyzer 已移入 `bitfun-product-domains`,core 保留原路径兼容 wrapper;core 只保留 AI client 调用、JSON 提取、错误映射、Git service adapter 和原路径 facade。已新增 core-owned Git snapshot、MiniApp storage/runtime port adapter 等价测试,并补齐 MiniApp manager import/sync/recompile/rollback/deps state 与 function-agent staged diff / AI response error mapping 的迁移前快照。PathManager、Git/AI service、prompt template、builtin asset seeding、host dispatch 执行、customization draft 存储 / 应用、worker pool / storage IO 执行逻辑和任何 tool runtime 仍未迁移。 @@ -1558,7 +1559,7 @@ cargo check --workspace - 已扩展 `scripts/check-core-boundaries.mjs`,增加 dependency profile / feature graph 静态保护:`core-types` default profile 禁止非 DTO 依赖,`runtime-ports` default profile 禁止 service implementation 依赖,`agent-tools` contract profile 禁止依赖 `bitfun-ai-adapters`,`product-domains` default profile 禁止无条件拉入 `dirs`,`services-integrations` default profile 禁止无条件拉入 feature-gated integration 依赖。 - 已将 `ToolImageAttachment` 提升到 `bitfun-core-types`,并由 `bitfun-ai-adapters`、`bitfun-agent-tools` 和 `bitfun-core::util::types` 保留旧路径兼容;`bitfun-agent-tools` 不再依赖 `bitfun-ai-adapters`。 - 已将 `product-domains` 的 `dirs` 依赖限制到 `miniapp` feature,默认 profile 保持轻量。 -- 已为 `product-domains` 增加 runtime-owner 静态保护,禁止在未确认 port/provider 迁移方案前引入进程启动、具体 Git/AI 服务、网络客户端或平台 API;也已锁定 `agent-tools` / `tool-packs` 暂不拥有 product tool manifest、`GetToolSpec` 或 collapsed-tool unlock state。 +- 已为 `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。 @@ -1590,7 +1591,7 @@ P2 后产品表面契约轨道(contract-only): 需要单独审视的高风险项: -- `ToolUseContext`、tool exposure / manifest / `GetToolSpec`、product tool provider assembly、concrete tool implementation 外移。 +- `ToolUseContext`、runtime manifest assembly / `GetToolSpec` 执行、product tool provider assembly、concrete tool implementation 外移。 - MCP concrete tool implementation / product registry / manifest assembly 外移。 - remote-connect、remote-SSH runtime、announcement runtime 外移。 - miniapp runtime/compiler/builtin 与 function-agent 运行逻辑外移。 @@ -1615,13 +1616,13 @@ P2 后产品表面契约轨道(contract-only): **剩余工作压缩为 5 个 PR(2026-05-13):** 1. `services-integrations` runtime 收口:迁移 remote-SSH 中不直接持有 SSH channel / SFTP / terminal handle 的 workspace registry、session mirror 与轻量 runtime helper;继续保留 SSH manager / remote FS / remote terminal 的 core-owned assembly,直到 port/provider 合约明确。`file-watch` 已由 `services-integrations` 拥有,只做 contract 复核;announcement 只迁移不依赖 config service / embedded content / remote fetch 的 state 或 eligibility helper。验收重点是 owner crate contract test、旧路径 facade、boundary check、workspace check/test。 -2. 已完成:MCP runtime 与 dynamic tools:MCP config service orchestration、server process / transport lifecycle、adapter、dynamic tool/resource/prompt provider 已归属 `bitfun-services-integrations`;未混入 remote-connect 或 product tool manifest/exposure owner 化。验收重点是 MCP wire shape、auth/config merge、dynamic manifest 快照和 core registry / manifest 集成等价。 +2. 已完成:MCP runtime 与 dynamic tools:MCP config service orchestration、server process / transport lifecycle、adapter、dynamic tool/resource/prompt provider 已归属 `bitfun-services-integrations`;未混入 remote-connect 或 product tool runtime manifest / `GetToolSpec` 执行 owner 化。验收重点是 MCP wire shape、auth/config merge、dynamic manifest 快照和 core registry / manifest 集成等价。 - 保留边界:`bitfun-core` 只保留 core `ConfigService` store adapter、OAuth data-dir 注入、`BitFunError` 映射、legacy facade 和与全局 tool registry / manifest 的组装调用;配置写入、OAuth、SSE/session 与 registry / manifest 行为不得在本 PR 中改变。 - 后续切片:MCP concrete tool integration / product registry / manifest assembly 继续保留 dynamic provider metadata、工具清单顺序、expanded/collapsed exposure 和 snapshot wrapper 等价测试。 - 文档校正: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 已迁入 `bitfun-agent-tools`,并为 dynamic provider contract 提供 `agent-tools` 兼容 re-export;core tool runtime 保留产品完整工具列表、snapshot decorator、`dyn Tool` 适配、tool exposure / manifest resolution 和 `GetToolSpec` 按需工具说明发现。`ToolUseContext`、tool manifest/exposure 与 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 / 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 等价,作为后续高风险迁移单独审视。 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。 @@ -1652,11 +1653,11 @@ P2 后产品表面契约轨道(contract-only): **P3 进入条件与最新主干补充(2026-05-18):** -- P3 只能在 P2 剩余迁移闭环后启动:重 service 迁移、`ToolUseContext` / tool exposure / manifest / `GetToolSpec` / concrete tool implementation 迁移、product registry / manifest / provider assembly、miniapp/function-agent 运行逻辑迁移都必须先完成或显式保留为 core-owned runtime;generic registry/provider container 已在 PR4 中完成低风险外移。 +- 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 低风险外移中完成。 - 最近 `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 过滤语义。 -- 最新主干的 on-demand tool spec discovery 将 `ToolExposure`、`manifest_resolver`、`GetToolSpec`、collapsed-tool prompt stub 和 `ToolUseContext.unlocked_collapsed_tools` 接入 agent prompt / execution pipeline / desktop-MCP-ACP catalog。P3 facade 收敛前必须把这些显式保留在 core product tool runtime,或先完成等价快照与 port/provider 设计后再迁移。 +- 最新主干的 on-demand tool spec discovery 将 `manifest_resolver`、`GetToolSpecTool` 执行、collapsed-tool catalog 和 `ToolUseContext.unlocked_collapsed_tools` 接入 agent prompt / execution pipeline / desktop-MCP-ACP catalog。P3 facade 收敛前必须把这些显式保留在 core product tool runtime,或先完成等价快照与 port/provider 设计后再迁移;`ToolExposure`、`GetToolSpec` 名称、collapsed-tool prompt stub 和 manifest ordering 仅作为纯契约保留在 `bitfun-agent-tools`。 - 最新主干的 search result rendering / context handling 与 remote workspace compatibility guard 要求后续 `service::search`、`workspace` 或 remote runtime 迁移保留 startup restored workspace guard、remote runtime ensure、remote flashgrep FilesWithMatches fallback、preview split 和 local/remote fallback contract。 - ACP startup timeout 和 Web file-operation diff fallback 属于 product surface 行为:可以在后续 contract 中记录 operation/diff facts,但不能把 ACP timeout policy 或 Web diff rendering 迁入 core contract crate。 - 最新主干的 Web 启动性能优化新增 startup trace、deferred background scheduler、narrow tool initializer、Monaco warmup 与历史会话非阻塞 hydrate;这些属于 web app / Flow Chat product surface,不是 core service 迁移前置条件。后续只能通过 web product checks 验证,不得把 `startupTrace`、`backgroundTaskScheduler`、history hydration 或 tool warmup 下沉到 core-types / runtime-ports / agent-tools。 @@ -1723,7 +1724,7 @@ git diff -- package.json scripts/dev.cjs scripts/desktop-tauri-build.mjs scripts 5. 已完成:抽取 `bitfun-agent-stream`,迁移 stream processor 测试。 6. 已完成:引入 runtime ports 初始边界;后续在 service 迁移中逐步打断 `service <-> agentic` concrete 循环。 7. 已完成:抽取 `bitfun-services-core`。 -8. 已完成:抽取 `bitfun-services-integrations` 的低风险 feature group 和纯 helper,闭环 `git`、remote-SSH contract/helper、MCP 纯 protocol/config/auth helper;MCP runtime / dynamic provider 已在 PR2 补齐,未把 remote-connect 或 product tool manifest/exposure owner 化顺带迁入。 +8. 已完成:抽取 `bitfun-services-integrations` 的低风险 feature group 和纯 helper,闭环 `git`、remote-SSH contract/helper、MCP 纯 protocol/config/auth helper;MCP runtime / dynamic provider 已在 PR2 补齐,未把 remote-connect 或 product tool runtime manifest / `GetToolSpec` 执行 owner 化顺带迁入。 9. 已完成:前移低风险保护项:dependency profile / feature graph 基线、轻量 contract crate 依赖瘦身、feature group 说明、boundary check 扩展、迁移前快照测试。 10. 已提交:PR 1 `services-integrations` runtime 收口,处理 remote-SSH workspace registry / session mirror helper 和已迁移 file-watch 的 contract 复核;announcement 仅迁移无 config/content/remote fetch 依赖的 helper。 11. 已提交:PR 2 `Services/Product Runtime Owner Closure`,收口 remote-SSH session identity / mirror path / unresolved-session layout 与 MiniApp storage file layout owner;core 保留 `PathManager` 注入、SSH manager、remote FS / terminal、MiniApp filesystem IO 和 worker runtime。 diff --git a/scripts/check-core-boundaries.mjs b/scripts/check-core-boundaries.mjs index 683dfb33c..07cc8a454 100644 --- a/scripts/check-core-boundaries.mjs +++ b/scripts/check-core-boundaries.mjs @@ -1004,16 +1004,12 @@ const forbiddenContentUnderRules = [ { path: 'src/crates/agent-tools/src', reason: - 'agent-tools must not own product tool manifest/exposure or GetToolSpec runtime without an approved provider migration', + 'agent-tools may own pure tool manifest contracts, but not product manifest runtime or GetToolSpec execution without an approved provider migration', patterns: [ { regex: /\bGetToolSpecTool\b/, message: 'GetToolSpec implementation stays in core product tool runtime', }, - { - regex: /\bGET_TOOL_SPEC_TOOL_NAME\b/, - message: 'GetToolSpec manifest insertion stays in core product tool runtime', - }, { regex: /\bmanifest_resolver\b/, message: 'tool manifest resolution stays in core product tool runtime', @@ -1022,10 +1018,6 @@ const forbiddenContentUnderRules = [ regex: /\bunlocked_collapsed_tools\b/, message: 'collapsed-tool unlock state stays in core ToolUseContext/runtime', }, - { - regex: /\bToolExposure\b/, - message: 'expanded/collapsed exposure policy stays in core until provider migration', - }, { regex: /\bToolUseContext\b/, message: 'ToolUseContext stays in core until a portable context port is reviewed', @@ -1085,6 +1077,37 @@ const requiredContentRules = [ }, ], }, + { + path: 'src/crates/agent-tools/src/framework.rs', + reason: + 'agent-tools may own pure prompt-visible tool manifest contracts without owning product runtime execution', + patterns: [ + { + regex: /\bpub const GET_TOOL_SPEC_TOOL_NAME\b/, + message: 'missing shared GetToolSpec manifest name contract', + }, + { + regex: /\bpub enum ToolExposure\b/, + message: 'missing lightweight tool exposure contract', + }, + { + regex: /\bpub struct ToolManifestPolicyTool\b/, + message: 'missing pure tool manifest policy input contract', + }, + { + regex: /\bpub fn resolve_tool_manifest_policy\b/, + message: 'missing pure tool manifest policy resolver', + }, + { + regex: /\bpub fn build_collapsed_tool_stub_definition\b/, + message: 'missing collapsed-tool prompt stub contract', + }, + { + regex: /\bpub fn sort_tool_manifest_definitions\b/, + message: 'missing prompt-visible manifest ordering helper', + }, + ], + }, { path: 'src/crates/core/src/agentic/coordination/coordinator.rs', reason: @@ -1138,7 +1161,7 @@ const requiredContentRules = [ { path: 'src/crates/core/src/agentic/tools/manifest_resolver.rs', reason: - 'core must continue owning prompt-visible tool manifest assembly until an approved provider migration exists', + 'core must continue owning prompt-visible tool manifest assembly and runtime context filtering until an approved provider migration exists', patterns: [ { regex: /\bpub async fn resolve_tool_manifest\b/, @@ -1149,16 +1172,16 @@ const requiredContentRules = [ message: 'missing GetToolSpec manifest insertion anchor', }, { - regex: /\bToolExposure::Collapsed\b/, - message: 'missing collapsed exposure branch', + regex: /\bresolve_tool_manifest_policy\b/, + message: 'missing agent-tools manifest policy contract use', }, { regex: /\bcollapsed_tool_names\b/, message: 'missing collapsed-tool name tracking', }, { - regex: /First call `GetToolSpec`|Use GetToolSpec.*first/, - message: 'missing collapsed-tool prompt stub', + regex: /\bbuild_collapsed_tool_stub_definition\b/, + message: 'missing collapsed-tool prompt stub contract use', }, ], }, @@ -1184,11 +1207,11 @@ const requiredContentRules = [ { path: 'src/crates/core/src/agentic/tools/framework.rs', reason: - 'core must continue owning ToolUseContext and exposure policy until a portable context port is reviewed', + 'core must continue owning ToolUseContext while re-exporting pure exposure contracts until a portable context port is reviewed', patterns: [ { - regex: /\bpub enum ToolExposure\b/, - message: 'missing ToolExposure owner type', + regex: /\bToolExposure\b/, + message: 'missing ToolExposure compatibility re-export', }, { regex: /\bpub struct ToolUseContext\b/, @@ -2505,16 +2528,16 @@ function runManifestParserSelfTest() { if (!agentToolsManifestRule) { throw new Error('missing agent-tools manifest-owner boundary rule'); } - const toolManifestContracts = [ + const agentToolsRuntimeForbiddenContracts = [ 'GetToolSpecTool', - 'GET_TOOL_SPEC_TOOL_NAME', 'manifest_resolver', 'unlocked_collapsed_tools', + 'ToolUseContext', ]; const agentToolsManifestRuleText = agentToolsManifestRule.patterns .map((pattern) => pattern.regex.source) .join('\n'); - for (const contract of toolManifestContracts) { + for (const contract of agentToolsRuntimeForbiddenContracts) { if (!agentToolsManifestRuleText.includes(contract)) { throw new Error(`agent-tools manifest boundary rule must forbid: ${contract}`); } @@ -2528,7 +2551,14 @@ function runManifestParserSelfTest() { const toolPacksManifestRuleText = toolPacksManifestRule.patterns .map((pattern) => pattern.regex.source) .join('\n'); - for (const contract of toolManifestContracts) { + const toolPacksManifestContracts = [ + 'GetToolSpecTool', + 'GET_TOOL_SPEC_TOOL_NAME', + 'manifest_resolver', + 'unlocked_collapsed_tools', + 'ToolExposure', + ]; + for (const contract of toolPacksManifestContracts) { if (!toolPacksManifestRuleText.includes(contract)) { throw new Error(`tool-packs manifest boundary rule must forbid: ${contract}`); } @@ -2544,6 +2574,17 @@ function runManifestParserSelfTest() { 'remote_image', ], }, + { + path: 'src/crates/agent-tools/src/framework.rs', + contracts: [ + 'GET_TOOL_SPEC_TOOL_NAME', + 'ToolExposure', + 'ToolManifestPolicyTool', + 'resolve_tool_manifest_policy', + 'build_collapsed_tool_stub_definition', + 'sort_tool_manifest_definitions', + ], + }, { path: 'src/crates/core/src/agentic/coordination/coordinator.rs', contracts: [ @@ -2625,7 +2666,13 @@ function runManifestParserSelfTest() { }, { path: 'src/crates/core/src/agentic/tools/manifest_resolver.rs', - contracts: ['resolve_tool_manifest', 'GET_TOOL_SPEC_TOOL_NAME', 'ToolExposure', 'First call `GetToolSpec`'], + contracts: [ + 'resolve_tool_manifest', + 'GET_TOOL_SPEC_TOOL_NAME', + 'resolve_tool_manifest_policy', + 'build_collapsed_tool_stub_definition', + 'collapsed_tool_names', + ], }, { path: 'src/crates/core/src/agentic/tools/implementations/get_tool_spec_tool.rs', diff --git a/src/crates/agent-tools/src/framework.rs b/src/crates/agent-tools/src/framework.rs index 806ec1099..27d88400f 100644 --- a/src/crates/agent-tools/src/framework.rs +++ b/src/crates/agent-tools/src/framework.rs @@ -31,6 +31,149 @@ pub struct DynamicToolInfo { pub mcp: Option, } +pub const GET_TOOL_SPEC_TOOL_NAME: &str = "GetToolSpec"; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum ToolExposure { + Expanded, + Collapsed, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct ToolManifestDefinition { + pub name: String, + pub description: String, + pub parameters: Value, +} + +impl ToolManifestDefinition { + pub fn new(name: impl Into, description: impl Into, parameters: Value) -> Self { + Self { + name: name.into(), + description: description.into(), + parameters, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ToolManifestPolicyTool { + pub name: String, + pub default_exposure: ToolExposure, + pub available: bool, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ToolManifestPolicyResolution { + pub allowed_tool_names: Vec, + pub expanded_tool_names: Vec, + pub collapsed_tool_names: Vec, +} + +pub fn resolve_tool_manifest_policy( + tool_snapshot: &[ToolManifestPolicyTool], + allowed_tools: &[String], + exposure_overrides: &IndexMap, + get_tool_spec_tool_name: &str, +) -> ToolManifestPolicyResolution { + let allowed_set = allowed_tools + .iter() + .map(String::as_str) + .collect::>(); + let mut allowed_tool_names = allowed_tools.to_vec(); + let mut expanded_tool_names = Vec::new(); + let mut collapsed_tool_names = Vec::new(); + + for tool in tool_snapshot { + if !tool.available || !allowed_set.contains(tool.name.as_str()) { + continue; + } + + let exposure = exposure_overrides + .get(&tool.name) + .copied() + .unwrap_or(tool.default_exposure); + match exposure { + ToolExposure::Expanded => expanded_tool_names.push(tool.name.clone()), + ToolExposure::Collapsed => collapsed_tool_names.push(tool.name.clone()), + } + } + + if !collapsed_tool_names.is_empty() { + if !allowed_tool_names + .iter() + .any(|name| name == get_tool_spec_tool_name) + { + allowed_tool_names.push(get_tool_spec_tool_name.to_string()); + } + if tool_snapshot + .iter() + .any(|tool| tool.name == get_tool_spec_tool_name) + { + expanded_tool_names.push(get_tool_spec_tool_name.to_string()); + } + } + + ToolManifestPolicyResolution { + allowed_tool_names, + expanded_tool_names, + collapsed_tool_names, + } +} + +pub fn build_collapsed_tool_stub_definition( + tool_name: &str, + short_description: &str, +) -> ToolManifestDefinition { + ToolManifestDefinition::new( + tool_name, + format!( + "{} [This tool is collapsed. Do not call `{}` directly yet. First call `GetToolSpec` with {{\"tool_name\":\"{}\"}} to load its full description and input schema, then retry `{}` using the returned schema.]", + short_description, tool_name, tool_name, tool_name + ), + serde_json::json!({ + "type": "object", + "additionalProperties": false, + "properties": { + "tool_name": { + "type": "string", + "description": format!( + "Do not supply {} arguments here while the tool is collapsed. Use GetToolSpec with {{\"tool_name\":\"{}\"}} first.", + tool_name, + tool_name + ) + } + } + }), + ) +} + +pub fn tool_manifest_sort_rank(tool_name: &str) -> usize { + match tool_name { + "Task" => 1, + "Bash" => 2, + "TerminalControl" => 3, + "Glob" => 4, + "Grep" => 5, + "Read" => 6, + "Edit" => 7, + "Write" => 8, + "Delete" => 9, + "WebFetch" => 10, + "WebSearch" => 11, + "TodoWrite" => 12, + "Skill" => 13, + "Log" => 14, + GET_TOOL_SPEC_TOOL_NAME => 15, + "ControlHub" => 16, + _ => 100, + } +} + +pub fn sort_tool_manifest_definitions(tool_definitions: &mut [ToolManifestDefinition]) { + tool_definitions.sort_by_key(|tool| tool_manifest_sort_rank(&tool.name)); +} + #[async_trait] pub trait ToolRegistryItem: Send + Sync { fn name(&self) -> &str; diff --git a/src/crates/agent-tools/src/lib.rs b/src/crates/agent-tools/src/lib.rs index 4e2f358da..da071a74a 100644 --- a/src/crates/agent-tools/src/lib.rs +++ b/src/crates/agent-tools/src/lib.rs @@ -11,8 +11,11 @@ pub use bitfun_runtime_ports::{ DynamicToolDescriptor, DynamicToolProvider, PortError, PortErrorKind, PortResult, ToolDecorator, }; pub use framework::{ - DynamicMcpToolInfo, DynamicToolInfo, ToolPathBackend, ToolPathOperation, ToolPathPolicy, - ToolPathResolution, ToolRef, ToolRegistry, ToolRegistryItem, ToolRenderOptions, - ToolRestrictionError, ToolResult, ToolRuntimeRestrictions, ValidationResult, + DynamicMcpToolInfo, DynamicToolInfo, GET_TOOL_SPEC_TOOL_NAME, 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, }; 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 1cb2de965..a48b91cb8 100644 --- a/src/crates/agent-tools/tests/tool_contracts.rs +++ b/src/crates/agent-tools/tests/tool_contracts.rs @@ -1,6 +1,9 @@ use bitfun_agent_tools::{ - DynamicMcpToolInfo, DynamicToolInfo, InputValidator, ToolImageAttachment, ToolPathBackend, + 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, }; use bitfun_agent_tools::{ DynamicToolDescriptor, DynamicToolProvider, PortResult, ToolDecorator, ToolRegistry, @@ -224,6 +227,181 @@ fn dynamic_tool_provider_contract_is_available_from_agent_tools_boundary() { assert_decorator_contract::(); } +#[test] +fn tool_exposure_contract_keeps_lightweight_wire_shape() { + let collapsed = ToolExposure::Collapsed; + let value = serde_json::to_value(collapsed).expect("serialize exposure"); + + assert_eq!(value, json!("Collapsed")); + assert_eq!( + serde_json::from_value::(value).expect("deserialize exposure"), + ToolExposure::Collapsed + ); +} + +#[test] +fn tool_manifest_definition_keeps_lightweight_wire_shape() { + let definition = ToolManifestDefinition::new( + "Read", + "Read a file", + json!({ + "type": "object", + "properties": { + "file_path": { "type": "string" } + }, + "required": ["file_path"] + }), + ); + + let value = serde_json::to_value(&definition).expect("serialize definition"); + + assert_eq!(value["name"], json!("Read")); + assert_eq!(value["description"], json!("Read a file")); + assert_eq!(value["parameters"]["required"], json!(["file_path"])); + assert_eq!( + serde_json::from_value::(value).expect("deserialize definition"), + definition + ); +} + +#[test] +fn tool_manifest_policy_keeps_get_tool_spec_insertion_and_registry_order() { + let tools = vec![ + ToolManifestPolicyTool { + name: "Read".to_string(), + default_exposure: ToolExposure::Expanded, + available: true, + }, + ToolManifestPolicyTool { + name: "WebSearch".to_string(), + default_exposure: ToolExposure::Collapsed, + available: true, + }, + ToolManifestPolicyTool { + name: "WebFetch".to_string(), + default_exposure: ToolExposure::Collapsed, + available: true, + }, + ToolManifestPolicyTool { + name: GET_TOOL_SPEC_TOOL_NAME.to_string(), + default_exposure: ToolExposure::Expanded, + available: true, + }, + ToolManifestPolicyTool { + name: "HiddenUnavailable".to_string(), + default_exposure: ToolExposure::Expanded, + available: false, + }, + ]; + let allowed_tools = vec![ + "WebFetch".to_string(), + "Read".to_string(), + "WebSearch".to_string(), + "HiddenUnavailable".to_string(), + ]; + let overrides = Default::default(); + + let policy = + resolve_tool_manifest_policy(&tools, &allowed_tools, &overrides, GET_TOOL_SPEC_TOOL_NAME); + + assert_eq!( + policy.allowed_tool_names, + vec![ + "WebFetch", + "Read", + "WebSearch", + "HiddenUnavailable", + GET_TOOL_SPEC_TOOL_NAME, + ] + ); + assert_eq!( + policy.expanded_tool_names, + vec!["Read", GET_TOOL_SPEC_TOOL_NAME] + ); + assert_eq!(policy.collapsed_tool_names, vec!["WebSearch", "WebFetch"]); +} + +#[test] +fn tool_manifest_policy_preserves_explicit_get_tool_spec_duplicate_runtime_contract() { + let tools = vec![ + ToolManifestPolicyTool { + name: GET_TOOL_SPEC_TOOL_NAME.to_string(), + default_exposure: ToolExposure::Expanded, + available: true, + }, + ToolManifestPolicyTool { + name: "WebFetch".to_string(), + default_exposure: ToolExposure::Collapsed, + available: true, + }, + ]; + let allowed_tools = vec![GET_TOOL_SPEC_TOOL_NAME.to_string(), "WebFetch".to_string()]; + let overrides = Default::default(); + + let policy = + resolve_tool_manifest_policy(&tools, &allowed_tools, &overrides, GET_TOOL_SPEC_TOOL_NAME); + + assert_eq!( + policy.allowed_tool_names, + vec![GET_TOOL_SPEC_TOOL_NAME, "WebFetch"] + ); + assert_eq!( + policy.expanded_tool_names, + vec![GET_TOOL_SPEC_TOOL_NAME, GET_TOOL_SPEC_TOOL_NAME], + "core currently appends the runtime GetToolSpec entry whenever collapsed tools exist" + ); + assert_eq!(policy.collapsed_tool_names, vec!["WebFetch"]); +} + +#[test] +fn collapsed_tool_stub_definition_preserves_prompt_visible_guardrail() { + let stub = build_collapsed_tool_stub_definition( + "WebFetch", + "Fetch a URL and return readable content.", + ); + + assert_eq!(stub.name, "WebFetch"); + assert!(stub.description.contains("Fetch a URL")); + assert!( + stub.description + .contains("First call `GetToolSpec` with {\"tool_name\":\"WebFetch\"}") + ); + assert_eq!( + stub.parameters, + json!({ + "type": "object", + "additionalProperties": false, + "properties": { + "tool_name": { + "type": "string", + "description": "Do not supply WebFetch arguments here while the tool is collapsed. Use GetToolSpec with {\"tool_name\":\"WebFetch\"} first." + } + } + }) + ); +} + +#[test] +fn tool_manifest_sorting_preserves_prompt_visible_order() { + let mut definitions = vec![ + ToolManifestDefinition::new("ControlHub", "control", json!({ "type": "object" })), + ToolManifestDefinition::new("Read", "read", json!({ "type": "object" })), + ToolManifestDefinition::new("ExternalTool", "external", json!({ "type": "object" })), + ToolManifestDefinition::new("GetToolSpec", "spec", json!({ "type": "object" })), + ToolManifestDefinition::new("Task", "task", json!({ "type": "object" })), + ]; + + sort_tool_manifest_definitions(&mut definitions); + + assert_eq!( + definitions + .iter() + .map(|definition| definition.name.as_str()) + .collect::>(), + vec!["Task", "Read", "GetToolSpec", "ControlHub", "ExternalTool"] + ); +} + struct RegistryMarkerTool { name: String, provider_id: Option, diff --git a/src/crates/core/AGENTS.md b/src/crates/core/AGENTS.md index ceadaf820..a3babbdcb 100644 --- a/src/crates/core/AGENTS.md +++ b/src/crates/core/AGENTS.md @@ -31,10 +31,11 @@ SessionManager → Session → DialogTurn → ModelRound - During core decomposition, `bitfun-core` is a compatibility facade and full product runtime assembly point. New modules should prefer the extracted owner crate listed in `docs/architecture/core-decomposition.md`. -- For tools, keep lightweight contracts and generic registry/provider container - logic in `bitfun-agent-tools`. Core tool runtime should assemble product - tools, adapt `dyn Tool`, apply snapshot decoration, and own tool exposure / - manifest resolution plus on-demand spec discovery (`GetToolSpec`) for now. +- 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 + 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 reviewed port/provider plan and equivalence tests exist. - Any tool migration must preserve expanded/collapsed exposure, prompt-visible diff --git a/src/crates/core/src/agentic/tools/framework.rs b/src/crates/core/src/agentic/tools/framework.rs index fe36b36d1..3f45227d4 100644 --- a/src/crates/core/src/agentic/tools/framework.rs +++ b/src/crates/core/src/agentic/tools/framework.rs @@ -1,26 +1,26 @@ //! 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::{ - is_local_path_within_root, is_remote_posix_path_within_root, ToolPathOperation, - ToolRuntimeRestrictions, + ToolPathOperation, ToolRuntimeRestrictions, is_local_path_within_root, + is_remote_posix_path_within_root, }; 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::{get_workspace_runtime_service_arc, WorkspaceRuntimeContext}; +use crate::service::{WorkspaceRuntimeContext, get_workspace_runtime_service_arc}; use crate::util::errors::BitFunResult; use async_trait::async_trait; pub use bitfun_agent_tools::{ - DynamicMcpToolInfo, DynamicToolInfo, ToolPathBackend, ToolPathResolution, ToolRenderOptions, - ToolResult, ValidationResult, + DynamicMcpToolInfo, DynamicToolInfo, ToolExposure, ToolPathBackend, ToolPathResolution, + ToolRenderOptions, ToolResult, ValidationResult, }; use log::warn; use serde_json::Value; @@ -29,11 +29,6 @@ use std::collections::HashMap; use std::path::{Path, PathBuf}; use tokio_util::sync::CancellationToken; -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum ToolExposure { - Expanded, - Collapsed, -} /// Tool use context #[derive(Debug, Clone)] pub struct ToolUseContext { @@ -442,8 +437,8 @@ impl ToolUseContext { #[cfg(test)] mod path_resolution_tests { use super::ToolUseContext; - use crate::agentic::tools::ToolRuntimeRestrictions; use crate::agentic::WorkspaceBinding; + use crate::agentic::tools::ToolRuntimeRestrictions; use std::collections::HashMap; use std::path::PathBuf; @@ -715,7 +710,7 @@ mod shared_context_tests { use crate::agentic::tools::ToolRuntimeRestrictions; use crate::util::errors::BitFunResult; use async_trait::async_trait; - use serde_json::{json, Value}; + use serde_json::{Value, json}; use std::collections::HashMap; struct MeasurementReadTool; diff --git a/src/crates/core/src/agentic/tools/manifest_resolver.rs b/src/crates/core/src/agentic/tools/manifest_resolver.rs index 6c7375270..d00628965 100644 --- a/src/crates/core/src/agentic/tools/manifest_resolver.rs +++ b/src/crates/core/src/agentic/tools/manifest_resolver.rs @@ -1,9 +1,12 @@ use crate::agentic::agents::AgentToolPolicyOverrides; -use crate::agentic::tools::framework::{Tool, ToolExposure, ToolUseContext}; -use crate::agentic::tools::registry::{get_global_tool_registry, GET_TOOL_SPEC_TOOL_NAME}; +use crate::agentic::tools::framework::{Tool, ToolUseContext}; +use crate::agentic::tools::registry::{GET_TOOL_SPEC_TOOL_NAME, get_global_tool_registry}; use crate::util::types::ToolDefinition; -use serde_json::json; -use std::collections::{HashMap, HashSet}; +use bitfun_agent_tools::{ + ToolManifestDefinition, ToolManifestPolicyTool, build_collapsed_tool_stub_definition, + resolve_tool_manifest_policy, sort_tool_manifest_definitions, +}; +use std::collections::HashSet; use std::sync::Arc; type ToolRef = Arc; @@ -29,55 +32,54 @@ fn build_visible_tools( exposure_overrides: &AgentToolPolicyOverrides, available_tool_names: &HashSet, ) -> ResolvedVisibleTools { - let allowed_set: HashSet<&str> = allowed_tools.iter().map(String::as_str).collect(); - let mut allowed_tool_names = allowed_tools.to_vec(); - let mut expanded_tools = Vec::new(); - let mut collapsed_tool_names = Vec::new(); - let mut collapsed_tools = Vec::new(); - - for tool in tool_snapshot { - let tool_name = tool.name().to_string(); - if !available_tool_names.contains(&tool_name) || !allowed_set.contains(tool_name.as_str()) { - continue; - } - - let exposure = exposure_overrides - .get(&tool_name) - .copied() - .unwrap_or_else(|| tool.default_exposure()); - match exposure { - ToolExposure::Collapsed => { - collapsed_tool_names.push(tool_name); - collapsed_tools.push(tool.clone()); + let policy_tools = tool_snapshot + .iter() + .map(|tool| { + let name = tool.name().to_string(); + ToolManifestPolicyTool { + available: available_tool_names.contains(&name), + default_exposure: tool.default_exposure(), + name, } - ToolExposure::Expanded => expanded_tools.push(tool.clone()), - } - } - - if !collapsed_tool_names.is_empty() { - if !allowed_tool_names - .iter() - .any(|name| name == GET_TOOL_SPEC_TOOL_NAME) - { - allowed_tool_names.push(GET_TOOL_SPEC_TOOL_NAME.to_string()); - } - if let Some(tool) = tool_snapshot - .iter() - .find(|tool| tool.name() == GET_TOOL_SPEC_TOOL_NAME) - .cloned() - { - expanded_tools.push(tool); - } - } + }) + .collect::>(); + let policy = resolve_tool_manifest_policy( + &policy_tools, + allowed_tools, + exposure_overrides, + GET_TOOL_SPEC_TOOL_NAME, + ); + let expanded_tools = tools_by_name(tool_snapshot, &policy.expanded_tool_names); + let collapsed_tools = tools_by_name(tool_snapshot, &policy.collapsed_tool_names); ResolvedVisibleTools { - allowed_tool_names, + allowed_tool_names: policy.allowed_tool_names, expanded_tools, - collapsed_tool_names, + collapsed_tool_names: policy.collapsed_tool_names, collapsed_tools, } } +fn tools_by_name(tool_snapshot: &[ToolRef], tool_names: &[String]) -> Vec { + tool_names + .iter() + .filter_map(|name| { + tool_snapshot + .iter() + .find(|tool| tool.name() == name) + .cloned() + }) + .collect() +} + +fn to_core_tool_definition(definition: ToolManifestDefinition) -> ToolDefinition { + ToolDefinition { + name: definition.name, + description: definition.description, + parameters: definition.parameters, + } +} + pub async fn resolve_visible_tools( allowed_tools: &[String], exposure_overrides: &AgentToolPolicyOverrides, @@ -123,68 +125,28 @@ pub async fn resolve_tool_manifest( .input_schema_for_model_with_context(Some(context)) .await; - tool_definitions.push(ToolDefinition { - name: tool.name().to_string(), + tool_definitions.push(ToolManifestDefinition::new( + tool.name().to_string(), description, parameters, - }); + )); } for tool in &visible_tools.collapsed_tools { - let description = format!( - "{} [This tool is collapsed. Do not call `{}` directly yet. First call `GetToolSpec` with {{\"tool_name\":\"{}\"}} to load its full description and input schema, then retry `{}` using the returned schema.]", - tool.short_description(), - tool.name(), + tool_definitions.push(build_collapsed_tool_stub_definition( tool.name(), - tool.name() - ); - - tool_definitions.push(ToolDefinition { - name: tool.name().to_string(), - description, - parameters: json!({ - "type": "object", - "additionalProperties": false, - "properties": { - "tool_name": { - "type": "string", - "description": format!( - "Do not supply {} arguments here while the tool is collapsed. Use GetToolSpec with {{\"tool_name\":\"{}\"}} first.", - tool.name(), - tool.name() - ) - } - } - }), - }); + &tool.short_description(), + )); } - let tool_ordering: HashMap = [ - ("Task", 1), - ("Bash", 2), - ("TerminalControl", 3), - ("Glob", 4), - ("Grep", 5), - ("Read", 6), - ("Edit", 7), - ("Write", 8), - ("Delete", 9), - ("WebFetch", 10), - ("WebSearch", 11), - ("TodoWrite", 12), - ("Skill", 13), - ("Log", 14), - ("GetToolSpec", 15), - ("ControlHub", 16), - ] - .into_iter() - .map(|(k, v)| (k.to_string(), v)) - .collect(); - tool_definitions.sort_by_key(|tool| tool_ordering.get(&tool.name).unwrap_or(&100)); + sort_tool_manifest_definitions(&mut tool_definitions); ResolvedToolManifest { allowed_tool_names: visible_tools.allowed_tool_names, - tool_definitions, + tool_definitions: tool_definitions + .into_iter() + .map(to_core_tool_definition) + .collect(), collapsed_tool_names: visible_tools.collapsed_tool_names, } } @@ -193,9 +155,9 @@ pub async fn resolve_tool_manifest( mod tests { use super::resolve_tool_manifest; use crate::agentic::agents::AgentToolPolicyOverrides; + use crate::agentic::tools::ToolRuntimeRestrictions; use crate::agentic::tools::framework::{ToolExposure, ToolUseContext}; use crate::agentic::tools::registry::GET_TOOL_SPEC_TOOL_NAME; - use crate::agentic::tools::ToolRuntimeRestrictions; use serde_json::json; use std::collections::HashMap; @@ -228,10 +190,12 @@ mod tests { assert!(manifest.collapsed_tool_names.is_empty()); assert_eq!(manifest.allowed_tool_names, allowed_tools); - assert!(!manifest - .tool_definitions - .iter() - .any(|tool| tool.name == GET_TOOL_SPEC_TOOL_NAME)); + assert!( + !manifest + .tool_definitions + .iter() + .any(|tool| tool.name == GET_TOOL_SPEC_TOOL_NAME) + ); } #[tokio::test] @@ -246,21 +210,29 @@ mod tests { .await; assert_eq!(manifest.collapsed_tool_names, vec!["WebFetch".to_string()]); - assert!(manifest - .allowed_tool_names - .contains(&GET_TOOL_SPEC_TOOL_NAME.to_string())); - assert!(manifest - .tool_definitions - .iter() - .any(|tool| tool.name == "Read")); - assert!(manifest - .tool_definitions - .iter() - .any(|tool| tool.name == "WebFetch")); - assert!(manifest - .tool_definitions - .iter() - .any(|tool| tool.name == GET_TOOL_SPEC_TOOL_NAME)); + assert!( + manifest + .allowed_tool_names + .contains(&GET_TOOL_SPEC_TOOL_NAME.to_string()) + ); + assert!( + manifest + .tool_definitions + .iter() + .any(|tool| tool.name == "Read") + ); + assert!( + manifest + .tool_definitions + .iter() + .any(|tool| tool.name == "WebFetch") + ); + assert!( + manifest + .tool_definitions + .iter() + .any(|tool| tool.name == GET_TOOL_SPEC_TOOL_NAME) + ); let stub = manifest .tool_definitions .iter() @@ -269,10 +241,12 @@ mod tests { assert!(stub.description.contains("First call `GetToolSpec`")); assert_eq!(stub.parameters["type"], json!("object")); assert_eq!(stub.parameters["additionalProperties"], json!(false)); - assert!(stub.parameters["properties"]["tool_name"]["description"] - .as_str() - .unwrap() - .contains("{\"tool_name\":\"WebFetch\"}")); + assert!( + stub.parameters["properties"]["tool_name"]["description"] + .as_str() + .unwrap() + .contains("{\"tool_name\":\"WebFetch\"}") + ); } #[tokio::test] @@ -322,9 +296,11 @@ mod tests { .iter() .find(|tool| tool.name == "WebFetch") .expect("collapsed WebFetch stub"); - assert!(web_fetch - .description - .contains("First call `GetToolSpec` with {\"tool_name\":\"WebFetch\"}")); + assert!( + web_fetch + .description + .contains("First call `GetToolSpec` with {\"tool_name\":\"WebFetch\"}") + ); assert_eq!( web_fetch.parameters, json!({ @@ -349,13 +325,17 @@ mod tests { let manifest = resolve_tool_manifest(&allowed_tools, &overrides, &tool_context()).await; assert!(manifest.collapsed_tool_names.is_empty()); - assert!(manifest - .tool_definitions - .iter() - .any(|tool| tool.name == "WebFetch")); - assert!(!manifest - .tool_definitions - .iter() - .any(|tool| tool.name == GET_TOOL_SPEC_TOOL_NAME)); + assert!( + manifest + .tool_definitions + .iter() + .any(|tool| tool.name == "WebFetch") + ); + assert!( + !manifest + .tool_definitions + .iter() + .any(|tool| tool.name == GET_TOOL_SPEC_TOOL_NAME) + ); } } diff --git a/src/crates/core/src/agentic/tools/registry.rs b/src/crates/core/src/agentic/tools/registry.rs index 278298668..314252fb3 100644 --- a/src/crates/core/src/agentic/tools/registry.rs +++ b/src/crates/core/src/agentic/tools/registry.rs @@ -14,7 +14,7 @@ use std::sync::Arc; type ToolRef = Arc; type ToolDecoratorRef = Arc>; -pub const GET_TOOL_SPEC_TOOL_NAME: &str = "GetToolSpec"; +pub use bitfun_agent_tools::GET_TOOL_SPEC_TOOL_NAME; #[derive(Debug, Clone)] struct SnapshotToolDecorator; @@ -279,16 +279,16 @@ impl ToolRegistryItem for dyn Tool { #[cfg(test)] mod tests { - use super::create_tool_registry; use super::ToolRef; use super::ToolRegistry; + use super::create_tool_registry; use crate::agentic::tools::framework::{ DynamicMcpToolInfo, DynamicToolInfo, Tool, ToolResult, ToolUseContext, ValidationResult, }; use async_trait::async_trait; use bitfun_agent_tools::DynamicToolProvider; - use serde_json::json; use serde_json::Value; + use serde_json::json; use std::sync::Arc; struct DynamicMetadataTool {