Agentic orchestrator for parallel coding agents — plans tasks, spawns agents, and autonomously handles CI fixes, merge conflicts, and code reviews.
agent-fleetagent-swarmclaude-codecodex-cligit-worktreesmulti-agentorchestrationorchestratorparallel-agentsparallel-codingskillstmux
* feat: implement runtime and workspace plugins (tmux, process, worktree, clone) Implement all 4 runtime/workspace plugins for the agent orchestrator, replacing the stub files with full implementations of the Runtime and Workspace interfaces from @agent-orchestrator/core. - runtime-tmux: tmux session lifecycle with busy detection, wait-for-idle message delivery, capture-pane output, and load-buffer for long messages - runtime-process: child process management with rolling output buffer, graceful SIGTERM/SIGKILL shutdown, and stdin message delivery - workspace-worktree: git worktree create/destroy/list with symlink support for shared resources and postCreate hook execution - workspace-clone: git clone --reference for fast isolated clones with postCreate hooks Closes INT-1328 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address all PR review comments on runtime/workspace plugins Fixes for all 13 review issues: 1. Move processes Map inside create() for per-instance isolation (#1) 2. Wrap stdin.write in promise with error/backpressure handling (#2) 3. Use child.once("exit") instead of on("exit") to prevent leaks (#3) 4. Use child.once("exit") in destroy() timeout to prevent leaks (#4) 5. Add assertValidSessionId() with /^[a-zA-Z0-9_-]+$/ regex (#5) 6. Single capture-pane call in isBusy() to avoid TOCTOU (#6) 7. Throw error when sendMessage sends to busy session after timeout (#7) 8. Use crypto.randomUUID() for temp file names to avoid collisions (#8) 9. Document that postCreate commands run with full shell privileges (#9) 10. Inspect worktree add error — only retry on "already exists" (#10) 11. Delete orphaned branch after worktree remove in destroy() (#11) 12. Log warning for corrupted clones in list() instead of silent skip (#12) 13. Return "not running" attach info for exited processes (#13) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: harden runtime/workspace plugins (spawn errors, stdin races, git injection) - runtime-process: catch spawn errors with setImmediate pattern, prevent double resolve/reject in sendMessage with done-flag, add late error handler to prevent unhandled crashes, eliminate non-null assertions - runtime-tmux: guard config.environment with ?? {} fallback - workspace-worktree: add -- separator to git checkout/branch commands to prevent option injection, guard branch deletion to feature branches only (must contain /), ensure parent dirs exist before symlink creation - workspace-clone: add -- separator to git checkout commands, suppress no-console lint for expected diagnostic warning Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address codex review — spawn race, path traversal, temp file perms - runtime-process: replace setImmediate with spawn/error event listeners to fix race where create() returns handle for failed process; document intentional shell:true usage - runtime-tmux: restrict temp file permissions to 0o600 - workspace-worktree: validate projectId/sessionId as safe path segments, reject absolute/traversal symlink paths, verify resolved targets stay within workspace - workspace-clone: validate projectId/sessionId as safe path segments in both create() and list() Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address all review comments and fix CI - workspace-clone/worktree: remove incorrect -- from git checkout (switches to path mode, breaking branch operations) - workspace-worktree: fix prefix collision in list() by appending / to startsWith check (foo no longer matches foobar) - runtime-tmux: use named tmux buffers (-b flag) to prevent concurrent sendMessage calls from overwriting each other's paste content - runtime-process: check signalCode in addition to exitCode for accurate liveness detection of signal-killed processes - runtime-process: reject duplicate session IDs to prevent orphaned child processes from overwritten map entries - runtime-tmux: anchor $ in busy detection to line start (^\$\s) to avoid false idle detection from dollar signs in output Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: forward plugin config through registry so workspace dirs take effect The plugin registry was calling plugin.create() with no arguments, silently ignoring configured worktreeDir/cloneDir settings. Added extractPluginConfig() to map orchestrator config to per-plugin config, and updated register()/loadBuiltins() to pass config through. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: clean up orphaned worktrees/clones on checkout failure, remove risky branch -D - Worktree: remove git branch -D from destroy() — the "/" heuristic could delete pre-existing local branches unrelated to the workspace - Worktree: wrap fallback checkout in try/catch, clean up orphaned worktree if checkout fails - Clone: wrap fallback checkout in try/catch, rmSync the orphaned clone directory if both checkout attempts fail Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: clean up partial clone directory when git clone fails Wrap the git clone call in try/catch so a failed clone removes any partial clonePath left on disk, preventing orphaned directories that block retries for the same sessionId. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: remove busy detection from tmux runtime Busy/idle detection is an agent-layer concern, not a runtime concern. Each agent (Claude Code, Codex, Aider) has its own native mechanism for checking activity status. The tmux runtime should only manage sessions and send messages immediately without polling. Removed: isBusy() heuristics, sleep-based polling loop, sentWhileBusy error. sendMessage() now sends immediately. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: clean up tmux session when launch command send-keys fails If send-keys fails after new-session succeeds, the orphaned tmux session was left running and unmanaged. Now wraps send-keys in try/catch that kills the session before rethrowing. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: prevent clone error handler from deleting pre-existing workspace Add early existence check before git clone so create() fails immediately if clonePath already exists, rather than cloning into it, failing, and then deleting the pre-existing workspace in the catch block. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: clean up leaked tmux buffer when paste-buffer fails The -d flag on paste-buffer only deletes the named buffer on success. If paste fails, the buffer persists in the tmux server. Added delete-buffer in the finally block to ensure cleanup regardless of paste outcome. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: prevent orphan processes on duplicate sessionId, auto-remove exited sessions - Move processes.has() check before spawn() so duplicate sessionIds are rejected before any child process is created - Auto-remove exited sessions from the process map in the exit handler so the sessionId can be reused without manual destroy() Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: register process in map before exit handler to prevent stale entries A fast-exiting child could fire the exit event before processes.set() ran, causing delete() to no-op and set() to insert a permanently stale entry. Moving set() before the exit handler registration ensures delete() always finds the entry. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: atomic session reservation and immediate exit handler registration - Reserve map slot synchronously (no await gap between has() and set()) so concurrent create() calls cannot both pass the duplicate check - Register exit handler immediately after spawn(), before any await, so fast-exiting processes cannot miss cleanup Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: kill process group on destroy, use literal mode for tmux send-keys - runtime-process: use process.kill(-pid) to kill the entire process group, not just the shell. Added detached:true so child gets its own process group. Falls back to child.kill() if group kill fails. - runtime-tmux: add -l flag to send-keys for short messages so text like "Enter" or "Space" is sent literally, not as key names. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: add comprehensive unit and integration tests for all 4 plugins Add 136 unit tests and 35 integration tests covering runtime-tmux, runtime-process, workspace-worktree, and workspace-clone plugins. Also adds 13 unit tests for the core plugin-registry. Unit tests (136 total): - runtime-tmux (25): create/destroy, send-keys modes, getOutput, isAlive, getMetrics - runtime-process (40): spawn lifecycle, SIGKILL escalation, output buffering, isolation - workspace-worktree (40): create/destroy, tilde expansion, branch fallback, symlinks - workspace-clone (31): create/destroy, --reference clone, pre-existence check, cleanup Integration tests (35 total): - runtime-tmux (8): full lifecycle with real tmux sessions - runtime-process (11): full lifecycle with real child processes - workspace-worktree (8): real git worktree operations - workspace-clone (8): real git clone operations Plugin-registry tests (13): - register/get/list, config forwarding, slot isolation, loadBuiltins, loadFromConfig Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: guard against null process in runtime-process methods ProcessEntry.process is null between reservation and spawn completion. Add explicit null checks in destroy(), sendMessage(), isAlive(), and getAttachInfo() to prevent TypeError if called during that window. Addresses bugbot review comment about transient null process crash. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: buffer partial lines in runtime-process output capture Stream chunks split at arbitrary boundaries, so splitting on "\n" and pushing each piece as a complete line corrupts output when a line spans two chunks. Buffer the trailing partial line and only push complete (newline-terminated) lines. Flush remaining partial on process exit. Addresses bugbot review comment about incorrect output chunk splitting. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: correct vitest peer dep resolution in pnpm-lock.yaml The lockfile referenced vitest@3.2.4 without jiti/jsdom/lightningcss peer deps, but that resolution didn't exist in the packages section. Update to match the full resolution available on main. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: use per-stream partial buffers in runtime-process output capture stdout and stderr shared one partial-line buffer, so interleaved chunks from different streams could be concatenated into corrupted lines. Each stream now gets its own closure with an independent partial buffer. Addresses bugbot review comment about mixed stream chunk corruption. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |
||
|---|---|---|
| .cursor | ||
| .github/workflows | ||
| artifacts | ||
| packages | ||
| scripts | ||
| .gitignore | ||
| .prettierignore | ||
| .prettierrc | ||
| CLAUDE.md | ||
| CLAUDE.orchestrator.md | ||
| agent-orchestrator.yaml.example | ||
| eslint.config.js | ||
| package.json | ||
| pnpm-lock.yaml | ||
| pnpm-workspace.yaml | ||
| tsconfig.base.json | ||