mirror of
https://github.com/sipeed/picoclaw.git
synced 2026-06-12 18:08:54 +00:00
272536a11a
Implement per-agent workspace/model/session isolation with 7-level priority routing cascade (peer > parent_peer > guild > team > account > channel > default). Backward compatible - empty agents.list creates implicit "main" agent from defaults. Core components: - routing/agent_id.go: ID normalization with pre-compiled regex - routing/session_key.go: 4 DM scope modes with identity links - routing/route.go: RouteResolver with priority-based binding matcher - agent/instance.go: Per-agent state (workspace, sessions, tools, model) - agent/registry.go: Agent lifecycle, route resolution, subagent ACL Integration: - config.go: AgentModelConfig (flexible JSON), bindings, session config - loop.go: Complete rewrite for multi-agent dispatch - Channel adapters: peer_kind/peer_id metadata (telegram, discord, slack) - spawn.go: Subagent allowlist enforcement per agent Validated end-to-end with Discord channel-based bindings, default fallback routing, and per-agent session persistence.
88 lines
2.2 KiB
Go
88 lines
2.2 KiB
Go
package tools
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
)
|
|
|
|
type SpawnTool struct {
|
|
manager *SubagentManager
|
|
originChannel string
|
|
originChatID string
|
|
allowlistCheck func(targetAgentID string) bool
|
|
}
|
|
|
|
func NewSpawnTool(manager *SubagentManager) *SpawnTool {
|
|
return &SpawnTool{
|
|
manager: manager,
|
|
originChannel: "cli",
|
|
originChatID: "direct",
|
|
}
|
|
}
|
|
|
|
func (t *SpawnTool) Name() string {
|
|
return "spawn"
|
|
}
|
|
|
|
func (t *SpawnTool) Description() string {
|
|
return "Spawn a subagent to handle a task in the background. Use this for complex or time-consuming tasks that can run independently. The subagent will complete the task and report back when done."
|
|
}
|
|
|
|
func (t *SpawnTool) Parameters() map[string]interface{} {
|
|
return map[string]interface{}{
|
|
"type": "object",
|
|
"properties": map[string]interface{}{
|
|
"task": map[string]interface{}{
|
|
"type": "string",
|
|
"description": "The task for subagent to complete",
|
|
},
|
|
"label": map[string]interface{}{
|
|
"type": "string",
|
|
"description": "Optional short label for the task (for display)",
|
|
},
|
|
"agent_id": map[string]interface{}{
|
|
"type": "string",
|
|
"description": "Optional target agent ID to delegate the task to",
|
|
},
|
|
},
|
|
"required": []string{"task"},
|
|
}
|
|
}
|
|
|
|
func (t *SpawnTool) SetContext(channel, chatID string) {
|
|
t.originChannel = channel
|
|
t.originChatID = chatID
|
|
}
|
|
|
|
func (t *SpawnTool) SetAllowlistChecker(check func(targetAgentID string) bool) {
|
|
t.allowlistCheck = check
|
|
}
|
|
|
|
func (t *SpawnTool) Execute(ctx context.Context, args map[string]interface{}) (string, error) {
|
|
task, ok := args["task"].(string)
|
|
if !ok {
|
|
return "", fmt.Errorf("task is required")
|
|
}
|
|
|
|
label, _ := args["label"].(string)
|
|
agentID, _ := args["agent_id"].(string)
|
|
|
|
// Check allowlist if targeting a specific agent
|
|
if agentID != "" && t.allowlistCheck != nil {
|
|
if !t.allowlistCheck(agentID) {
|
|
return fmt.Sprintf("Error: not allowed to spawn agent '%s'", agentID), nil
|
|
}
|
|
}
|
|
|
|
if t.manager == nil {
|
|
return "Error: Subagent manager not configured", nil
|
|
}
|
|
|
|
result, err := t.manager.Spawn(ctx, task, label, agentID, t.originChannel, t.originChatID)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to spawn subagent: %w", err)
|
|
}
|
|
|
|
return result, nil
|
|
}
|