Files
goclaw/internal/tools/delegate_sync.go
T
viettranxandClaude Opus 4.6 61da687ad1 feat(ws): comprehensive team & delegation WS events with typed payloads
Add 16 new event constants and typed payload structs for full WS
visibility into team agent operations. Enrich AgentEvent with delegation
and routing context (delegationId, teamId, parentAgentId, userId,
channel, chatId). Emit thinking/chunk events for non-streaming runs
so delegate member agents also produce WS events.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 12:56:15 +07:00

71 lines
2.6 KiB
Go

package tools
import (
"context"
"fmt"
"log/slog"
"time"
"github.com/google/uuid"
"github.com/nextlevelbuilder/goclaw/internal/store"
"github.com/nextlevelbuilder/goclaw/internal/tracing"
"github.com/nextlevelbuilder/goclaw/pkg/protocol"
)
// Delegate executes a synchronous delegation to another agent.
func (dm *DelegateManager) Delegate(ctx context.Context, opts DelegateOpts) (*DelegateResult, error) {
task, _, err := dm.prepareDelegation(ctx, opts, "sync")
if err != nil {
return nil, err
}
dm.active.Store(task.ID, task)
defer func() {
now := time.Now()
task.CompletedAt = &now
dm.active.Delete(task.ID)
}()
dm.injectDependencyResults(ctx, &opts)
message := buildDelegateMessage(opts)
dm.emitDelegationEvent(protocol.EventDelegationStarted, task)
slog.Info("delegation started", "id", task.ID, "target", opts.TargetAgentKey, "mode", "sync")
// Propagate parent trace ID so the delegate trace links back.
// Clear senderID — delegations are system-initiated, the delegate agent
// should not inherit the caller's group writer permissions (the delegate
// agent has its own writer list, and would incorrectly deny writes).
delegateCtx := store.WithSenderID(ctx, "")
if parentTraceID := tracing.TraceIDFromContext(ctx); parentTraceID != uuid.Nil {
delegateCtx = tracing.WithDelegateParentTraceID(delegateCtx, parentTraceID)
}
startTime := time.Now()
result, err := dm.runAgent(delegateCtx, opts.TargetAgentKey, dm.buildRunRequest(task, message))
duration := time.Since(startTime)
if err != nil {
task.Status = "failed"
dm.emitDelegationEventWithError(task, err)
dm.saveDelegationHistory(task, "", err, duration)
return nil, fmt.Errorf("delegation to %q failed: %w", opts.TargetAgentKey, err)
}
// Apply quality gates before marking completed.
if result, err = dm.applyQualityGates(delegateCtx, task, opts, result); err != nil {
task.Status = "failed"
dm.emitDelegationEventWithError(task, err)
dm.saveDelegationHistory(task, "", err, duration)
return nil, fmt.Errorf("delegation to %q failed quality gate: %w", opts.TargetAgentKey, err)
}
task.Status = "completed"
dm.emitDelegationEvent(protocol.EventDelegationCompleted, task)
dm.trackCompleted(task)
dm.autoCompleteTeamTask(task, result.Content, result.Deliverables)
dm.saveDelegationHistory(task, result.Content, nil, duration)
slog.Info("delegation completed", "id", task.ID, "target", opts.TargetAgentKey, "iterations", result.Iterations)
return &DelegateResult{Content: result.Content, Iterations: result.Iterations, DelegationID: task.ID, TeamTaskID: task.TeamTaskID.String(), MediaPaths: result.MediaPaths}, nil
}