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.
89 lines
1.8 KiB
Go
89 lines
1.8 KiB
Go
package channels
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
|
|
"github.com/sipeed/picoclaw/pkg/bus"
|
|
)
|
|
|
|
type Channel interface {
|
|
Name() string
|
|
Start(ctx context.Context) error
|
|
Stop(ctx context.Context) error
|
|
Send(ctx context.Context, msg bus.OutboundMessage) error
|
|
IsRunning() bool
|
|
IsAllowed(senderID string) bool
|
|
}
|
|
|
|
type BaseChannel struct {
|
|
config interface{}
|
|
bus *bus.MessageBus
|
|
running bool
|
|
name string
|
|
allowList []string
|
|
}
|
|
|
|
func NewBaseChannel(name string, config interface{}, bus *bus.MessageBus, allowList []string) *BaseChannel {
|
|
return &BaseChannel{
|
|
config: config,
|
|
bus: bus,
|
|
name: name,
|
|
allowList: allowList,
|
|
running: false,
|
|
}
|
|
}
|
|
|
|
func (c *BaseChannel) Name() string {
|
|
return c.name
|
|
}
|
|
|
|
func (c *BaseChannel) IsRunning() bool {
|
|
return c.running
|
|
}
|
|
|
|
func (c *BaseChannel) IsAllowed(senderID string) bool {
|
|
if len(c.allowList) == 0 {
|
|
return true
|
|
}
|
|
|
|
// Extract parts from compound senderID like "123456|username"
|
|
idPart := senderID
|
|
userPart := ""
|
|
if idx := strings.Index(senderID, "|"); idx > 0 {
|
|
idPart = senderID[:idx]
|
|
userPart = senderID[idx+1:]
|
|
}
|
|
|
|
for _, allowed := range c.allowList {
|
|
// Strip leading "@" from allowed value for username matching
|
|
trimmed := strings.TrimPrefix(allowed, "@")
|
|
if senderID == allowed || idPart == allowed || senderID == trimmed || idPart == trimmed || (userPart != "" && (userPart == allowed || userPart == trimmed)) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func (c *BaseChannel) HandleMessage(senderID, chatID, content string, media []string, metadata map[string]string) {
|
|
if !c.IsAllowed(senderID) {
|
|
return
|
|
}
|
|
|
|
msg := bus.InboundMessage{
|
|
Channel: c.name,
|
|
SenderID: senderID,
|
|
ChatID: chatID,
|
|
Content: content,
|
|
Media: media,
|
|
Metadata: metadata,
|
|
}
|
|
|
|
c.bus.PublishInbound(msg)
|
|
}
|
|
|
|
func (c *BaseChannel) setRunning(running bool) {
|
|
c.running = running
|
|
}
|