mirror of
https://github.com/sipeed/picoclaw.git
synced 2026-06-12 18:08:54 +00:00
71134babb9
* feat(telegram): stream LLM responses in real-time via sendMessageDraft Implements real-time token streaming to Telegram using the sendMessageDraft API (telego v1.6.0). Instead of showing only a "Thinking..." placeholder until the full response arrives, users now see partial LLM output appear in the chat as it's generated. The streaming pipeline threads through all layers: - StreamingProvider interface (providers/types.go): opt-in ChatStream() method that receives an onChunk callback with accumulated text - OpenAI-compatible SSE streaming (openai_compat/provider.go): parses SSE events with stream:true, handles text deltas and tool call assembly - Anthropic native streaming (anthropic/provider.go): uses SDK's NewStreaming() for direct Anthropic API connections - HTTPProvider delegation (http_provider.go): delegates ChatStream to the underlying openai_compat provider - StreamingCapable + Streamer interfaces (channels/interfaces.go): opt-in channel capability like TypingCapable/PlaceholderCapable - Telegram streamer (telegram/telegram.go): BeginStream returns a telegramStreamer that throttles sendMessageDraft calls (3s/200 chars) with graceful degradation on API errors - StreamDelegate bridge (bus/bus.go): decouples agent loop from channel manager without tight imports - Manager integration (manager.go): implements StreamDelegate, tracks streamActive state, coordinates with placeholder editing - Agent loop (loop.go): uses ChatStream when both provider and channel support streaming, cancels stream on tool calls, skips PublishOutbound when Finalize already delivered the message Graceful degradation: - Bots without forum/topics mode: first sendMessageDraft error sets failed=true, subsequent Updates become no-ops, Finalize still delivers via SendMessage. User sees normal non-streaming behavior. - Non-streaming providers: type assertion fails, falls back to Chat() - Config opt-out: streaming.enabled (default true) in telegram config Closes #1098 * fix(telegram): delete placeholder message when streaming delivers response When streaming was active, the "Thinking..." placeholder message stayed in the chat because preSend only deleted the tracking entry without removing the actual Telegram message. Now preSend deletes the placeholder via the new MessageDeleter interface when streamActive is set. * refactor(streaming): remove dead code and simplify streaming wiring - Delete unused Anthropic ChatStream/parseStream (-131 lines) — factory creates HTTPProvider for all OpenAI-compat providers including OpenRouter - Simplify runLLMIteration from 4 to 3 return values (remove unused streamed bool) - Replace managerStreamer struct with finalizeHookStreamer using embedding (Update/Cancel promoted, only Finalize overridden) * fix(streaming): skip streamer acquisition when SendResponse is false Heartbeat messages set SendResponse=false but the streaming path was unconditionally acquiring a streamer, causing HEARTBEAT_OK to leak to Telegram via streamer.Finalize(). * fix(streaming): guard streamer for non-sendable messages, add streaming config Skip streamer acquisition for heartbeat (NoHistory=true), preventing HEARTBEAT_OK from leaking to Telegram via streamer.Finalize(). Add streaming.enabled to Telegram defaults and example config. * feat(telegram): stream LLM responses in real-time via sendMessageDraft Implements real-time token streaming to Telegram using the sendMessageDraft API (telego v1.6.0). Instead of showing only a "Thinking..." placeholder until the full response arrives, users now see partial LLM output appear in the chat as it's generated. The streaming pipeline threads through all layers: - StreamingProvider interface (providers/types.go): opt-in ChatStream() method that receives an onChunk callback with accumulated text - OpenAI-compatible SSE streaming (openai_compat/provider.go): parses SSE events with stream:true, handles text deltas and tool call assembly - Anthropic native streaming (anthropic/provider.go): uses SDK's NewStreaming() for direct Anthropic API connections - HTTPProvider delegation (http_provider.go): delegates ChatStream to the underlying openai_compat provider - StreamingCapable + Streamer interfaces (channels/interfaces.go): opt-in channel capability like TypingCapable/PlaceholderCapable - Telegram streamer (telegram/telegram.go): BeginStream returns a telegramStreamer that throttles sendMessageDraft calls (3s/200 chars) with graceful degradation on API errors - StreamDelegate bridge (bus/bus.go): decouples agent loop from channel manager without tight imports - Manager integration (manager.go): implements StreamDelegate, tracks streamActive state, coordinates with placeholder editing - Agent loop (loop.go): uses ChatStream when both provider and channel support streaming, cancels stream on tool calls, skips PublishOutbound when Finalize already delivered the message Graceful degradation: - Bots without forum/topics mode: first sendMessageDraft error sets failed=true, subsequent Updates become no-ops, Finalize still delivers via SendMessage. User sees normal non-streaming behavior. - Non-streaming providers: type assertion fails, falls back to Chat() - Config opt-out: streaming.enabled (default true) in telegram config Closes #1098 * fix(telegram): delete placeholder message when streaming delivers response When streaming was active, the "Thinking..." placeholder message stayed in the chat because preSend only deleted the tracking entry without removing the actual Telegram message. Now preSend deletes the placeholder via the new MessageDeleter interface when streamActive is set. * refactor(streaming): remove dead code and simplify streaming wiring - Delete unused Anthropic ChatStream/parseStream (-131 lines) — factory creates HTTPProvider for all OpenAI-compat providers including OpenRouter - Simplify runLLMIteration from 4 to 3 return values (remove unused streamed bool) - Replace managerStreamer struct with finalizeHookStreamer using embedding (Update/Cancel promoted, only Finalize overridden) * fix(streaming): skip streamer acquisition when SendResponse is false Heartbeat messages set SendResponse=false but the streaming path was unconditionally acquiring a streamer, causing HEARTBEAT_OK to leak to Telegram via streamer.Finalize(). * fix(streaming): guard streamer for non-sendable messages, add streaming config Skip streamer acquisition for heartbeat (NoHistory=true), preventing HEARTBEAT_OK from leaking to Telegram via streamer.Finalize(). Add streaming.enabled to Telegram defaults and example config. * fix(picoclaw): add missing closing brace for StreamingProvider interface Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: resolve golangci-lint formatting issues Fix gci import ordering in telegram and anthropic provider, and break long function signature in openai_compat provider to satisfy golines. * fix: address code review feedback on streaming PR - Deduplicate Streamer interface: alias channels.Streamer to bus.Streamer to prevent type drift across packages - Increase SSE scanner buffer to 10MB max to handle large single-line responses that exceed bufio.Scanner's 64KB default - Switch draftID generation from math/rand to crypto/rand for collision-resistant random IDs - Add context cancellation check in SSE parsing loop so cancelled streams stop processing immediately - Log Finalize failures with chat_id and content length for debugging silent message delivery failures * feat: make streaming throttle interval and min growth configurable Move hardcoded streamThrottleInterval (3s) and streamMinGrowth (200) into StreamingConfig so they can be tuned per deployment via config or environment variables. * fix(telegram): use parseTelegramChatID in DeleteMessage and BeginStream These two functions called undefined parseChatID. Use parseTelegramChatID with _ for the unused threadID instead of adding a wrapper function. Fixes all three CI checks. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(streaming): set streamActive only after successful Finalize Move onFinalize hook to run after Streamer.Finalize succeeds, so that if Finalize fails the streamActive flag stays false and the regular placeholder fallback path remains available. Addresses review feedback from @alexhoshina. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
571 lines
14 KiB
Go
571 lines
14 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(EnvHome); 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,
|
|
ToolFeedback: ToolFeedbackConfig{
|
|
Enabled: true,
|
|
MaxArgsLength: 300,
|
|
},
|
|
},
|
|
},
|
|
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... 💭",
|
|
},
|
|
Streaming: StreamingConfig{Enabled: true, ThrottleSeconds: 3, MinGrowthChars: 200},
|
|
UseMarkdownV2: false,
|
|
},
|
|
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,
|
|
MaxBase64FileSizeMiB: 0,
|
|
},
|
|
DingTalk: DingTalkConfig{
|
|
Enabled: false,
|
|
ClientID: "",
|
|
ClientSecret: "",
|
|
AllowFrom: FlexibleStringSlice{},
|
|
},
|
|
Slack: SlackConfig{
|
|
Enabled: false,
|
|
BotToken: "",
|
|
AppToken: "",
|
|
AllowFrom: FlexibleStringSlice{},
|
|
},
|
|
Matrix: MatrixConfig{
|
|
Enabled: false,
|
|
Homeserver: "https://matrix.org",
|
|
UserID: "",
|
|
AccessToken: "",
|
|
DeviceID: "",
|
|
JoinOnInvite: true,
|
|
AllowFrom: FlexibleStringSlice{},
|
|
GroupTrigger: GroupTriggerConfig{
|
|
MentionOnly: true,
|
|
},
|
|
Placeholder: PlaceholderConfig{
|
|
Enabled: true,
|
|
Text: "Thinking... 💭",
|
|
},
|
|
},
|
|
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?",
|
|
ProcessingMessage: DefaultWeComAIBotProcessingMessage,
|
|
},
|
|
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.4",
|
|
Model: "openai/gpt-5.4",
|
|
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.4",
|
|
Model: "openrouter/openai/gpt-5.4",
|
|
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: "",
|
|
},
|
|
|
|
// Vivgrid - https://vivgrid.com
|
|
{
|
|
ModelName: "vivgrid-auto",
|
|
Model: "vivgrid/auto",
|
|
APIBase: "https://api.vivgrid.com/v1",
|
|
APIKey: "",
|
|
},
|
|
|
|
// Volcengine (火山引擎) - https://console.volcengine.com/ark
|
|
{
|
|
ModelName: "ark-code-latest",
|
|
Model: "volcengine/ark-code-latest",
|
|
APIBase: "https://ark.cn-beijing.volces.com/api/v3",
|
|
APIKey: "",
|
|
},
|
|
{
|
|
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.4",
|
|
Model: "github-copilot/gpt-5.4",
|
|
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: "",
|
|
},
|
|
|
|
// Minimax - https://api.minimaxi.com/
|
|
{
|
|
ModelName: "MiniMax-M2.5",
|
|
Model: "minimax/MiniMax-M2.5",
|
|
APIBase: "https://api.minimaxi.com/v1",
|
|
APIKey: "",
|
|
},
|
|
|
|
// LongCat - https://longcat.chat/platform
|
|
{
|
|
ModelName: "LongCat-Flash-Thinking",
|
|
Model: "longcat/LongCat-Flash-Thinking",
|
|
APIBase: "https://api.longcat.chat/openai",
|
|
APIKey: "",
|
|
},
|
|
|
|
// ModelScope (魔搭社区) - https://modelscope.cn/my/tokens
|
|
{
|
|
ModelName: "modelscope-qwen",
|
|
Model: "modelscope/Qwen/Qwen3-235B-A22B-Instruct-2507",
|
|
APIBase: "https://api-inference.modelscope.cn/v1",
|
|
APIKey: "",
|
|
},
|
|
|
|
// VLLM (local) - http://localhost:8000
|
|
{
|
|
ModelName: "local-model",
|
|
Model: "vllm/custom-model",
|
|
APIBase: "http://localhost:8000/v1",
|
|
APIKey: "",
|
|
},
|
|
|
|
// Azure OpenAI - https://portal.azure.com
|
|
// model_name is a user-friendly alias; the model field's path after "azure/" is your deployment name
|
|
{
|
|
ModelName: "azure-gpt5",
|
|
Model: "azure/my-gpt5-deployment",
|
|
APIBase: "https://your-resource.openai.azure.com",
|
|
APIKey: "",
|
|
},
|
|
},
|
|
Gateway: GatewayConfig{
|
|
Host: "127.0.0.1",
|
|
Port: 18790,
|
|
HotReload: false,
|
|
},
|
|
Tools: ToolsConfig{
|
|
MediaCleanup: MediaCleanupConfig{
|
|
ToolConfig: ToolConfig{
|
|
Enabled: true,
|
|
},
|
|
MaxAge: 30,
|
|
Interval: 5,
|
|
},
|
|
Web: WebToolsConfig{
|
|
ToolConfig: ToolConfig{
|
|
Enabled: true,
|
|
},
|
|
PreferNative: true,
|
|
Proxy: "",
|
|
FetchLimitBytes: 10 * 1024 * 1024, // 10MB by default
|
|
Format: "plaintext",
|
|
Brave: BraveConfig{
|
|
Enabled: false,
|
|
APIKey: "",
|
|
APIKeys: nil,
|
|
MaxResults: 5,
|
|
},
|
|
Tavily: TavilyConfig{
|
|
Enabled: false,
|
|
APIKey: "",
|
|
APIKeys: nil,
|
|
MaxResults: 5,
|
|
},
|
|
DuckDuckGo: DuckDuckGoConfig{
|
|
Enabled: true,
|
|
MaxResults: 5,
|
|
},
|
|
Perplexity: PerplexityConfig{
|
|
Enabled: false,
|
|
APIKey: "",
|
|
APIKeys: nil,
|
|
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,
|
|
AllowCommand: true,
|
|
},
|
|
Exec: ExecConfig{
|
|
ToolConfig: ToolConfig{
|
|
Enabled: true,
|
|
},
|
|
EnableDenyPatterns: true,
|
|
AllowRemote: 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,
|
|
},
|
|
Discovery: ToolDiscoveryConfig{
|
|
Enabled: false,
|
|
TTL: 5,
|
|
MaxSearchResults: 5,
|
|
UseBM25: true,
|
|
UseRegex: 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: ReadFileToolConfig{
|
|
Enabled: true,
|
|
MaxReadFileSize: 64 * 1024, // 64KB
|
|
},
|
|
Spawn: ToolConfig{
|
|
Enabled: true,
|
|
},
|
|
SpawnStatus: ToolConfig{
|
|
Enabled: false,
|
|
},
|
|
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,
|
|
},
|
|
Voice: VoiceConfig{
|
|
EchoTranscription: false,
|
|
},
|
|
BuildInfo: BuildInfo{
|
|
Version: Version,
|
|
GitCommit: GitCommit,
|
|
BuildTime: BuildTime,
|
|
GoVersion: GoVersion,
|
|
},
|
|
}
|
|
}
|