mirror of
https://github.com/usestrix/strix.git
synced 2026-08-23 03:12:37 +02:00
refactor(tools): split wait_for_message into respond_to_user + wait_for_agents
One tool was doing three jobs (wait on the user, wait on other agents, and - wrongly - wait for a long-running command), so the driver had to guess which one an agent meant and used parent_id as the proxy: the root waits for a human, everyone else waits for agents. That proxy is wrong, since the user can message any agent from the TUI's agent tree. Tool identity now carries the intent, and the coordinator records it as a wait_kind that survives snapshot/restore: respond_to_user -> wait_kind="user", never auto-resumed (root or not) wait_for_agents -> wait_kind="agents", auto-resumed on a 300s timer recovery exhaust -> wait_kind="stalled" respond_to_user fuses the message and the yield into one call, so there is no way to answer and then forget to stop - the two-step that gpt-4o-mini skipped 2/2 in live testing. Plain text still renders as before. Auto-resume is also bounded now: an agent that re-parks after every timeout burned a model turn every 300s for the rest of the scan (and, since parked children notify their parent, spammed the parent's inbox on the same cycle). After _MAX_IDLE_AUTO_RESUMES it stays parked until a real message arrives.
This commit is contained in:
@@ -2,18 +2,29 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
import pytest
|
||||
from agents.tool import FunctionTool
|
||||
|
||||
from strix.agents import factory
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from agents.tool_context import ToolContext
|
||||
|
||||
|
||||
def _tool(name: str) -> FunctionTool:
|
||||
# A per-tool closure keeps two same-named tools unequal, which is what the
|
||||
# duplicate-name tests exercise.
|
||||
async def invoke(_ctx: ToolContext[Any], _input: str) -> str:
|
||||
return "ok"
|
||||
|
||||
return FunctionTool(
|
||||
name=name,
|
||||
description="test tool",
|
||||
params_json_schema={"type": "object", "properties": {}, "additionalProperties": False},
|
||||
on_invoke_tool=lambda _ctx, _inp: "ok",
|
||||
on_invoke_tool=invoke,
|
||||
)
|
||||
|
||||
|
||||
@@ -86,3 +97,18 @@ def test_no_override_renders_builtin_prompt() -> None:
|
||||
|
||||
assert isinstance(agent.instructions, str)
|
||||
assert agent.instructions != ""
|
||||
|
||||
|
||||
def test_respond_to_user_is_interactive_only() -> None:
|
||||
"""Yielding to the user is meaningless when no user is attached."""
|
||||
interactive = factory.build_strix_agent(is_root=True, interactive=True)
|
||||
autonomous = factory.build_strix_agent(is_root=True, interactive=False)
|
||||
|
||||
assert "respond_to_user" in [t.name for t in interactive.tools]
|
||||
assert "respond_to_user" not in [t.name for t in autonomous.tools]
|
||||
|
||||
|
||||
def test_wait_for_agents_is_available_in_both_modes() -> None:
|
||||
for interactive in (True, False):
|
||||
agent = factory.build_strix_agent(is_root=True, interactive=interactive)
|
||||
assert "wait_for_agents" in [t.name for t in agent.tools]
|
||||
|
||||
Reference in New Issue
Block a user