diff --git a/NEWS.md b/NEWS.md index 59888de6..2c3d9d65 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,9 @@ # btw (development version) +## Bug fixes + +* `btw_tool_agent_subagent()` now correctly uses the options from your `btw.md` file when called directly rather than through `btw_client()` or `btw_app()` (#185). + # btw 1.2.1 ## Bug fixes diff --git a/R/tool-agent-subagent.R b/R/tool-agent-subagent.R index 1d31b298..d410f158 100644 --- a/R/tool-agent-subagent.R +++ b/R/tool-agent-subagent.R @@ -410,20 +410,35 @@ subagent_client <- function( )) } + # We may need to read btw.md directly to determine client settings + config <- NULL + subagent_client_resolved <- client %||% getOption("btw.subagent.client") %||% getOption("btw.client") + if (is.null(subagent_client_resolved)) { + config <- read_btw_file() + + subagent_client_resolved <- + config$options$btw.subagent.client %||% + btw_client_config(config = config, tools = FALSE)$client + } + tools_default <- tools_default %||% getOption("btw.subagent.tools_default") %||% - getOption("btw.tools") + getOption("btw.tools") %||% + config$options$btw.subagent.tools_default %||% + config$tools + tools_default <- subagent_disallow_recursion(tools_default) tools_allowed <- tools_allowed %||% - getOption("btw.subagent.tools_allowed") + getOption("btw.subagent.tools_allowed") %||% + config$options$btw.subagent.tools_allowed # Note: Don't filter subagent from tools_allowed here. # The allowed list should be used as-is for validation. # The final subagent_disallow_recursion() at the end handles the actual filtering. diff --git a/tests/testthat/helpers.R b/tests/testthat/helpers.R index 0c040aa9..c0b1e1d9 100644 --- a/tests/testthat/helpers.R +++ b/tests/testthat/helpers.R @@ -1,4 +1,11 @@ use_latest_pandoc <- function(.envir = parent.frame()) { + has_internet <- asNamespace("testthat")[["has_internet"]] + + if (!is.null(has_internet) && !has_internet("captive.apple.com")) { + # No internet, skip + return() + } + if (!identical(Sys.getenv("NOT_CRAN"), "true")) { # On CRAN, don't attempt to do anything with pandoc return() @@ -151,3 +158,15 @@ local_skip_pandoc_convert <- function(.env = caller_env()) { .env = .env ) } + +local_btw_md <- function(project = NULL, user = NULL, .env = caller_env()) { + local_mocked_bindings( + find_btw_context_file = function(...) { + project + }, + path_find_user = function(...) { + user + }, + .env = .env + ) +} diff --git a/tests/testthat/test-tool-agent-subagent.R b/tests/testthat/test-tool-agent-subagent.R index 0eefd4cd..dd593f0a 100644 --- a/tests/testthat/test-tool-agent-subagent.R +++ b/tests/testthat/test-tool-agent-subagent.R @@ -62,6 +62,37 @@ test_that("subagent_client() clones clients from options", { expect_false(identical(chat1, chat_obj)) }) +test_that("subagent_client() consults btw.md if options are unset", { + # i.e. btw_tool_agent_subagent() uses btw.md settings when called directly + tmp_btw_md <- withr::local_tempfile( + lines = c( + "---", + "options:", + " subagent:", + " client: openrouter/super-cool-model", + " tools_allowed: docs", + " tools_default: docs_help_page", + "---" + ) + ) + + local_btw_md(project = tmp_btw_md) + + agent_client <- subagent_client() + expect_equal(agent_client$get_provider()@name, "OpenRouter") + expect_equal(agent_client$get_provider()@model, "super-cool-model") + + expect_equal( + names(agent_client$get_tools()), + "btw_tool_docs_help_page" + ) + + expect_error( + subagent_client(tools = "btw_tool_skill"), + "disallowed tools" + ) +}) + # subagent_build_description() is internal - description content is tested # through btw_tool_agent_subagent registration tests below @@ -314,6 +345,8 @@ test_that("btw_tool_agent_subagent is filtered out from default tools", { }) test_that("btw_tool_agent_subagent is silently filtered out from 'agent' tool group", { + local_btw_md() + # Request the 'agent' tool group which includes btw_tool_agent_subagent # The subagent tool is silently filtered via can_register (no warning) chat <- subagent_client(tools = c("agent")) @@ -343,6 +376,8 @@ test_that("btw_tool_agent_subagent is silently filtered out even when in tools_a }) test_that("btw_tool_agent_subagent never appears in chat$get_tools() for subagent", { + local_btw_md() + # Test multiple scenarios to ensure subagent tool never appears # Scenario 1: Explicit request → throws error @@ -377,6 +412,8 @@ test_that("btw_tool_agent_subagent never appears in chat$get_tools() for subagen }) test_that("subagent tool errors even when in tools_allowed", { + local_btw_md() + withr::local_options( btw.subagent.tools_allowed = c("btw_tool_agent_subagent", "docs") ) @@ -401,6 +438,8 @@ test_that("subagent tool errors even when in tools_allowed", { # ---- Chat Client Configuration ---------------------------------------------- test_that("subagent_client creates chat with filtered tools", { + local_btw_md() + chat <- subagent_client(tools = "files") expect_true(inherits(chat, "Chat")) diff --git a/tests/testthat/test-tool-session-package-installed.R b/tests/testthat/test-tool-session-package-installed.R index eddadc99..b6eb4e38 100644 --- a/tests/testthat/test-tool-session-package-installed.R +++ b/tests/testthat/test-tool-session-package-installed.R @@ -13,6 +13,9 @@ test_that("btw_tool_sessioninfo_is_package_installed()", { expect_equal(res_installed@extra$package, "dplyr") expect_equal(res_installed@extra$version, "1.0.0") + # Listing available packages requires an internet connection + skip_if_offline() + expect_error( btw_tool_sessioninfo_is_package_installed("skibidi") )