feat: /stop cancels spawned subagents via session tracking

- SubagentManager tracks _session_tasks: session_key -> {task_id, ...}
- cancel_by_session() cancels all subagents for a session
- SpawnTool passes session_key through to SubagentManager
- /stop response reports subagent cancellation count
- Cleanup callback removes from both _running_tasks and _session_tasks

Builds on #1179
This commit is contained in:
coldxiangyu
2026-02-25 17:53:54 +08:00
parent 3c12efa728
commit 2466b8b843
4 changed files with 142 additions and 4 deletions
+10 -1
View File
@@ -278,15 +278,24 @@ class AgentLoop:
"""Handle a command that must be processed while the agent may be busy."""
if cmd == "/stop":
task = self._active_tasks.get(msg.session_key)
sub_cancelled = await self.subagents.cancel_by_session(msg.session_key)
if task and not task.done():
task.cancel()
try:
await task
except (asyncio.CancelledError, Exception):
pass
parts = ["⏹ Task stopped."]
if sub_cancelled:
parts.append(f"Also stopped {sub_cancelled} background task(s).")
await self.bus.publish_outbound(OutboundMessage(
channel=msg.channel, chat_id=msg.chat_id,
content="⏹ Task stopped.",
content=" ".join(parts),
))
elif sub_cancelled:
await self.bus.publish_outbound(OutboundMessage(
channel=msg.channel, chat_id=msg.chat_id,
content=f"⏹ Stopped {sub_cancelled} background task(s).",
))
else:
await self.bus.publish_outbound(OutboundMessage(