mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-17 06:14:48 +00:00
fix(delegation): add pending tasks hint when team_task_id not found
LLM models often hallucinate UUIDs when delegating, passing a wrong
team_task_id that doesn't exist. Previously the error was bare
("task not found") with no guidance, causing the model to get stuck.
Now the error includes a list of pending tasks so the model can
self-correct. Also refactored prepareDelegation to resolve team once
instead of 3 separate GetTeamForAgent calls, and extracted
pendingTasksHint() to deduplicate hint-building logic.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
37158af231
commit
dbd317ac81
4 files changed
+126
-31
No files matched your search
@@ -107,6 +107,9 @@ type Loop struct {
|
||||
|
||||
// Group writer cache for system prompt injection (managed mode)
|
||||
groupWriterCache *store.GroupWriterCache
|
||||
|
||||
// Team store for cross-session pending task detection (managed mode)
|
||||
teamStore store.TeamStore
|
||||
}
|
||||
|
||||
// AgentEvent is emitted during agent execution for WS broadcasting.
|
||||
@@ -175,6 +178,9 @@ type LoopConfig struct {
|
||||
|
||||
// Group writer cache for system prompt injection (managed mode)
|
||||
GroupWriterCache *store.GroupWriterCache
|
||||
|
||||
// Team store for cross-session pending task detection (managed mode)
|
||||
TeamStore store.TeamStore
|
||||
}
|
||||
|
||||
func NewLoop(cfg LoopConfig) *Loop {
|
||||
@@ -235,6 +241,7 @@ func NewLoop(cfg LoopConfig) *Loop {
|
||||
builtinToolSettings: cfg.BuiltinToolSettings,
|
||||
thinkingLevel: cfg.ThinkingLevel,
|
||||
groupWriterCache: cfg.GroupWriterCache,
|
||||
teamStore: cfg.TeamStore,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -530,6 +537,33 @@ func (l *Loop) runLoop(ctx context.Context, req RunRequest) (*RunResult, error)
|
||||
}
|
||||
}
|
||||
|
||||
// 2b. Cross-session recovery: notify team leads about orphaned pending tasks.
|
||||
// Safe because Bước 1 (early ClaimTask) ensures running tasks are in_progress,
|
||||
// so only truly un-spawned tasks remain pending.
|
||||
if l.teamStore != nil && l.agentUUID != uuid.Nil {
|
||||
if team, _ := l.teamStore.GetTeamForAgent(ctx, l.agentUUID); team != nil && team.LeadAgentID == l.agentUUID {
|
||||
if tasks, err := l.teamStore.ListTasks(ctx, team.ID, "newest", "", ""); err == nil {
|
||||
var stale []string
|
||||
for _, t := range tasks {
|
||||
if t.Status == store.TeamTaskStatusPending {
|
||||
age := time.Since(t.CreatedAt).Truncate(time.Minute)
|
||||
stale = append(stale, fmt.Sprintf("- %s: \"%s\" (pending %s)", t.ID, t.Subject, age))
|
||||
}
|
||||
}
|
||||
if len(stale) > 0 {
|
||||
reminder := fmt.Sprintf(
|
||||
"[System] You have %d pending team task(s) that were never spawned:\n%s\n"+
|
||||
"Spawn each one, or cancel with team_tasks action=cancel if no longer needed.",
|
||||
len(stale), strings.Join(stale, "\n"))
|
||||
messages = append(messages,
|
||||
providers.Message{Role: "user", Content: reminder},
|
||||
providers.Message{Role: "assistant", Content: "I see the pending tasks. Let me handle them."},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Buffer new messages — write to session only AFTER the run completes.
|
||||
// This prevents concurrent runs from seeing each other's in-progress messages.
|
||||
// NOTE: pendingMsgs stores TEXT ONLY (no images) to avoid bloating session storage.
|
||||
|
||||
Reference in new issue
Block a user