Files
Duc Nguyenandntduc bb7712a9ff 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>
2026-07-30 14:17:40 +07:00

104 lines
3.2 KiB
Go

package tools
import (
"fmt"
)
// DefaultSubagentConfig returns GoClaw's runtime defaults. Per-root admission is
// independent from the Standard/Lite process safety cap.
func DefaultSubagentConfig() SubagentConfig {
return SubagentConfig{
MaxConcurrent: 20,
MaxSpawnDepth: 1,
MaxChildrenPerAgent: 5,
ArchiveAfterMinutes: 60,
MaxRetries: 2,
}
}
// applyDenyList removes denied tools from the registry based on depth.
func (sm *SubagentManager) applyDenyList(reg *Registry, depth int, cfg SubagentConfig) {
// Always deny
for _, name := range SubagentDenyAlways {
reg.Unregister(name)
}
// Leaf deny (at max depth)
if depth >= cfg.MaxSpawnDepth {
for _, name := range SubagentDenyLeaf {
reg.Unregister(name)
}
}
}
// buildSubagentSystemPrompt constructs the system prompt for a subagent,
// matching the TS buildSubagentSystemPrompt pattern from subagent-announce.ts.
func (sm *SubagentManager) buildSubagentSystemPrompt(task *SubagentTask, cfg SubagentConfig, workspace string) string {
parentLabel := "main agent"
if task.Depth >= 2 {
parentLabel = "parent orchestrator"
}
canSpawn := task.Depth < cfg.MaxSpawnDepth
prompt := fmt.Sprintf(`# Subagent Context
You are a **subagent** spawned by the %s for a specific task.
## Your Role
- You were created to handle: %s
- Complete this task. That is your entire purpose.
- You are NOT the %s. Do not try to be.
## Rules
1. **Stay focused** — Do your assigned task, nothing else.
2. **Complete the task** — Your final message will be automatically reported to the %s.
3. **Never ask for clarification** — Work with what you have. If asked to create content, generate it yourself.
4. **Be ephemeral** — You may be terminated after task completion. That is fine.
## Output Format
Your final response IS the deliverable — it will be forwarded to the user.
- If asked to create content (posts, articles, messages, etc.), output the FULL content directly. Do NOT describe what you wrote — just write it.
- Do NOT say "I wrote a post about..." or "Here is what I created...". Output the content itself as your response.
- If the task is research or analysis, provide the complete findings.
- The %s will receive your exact final response, so make it user-ready.
## What You Do NOT Do
- NO user conversations (that is the %s's job)
- NO external messages unless explicitly tasked
- NO cron jobs or persistent state
- NO pretending to be the %s`,
parentLabel, task.Task,
parentLabel, parentLabel, parentLabel, parentLabel, parentLabel)
if canSpawn {
prompt += `
## Sub-Agent Spawning
You CAN spawn your own sub-agents for parallel or complex work using the spawn tool.
Asynchronous descendants report to the root orchestrator with their direct-parent lineage.
Synchronous descendants return their result directly to you.`
} else if task.Depth >= 2 {
prompt += `
## Sub-Agent Spawning
You are a leaf worker and CANNOT spawn further sub-agents. Focus on your assigned task.`
}
prompt += fmt.Sprintf(`
## Session Context
- Label: %s
- Depth: %d / %d`, task.Label, task.Depth, cfg.MaxSpawnDepth)
if workspace != "" {
prompt += fmt.Sprintf(`
## Workspace
Your working directory is: %s
Use relative paths for file operations — do not guess absolute paths.`, workspace)
}
return prompt
}