From 295c17e9081a182f0530635f2d013e88844351d1 Mon Sep 17 00:00:00 2001 From: grandcamel Date: Wed, 12 Aug 2026 15:14:34 -0500 Subject: [PATCH] fix(FunctionResult): emit execute_swml transfer beside the SWML document MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit execute_swml(transfer=True) wrote the flag INSIDE the SWML document — {"SWML": {..., "transfer": "true"}} — where it is not a SWML key, so the document executed but the call never exited the agent. The platform documents transfer as a sibling of the SWML key in the action object, which is exactly the shape the live-proven connect() and swml_transfer() helpers already emit. The action is now {"SWML": , "transfer": "true"}; transfer=False still omits the key, and the caller's dict is still never mutated. The three tests that pinned the inside placement now pin the sibling placement, one of them asserting shape-parity with connect()'s action so the two paths cannot drift apart again. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01MgA3KeCEPMKMJVvroZY1wV --- signalwire/signalwire/core/function_result.py | 10 +++++++--- tests/unit/core/test_function_result.py | 15 +++++++++++---- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/signalwire/signalwire/core/function_result.py b/signalwire/signalwire/core/function_result.py index 23f3d286..60607b05 100644 --- a/signalwire/signalwire/core/function_result.py +++ b/signalwire/signalwire/core/function_result.py @@ -480,7 +480,7 @@ def execute_swml( """ # Detect input type and normalize to appropriate format if isinstance(swml_content, str): - # Raw SWML string - parse to dict so we can add transfer key if needed + # Raw SWML string - parse to dict so the action carries a document try: import json @@ -496,11 +496,15 @@ def execute_swml( else: raise TypeError("swml_content must be string, dict, or SWML object") - action = swml_data + # transfer rides BESIDE the SWML document, not inside it — the same + # shape connect() and swml_transfer() emit. Inside the document it is + # not a SWML key and the call never exits the agent. + action: dict[str, Any] = {"SWML": swml_data} if transfer: action["transfer"] = "true" - return self.add_action("SWML", action) + self.action.append(action) + return self def hangup(self) -> "FunctionResult": """ diff --git a/tests/unit/core/test_function_result.py b/tests/unit/core/test_function_result.py index 0803de39..842fd4d8 100644 --- a/tests/unit/core/test_function_result.py +++ b/tests/unit/core/test_function_result.py @@ -542,8 +542,8 @@ def test_execute_swml_dict_does_not_mutate_original(self) -> None: # The original dict should NOT have 'transfer' key added assert "transfer" not in original - # But the action's SWML should have it - assert result.action[0]["SWML"]["transfer"] == "true" + # The action carries it beside the document + assert result.action[0]["transfer"] == "true" def test_execute_swml_sdk_object_with_to_dict(self) -> None: """Test execute_swml with an SDK object that has to_dict()""" @@ -568,12 +568,18 @@ def test_execute_swml_invalid_type_list(self) -> None: FunctionResult().execute_swml([1, 2, 3]) def test_execute_swml_with_transfer_true(self) -> None: - """Test execute_swml with transfer=True adds transfer key""" + """transfer is a SIBLING of the SWML key — the platform's documented + action shape, and the one connect()/swml_transfer() emit. Inside the + document it is not a SWML key and the call never exits the agent.""" swml_dict = {"version": "1.0.0", "sections": {"main": []}} result = FunctionResult().execute_swml(swml_dict, transfer=True) action = result.action[0] - assert action["SWML"]["transfer"] == "true" + assert action["transfer"] == "true" + assert "transfer" not in action["SWML"] + # Same action shape as the live-proven connect() helper + connect_action = FunctionResult().connect("+15551234567").action[0] + assert set(action.keys()) == set(connect_action.keys()) def test_execute_swml_with_transfer_false(self) -> None: """Test execute_swml with transfer=False does not add transfer key""" @@ -581,6 +587,7 @@ def test_execute_swml_with_transfer_false(self) -> None: result = FunctionResult().execute_swml(swml_dict, transfer=False) action = result.action[0] + assert "transfer" not in action assert "transfer" not in action["SWML"] def test_execute_swml_chaining(self) -> None: