From 615ad5a1b6dae9c87351c8ad3e283ca5512c97c3 Mon Sep 17 00:00:00 2001 From: Garrick Aden-Buie Date: Mon, 23 Mar 2026 17:55:23 -0400 Subject: [PATCH 1/4] tests: Add failing test --- tests/testthat/test-tool-agent-subagent.R | 36 +++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/tests/testthat/test-tool-agent-subagent.R b/tests/testthat/test-tool-agent-subagent.R index 0eefd4cd..26bffc47 100644 --- a/tests/testthat/test-tool-agent-subagent.R +++ b/tests/testthat/test-tool-agent-subagent.R @@ -62,6 +62,42 @@ 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_mocked_bindings( + find_btw_context_file = function(...) { + tmp_btw_md + }, + path_find_user = function(...) NULL + ) + + 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 From 713d046aced85943fa2ab2f6590258194ef1c7cb Mon Sep 17 00:00:00 2001 From: Garrick Aden-Buie Date: Mon, 23 Mar 2026 18:11:13 -0400 Subject: [PATCH 2/4] fix(subagent): Read config from btw.md if tool is called directly --- R/tool-agent-subagent.R | 19 +++++++++++++++++-- tests/testthat/helpers.R | 19 +++++++++++++++++++ tests/testthat/test-tool-agent-subagent.R | 15 +++++++++------ 3 files changed, 45 insertions(+), 8 deletions(-) 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 26bffc47..dd593f0a 100644 --- a/tests/testthat/test-tool-agent-subagent.R +++ b/tests/testthat/test-tool-agent-subagent.R @@ -76,12 +76,7 @@ test_that("subagent_client() consults btw.md if options are unset", { ) ) - local_mocked_bindings( - find_btw_context_file = function(...) { - tmp_btw_md - }, - path_find_user = function(...) NULL - ) + local_btw_md(project = tmp_btw_md) agent_client <- subagent_client() expect_equal(agent_client$get_provider()@name, "OpenRouter") @@ -350,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")) @@ -379,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 @@ -413,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") ) @@ -437,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")) From ba0aef77f43d3be9a551443641cca4f9ed06329f Mon Sep 17 00:00:00 2001 From: Garrick Aden-Buie Date: Mon, 23 Mar 2026 18:11:28 -0400 Subject: [PATCH 3/4] tests: add skip if offline --- tests/testthat/test-tool-session-package-installed.R | 3 +++ 1 file changed, 3 insertions(+) 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") ) From d5d134cdc30e70bb96885ef2c316ab6193ecb0d9 Mon Sep 17 00:00:00 2001 From: Garrick Aden-Buie Date: Mon, 23 Mar 2026 17:41:31 -0700 Subject: [PATCH 4/4] chore: Add news entry --- NEWS.md | 4 ++++ 1 file changed, 4 insertions(+) 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