fix(photon): a threaded reply to our bubble stays "own" after a restart
Cloud spectrum targets carry no direction and _sent_message_ids is in-memory, so after a gateway restart a swipe-reply to one of our bubbles hydrated the quoted text from the sent-text index but was marked as not ours: the agent saw [Replying to: "..."] instead of [Replying to your previous message: "..."]. Only our sends are indexed, so an index hit is itself proof the target was ours.
This commit is contained in:
@@ -835,14 +835,16 @@ class PhotonAdapter(BasePlatformAdapter):
|
||||
if content.get("type") == "reply" and isinstance(content.get("content"), dict) \
|
||||
and content["content"].get("type") not in (None, "unknown"):
|
||||
target_id = content.get("targetMessageId")
|
||||
reply_ctx = {"reply_to_message_id": target_id,
|
||||
"reply_to_text": content.get("targetText") or None,
|
||||
"reply_to_is_own_message": content.get("targetDirection") == "outbound" or bool(
|
||||
target_id and target_id in self._sent_message_ids)}
|
||||
if target_id and not reply_ctx["reply_to_text"]:
|
||||
# spectrum often can't hydrate the target's text (e.g. our own sends, older
|
||||
# bubbles): fall back to the local index of what we sent.
|
||||
reply_ctx["reply_to_text"] = _lookup_sent_text(space_id, target_id)
|
||||
target_text = content.get("targetText") or None
|
||||
# spectrum often can't hydrate the target's text (e.g. our own sends, older
|
||||
# bubbles): fall back to the local index of what we sent.
|
||||
stored_text = _lookup_sent_text(space_id, target_id) if target_id and not target_text else None
|
||||
# Cloud targets carry no direction and _sent_message_ids is in-memory, so after a
|
||||
# restart an index hit is the only proof the target was ours (only our sends are indexed).
|
||||
is_own = content.get("targetDirection") == "outbound" or bool(
|
||||
target_id and target_id in self._sent_message_ids) or stored_text is not None
|
||||
reply_ctx = {"reply_to_message_id": target_id, "reply_to_text": target_text or stored_text,
|
||||
"reply_to_is_own_message": is_own}
|
||||
content = content["content"]
|
||||
ctype = content.get("type")
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ def _capture_sidecar(adapter: PhotonAdapter, message_id: str = "out-1") -> List[
|
||||
|
||||
|
||||
def _reply_event(inner: Dict[str, Any], *, message_id: str = "in-2", target_id: str = "out-0",
|
||||
direction: str = "outbound", target_text: str | None = "earlier answer") -> Dict[str, Any]:
|
||||
direction: str | None = "outbound", target_text: str | None = "earlier answer") -> Dict[str, Any]:
|
||||
return {
|
||||
"messageId": message_id,
|
||||
"space": {"id": DM, "type": "dm", "phone": PHONE},
|
||||
@@ -143,6 +143,22 @@ async def test_reply_to_our_message_hydrates_quoted_text_from_sent_index(monkeyp
|
||||
assert handled[-1].reply_to_is_own_message is True
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_reply_after_restart_is_still_marked_own(monkeypatch):
|
||||
"""Cloud targets carry no direction; after a restart the index hit alone proves the bubble was ours."""
|
||||
sender = _make_adapter(monkeypatch)
|
||||
_capture_sidecar(sender, message_id="out-9")
|
||||
await sender.send(DM, "the plan is A then B")
|
||||
|
||||
restarted = _make_adapter(monkeypatch) # fresh in-memory _sent_message_ids
|
||||
handled = _capture_handled(restarted, monkeypatch)
|
||||
event = _reply_event({"type": "text", "text": "ok"}, target_id="out-9", direction=None, target_text=None)
|
||||
await restarted._dispatch_inbound(event)
|
||||
|
||||
assert handled[-1].reply_to_text == "the plan is A then B"
|
||||
assert handled[-1].reply_to_is_own_message is True
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_send_to_bare_phone_is_found_from_dm_guid(monkeypatch):
|
||||
"""Cron/standalone sends often target the bare E.164; replies arrive in the DM GUID space."""
|
||||
|
||||
Reference in New Issue
Block a user