fix(collaboration): harden delegated task isolation (#1486)

* feat(collaboration): isolate delegated artifacts and child runs

Isolate delegated inputs and outputs behind secure artifact exchange lifecycles. Scope Agent Link tasks by tenant and root agent, and enforce delegation spawn-tree boundaries. Add process-wide child-run admission and preserve logical media paths across native, MCP, and sandbox execution.

* fix(collaboration): harden delegated task isolation

Enforce tenant and root-agent task scope across migrations and stores. Add exactly-once async completion delivery, delegated sandbox boundaries, and confined artifact and media recovery across runtime surfaces.

* fix(collaboration): recover interrupted async tasks

* fix(collaboration): normalize persisted child-run status

---------

Co-authored-by: ntduc <ntduc@cpp.ai.vn>
This commit is contained in:
Duc Nguyenandntduc authored and GitHub committed 2026-07-30 14:17:40 +07:00
1 parent 496b7ffce6
commit bb7712a9ff
186 files changed
+18858 -1233

No files matched your search

+7 -5
View File
@@ -18,6 +18,7 @@ import (
func (l *Loop) Run(ctx context.Context, req RunRequest) (*RunResult, error) {
l.activeRuns.Add(1)
defer l.activeRuns.Add(-1)
ctx = withDelegationArtifactTextRedactor(ctx, &req)
// Per-run emit wrapper: enriches every AgentEvent with delegation + routing context.
emitRun := func(event AgentEvent) {
@@ -32,7 +33,7 @@ func (l *Loop) Run(ctx context.Context, req RunRequest) (*RunResult, error) {
event.ChatID = req.ChatID
event.SessionKey = req.SessionKey
event.TenantID = store.TenantIDFromContext(ctx)
l.emit(event)
l.emit(redactDelegationAgentEvent(&req, event))
}
emitRun(AgentEvent{
@@ -75,7 +76,7 @@ func (l *Loop) Run(ctx context.Context, req RunRequest) (*RunResult, error) {
UserID: req.UserID,
Channel: req.Channel,
Name: traceName,
InputPreview: truncateStr(req.Message, l.traceCollector.PreviewMaxLen()),
InputPreview: tracing.RedactText(ctx, truncateStr(req.Message, l.traceCollector.PreviewMaxLen())),
Status: store.TraceStatusRunning,
StartTime: now,
CreatedAt: now,
@@ -140,7 +141,7 @@ func (l *Loop) Run(ctx context.Context, req RunRequest) (*RunResult, error) {
l.emitAgentSpanEnd(safeCtx, agentSpanID, runStart, nil, context.Canceled)
}
l.traceCollector.FinishTrace(safeCtx, traceID, store.TraceStatusError,
"trace finalized by safety net (likely panic or goroutine leak)", "")
tracing.RedactText(safeCtx, "trace finalized by safety net (likely panic or goroutine leak)"), "")
}()
}
@@ -195,7 +196,7 @@ func (l *Loop) Run(ctx context.Context, req RunRequest) (*RunResult, error) {
traceCtx = context.WithoutCancel(ctx)
traceStatus = store.TraceStatusCancelled
}
l.traceCollector.FinishTrace(traceCtx, traceID, traceStatus, err.Error(), "")
l.traceCollector.FinishTrace(traceCtx, traceID, traceStatus, tracing.RedactText(traceCtx, err.Error()), "")
}
return nil, err
}
@@ -243,7 +244,8 @@ func (l *Loop) Run(ctx context.Context, req RunRequest) (*RunResult, error) {
if !isChildTrace && l.traceCollector != nil && traceID != uuid.Nil {
traceFinalized = true
if result != nil {
l.traceCollector.FinishTrace(ctx, traceID, store.TraceStatusCompleted, "", truncateStr(result.Content, l.traceCollector.PreviewMaxLen()))
l.traceCollector.FinishTrace(ctx, traceID, store.TraceStatusCompleted, "",
tracing.RedactText(ctx, truncateStr(result.Content, l.traceCollector.PreviewMaxLen())))
} else {
l.traceCollector.FinishTrace(ctx, traceID, store.TraceStatusCompleted, "", "")
}