diff --git a/backend/app/services/handoff_email_templates.py b/backend/app/services/handoff_email_templates.py
index f63451e..c5df823 100644
--- a/backend/app/services/handoff_email_templates.py
+++ b/backend/app/services/handoff_email_templates.py
@@ -1,3 +1,4 @@
+from datetime import datetime
from html import escape
@@ -25,13 +26,72 @@ def format_sender_line(sender: dict) -> str:
name = (sender.get("name") or "").strip()
address = (sender.get("address") or "").strip()
if not address:
- return "unknown sender"
+ return name or "unknown sender"
local = address.split("@", 1)[0].lower()
if name and name.lower() not in {local, address.lower()}:
return f"{name} <{address}>"
return address
+def sender_display_name(sender: dict | None) -> str:
+ sender = sender or {}
+ name = (sender.get("name") or "").strip()
+ address = (sender.get("address") or "").strip()
+ if name:
+ return name
+ if address:
+ return address.split("@", 1)[0].replace(".", " ").replace("_", " ").replace("-", " ").title()
+ return "unknown sender"
+
+
+def _attribution_date(value) -> str:
+ if not isinstance(value, datetime):
+ return ""
+ return value.strftime("%a, %b %d, %Y at %I:%M %p").replace(" 0", " ")
+
+
+def build_gmail_quote(
+ *,
+ sender: dict | None,
+ event_at,
+ body_text: str | None,
+ body_html: str | None,
+) -> tuple[str, str]:
+ identity = format_sender_line(sender or {})
+ date = _attribution_date(event_at)
+ attribution = f"On {date} {identity} wrote:" if date else f"{identity} wrote:"
+ text_source = (body_text or "").rstrip()
+ quoted_lines = [f"> {line}" if line else ">" for line in text_source.splitlines()]
+ quoted_text = f"{attribution}\n" + "\n".join(quoted_lines)
+ inner_html = (body_html or "").strip() or (
+ f"
diff --git a/backend/app/services/human_handoff_service.py b/backend/app/services/human_handoff_service.py
index bd9b9f1..fc6d95d 100644
--- a/backend/app/services/human_handoff_service.py
+++ b/backend/app/services/human_handoff_service.py
@@ -22,6 +22,12 @@ from app.services.handoff_delivery import (
JOB_RELAY,
enqueue_handoff_delivery,
)
+from app.services.handoff_email_templates import (
+ attach_quoted_history,
+ build_gmail_quote,
+ format_sender_line,
+ sender_display_name,
+)
from app.services.handoff_evaluator import evaluate_handoff
from app.services.handoff_mirror import load_mirror_payload
from app.services.handoff_mirror_policy import (
@@ -89,13 +95,15 @@ def _safe_subject_part(value: str, fallback: str) -> str:
def build_handoff_subject(
*,
- rule_name: str,
+ sender_name: str,
external_sender: str,
external_subject: str,
+ rule_name: str | None = None,
) -> str:
+ del rule_name
subject = " · ".join(
[
- _safe_subject_part(rule_name, "Human"),
+ _safe_subject_part(sender_name, "unknown sender"),
"handoff",
_safe_subject_part(external_sender, "unknown sender"),
_safe_subject_part(external_subject, "(No subject)"),
@@ -472,10 +480,11 @@ async def _create_handoff_mapping(
rule: dict,
) -> dict:
sender = external_message.get("sender") or {}
- sender_text = sender.get("address") or sender.get("name") or "unknown sender"
+ sender_email = (sender.get("address") or "").strip()
+ customer_name = sender_display_name(sender)
private_subject = build_handoff_subject(
- rule_name=rule.get("name") or rule.get("owner_name") or "Human",
- external_sender=sender_text,
+ sender_name=customer_name,
+ external_sender=sender_email or customer_name,
external_subject=external_message.get("subject", ""),
)
thread_id = await create_handoff_thread(
@@ -494,7 +503,8 @@ async def _create_handoff_mapping(
"human_email": rule["owner_email"].lower(),
"human_name": rule.get("owner_name") or rule["owner_email"].split("@", 1)[0],
"agent_email": agent_email.lower(),
- "external_sender": sender.get("address", "").lower(),
+ "external_sender": sender_email.lower(),
+ "external_sender_name": customer_name,
"external_subject": external_message.get("subject", ""),
"private_subject": private_subject,
"allow_replies": rule.get("allow_replies", True),
@@ -710,6 +720,20 @@ async def process_human_reply(
return human_message
+def _mirror_from_identity(source: dict, agent_name: str) -> str:
+ if source.get("direction") == "inbound":
+ return format_sender_line(source.get("sender") or {})
+ return agent_name
+
+
+def _mirror_body(source: dict, payload) -> tuple[str, str | None]:
+ new_text = (source.get("new_reply_content") or "").strip()
+ new_html = (source.get("new_reply_html") or "").strip() or None
+ if new_text:
+ return new_text, new_html
+ return payload.body_text, payload.body_html
+
+
async def _deliver_mirror(database, job: dict, handoff: dict, source: dict) -> ObjectId:
existing = await database.messages.find_one(
{
@@ -724,6 +748,8 @@ async def _deliver_mirror(database, job: dict, handoff: dict, source: dict) -> O
job["account_id"], handoff["agent_email"]
)
payload = await load_mirror_payload(source)
+ body_text, body_html = _mirror_body(source, payload)
+ from_name = _mirror_from_identity(source, sender_name)
parent = await _latest_thread_message(
database,
account_id=job["account_id"],
@@ -736,13 +762,13 @@ async def _deliver_mirror(database, job: dict, handoff: dict, source: dict) -> O
subject = f"Re: {subject}"
internet_message_id = await deliver_email(
sender=handoff["agent_email"],
- sender_name=sender_name,
+ sender_name=from_name,
recipients=[handoff["human_email"]],
cc=[],
bcc=[],
subject=subject,
- body_text=payload.body_text,
- body_html=payload.body_html,
+ body_text=body_text,
+ body_html=body_html,
domain=domain,
in_reply_to=threading.get("in_reply_to"),
references=threading.get("references"),
@@ -758,7 +784,7 @@ async def _deliver_mirror(database, job: dict, handoff: dict, source: dict) -> O
account_id=job["account_id"],
thread_id=handoff["handoff_thread_id"],
direction="outbound",
- sender={"name": sender_name, "address": handoff["agent_email"]},
+ sender={"name": from_name, "address": handoff["agent_email"]},
recipients=[
{
"name": handoff.get("human_name", ""),
@@ -766,8 +792,8 @@ async def _deliver_mirror(database, job: dict, handoff: dict, source: dict) -> O
}
],
subject=subject,
- body_text=payload.body_text,
- body_html=payload.body_html,
+ body_text=body_text,
+ body_html=body_html,
internet_message_id=internet_message_id,
in_reply_to=threading.get("in_reply_to"),
references=threading.get("references", []),
@@ -844,10 +870,26 @@ async def _deliver_relay(database, job: dict, handoff: dict, source: dict) -> Ob
rich = extract_new_reply_mime_content(raw)
inline_parts = rich.inline_parts
file_attachments = rich.file_attachments
+ quoted_text, quoted_html = build_gmail_quote(
+ sender=external_parent.get("sender") or {},
+ event_at=(
+ external_parent.get("sent_at")
+ or external_parent.get("received_at")
+ or external_parent.get("created_at")
+ ),
+ body_text=external_parent.get("body_text") or external_parent.get("snippet") or "",
+ body_html=external_parent.get("body_html"),
+ )
+ relay_text, relay_html = attach_quoted_history(
+ new_text=new_text,
+ new_html=body_html,
+ quoted_text=quoted_text,
+ quoted_html=quoted_html,
+ )
plan = build_external_reply_plan(
parent_external_message=external_parent,
- body_text=new_text,
- body_html=body_html,
+ body_text=relay_text,
+ body_html=relay_html,
subject_override=None,
recipients=[recipient],
parent_internet_id_override=parent_internet_id,
diff --git a/backend/tests/test_handoff_mirror.py b/backend/tests/test_handoff_mirror.py
index 7bf5108..fa78b8e 100644
--- a/backend/tests/test_handoff_mirror.py
+++ b/backend/tests/test_handoff_mirror.py
@@ -49,6 +49,33 @@ def test_format_sender_with_name():
)
+def test_quoted_history_uses_original_sender_not_human_owner():
+ from datetime import datetime, timezone
+
+ from app.services.handoff_email_templates import (
+ attach_quoted_history,
+ build_gmail_quote,
+ )
+
+ quoted_text, quoted_html = build_gmail_quote(
+ sender={"name": "Parth Dixit", "address": "parthdixit091@gmail.com"},
+ event_at=datetime(2026, 10, 6, 7, 29, tzinfo=timezone.utc),
+ body_text="sender: C",
+ body_html="
sender: C
",
+ )
+ body_text, body_html = attach_quoted_history(
+ new_text="responder: D",
+ new_html="
responder: D
",
+ quoted_text=quoted_text,
+ quoted_html=quoted_html,
+ )
+ assert body_text.startswith("responder: D")
+ assert "Parth Dixit
" in body_text
+ assert "> sender: C" in body_text
+ assert "parthdixit091@gmail.com" in body_html
+ assert "Suyash AIML" not in body_text
+
+
def test_external_threading_headers_from_customer_message():
parent = {
"_id": "mongo-id",
diff --git a/backend/tests/test_handoff_runtime.py b/backend/tests/test_handoff_runtime.py
index d11cd9c..46aa620 100644
--- a/backend/tests/test_handoff_runtime.py
+++ b/backend/tests/test_handoff_runtime.py
@@ -278,12 +278,13 @@ def test_human_echo_and_agent_mirror_policy():
def test_private_subject_stays_off_the_external_plane():
private = build_handoff_subject(
- rule_name="Finance approvals",
+ sender_name="Parth Dixit",
external_sender="customer@example.com",
external_subject="Invoice 42",
)
- assert private.startswith("Finance approvals · handoff ·")
+ assert private.startswith("Parth Dixit · handoff ·")
assert "Invoice 42" in private
+ assert "Finance approvals" not in private
@pytest.mark.asyncio
@@ -314,7 +315,7 @@ async def test_first_matching_rule_creates_a_new_linked_handoff():
"_id": source_id,
"message_scope": MESSAGE_SCOPE_EXTERNAL,
"direction": "inbound",
- "sender": {"address": "customer@example.com"},
+ "sender": {"name": "Parth Dixit", "address": "customer@example.com"},
"subject": "Invoice 42",
"body_text": "please review this invoice",
"snippet": "please review this invoice",
@@ -331,7 +332,7 @@ async def test_first_matching_rule_creates_a_new_linked_handoff():
assert result["rule_name"] == "Finance approvals"
assert result["external_thread_id"] == external_thread_id
assert result["handoff_thread_id"]
- assert result["private_subject"].startswith("Finance approvals · handoff ·")
+ assert result["private_subject"].startswith("Parth Dixit · handoff ·")
assert database.handoff_delivery_jobs.items[0]["kind"] == "MIRROR_TO_HUMAN"
diff --git a/backend/tests/test_human_handoff_service.py b/backend/tests/test_human_handoff_service.py
index ccec723..b63ec37 100644
--- a/backend/tests/test_human_handoff_service.py
+++ b/backend/tests/test_human_handoff_service.py
@@ -20,20 +20,20 @@ def test_human_handoff_runtime_is_enabled():
assert callable(resolve_handoff_from_inbound)
-def test_private_subject_contains_rule_sender_and_external_subject():
+def test_private_subject_contains_customer_name_and_external_subject():
subject = build_handoff_subject(
- rule_name="Finance approvals",
- external_sender="customer@example.com",
+ sender_name="Parth Dixit",
+ external_sender="parthdixit091@gmail.com",
external_subject="Invoice 42",
)
assert subject == (
- "Finance approvals · handoff · customer@example.com · Invoice 42"
+ "Parth Dixit · handoff · parthdixit091@gmail.com · Invoice 42"
)
def test_private_subject_removes_header_line_breaks():
subject = build_handoff_subject(
- rule_name="Urgent\r\nBcc: attacker@example.com",
+ sender_name="Urgent\r\nBcc: attacker@example.com",
external_sender="customer@example.com",
external_subject="Help",
)