refactor(agent): extract reusable tool loop and make subagents independent

Extract core LLM tool loop logic into shared RunToolLoop function that can be
used by both main agent and subagents. Subagents now run their own tool loop
with dedicated tool registry, enabling full independence.

Key changes:
- New pkg/tools/toolloop.go with reusable tool execution logic
- Subagents use message tool to communicate directly with users
- Heartbeat processing is now stateless via ProcessHeartbeat
- Simplified system message routing without result forwarding
- Shared tool registry creation for consistency between agents

This architecture follows openclaw's design where async tools notify via
bus and subagents handle their own user communication.
This commit is contained in:
yinwm
2026-02-13 14:39:39 +08:00
parent 4dfa133cb8
commit 0cce9fc905
5 changed files with 319 additions and 57 deletions

View File

@@ -173,6 +173,9 @@ func (hs *HeartbeatService) executeHeartbeat() {
lastChannel := hs.state.GetLastChannel()
channel, chatID := hs.parseLastChannel(lastChannel)
// Debug log for channel resolution
hs.logInfo("Resolved channel: %s, chatID: %s (from lastChannel: %s)", channel, chatID, lastChannel)
result := handler(prompt, channel, chatID)
if result == nil {
@@ -259,8 +262,12 @@ This file contains tasks for the heartbeat service to check periodically.
## Instructions
If there's nothing that needs attention, respond with: HEARTBEAT_OK
This ensures the heartbeat runs silently when everything is fine.
- Execute ALL tasks listed below. Do NOT skip any task.
- For simple tasks (e.g., report current time), respond directly.
- For complex tasks that may take time, use the spawn tool to create a subagent.
- The spawn tool is async - subagent results will be sent to the user automatically.
- After spawning a subagent, CONTINUE to process remaining tasks.
- Only respond with HEARTBEAT_OK when ALL tasks are done AND nothing needs attention.
---