package tools import ( "context" "fmt" "strings" "github.com/nextlevelbuilder/goclaw/internal/bus" "github.com/nextlevelbuilder/goclaw/internal/store" ) // ============================================================ // sessions_send // ============================================================ type SessionsSendTool struct { sessions store.SessionStore msgBus *bus.MessageBus } func NewSessionsSendTool() *SessionsSendTool { return &SessionsSendTool{} } func (t *SessionsSendTool) SetSessionStore(s store.SessionStore) { t.sessions = s } func (t *SessionsSendTool) SetMessageBus(b *bus.MessageBus) { t.msgBus = b } func (t *SessionsSendTool) Name() string { return "sessions_send" } func (t *SessionsSendTool) Description() string { return "Send a message into another session. Use session_key or label to identify the target." } func (t *SessionsSendTool) Parameters() map[string]any { return map[string]any{ "type": "object", "properties": map[string]any{ "session_key": map[string]any{ "type": "string", "description": "Target session key", }, "label": map[string]any{ "type": "string", "description": "Target session label (alternative to session_key)", }, "message": map[string]any{ "type": "string", "description": "Message to send", }, }, "required": []string{"message"}, } } func (t *SessionsSendTool) Execute(ctx context.Context, args map[string]any) *Result { if t.sessions == nil { return ErrorResult("session store not available") } if t.msgBus == nil { return ErrorResult("message bus not available") } sessionKey, _ := args["session_key"].(string) label, _ := args["label"].(string) message, _ := args["message"].(string) if message == "" { return ErrorResult("message is required") } if sessionKey == "" && label == "" { return ErrorResult("either session_key or label is required") } agentID := resolveAgentIDString(ctx) // Resolve by label if needed if sessionKey == "" && label != "" { sessions := t.sessions.List(agentID) for _, s := range sessions { // Check if label matches by loading session data data := t.sessions.GetOrCreate(s.Key) if data.Label == label { sessionKey = s.Key break } } if sessionKey == "" { return ErrorResult(fmt.Sprintf("no session found with label: %s", label)) } } // Security: validate target session belongs to same agent if agentID != "" && !strings.HasPrefix(sessionKey, "agent:"+agentID+":") { return ErrorResult("access denied: target session belongs to a different agent") } // Publish as an inbound message (same mechanism as channels) t.msgBus.PublishInbound(bus.InboundMessage{ Channel: "system", SenderID: "session_send_tool", ChatID: sessionKey, Content: message, PeerKind: "direct", }) return SilentResult(fmt.Sprintf(`{"status":"accepted","session_key":"%s"}`, sessionKey)) } // ============================================================ // helpers // ============================================================ func resolveAgentIDString(ctx context.Context) string { id := store.AgentIDFromContext(ctx) if id.String() == "00000000-0000-0000-0000-000000000000" { return "" // no agent ID in context } return id.String() }