diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..4be385b22 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,28 @@ +--- +name: Bug report +about: Report a bug or unexpected behavior +title: "[BUG]" +labels: bug +assignees: '' + +--- + +## Quick Summary + +## Environment & Tools +- **PicoClaw Version:** (e.g., v0.1.2 or commit hash) +- **Go Version:** (e.g., go 1.22) +- **AI Model & Provider:** (e.g., GPT-4o via OpenAI / DeepSeek via SiliconFlow) +- **Operating System:** (e.g., Ubuntu 22.04 / macOS / Android Termux) +- **Channels:** (e.g., Discord, Telegram, Feishu, ...) + +## 📸 Steps to Reproduce +1. +2. +3. + +## ❌ Actual Behavior + +## ✅ Expected Behavior + +## 💬 Additional Context diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..d3df0e79c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,23 @@ +--- +name: Feature request +about: Suggest a new idea or improvement +title: "[Feature]" +labels: enhancement +assignees: '' + +--- + +## 🎯 The Goal / Use Case + +## 💡 Proposed Solution + +## 🛠 Potential Implementation (Optional) + +## 🚦 Impact & Roadmap Alignment +- [ ] This is a Core Feature +- [ ] This is a Nice-to-Have / Enhancement +- [ ] This aligns with the current Roadmap + +## 🔄 Alternatives Considered + +## 💬 Additional Context diff --git a/.github/ISSUE_TEMPLATE/general-task---todo.md b/.github/ISSUE_TEMPLATE/general-task---todo.md new file mode 100644 index 000000000..eab70c030 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/general-task---todo.md @@ -0,0 +1,26 @@ +--- +name: General Task / Todo +about: A specific piece of work like doc, refactoring, or maintenance. +title: "[Task]" +labels: '' +assignees: '' + +--- + +## 📝 Objective + +## 📋 To-Do List +- [ ] Step 1 +- [ ] Step 2 +- [ ] Step 3 + +## 🎯 Definition of Done (Acceptance Criteria) +- [ ] Documentation is updated in the README/docs folder. +- [ ] Code follows project linting standards. +- [ ] (If applicable) Basic tests pass. + +## 💡 Context / Motivation + +## 🔗 Related Issues / PRs +- Fixes # +- Relates to # diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..7910cb1e2 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,37 @@ +## 📝 Description +## 🗣️ Type of Change +- [ ] 🐞 Bug fix (non-breaking change which fixes an issue) +- [ ] ✨ New feature (non-breaking change which adds functionality) +- [ ] 📖 Documentation update +- [ ] ⚡ Code refactoring (no functional changes, no api changes) + +## 🤖 AI Code Generation +- [ ] 🤖 Fully AI-generated (100% AI, 0% Human) +- [ ] 🛠️ Mostly AI-generated (AI draft, Human verified/modified) +- [ ] 👨‍💻 Mostly Human-written (Human lead, AI assisted or none) + + +## 🔗 Linked Issue +## 📚 Technical Context (Skip for Docs) +* **Reference:** [URL] +* **Reasoning:** ... + + +## 🧪 Test Environment & Hardware +- **Hardware:** [e.g. Raspberry Pi 5, Orange Pi, PC] +- **OS:** [e.g. Debian 12, Ubuntu 22.04] +- **Model/Provider:** [e.g. OpenAI GPT-4o, Kimi k2, DeepSeek-V3] +- **Channels:** [e.g. Discord, Telegram, Feishu, ...] + + +## 📸 Proof of Work (Optional for Docs) +
+Click to view Logs/Screenshots + +
+ + +## ☑️ Checklist +- [ ] My code/docs follow the style of this project. +- [ ] I have performed a self-review of my own changes. +- [ ] I have updated the documentation accordingly. \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f9987b35f..9fe3a684e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -32,11 +32,13 @@ jobs: - name: Create and push tag shell: bash + env: + RELEASE_TAG: ${{ inputs.tag }} run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - git tag -a "${{ inputs.tag }}" -m "Release ${{ inputs.tag }}" - git push origin "${{ inputs.tag }}" + git tag -a "$RELEASE_TAG" -m "Release $RELEASE_TAG" + git push origin "$RELEASE_TAG" release: name: GoReleaser Release diff --git a/Makefile b/Makefile index 9786b30bb..bb31243dd 100644 --- a/Makefile +++ b/Makefile @@ -39,6 +39,8 @@ ifeq ($(UNAME_S),Linux) ARCH=amd64 else ifeq ($(UNAME_M),aarch64) ARCH=arm64 + else ifeq ($(UNAME_M),loongarch64) + ARCH=loong64 else ifeq ($(UNAME_M),riscv64) ARCH=riscv64 else @@ -84,6 +86,7 @@ build-all: generate @mkdir -p $(BUILD_DIR) GOOS=linux GOARCH=amd64 $(GO) build $(LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-linux-amd64 ./$(CMD_DIR) GOOS=linux GOARCH=arm64 $(GO) build $(LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-linux-arm64 ./$(CMD_DIR) + GOOS=linux GOARCH=loong64 $(GO) build $(LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-linux-loong64 ./$(CMD_DIR) GOOS=linux GOARCH=riscv64 $(GO) build $(LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-linux-riscv64 ./$(CMD_DIR) GOOS=darwin GOARCH=arm64 $(GO) build $(LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-darwin-arm64 ./$(CMD_DIR) GOOS=windows GOARCH=amd64 $(GO) build $(LDFLAGS) -o $(BUILD_DIR)/$(BINARY_NAME)-windows-amd64.exe ./$(CMD_DIR) diff --git a/README.ja.md b/README.ja.md index e33b312f9..b86d636ac 100644 --- a/README.ja.md +++ b/README.ja.md @@ -12,7 +12,7 @@ License

-**日本語** | [English](README.md) +[中文](README.zh.md) | **日本語** | [English](README.md) @@ -195,6 +195,9 @@ picoclaw onboard "api_key": "YOUR_BRAVE_API_KEY", "max_results": 5 } + }, + "cron": { + "exec_timeout_minutes": 5 } }, "heartbeat": { @@ -697,6 +700,9 @@ HEARTBEAT_OK 応答 ユーザーが直接結果を受け取る "search": { "apiKey": "BSA..." } + }, + "cron": { + "exec_timeout_minutes": 5 } }, "heartbeat": { diff --git a/README.md b/README.md index 091af2811..e80e2213c 100644 --- a/README.md +++ b/README.md @@ -45,8 +45,11 @@ > * **OFFICIAL DOMAIN:** The **ONLY** official website is **[picoclaw.io](https://picoclaw.io)**, and company website is **[sipeed.com](https://sipeed.com)** > * **Warning:** Many `.ai/.org/.com/.net/...` domains are registered by third parties. > * **Warning:** picoclaw is in early development now and may have unresolved network security issues. Do not deploy to production environments before the v1.0 release. +> * **Note:** picoclaw has recently merged a lot of PRs, which may result in a larger memory footprint (10–20MB) in the latest versions. We plan to prioritize resource optimization as soon as the current feature set reaches a stable state. + ## 📢 News +2026-02-16 🎉 PicoClaw hit 12K stars in one week! Thank you all for your support! PicoClaw is growing faster than we ever imagined. Given the high volume of PRs, we urgently need community maintainers. Our volunteer roles and roadmap are officially posted [here](docs/picoclaw_community_roadmap_260216.md) —we can’t wait to have you on board! 2026-02-13 🎉 PicoClaw hit 5000 stars in 4days! Thank you for the community! There are so many PRs&issues come in (during Chinese New Year holidays), we are finalizing the Project Roadmap and setting up the Developer Group to accelerate PicoClaw's development. 🚀 Call to Action: Please submit your feature requests in GitHub Discussions. We will review and prioritize them during our upcoming weekly meeting. @@ -96,6 +99,20 @@ +### 📱 Run on old Android Phones +Give your decade-old phone a second life! Turn it into a smart AI Assistant with PicoClaw. Quick Start: +1. **Install Termux** (Available on F-Droid or Google Play). +2. **Execute cmds** +```bash +# Note: Replace v0.1.1 with the latest version from the Releases page +wget https://github.com/sipeed/picoclaw/releases/download/v0.1.1/picoclaw-linux-arm64 +chmod +x picoclaw-linux-arm64 +pkg install proot +termux-chroot ./picoclaw-linux-arm64 onboard +``` +And then follow the instructions in the "Quick Start" section to complete the configuration! +PicoClaw + ### 🐜 Innovative Low-Footprint Deploy PicoClaw can be deployed on almost any Linux device! @@ -757,6 +774,9 @@ picoclaw agent -m "Hello" "enabled": true, "max_results": 5 } + }, + "cron": { + "exec_timeout_minutes": 5 } }, "heartbeat": { diff --git a/README.zh.md b/README.zh.md index 5a1c3c50b..e7dc8d769 100644 --- a/README.zh.md +++ b/README.zh.md @@ -46,9 +46,11 @@ > * **官方域名:** 唯一的官方网站是 **[picoclaw.io](https://picoclaw.io)**,公司官网是 **[sipeed.com](https://sipeed.com)**。 > * **警惕:** 许多 `.ai/.org/.com/.net/...` 后缀的域名被第三方抢注,请勿轻信。 > * **注意:** picoclaw正在初期的快速功能开发阶段,可能有尚未修复的网络安全问题,在1.0正式版发布前,请不要将其部署到生产环境中 +> * **注意:** picoclaw最近合并了大量PRs,近期版本可能内存占用较大(10~20MB),我们将在功能较为收敛后进行资源占用优化. ## 📢 新闻 (News) +2026-02-16 🎉 PicoClaw 在一周内突破了12K star! 感谢大家的关注!PicoClaw 的成长速度超乎我们预期. 由于PR数量的快速膨胀,我们亟需社区开发者参与维护. 我们需要的志愿者角色和roadmap已经发布到了[这里](docs/picoclaw_community_roadmap_260216.md), 期待你的参与! 2026-02-13 🎉 **PicoClaw 在 4 天内突破 5000 Stars!** 感谢社区的支持!由于正值中国春节假期,PR 和 Issue 涌入较多,我们正在利用这段时间敲定 **项目路线图 (Roadmap)** 并组建 **开发者群组**,以便加速 PicoClaw 的开发。 🚀 **行动号召:** 请在 GitHub Discussions 中提交您的功能请求 (Feature Requests)。我们将在接下来的周会上进行审查和优先级排序。 @@ -98,6 +100,23 @@ +### 📱 在手机上轻松运行 +picoclaw 可以将你10年前的老旧手机废物利用,变身成为你的AI助理!快速指南: +1. 先去应用商店下载安装Termux +2. 打开后执行指令 +```bash +# 注意: 下面的v0.1.1 可以换为你实际看到的最新版本 +wget https://github.com/sipeed/picoclaw/releases/download/v0.1.1/picoclaw-linux-arm64 +chmod +x picoclaw-linux-arm64 +pkg install proot +termux-chroot ./picoclaw-linux-arm64 onboard +``` +然后跟随下面的“快速开始”章节继续配置picoclaw即可使用! +PicoClaw + + + + ### 🐜 创新的低占用部署 PicoClaw 几乎可以部署在任何 Linux 设备上! @@ -217,6 +236,9 @@ picoclaw onboard "api_key": "YOUR_BRAVE_API_KEY", "max_results": 5 } + }, + "cron": { + "exec_timeout_minutes": 5 } } } @@ -625,6 +647,9 @@ picoclaw agent -m "你好" "search": { "api_key": "BSA..." } + }, + "cron": { + "exec_timeout_minutes": 5 } }, "heartbeat": { diff --git a/ROADMAP.md b/ROADMAP.md new file mode 100644 index 000000000..8c5c0e252 --- /dev/null +++ b/ROADMAP.md @@ -0,0 +1,116 @@ + +# 🦐 PicoClaw Roadmap + +> **Vision**: To build the ultimate lightweight, secure, and fully autonomous AI Agent infrastructure.automate the mundane, unleash your creativity + +--- + +## 🚀 1. Core Optimization: Extreme Lightweight + +*Our defining characteristic. We fight software bloat to ensure PicoClaw runs smoothly on the smallest embedded devices.* + +* [**Memory Footprint Reduction**](https://github.com/sipeed/picoclaw/issues/346) + * **Goal**: Run smoothly on 64MB RAM embedded boards (e.g., low-end RISC-V SBCs) with the core process consuming < 20MB. + * **Context**: RAM is expensive and scarce on edge devices. Memory optimization takes precedence over storage size. + * **Action**: Analyze memory growth between releases, remove redundant dependencies, and optimize data structures. + + +## 🛡️ 2. Security Hardening: Defense in Depth + +*Paying off early technical debt. We invite security experts to help build a "Secure-by-Default" agent.* + +* **Input Defense & Permission Control** + * **Prompt Injection Defense**: Harden JSON extraction logic to prevent LLM manipulation. + * **Tool Abuse Prevention**: Strict parameter validation to ensure generated commands stay within safe boundaries. + * **SSRF Protection**: Built-in blocklists for network tools to prevent accessing internal IPs (LAN/Metadata services). + + +* **Sandboxing & Isolation** + * **Filesystem Sandbox**: Restrict file R/W operations to specific directories only. + * **Context Isolation**: Prevent data leakage between different user sessions or channels. + * **Privacy Redaction**: Auto-redact sensitive info (API Keys, PII) from logs and standard outputs. + + +* **Authentication & Secrets** + * **Crypto Upgrade**: Adopt modern algorithms like `ChaCha20-Poly1305` for secret storage. + * **OAuth 2.0 Flow**: Deprecate hardcoded API keys in the CLI; move to secure OAuth flows. + + + +## 🔌 3. Connectivity: Protocol-First Architecture + +*Connect every model, reach every platform.* + +* **Provider** + * [**Architecture Upgrade**](https://github.com/sipeed/picoclaw/issues/283): Refactor from "Vendor-based" to "Protocol-based" classification (e.g., OpenAI-compatible, Ollama-compatible). *(Status: In progress by @Daming, ETA 5 days)* + * **Local Models**: Deep integration with **Ollama**, **vLLM**, **LM Studio**, and **Mistral** (local inference). + * **Online Models**: Continued support for frontier closed-source models. + + +* **Channel** + * **IM Matrix**: QQ, WeChat (Work), DingTalk, Feishu (Lark), Telegram, Discord, WhatsApp, LINE, Slack, Email, KOOK, Signal, ... + * **Standards**: Support for the **OneBot** protocol. + * [**attachment**](https://github.com/sipeed/picoclaw/issues/348): Native handling of images, audio, and video attachments. + + +* **Skill Marketplace** + * [**Discovery skills**](https://github.com/sipeed/picoclaw/issues/287): Implement `find_skill` to automatically discover and install skills from the [GitHub Skills Repo] or other registries. + + + +## 🧠 4. Advanced Capabilities: From Chatbot to Agentic AI + +*Beyond conversation—focusing on action and collaboration.* + +* **Operations** + * [**MCP Support**](https://github.com/sipeed/picoclaw/issues/290): Native support for the **Model Context Protocol (MCP)**. + * [**Browser Automation**](https://github.com/sipeed/picoclaw/issues/293): Headless browser control via CDP (Chrome DevTools Protocol) or ActionBook. + * [**Mobile Operation**](https://github.com/sipeed/picoclaw/issues/292): Android device control (similar to BotDrop). + + +* **Multi-Agent Collaboration** + * [**Basic Multi-Agent**](https://github.com/sipeed/picoclaw/issues/294) implement + * [**Model Routing**](https://github.com/sipeed/picoclaw/issues/295): "Smart Routing" — dispatch simple tasks to small/local models (fast/cheap) and complex tasks to SOTA models (smart). + * [**Swarm Mode**](https://github.com/sipeed/picoclaw/issues/284): Collaboration between multiple PicoClaw instances on the same network. + * [**AIEOS**](https://github.com/sipeed/picoclaw/issues/296): Exploring AI-Native Operating System interaction paradigms. + + + +## 📚 5. Developer Experience (DevEx) & Documentation + +*Lowering the barrier to entry so anyone can deploy in minutes.* + +* [**QuickGuide (Zero-Config Start)**](https://github.com/sipeed/picoclaw/issues/350) + * Interactive CLI Wizard: If launched without config, automatically detect the environment and guide the user through Token/Network setup step-by-step. + + +* **Comprehensive Documentation** + * **Platform Guides**: Dedicated guides for Windows, macOS, Linux, and Android. + * **Step-by-Step Tutorials**: "Babysitter-level" guides for configuring Providers and Channels. + * **AI-Assisted Docs**: Using AI to auto-generate API references and code comments (with human verification to prevent hallucinations). + + + +## 🤖 6. Engineering: AI-Powered Open Source + +*Born from Vibe Coding, we continue to use AI to accelerate development.* + +* **AI-Enhanced CI/CD** + * Integrate AI for automated Code Review, Linting, and PR Labeling. + * **Bot Noise Reduction**: Optimize bot interactions to keep PR timelines clean. + * **Issue Triage**: AI agents to analyze incoming issues and suggest preliminary fixes. + + + +## 🎨 7. Brand & Community + +* [**Logo Design**](https://github.com/sipeed/picoclaw/issues/297): We are looking for a **Mantis Shrimp (Stomatopoda)** logo design! + * *Concept*: Needs to reflect "Small but Mighty" and "Lightning Fast Strikes." + + + +--- + +### 🤝 Call for Contributions + +We welcome community contributions to any item on this roadmap! Please comment on the relevant Issue or submit a PR. Let's build the best Edge AI Agent together! \ No newline at end of file diff --git a/assets/termux.jpg b/assets/termux.jpg new file mode 100644 index 000000000..30c724a20 Binary files /dev/null and b/assets/termux.jpg differ diff --git a/assets/wechat.png b/assets/wechat.png index d62c8d09d..6e6f50115 100644 Binary files a/assets/wechat.png and b/assets/wechat.png differ diff --git a/cmd/picoclaw/main.go b/cmd/picoclaw/main.go index 10b53948b..fd7ec484a 100644 --- a/cmd/picoclaw/main.go +++ b/cmd/picoclaw/main.go @@ -562,7 +562,8 @@ func gatewayCmd() { }) // Setup cron tool and service - cronService := setupCronTool(agentLoop, msgBus, cfg.WorkspacePath(), cfg.Agents.Defaults.RestrictToWorkspace) + execTimeout := time.Duration(cfg.Tools.Cron.ExecTimeoutMinutes) * time.Minute + cronService := setupCronTool(agentLoop, msgBus, cfg.WorkspacePath(), cfg.Agents.Defaults.RestrictToWorkspace, execTimeout) heartbeatService := heartbeat.NewHeartbeatService( cfg.WorkspacePath(), @@ -987,14 +988,14 @@ func getConfigPath() string { return filepath.Join(home, ".picoclaw", "config.json") } -func setupCronTool(agentLoop *agent.AgentLoop, msgBus *bus.MessageBus, workspace string, restrict bool) *cron.CronService { +func setupCronTool(agentLoop *agent.AgentLoop, msgBus *bus.MessageBus, workspace string, restrict bool, execTimeout time.Duration) *cron.CronService { cronStorePath := filepath.Join(workspace, "cron", "jobs.json") // Create cron service cronService := cron.NewCronService(cronStorePath, nil) // Create and register CronTool - cronTool := tools.NewCronTool(cronService, agentLoop, msgBus, workspace, restrict) + cronTool := tools.NewCronTool(cronService, agentLoop, msgBus, workspace, restrict, execTimeout) agentLoop.RegisterTool(cronTool) // Set the onJob handler diff --git a/config/config.example.json b/config/config.example.json index 3c9158e9c..7cd0ab8c6 100644 --- a/config/config.example.json +++ b/config/config.example.json @@ -14,7 +14,9 @@ "enabled": false, "token": "YOUR_TELEGRAM_BOT_TOKEN", "proxy": "", - "allow_from": ["YOUR_USER_ID"] + "allow_from": [ + "YOUR_USER_ID" + ] }, "discord": { "enabled": false, @@ -115,10 +117,19 @@ }, "tools": { "web": { - "search": { + "brave": { + "enabled": false, "api_key": "YOUR_BRAVE_API_KEY", "max_results": 5 + }, + "perplexity": { + "enabled": false, + "api_key": "pplx-xxx", + "max_results": 5 } + }, + "cron": { + "exec_timeout_minutes": 5 } }, "heartbeat": { @@ -133,4 +144,4 @@ "host": "0.0.0.0", "port": 18790 } -} +} \ No newline at end of file diff --git a/docs/picoclaw_community_roadmap_260216.md b/docs/picoclaw_community_roadmap_260216.md new file mode 100644 index 000000000..cfcc30f17 --- /dev/null +++ b/docs/picoclaw_community_roadmap_260216.md @@ -0,0 +1,112 @@ +## 🚀 Join the PicoClaw Journey: Call for Community Volunteers & Roadmap Reveal + +**Hello, PicoClaw Community!** + +First, a massive thank you to everyone for your enthusiasm and PR contributions. It is because of you that PicoClaw continues to iterate and evolve so rapidly. Thanks to the simplicity and accessibility of the **Go language**, we’ve seen a non-stop stream of high-quality PRs! + +PicoClaw is growing much faster than we anticipated. As we are currently in the midst of the **Chinese New Year holiday**, we are looking to recruit community volunteers to help us maintain this incredible momentum. + +This document outlines the specific volunteer roles we need right now and provides a look at our upcoming **Roadmap**. + +### 🎁 Community Perks + +To show our appreciation, developers who officially join our community operations will receive: + +* **Exclusive AI Hardware:** Our upcoming, unreleased AI device. +* **Token Discounts:** Potential discounts on LLM tokens (currently in negotiations with major providers). + +### 🎥 Calling All Content Creators! + +Not a developer? You can still help! We welcome users to post **PicoClaw reviews or tutorials**. + +* **Twitter:** Use the tag **#picoclaw** and mention **@SipeedIO**. +* **Bilibili:** Mention **@Sipeed矽速科技** or send us a DM. +We will be rewarding high-quality content creators with the same perks as our community developers! + +--- + +## 🛠️ Urgent Volunteer Roles + +We are looking for experts in the following areas: + +1. **Issue/PR Reviewers** +* **The Mission:** With PRs and Issues exploding in volume, we need help with initial triage, evaluation, and merging. +* **Focus:** Preliminary merging and community health. Efficiency optimization and security audits will be handled by specialized roles. + + +2. **Resource Optimization Experts** +* **The Mission:** Rapid growth has introduced dependencies that are making PicoClaw a bit "heavy." We want to keep it lean. +* **Focus:** Analyzing resource growth between releases and trimming redundancy. +* **Priority:** **RAM usage optimization** > Binary size reduction. + + +3. **Security Audit & Bug Fixes** +* **The Mission:** Due to the "vibe coding" nature of our early stages, we need a thorough review of network security and AI permission management. +* **Focus:** Auditing the codebase for vulnerabilities and implementing robust fixes. + + +4. **Documentation & DX (Developer Experience)** +* **The Mission:** Our current README is a bit outdated. We need "step-by-step" guides that even beginners can follow. +* **Focus:** Creating clear, user-friendly documentation for both setup and development. + + +5. **AI-Powered CI/CD Optimization** +* **The Mission:** PicoClaw started as a "vibe coding" experiment; now we want to use AI to manage it. +* **Focus:** Automating builds with AI and exploring AI-driven issue resolution. + +**How to Apply:** > If you are interested in any of the roles above, please send an email to support@sipeed.com with the subject line: [Apply: PicoClaw Expert Volunteer] + Your Desired Role. +Please include a brief introduction and any relevant experience or portfolio links. We will review all applications and grant project permissions to selected contributors! + +--- + +## 📍 The Roadmap + +Interested in a specific feature? You can "claim" these tasks and start building: + +### +* **Provider:** + * **Provider Refactor:** Currently being handled by **@Daming** (ETA: 5 days) + * You can still submit code; Daming will merge it into the new implementation. +* **Channels:** + * Support for OneBot, additional platforms + * attachments (images, audio, video, files). +* **Skills:** + * Implementing `find_skill` to discover tools via [openclaw/skills](https://github.com/openclaw/skills) and other platforms. +* **Operations:** * MCP Support. + * Android operations (e.g., botdrop). + * Browser automation via CDP or ActionBook. + + +* **Multi-Agent Ecosystem:** + * **Basic Model-Agnet** S + * **Model Routing:** Small models for easy tasks, large models for hard ones (to save tokens). + * **Swarm Mode.** + * **AIEOS Integration.** + + +* **Branding:** + * **Logo**: We need a cute logo! We’re leaning toward a **Mantis Shrimp**—small, but packs a legendary punch! + + +We have officially created these tasks as GitHub Issues, all marked with the roadmap tag. +This list will be updated continuously as we progress. +If you would like to claim a task, please feel free to start a conversation by commenting directly on the corresponding issue! + +--- + +## 🤝 How to Join + +**Everything is open to your creativity!** If you have a wild idea, just PR it. + +1. **The Fast Track:** Once you have at least **one merged PR**, you are eligible to join our **Developer Discord** to help plan the future of PicoClaw. +2. **The Application Track:** If you haven’t submitted a PR yet but want to dive in, email **support@sipeed.com** with the subject: +> `[Apply Join PicoClaw Dev Group] + Your GitHub Account` +> Include the role you're interested in and any evidence of your development experience. + + + +### Looking Ahead + +Powered by PicoClaw, we are crafting a Swarm AI Assistant to transform your environment into a seamless network of personal stewards. By automating the friction of daily life, we empower you to transcend the ordinary and freely explore your creative potential. + +**Finally, Happy Chinese New Year to everyone!** May PicoClaw gallop forward in this **Year of the Horse!** 🐎 diff --git a/pkg/agent/loop.go b/pkg/agent/loop.go index cd4276155..d3afa298e 100644 --- a/pkg/agent/loop.go +++ b/pkg/agent/loop.go @@ -79,6 +79,9 @@ func createToolRegistry(workspace string, restrict bool, cfg *config.Config, msg BraveEnabled: cfg.Tools.Web.Brave.Enabled, DuckDuckGoMaxResults: cfg.Tools.Web.DuckDuckGo.MaxResults, DuckDuckGoEnabled: cfg.Tools.Web.DuckDuckGo.Enabled, + PerplexityAPIKey: cfg.Tools.Web.Perplexity.APIKey, + PerplexityMaxResults: cfg.Tools.Web.Perplexity.MaxResults, + PerplexityEnabled: cfg.Tools.Web.Perplexity.Enabled, }); searchTool != nil { registry.Register(searchTool) } diff --git a/pkg/channels/maixcam.go b/pkg/channels/maixcam.go index 5fc19adbe..01e570b25 100644 --- a/pkg/channels/maixcam.go +++ b/pkg/channels/maixcam.go @@ -18,7 +18,6 @@ type MaixCamChannel struct { listener net.Listener clients map[net.Conn]bool clientsMux sync.RWMutex - running bool } type MaixCamMessage struct { @@ -35,7 +34,6 @@ func NewMaixCamChannel(cfg config.MaixCamConfig, bus *bus.MessageBus) (*MaixCamC BaseChannel: base, config: cfg, clients: make(map[net.Conn]bool), - running: false, }, nil } diff --git a/pkg/config/config.go b/pkg/config/config.go index d189ff00b..1d34f56f3 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -206,13 +206,25 @@ type DuckDuckGoConfig struct { MaxResults int `json:"max_results" env:"PICOCLAW_TOOLS_WEB_DUCKDUCKGO_MAX_RESULTS"` } +type PerplexityConfig struct { + Enabled bool `json:"enabled" env:"PICOCLAW_TOOLS_WEB_PERPLEXITY_ENABLED"` + APIKey string `json:"api_key" env:"PICOCLAW_TOOLS_WEB_PERPLEXITY_API_KEY"` + MaxResults int `json:"max_results" env:"PICOCLAW_TOOLS_WEB_PERPLEXITY_MAX_RESULTS"` +} + type WebToolsConfig struct { Brave BraveConfig `json:"brave"` DuckDuckGo DuckDuckGoConfig `json:"duckduckgo"` + Perplexity PerplexityConfig `json:"perplexity"` +} + +type CronToolsConfig struct { + ExecTimeoutMinutes int `json:"exec_timeout_minutes" env:"PICOCLAW_TOOLS_CRON_EXEC_TIMEOUT_MINUTES"` // 0 means no timeout } type ToolsConfig struct { - Web WebToolsConfig `json:"web"` + Web WebToolsConfig `json:"web"` + Cron CronToolsConfig `json:"cron"` } func DefaultConfig() *Config { @@ -321,6 +333,14 @@ func DefaultConfig() *Config { Enabled: true, MaxResults: 5, }, + Perplexity: PerplexityConfig{ + Enabled: false, + APIKey: "", + MaxResults: 5, + }, + }, + Cron: CronToolsConfig{ + ExecTimeoutMinutes: 5, // default 5 minutes for LLM operations }, }, Heartbeat: HeartbeatConfig{ diff --git a/pkg/providers/codex_provider.go b/pkg/providers/codex_provider.go index 6dff3a52e..7617bf716 100644 --- a/pkg/providers/codex_provider.go +++ b/pkg/providers/codex_provider.go @@ -217,12 +217,18 @@ func buildCodexParams(messages []Message, tools []ToolDefinition, model string, }) } for _, tc := range msg.ToolCalls { - argsJSON, _ := json.Marshal(tc.Arguments) + name, args, ok := resolveCodexToolCall(tc) + if !ok { + logger.WarnCF("provider.codex", "Skipping invalid tool call in history", map[string]interface{}{ + "call_id": tc.ID, + }) + continue + } inputItems = append(inputItems, responses.ResponseInputItemUnionParam{ OfFunctionCall: &responses.ResponseFunctionToolCallParam{ CallID: tc.ID, - Name: tc.Name, - Arguments: string(argsJSON), + Name: name, + Arguments: args, }, }) } @@ -260,10 +266,6 @@ func buildCodexParams(messages []Message, tools []ToolDefinition, model string, params.Instructions = openai.Opt(defaultCodexInstructions) } - if maxTokens, ok := options["max_tokens"].(int); ok { - params.MaxOutputTokens = openai.Opt(int64(maxTokens)) - } - if len(tools) > 0 { params.Tools = translateToolsForCodex(tools) } @@ -271,6 +273,30 @@ func buildCodexParams(messages []Message, tools []ToolDefinition, model string, return params } +func resolveCodexToolCall(tc ToolCall) (name string, arguments string, ok bool) { + name = tc.Name + if name == "" && tc.Function != nil { + name = tc.Function.Name + } + if name == "" { + return "", "", false + } + + if len(tc.Arguments) > 0 { + argsJSON, err := json.Marshal(tc.Arguments) + if err != nil { + return "", "", false + } + return name, string(argsJSON), true + } + + if tc.Function != nil && tc.Function.Arguments != "" { + return name, tc.Function.Arguments, true + } + + return name, "{}", true +} + func translateToolsForCodex(tools []ToolDefinition) []responses.ToolUnionParam { result := make([]responses.ToolUnionParam, 0, len(tools)) for _, t := range tools { diff --git a/pkg/providers/codex_provider_test.go b/pkg/providers/codex_provider_test.go index 317b1a5de..8406760c4 100644 --- a/pkg/providers/codex_provider_test.go +++ b/pkg/providers/codex_provider_test.go @@ -29,6 +29,9 @@ func TestBuildCodexParams_BasicMessage(t *testing.T) { if params.Instructions.Or("") != defaultCodexInstructions { t.Errorf("Instructions = %q, want %q", params.Instructions.Or(""), defaultCodexInstructions) } + if params.MaxOutputTokens.Valid() { + t.Fatalf("MaxOutputTokens should not be set for Codex backend") + } } func TestBuildCodexParams_SystemAsInstructions(t *testing.T) { @@ -65,6 +68,45 @@ func TestBuildCodexParams_ToolCallConversation(t *testing.T) { } } +func TestBuildCodexParams_ToolCallFunctionFallback(t *testing.T) { + messages := []Message{ + {Role: "user", Content: "Read a file"}, + { + Role: "assistant", + ToolCalls: []ToolCall{ + { + ID: "call_1", + Type: "function", + Function: &FunctionCall{ + Name: "read_file", + Arguments: `{"path":"README.md"}`, + }, + }, + }, + }, + {Role: "tool", Content: "ok", ToolCallID: "call_1"}, + } + + params := buildCodexParams(messages, nil, "gpt-4o", map[string]interface{}{}) + if params.Input.OfInputItemList == nil { + t.Fatal("Input.OfInputItemList should not be nil") + } + if len(params.Input.OfInputItemList) != 3 { + t.Fatalf("len(Input items) = %d, want 3", len(params.Input.OfInputItemList)) + } + + fc := params.Input.OfInputItemList[1].OfFunctionCall + if fc == nil { + t.Fatal("assistant tool call should be converted to function_call input item") + } + if fc.Name != "read_file" { + t.Errorf("Function call name = %q, want %q", fc.Name, "read_file") + } + if fc.Arguments != `{"path":"README.md"}` { + t.Errorf("Function call arguments = %q, want %q", fc.Arguments, `{"path":"README.md"}`) + } +} + func TestBuildCodexParams_WithTools(t *testing.T) { tools := []ToolDefinition{ { @@ -214,6 +256,10 @@ func TestCodexProvider_ChatRoundTrip(t *testing.T) { http.Error(w, "stream must be true", http.StatusBadRequest) return } + if _, ok := reqBody["max_output_tokens"]; ok { + http.Error(w, "max_output_tokens is not supported", http.StatusBadRequest) + return + } resp := map[string]interface{}{ "id": "resp_test", @@ -293,6 +339,10 @@ func TestCodexProvider_ChatRoundTrip_TokenSourceFallbackAccountID(t *testing.T) http.Error(w, "temperature is not supported", http.StatusBadRequest) return } + if _, ok := reqBody["max_output_tokens"]; ok { + http.Error(w, "max_output_tokens is not supported", http.StatusBadRequest) + return + } if reqBody["stream"] != true { http.Error(w, "stream must be true", http.StatusBadRequest) return diff --git a/pkg/tools/cron.go b/pkg/tools/cron.go index 4b6f973d8..21bee42ef 100644 --- a/pkg/tools/cron.go +++ b/pkg/tools/cron.go @@ -28,12 +28,15 @@ type CronTool struct { } // NewCronTool creates a new CronTool -func NewCronTool(cronService *cron.CronService, executor JobExecutor, msgBus *bus.MessageBus, workspace string, restrict bool) *CronTool { +// execTimeout: 0 means no timeout, >0 sets the timeout duration +func NewCronTool(cronService *cron.CronService, executor JobExecutor, msgBus *bus.MessageBus, workspace string, restrict bool, execTimeout time.Duration) *CronTool { + execTool := NewExecTool(workspace, restrict) + execTool.SetTimeout(execTimeout) // 0 means no timeout return &CronTool{ cronService: cronService, executor: executor, msgBus: msgBus, - execTool: NewExecTool(workspace, restrict), + execTool: execTool, } } diff --git a/pkg/tools/shell.go b/pkg/tools/shell.go index 1ca3fc35a..713850f97 100644 --- a/pkg/tools/shell.go +++ b/pkg/tools/shell.go @@ -89,7 +89,14 @@ func (t *ExecTool) Execute(ctx context.Context, args map[string]interface{}) *To return ErrorResult(guardError) } - cmdCtx, cancel := context.WithTimeout(ctx, t.timeout) + // timeout == 0 means no timeout + var cmdCtx context.Context + var cancel context.CancelFunc + if t.timeout > 0 { + cmdCtx, cancel = context.WithTimeout(ctx, t.timeout) + } else { + cmdCtx, cancel = context.WithCancel(ctx) + } defer cancel() var cmd *exec.Cmd diff --git a/pkg/tools/web.go b/pkg/tools/web.go index ccd995842..6a6d40ecf 100644 --- a/pkg/tools/web.go +++ b/pkg/tools/web.go @@ -176,6 +176,71 @@ func stripTags(content string) string { return re.ReplaceAllString(content, "") } +type PerplexitySearchProvider struct { + apiKey string +} + +func (p *PerplexitySearchProvider) Search(ctx context.Context, query string, count int) (string, error) { + searchURL := "https://api.perplexity.ai/chat/completions" + + payload := map[string]interface{}{ + "model": "sonar", + "messages": []map[string]string{ + {"role": "system", "content": "You are a search assistant. Provide concise search results with titles, URLs, and brief descriptions in the following format:\n1. Title\n URL\n Description\n\nDo not add extra commentary."}, + {"role": "user", "content": fmt.Sprintf("Search for: %s. Provide up to %d relevant results.", query, count)}, + }, + "max_tokens": 1000, + } + + payloadBytes, err := json.Marshal(payload) + if err != nil { + return "", fmt.Errorf("failed to marshal request: %w", err) + } + + req, err := http.NewRequestWithContext(ctx, "POST", searchURL, strings.NewReader(string(payloadBytes))) + if err != nil { + return "", fmt.Errorf("failed to create request: %w", err) + } + + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "Bearer "+p.apiKey) + req.Header.Set("User-Agent", userAgent) + + client := &http.Client{Timeout: 30 * time.Second} + resp, err := client.Do(req) + if err != nil { + return "", fmt.Errorf("request failed: %w", err) + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return "", fmt.Errorf("failed to read response: %w", err) + } + + if resp.StatusCode != http.StatusOK { + return "", fmt.Errorf("Perplexity API error: %s", string(body)) + } + + var searchResp struct { + Choices []struct { + Message struct { + Content string `json:"content"` + } `json:"message"` + } `json:"choices"` + } + + if err := json.Unmarshal(body, &searchResp); err != nil { + return "", fmt.Errorf("failed to parse response: %w", err) + } + + if len(searchResp.Choices) == 0 { + return fmt.Sprintf("No results for: %s", query), nil + } + + return fmt.Sprintf("Results for: %s (via Perplexity)\n%s", query, searchResp.Choices[0].Message.Content), nil +} + type WebSearchTool struct { provider SearchProvider maxResults int @@ -187,14 +252,22 @@ type WebSearchToolOptions struct { BraveEnabled bool DuckDuckGoMaxResults int DuckDuckGoEnabled bool + PerplexityAPIKey string + PerplexityMaxResults int + PerplexityEnabled bool } func NewWebSearchTool(opts WebSearchToolOptions) *WebSearchTool { var provider SearchProvider maxResults := 5 - // Priority: Brave > DuckDuckGo - if opts.BraveEnabled && opts.BraveAPIKey != "" { + // Priority: Perplexity > Brave > DuckDuckGo + if opts.PerplexityEnabled && opts.PerplexityAPIKey != "" { + provider = &PerplexitySearchProvider{apiKey: opts.PerplexityAPIKey} + if opts.PerplexityMaxResults > 0 { + maxResults = opts.PerplexityMaxResults + } + } else if opts.BraveEnabled && opts.BraveAPIKey != "" { provider = &BraveSearchProvider{apiKey: opts.BraveAPIKey} if opts.BraveMaxResults > 0 { maxResults = opts.BraveMaxResults