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:
cexll
2026-01-14 10:43:25 +08:00
parent 4395c5785d
commit 8f05626075
3 changed files with 129 additions and 22 deletions

View File

@@ -17,6 +17,7 @@ import (
)
const postMessageTerminateDelay = 1 * time.Second
const forceKillWaitTimeout = 5 * time.Second
// commandRunner abstracts exec.Cmd for testability
type commandRunner interface {
@@ -1109,7 +1110,8 @@ func runCodexTaskWithContext(parentCtx context.Context, taskSpec TaskSpec, backe
waitLoop:
for {
select {
case waitErr = <-waitCh:
case err := <-waitCh:
waitErr = err
break waitLoop
case <-ctx.Done():
ctxCancelled = true
@@ -1120,8 +1122,17 @@ waitLoop:
terminated = true
}
}
waitErr = <-waitCh
break waitLoop
for {
select {
case err := <-waitCh:
waitErr = err
break waitLoop
case <-time.After(forceKillWaitTimeout):
if proc := cmd.Process(); proc != nil {
_ = proc.Kill()
}
}
}
case <-messageTimerCh:
forcedAfterComplete = true
messageTimerCh = nil
@@ -1135,8 +1146,17 @@ waitLoop:
// Close pipes to unblock stream readers, then wait for process exit.
closeWithReason(stdout, "terminate")
closeWithReason(stderr, "terminate")
waitErr = <-waitCh
break waitLoop
for {
select {
case err := <-waitCh:
waitErr = err
break waitLoop
case <-time.After(forceKillWaitTimeout):
if proc := cmd.Process(); proc != nil {
_ = proc.Kill()
}
}
}
case <-completeSeen:
completeSeenObserved = true
if messageTimer != nil {

View File

@@ -9,6 +9,7 @@ import (
"os/exec"
"path/filepath"
"strconv"
"strings"
)
// sendTermSignal on Windows directly kills the process.
@@ -31,6 +32,56 @@ func sendTermSignal(proc processHandle) error {
if err := cmd.Run(); err == nil {
return nil
}
if err := killProcessTree(pid); err == nil {
return nil
}
}
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
}

View File

@@ -1,6 +1,12 @@
# 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
@@ -12,19 +18,17 @@ OmO (Oh-My-OpenCode) is a multi-agent orchestration skill that uses Sisyphus as
| Agent | Role | Backend | Model |
|-------|------|---------|-------|
| sisyphus | Primary orchestrator | claude | claude-sonnet-4-20250514 |
| oracle | Technical advisor (EXPENSIVE) | claude | claude-sonnet-4-20250514 |
| librarian | External research | claude | claude-sonnet-4-5-20250514 |
| explore | Codebase search (FREE) | opencode | opencode/grok-code |
| develop | Code implementation | codex | (default) |
| frontend-ui-ux-engineer | UI/UX specialist | gemini | gemini-3-pro-preview |
| document-writer | Documentation | gemini | gemini-3-flash-preview |
| oracle | Technical advisor | claude | claude-opus-4-5-20251101 |
| librarian | External research | claude | claude-sonnet-4-5-20250929 |
| explore | Codebase search | opencode | opencode/grok-code |
| develop | Code implementation | codex | gpt-5.2 |
| frontend-ui-ux-engineer | UI/UX specialist | gemini | gemini-3-pro-high |
| document-writer | Documentation | gemini | gemini-3-flash |
## How It Works
1. `/omo` loads Sisyphus as the entry point
2. Sisyphus analyzes your request via routing signals
3. Based on task type, Sisyphus either:
1. `/omo` analyzes your request via routing signals
2. Based on task type, it either:
- Answers directly (analysis/explanation tasks - no code changes)
- Delegates to specialized agents (implementation tasks)
- 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
Sisyphus delegates via codeagent-wrapper with full Context Pack:
Delegates via codeagent-wrapper with full Context Pack:
```bash
codeagent-wrapper --agent oracle - . <<'EOF'
@@ -70,11 +74,43 @@ Agent-model mappings are configured in `~/.codeagent/models.json`:
```json
{
"default_backend": "opencode",
"default_model": "opencode/grok-code",
"default_backend": "codex",
"default_model": "gpt-5.2",
"agents": {
"sisyphus": {"backend": "claude", "model": "claude-sonnet-4-20250514"},
"oracle": {"backend": "claude", "model": "claude-sonnet-4-20250514"}
"oracle": {
"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
- codeagent-wrapper with `--agent` support
- Backend CLIs: claude, opencode, gemini
- Backend CLIs: claude, opencode, codex, gemini