Add group message routing alongside existing C2C (direct) support using chatType sync.Map to track whether a chatID is group or direct. Implement passive reply with msg_id/msg_seq tracking for multi-part responses. Add StartTyping (InputNotify msg_type=6 with periodic resend), SendMedia (RichMediaMessage for HTTP/HTTPS URLs), and configurable Markdown message support. Replace unbounded dedup map with TTL-based expiry and janitor goroutine. Sanitize URLs in group messages by replacing dots in domains with fullwidth period to avoid QQ's URL blacklist rejection (error 40054010). Add rate limit config (5 msg/s) and MaxMessageLength/SendMarkdown config fields.
466 lines
11 KiB
Go
466 lines
11 KiB
Go
// PicoClaw - Ultra-lightweight personal AI agent
|
|
// License: MIT
|
|
//
|
|
// Copyright (c) 2026 PicoClaw contributors
|
|
|
|
package config
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
)
|
|
|
|
// DefaultConfig returns the default configuration for PicoClaw.
|
|
func DefaultConfig() *Config {
|
|
// Determine the base path for the workspace.
|
|
// Priority: $PICOCLAW_HOME > ~/.picoclaw
|
|
var homePath string
|
|
if picoclawHome := os.Getenv("PICOCLAW_HOME"); picoclawHome != "" {
|
|
homePath = picoclawHome
|
|
} else {
|
|
userHome, _ := os.UserHomeDir()
|
|
homePath = filepath.Join(userHome, ".picoclaw")
|
|
}
|
|
workspacePath := filepath.Join(homePath, "workspace")
|
|
|
|
return &Config{
|
|
Agents: AgentsConfig{
|
|
Defaults: AgentDefaults{
|
|
Workspace: workspacePath,
|
|
RestrictToWorkspace: true,
|
|
Provider: "",
|
|
Model: "",
|
|
MaxTokens: 32768,
|
|
Temperature: nil, // nil means use provider default
|
|
MaxToolIterations: 50,
|
|
SummarizeMessageThreshold: 20,
|
|
SummarizeTokenPercent: 75,
|
|
},
|
|
},
|
|
Bindings: []AgentBinding{},
|
|
Session: SessionConfig{
|
|
DMScope: "per-channel-peer",
|
|
},
|
|
Channels: ChannelsConfig{
|
|
WhatsApp: WhatsAppConfig{
|
|
Enabled: false,
|
|
BridgeURL: "ws://localhost:3001",
|
|
UseNative: false,
|
|
SessionStorePath: "",
|
|
AllowFrom: FlexibleStringSlice{},
|
|
},
|
|
Telegram: TelegramConfig{
|
|
Enabled: false,
|
|
Token: "",
|
|
AllowFrom: FlexibleStringSlice{},
|
|
Typing: TypingConfig{Enabled: true},
|
|
Placeholder: PlaceholderConfig{
|
|
Enabled: true,
|
|
Text: "Thinking... 💭",
|
|
},
|
|
},
|
|
Feishu: FeishuConfig{
|
|
Enabled: false,
|
|
AppID: "",
|
|
AppSecret: "",
|
|
EncryptKey: "",
|
|
VerificationToken: "",
|
|
AllowFrom: FlexibleStringSlice{},
|
|
},
|
|
Discord: DiscordConfig{
|
|
Enabled: false,
|
|
Token: "",
|
|
AllowFrom: FlexibleStringSlice{},
|
|
MentionOnly: false,
|
|
},
|
|
MaixCam: MaixCamConfig{
|
|
Enabled: false,
|
|
Host: "0.0.0.0",
|
|
Port: 18790,
|
|
AllowFrom: FlexibleStringSlice{},
|
|
},
|
|
QQ: QQConfig{
|
|
Enabled: false,
|
|
AppID: "",
|
|
AppSecret: "",
|
|
AllowFrom: FlexibleStringSlice{},
|
|
MaxMessageLength: 2000,
|
|
},
|
|
DingTalk: DingTalkConfig{
|
|
Enabled: false,
|
|
ClientID: "",
|
|
ClientSecret: "",
|
|
AllowFrom: FlexibleStringSlice{},
|
|
},
|
|
Slack: SlackConfig{
|
|
Enabled: false,
|
|
BotToken: "",
|
|
AppToken: "",
|
|
AllowFrom: FlexibleStringSlice{},
|
|
},
|
|
LINE: LINEConfig{
|
|
Enabled: false,
|
|
ChannelSecret: "",
|
|
ChannelAccessToken: "",
|
|
WebhookHost: "0.0.0.0",
|
|
WebhookPort: 18791,
|
|
WebhookPath: "/webhook/line",
|
|
AllowFrom: FlexibleStringSlice{},
|
|
GroupTrigger: GroupTriggerConfig{MentionOnly: true},
|
|
},
|
|
OneBot: OneBotConfig{
|
|
Enabled: false,
|
|
WSUrl: "ws://127.0.0.1:3001",
|
|
AccessToken: "",
|
|
ReconnectInterval: 5,
|
|
GroupTriggerPrefix: []string{},
|
|
AllowFrom: FlexibleStringSlice{},
|
|
},
|
|
WeCom: WeComConfig{
|
|
Enabled: false,
|
|
Token: "",
|
|
EncodingAESKey: "",
|
|
WebhookURL: "",
|
|
WebhookHost: "0.0.0.0",
|
|
WebhookPort: 18793,
|
|
WebhookPath: "/webhook/wecom",
|
|
AllowFrom: FlexibleStringSlice{},
|
|
ReplyTimeout: 5,
|
|
},
|
|
WeComApp: WeComAppConfig{
|
|
Enabled: false,
|
|
CorpID: "",
|
|
CorpSecret: "",
|
|
AgentID: 0,
|
|
Token: "",
|
|
EncodingAESKey: "",
|
|
WebhookHost: "0.0.0.0",
|
|
WebhookPort: 18792,
|
|
WebhookPath: "/webhook/wecom-app",
|
|
AllowFrom: FlexibleStringSlice{},
|
|
ReplyTimeout: 5,
|
|
},
|
|
WeComAIBot: WeComAIBotConfig{
|
|
Enabled: false,
|
|
Token: "",
|
|
EncodingAESKey: "",
|
|
WebhookPath: "/webhook/wecom-aibot",
|
|
AllowFrom: FlexibleStringSlice{},
|
|
ReplyTimeout: 5,
|
|
MaxSteps: 10,
|
|
WelcomeMessage: "Hello! I'm your AI assistant. How can I help you today?",
|
|
},
|
|
Pico: PicoConfig{
|
|
Enabled: false,
|
|
Token: "",
|
|
PingInterval: 30,
|
|
ReadTimeout: 60,
|
|
WriteTimeout: 10,
|
|
MaxConnections: 100,
|
|
AllowFrom: FlexibleStringSlice{},
|
|
},
|
|
},
|
|
Providers: ProvidersConfig{
|
|
OpenAI: OpenAIProviderConfig{WebSearch: true},
|
|
},
|
|
ModelList: []ModelConfig{
|
|
// ============================================
|
|
// Add your API key to the model you want to use
|
|
// ============================================
|
|
|
|
// Zhipu AI (智谱) - https://open.bigmodel.cn/usercenter/apikeys
|
|
{
|
|
ModelName: "glm-4.7",
|
|
Model: "zhipu/glm-4.7",
|
|
APIBase: "https://open.bigmodel.cn/api/paas/v4",
|
|
APIKey: "",
|
|
},
|
|
|
|
// OpenAI - https://platform.openai.com/api-keys
|
|
{
|
|
ModelName: "gpt-5.2",
|
|
Model: "openai/gpt-5.2",
|
|
APIBase: "https://api.openai.com/v1",
|
|
APIKey: "",
|
|
},
|
|
|
|
// Anthropic Claude - https://console.anthropic.com/settings/keys
|
|
{
|
|
ModelName: "claude-sonnet-4.6",
|
|
Model: "anthropic/claude-sonnet-4.6",
|
|
APIBase: "https://api.anthropic.com/v1",
|
|
APIKey: "",
|
|
},
|
|
|
|
// DeepSeek - https://platform.deepseek.com/
|
|
{
|
|
ModelName: "deepseek-chat",
|
|
Model: "deepseek/deepseek-chat",
|
|
APIBase: "https://api.deepseek.com/v1",
|
|
APIKey: "",
|
|
},
|
|
|
|
// Google Gemini - https://ai.google.dev/
|
|
{
|
|
ModelName: "gemini-2.0-flash",
|
|
Model: "gemini/gemini-2.0-flash-exp",
|
|
APIBase: "https://generativelanguage.googleapis.com/v1beta",
|
|
APIKey: "",
|
|
},
|
|
|
|
// Qwen (通义千问) - https://dashscope.console.aliyun.com/apiKey
|
|
{
|
|
ModelName: "qwen-plus",
|
|
Model: "qwen/qwen-plus",
|
|
APIBase: "https://dashscope.aliyuncs.com/compatible-mode/v1",
|
|
APIKey: "",
|
|
},
|
|
|
|
// Moonshot (月之暗面) - https://platform.moonshot.cn/console/api-keys
|
|
{
|
|
ModelName: "moonshot-v1-8k",
|
|
Model: "moonshot/moonshot-v1-8k",
|
|
APIBase: "https://api.moonshot.cn/v1",
|
|
APIKey: "",
|
|
},
|
|
|
|
// Groq - https://console.groq.com/keys
|
|
{
|
|
ModelName: "llama-3.3-70b",
|
|
Model: "groq/llama-3.3-70b-versatile",
|
|
APIBase: "https://api.groq.com/openai/v1",
|
|
APIKey: "",
|
|
},
|
|
|
|
// OpenRouter (100+ models) - https://openrouter.ai/keys
|
|
{
|
|
ModelName: "openrouter-auto",
|
|
Model: "openrouter/auto",
|
|
APIBase: "https://openrouter.ai/api/v1",
|
|
APIKey: "",
|
|
},
|
|
{
|
|
ModelName: "openrouter-gpt-5.2",
|
|
Model: "openrouter/openai/gpt-5.2",
|
|
APIBase: "https://openrouter.ai/api/v1",
|
|
APIKey: "",
|
|
},
|
|
|
|
// NVIDIA - https://build.nvidia.com/
|
|
{
|
|
ModelName: "nemotron-4-340b",
|
|
Model: "nvidia/nemotron-4-340b-instruct",
|
|
APIBase: "https://integrate.api.nvidia.com/v1",
|
|
APIKey: "",
|
|
},
|
|
|
|
// Cerebras - https://inference.cerebras.ai/
|
|
{
|
|
ModelName: "cerebras-llama-3.3-70b",
|
|
Model: "cerebras/llama-3.3-70b",
|
|
APIBase: "https://api.cerebras.ai/v1",
|
|
APIKey: "",
|
|
},
|
|
|
|
// Volcengine (火山引擎) - https://console.volcengine.com/ark
|
|
{
|
|
ModelName: "doubao-pro",
|
|
Model: "volcengine/doubao-pro-32k",
|
|
APIBase: "https://ark.cn-beijing.volces.com/api/v3",
|
|
APIKey: "",
|
|
},
|
|
|
|
// ShengsuanYun (神算云)
|
|
{
|
|
ModelName: "deepseek-v3",
|
|
Model: "shengsuanyun/deepseek-v3",
|
|
APIBase: "https://api.shengsuanyun.com/v1",
|
|
APIKey: "",
|
|
},
|
|
|
|
// Antigravity (Google Cloud Code Assist) - OAuth only
|
|
{
|
|
ModelName: "gemini-flash",
|
|
Model: "antigravity/gemini-3-flash",
|
|
AuthMethod: "oauth",
|
|
},
|
|
|
|
// GitHub Copilot - https://github.com/settings/tokens
|
|
{
|
|
ModelName: "copilot-gpt-5.2",
|
|
Model: "github-copilot/gpt-5.2",
|
|
APIBase: "http://localhost:4321",
|
|
AuthMethod: "oauth",
|
|
},
|
|
|
|
// Ollama (local) - https://ollama.com
|
|
{
|
|
ModelName: "llama3",
|
|
Model: "ollama/llama3",
|
|
APIBase: "http://localhost:11434/v1",
|
|
APIKey: "ollama",
|
|
},
|
|
|
|
// Mistral AI - https://console.mistral.ai/api-keys
|
|
{
|
|
ModelName: "mistral-small",
|
|
Model: "mistral/mistral-small-latest",
|
|
APIBase: "https://api.mistral.ai/v1",
|
|
APIKey: "",
|
|
},
|
|
|
|
// Avian - https://avian.io
|
|
{
|
|
ModelName: "deepseek-v3.2",
|
|
Model: "avian/deepseek/deepseek-v3.2",
|
|
APIBase: "https://api.avian.io/v1",
|
|
APIKey: "",
|
|
},
|
|
{
|
|
ModelName: "kimi-k2.5",
|
|
Model: "avian/moonshotai/kimi-k2.5",
|
|
APIBase: "https://api.avian.io/v1",
|
|
APIKey: "",
|
|
},
|
|
|
|
// VLLM (local) - http://localhost:8000
|
|
{
|
|
ModelName: "local-model",
|
|
Model: "vllm/custom-model",
|
|
APIBase: "http://localhost:8000/v1",
|
|
APIKey: "",
|
|
},
|
|
},
|
|
Gateway: GatewayConfig{
|
|
Host: "127.0.0.1",
|
|
Port: 18790,
|
|
},
|
|
Tools: ToolsConfig{
|
|
MediaCleanup: MediaCleanupConfig{
|
|
ToolConfig: ToolConfig{
|
|
Enabled: true,
|
|
},
|
|
MaxAge: 30,
|
|
Interval: 5,
|
|
},
|
|
Web: WebToolsConfig{
|
|
ToolConfig: ToolConfig{
|
|
Enabled: true,
|
|
},
|
|
Proxy: "",
|
|
FetchLimitBytes: 10 * 1024 * 1024, // 10MB by default
|
|
Brave: BraveConfig{
|
|
Enabled: false,
|
|
APIKey: "",
|
|
MaxResults: 5,
|
|
},
|
|
DuckDuckGo: DuckDuckGoConfig{
|
|
Enabled: true,
|
|
MaxResults: 5,
|
|
},
|
|
Perplexity: PerplexityConfig{
|
|
Enabled: false,
|
|
APIKey: "",
|
|
MaxResults: 5,
|
|
},
|
|
SearXNG: SearXNGConfig{
|
|
Enabled: false,
|
|
BaseURL: "",
|
|
MaxResults: 5,
|
|
},
|
|
GLMSearch: GLMSearchConfig{
|
|
Enabled: false,
|
|
APIKey: "",
|
|
BaseURL: "https://open.bigmodel.cn/api/paas/v4/web_search",
|
|
SearchEngine: "search_std",
|
|
MaxResults: 5,
|
|
},
|
|
},
|
|
Cron: CronToolsConfig{
|
|
ToolConfig: ToolConfig{
|
|
Enabled: true,
|
|
},
|
|
ExecTimeoutMinutes: 5,
|
|
},
|
|
Exec: ExecConfig{
|
|
ToolConfig: ToolConfig{
|
|
Enabled: true,
|
|
},
|
|
EnableDenyPatterns: true,
|
|
TimeoutSeconds: 60,
|
|
},
|
|
Skills: SkillsToolsConfig{
|
|
ToolConfig: ToolConfig{
|
|
Enabled: true,
|
|
},
|
|
Registries: SkillsRegistriesConfig{
|
|
ClawHub: ClawHubRegistryConfig{
|
|
Enabled: true,
|
|
BaseURL: "https://clawhub.ai",
|
|
},
|
|
},
|
|
MaxConcurrentSearches: 2,
|
|
SearchCache: SearchCacheConfig{
|
|
MaxSize: 50,
|
|
TTLSeconds: 300,
|
|
},
|
|
},
|
|
SendFile: ToolConfig{
|
|
Enabled: true,
|
|
},
|
|
MCP: MCPConfig{
|
|
ToolConfig: ToolConfig{
|
|
Enabled: false,
|
|
},
|
|
Servers: map[string]MCPServerConfig{},
|
|
},
|
|
AppendFile: ToolConfig{
|
|
Enabled: true,
|
|
},
|
|
EditFile: ToolConfig{
|
|
Enabled: true,
|
|
},
|
|
FindSkills: ToolConfig{
|
|
Enabled: true,
|
|
},
|
|
I2C: ToolConfig{
|
|
Enabled: false, // Hardware tool - Linux only
|
|
},
|
|
InstallSkill: ToolConfig{
|
|
Enabled: true,
|
|
},
|
|
ListDir: ToolConfig{
|
|
Enabled: true,
|
|
},
|
|
Message: ToolConfig{
|
|
Enabled: true,
|
|
},
|
|
ReadFile: ToolConfig{
|
|
Enabled: true,
|
|
},
|
|
Spawn: ToolConfig{
|
|
Enabled: true,
|
|
},
|
|
SPI: ToolConfig{
|
|
Enabled: false, // Hardware tool - Linux only
|
|
},
|
|
Subagent: ToolConfig{
|
|
Enabled: true,
|
|
},
|
|
WebFetch: ToolConfig{
|
|
Enabled: true,
|
|
},
|
|
WriteFile: ToolConfig{
|
|
Enabled: true,
|
|
},
|
|
},
|
|
Heartbeat: HeartbeatConfig{
|
|
Enabled: true,
|
|
Interval: 30,
|
|
},
|
|
Devices: DevicesConfig{
|
|
Enabled: false,
|
|
MonitorUSB: true,
|
|
},
|
|
}
|
|
}
|