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
62 changes: 36 additions & 26 deletions loopx/extensions/lark/goal_channel_contracts.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,40 +216,50 @@ def binding_for_goal(
agent_id: str | None = None,
connection_id: str | None = None,
) -> dict[str, Any] | None:
candidates = bindings_for_goal(
payload,
goal_id,
provider_target=provider_target,
)
# Select one raw connection before resolving its provider target. A target
# belongs to one connection; applying it to every sibling lets an unrelated
# Agent's target invalidate the requested Agent's otherwise valid binding.
candidates = bindings_for_goal(payload, goal_id)
selected: dict[str, Any] | None = None
if connection_id:
return next(
selected = next(
(item for item in candidates if item.get("connection_id") == connection_id),
None,
)
if agent_id is not None:
return next(
elif agent_id is not None:
selected = next(
(item for item in candidates if item.get("agent_id") == agent_id),
None,
)
bindings = payload.get("bindings")
stored = bindings.get(goal_id) if isinstance(bindings, Mapping) else None
default_id = (
str(stored.get("default_connection_id") or "")
if isinstance(stored, Mapping)
else ""
)
if default_id:
selected = next(
(item for item in candidates if item.get("connection_id") == default_id),
None,
else:
bindings = payload.get("bindings")
stored = bindings.get(goal_id) if isinstance(bindings, Mapping) else None
default_id = (
str(stored.get("default_connection_id") or "")
if isinstance(stored, Mapping)
else ""
)
if selected is not None:
return selected
# Keep the invalid-default fallback aligned with the writer in
# _without_goal_topic_connection, which promotes min(connection_id).
if not candidates:
return None
return min(candidates, key=lambda item: str(item.get("connection_id") or ""))
if default_id:
selected = next(
(
item
for item in candidates
if item.get("connection_id") == default_id
),
None,
)
if selected is None and candidates:
# Keep the invalid-default fallback aligned with the writer in
# _without_goal_topic_connection, which promotes min(connection_id).
selected = min(
candidates,
key=lambda item: str(item.get("connection_id") or ""),
)
return (
_resolve_goal_binding(selected, provider_target=provider_target)
if selected is not None
else None
)


def human_gate_auto_notify_enabled(binding: Mapping[str, Any] | None) -> bool:
Expand Down
62 changes: 62 additions & 0 deletions tests/extensions/test_lark_goal_channel_binding_isolation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
from __future__ import annotations

import pytest

from loopx.extensions.lark.goal_channel_contracts import binding_for_goal


def _connection(agent_id: str, target_ref: str) -> dict[str, object]:
return {
"goal_id": "goal-alpha",
"agent_id": agent_id,
"provider": "lark",
"enabled": True,
"target_ref": target_ref,
"channel": {},
}


def _target(name: str, chat_id: str) -> dict[str, object]:
return {
"name": name,
"provider": "lark",
"channel": {"chat_id": chat_id},
"identity": {
"mode": "project_bot",
"sender_identity": "bot",
"sender_profile": f"{name}-profile",
},
}


def test_binding_resolution_isolates_selected_agent_from_sibling_target() -> None:
payload = {
"bindings": {
"goal-alpha": {
"schema_version": "loopx_goal_channel_connection_set_v0",
"default_connection_id": "connection-alpha",
"connections": {
"connection-alpha": _connection("agent-alpha", "target-alpha"),
"connection-beta": _connection("agent-beta", "target-beta"),
},
}
}
}

alpha = binding_for_goal(
payload,
"goal-alpha",
connection_id="connection-alpha",
provider_target=_target("target-alpha", "oc_alpha"),
)

assert alpha is not None
assert alpha["agent_id"] == "agent-alpha"
assert alpha["channel"] == {"chat_id": "oc_alpha"}
with pytest.raises(ValueError, match="does not match target_ref"):

Check warning on line 56 in tests/extensions/test_lark_goal_channel_binding_isolation.py

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this exception test to have only one invocation possibly throwing an exception.

See more on https://sonarcloud.io/project/issues?id=huangruiteng_loopx&issues=AaB2Yuk_ZPeUs2YgmHXj&open=AaB2Yuk_ZPeUs2YgmHXj&pullRequest=3992
binding_for_goal(
payload,
"goal-alpha",
connection_id="connection-beta",
provider_target=_target("target-alpha", "oc_alpha"),
)