- write config and cron store with 0600 instead of 0644
- check allow list in Slack slash commands and app mentions
- pass workspace restrict flag to cron exec tool
Closes#179
Fixes four issues identified in the community code review:
- Session persistence broken on Windows: session keys like
"telegram:123456" contain ':', which is illegal in Windows
filenames. filepath.Base() strips drive-letter prefixes on Windows,
causing Save() to silently fail. Added sanitizeFilename() to
replace invalid chars in the filename while keeping the original
key in the JSON payload.
- HTTP client with no timeout: HTTPProvider used Timeout: 0 (infinite
wait), which can hang the entire agent if an API endpoint becomes
unresponsive. Set a 120s safety timeout.
- Slack AllowFrom type mismatch: SlackConfig used plain []string
while every other channel uses FlexibleStringSlice, so numeric
user IDs in Slack config would fail to parse.
- Token estimation wrong for CJK: estimateTokens() divided byte
length by 4, but CJK characters are 3 bytes each, causing ~3x
overestimation and premature summarization. Switched to
utf8.RuneCountInString() / 3 for better cross-language accuracy.
Also added unit tests for the session filename sanitization.
Ref #116
Fix web_test.go and config_test.go to use current function signatures
after merging upstream changes (WebSearchToolOptions, BraveConfig).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add LINE Messaging API as the 9th messaging channel using HTTP Webhook.
Supports text/image/audio messages, group chat @mention detection,
reply with quote, and loading animation.
- No external SDK required (standard library only)
- HMAC-SHA256 webhook signature verification
- Reply Token (free) with Push API fallback
- Group chat: respond only when @mentioned
- Quote original message in replies using quoteToken
Closes#146
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
feat(config): add heartbeat interval configuration with default 30 minutes
feat(state): migrate state file from workspace root to state directory
feat(channels): skip internal channels in outbound dispatcher
feat(agent): record last active channel for heartbeat context
refactor(subagent): use configurable default model instead of provider default
Resolved conflicts:
- pkg/heartbeat/service.go: merged both 'started' field and 'onHeartbeatWithTools'
- pkg/tools/edit.go: use validatePath() with ToolResult return
- pkg/tools/filesystem.go: fixed return values to use ToolResult
- cmd/picoclaw/main.go: kept active setupCronTool, fixed toolsPkg import
- pkg/tools/cron.go: fixed Execute return value handling
Fixed tests for new function signatures (NewEditFileTool, NewAppendFileTool, NewExecTool)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove .ralph/ directory files from git tracking.
These are no longer needed as the tool-result-refactor is complete.
Also removes root-level prd.json and progress.txt.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add proxy config field for Telegram channel to support HTTP/SOCKS proxies
- Use telego.WithHTTPClient to route all Telegram API requests through proxy
- Add FlexibleStringSlice type so allow_from accepts both strings and numbers
- Improve IsAllowed to match numeric ID, username, and @username formats
- Update config.example.json with proxy field
- Added HeartbeatConfig struct with Enabled field
- Added Heartbeat to Config struct
- Set default Heartbeat.Enabled = true in DefaultConfig()
- Updated main.go to use cfg.Heartbeat.Enabled instead of hardcoded true
- Added config tests verifying heartbeat is enabled by default
Acceptance criteria met:
- DefaultConfig() Heartbeat.Enabled changed to true
- Can override via PICOCLAW_HEARTBEAT_ENABLED=false env var
- Config documentation updated showing default enabled
- Typecheck passes (go build ./... succeeds)
- go test ./pkg/config -run TestDefaultConfig passes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implemented a unified path validation helper to ensure filesystem operations stay within the designated workspace. This now supports a 'restrict_to_workspace' option in config.json (enabled by default) to allow flexibility for specific environments while maintaining a secure default posture. I've updated read_file, write_file, list_dir, append_file, edit_file, and exec tools to respect this setting and included tests for both restricted and unrestricted modes.
- Add Provider field to AgentDefaults struct
- Modify CreateProvider to use explicit provider field first, fallback to model name detection
- Allows using models without provider prefix (e.g., llama-3.1-8b-instant instead of groq/llama-3.1-8b-instant)
- Supports all providers: groq, openai, anthropic, openrouter, zhipu, gemini, vllm
- Backward compatible with existing configs
Fixes issue where models without provider prefix could not use configured API keys.
Add Slack as a messaging channel using Socket Mode (WebSocket), bringing
the total supported channels to 8. Features include bidirectional
messaging, thread support with per-thread session context, @mention
handling, ack reactions (👀/✅), slash commands,
file/attachment support with Groq Whisper audio transcription, and
allowlist filtering by Slack user ID.
Closes#31
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>