Files
goclaw/internal/tools/delegate_sync.go
T
viettranx 0f2737ce53 feat(media): persistent media storage, read_document tool, and pipeline refactor
- Add persistent media storage (internal/media/) replacing temp file deletion
- Add MediaRef type for lightweight media references in session messages
- Refactor media pipeline to use bus.MediaFile{Path, MimeType} across all channels
- Add read_document builtin tool for PDF/DOCX/XLSX analysis via Gemini native API
- Move image sanitization from Telegram to shared agent/media layer
- Add media reload for multi-turn conversations (images from last 5 messages)
- Add reply-to-message media resolution for Telegram (re-download on reply)
- Add media inventory to compaction summary to preserve awareness after truncation
- Fix coreToolSummaries for read_image, read_document, create_image tools
- Add real-time trace update events via WebSocket broadcast
- Improve trace detail UI with media refs and tool result display
2026-03-08 14:00:34 +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(), Media: result.Media}, nil
}