feat(email): add email_update tool for sending progress emails without ending agent loop
linuztx committed
Mar 15, 2026 at 23:54 UTC
4ebb5f472cdb0a22b6891685087c3b698a6cc349
6 files changed
+79
-7
plugins/_email_integration/extensions/python/system_prompt/_20_email_context.py
+6
-2
@@ -17,5 +17,9 @@ class EmailContextPrompt(Extension):
17
return
18
19
if self.agent.context.data.get(CTX_EMAIL_HANDLER):
20
- prompt = self.agent.read_prompt("fw.email.system_context_reply.md")
21
- system_prompt.append(prompt)
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/prompts/fw.email.system_context_reply.md
+7
-5
@@ -1,15 +1,17 @@
1
## Email conversation
2
-you are in an email conversation, user communicates via email
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
4
+be concise professional clear
5
do not include email headers or signatures — they are added automatically
6
-if task requires extended processing, provide brief status then continue work
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
10
11
### Sending file attachments
9
-to attach files to your email reply, add `attachments` list with absolute file paths to the response tool:
12
+to attach files to your email reply add attachments list with absolute file paths:
13
~~~json
14
{
12
- ...
15
"tool_name": "response",
16
"tool_args": {
17
"text": "Here is the file you requested.",
plugins/_email_integration/prompts/fw.email.tool_email_update.md
new
+28
@@ -0,0 +1,28 @@
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/prompts/fw.email.update_error.md
new
+1
@@ -0,0 +1 @@
1
+email update failed: {{error}}
plugins/_email_integration/prompts/fw.email.update_ok.md
new
+1
@@ -0,0 +1 @@
1
+email update sent, continue working
plugins/_email_integration/tools/email_update.py
new
+36
@@ -0,0 +1,36 @@
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
+ await send_email_reply(self.agent.context, text, attachments or None)
32
+
33
+ return Response(
34
+ message=self.agent.read_prompt("fw.email.update_ok.md"),
35
+ break_loop=False,
36
+ )