fix(kanban): preserve worker tools with restricted toolsets
This commit is contained in:
@@ -20,6 +20,7 @@ Public API (signatures preserved from the original 2,400-line version):
|
|||||||
check_tool_availability(quiet) -> tuple
|
check_tool_availability(quiet) -> tuple
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
import asyncio
|
import asyncio
|
||||||
@@ -299,6 +300,7 @@ def get_tool_definitions(
|
|||||||
frozenset(disabled_toolsets) if disabled_toolsets else None,
|
frozenset(disabled_toolsets) if disabled_toolsets else None,
|
||||||
registry._generation,
|
registry._generation,
|
||||||
cfg_fp,
|
cfg_fp,
|
||||||
|
bool(os.environ.get("HERMES_KANBAN_TASK")),
|
||||||
)
|
)
|
||||||
cached = _tool_defs_cache.get(cache_key)
|
cached = _tool_defs_cache.get(cache_key)
|
||||||
if cached is not None:
|
if cached is not None:
|
||||||
@@ -334,7 +336,15 @@ def _compute_tool_definitions(
|
|||||||
tools_to_include: set = set()
|
tools_to_include: set = set()
|
||||||
|
|
||||||
if enabled_toolsets is not None:
|
if enabled_toolsets is not None:
|
||||||
for toolset_name in enabled_toolsets:
|
effective_enabled_toolsets = list(enabled_toolsets)
|
||||||
|
if os.environ.get("HERMES_KANBAN_TASK") and "kanban" not in effective_enabled_toolsets:
|
||||||
|
# Dispatcher-spawned workers are scoped by HERMES_KANBAN_TASK and
|
||||||
|
# must always receive the lifecycle handoff tools. Assignee
|
||||||
|
# profiles may intentionally restrict their normal chat toolsets
|
||||||
|
# (for token/cost reasons), but that should not strip the kanban
|
||||||
|
# worker's completion/block/heartbeat surface.
|
||||||
|
effective_enabled_toolsets.append("kanban")
|
||||||
|
for toolset_name in effective_enabled_toolsets:
|
||||||
if validate_toolset(toolset_name):
|
if validate_toolset(toolset_name):
|
||||||
resolved = resolve_toolset(toolset_name)
|
resolved = resolve_toolset(toolset_name)
|
||||||
tools_to_include.update(resolved)
|
tools_to_include.update(resolved)
|
||||||
|
|||||||
@@ -61,6 +61,32 @@ def test_kanban_tools_visible_with_env_var(monkeypatch, tmp_path):
|
|||||||
assert kanban == expected, f"expected {expected}, got {kanban}"
|
assert kanban == expected, f"expected {expected}, got {kanban}"
|
||||||
|
|
||||||
|
|
||||||
|
def test_kanban_worker_env_overrides_profile_toolset_filter(monkeypatch, tmp_path):
|
||||||
|
"""Dispatcher-spawned workers must get lifecycle tools even when the
|
||||||
|
assignee profile restricts enabled toolsets and does not list kanban.
|
||||||
|
"""
|
||||||
|
monkeypatch.setenv("HERMES_KANBAN_TASK", "t_fake")
|
||||||
|
home = tmp_path / ".hermes"
|
||||||
|
home.mkdir()
|
||||||
|
monkeypatch.setenv("HERMES_HOME", str(home))
|
||||||
|
|
||||||
|
import tools.kanban_tools # ensure registered
|
||||||
|
from model_tools import _clear_tool_defs_cache, get_tool_definitions
|
||||||
|
from tools.registry import invalidate_check_fn_cache
|
||||||
|
|
||||||
|
invalidate_check_fn_cache()
|
||||||
|
_clear_tool_defs_cache()
|
||||||
|
schema = get_tool_definitions(
|
||||||
|
enabled_toolsets=["terminal"],
|
||||||
|
quiet_mode=True,
|
||||||
|
)
|
||||||
|
names = {s["function"].get("name") for s in schema if "function" in s}
|
||||||
|
assert "kanban_show" in names
|
||||||
|
assert "kanban_complete" in names
|
||||||
|
assert "kanban_block" in names
|
||||||
|
assert "kanban_list" not in names
|
||||||
|
|
||||||
|
|
||||||
def test_worker_with_kanban_toolset_still_hides_board_routing(monkeypatch, tmp_path):
|
def test_worker_with_kanban_toolset_still_hides_board_routing(monkeypatch, tmp_path):
|
||||||
"""Task scope wins over profile config for board-routing tools.
|
"""Task scope wins over profile config for board-routing tools.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user