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
- )