Agent Orchestrator
April 4, 2026 · View on GitHub
VS Code extension for managing multiple AI coding agents in isolated devcontainers. Each agent gets its own git clone, container, and port range — so agents can work on different tasks in parallel without stepping on each other.
Built as a Serverpod specialist: the orchestrator understands the Serverpod project structure and automatically injects gitignored config files (secrets, environment, volume mounts) into each agent's clone.
How It Works
┌─────────────┐ launch "auth" ┌──────────────────────────┐
│ VS Code │ ──────────────────────▶│ .claude/worktrees/auth/ │ (git clone)
│ Sidebar │ │ .devcontainer/.env │ (ports + mode)
│ │ devpod up │ passwords.yaml │ (copied from host)
│ Agents: │ ──────────────────────▶│ .env │ (copied from host)
│ ● auth │ └──────────┬───────────────┘
│ ○ redesign │ │
└─────────────┘ ▼
┌──────────────────────────┐
│ DevPod Container │
│ agent-auth │
│ ports: 8180, 8181, ... │
└──────────────────────────┘
- Launch — creates a
git cloneon branchagent/{name}, allocates unique ports, writes.devcontainer/.env, copies host secrets - Open in Container — runs
devpod upto build and start the container with an IDE - Shell — ensures the container is running, then opens an SSH session
- Diff / PR / Merge — review changes, create PRs, merge back when done
- Clean — removes clone, container, branch, and metadata
Prerequisites
| Tool | Purpose |
|---|---|
| DevPod | Container lifecycle management |
| Git | Clone and branch management |
GitHub CLI (gh) | PR creation and lookup |
| Docker (or compatible runtime) | Container backend for DevPod |
Installation
Install from a .vsix file:
npm run build # produces build/agent-orchestrator-X.Y.Z.vsix
code --install-extension build/agent-orchestrator-*.vsix
Or publish and install from a marketplace:
npm run publish-vscode # VS Code Marketplace
npm run publish-openvsx # Open VSX Registry
Commands
All commands are available from the sidebar context menu and the command palette (Cmd+Shift+P).
| Command | Description |
|---|---|
| Launch Agent | Create a new agent with isolated clone and ports |
| Open in Container | Start container and open IDE via DevPod |
| Open Agent Shell | Start container if needed, then SSH into it |
| Show Agent Diff | Side-by-side diff of agent's changes vs base branch |
| Show / Create PR | Open existing PR or create a new one |
| Merge Agent Branch | Merge agent's branch into a target branch |
| Stop Agent | Stop the container (preserves data) |
| Clean Agent | Remove everything: clone, container, branch, metadata |
| Clean All Agents | Clean all agents at once |
| Refresh Agent Status | Manually refresh the sidebar |
Sidebar
The Agent Orchestrator panel in the sidebar shows all agents with status icons:
●Running — container is up○Stopped — container exists but is not running⚠Not Found — no container (needsOpen in Container)
Expand an agent to see:
- Branch —
agent/{name}(from{baseBranch}) - Status — running / stopped / not found
- Changed — number of modified files vs base branch
- PR — link to GitHub PR (if one exists)
- Ports — allocated API, Web, and DB ports
Configuration
Settings under agentOrchestrator.* in VS Code:
| Setting | Default | Description |
|---|---|---|
defaultBaseBranch | master | Base branch for new agents |
portStep | 100 | Port increment between agents |
refreshIntervalSeconds | 15 | Auto-refresh interval (5–300s) |
worktreeDir | .claude/worktrees | Directory for git clones (relative to workspace) |
agentsDir | .claude/agents | Directory for metadata JSON files (relative to workspace) |
Port Allocation
Each agent gets a unique port range to avoid conflicts. Offset 0 is reserved for the main project's devcontainer.
Formula: port = base_port + (offset * portStep)
Project-specific port maps
Each project declares its port layout in .devcontainer/port-map.jsonc:
{
// Each key is the env var name injected into docker-compose via .env.
// Each value is the base port (offset 0 = host mode).
// Agent ports are computed as: base + (offset * portStep).
"API_PORT": 8080,
"INSIGHTS_PORT": 8081,
"WEB_PORT": 8082,
"FLUTTER_DEBUG_PORT": 8083,
"POSTGRES_PORT": 8090,
"REDIS_PORT": 8091
}
The orchestrator reads this file at activation. If the file is missing, it falls back to the built-in defaults shown below.
Using distinct port ranges per project allows multiple devcontainer stacks to run simultaneously without port conflicts.
Default port map
Used when no port-map.jsonc is present:
| Port | Offset 0 (main) | Offset 1 (agent 1) | Offset 2 (agent 2) |
|---|---|---|---|
| API | 8080 | 8180 | 8280 |
| Insights | 8081 | 8181 | 8281 |
| Web | 8082 | 8182 | 8282 |
| Flutter Debug | 8083 | 8183 | 8283 |
| PostgreSQL | 8090 | 8190 | 8290 |
| RustFS API | 9000 | 9100 | 9200 |
| RustFS Console | 9001 | 9101 | 9201 |
Host File Injection
When launching an agent, the orchestrator copies gitignored files from the host project into the clone so the container has a working environment:
| File | Purpose |
|---|---|
easybiz_server/config/passwords.yaml | Serverpod secrets (API keys, DB passwords, SMTP, S3) |
.env | Root environment variables (e.g. OLLAMA_CLOUD_API_KEY) |
.devcontainer/docker-compose.override.yml | Generated with absolute path for plugins volume mount |
The .devcontainer/.env is also written with:
DEVCONTAINER_MODE=agent— tellsinit.shto run in agent modePROJECT_NAME=agent_{name}— unique Postgres database per agent- Port assignments (see table above)
Architecture
src/
├── extension.ts # Activation, command registration, refresh timer
├── config.ts # VS Code settings reader
├── types.ts # Interfaces and enums
├── outputChannel.ts # Output panel logging
├── statusBar.ts # "X/Y agents" status bar item
├── baseFileProvider.ts # orch-base:// scheme for diff viewer
├── services/
│ ├── agentManager.ts # Main orchestrator (launch, clean, diff, etc.)
│ ├── devpodService.ts # DevPod CLI wrapper
│ ├── gitService.ts # Git operations (clone, branch, diff, merge)
│ ├── ghService.ts # GitHub CLI wrapper (PR list/create)
│ ├── metadataStore.ts # JSON file persistence per agent
│ └── portAllocator.ts # Port allocation and .env generation
├── commands/
│ ├── launch.ts # Launch Agent
│ ├── openContainer.ts # Open in Container
│ ├── shell.ts # Open Agent Shell
│ ├── diff.ts # Show Agent Diff
│ ├── pr.ts # Show / Create PR
│ ├── merge.ts # Merge Agent Branch
│ ├── stop.ts # Stop Agent
│ ├── clean.ts # Clean Agent / Clean All
│ └── utils.ts # Agent picker helper
└── views/
└── agentTreeProvider.ts # Sidebar tree view
Development
npm install # Install dependencies
npm run esbuild-watch # Watch mode (rebuilds on save)
# Press F5 in VS Code to launch Extension Development Host
npm run lint # ESLint
npm run compile # TypeScript type check
npm run build # Package .vsix to build/
Design Decisions
Git clones over worktrees — Worktrees store .git as a file pointing to the main repo via host-relative paths, which break inside containers. Clones have independent .git/ directories that work identically on host and in containers.
Relative paths for DevPod — DevPod interprets absolute and bare relative paths as git URLs. The ./ prefix signals "local folder".
Underscores in PROJECT_NAME — Agent names use kebab-case (auth-feature) but PROJECT_NAME uses underscores (agent_auth_feature) because hyphens are invalid SQL identifiers in PostgreSQL.
Serverpod specialist — The orchestrator knows about Serverpod's file layout (passwords.yaml, config structure, docker-compose) rather than being tech-stack agnostic. This eliminates the need for generic hook mechanisms and makes the injection reliable.
Per-project port maps — Different projects have different services (object storage, Redis, test databases, etc.), so the port layout can't be one-size-fits-all. Each project declares its ports in .devcontainer/port-map.jsonc, and the orchestrator applies the offset formula generically. A built-in default covers projects that don't provide the file.
License
MIT