Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion loopx/control_plane/quota/turn_envelope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
7 changes: 2 additions & 5 deletions loopx/control_plane/turn_driver/host_candidate.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"))
Expand Down
22 changes: 22 additions & 0 deletions tests/test_turn_envelope.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""
Expand Down
Loading