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

+10 -4
View File
@@ -14,7 +14,6 @@ import (
"github.com/nextlevelbuilder/goclaw/internal/i18n"
"github.com/nextlevelbuilder/goclaw/internal/providers"
"github.com/nextlevelbuilder/goclaw/internal/store"
"github.com/nextlevelbuilder/goclaw/internal/tools"
)
// isUserFilePopulated checks if USER.md has been filled with actual user data
@@ -48,7 +47,7 @@ func (l *Loop) finalizeRun(
// BEFORE sanitize strips them. Covers cases where a tool returned its
// artifact via the ForLLM MEDIA: prefix but the agent relayed it as plain
// text (e.g. PDF from exec/weasyprint, TTS mp3 paths the LLM quotes back).
if extracted := extractMediaFromContent(rs.finalContent, tools.ToolWorkspaceFromCtx(ctx)); len(extracted) > 0 {
if extracted := extractMediaFromContent(rs.finalContent, l.mediaEgressRoots(ctx)); len(extracted) > 0 {
rs.mediaResults = append(rs.mediaResults, extracted...)
}
@@ -67,8 +66,12 @@ func (l *Loop) finalizeRun(
rs.finalContent += "\n\n---\n_" + i18n.T(locale, i18n.MsgSkillNudgePostscript) + "_"
}
// 7. Fallback for empty content
if rs.finalContent == "" {
// 7. Fallback only when there is no other deliverable output. Media-only
// runs must remain media-only instead of gaining a visible "..." caption.
hasDeliverableOutput := len(rs.mediaResults) > 0 ||
len(req.ForwardMedia) > 0 ||
req.ContentSuffix != ""
if rs.finalContent == "" && !hasDeliverableOutput {
if len(rs.asyncToolCalls) > 0 {
rs.finalContent = "..."
} else {
@@ -200,6 +203,9 @@ func (l *Loop) finalizeRun(
slog.Info("agent loop: NO_REPLY detected, suppressing delivery",
"agent", l.id, "session", req.SessionKey)
rs.finalContent = ""
if req.ContentSuffix != "" {
rs.finalContent = deduplicateMediaSuffix("", req.ContentSuffix)
}
}
// 9. Maybe summarize