mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-15 00:17:27 +00:00
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:
1 parent
496b7ffce6
commit
bb7712a9ff
186 files changed
+18858
-1233
No files matched your search
@@ -36,6 +36,12 @@ func (l *Loop) processToolResult(
|
||||
hadBootstrap bool,
|
||||
) (toolMsg providers.Message, warningMsgs []providers.Message, action toolResultAction) {
|
||||
|
||||
// Agent Link outputs are not valid outbound media until the runtime has
|
||||
// validated and atomically published the delegation manifest. Apply this
|
||||
// before logs, events, loop detection, or message construction so no
|
||||
// pre-publication MEDIA marker escapes through observability surfaces.
|
||||
tools.ApplyDelegationArtifactResultPolicy(ctx, result)
|
||||
|
||||
// Record for loop detection.
|
||||
argsHash := rs.loopDetector.record(registryName, tc.Arguments)
|
||||
rs.loopDetector.recordResult(argsHash, result.ForLLM)
|
||||
@@ -85,6 +91,7 @@ func (l *Loop) processToolResult(
|
||||
|
||||
// Collect MEDIA: paths from tool results.
|
||||
// Prefer result.Media (explicit) over ForLLM MEDIA: prefix (legacy) to avoid duplicates.
|
||||
mediaRoots := l.mediaEgressRoots(ctx)
|
||||
if len(result.Media) > 0 {
|
||||
// Egress containment: a tool that sets result.Media[].Path to a path
|
||||
// outside every allowed scope (e.g. /etc/passwd from a prompt-injected
|
||||
@@ -94,10 +101,6 @@ func (l *Loop) processToolResult(
|
||||
// to — agent workspace, team workspace, and tenant-allowed paths — so a
|
||||
// cross-workspace file (e.g. a teammate-produced file in the shared team
|
||||
// workspace, or a synchronous delegatee's output) is not wrongly dropped.
|
||||
mediaRoots := append([]string{
|
||||
tools.ToolWorkspaceFromCtx(ctx),
|
||||
tools.ToolTeamWorkspaceFromCtx(ctx),
|
||||
}, l.tenantAllowedPaths...)
|
||||
for i, mf := range result.Media {
|
||||
cleaned, ok := confineToAnyRoot(mf.Path, mediaRoots)
|
||||
if !ok {
|
||||
@@ -123,7 +126,7 @@ func (l *Loop) processToolResult(
|
||||
// Telegram sendDocument). A malicious or buggy tool emitting
|
||||
// MEDIA:/etc/passwd is dropped here — fixing every channel at the source
|
||||
// rather than per-channel. Mirrors extractMediaFromContent containment.
|
||||
if cleaned, ok := confineToWorkspace(mr.Path, tools.ToolWorkspaceFromCtx(ctx)); ok {
|
||||
if cleaned, ok := confineToAnyRoot(mr.Path, mediaRoots); ok {
|
||||
mr.Path = cleaned
|
||||
rs.mediaResults = append(rs.mediaResults, *mr)
|
||||
} else {
|
||||
|
||||
Reference in new issue
Block a user