Sync with sipeed/picoclaw upstream/main (~1095 commits since last merge), including major restructures: - Launcher migrated to web/backend (#1275): cmd/picoclaw-launcher/* removed. Our launcher is dropped (MagicForm doesn't use it; web/backend is the upstream replacement when needed). - Agent loop split (#2585,12d5421c): pkg/agent/loop.go deleted, replaced by pkg/agent/agent_*.go files. Multi-tenancy customizations to be forward-ported in follow-up commits on this branch. - Tool packages reorganized (4c133dc2): pkg/tools/{filesystem,web}.go moved to pkg/tools/{fs,integration}/. Customizations to be forward-ported. - Channels switched to map+Settings model: MagicFormConfig → MagicFormSettings registered in config_channel.go alongside other channels; channel constructor updated to (channelName, channelType, *Config, *MessageBus) factory pattern. - Config schema updates: removed deprecated AgentDefaults.Model field; removed Config.Bindings (legacy migration in upstream); SessionConfig now uses Dimensions instead of DMScope; ExecConfig adds AllowRemote. Conflicts resolved: - pkg/agent/loop.go + loop_test.go: accepted upstream deletion (split) - pkg/tools/{filesystem,web}.go: accepted upstream deletion (moved) - cmd/picoclaw-launcher/*: dropped (deps deleted upstream, unused by MagicForm) - cmd/picoclaw/internal/gateway/helpers.go: accepted upstream deletion - pkg/channels/manager.go: switched to upstream's map-driven channel init - pkg/config/config.go: merged AgentDefaults fields, kept WorkspaceRoot, removed deprecated Model - pkg/bus/types.go: merged OutboundMessage with upstream's new fields (Context, AgentID, SessionKey, Scope, ContextUsage) plus our magicform callback fields (Type, Metrics, Progress, Escalation) - pkg/tools/{cron,shell}.go: combined our customizations with upstream changes - pkg/skills/installer.go: dropped our LimitReader path (upstream now uses chunked DownloadToFile which is size-bounded) The magicform channel was rebuilt to match upstream's new API: - *config.MagicFormSettings (SecureString token) instead of MagicFormConfig - Send returns ([]string, error) per upstream Channel interface - InboundMessage built with Context (with Raw map for tenancy hints) instead of bus.Peer + Metadata field which were removed. Multi-tenancy hints (workspace_override, config_dir, allowed_tools, allowed_skills) are still flowing via Context.Raw — the agent loop side of the integration is forward-ported in a follow-up commit. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
181 lines
7.5 KiB
Go
181 lines
7.5 KiB
Go
package bus
|
|
|
|
// SenderInfo provides structured sender identity information.
|
|
type SenderInfo struct {
|
|
Platform string `json:"platform,omitempty"` // "telegram", "discord", "slack", ...
|
|
PlatformID string `json:"platform_id,omitempty"` // raw platform ID, e.g. "123456"
|
|
CanonicalID string `json:"canonical_id,omitempty"` // "platform:id" format
|
|
Username string `json:"username,omitempty"` // username (e.g. @alice)
|
|
DisplayName string `json:"display_name,omitempty"` // display name
|
|
}
|
|
|
|
// InboundContext captures the normalized, platform-agnostic facts about an
|
|
// inbound message. This is the source of truth for routing and session
|
|
// allocation.
|
|
type InboundContext struct {
|
|
Channel string `json:"channel"`
|
|
Account string `json:"account,omitempty"`
|
|
|
|
ChatID string `json:"chat_id"`
|
|
ChatType string `json:"chat_type,omitempty"` // direct / group / channel
|
|
TopicID string `json:"topic_id,omitempty"`
|
|
|
|
SpaceID string `json:"space_id,omitempty"`
|
|
SpaceType string `json:"space_type,omitempty"` // guild / team / workspace / tenant
|
|
|
|
SenderID string `json:"sender_id"`
|
|
MessageID string `json:"message_id,omitempty"`
|
|
|
|
Mentioned bool `json:"mentioned,omitempty"`
|
|
|
|
ReplyToMessageID string `json:"reply_to_message_id,omitempty"`
|
|
ReplyToSenderID string `json:"reply_to_sender_id,omitempty"`
|
|
|
|
ReplyHandles map[string]string `json:"reply_handles,omitempty"`
|
|
Raw map[string]string `json:"raw,omitempty"`
|
|
}
|
|
|
|
type InboundMessage struct {
|
|
Context InboundContext `json:"context"`
|
|
Sender SenderInfo `json:"sender"`
|
|
Content string `json:"content"`
|
|
Media []string `json:"media,omitempty"`
|
|
MediaScope string `json:"media_scope,omitempty"` // media lifecycle scope
|
|
SessionKey string `json:"session_key"`
|
|
|
|
// Convenience mirrors derived from Context for runtime consumers.
|
|
Channel string `json:"channel"`
|
|
SenderID string `json:"sender_id"`
|
|
ChatID string `json:"chat_id"`
|
|
MessageID string `json:"message_id,omitempty"` // platform message ID
|
|
}
|
|
|
|
// OutboundScope captures the structured session scope associated with an
|
|
// outbound turn result without depending on the session package.
|
|
type OutboundScope struct {
|
|
Version int `json:"version,omitempty"`
|
|
AgentID string `json:"agent_id,omitempty"`
|
|
Channel string `json:"channel,omitempty"`
|
|
Account string `json:"account,omitempty"`
|
|
Dimensions []string `json:"dimensions,omitempty"`
|
|
Values map[string]string `json:"values,omitempty"`
|
|
}
|
|
|
|
// ContextUsage describes how much of the model's context window the current
|
|
// session consumes, and how far it is from triggering compression.
|
|
type ContextUsage struct {
|
|
UsedTokens int `json:"used_tokens"`
|
|
TotalTokens int `json:"total_tokens"` // model context window
|
|
CompressAtTokens int `json:"compress_at_tokens"` // threshold that triggers compression
|
|
UsedPercent int `json:"used_percent"` // 0-100
|
|
}
|
|
|
|
// Outbound message type constants identify the kind of outbound message.
|
|
// Channels that don't support progress/escalation treat all messages as content.
|
|
const (
|
|
MessageTypeFinal = "" // default: final response
|
|
MessageTypeProgress = "progress" // intermediate progress update
|
|
MessageTypeEscalation = "escalation" // needs human intervention
|
|
)
|
|
|
|
type OutboundMessage struct {
|
|
Channel string `json:"channel"`
|
|
ChatID string `json:"chat_id"`
|
|
Context InboundContext `json:"context"`
|
|
AgentID string `json:"agent_id,omitempty"`
|
|
SessionKey string `json:"session_key,omitempty"`
|
|
Scope *OutboundScope `json:"scope,omitempty"`
|
|
Content string `json:"content"`
|
|
ReplyToMessageID string `json:"reply_to_message_id,omitempty"`
|
|
ContextUsage *ContextUsage `json:"context_usage,omitempty"`
|
|
|
|
// Type distinguishes final responses from progress/escalation updates.
|
|
// Empty string (default) means final response. Channels that don't support
|
|
// progress callbacks ignore this field and treat all messages as content.
|
|
Type string `json:"type,omitempty"`
|
|
|
|
// Metrics is populated by the agent loop for the final response message.
|
|
// Channels that support rich callbacks (e.g. MagicForm) use this to include
|
|
// execution metrics in their response payloads. Nil for intermediate messages.
|
|
Metrics *ResponseMetrics `json:"metrics,omitempty"`
|
|
|
|
// Progress is populated for Type="progress" messages.
|
|
Progress *OutboundProgress `json:"progress,omitempty"`
|
|
|
|
// Escalation is populated for Type="escalation" messages.
|
|
Escalation *OutboundEscalation `json:"escalation,omitempty"`
|
|
}
|
|
|
|
// OutboundProgress carries progress update details.
|
|
type OutboundProgress struct {
|
|
Status string `json:"status"`
|
|
ToolName string `json:"toolName,omitempty"`
|
|
StepNumber int `json:"stepNumber,omitempty"`
|
|
Message string `json:"message,omitempty"`
|
|
}
|
|
|
|
// OutboundEscalation carries escalation details.
|
|
type OutboundEscalation struct {
|
|
Reason string `json:"reason"`
|
|
Notes string `json:"notes,omitempty"`
|
|
}
|
|
|
|
// TokenUsage tracks LLM token consumption across all iterations in a turn.
|
|
type TokenUsage struct {
|
|
PromptTokens int `json:"promptTokens"`
|
|
CompletionTokens int `json:"completionTokens"`
|
|
TotalTokens int `json:"totalTokens"`
|
|
Model string `json:"model"`
|
|
Provider string `json:"provider,omitempty"`
|
|
}
|
|
|
|
// ResponseMetrics captures execution metrics for a single agent processing turn.
|
|
type ResponseMetrics struct {
|
|
DurationMs int64 `json:"durationMs"`
|
|
TokenUsage *TokenUsage `json:"tokenUsage,omitempty"`
|
|
ToolCalls int `json:"toolCalls"`
|
|
Iterations int `json:"iterations"`
|
|
Model string `json:"model,omitempty"`
|
|
}
|
|
|
|
// MediaPart describes a single media attachment to send.
|
|
type MediaPart struct {
|
|
Type string `json:"type"` // "image" | "audio" | "video" | "file"
|
|
Ref string `json:"ref"` // media store ref, e.g. "media://abc123"
|
|
Caption string `json:"caption,omitempty"` // optional caption text
|
|
Filename string `json:"filename,omitempty"` // original filename hint
|
|
ContentType string `json:"content_type,omitempty"` // MIME type hint
|
|
}
|
|
|
|
// OutboundMediaMessage carries media attachments from Agent to channels via the bus.
|
|
type OutboundMediaMessage struct {
|
|
Channel string `json:"channel"`
|
|
ChatID string `json:"chat_id"`
|
|
Context InboundContext `json:"context"`
|
|
AgentID string `json:"agent_id,omitempty"`
|
|
SessionKey string `json:"session_key,omitempty"`
|
|
Scope *OutboundScope `json:"scope,omitempty"`
|
|
Parts []MediaPart `json:"parts"`
|
|
}
|
|
|
|
// AudioChunk represents a chunk of streaming voice data.
|
|
type AudioChunk struct {
|
|
SessionID string `json:"session_id"`
|
|
SpeakerID string `json:"speaker_id"` // User ID or SSRC
|
|
ChatID string `json:"chat_id"` // Where to respond
|
|
Channel string `json:"channel"` // Source channel type (e.g. "discord")
|
|
Sequence uint64 `json:"sequence"`
|
|
Timestamp uint32 `json:"timestamp"`
|
|
SampleRate int `json:"sample_rate"`
|
|
Channels int `json:"channels"`
|
|
Format string `json:"format"` // "opus", "pcm", etc
|
|
Data []byte `json:"data"`
|
|
}
|
|
|
|
// VoiceControl represents state or commands for voice sessions.
|
|
type VoiceControl struct {
|
|
SessionID string `json:"session_id"`
|
|
ChatID string `json:"chat_id"`
|
|
Type string `json:"type"` // "state", "command"
|
|
Action string `json:"action"` // "idle", "listening", "start", "stop", "leave"
|
|
}
|