refactor: replace email_update tool with response break_loop param

linuztx committed Mar 16, 2026 at 16:16 UTC 25a7a25973a6d9feade7cca6fc53e426c0cc3e08
7 files changed +89 -114
plugins/_email_integration/extensions/python/system_prompt/_20_email_context.py
-3
@@ -20,6 +20,3 @@ class EmailContextPrompt(Extension):
20 system_prompt.append(
21 self.agent.read_prompt("fw.email.system_context_reply.md")
22 )
23 - system_prompt.append(
24 - self.agent.read_prompt("fw.email.tool_email_update.md")
25 - )
plugins/_email_integration/extensions/python/tool_execute_after/_50_email_attachments.py deleted
-25
@@ -1,25 +0,0 @@
1 -"""Intercept response tool to capture email attachments."""
2 -
3 -from helpers.extension import Extension
4 -from helpers.print_style import PrintStyle
5 -from plugins._email_integration.helpers.dispatcher import CTX_EMAIL_HANDLER, CTX_EMAIL_ATTACHMENTS
6 -
7 -
8 -class EmailResponseAttachments(Extension):
9 -
10 - async def execute(self, tool_name: str = "", **kwargs):
11 - if tool_name != "response":
12 - return
13 - if not self.agent:
14 - return
15 - if not self.agent.context.data.get(CTX_EMAIL_HANDLER):
16 - return
17 -
18 - tool = self.agent.loop_data.current_tool
19 - if not tool:
20 - return
21 -
22 - attachments = tool.args.get("attachments", [])
23 - if attachments:
24 - self.agent.context.data[CTX_EMAIL_ATTACHMENTS] = attachments
25 - PrintStyle.info(f"Email: queued {len(attachments)} attachment(s)")
plugins/_email_integration/extensions/python/tool_execute_after/_50_email_response.py new
+60
@@ -0,0 +1,60 @@
1 +"""Intercept response tool in email sessions for attachments and break_loop."""
2 +
3 +from helpers.extension import Extension
4 +from helpers.print_style import PrintStyle
5 +from helpers.tool import Response
6 +from plugins._email_integration.helpers.dispatcher import CTX_EMAIL_HANDLER, CTX_EMAIL_ATTACHMENTS
7 +
8 +
9 +class EmailResponseIntercept(Extension):
10 +
11 + async def execute(
12 + self, tool_name: str = "", response: Response | None = None, **kwargs,
13 + ):
14 + if tool_name != "response":
15 + return
16 + if not self.agent:
17 + return
18 + context = self.agent.context
19 + if not context.data.get(CTX_EMAIL_HANDLER):
20 + return
21 +
22 + tool = self.agent.loop_data.current_tool
23 + if not tool:
24 + return
25 +
26 + # Capture attachments for later (process_chain_end) or inline send
27 + attachments = tool.args.get("attachments", [])
28 + if attachments:
29 + context.data[CTX_EMAIL_ATTACHMENTS] = attachments
30 +
31 + # Check break_loop arg from agent
32 + agent_break = tool.args.get("break_loop", True)
33 + if agent_break is False and response:
34 + await self._send_inline(context, tool, response)
35 +
36 + async def _send_inline(
37 + self, context, tool, response: Response,
38 + ):
39 + from plugins._email_integration.helpers.handler import send_email_reply
40 +
41 + agent = self.agent
42 + assert agent is not None
43 +
44 + text = tool.args.get("text", tool.args.get("message", ""))
45 + attachments = context.data.pop(CTX_EMAIL_ATTACHMENTS, [])
46 +
47 + if attachments:
48 + PrintStyle.info(f"Email: sending update with {len(attachments)} attachment(s)")
49 +
50 + error = await send_email_reply(context, text, attachments or None)
51 +
52 + if error:
53 + result = agent.read_prompt("fw.email.update_error.md", error=error)
54 + else:
55 + result = agent.read_prompt("fw.email.update_ok.md")
56 +
57 + # Override response: don't break loop, add result to history
58 + response.break_loop = False
59 + response.message = result
60 + agent.hist_add_tool_result("response", result)
plugins/_email_integration/prompts/fw.email.system_context.md
+4 -3
@@ -1,3 +1,4 @@
1 -you are communicating via email, user sent email message
2 -use response tool to send reply — response sent as email reply automatically
3 -be concise, professional, clear
1 +email session user communicates via email
2 +response tool sends reply automatically
3 +before each action inform user next step then execute
4 +long tasks break_loop false sends progress without ending loop
plugins/_email_integration/prompts/fw.email.system_context_reply.md
+25 -11
@@ -1,21 +1,35 @@
1 ## Email conversation
2 -you are in an email conversation user communicates via email
3 -your response (via response tool) will be sent as email reply automatically
4 -be concise professional clear
5 -do not include email headers or signatures — they are added automatically
2 +email session user communicates via email
3 +response tool sends reply
4 +before every action inform user
5 +what you will do next then execute
6
7 -### Progress updates
8 -for long tasks use email_update tool to send progress emails without ending your task
9 -use response tool only for the final answer
7 +### progress updates
8 +long tasks > response with "break_loop"
9 +false sends email without ending loop
10 +omit or true for final answer
11 +~~~json
12 +{
13 + ...
14 + "tool_name": "response",
15 + "tool_args": {
16 + "text": "Completed step 1...",
17 + "break_loop": false
18 + }
19 +}
20 +~~~
21
11 -### Sending file attachments
12 -to attach files to your email reply add attachments list with absolute file paths:
22 +### file attachments
23 +include file paths in attachments array
24 ~~~json
25 {
26 + ...
27 "tool_name": "response",
28 "tool_args": {
17 - "text": "Here is the file you requested.",
18 - "attachments": ["/path/to/file.txt"]
29 + "text": "Here is...",
30 + "attachments": [
31 + "/path/file.txt"
32 + ]
33 }
34 }
35 ~~~
plugins/_email_integration/prompts/fw.email.tool_email_update.md deleted
-28
@@ -1,28 +0,0 @@
1 -### email_update
2 -send progress or status email to user without ending your task
3 -use when working on long tasks keep user informed while continuing work
4 -does NOT end your agentic loop use "response" tool for final answer
5 -usage:
6 -~~~json
7 -{
8 - ...
9 - "tool_name": "email_update",
10 - "tool_args": {
11 - "text": "... Completed step 1 of 3..."
12 - }
13 -}
14 -~~~
15 -
16 -optional attach files with attachments
17 -~~~json
18 -{
19 - ...
20 - "tool_name": "email_update",
21 - "tool_args": {
22 - "text": "... preliminary results ...",
23 - "attachments": [
24 - "/path/report.csv"
25 - ]
26 - }
27 -}
28 -~~~
plugins/_email_integration/tools/email_update.py deleted
-44
@@ -1,44 +0,0 @@
1 -"""Send progress email without ending the agent loop."""
2 -
3 -from helpers.tool import Tool, Response
4 -from plugins._email_integration.helpers.dispatcher import CTX_EMAIL_HANDLER
5 -
6 -
7 -class EmailUpdate(Tool):
8 -
9 - async def execute(self, **kwargs) -> Response:
10 - if not self.agent.context.data.get(CTX_EMAIL_HANDLER):
11 - return Response(
12 - message=self.agent.read_prompt(
13 - "fw.email.update_error.md",
14 - error="not in an email session"),
15 - break_loop=False,
16 - )
17 -
18 - text = self.args.get("text", "")
19 - if not text:
20 - return Response(
21 - message=self.agent.read_prompt(
22 - "fw.email.update_error.md",
23 - error="text is required",
24 - ),
25 - break_loop=False,
26 - )
27 -
28 - attachments = list(self.args.get("attachments", []))
29 -
30 - from plugins._email_integration.helpers.handler import send_email_reply
31 - error = await send_email_reply(self.agent.context, text, attachments or None)
32 -
33 - if error:
34 - return Response(
35 - message=self.agent.read_prompt(
36 - "fw.email.update_error.md", error=error,
37 - ),
38 - break_loop=False,
39 - )
40 -
41 - return Response(
42 - message=self.agent.read_prompt("fw.email.update_ok.md"),
43 - break_loop=False,
44 - )