refactor: remove _read_fw helper, add ChatSummary TypedDict to dispatcher

linuztx committed Mar 17, 2026 at 02:58 UTC 489a8634f13781aff20a2b4dd8cdd52eb651e922
3 files changed +16 -13
plugins/_email_integration/extensions/python/process_chain_end/_55_email_reply.py
+1 -2
@@ -70,8 +70,7 @@ def _extract_last_response(context: AgentContext) -> str:
70 def _notify_agent_of_failure(
71 context: AgentContext, error: str, attempt: int,
72 ):
73 - from plugins._email_integration.helpers.handler import _read_fw
74 - msg = _read_fw(
73 + msg = context.agent0.read_prompt(
74 "fw.email.send_failed.md", error=error, attempt=str(attempt),
75 max_retries=str(MAX_SEND_RETRIES),
76 )
plugins/_email_integration/helpers/dispatcher.py
+13 -3
@@ -6,7 +6,7 @@ No agent deps in pure helpers.
6
7 import re
8 from dataclasses import dataclass
9 -from typing import Literal
9 +from typing import Literal, TypedDict
10
11 # Pattern for extracting chat thread ID from email subject
12 # Matches: [a0-xxxxxxxx] at end of subject
@@ -46,13 +46,23 @@ def build_reply_subject(original_subject: str, thread_id: str) -> str:
46 return f"{clean} [a0-{thread_id}]"
47
48
49 -def build_chat_summary(context_id: str, data: dict) -> dict:
49 +class ChatSummary(TypedDict):
50 + context_id: str
51 + thread_id: str
52 + sender: str
53 + subject: str
54 + handler: str
55 + history_preview: str
56 +
57 +
58 +def build_chat_summary(context_id: str, data: dict) -> ChatSummary:
59 return {
60 "context_id": context_id,
61 "thread_id": data.get(CTX_EMAIL_THREAD_ID, ""),
62 "sender": data.get(CTX_EMAIL_SENDER, ""),
63 "subject": data.get(CTX_EMAIL_SUBJECT, ""),
64 "handler": data.get(CTX_EMAIL_HANDLER, ""),
65 + "history_preview": "",
66 }
67
68
@@ -66,7 +76,7 @@ def truncate_body(body: str) -> str:
76 return body[:BODY_PREVIEW_MAX_CHARS] + "... (truncated)"
77
78
69 -def format_chats_list(existing_chats: list[dict]) -> str:
79 +def format_chats_list(existing_chats: list[ChatSummary]) -> str:
80 if not existing_chats:
81 return "No existing chats for this handler."
82 sections = []
plugins/_email_integration/helpers/handler.py
+2 -8
@@ -36,12 +36,6 @@ PLUGIN_NAME = "_email_integration"
36 DOWNLOAD_FOLDER = "usr/email/attachments"
37 STATE_FILE = "usr/email/state.json"
38
39 -_PROMPTS_DIR = os.path.join(os.path.dirname(os.path.dirname(__file__)), "prompts")
40 -
41 -
42 -def _read_fw(filename: str, **kwargs: str) -> str:
43 - return files.read_prompt_file(filename, _directories=[_PROMPTS_DIR], **kwargs)
44 -
39
40 # ------------------------------------------------------------------
41 # UID state persistence
@@ -217,7 +211,7 @@ async def _call_dispatcher(
211 agent: Agent,
212 handler_cfg: dict,
213 msg: InboundMessage,
220 - existing_chats: list[dict],
214 + existing_chats: list[disp.ChatSummary],
215 ) -> disp.DispatchDecision:
216 body_preview = disp.truncate_body(msg.body)
217 chats_text = disp.format_chats_list(existing_chats)
@@ -318,7 +312,7 @@ async def _route_to_chat(
312 HISTORY_PREVIEW_MAX_CHARS: int = 500
313
314
321 -def _find_handler_chats(handler_name: str, sender: str) -> list[dict]:
315 +def _find_handler_chats(handler_name: str, sender: str) -> list[disp.ChatSummary]:
316 results = []
317 for ctx_id, ctx in AgentContext._contexts.items():
318 if not isinstance(ctx, AgentContext):