diff --git a/backend/packages/harness/deerflow/tools/builtins/batch_task_tool.py b/backend/packages/harness/deerflow/tools/builtins/batch_task_tool.py index 5329c17d80..5f32e03d25 100644 --- a/backend/packages/harness/deerflow/tools/builtins/batch_task_tool.py +++ b/backend/packages/harness/deerflow/tools/builtins/batch_task_tool.py @@ -35,6 +35,7 @@ from deerflow.subagents.batch_runtime import ( from deerflow.subagents.registry import get_available_subagent_names, get_subagent_config from deerflow.tools.sync import make_sync_tool_wrapper from deerflow.tools.types import Runtime +from deerflow.utils.assembly_io import run_assembly class BatchTaskItem(BaseModel): @@ -215,8 +216,11 @@ async def batch_task( metadata = runtime.config.get("metadata", {}) if runtime is not None else {} app_config = _batch_app_config(runtime) allowed_subagents = metadata.get("allowed_subagents") - available = get_available_subagent_names(app_config=app_config, allowed_subagents=allowed_subagents) - config = get_subagent_config(subagent_type, app_config=app_config) + # Registry reads sweep the managed-definition store for its signature + # (file stats, or a SQL query) on every TTL miss; resolve them on the + # assembly pool so they cannot stall the streaming event loop (issue #5172). + available = await run_assembly(get_available_subagent_names, app_config=app_config, allowed_subagents=allowed_subagents) + config = await run_assembly(get_subagent_config, subagent_type, app_config=app_config) if config is None or subagent_type not in available: names = ", ".join(available) if available else "none" return _result( diff --git a/backend/packages/harness/deerflow/tools/builtins/task_tool.py b/backend/packages/harness/deerflow/tools/builtins/task_tool.py index 57f2320745..4de3be1630 100644 --- a/backend/packages/harness/deerflow/tools/builtins/task_tool.py +++ b/backend/packages/harness/deerflow/tools/builtins/task_tool.py @@ -954,7 +954,12 @@ async def task_tool( # caller can retry safely. The context_mode guard is the first branch # executed inside the helper, and catalog discovery was moved after the # guard so an invalid mode never triggers a managed-subagent store access. - preflight_config, preflight_error = _preflight_delegation_checks( + # The helper sweeps the managed-definition store for its signature (file + # stats, or a SQL query) on every TTL miss, and the sandbox-policy check + # may read config files, so it resolves on the assembly pool like every + # other blocking hop below (issue #5172). + preflight_config, preflight_error = await run_assembly( + _preflight_delegation_checks, subagent_type=subagent_type, context_mode=context_mode, runtime_app_config=runtime_app_config, diff --git a/backend/tests/blocking_io/test_subagent_registry_offloop.py b/backend/tests/blocking_io/test_subagent_registry_offloop.py new file mode 100644 index 0000000000..09283eeec6 --- /dev/null +++ b/backend/tests/blocking_io/test_subagent_registry_offloop.py @@ -0,0 +1,264 @@ +"""Regression: subagent registry reads resolve off the event loop. + +``_managed_definitions`` sweeps the managed-definition store for its cache +signature on every TTL miss (one second): the file store globs and stats the +definitions directory, the SQL store runs a per-row query, and +``get_available_subagent_names`` may additionally read config files through +``is_host_bash_allowed``. The ``task`` and ``batch_task`` tools run on the +streaming event loop, so those reads must be dispatched to the assembly pool +like every other blocking hop (issue #5172). + +Under the strict Blockbuster context (this directory's conftest), the real +``FileManagedSubagentStore`` signature sweep raises ``BlockingError`` when it +runs on the loop, so the anchors below fail against a plain synchronous +registry call and pass only when the dispatch survives. The meta-check at the +end proves the probe has teeth by calling the store directly on the loop. +""" + +from __future__ import annotations + +import importlib +import threading +from enum import Enum +from pathlib import Path +from types import SimpleNamespace + +import pytest +from langchain_core.messages import ToolMessage + +from deerflow.config.subagents_config import SubagentsAppConfig +from deerflow.persistence.managed_subagents import ManagedSubagentDefinition +from deerflow.persistence.managed_subagents.file import FileManagedSubagentStore +from deerflow.subagents import registry +from deerflow.tools.builtins.batch_task_tool import BatchTaskItem + +# importlib.import_module binds the real module: the package attribute +# ``deerflow.tools.builtins.task_tool`` is shadowed by the StructuredTool. +task_tool_module = importlib.import_module("deerflow.tools.builtins.task_tool") +batch_task_tool_module = importlib.import_module("deerflow.tools.builtins.batch_task_tool") + +pytestmark = pytest.mark.asyncio + + +class _FakeSubagentStatus(Enum): + COMPLETED = "completed" + FAILED = "failed" + RUNNING = "running" + + @property + def is_terminal(self) -> bool: + return self is not _FakeSubagentStatus.RUNNING + + +def _managed_definition_payload() -> str: + return ManagedSubagentDefinition( + name="researcher", + description="Managed researcher", + system_prompt="You are researcher.", + enabled=True, + ).model_dump_json() + + +def _observe_file_store(observed_threads: list) -> FileManagedSubagentStore: + class _ObservingFileStore(FileManagedSubagentStore): + def signature(self): + observed_threads.append(threading.current_thread()) + # Real production blocking read (glob + stat inside a deerflow + # frame): trips the strict gate when it runs on the loop. + return super().signature() + + def list(self): + observed_threads.append(threading.current_thread()) + return super().list() + + return _ObservingFileStore() + + +def _install_real_file_store(monkeypatch, tmp_path: Path, observed_threads: list) -> FileManagedSubagentStore: + # Patch the file store's own get_paths binding instead of constructing a + # Paths(base_dir=...) here: resolving the base dir does blocking IO and + # this helper runs inside the strict gate, in async test bodies. + root = tmp_path / "managed-subagents" + root.mkdir(parents=True, exist_ok=True) + (root / "researcher.json").write_text(_managed_definition_payload(), encoding="utf-8") + store = _observe_file_store(observed_threads) + monkeypatch.setattr( + "deerflow.persistence.managed_subagents.file.get_paths", + lambda: SimpleNamespace(managed_subagents_dir=root), + ) + monkeypatch.setattr(registry, "get_managed_subagent_store", lambda _config=None: store) + # Keep the per-agent override resolution off the real config files: the + # sweep under test is the managed-definition store, not AppConfig loading. + monkeypatch.setattr(registry, "_resolve_subagents_app_config", lambda _app_config=None: SubagentsAppConfig()) + registry._clear_managed_definitions_cache() + return store + + +def _completed_result() -> SimpleNamespace: + return SimpleNamespace( + status=_FakeSubagentStatus.COMPLETED, + ai_messages=[], + result="done", + error=None, + stop_reason=None, + token_usage_records=[], + usage_reported=False, + tool_receipts=None, + bash_executions=None, + ) + + +class _DummyExecutor: + def __init__(self, **_kwargs): + pass + + def execute_async(self, _prompt, task_id=None): + return task_id or "generated-task-id" + + +async def test_task_tool_registry_reads_resolve_off_loop(monkeypatch, tmp_path): + """task_tool resolves the subagent registry on a worker thread.""" + observed_threads: list = [] + _install_real_file_store(monkeypatch, tmp_path, observed_threads) + monkeypatch.setattr(task_tool_module, "SubagentStatus", _FakeSubagentStatus) + monkeypatch.setattr(task_tool_module, "SubagentExecutor", _DummyExecutor) + # Host-bash policy and the inherit-path app-config fallback read + # AppConfig files; neither is the probe target here. + monkeypatch.setattr(task_tool_module, "is_host_bash_allowed", lambda *_a, **_k: True) + monkeypatch.setattr(task_tool_module, "get_app_config", lambda: SimpleNamespace(models=[SimpleNamespace(name="default-model")])) + # Tool assembly is already anchored in test_tool_assembly_offloop.py; stub + # it here so the probe target stays the registry sweep. + monkeypatch.setattr("deerflow.tools.get_available_tools", lambda **_kwargs: []) + monkeypatch.setattr(task_tool_module, "get_background_task_result", lambda _task_id: _completed_result()) + monkeypatch.setattr(task_tool_module, "get_stream_writer", lambda: lambda _event: None) + + async def _no_sleep(_: float) -> None: + return None + + monkeypatch.setattr(task_tool_module.asyncio, "sleep", _no_sleep) + + tool = task_tool_module.task_tool + invoke = getattr(tool, "coroutine", None) or getattr(tool, "func", None) + assert invoke is not None + command = await invoke( + runtime=None, + description="test", + prompt="p", + subagent_type="researcher", + tool_call_id="tc-registry-offloop", + ) + + messages = command.update["messages"] + assert len(messages) == 1 + assert isinstance(messages[0], ToolMessage) + assert "Task Succeeded" in messages[0].content + assert observed_threads, "the managed-definition signature sweep must be invoked" + assert all(thread is not threading.main_thread() for thread in observed_threads) + + +async def test_batch_task_registry_reads_resolve_off_loop(monkeypatch, tmp_path): + """batch_task resolves the subagent registry on a worker thread. + + The submitter is a failing stub: batch_task validates the registry (and + the caller policy) BEFORE attempting submission, so the sweep must have + run — off the loop — by the time the submission error is reported. + """ + observed_threads: list = [] + _install_real_file_store(monkeypatch, tmp_path, observed_threads) + + class _FailingSubmitter: + accepts_continuation_authority = False + + async def submit(self, _request): + raise RuntimeError("no durable batch backend in this test") + + async def get_batch(self, **_kwargs): + return None + + monkeypatch.setattr(batch_task_tool_module, "_batch_submitter", lambda: _FailingSubmitter()) + tool = batch_task_tool_module.batch_task + invoke = getattr(tool, "coroutine", None) or getattr(tool, "func", None) + assert invoke is not None + result = await invoke( + runtime=SimpleNamespace( + state={}, + context={}, + config={"configurable": {"thread_id": "thread-1"}}, + ), + title="batch", + items=[BatchTaskItem(key="k1", prompt="p")], + subagent_type="researcher", + tool_call_id="tc-batch-registry-offloop", + ) + + messages = result.update["messages"] + assert len(messages) == 1 + assert "Batch submission failed" in messages[0].content + assert observed_threads, "the managed-definition signature sweep must be invoked" + assert all(thread is not threading.main_thread() for thread in observed_threads) + + +async def test_registry_signature_sweep_trips_the_gate(monkeypatch, tmp_path): + """Meta-check: the real file-store signature sweep called on the loop must + raise BlockingError, so the anchors above cannot go vacuously green.""" + from blockbuster import BlockingError + + observed_threads: list = [] + store = _install_real_file_store(monkeypatch, tmp_path, observed_threads) + + with pytest.raises(BlockingError): + store.signature() + assert observed_threads == [threading.main_thread()] + + +async def test_task_tool_sandbox_policy_resolves_off_loop(monkeypatch, tmp_path): + """The subagent_type="bash" sandbox-policy hop dispatches to a worker thread. + + ``is_host_bash_allowed(config=None)`` falls through to ``get_app_config()``, + which stats and hashes the config files on every call — so flattening this + one dispatch back to a synchronous call would silently return that read to + the loop even though the registry anchors stay green (they never enter the + bash branch). A real blocking file-read probe as ``is_host_bash_allowed`` + keeps it teeth-verified like every other offload in this file. + """ + probe_file = tmp_path / "sandbox-policy-probe.txt" + probe_file.write_text("probe body", encoding="utf-8") + observed_threads: list = [] + + def _blocking_probe_is_host_bash_allowed(_config=None): + # Real filesystem IO: trips the strict gate when it runs on the loop. + probe_file.read_text(encoding="utf-8") + observed_threads.append(threading.current_thread()) + return True + + _install_real_file_store(monkeypatch, tmp_path, []) + monkeypatch.setattr(task_tool_module, "SubagentStatus", _FakeSubagentStatus) + monkeypatch.setattr(task_tool_module, "SubagentExecutor", _DummyExecutor) + monkeypatch.setattr(task_tool_module, "is_host_bash_allowed", _blocking_probe_is_host_bash_allowed) + monkeypatch.setattr(task_tool_module, "get_app_config", lambda: SimpleNamespace(models=[SimpleNamespace(name="default-model")])) + monkeypatch.setattr("deerflow.tools.get_available_tools", lambda **_kwargs: []) + monkeypatch.setattr(task_tool_module, "get_background_task_result", lambda _task_id: _completed_result()) + monkeypatch.setattr(task_tool_module, "get_stream_writer", lambda: lambda _event: None) + + async def _no_sleep(_: float) -> None: + return None + + monkeypatch.setattr(task_tool_module.asyncio, "sleep", _no_sleep) + + tool = task_tool_module.task_tool + invoke = getattr(tool, "coroutine", None) or getattr(tool, "func", None) + assert invoke is not None + command = await invoke( + runtime=None, + description="test", + prompt="p", + subagent_type="bash", + tool_call_id="tc-sandbox-policy-offloop", + ) + + messages = command.update["messages"] + assert len(messages) == 1 + assert isinstance(messages[0], ToolMessage) + assert "Task Succeeded" in messages[0].content + assert observed_threads, "the sandbox-policy read must be invoked" + assert all(thread is not threading.main_thread() for thread in observed_threads) diff --git a/backend/tests/blocking_io/test_tool_assembly_offloop.py b/backend/tests/blocking_io/test_tool_assembly_offloop.py index c6a089eb57..064ca36515 100644 --- a/backend/tests/blocking_io/test_tool_assembly_offloop.py +++ b/backend/tests/blocking_io/test_tool_assembly_offloop.py @@ -110,7 +110,7 @@ async def test_task_tool_assembles_off_loop(monkeypatch, tmp_path): monkeypatch.setattr( task_tool_module, "get_subagent_config", - lambda _name: SubagentConfig( + lambda _name, **_kwargs: SubagentConfig( name="general-purpose", description="General helper", system_prompt="Base system prompt", @@ -188,7 +188,7 @@ async def test_task_tool_inherit_app_config_resolves_off_loop(monkeypatch, tmp_p monkeypatch.setattr( task_tool_module, "get_subagent_config", - lambda _name: SubagentConfig( + lambda _name, **_kwargs: SubagentConfig( name="general-purpose", description="General helper", system_prompt="Base system prompt", diff --git a/backend/tests/test_task_tool_core_logic.py b/backend/tests/test_task_tool_core_logic.py index ec98aa0465..14c4d39eb6 100644 --- a/backend/tests/test_task_tool_core_logic.py +++ b/backend/tests/test_task_tool_core_logic.py @@ -346,8 +346,8 @@ class _DummyScheduledTask: def test_task_tool_returns_error_for_unknown_subagent(monkeypatch): - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: None) - monkeypatch.setattr(task_tool_module, "get_available_subagent_names", lambda: ["general-purpose"]) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: None) + monkeypatch.setattr(task_tool_module, "get_available_subagent_names", lambda **_kwargs: ["general-purpose"]) result = _run_task_tool( runtime=None, @@ -368,12 +368,12 @@ def test_task_tool_enforces_caller_subagent_snapshot(monkeypatch): runtime.config["metadata"]["allowed_subagents"] = ["planner"] captured = {} - def available(*, allowed_subagents): - captured["allowed"] = allowed_subagents + def available(**kwargs): + captured["allowed"] = kwargs.get("allowed_subagents") return ["planner"] monkeypatch.setattr(task_tool_module, "get_available_subagent_names", available) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: _make_subagent_config()) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: _make_subagent_config()) result = _run_task_tool( runtime=runtime, @@ -392,8 +392,8 @@ def test_task_tool_enforces_caller_subagent_snapshot(monkeypatch): def test_task_tool_explains_when_caller_policy_permits_no_subagents(monkeypatch): runtime = _make_runtime() runtime.config["metadata"]["allowed_subagents"] = [] - monkeypatch.setattr(task_tool_module, "get_available_subagent_names", lambda *, allowed_subagents: []) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: _make_subagent_config()) + monkeypatch.setattr(task_tool_module, "get_available_subagent_names", lambda **_kwargs: []) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: _make_subagent_config()) result = _run_task_tool( runtime=runtime, @@ -429,7 +429,7 @@ def test_task_tool_forwards_the_run_extension_snapshot_to_executor(monkeypatch): monkeypatch.setattr(task_tool_module, "SubagentStatus", FakeSubagentStatus) monkeypatch.setattr(task_tool_module, "SubagentExecutor", DummyExecutor) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: _make_subagent_config()) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: _make_subagent_config()) monkeypatch.setattr( task_tool_module, "get_background_task_result", @@ -462,7 +462,7 @@ def test_task_tool_installs_and_closes_narrow_middleware_recorder(monkeypatch): monkeypatch.setattr(task_tool_module, "SubagentStatus", FakeSubagentStatus) monkeypatch.setattr(task_tool_module, "SubagentExecutor", DummyExecutor) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: _make_subagent_config()) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: _make_subagent_config()) monkeypatch.setattr( task_tool_module, "get_background_task_result", @@ -499,7 +499,7 @@ def test_task_tool_omits_extensions_without_a_run_snapshot(monkeypatch): monkeypatch.setattr(task_tool_module, "SubagentStatus", FakeSubagentStatus) monkeypatch.setattr(task_tool_module, "SubagentExecutor", DummyExecutor) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: _make_subagent_config()) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: _make_subagent_config()) monkeypatch.setattr( task_tool_module, "get_background_task_result", @@ -631,7 +631,7 @@ def test_task_tool_forwards_channel_user_id_to_executor(monkeypatch): monkeypatch.setattr(task_tool_module, "SubagentStatus", FakeSubagentStatus) monkeypatch.setattr(task_tool_module, "SubagentExecutor", DummyExecutor) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: _make_subagent_config()) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: _make_subagent_config()) monkeypatch.setattr( task_tool_module, "get_background_task_result", @@ -669,7 +669,7 @@ def test_task_tool_forwards_is_internal_true_to_executor(monkeypatch): monkeypatch.setattr(task_tool_module, "SubagentStatus", FakeSubagentStatus) monkeypatch.setattr(task_tool_module, "SubagentExecutor", DummyExecutor) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: _make_subagent_config()) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: _make_subagent_config()) monkeypatch.setattr( task_tool_module, "get_background_task_result", @@ -700,7 +700,7 @@ def test_task_tool_preserves_overwrite_sandbox_state_for_executor(monkeypatch): monkeypatch.setattr(task_tool_module, "SubagentStatus", FakeSubagentStatus) monkeypatch.setattr(task_tool_module, "SubagentExecutor", DummyExecutor) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: _make_subagent_config()) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: _make_subagent_config()) monkeypatch.setattr( task_tool_module, "get_background_task_result", @@ -731,7 +731,7 @@ def test_task_tool_forwards_is_internal_false_to_executor(monkeypatch): monkeypatch.setattr(task_tool_module, "SubagentStatus", FakeSubagentStatus) monkeypatch.setattr(task_tool_module, "SubagentExecutor", DummyExecutor) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: _make_subagent_config()) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: _make_subagent_config()) monkeypatch.setattr( task_tool_module, "get_background_task_result", @@ -760,7 +760,7 @@ def test_task_tool_copies_attributes_to_executor(monkeypatch): monkeypatch.setattr(task_tool_module, "SubagentStatus", FakeSubagentStatus) monkeypatch.setattr(task_tool_module, "SubagentExecutor", DummyExecutor) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: _make_subagent_config()) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: _make_subagent_config()) monkeypatch.setattr( task_tool_module, "get_background_task_result", @@ -790,7 +790,7 @@ def test_task_tool_rejects_non_mapping_attributes(monkeypatch): monkeypatch.setattr(task_tool_module, "SubagentStatus", FakeSubagentStatus) monkeypatch.setattr(task_tool_module, "SubagentExecutor", DummyExecutor) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: _make_subagent_config()) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: _make_subagent_config()) monkeypatch.setattr("deerflow.tools.get_available_tools", lambda **kwargs: []) runtime = _make_runtime() @@ -800,9 +800,9 @@ def test_task_tool_rejects_non_mapping_attributes(monkeypatch): def test_task_tool_rejects_bash_subagent_when_host_bash_disabled(monkeypatch): - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: _make_subagent_config()) - monkeypatch.setattr(task_tool_module, "get_available_subagent_names", lambda: ["general-purpose"]) - monkeypatch.setattr(task_tool_module, "is_host_bash_allowed", lambda: False) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: _make_subagent_config()) + monkeypatch.setattr(task_tool_module, "get_available_subagent_names", lambda **_kwargs: ["general-purpose"]) + monkeypatch.setattr(task_tool_module, "is_host_bash_allowed", lambda _config=None: False) result = _run_task_tool( runtime=_make_runtime(), @@ -834,7 +834,7 @@ def test_task_tool_threads_runtime_app_config_to_subagent_dependencies(monkeypat captured["prompt"] = prompt return task_id or "generated-task-id" - def fake_get_available_subagent_names(*, app_config): + def fake_get_available_subagent_names(*, app_config, allowed_subagents=None): captured["names_app_config"] = app_config return ["bash"] @@ -928,7 +928,7 @@ def test_task_tool_emits_running_and_completed_events(monkeypatch): monkeypatch.setattr(task_tool_module, "SubagentStatus", FakeSubagentStatus) monkeypatch.setattr(task_tool_module, "SubagentExecutor", DummyExecutor) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: config) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: config) def get_result(execution_id): polled_execution_ids.append(execution_id) @@ -1009,7 +1009,7 @@ def test_task_tool_emits_cumulative_usage_on_running_event(monkeypatch): "SubagentExecutor", type("DummyExecutor", (), {"__init__": lambda self, **kwargs: None, "execute_async": lambda self, prompt, task_id=None: task_id}), ) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: config) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: config) monkeypatch.setattr(task_tool_module, "get_background_task_result", lambda _: next(responses)) monkeypatch.setattr(task_tool_module, "get_stream_writer", lambda: events.append) monkeypatch.setattr(task_tool_module.asyncio, "sleep", _no_sleep) @@ -1043,8 +1043,8 @@ def test_rejected_task_does_not_capture_parent_history(monkeypatch, context_mode if rejection == "caller-policy": runtime.config["metadata"]["allowed_subagents"] = [] monkeypatch.setattr(task_tool_module, "get_available_subagent_names", lambda **kwargs: [] if rejection == "caller-policy" else ["general-purpose"]) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: None if rejection == "unknown" else _make_subagent_config()) - monkeypatch.setattr(task_tool_module, "is_host_bash_allowed", lambda: False) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: None if rejection == "unknown" else _make_subagent_config()) + monkeypatch.setattr(task_tool_module, "is_host_bash_allowed", lambda _config=None: False) capture = MagicMock(wraps=task_tool_module.ParentContextSnapshot.from_state) monkeypatch.setattr(task_tool_module.ParentContextSnapshot, "from_state", capture) executor = MagicMock() @@ -1082,7 +1082,7 @@ def test_task_tool_context_mode_captures_dispatch_time_history(monkeypatch, cont monkeypatch.setattr(task_tool_module, "SubagentStatus", FakeSubagentStatus) monkeypatch.setattr(task_tool_module, "SubagentExecutor", DummyExecutor) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: _make_subagent_config()) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: _make_subagent_config()) monkeypatch.setattr(task_tool_module, "get_background_task_result", lambda _: _make_result(FakeSubagentStatus.COMPLETED, result="done")) monkeypatch.setattr(task_tool_module, "get_stream_writer", lambda: lambda event: None) monkeypatch.setattr(task_tool_module.asyncio, "sleep", _no_sleep) @@ -1126,7 +1126,7 @@ def test_task_dispatch_snapshot_does_not_include_unfinished_ambiguous_calls(monk monkeypatch.setattr(task_tool_module, "SubagentStatus", FakeSubagentStatus) monkeypatch.setattr(task_tool_module, "SubagentExecutor", DummyExecutor) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: _make_subagent_config()) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: _make_subagent_config()) monkeypatch.setattr(task_tool_module, "get_background_task_result", lambda _: _make_result(FakeSubagentStatus.COMPLETED, result="done")) monkeypatch.setattr(task_tool_module, "get_stream_writer", lambda: lambda event: None) monkeypatch.setattr(task_tool_module.asyncio, "sleep", _no_sleep) @@ -1181,7 +1181,7 @@ def test_task_tool_propagates_tool_groups_to_subagent(monkeypatch, mcp_plugins): monkeypatch.setattr(task_tool_module, "SubagentStatus", FakeSubagentStatus) monkeypatch.setattr(task_tool_module, "SubagentExecutor", DummyExecutor) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: config) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: config) monkeypatch.setattr( task_tool_module, "get_background_task_result", @@ -1232,7 +1232,7 @@ def test_task_tool_uses_subagent_model_override_for_tool_loading(monkeypatch): monkeypatch.setattr(task_tool_module, "SubagentStatus", FakeSubagentStatus) monkeypatch.setattr(task_tool_module, "SubagentExecutor", DummyExecutor) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: config) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: config) monkeypatch.setattr( task_tool_module, "get_background_task_result", @@ -1275,7 +1275,7 @@ def test_task_tool_inherits_parent_skill_allowlist_for_default_subagent(monkeypa monkeypatch.setattr(task_tool_module, "SubagentStatus", FakeSubagentStatus) monkeypatch.setattr(task_tool_module, "SubagentExecutor", DummyExecutor) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: config) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: config) monkeypatch.setattr( task_tool_module, "get_background_task_result", @@ -1321,7 +1321,7 @@ def test_task_tool_intersects_parent_and_subagent_skill_allowlists(monkeypatch): monkeypatch.setattr(task_tool_module, "SubagentStatus", FakeSubagentStatus) monkeypatch.setattr(task_tool_module, "SubagentExecutor", DummyExecutor) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: config) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: config) monkeypatch.setattr( task_tool_module, "get_background_task_result", @@ -1360,7 +1360,7 @@ def test_task_tool_no_tool_groups_passes_none(monkeypatch): monkeypatch.setattr(task_tool_module, "SubagentStatus", FakeSubagentStatus) monkeypatch.setattr(task_tool_module, "SubagentExecutor", DummyExecutor) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: config) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: config) monkeypatch.setattr( task_tool_module, "get_background_task_result", @@ -1398,7 +1398,7 @@ def test_task_tool_runtime_none_passes_groups_none(monkeypatch): monkeypatch.setattr(task_tool_module, "SubagentStatus", FakeSubagentStatus) monkeypatch.setattr(task_tool_module, "SubagentExecutor", DummyExecutor) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: config) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: config) monkeypatch.setattr( task_tool_module, "get_background_task_result", @@ -1437,7 +1437,7 @@ def test_task_tool_runtime_none_passes_groups_none(monkeypatch): "SubagentExecutor", type("DummyExecutor", (), {"__init__": lambda self, **kwargs: None, "execute_async": lambda self, prompt, task_id=None: task_id}), ) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: config) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: config) monkeypatch.setattr( task_tool_module, @@ -1485,7 +1485,7 @@ def test_task_tool_inherit_model_resolves_app_config_off_loop(monkeypatch): monkeypatch.setattr(task_tool_module, "SubagentStatus", FakeSubagentStatus) monkeypatch.setattr(task_tool_module, "SubagentExecutor", DummyExecutor) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: config) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: config) monkeypatch.setattr( task_tool_module, "get_background_task_result", @@ -1521,7 +1521,7 @@ def test_task_tool_returns_timed_out_message(monkeypatch): "SubagentExecutor", type("DummyExecutor", (), {"__init__": lambda self, **kwargs: None, "execute_async": lambda self, prompt, task_id=None: task_id}), ) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: config) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: config) monkeypatch.setattr( task_tool_module, @@ -1557,7 +1557,7 @@ def test_task_tool_surfaces_stop_reason_for_capped_run(monkeypatch): events = [] monkeypatch.setattr(task_tool_module, "SubagentStatus", FakeSubagentStatus) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: config) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: config) monkeypatch.setattr( task_tool_module, "get_background_task_result", @@ -1599,7 +1599,7 @@ def test_task_tool_polling_safety_timeout(monkeypatch): "SubagentExecutor", type("DummyExecutor", (), {"__init__": lambda self, **kwargs: None, "execute_async": lambda self, prompt, task_id=None: task_id}), ) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: config) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: config) receipts = [ { @@ -1652,7 +1652,7 @@ def test_cleanup_called_on_completed(monkeypatch): "SubagentExecutor", type("DummyExecutor", (), {"__init__": lambda self, **kwargs: None, "execute_async": lambda self, prompt, task_id=None: task_id}), ) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: config) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: config) monkeypatch.setattr( task_tool_module, @@ -1692,7 +1692,7 @@ def test_cleanup_called_on_failed(monkeypatch): "SubagentExecutor", type("DummyExecutor", (), {"__init__": lambda self, **kwargs: None, "execute_async": lambda self, prompt, task_id=None: task_id}), ) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: config) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: config) monkeypatch.setattr( task_tool_module, @@ -1735,7 +1735,7 @@ def test_cleanup_called_on_timed_out(monkeypatch): "SubagentExecutor", type("DummyExecutor", (), {"__init__": lambda self, **kwargs: None, "execute_async": lambda self, prompt, task_id=None: task_id}), ) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: config) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: config) monkeypatch.setattr( task_tool_module, @@ -1812,7 +1812,7 @@ def test_cleanup_not_called_on_polling_safety_timeout(monkeypatch): "SubagentExecutor", type("DummyExecutor", (), {"__init__": lambda self, **kwargs: None, "execute_async": lambda self, prompt, task_id=None: task_id}), ) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: config) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: config) monkeypatch.setattr( task_tool_module, @@ -1882,7 +1882,7 @@ def test_cleanup_scheduled_on_cancellation(monkeypatch): "SubagentExecutor", type("DummyExecutor", (), {"__init__": lambda self, **kwargs: None, "execute_async": lambda self, prompt, task_id=None: task_id}), ) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: config) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: config) monkeypatch.setattr(task_tool_module, "get_background_task_result", get_result) monkeypatch.setattr(task_tool_module, "get_stream_writer", lambda: events.append) @@ -1928,7 +1928,7 @@ def test_task_started_emit_failure_stops_subagent_reports_usage_and_cleans_up(mo "SubagentExecutor", type("DummyExecutor", (), {"__init__": lambda self, **kwargs: None, "execute_async": lambda self, prompt, task_id=None: task_id}), ) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: config) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: config) monkeypatch.setattr(task_tool_module, "get_background_task_result", lambda _: terminal_result) monkeypatch.setattr(task_tool_module, "get_stream_writer", lambda: lambda _event: None) monkeypatch.setattr(task_tool_module, "aemit_custom_event", failing_emit) @@ -2014,7 +2014,7 @@ def test_unexpected_poller_error_deferred_cleanup_survives_sync_invocation(monke "SubagentExecutor", type("DummyExecutor", (), {"__init__": lambda self, **kwargs: None, "execute_async": lambda self, prompt, task_id=None: execution_id}), ) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: config) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: config) monkeypatch.setattr(task_tool_module, "get_background_task_result", flip_terminal_off_caller_thread) monkeypatch.setattr(task_tool_module, "get_stream_writer", lambda: lambda _event: None) monkeypatch.setattr(task_tool_module, "aemit_custom_event", fail_on_status_emit) @@ -2108,7 +2108,7 @@ def test_unexpected_error_with_failing_status_accessor_preserves_exception_and_a "SubagentExecutor", type("DummyExecutor", (), {"__init__": lambda self, **kwargs: None, "execute_async": lambda self, prompt, task_id=None: execution_id}), ) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: config) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: config) monkeypatch.setattr(task_tool_module, "get_background_task_result", broken_accessor) monkeypatch.setattr(task_tool_module, "get_stream_writer", lambda: lambda _event: None) monkeypatch.setattr(task_tool_module, "aemit_custom_event", ok_emit) @@ -2190,7 +2190,7 @@ def test_unexpected_error_re_raised_promptly_via_short_grace(monkeypatch): "SubagentExecutor", type("DummyExecutor", (), {"__init__": lambda self, **kwargs: None, "execute_async": lambda self, prompt, task_id=None: "exec-grace"}), ) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: config) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: config) monkeypatch.setattr(task_tool_module, "get_background_task_result", lambda _: _make_result(FakeSubagentStatus.RUNNING, ai_messages=["partial"])) monkeypatch.setattr(task_tool_module, "get_stream_writer", lambda: lambda _event: None) monkeypatch.setattr(task_tool_module, "aemit_custom_event", fail_on_second_emit) @@ -2298,7 +2298,7 @@ def test_deferred_cleanup_drops_final_usage_when_parent_loop_closed(monkeypatch) "SubagentExecutor", type("DummyExecutor", (), {"__init__": lambda self, **kwargs: None, "execute_async": lambda self, prompt, task_id=None: execution_id}), ) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: config) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: config) monkeypatch.setattr(task_tool_module, "get_background_task_result", flip_terminal_and_grow_usage_after_caller_teardown) monkeypatch.setattr(task_tool_module, "get_stream_writer", lambda: lambda _event: None) monkeypatch.setattr(task_tool_module, "aemit_custom_event", fail_on_status_emit) @@ -2437,7 +2437,7 @@ def test_deferred_final_usage_reported_on_parent_loop_with_real_recorder(monkeyp "SubagentExecutor", type("DummyExecutor", (), {"__init__": lambda self, **kwargs: None, "execute_async": lambda self, prompt, task_id=None: execution_id}), ) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: config) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: config) monkeypatch.setattr(task_tool_module, "get_background_task_result", flip_terminal_and_grow_usage_from_deferred_thread) monkeypatch.setattr(task_tool_module, "get_stream_writer", lambda: lambda _event: None) monkeypatch.setattr(task_tool_module, "aemit_custom_event", fail_on_status_emit) @@ -2646,7 +2646,7 @@ async def test_unexpected_error_grace_wait_cancellation_is_honored(monkeypatch): "SubagentExecutor", type("DummyExecutor", (), {"__init__": lambda self, **kwargs: None, "execute_async": lambda self, prompt, task_id=None: execution_id}), ) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: config) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: config) monkeypatch.setattr(task_tool_module, "get_background_task_result", lambda _: _make_result(FakeSubagentStatus.RUNNING, ai_messages=[])) monkeypatch.setattr(task_tool_module, "get_stream_writer", lambda: lambda _event: None) monkeypatch.setattr(task_tool_module, "aemit_custom_event", fail_on_status_emit) @@ -2735,7 +2735,7 @@ def test_execute_async_failure_leaves_no_background_residue(monkeypatch): }, ), ) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: config) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: config) monkeypatch.setattr(task_tool_module, "cleanup_background_task", lambda execution_id_arg: cleanup_calls.append(execution_id_arg)) monkeypatch.setattr( task_tool_module, @@ -2803,7 +2803,7 @@ def test_cancelled_cleanup_stops_after_timeout(monkeypatch): "SubagentExecutor", type("DummyExecutor", (), {"__init__": lambda self, **kwargs: None, "execute_async": lambda self, prompt, task_id=None: task_id}), ) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: config) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: config) monkeypatch.setattr(task_tool_module, "get_stream_writer", lambda: events.append) monkeypatch.setattr(task_tool_module.asyncio, "sleep", cancel_on_first_sleep) monkeypatch.setattr(task_tool_module, "run_on_isolated_subagent_loop", fake_run_on_isolated_subagent_loop) @@ -2867,7 +2867,7 @@ def test_cancellation_wait_uses_subagent_polling_budget(monkeypatch): "SubagentExecutor", type("DummyExecutor", (), {"__init__": lambda self, **kwargs: None, "execute_async": lambda self, prompt, task_id=None: task_id}), ) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: config) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: config) monkeypatch.setattr(task_tool_module, "get_background_task_result", get_result) monkeypatch.setattr(task_tool_module, "get_stream_writer", lambda: events.append) monkeypatch.setattr(task_tool_module.asyncio, "sleep", cancel_then_continue) @@ -2968,7 +2968,7 @@ def test_cancellation_calls_request_cancel(monkeypatch): "SubagentExecutor", type("DummyExecutor", (), {"__init__": lambda self, **kwargs: None, "execute_async": lambda self, prompt, task_id=None: "execution-cancel"}), ) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: config) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: config) monkeypatch.setattr( task_tool_module, @@ -3021,7 +3021,7 @@ def test_task_tool_returns_cancelled_message(monkeypatch): "SubagentExecutor", type("DummyExecutor", (), {"__init__": lambda self, **kwargs: None, "execute_async": lambda self, prompt, task_id=None: task_id}), ) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: config) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: config) monkeypatch.setattr(task_tool_module, "get_background_task_result", lambda _: next(responses)) monkeypatch.setattr(task_tool_module, "get_stream_writer", lambda: events.append) @@ -3058,7 +3058,7 @@ def test_task_tool_emits_completed_metadata(monkeypatch): "SubagentExecutor", type("DummyExecutor", (), {"__init__": lambda self, **kwargs: None, "execute_async": lambda self, prompt, task_id=None: task_id}), ) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: config) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: config) monkeypatch.setattr(task_tool_module, "get_background_task_result", lambda _: _make_result(FakeSubagentStatus.COMPLETED, result="done")) monkeypatch.setattr(task_tool_module, "get_stream_writer", lambda: lambda _: None) monkeypatch.setattr(task_tool_module.asyncio, "sleep", _no_sleep) @@ -3092,7 +3092,7 @@ def test_task_tool_emits_disappeared_task_metadata(monkeypatch): "SubagentExecutor", type("DummyExecutor", (), {"__init__": lambda self, **kwargs: None, "execute_async": lambda self, prompt, task_id=None: task_id}), ) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: config) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: config) monkeypatch.setattr(task_tool_module, "get_background_task_result", lambda _: None) monkeypatch.setattr(task_tool_module, "get_stream_writer", lambda: events.append) monkeypatch.setattr(task_tool_module, "cleanup_background_task", lambda _: None) @@ -3124,7 +3124,7 @@ def test_task_tool_bounds_large_result_metadata(monkeypatch): "SubagentExecutor", type("DummyExecutor", (), {"__init__": lambda self, **kwargs: None, "execute_async": lambda self, prompt, task_id=None: task_id}), ) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: config) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: config) monkeypatch.setattr(task_tool_module, "get_background_task_result", lambda _: _make_result(FakeSubagentStatus.COMPLETED, result=huge)) monkeypatch.setattr(task_tool_module, "get_stream_writer", lambda: lambda _: None) monkeypatch.setattr(task_tool_module.asyncio, "sleep", _no_sleep) @@ -3195,7 +3195,7 @@ def test_cancellation_reports_subagent_usage(monkeypatch): "SubagentExecutor", type("DummyExecutor", (), {"__init__": lambda self, **kwargs: None, "execute_async": lambda self, prompt, task_id=None: task_id}), ) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: config) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: config) monkeypatch.setattr(task_tool_module, "get_background_task_result", get_result) monkeypatch.setattr(task_tool_module, "get_stream_writer", lambda: events.append) monkeypatch.setattr(task_tool_module.asyncio, "sleep", cancel_on_third_sleep) @@ -3251,7 +3251,7 @@ def test_terminal_events_include_usage(monkeypatch, status, expected_type): result = _make_result(status, result="ok" if status == FakeSubagentStatus.COMPLETED else None, error="err" if status != FakeSubagentStatus.COMPLETED else None, token_usage_records=records) monkeypatch.setattr(task_tool_module, "SubagentStatus", FakeSubagentStatus) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: config) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: config) monkeypatch.setattr(task_tool_module, "get_background_task_result", lambda _: result) monkeypatch.setattr(task_tool_module, "get_stream_writer", lambda: events.append) monkeypatch.setattr(task_tool_module, "aemit_custom_event", fake_emit_custom_event) @@ -3287,7 +3287,7 @@ def test_terminal_event_usage_none_when_no_records(monkeypatch): result = _make_result(FakeSubagentStatus.COMPLETED, result="done", token_usage_records=[]) monkeypatch.setattr(task_tool_module, "SubagentStatus", FakeSubagentStatus) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: config) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: config) monkeypatch.setattr(task_tool_module, "get_background_task_result", lambda _: result) monkeypatch.setattr(task_tool_module, "get_stream_writer", lambda: events.append) monkeypatch.setattr(task_tool_module.asyncio, "sleep", _no_sleep) @@ -3381,7 +3381,7 @@ def _run_completed_task_tool(monkeypatch, *, result_text: str, tool_receipts: li monkeypatch.setattr(task_tool_module, "SubagentStatus", FakeSubagentStatus) monkeypatch.setattr(task_tool_module, "SubagentExecutor", DummyExecutor) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: _make_subagent_config()) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: _make_subagent_config()) monkeypatch.setattr( task_tool_module, "get_background_task_result", @@ -3430,7 +3430,7 @@ def _run_completed_task_tool_with_criteria(monkeypatch, *, criteria: list[str], monkeypatch.setattr(task_tool_module, "SubagentStatus", FakeSubagentStatus) monkeypatch.setattr(task_tool_module, "SubagentExecutor", DummyExecutor) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: _make_subagent_config()) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: _make_subagent_config()) monkeypatch.setattr( task_tool_module, "get_background_task_result", @@ -3490,7 +3490,7 @@ def test_task_tool_acceptance_check_failure_is_isolated(monkeypatch): monkeypatch.setattr(task_tool_module, "SubagentStatus", FakeSubagentStatus) monkeypatch.setattr(task_tool_module, "SubagentExecutor", DummyExecutor) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: _make_subagent_config()) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: _make_subagent_config()) monkeypatch.setattr( task_tool_module, "get_background_task_result", @@ -3564,7 +3564,7 @@ def test_task_tool_failed_carries_receipts_without_verdict(monkeypatch): monkeypatch.setattr(task_tool_module, "SubagentStatus", FakeSubagentStatus) monkeypatch.setattr(task_tool_module, "SubagentExecutor", DummyExecutor) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: _make_subagent_config()) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: _make_subagent_config()) monkeypatch.setattr( task_tool_module, "get_background_task_result", @@ -3600,7 +3600,7 @@ def _capture_executor_call(monkeypatch, **call_kwargs): monkeypatch.setattr(task_tool_module, "SubagentStatus", FakeSubagentStatus) monkeypatch.setattr(task_tool_module, "SubagentExecutor", DummyExecutor) - monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _: _make_subagent_config()) + monkeypatch.setattr(task_tool_module, "get_subagent_config", lambda _name, **_kwargs: _make_subagent_config()) monkeypatch.setattr( task_tool_module, "get_background_task_result",