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
4 changes: 4 additions & 0 deletions loopx/extensions/lark/manager_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ def manager_failure_reply(error: Exception) -> tuple[str, str]:
"hard_timeout": "处理超过时间限制",
"interrupted": "处理已中断",
"manager_authorization_unavailable": "当前连接的授权范围不可用",
# The executor's own gate refused the call, which is what an exhausted
# credential or a revoked login looks like from the channel. Naming the
# executor keeps the owner from reading it as a manager defect.
"host_gate": "上游执行器拒绝本次调用(额度或授权),请在管家执行器一侧检查",
"manager_channel_executor_rebind_required": (
"管家的执行器已由本机设置更改,需要重新应用一次管家连接"
),
Expand Down
20 changes: 20 additions & 0 deletions tests/test_manager_team_plan_guidance.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,23 @@ def test_manager_guidance_orders_one_team_preview_before_any_effect() -> None:
# An unstaffable lane is named as a gap rather than invented.
assert "as a gap, with the missing registration or grant" in text
assert "inventing a lane, an Agent, or a capability" in text


def test_the_owner_visible_failure_names_the_executor_that_refused() -> None:
"""A host gate is the executor's refusal, not a defect in the manager."""

from loopx.extensions.lark.manager_context import manager_failure_reply

class _Refused(RuntimeError):
error_code = "host_gate"

code, text = manager_failure_reply(_Refused("upstream refused"))

assert code == "host_gate"
assert "上游执行器" in text
assert "管家处理失败" not in text
# An unmapped code still falls back to the bounded generic label.
class _Unknown(RuntimeError):
error_code = "some_future_code"

assert manager_failure_reply(_Unknown("x"))[0] == "processing_failed"
Loading