From 9f662830b110ba2c6dd7afae181fb366dc58b46c Mon Sep 17 00:00:00 2001 From: dj-oyu <68707227+dj-oyu@users.noreply.github.com> Date: Wed, 4 Mar 2026 17:54:40 +0900 Subject: [PATCH] Fix CI: restore onboard embedded workspace and harden timeout test --- .../internal/onboard/workspace/AGENTS.md | 12 ++++ .../internal/onboard/workspace/IDENTITY.md | 56 +++++++++++++++++++ .../internal/onboard/workspace/SOUL.md | 17 ++++++ .../internal/onboard/workspace/USER.md | 21 +++++++ .../onboard/workspace/memory/MEMORY.md | 21 +++++++ pkg/tools/shell_process_unix.go | 53 ++++++++++++++++++ pkg/tools/shell_timeout_unix_test.go | 27 ++++++++- 7 files changed, 204 insertions(+), 3 deletions(-) create mode 100644 cmd/picoclaw/internal/onboard/workspace/AGENTS.md create mode 100644 cmd/picoclaw/internal/onboard/workspace/IDENTITY.md create mode 100644 cmd/picoclaw/internal/onboard/workspace/SOUL.md create mode 100644 cmd/picoclaw/internal/onboard/workspace/USER.md create mode 100644 cmd/picoclaw/internal/onboard/workspace/memory/MEMORY.md diff --git a/cmd/picoclaw/internal/onboard/workspace/AGENTS.md b/cmd/picoclaw/internal/onboard/workspace/AGENTS.md new file mode 100644 index 000000000..5f5fa6480 --- /dev/null +++ b/cmd/picoclaw/internal/onboard/workspace/AGENTS.md @@ -0,0 +1,12 @@ +# Agent Instructions + +You are a helpful AI assistant. Be concise, accurate, and friendly. + +## Guidelines + +- Always explain what you're doing before taking actions +- Ask for clarification when request is ambiguous +- Use tools to help accomplish tasks +- Remember important information in your memory files +- Be proactive and helpful +- Learn from user feedback \ No newline at end of file diff --git a/cmd/picoclaw/internal/onboard/workspace/IDENTITY.md b/cmd/picoclaw/internal/onboard/workspace/IDENTITY.md new file mode 100644 index 000000000..dabb0e14b --- /dev/null +++ b/cmd/picoclaw/internal/onboard/workspace/IDENTITY.md @@ -0,0 +1,56 @@ +# Identity + +## Name +PicoClaw 🦞 + +## Description +Ultra-lightweight personal AI assistant written in Go, inspired by nanobot. + +## Version +0.1.0 + +## Purpose +- Provide intelligent AI assistance with minimal resource usage +- Support multiple LLM providers (OpenAI, Anthropic, Zhipu, etc.) +- Enable easy customization through skills system +- Run on minimal hardware ($10 boards, <10MB RAM) + +## Capabilities + +- Web search and content fetching +- File system operations (read, write, edit) +- Shell command execution +- Multi-channel messaging (Telegram, WhatsApp, Feishu) +- Skill-based extensibility +- Memory and context management + +## Philosophy + +- Simplicity over complexity +- Performance over features +- User control and privacy +- Transparent operation +- Community-driven development + +## Goals + +- Provide a fast, lightweight AI assistant +- Support offline-first operation where possible +- Enable easy customization and extension +- Maintain high quality responses +- Run efficiently on constrained hardware + +## License +MIT License - Free and open source + +## Repository +https://github.com/sipeed/picoclaw + +## Contact +Issues: https://github.com/sipeed/picoclaw/issues +Discussions: https://github.com/sipeed/picoclaw/discussions + +--- + +"Every bit helps, every bit matters." +- Picoclaw \ No newline at end of file diff --git a/cmd/picoclaw/internal/onboard/workspace/SOUL.md b/cmd/picoclaw/internal/onboard/workspace/SOUL.md new file mode 100644 index 000000000..0be8834f5 --- /dev/null +++ b/cmd/picoclaw/internal/onboard/workspace/SOUL.md @@ -0,0 +1,17 @@ +# Soul + +I am picoclaw, a lightweight AI assistant powered by AI. + +## Personality + +- Helpful and friendly +- Concise and to the point +- Curious and eager to learn +- Honest and transparent + +## Values + +- Accuracy over speed +- User privacy and safety +- Transparency in actions +- Continuous improvement \ No newline at end of file diff --git a/cmd/picoclaw/internal/onboard/workspace/USER.md b/cmd/picoclaw/internal/onboard/workspace/USER.md new file mode 100644 index 000000000..91398a019 --- /dev/null +++ b/cmd/picoclaw/internal/onboard/workspace/USER.md @@ -0,0 +1,21 @@ +# User + +Information about user goes here. + +## Preferences + +- Communication style: (casual/formal) +- Timezone: (your timezone) +- Language: (your preferred language) + +## Personal Information + +- Name: (optional) +- Location: (optional) +- Occupation: (optional) + +## Learning Goals + +- What the user wants to learn from AI +- Preferred interaction style +- Areas of interest \ No newline at end of file diff --git a/cmd/picoclaw/internal/onboard/workspace/memory/MEMORY.md b/cmd/picoclaw/internal/onboard/workspace/memory/MEMORY.md new file mode 100644 index 000000000..265271db9 --- /dev/null +++ b/cmd/picoclaw/internal/onboard/workspace/memory/MEMORY.md @@ -0,0 +1,21 @@ +# Long-term Memory + +This file stores important information that should persist across sessions. + +## User Information + +(Important facts about user) + +## Preferences + +(User preferences learned over time) + +## Important Notes + +(Things to remember) + +## Configuration + +- Model preferences +- Channel settings +- Skills enabled \ No newline at end of file diff --git a/pkg/tools/shell_process_unix.go b/pkg/tools/shell_process_unix.go index 7b29a81bf..fa96d75da 100644 --- a/pkg/tools/shell_process_unix.go +++ b/pkg/tools/shell_process_unix.go @@ -3,7 +3,10 @@ package tools import ( + "os" "os/exec" + "strconv" + "strings" "syscall" ) @@ -26,7 +29,57 @@ func terminateProcessTree(cmd *exec.Cmd) error { // Kill the entire process group spawned by the shell command. _ = syscall.Kill(-pid, syscall.SIGKILL) + // Some shells/background jobs may still leave descendants around + // briefly; aggressively walk /proc and kill child processes too. + killDescendants(pid) // Fallback kill on the shell process itself. _ = cmd.Process.Kill() return nil } + +func killDescendants(ppid int) { + if ppid <= 0 { + return + } + + entries, err := os.ReadDir("/proc") + if err != nil { + return + } + + for _, e := range entries { + if !e.IsDir() { + continue + } + childPID, err := strconv.Atoi(e.Name()) + if err != nil || childPID <= 0 || childPID == ppid { + continue + } + + statPath := "/proc/" + e.Name() + "/stat" + data, err := os.ReadFile(statPath) + if err != nil { + continue + } + + // /proc//stat: pid (comm) state ppid ... + raw := string(data) + end := strings.LastIndex(raw, ")") + if end == -1 || end+2 >= len(raw) { + continue + } + fields := strings.Fields(raw[end+2:]) + if len(fields) < 2 { + continue + } + parent, err := strconv.Atoi(fields[1]) + if err != nil || parent != ppid { + continue + } + + // Recurse first, then kill child process/group. + killDescendants(childPID) + _ = syscall.Kill(-childPID, syscall.SIGKILL) + _ = syscall.Kill(childPID, syscall.SIGKILL) + } +} diff --git a/pkg/tools/shell_timeout_unix_test.go b/pkg/tools/shell_timeout_unix_test.go index 357e1276e..d0d4a5b9b 100644 --- a/pkg/tools/shell_timeout_unix_test.go +++ b/pkg/tools/shell_timeout_unix_test.go @@ -13,12 +13,33 @@ import ( "time" ) -func processExists(pid int) bool { +func processRunning(pid int) bool { if pid <= 0 { return false } + // kill(0) can return success for zombie processes too, so inspect /proc + // state and treat zombies as not-running for timeout cleanup assertions. err := syscall.Kill(pid, 0) - return err == nil || err == syscall.EPERM + if err != nil && err != syscall.EPERM { + return false + } + + data, readErr := os.ReadFile("/proc/" + strconv.Itoa(pid) + "/stat") + if readErr != nil { + return false + } + raw := string(data) + end := strings.LastIndex(raw, ")") + if end == -1 || end+2 >= len(raw) { + return true // best effort fallback + } + fields := strings.Fields(raw[end+2:]) + if len(fields) == 0 { + return true // best effort fallback + } + + state := fields[0] + return state != "Z" } func TestShellTool_TimeoutKillsChildProcess(t *testing.T) { @@ -55,7 +76,7 @@ func TestShellTool_TimeoutKillsChildProcess(t *testing.T) { deadline := time.Now().Add(2 * time.Second) for time.Now().Before(deadline) { - if !processExists(childPID) { + if !processRunning(childPID) { return } time.Sleep(50 * time.Millisecond)