mirror of
https://github.com/cexll/myclaude.git
synced 2026-02-14 03:31:58 +08:00
fix(codeagent-wrapper): add timeout for Windows process termination
- Add forceKillWaitTimeout (5s) to prevent cmd.Wait() blocking forever - Enhance sendTermSignal with killProcessTree fallback using wmic - Update omo README: remove sisyphus, fix model names, update config Fixes #115 Generated with SWE-Agent.ai Co-Authored-By: SWE-Agent.ai <noreply@swe-agent.ai>
This commit is contained in:
@@ -17,6 +17,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const postMessageTerminateDelay = 1 * time.Second
|
const postMessageTerminateDelay = 1 * time.Second
|
||||||
|
const forceKillWaitTimeout = 5 * time.Second
|
||||||
|
|
||||||
// commandRunner abstracts exec.Cmd for testability
|
// commandRunner abstracts exec.Cmd for testability
|
||||||
type commandRunner interface {
|
type commandRunner interface {
|
||||||
@@ -1109,7 +1110,8 @@ func runCodexTaskWithContext(parentCtx context.Context, taskSpec TaskSpec, backe
|
|||||||
waitLoop:
|
waitLoop:
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case waitErr = <-waitCh:
|
case err := <-waitCh:
|
||||||
|
waitErr = err
|
||||||
break waitLoop
|
break waitLoop
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
ctxCancelled = true
|
ctxCancelled = true
|
||||||
@@ -1120,8 +1122,17 @@ waitLoop:
|
|||||||
terminated = true
|
terminated = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
waitErr = <-waitCh
|
for {
|
||||||
break waitLoop
|
select {
|
||||||
|
case err := <-waitCh:
|
||||||
|
waitErr = err
|
||||||
|
break waitLoop
|
||||||
|
case <-time.After(forceKillWaitTimeout):
|
||||||
|
if proc := cmd.Process(); proc != nil {
|
||||||
|
_ = proc.Kill()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
case <-messageTimerCh:
|
case <-messageTimerCh:
|
||||||
forcedAfterComplete = true
|
forcedAfterComplete = true
|
||||||
messageTimerCh = nil
|
messageTimerCh = nil
|
||||||
@@ -1135,8 +1146,17 @@ waitLoop:
|
|||||||
// Close pipes to unblock stream readers, then wait for process exit.
|
// Close pipes to unblock stream readers, then wait for process exit.
|
||||||
closeWithReason(stdout, "terminate")
|
closeWithReason(stdout, "terminate")
|
||||||
closeWithReason(stderr, "terminate")
|
closeWithReason(stderr, "terminate")
|
||||||
waitErr = <-waitCh
|
for {
|
||||||
break waitLoop
|
select {
|
||||||
|
case err := <-waitCh:
|
||||||
|
waitErr = err
|
||||||
|
break waitLoop
|
||||||
|
case <-time.After(forceKillWaitTimeout):
|
||||||
|
if proc := cmd.Process(); proc != nil {
|
||||||
|
_ = proc.Kill()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
case <-completeSeen:
|
case <-completeSeen:
|
||||||
completeSeenObserved = true
|
completeSeenObserved = true
|
||||||
if messageTimer != nil {
|
if messageTimer != nil {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// sendTermSignal on Windows directly kills the process.
|
// sendTermSignal on Windows directly kills the process.
|
||||||
@@ -31,6 +32,56 @@ func sendTermSignal(proc processHandle) error {
|
|||||||
if err := cmd.Run(); err == nil {
|
if err := cmd.Run(); err == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
if err := killProcessTree(pid); err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return proc.Kill()
|
return proc.Kill()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func killProcessTree(pid int) error {
|
||||||
|
if pid <= 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
wmic := "wmic"
|
||||||
|
if root := os.Getenv("SystemRoot"); root != "" {
|
||||||
|
wmic = filepath.Join(root, "System32", "wbem", "WMIC.exe")
|
||||||
|
}
|
||||||
|
|
||||||
|
queryChildren := "(ParentProcessId=" + strconv.Itoa(pid) + ")"
|
||||||
|
listCmd := exec.Command(wmic, "process", "where", queryChildren, "get", "ProcessId", "/VALUE")
|
||||||
|
listCmd.Stderr = io.Discard
|
||||||
|
out, err := listCmd.Output()
|
||||||
|
if err == nil {
|
||||||
|
for _, childPID := range parseWMICPIDs(out) {
|
||||||
|
_ = killProcessTree(childPID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
querySelf := "(ProcessId=" + strconv.Itoa(pid) + ")"
|
||||||
|
termCmd := exec.Command(wmic, "process", "where", querySelf, "call", "terminate")
|
||||||
|
termCmd.Stdout = io.Discard
|
||||||
|
termCmd.Stderr = io.Discard
|
||||||
|
if termErr := termCmd.Run(); termErr != nil && err == nil {
|
||||||
|
err = termErr
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseWMICPIDs(out []byte) []int {
|
||||||
|
const prefix = "ProcessId="
|
||||||
|
var pids []int
|
||||||
|
for _, line := range strings.Split(string(out), "\n") {
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
if !strings.HasPrefix(line, prefix) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
n, err := strconv.Atoi(strings.TrimSpace(strings.TrimPrefix(line, prefix)))
|
||||||
|
if err != nil || n <= 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
pids = append(pids, n)
|
||||||
|
}
|
||||||
|
return pids
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,12 @@
|
|||||||
# OmO Multi-Agent Orchestration
|
# OmO Multi-Agent Orchestration
|
||||||
|
|
||||||
OmO (Oh-My-OpenCode) is a multi-agent orchestration skill that uses Sisyphus as the primary coordinator to delegate tasks to specialized agents.
|
OmO (Oh-My-OpenCode) is a multi-agent orchestration skill that delegates tasks to specialized agents based on routing signals.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 install.py --module omo
|
||||||
|
```
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
@@ -12,19 +18,17 @@ OmO (Oh-My-OpenCode) is a multi-agent orchestration skill that uses Sisyphus as
|
|||||||
|
|
||||||
| Agent | Role | Backend | Model |
|
| Agent | Role | Backend | Model |
|
||||||
|-------|------|---------|-------|
|
|-------|------|---------|-------|
|
||||||
| sisyphus | Primary orchestrator | claude | claude-sonnet-4-20250514 |
|
| oracle | Technical advisor | claude | claude-opus-4-5-20251101 |
|
||||||
| oracle | Technical advisor (EXPENSIVE) | claude | claude-sonnet-4-20250514 |
|
| librarian | External research | claude | claude-sonnet-4-5-20250929 |
|
||||||
| librarian | External research | claude | claude-sonnet-4-5-20250514 |
|
| explore | Codebase search | opencode | opencode/grok-code |
|
||||||
| explore | Codebase search (FREE) | opencode | opencode/grok-code |
|
| develop | Code implementation | codex | gpt-5.2 |
|
||||||
| develop | Code implementation | codex | (default) |
|
| frontend-ui-ux-engineer | UI/UX specialist | gemini | gemini-3-pro-high |
|
||||||
| frontend-ui-ux-engineer | UI/UX specialist | gemini | gemini-3-pro-preview |
|
| document-writer | Documentation | gemini | gemini-3-flash |
|
||||||
| document-writer | Documentation | gemini | gemini-3-flash-preview |
|
|
||||||
|
|
||||||
## How It Works
|
## How It Works
|
||||||
|
|
||||||
1. `/omo` loads Sisyphus as the entry point
|
1. `/omo` analyzes your request via routing signals
|
||||||
2. Sisyphus analyzes your request via routing signals
|
2. Based on task type, it either:
|
||||||
3. Based on task type, Sisyphus either:
|
|
||||||
- Answers directly (analysis/explanation tasks - no code changes)
|
- Answers directly (analysis/explanation tasks - no code changes)
|
||||||
- Delegates to specialized agents (implementation tasks)
|
- Delegates to specialized agents (implementation tasks)
|
||||||
- Fires parallel agents (exploration + research)
|
- Fires parallel agents (exploration + research)
|
||||||
@@ -44,7 +48,7 @@ OmO (Oh-My-OpenCode) is a multi-agent orchestration skill that uses Sisyphus as
|
|||||||
|
|
||||||
## Agent Delegation
|
## Agent Delegation
|
||||||
|
|
||||||
Sisyphus delegates via codeagent-wrapper with full Context Pack:
|
Delegates via codeagent-wrapper with full Context Pack:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
codeagent-wrapper --agent oracle - . <<'EOF'
|
codeagent-wrapper --agent oracle - . <<'EOF'
|
||||||
@@ -70,11 +74,43 @@ Agent-model mappings are configured in `~/.codeagent/models.json`:
|
|||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"default_backend": "opencode",
|
"default_backend": "codex",
|
||||||
"default_model": "opencode/grok-code",
|
"default_model": "gpt-5.2",
|
||||||
"agents": {
|
"agents": {
|
||||||
"sisyphus": {"backend": "claude", "model": "claude-sonnet-4-20250514"},
|
"oracle": {
|
||||||
"oracle": {"backend": "claude", "model": "claude-sonnet-4-20250514"}
|
"backend": "claude",
|
||||||
|
"model": "claude-opus-4-5-20251101",
|
||||||
|
"description": "Technical advisor",
|
||||||
|
"yolo": true
|
||||||
|
},
|
||||||
|
"librarian": {
|
||||||
|
"backend": "claude",
|
||||||
|
"model": "claude-sonnet-4-5-20250929",
|
||||||
|
"description": "Researcher",
|
||||||
|
"yolo": true
|
||||||
|
},
|
||||||
|
"explore": {
|
||||||
|
"backend": "opencode",
|
||||||
|
"model": "opencode/grok-code",
|
||||||
|
"description": "Code search"
|
||||||
|
},
|
||||||
|
"frontend-ui-ux-engineer": {
|
||||||
|
"backend": "gemini",
|
||||||
|
"model": "gemini-3-pro-high",
|
||||||
|
"description": "Frontend engineer"
|
||||||
|
},
|
||||||
|
"document-writer": {
|
||||||
|
"backend": "gemini",
|
||||||
|
"model": "gemini-3-flash",
|
||||||
|
"description": "Documentation"
|
||||||
|
},
|
||||||
|
"develop": {
|
||||||
|
"backend": "codex",
|
||||||
|
"model": "gpt-5.2",
|
||||||
|
"description": "codex develop",
|
||||||
|
"yolo": true,
|
||||||
|
"reasoning": "xhigh"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -82,4 +118,4 @@ Agent-model mappings are configured in `~/.codeagent/models.json`:
|
|||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
- codeagent-wrapper with `--agent` support
|
- codeagent-wrapper with `--agent` support
|
||||||
- Backend CLIs: claude, opencode, gemini
|
- Backend CLIs: claude, opencode, codex, gemini
|
||||||
|
|||||||
Reference in New Issue
Block a user