refactor: remove intervene_soft/hard dispatch actions, simplify to new_chat and continue

linuztx committed Mar 15, 2026 at 23:21 UTC 3868c85aee1f62d16054c149732ac9afa67d92e4
4 files changed +18 -57
plugins/_email_integration/helpers/dispatcher.py
+1 -3
@@ -24,7 +24,7 @@ CTX_EMAIL_ATTACHMENTS = "_email_response_attachments"
24
25 BODY_PREVIEW_MAX_CHARS: int = 2000
26
27 -DispatchAction = Literal["new_chat", "continue_chat", "intervene_soft", "intervene_hard"]
27 +DispatchAction = Literal["new_chat", "continue_chat"]
28
29
30 @dataclass
@@ -96,8 +96,6 @@ def parse_dispatcher_response(response: str) -> DispatchDecision:
96 action_map: dict[str, DispatchAction] = {
97 "NEW_CHAT": "new_chat",
98 "CONTINUE": "continue_chat",
99 - "INTERVENE_SOFT": "intervene_soft",
100 - "INTERVENE_HARD": "intervene_hard",
99 }
100 action = action_map.get(action_raw, "new_chat")
101 return DispatchDecision(action=action, context_id=ctx_id, reason=reason)
plugins/_email_integration/helpers/handler.py
+17 -45
@@ -152,28 +152,23 @@ async def _dispatch_message(agent: Agent, handler_cfg: dict, msg: InboundMessage
152 for chat in existing:
153 if chat["thread_id"] == thread_id:
154 await _route_to_chat(
155 - agent, handler_cfg, msg, chat["context_id"], "continue_chat",
155 + agent, handler_cfg, msg, chat["context_id"],
156 )
157 return
158
159 # Dispatcher AI decides
160 decision = await _call_dispatcher(agent, handler_cfg, msg, existing)
161
162 - if decision.action == "new_chat":
163 - await _start_new_chat(agent, handler_cfg, msg)
164 - elif decision.action in ("continue_chat", "intervene_soft", "intervene_hard"):
162 + if decision.action == "continue_chat" and decision.context_id:
163 ctx = AgentContext.get(decision.context_id)
166 - if not ctx:
167 - PrintStyle.warning(
168 - f"Dispatcher referenced unknown context {decision.context_id}, starting new chat"
169 - )
170 - await _start_new_chat(agent, handler_cfg, msg)
171 - else:
172 - await _route_to_chat(
173 - agent, handler_cfg, msg, decision.context_id, decision.action,
174 - )
175 - else:
176 - await _start_new_chat(agent, handler_cfg, msg)
164 + if ctx:
165 + await _route_to_chat(agent, handler_cfg, msg, decision.context_id)
166 + return
167 + PrintStyle.warning(
168 + f"Dispatcher referenced unknown context {decision.context_id}, starting new chat"
169 + )
170 +
171 + await _start_new_chat(agent, handler_cfg, msg)
172
173
174 async def _call_dispatcher(
@@ -253,7 +248,6 @@ async def _route_to_chat(
248 handler_cfg: dict,
249 msg: InboundMessage,
250 context_id: str,
256 - action: disp.DispatchAction,
251 ):
252 context = AgentContext.get(context_id)
253 if not context:
@@ -263,37 +257,15 @@ async def _route_to_chat(
257 if msg.references:
258 context.data[disp.CTX_EMAIL_REFERENCES] = msg.references
259
266 - if action == "intervene_soft":
267 - soft_msg = agent.read_prompt(
268 - "fw.email.intervene_soft.md",
269 - sender=msg.sender,
270 - subject=msg.subject,
271 - body=msg.body,
272 - )
273 - mq.add(context, soft_msg, msg.attachments)
274 - PrintStyle.info(f"Email: queued soft intervention for chat {context_id}")
275 -
276 - elif action == "intervene_hard":
277 - user_msg = _build_user_message(agent, msg, handler_cfg)
278 - system_ctx = agent.read_prompt("fw.email.system_context.md")
279 - mq.log_user_message(context, user_msg, msg.attachments or [], source=" (email)")
280 - context.communicate(UserMessage(
281 - message=user_msg,
282 - system_message=[system_ctx],
283 - attachments=msg.attachments,
284 - ))
285 - PrintStyle.info(f"Email: hard intervention on chat {context_id}")
286 -
287 - else:
288 - user_msg = _build_user_message(agent, msg, handler_cfg)
289 - mq.log_user_message(context, user_msg, msg.attachments or [], source=" (email)")
290 - context.communicate(UserMessage(
291 - message=user_msg,
292 - attachments=msg.attachments,
293 - ))
294 - PrintStyle.info(f"Email: continuing chat {context_id}")
260 + user_msg = _build_user_message(agent, msg, handler_cfg)
261 + mq.log_user_message(context, user_msg, msg.attachments or [], source=" (email)")
262 + context.communicate(UserMessage(
263 + message=user_msg,
264 + attachments=msg.attachments,
265 + ))
266
267 save_tmp_chat(context)
268 + PrintStyle.info(f"Email: continuing chat {context_id}")
269
270
271 # ------------------------------------------------------------------
plugins/_email_integration/prompts/fw.email.dispatcher_system.md
-4
@@ -3,8 +3,6 @@ You receive: the new email (sender, subject, body) and a list of existing chats
3
4 Rules:
5 - If the email content clearly relates to an existing chat (same topic, same sender), reply with CONTINUE and that chat's context_id
6 -- If the email is a status question or low-priority follow-up on a running chat, reply with INTERVENE_SOFT and the context_id — the agent will receive it after current step
7 -- If the email is urgent and requires immediate attention on a running chat, reply with INTERVENE_HARD and the context_id
6 - Otherwise reply with NEW_CHAT
7
8 Use conversation history to determine topic relevance when matching emails to chats.
@@ -15,5 +13,3 @@ ACTION context_id reason
13 Examples:
14 NEW_CHAT _ new request from user
15 CONTINUE ctx_abc123 same sender discussing AWS deployment
18 -INTERVENE_SOFT ctx_abc123 user asking for status update
19 -INTERVENE_HARD ctx_abc123 urgent correction needed
plugins/_email_integration/prompts/fw.email.intervene_soft.md deleted
-5
@@ -1,5 +0,0 @@
1 -[Email from {{sender}}] user following up, respond and continue current work
2 -
3 -Subject: {{subject}}
4 -
5 -{{body}}