How to Set Up OmniRoute Cloud Agents with Task Lifecycle Management
OmniRoute provides a lightweight Cloud-Agent framework that standardizes autonomous LLM-backed services through a four-step lifecycle (create, poll, approve, message) implemented via the abstract CloudAgentBase class in src/lib/cloudAgent/baseAgent.ts.
OmniRoute ships a production-ready cloud agent subsystem that unifies autonomous coding agents like Jules, Devin, and Codex under a single task lifecycle management interface. Located in src/lib/cloudAgent/, this framework lets you orchestrate remote LLM workers using consistent TypeScript abstractions and REST endpoints according to the diegosouzapw/OmniRoute source code.
Understanding the Cloud Agent Architecture
The Base Contract (CloudAgentBase)
All cloud agents inherit from CloudAgentBase defined in src/lib/cloudAgent/baseAgent.ts. This abstract class enforces a uniform contract across providers, ensuring that Jules, Devin, Codex, or custom agents behave identically within your OmniRoute installation.
The base class provides the mapStatus helper method, which normalizes provider-specific status strings (e.g., "in_progress", "pending") into canonical CloudAgentStatus values. This guarantees consistent UI rendering regardless of which LLM service powers the agent.
Provider Registry System
Concrete agent implementations are registered in src/lib/cloudAgent/registry.ts. This registry maps provider identifiers (e.g., "jules", "devin", "codex") to their respective classes. When the API receives a request for a specific provider, the registry instantiates the correct agent class and injects the necessary credentials via getCredentialsForProvider().
The Four-Step Task Lifecycle
The framework implements a standardized four-step lifecycle through abstract methods that each concrete agent must implement.
Step 1: Creating Tasks with createTask()
The createTask(params, credentials) method initiates work on the remote service. It transmits the prompt, metadata, and configuration options to the provider's endpoint, returning a CloudAgentTask record containing a generated externalId. This external ID serves as the persistent handle for all subsequent lifecycle operations.
Step 2: Polling Status via getStatus()
Use getStatus(externalId, credentials) to query the remote service for current state. The method returns standardized status values (queued, running, awaiting_approval, completed, failed) along with interim activities and optional results. This enables your UI or CLI to display real-time progress without provider-specific parsing logic.
Step 3: Approving Plans with approvePlan()
When getStatus returns awaiting_approval, the task pauses pending human authorization. The approvePlan(externalId, credentials) method sends the approval signal, allowing cost-sensitive or destructive operations (like PR creation) to proceed. This implements human-in-the-loop governance for autonomous agents.
Step 4: Interactive Messaging via sendMessage()
For multi-turn conversations or incremental refinements, sendMessage(externalId, message, credentials) transmits follow-up instructions to an active task. The method appends the interaction to the task's activity log stored in src/lib/cloudAgent/db.ts, maintaining a complete audit trail of agent interactions.
Setting Up Your Environment
Configuring Agent Credentials
Each agent requires an AgentCredentials object containing apiKey and optional baseUrl. Credentials are persisted through the helper functions in src/lib/cloudAgent/credentials.ts and retrieved internally via getCredentialsForProvider(). Store these securely in the OmniRoute database before activating agents.
Registering Custom Agents
To add a new provider, create a class in src/lib/cloudAgent/agents/<name>.ts that extends CloudAgentBase and implements the four lifecycle methods. Export this class and add an entry to the AGENT_REGISTRY object in src/lib/cloudAgent/registry.ts. The HTTP facade in src/lib/cloudAgent/api.ts automatically exposes REST endpoints for your new agent under /api/v1/cloud-agents/:provider/.
Consuming the Cloud Agent API
The public API routes are implemented in src/lib/cloudAgent/api.ts and exposed through the Next.js app router at src/app/api/v1/cloud-agents/. Below is a complete TypeScript example demonstrating the full lifecycle against a local OmniRoute instance:
import fetch from "node-fetch";
const provider = "jules"; // one of the built‑in agents
const base = "http://localhost:20128/v1";
const apiKey = process.env.OMNIROUTE_API_KEY; // your OmniRoute API key
// 1️⃣ Create a new task
async function createTask() {
const res = await fetch(`${base}/cloud-agents/${provider}/tasks`, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${apiKey}`,
},
body: JSON.stringify({
prompt: "Add a new feature to the README.md file",
source: "github",
options: { autoCreatePr: true, planApprovalRequired: true },
}),
});
const { externalId } = await res.json();
return externalId;
}
// 2️⃣ Poll status (simple loop)
async function pollStatus(externalId: string) {
while (true) {
const res = await fetch(`${base}/cloud-agents/${provider}/tasks/${externalId}/status`, {
headers: { Authorization: `Bearer ${apiKey}` },
});
const data = await res.json();
console.log("Current status:", data.status);
if (["completed", "failed", "cancelled"].includes(data.status)) break;
if (data.status === "awaiting_approval") {
// 3️⃣ Approve the plan
await fetch(`${base}/cloud-agents/${provider}/tasks/${externalId}/approve`, {
method: "POST",
headers: { Authorization: `Bearer ${apiKey}` },
});
console.log("Plan approved");
}
await new Promise(r => setTimeout(r, 2000)); // wait before next poll
}
}
// 4️⃣ Send a follow‑up message (optional)
async function sendMessage(externalId: string, msg: string) {
await fetch(`${base}/cloud-agents/${provider}/tasks/${externalId}/messages`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${apiKey}`,
},
body: JSON.stringify({ message: msg }),
});
}
// Example orchestration
(async () => {
const id = await createTask();
console.log("Task created, id =", id);
await pollStatus(id);
// optional follow‑up
await sendMessage(id, "Please add unit tests for the new code.");
})();
This example demonstrates credential injection via headers, asynchronous task creation, polling loops with approval gating, and optional messaging—all routed through the standardized API surface.
Summary
- OmniRoute cloud agents follow a strict four-step lifecycle implemented in
src/lib/cloudAgent/baseAgent.ts:createTask(),getStatus(),approvePlan(), andsendMessage(). - The
CloudAgentBaseabstract class enforces provider-agnostic behavior and includes themapStatus()helper for normalizing external state to canonicalCloudAgentStatusvalues. - Concrete implementations reside in
src/lib/cloudAgent/agents/(e.g.,jules.ts,devin.ts) and register viasrc/lib/cloudAgent/registry.ts. - Credentials are managed through
src/lib/cloudAgent/credentials.tsusing theAgentCredentialsinterface. - The HTTP API layer in
src/lib/cloudAgent/api.tsexposes versioned endpoints under/api/v1/cloud-agents/with full Zod validation and authentication middleware.
Frequently Asked Questions
What is the CloudAgentBase class in OmniRoute?
CloudAgentBase is an abstract TypeScript class defined in src/lib/cloudAgent/baseAgent.ts that defines the contract all cloud agents must implement. It specifies the four lifecycle methods (createTask, getStatus, approvePlan, sendMessage) and provides the mapStatus helper to normalize provider-specific status strings into canonical values like queued, running, or awaiting_approval.
How do I add a new cloud agent provider to OmniRoute?
Create a new file in src/lib/cloudAgent/agents/<name>.ts that extends CloudAgentBase and implements the required abstract methods. Then add the provider identifier and class to the AGENT_REGISTRY object in src/lib/cloudAgent/registry.ts. The system automatically exposes REST endpoints for your agent without modifying the API layer.
What are the canonical task statuses used in the lifecycle?
The mapStatus method in CloudAgentBase normalizes all provider responses to the following canonical statuses: queued, running, awaiting_approval, completed, failed, and cancelled. These values are defined in the CloudAgentStatus type and used consistently across the API response schema.
How does human-in-the-loop approval work in OmniRoute cloud agents?
When an agent returns a plan requiring authorization (e.g., before creating an expensive PR), the getStatus method returns awaiting_approval. Client code must then call the approvePlan(externalId, credentials) method—either manually or via automated policy—to authorize continuation. This gating mechanism prevents autonomous actions until explicitly approved, implementing cost controls and safety checks.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →