feat: agent-teams part C - expert selection UX (depends on part B) (#371)
* feat: bias main-agent delegation toward configurable.active_teams * feat: add /experts and /expert TUI commands for expert-skill summoning * feat: align expert-selection wording with WebUI (invite/dismiss) * chore: clear active_teams on /new, cleanup active_team.py comment * fix: use local append_to_system_message in ActiveTeamMiddleware * fix: prevent configurable_extra from overriding thread_id * fix: cache expert-skill lookup for /expert completions * fix: suppress /expert completions past first arg and on exact match * fix: invalidate /expert completion cache on skill install/uninstall * fix: refuse /expert invites for non-dispatchable expert skills * fix: fire /expert cache invalidation on every install_skill / uninstall_skill path * fix: propagate active_teams to Rich CLI and serve dispatch surfaces * fix: keep invited experts across channel shutdown * fix: match /expert completions case-insensitively
This commit is contained in:
@@ -34,3 +34,54 @@ class TestNewCommand:
|
||||
ctx = CommandContext(agent=None, thread_id="tid", ui=ui)
|
||||
# No AttributeError even though ctx.agent is None
|
||||
await NewCommand().execute(ctx, [])
|
||||
|
||||
async def test_clears_invited_experts_and_announces(self):
|
||||
"""/new dismisses invited experts uniformly with channel-shutdown clear."""
|
||||
from EvoScientist.commands.base import ChannelRuntime, CommandContext
|
||||
from EvoScientist.commands.implementation.session import NewCommand
|
||||
|
||||
ui = MagicMock()
|
||||
ui.start_new_session = AsyncMock()
|
||||
runtime = ChannelRuntime()
|
||||
runtime.active_teams = ["idea-brainstorm"]
|
||||
ctx = CommandContext(
|
||||
agent=None,
|
||||
thread_id="tid",
|
||||
ui=ui,
|
||||
channel_runtime=runtime,
|
||||
)
|
||||
await NewCommand().execute(ctx, [])
|
||||
assert runtime.active_teams == []
|
||||
messages = [call.args[0] for call in ui.append_system.call_args_list]
|
||||
assert any(
|
||||
"Dismissed experts on new session: idea-brainstorm" in msg
|
||||
for msg in messages
|
||||
)
|
||||
|
||||
async def test_no_announcement_when_no_experts_invited(self):
|
||||
"""No noise on ``/new`` when the invite list is already empty."""
|
||||
from EvoScientist.commands.base import ChannelRuntime, CommandContext
|
||||
from EvoScientist.commands.implementation.session import NewCommand
|
||||
|
||||
ui = MagicMock()
|
||||
ui.start_new_session = AsyncMock()
|
||||
runtime = ChannelRuntime()
|
||||
ctx = CommandContext(
|
||||
agent=None,
|
||||
thread_id="tid",
|
||||
ui=ui,
|
||||
channel_runtime=runtime,
|
||||
)
|
||||
await NewCommand().execute(ctx, [])
|
||||
ui.append_system.assert_not_called()
|
||||
|
||||
async def test_no_announcement_without_channel_runtime(self):
|
||||
"""Runs cleanly when no ChannelRuntime is attached."""
|
||||
from EvoScientist.commands.base import CommandContext
|
||||
from EvoScientist.commands.implementation.session import NewCommand
|
||||
|
||||
ui = MagicMock()
|
||||
ui.start_new_session = AsyncMock()
|
||||
ctx = CommandContext(agent=None, thread_id="tid", ui=ui, channel_runtime=None)
|
||||
await NewCommand().execute(ctx, [])
|
||||
ui.append_system.assert_not_called()
|
||||
|
||||
Reference in New Issue
Block a user