From 12990b208cb3bdb6c08bc34a9c9af64f124aa74c Mon Sep 17 00:00:00 2001 From: huangruiteng <14976749+huangruiteng@users.noreply.github.com> Date: Fri, 25 Sep 2026 02:24:06 +0800 Subject: [PATCH] fix(turn): preserve complete signed host actions Signed-off-by: huangruiteng <14976749+huangruiteng@users.noreply.github.com> --- loopx/control_plane/quota/turn_envelope.ts | 5 ++++- .../turn_driver/host_candidate.py | 7 ++---- tests/test_turn_envelope.py | 22 +++++++++++++++++++ 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/loopx/control_plane/quota/turn_envelope.ts b/loopx/control_plane/quota/turn_envelope.ts index 36df75720b..c2658f4491 100644 --- a/loopx/control_plane/quota/turn_envelope.ts +++ b/loopx/control_plane/quota/turn_envelope.ts @@ -602,11 +602,14 @@ function actionProjection(payload: JsonObject, protocolActionFields: JsonObject) const recommendedAction = replanPacket ? "apply replan_action_packet and emit one required semantic outcome" : text(turn.observation.recommended_action || payload.recommended_action, 480); + // The signed host action is executable authority, not a display summary. + // Budget diagnostics may warn on long commands but must not cut them. + const primaryAction = scalarString(agentChannel.primary_action, "agent_channel.primary_action").trim(); const action: JsonObject = { recommended_action: recommendedAction, primary_action: replanPacket ? "produce one required semantic outcome" - : text(agentChannel.primary_action, 480), + : primaryAction || null, must_attempt: Boolean(agentChannel.must_attempt), delivery_allowed: Boolean(agentChannel.delivery_allowed), quiet_noop_allowed: Boolean(agentChannel.quiet_noop_allowed), diff --git a/loopx/control_plane/turn_driver/host_candidate.py b/loopx/control_plane/turn_driver/host_candidate.py index 6224ff6d9d..6717d277dd 100644 --- a/loopx/control_plane/turn_driver/host_candidate.py +++ b/loopx/control_plane/turn_driver/host_candidate.py @@ -75,11 +75,8 @@ def extract_turn_authority(request: Mapping[str, Any]) -> dict[str, Any]: raise ValueError("TurnEnvelope action signature is missing or does not match") action = _mapping(envelope.get("action")) - primary_action = _bounded( - action.get("primary_action"), - limit=TEXT_LIMITS["recommended_action"], - ) - if not primary_action: + primary_action = action.get("primary_action") + if not isinstance(primary_action, str) or not primary_action.strip(): raise ValueError("signed TurnEnvelope has no primary_action") boundary = _mapping(envelope.get("boundary")) diff --git a/tests/test_turn_envelope.py b/tests/test_turn_envelope.py index f53b3f587a..220df5c9f0 100644 --- a/tests/test_turn_envelope.py +++ b/tests/test_turn_envelope.py @@ -909,6 +909,28 @@ def test_protocol_packet_derivation_retains_unverified_summary() -> None: assert packet["summary"] == "legacy opaque packet" +@pytest.mark.parametrize("path_segments", [170, 1_500]) +def test_signed_host_action_preserves_a_long_complete_command( + path_segments: int, +) -> None: + source = _compat_decision("absent") + command = ( + "loopx quota should-run --registry /" + + "project/" * path_segments + + " --codex-app" + ) + assert len(command) > 1_200 + source["interaction_contract"]["agent_channel"]["primary_action"] = command + + envelope = build_turn_envelope(source) + + assert envelope["action_signature"]["matches"] is True + assert envelope["action"]["primary_action"] == command + assert extract_turn_authority({"turn_envelope": envelope})["primary_action"] == command + if path_segments == 1_500: + assert envelope["compaction"]["within_budget"] is False + + @pytest.mark.parametrize("packet_format", ["absent", "historical_v0", "opaque", "residue"]) def test_protocol_packet_compatibility_through_real_readers(packet_format: str) -> None: """The summary is an observation; the typed decision owns execution."""