Files
goclaw/cmd/gateway_subagent_announce_format.go
T
viettranx 2c1ef25392 feat(subagent): token tracking, edition limits, waitAll, auto-retry, producer-consumer announce (#600)
- Token cost tracking: accumulate input/output tokens per subagent,
  include in announce messages and persist to DB
- Per-edition rate limits: MaxSubagentConcurrent/Depth on Edition struct,
  tenant-scoped concurrency enforcement in Spawn/RunSync
- WaitAll action: spawn(action=wait, timeout=N) blocks until all
  children complete, returns merged summary
- Auto-retry: configurable MaxRetries (default 2) with linear backoff
  for transient LLM failures
- Producer-consumer announce queue: merges staggered subagent results
  into single LLM run (same pattern as team task announces)
- Raw metadata in bus messages to prevent double-formatting
- Fire-and-forget DB persistence with detached context + tenant scope
- Split oversized files for <200 line compliance
2026-03-31 11:45:16 +07:00

71 lines
2.1 KiB
Go

package cmd
import (
"fmt"
"strings"
"time"
"github.com/nextlevelbuilder/goclaw/internal/tools"
)
// buildMergedSubagentAnnounce creates the announce message for one or more subagent results.
func buildMergedSubagentAnnounce(entries []subagentAnnounceEntry, roster tools.SubagentRoster) string {
var sb strings.Builder
if len(entries) == 1 {
e := entries[0]
statusLabel := "completed successfully"
if e.Status == "failed" {
statusLabel = "failed"
} else if e.Status == "cancelled" {
statusLabel = "was cancelled"
}
fmt.Fprintf(&sb, "[System Message] A subagent task %q just %s.\n\nResult:\n%s\n\nStats: runtime %s, iterations %d, tokens %d in / %d out\n",
e.Label, statusLabel, e.Content,
e.Runtime.Round(time.Millisecond), e.Iterations,
e.InputTokens, e.OutputTokens)
} else {
completed, failed := 0, 0
for _, e := range entries {
switch e.Status {
case "completed":
completed++
case "failed":
failed++
}
}
if failed > 0 && completed > 0 {
fmt.Fprintf(&sb, "[System Message] %d subagent task(s) completed, %d failed.\n", completed, failed)
} else if failed > 0 {
fmt.Fprintf(&sb, "[System Message] %d subagent task(s) failed.\n", failed)
} else {
fmt.Fprintf(&sb, "[System Message] %d subagent tasks completed.\n", completed)
}
for i, e := range entries {
fmt.Fprintf(&sb, "\n--- Task #%d: %q (%s, %s, tokens %d/%d) ---\nResult: %s\n",
i+1, e.Label, e.Status,
e.Runtime.Round(time.Millisecond),
e.InputTokens, e.OutputTokens, e.Content)
}
}
// Append roster for context.
sb.WriteString("\n")
sb.WriteString(tools.BuildReplyInstruction(roster))
// Smart prompting based on remaining tasks.
running := 0
for _, e := range roster.Entries {
if e.Status == tools.TaskStatusRunning {
running++
}
}
if running == 0 {
sb.WriteString("\n\nAll subagent tasks finished. Present a comprehensive summary of ALL results to the user.")
} else {
sb.WriteString("\n\nSome subagents are still running. Briefly acknowledge this result (1-2 sentences). A full summary will come when all tasks complete.")
}
return sb.String()
}