diff --git a/strix/agents/prompts/system_prompt.jinja b/strix/agents/prompts/system_prompt.jinja index b13c9a4c..cb26f756 100644 --- a/strix/agents/prompts/system_prompt.jinja +++ b/strix/agents/prompts/system_prompt.jinja @@ -44,7 +44,7 @@ INTERACTIVE BEHAVIOR: - If you end a turn on plain text and the nudge arrives, your words already reached the user. Do not repeat them: call wait_for_user. - You can narrate while you work — plain text is shown to the user as you go. Narrating is free; wait_for_user is specifically the act of WAITING for the user, so do not call it just to give a status update. - Respond naturally when the user asks questions or gives instructions. -- While actively working on a task, every turn should carry exactly one tool call — use think to plan, the appropriate tool to act, and wait_for_user only when you genuinely need the user. +- While actively working on a task, every turn should carry a tool call (independent calls can go in the same turn) — use think to plan, the appropriate tool to act, and wait_for_user only when you genuinely need the user. - Never loop through think or other tools just to prepare, polish, confirm, or announce an answer. Once you know the answer, write it and call wait_for_user. {% else %} AUTONOMOUS BEHAVIOR: diff --git a/strix/core/execution.py b/strix/core/execution.py index 3bb9a5fe..4bcd31d8 100644 --- a/strix/core/execution.py +++ b/strix/core/execution.py @@ -1004,7 +1004,7 @@ async def _append_tool_required_message( message = ( "Your previous message ended a turn without a tool call. Plain text never ends " "execution and never hands control to the user: it is shown to the user, and the " - "run continues. Continue immediately and call exactly one tool. " + "run continues. Continue immediately and call a tool. " "If you have nothing to do until the user replies, call wait_for_user; your " "text already reached them, so do not repeat it. " "If you are blocked waiting for another agent, call wait_for_agents. " @@ -1016,7 +1016,7 @@ async def _append_tool_required_message( message = ( "Your previous response ended the autonomous run without a lifecycle tool " "call. That is invalid in non-interactive mode; plain text final answers are " - "ignored. Continue immediately and call exactly one tool. " + "ignored. Continue immediately and call a tool. " f"If your work is complete, call {finish_tool}. " "If you are blocked waiting for another agent, call wait_for_agents. " "Otherwise use the appropriate execution or planning tool. " diff --git a/strix/core/inputs.py b/strix/core/inputs.py index 7bb2b72c..51a5f652 100644 --- a/strix/core/inputs.py +++ b/strix/core/inputs.py @@ -255,7 +255,7 @@ def make_model_settings( ) -> ModelSettings: headers = _request_headers(model_name, extra_headers) model_settings = ModelSettings( - parallel_tool_calls=False if has_tools else None, + parallel_tool_calls=True if has_tools else None, retry=DEFAULT_MODEL_RETRY, include_usage=True, extra_args=request_timeout_extra_args(request_timeout), diff --git a/tests/test_inputs.py b/tests/test_inputs.py index f54d3717..fd0b05f0 100644 --- a/tests/test_inputs.py +++ b/tests/test_inputs.py @@ -310,8 +310,8 @@ def test_make_model_settings_forces_required_for_anyllm_routed_openai_model() -> assert settings.tool_choice == "required" -def test_make_model_settings_disables_parallel_tool_calls_by_default() -> None: - assert make_model_settings("none", model_name="gpt-4o").parallel_tool_calls is False +def test_make_model_settings_enables_parallel_tool_calls_by_default() -> None: + assert make_model_settings("none", model_name="gpt-4o").parallel_tool_calls is True def test_make_model_settings_omits_parallel_tool_calls_without_tools() -> None: