localTools = ToolRegistry.fromInstance(new ReportTools());
- // HTTP tool — executes server-side via Conductor HttpTask
- ToolDef weatherApi = HttpTool.builder()
- .name("get_current_weather")
- .description("Get current weather for a city from the weather API")
- .url("http://localhost:3001/mcp")
- .method("POST")
- .accept("text/event-stream", "application/json")
- .contentType("application/json")
+ // HTTP tool — uses the regular REST endpoint on the same test server, not /mcp.
+ ToolDef httpWeather = HttpTool.builder()
+ .name("get_current_weather_http")
+ .description("Get the current weather for a city through the test server's HTTP API")
+ .url("http://localhost:3001/api/weather")
+ .method("GET")
.inputSchema(Map.of(
"type", "object",
- "properties", Map.of(
- "jsonrpc", Map.of("type", "string", "const", "2.0"),
- "id", Map.of("const", 1),
- "method", Map.of("type", "string", "const", "tools/call"),
- "params", Map.of(
- "type", "object",
- "additionalProperties", false,
- "properties", Map.of(
- "name", Map.of("type", "string", "const", "get_current_weather"),
- "arguments", Map.of(
- "type", "object",
- "additionalProperties", false,
- "properties", Map.of("city", Map.of("type", "string")),
- "required", List.of("city")
- )
- ),
- "required", List.of("name", "arguments")
- )
- ),
- "required", List.of("jsonrpc", "id", "method", "params")
+ "properties", Map.of("city", Map.of("type", "string")),
+ "required", List.of("city")
))
.build();
- // MCP tool — discovered from MCP server at runtime
+ // MCP tool — Conductor discovers and calls the weather tools through the MCP protocol.
ToolDef mcpWeather = McpTool.builder()
- .name("github")
- .description("GitHub operations via MCP")
+ .name("weather_mcp")
+ .description("Weather tools for retrieving the current weather by city")
.serverUrl("http://localhost:3001/mcp")
.build();
Agent agent = Agent.builder()
.name("api_assistant")
.model(Settings.LLM_MODEL)
- .instructions("You have access to weather data, GitHub, and report formatting.")
- .tools(localTools)
- .tools(weatherApi)
+ .instructions("Use weather_mcp for weather requests.")
+ .tools(mcpWeather)
.maxTokens(102040)
.build();
AgentResult result = runtime.run(agent,
- "Get the weather in London and format it as a report.");
+ "Get the weather in London");
result.printResult();
runtime.shutdown();
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example108PlanExecuteRefs.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example108PlanExecuteRefs.java
index e5d87e3fb..0d438e118 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example108PlanExecuteRefs.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example108PlanExecuteRefs.java
@@ -12,16 +12,11 @@
*/
package org.conductoross.conductor.ai.examples;
-import java.net.URI;
-import java.net.http.HttpClient;
-import java.net.http.HttpRequest;
-import java.net.http.HttpResponse;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.conductoross.conductor.ai.Agent;
-import org.conductoross.conductor.ai.AgentConfig;
import org.conductoross.conductor.ai.AgentRuntime;
import org.conductoross.conductor.ai.enums.Strategy;
import org.conductoross.conductor.ai.model.AgentResult;
@@ -31,8 +26,16 @@
import org.conductoross.conductor.ai.plans.Ref;
import org.conductoross.conductor.ai.plans.Step;
+import com.netflix.conductor.client.http.WorkflowClient;
+import com.netflix.conductor.common.metadata.tasks.Task;
+import com.netflix.conductor.common.run.Workflow;
+
+import io.orkes.conductor.client.ApiClient;
+import io.orkes.conductor.client.OrkesClients;
+
import com.fasterxml.jackson.databind.ObjectMapper;
+
/**
* 108 — Plan-Execute with cross-step output piping via {@link Ref}.
*
@@ -50,15 +53,12 @@
* to format a final summary. The plan is fully deterministic — no planner
* LLM required — because we pass it directly to {@code runtime.run}.
*
- * Run: {@code ./gradlew :examples:run -PmainClass=org.conductoross.conductor.ai.examples.Example108PlanExecuteRefs}
+ *
Run: {@code ./gradlew :agent-examples:run -PmainClass=org.conductoross.conductor.ai.examples.Example108PlanExecuteRefs}
*/
public class Example108PlanExecuteRefs {
private static final String MODEL =
- System.getenv().getOrDefault("AGENTSPAN_LLM_MODEL", "anthropic/claude-sonnet-4-6");
- private static final String BASE_URL =
- System.getenv().getOrDefault("AGENTSPAN_SERVER_URL", "http://localhost:6767/api")
- .replace("/api", "");
+ System.getenv().getOrDefault("CONDUCTOR_AGENT_LLM_MODEL", "anthropic/claude-sonnet-4-6");
public static void main(String[] args) throws Exception {
ToolDef produce = ToolDef.builder()
@@ -165,56 +165,39 @@ public static void main(String[] args) throws Exception {
.build())
.build();
- try (AgentRuntime runtime = new AgentRuntime(
- io.orkes.conductor.client.ApiClient.builder()
- .basePath(BASE_URL + "/api")
- .build(),
- new AgentConfig(100, 1))) {
+ ApiClient transport = new ApiClient();
+ WorkflowClient workflows = new OrkesClients(transport).getWorkflowClient();
+ try (AgentRuntime runtime = new AgentRuntime(transport)) {
AgentResult result = runtime.run(harness, "demo", plan);
System.out.println("status=" + result.getStatus()
+ " executionId=" + result.getExecutionId());
- showPipelineOutputs(result.getExecutionId());
+ showPipelineOutputs(workflows, result.getExecutionId());
}
}
- @SuppressWarnings("unchecked")
- private static void showPipelineOutputs(String executionId) throws Exception {
- HttpClient http = HttpClient.newHttpClient();
+ private static void showPipelineOutputs(WorkflowClient workflows, String executionId) throws Exception {
ObjectMapper mapper = new ObjectMapper();
- Map parent = fetchWorkflow(http, mapper, executionId);
+ Workflow parent = workflows.getWorkflow(executionId, true);
String subId = null;
- for (Map t : (List>) parent.getOrDefault("tasks", List.of())) {
- String ref = String.valueOf(t.getOrDefault("referenceTaskName", ""));
+ for (Task task : parent.getTasks()) {
+ String ref = task.getReferenceTaskName();
if (ref.endsWith("_plan_exec")) {
- Map out = (Map) t.get("outputData");
- subId = out == null ? null : (String) out.get("subWorkflowId");
+ subId = (String) task.getOutputData().get("subWorkflowId");
break;
}
}
if (subId == null) return;
- Map sub = fetchWorkflow(http, mapper, subId);
+ Workflow sub = workflows.getWorkflow(subId, true);
System.out.println("\n── pipeline trace (Ref data flow) ────────────────────────");
- for (Map t : (List>) sub.getOrDefault("tasks", List.of())) {
- String name = String.valueOf(t.get("taskDefName"));
+ for (Task task : sub.getTasks()) {
+ String name = task.getTaskDefName();
if (name.equals("produce") || name.equals("enrich") || name.equals("report")) {
System.out.println("\n" + name + ":");
- System.out.println(
- mapper.writerWithDefaultPrettyPrinter().writeValueAsString(t.get("outputData")));
+ System.out.println(mapper.writerWithDefaultPrettyPrinter()
+ .writeValueAsString(task.getOutputData()));
}
}
}
-
- @SuppressWarnings("unchecked")
- private static Map fetchWorkflow(
- HttpClient http, ObjectMapper mapper, String id) throws Exception {
- HttpResponse resp = http.send(
- HttpRequest.newBuilder()
- .uri(URI.create(BASE_URL + "/api/workflow/" + id + "?includeTasks=true"))
- .GET()
- .build(),
- HttpResponse.BodyHandlers.ofString());
- return mapper.readValue(resp.body(), Map.class);
- }
}
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example115PlannerContext.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example115PlannerContext.java
index 3353aeaf4..510baaa75 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example115PlannerContext.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example115PlannerContext.java
@@ -12,10 +12,6 @@
*/
package org.conductoross.conductor.ai.examples;
-import java.net.URI;
-import java.net.http.HttpClient;
-import java.net.http.HttpRequest;
-import java.net.http.HttpResponse;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -27,7 +23,12 @@
import org.conductoross.conductor.ai.model.ToolDef;
import org.conductoross.conductor.ai.plans.Context;
-import com.fasterxml.jackson.databind.ObjectMapper;
+import com.netflix.conductor.client.http.WorkflowClient;
+import com.netflix.conductor.common.metadata.tasks.Task;
+import com.netflix.conductor.common.run.Workflow;
+
+import io.orkes.conductor.client.ApiClient;
+import io.orkes.conductor.client.OrkesClients;
/**
* 115 — Plan-Execute with {@code plannerContext}: customer onboarding plan.
@@ -54,16 +55,12 @@
* Mirrors sdk/python/examples/115_plan_execute_planner_context.py and
* sdk/typescript/examples/115-plan-execute-planner-context.ts.
*
- *
Run: {@code ./gradlew :examples:run -PmainClass=org.conductoross.conductor.ai.examples.Example115PlannerContext}
+ *
Run: {@code ./gradlew :agent-examples:run -PmainClass=org.conductoross.conductor.ai.examples.Example115PlannerContext}
*/
public class Example115PlannerContext {
private static final String MODEL =
- System.getenv().getOrDefault("AGENTSPAN_LLM_MODEL", "anthropic/claude-sonnet-4-6");
- private static final String BASE_URL =
- System.getenv().getOrDefault("AGENTSPAN_SERVER_URL", "http://localhost:6767/api")
- .replace("/api", "");
-
+ System.getenv().getOrDefault("CONDUCTOR_AGENT_LLM_MODEL", "anthropic/claude-sonnet-4-6");
public static void main(String[] args) throws Exception {
// ── Onboarding tools (deterministic, no external calls) ──────
@@ -206,38 +203,26 @@ public static void main(String[] args) throws Exception {
String prompt = "Onboard customer cust-001 at tier 'enterprise'. "
+ "Use customer_id='cust-001' and tier='enterprise' for the tools.";
- try (AgentRuntime runtime = new AgentRuntime()) {
+ ApiClient transport = new ApiClient();
+ WorkflowClient workflows = new OrkesClients(transport).getWorkflowClient();
+ try (AgentRuntime runtime = new AgentRuntime(transport)) {
AgentResult result = runtime.run(harness, prompt);
System.out.println("status: " + result.getStatus());
System.out.println("output: " + result.getOutput());
- showExecutedSteps(result.getExecutionId());
+ showExecutedSteps(workflows, result.getExecutionId());
}
}
- private static void showExecutedSteps(String executionId) throws Exception {
- ObjectMapper mapper = new ObjectMapper();
- HttpClient client = HttpClient.newHttpClient();
-
- HttpRequest parentReq = HttpRequest.newBuilder()
- .uri(URI.create(BASE_URL + "/api/workflow/" + executionId + "?includeTasks=true"))
- .build();
- HttpResponse parentResp =
- client.send(parentReq, HttpResponse.BodyHandlers.ofString());
- @SuppressWarnings("unchecked")
- Map parent = mapper.readValue(parentResp.body(), Map.class);
+ private static void showExecutedSteps(WorkflowClient workflows, String executionId) {
+ Workflow parent = workflows.getWorkflow(executionId, true);
System.out.println("\n=== Executed onboarding plan ===");
- @SuppressWarnings("unchecked")
- List> parentTasks =
- (List>) parent.getOrDefault("tasks", List.of());
String subId = null;
- for (Map t : parentTasks) {
- String ref = (String) t.getOrDefault("referenceTaskName", "");
+ for (Task task : parent.getTasks()) {
+ String ref = task.getReferenceTaskName();
if (ref.endsWith("_plan_exec")) {
- @SuppressWarnings("unchecked")
- Map out = (Map) t.get("outputData");
- if (out != null) subId = (String) out.get("subWorkflowId");
+ subId = (String) task.getOutputData().get("subWorkflowId");
break;
}
}
@@ -246,27 +231,18 @@ private static void showExecutedSteps(String executionId) throws Exception {
return;
}
- HttpRequest subReq = HttpRequest.newBuilder()
- .uri(URI.create(BASE_URL + "/api/workflow/" + subId + "?includeTasks=true"))
- .build();
- HttpResponse subResp =
- client.send(subReq, HttpResponse.BodyHandlers.ofString());
- @SuppressWarnings("unchecked")
- Map sub = mapper.readValue(subResp.body(), Map.class);
- @SuppressWarnings("unchecked")
- List> subTasks =
- (List>) sub.getOrDefault("tasks", List.of());
+ Workflow sub = workflows.getWorkflow(subId, true);
java.util.Set expected = java.util.Set.of(
"validate_kyc", "create_account", "send_welcome_email", "schedule_kickoff_call");
int count = 0;
boolean sawKickoff = false;
- for (Map t : subTasks) {
- String name = (String) t.getOrDefault("taskDefName", "");
+ for (Task task : sub.getTasks()) {
+ String name = task.getTaskDefName();
if (expected.contains(name)) {
count++;
if ("schedule_kickoff_call".equals(name)) sawKickoff = true;
- System.out.printf(" %-10s %s%n", t.get("status"), name);
+ System.out.printf(" %-10s %s%n", task.getStatus(), name);
}
}
System.out.println(" " + count + " step(s) executed");
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example16CredentialsTool.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example16CredentialsTool.java
index febe23001..1ca3c2b0c 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example16CredentialsTool.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example16CredentialsTool.java
@@ -42,11 +42,11 @@
* runtime, so unlike Python/.NET/TypeScript there is no env-injection mode.
* Tools MUST read declared credentials via {@code ctx.getCredential(name)}; reading
* via {@code System.getenv} would only see whatever the JVM inherited from
- * the shell at startup. See {@code docs/design/secret-injection-contract.md} §6.
+ * the shell at startup. See {@code design/secret-injection-contract.md} §6.
*
* Setup (one-time, via CLI):
*
- * agentspan secrets set GITHUB_TOKEN ghp_xxx
+ * conductor secrets set GITHUB_TOKEN ghp_xxx
*
*
* If the credential isn't set on the server, this tool's task is reported
@@ -78,7 +78,7 @@ public Map listGithubRepos(String username, int limit, ToolConte
+ "/repos?per_page=" + n + "&sort=updated"))
.timeout(Duration.ofSeconds(10))
.header("Accept", "application/vnd.github+json")
- .header("User-Agent", "agentspan-java-example/1.0");
+ .header("User-Agent", "conductor-java-example/1.0");
if (token != null && !token.isEmpty()) {
reqBuilder.header("Authorization", "Bearer " + token);
}
@@ -111,7 +111,7 @@ public Map getGithubUser(String username, ToolContext ctx) {
.uri(URI.create("https://api.github.com/users/" + username))
.timeout(Duration.ofSeconds(10))
.header("Accept", "application/vnd.github+json")
- .header("User-Agent", "agentspan-java-example/1.0");
+ .header("User-Agent", "conductor-java-example/1.0");
if (token != null && !token.isEmpty()) {
reqBuilder.header("Authorization", "Bearer " + token);
}
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example17SwarmOrchestration.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example17SwarmOrchestration.java
index 9041b9454..f64fad20e 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example17SwarmOrchestration.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example17SwarmOrchestration.java
@@ -77,7 +77,7 @@ public static void main(String[] args) {
System.out.println("=== Refund Scenario ===");
AgentResult refundResult = runtime.run(support,
- "I bought a product last week and it arrived damaged. I want my money back.");
+ "I bought a product last week and it arrived damaged. I want my money back. my order number is O123 and product SKU is S124");
refundResult.printResult();
System.out.println("\n=== Technical Issue Scenario ===");
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example31ToolInputGuardrail.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example31ToolInputGuardrail.java
index 852c810a3..d57182ccc 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example31ToolInputGuardrail.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example31ToolInputGuardrail.java
@@ -68,16 +68,8 @@ public static void main(String[] args) {
})
.build();
- // Re-wrap the tool with the guardrail attached at tool level
- ToolDef guardedTool = ToolDef.builder()
- .name(rawTool.getName())
- .description(rawTool.getDescription())
- .inputSchema(rawTool.getInputSchema())
- .outputSchema(rawTool.getOutputSchema())
- .toolType(rawTool.getToolType())
- .func(rawTool.getFunc())
- .guardrails(List.of(sqlInjectionGuard))
- .build();
+ // Preserve all fields discovered from @Tool while attaching the guardrail.
+ ToolDef guardedTool = rawTool.withGuardrails(List.of(sqlInjectionGuard));
Agent agent = Agent.builder()
.name("db_assistant")
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example38TechTrends.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example38TechTrends.java
index 2a4ea6ac7..2ce79bb83 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example38TechTrends.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example38TechTrends.java
@@ -148,7 +148,7 @@ private static String get(String url) throws IOException, InterruptedException {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.timeout(Duration.ofSeconds(10))
- .header("User-Agent", "agentspan-java-example/1.0")
+ .header("User-Agent", "conductor-java-example/1.0")
.GET()
.build();
return HTTP_CLIENT.send(request, HttpResponse.BodyHandlers.ofString()).body();
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example56RagAgent.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example56RagAgent.java
index 4d0d5a3dc..768a91e2b 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example56RagAgent.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example56RagAgent.java
@@ -39,7 +39,7 @@
public class Example56RagAgent {
private static final String VECTOR_DB = "pgvectordb";
- private static final String INDEX = "agentspan_docs_56";
+ private static final String INDEX = "conductor_docs_56";
private static final String EMBED_PROVIDER = "openai";
private static final String EMBED_MODEL = "text-embedding-3-small";
@@ -72,16 +72,16 @@ public static void main(String[] args) {
System.out.println("=== Phase 1: Indexing documents ===");
AgentResult indexResult = runtime.run(indexerAgent,
"Index the following documents:\n\n" +
- "Title: Agentspan Overview\n" +
- "Content: Agentspan is a multi-agent orchestration platform built on Conductor. " +
+ "Title: Conductor Overview\n" +
+ "Content: Conductor is a multi-agent orchestration platform built on Conductor. " +
"It enables developers to build, deploy, and manage AI agent workflows at scale. " +
"Agents can use tools, call sub-agents, and maintain shared state.\n\n" +
"Title: Agent Strategies\n" +
- "Content: Agentspan supports multiple agent strategies: ROUTER (LLM picks the next agent), " +
+ "Content: Conductor supports multiple agent strategies: ROUTER (LLM picks the next agent), " +
"HANDOFF (transfer control to another agent), SWARM (agents collaborate simultaneously), " +
"and LOOP (repeat until a condition is met). Each strategy suits different workflow patterns.\n\n" +
"Title: Tool Types\n" +
- "Content: Agentspan tools include local Python/Java workers, RAG search/index tools, " +
+ "Content: Conductor tools include local Python/Java workers, RAG search/index tools, " +
"HTTP tools for calling REST APIs, and AgentTool for calling sub-agents. " +
"Tools are registered with name, description, and JSON schema for the LLM.\n\n" +
"Title: Guardrails\n" +
@@ -105,7 +105,7 @@ public static void main(String[] args) {
System.out.println("\n=== Phase 2: Answering questions from indexed docs ===");
AgentResult qaResult = runtime.run(qaAgent,
"Answer these questions using the knowledge base:\n" +
- "1. What is Agentspan and what platform is it built on?\n" +
+ "1. What is Conductor and what platform is it built on?\n" +
"2. What agent strategies are available and when would you use HANDOFF?\n" +
"3. What happens when a guardrail fails?");
qaResult.printResult();
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example57PlanDryRun.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example57PlanDryRun.java
index 08e79bc06..2af4b573f 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example57PlanDryRun.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example57PlanDryRun.java
@@ -32,7 +32,7 @@
*
* Debugging agent setup without consuming LLM credits
* Validating tool schemas and guardrail definitions
- * Understanding exactly what gets sent to the Agentspan server
+ * Understanding exactly what gets sent to the Conductor server
*
*
* The {@link AgentConfigSerializer} converts the in-memory {@link Agent} into
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example58ScatterGather.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example58ScatterGather.java
index 4eefbc667..6d618ee9d 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example58ScatterGather.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example58ScatterGather.java
@@ -12,6 +12,7 @@
*/
package org.conductoross.conductor.ai.examples;
+import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -39,6 +40,29 @@
*/
public class Example58ScatterGather {
+ private static final String[] countries = new String[]{
+ "Afghanistan", "Albania", "Algeria", "Andorra", "Angola",
+ "Argentina", "Armenia", "Australia", "Austria", "Azerbaijan",
+ "Bahamas", "Bahrain", "Bangladesh", "Barbados", "Belarus",
+ "Belgium", "Belize", "Benin", "Bhutan", "Bolivia",
+ "Bosnia and Herzegovina", "Botswana", "Brazil", "Brunei", "Bulgaria",
+ "Burkina Faso", "Burundi", "Cambodia", "Cameroon", "Canada",
+ "Chad", "Chile", "China", "Colombia", "Congo",
+ "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czech Republic",
+ "Denmark", "Djibouti", "Dominican Republic", "Ecuador", "Egypt",
+ "El Salvador", "Estonia", "Ethiopia", "Fiji", "Finland",
+ "France", "Gabon", "Georgia", "Germany", "Ghana",
+ "Greece", "Guatemala", "Guinea", "Haiti", "Honduras",
+ "Hungary", "Iceland", "India", "Indonesia", "Iran",
+ "Iraq", "Ireland", "Israel", "Italy", "Jamaica",
+ "Japan", "Jordan", "Kazakhstan", "Kenya", "Kuwait",
+ "Laos", "Latvia", "Lebanon", "Libya", "Lithuania",
+ "Luxembourg", "Madagascar", "Malaysia", "Mali", "Malta",
+ "Mexico", "Mongolia", "Morocco", "Mozambique", "Myanmar",
+ "Nepal", "Netherlands", "New Zealand", "Nigeria", "North Korea",
+ "Norway", "Oman", "Pakistan", "Panama", "Paraguay",
+ };
+
static class ResearchTools {
@Tool(name = "search_knowledge_base",
description = "Search the knowledge base for information on a topic")
@@ -63,7 +87,7 @@ public static void main(String[] args) {
Agent researcher = Agent.builder()
.name("researcher")
- .model("anthropic/claude-sonnet-4-20250514")
+ .model("anthropic/claude-sonnet-5")
.instructions(
"You are a country analyst. You will be given the name of a country. "
+ "Use the search_knowledge_base tool ONCE to research that country, then "
@@ -96,7 +120,7 @@ public static void main(String[] args) {
String workerName = researcher.getName();
String instructions =
"You are a scatter-gather coordinator. Your job is to:\n"
- + "1. Decompose the input into N independent sub-problems\n"
+ + "1. Decompose the input into independent sub-problems ONE for each input\n"
+ "2. Call the '" + workerName + "' tool MULTIPLE TIMES IN PARALLEL — once per sub-problem, "
+ "each with a clear, self-contained prompt\n"
+ "3. After all results return, synthesize them into a unified answer\n\n"
@@ -114,7 +138,7 @@ public static void main(String[] args) {
.build();
AgentResult result = runtime.run(coordinator,
- "Create a comprehensive profile for each of the 100 countries listed.");
+ "Create a comprehensive profile for each of the 100 countries listed. " + Arrays.toString(countries));
result.printResult();
runtime.shutdown();
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example68ContextCondensation.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example68ContextCondensation.java
index fabd87de1..29c2e4a6b 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example68ContextCondensation.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example68ContextCondensation.java
@@ -42,7 +42,7 @@
*
*
To trigger condensation, add to server's {@code application.properties}:
*
- * agentspan.default-context-window=10000
+ * conductor.default-context-window=10000
*
*/
public class Example68ContextCondensation {
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example69Skills.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example69Skills.java
index 821f9d33d..ec55b7d2f 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example69Skills.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example69Skills.java
@@ -26,14 +26,14 @@
/**
* Example 69 — Skills
*
- * Loads an agentskills.io skill directory as an Agentspan Agent. Skill scripts
+ *
Loads an agentskills.io skill directory as an Conductor Agent. Skill scripts
* become worker tools, and resource files are available through the generated
* read_skill_file tool.
*
*
Usage:
*
- * AGENTSPAN_SERVER_URL=http://localhost:6767/api \
- * AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini \
+ * CONDUCTOR_SERVER_URL=http://localhost:6767/api \
+ * CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini \
* ./gradlew :examples:run -PmainClass=org.conductoross.conductor.ai.examples.Example69Skills \
* --args="/path/to/skill 'Review this repository'"
*
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example70AnnotatedAgent.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example70AnnotatedAgent.java
index 0761b9ecd..ca639bb52 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example70AnnotatedAgent.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example70AnnotatedAgent.java
@@ -28,8 +28,8 @@
*
* Requirements:
*
- * AGENTSPAN_SERVER_URL=http://localhost:6767/api
- * AGENTSPAN_LLM_MODEL=openai/gpt-4o
+ * CONDUCTOR_SERVER_URL=http://localhost:6767/api
+ * CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o
*
*/
public class Example70AnnotatedAgent {
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example71AgentControlPlane.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example71AgentControlPlane.java
new file mode 100644
index 000000000..b541e8ec4
--- /dev/null
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example71AgentControlPlane.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2026 Conductor Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+package org.conductoross.conductor.ai.examples;
+
+import io.orkes.conductor.client.AgentClient;
+import io.orkes.conductor.client.ApiClient;
+import io.orkes.conductor.client.OrkesClients;
+import io.orkes.conductor.client.model.agent.AgentRequest;
+import io.orkes.conductor.client.model.agent.AgentStatusResponse;
+import io.orkes.conductor.client.model.agent.StartResponse;
+
+/**
+ * Example 71 — start and control an already-deployed agent through {@link AgentClient}.
+ *
+ *
Deploy the agent definition separately, then run:
+ *
+ *
{@code
+ * ./gradlew :agent-examples:run \
+ * -PmainClass=org.conductoross.conductor.ai.examples.Example71AgentControlPlane \
+ * --args="researcher 3 'Summarize the latest release' status"
+ * }
+ *
+ * The final argument is {@code status}, {@code stop}, or {@code cancel}. Use {@code -} for the
+ * version to select the server's deployed default.
+ */
+public final class Example71AgentControlPlane {
+
+ private Example71AgentControlPlane() {}
+
+ public static void main(String[] args) {
+ if (args.length == 0) {
+ throw new IllegalArgumentException(
+ "Usage: [version|-] [prompt] [status|stop|cancel]");
+ }
+
+ String name = args[0];
+ Integer version = args.length > 1 && !"-".equals(args[1])
+ ? Integer.valueOf(args[1])
+ : null;
+ String prompt = args.length > 2 ? args[2] : "Summarize the latest release.";
+ String action = args.length > 3 ? args[3] : "status";
+
+ ApiClient transport = createTransport();
+ try (AgentClient agents = new OrkesClients(transport).getAgentClient()) {
+ AgentRequest request = AgentRequest.deployedAgent(name, version)
+ .prompt(prompt)
+ .build();
+ StartResponse started = agents.startAgent(request);
+ String executionId = started.getExecutionId();
+ System.out.println("Started " + name + " as " + executionId);
+
+ switch (action) {
+ case "stop" -> {
+ agents.stopAgent(executionId);
+ System.out.println("Graceful stop requested after the current iteration.");
+ }
+ case "cancel" -> {
+ agents.cancelAgent(executionId, "Cancelled from Example71AgentControlPlane");
+ System.out.println("Execution cancelled immediately.");
+ }
+ case "status" -> printStatus(agents.getAgentStatus(executionId));
+ default -> throw new IllegalArgumentException(
+ "Action must be status, stop, or cancel: " + action);
+ }
+ }
+ }
+
+ private static ApiClient createTransport() {
+ if (Settings.AUTH_KEY != null && Settings.AUTH_SECRET != null) {
+ return new ApiClient(Settings.SERVER_URL, Settings.AUTH_KEY, Settings.AUTH_SECRET);
+ }
+ return new ApiClient(Settings.SERVER_URL);
+ }
+
+ private static void printStatus(AgentStatusResponse status) {
+ System.out.println("Status: " + status.getStatus());
+ System.out.println("Started: " + status.getStartTime());
+ System.out.println("Ended: " + status.getEndTime());
+ }
+}
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example99ScheduledAgent.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example99ScheduledAgent.java
index ec6fd2f49..d222cb4d9 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example99ScheduledAgent.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Example99ScheduledAgent.java
@@ -17,27 +17,30 @@
import org.conductoross.conductor.ai.Agent;
import org.conductoross.conductor.ai.AgentRuntime;
-import org.conductoross.conductor.ai.schedule.Schedule;
-import org.conductoross.conductor.ai.schedule.ScheduleInfo;
+
+import com.netflix.conductor.common.metadata.workflow.StartWorkflowRequest;
+
+import io.orkes.conductor.client.SchedulerClient;
+import io.orkes.conductor.client.model.SaveScheduleRequest;
+import io.orkes.conductor.client.model.WorkflowSchedule;
/**
* Example 99 — Scheduled Agent
*
- * Deploys an agent on two named cron schedules and exercises the full
- * lifecycle: list, pause, resume, run-now (ad-hoc), preview next fires,
- * and purge on cleanup.
+ *
Deploys an agent, creates two native Conductor schedules, and exercises
+ * the scheduler lifecycle: list, pause, resume, preview, and cleanup.
*
*
Usage:
*
- * AGENTSPAN_SERVER_URL=http://localhost:6767/api \
- * AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini \
+ * CONDUCTOR_SERVER_URL=http://localhost:6767/api \
+ * CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini \
* ./gradlew :examples:run -PmainClass=org.conductoross.conductor.ai.examples.Example99ScheduledAgent
*
*/
public class Example99ScheduledAgent {
public static void main(String[] args) throws Exception {
- String model = System.getenv().getOrDefault("AGENTSPAN_LLM_MODEL", "anthropic/claude-sonnet-4-6");
+ String model = System.getenv().getOrDefault("CONDUCTOR_AGENT_LLM_MODEL", "anthropic/claude-sonnet-4-6");
Agent agent = Agent.builder()
.name("eng_digest_99")
@@ -50,33 +53,33 @@ public static void main(String[] args) throws Exception {
try (AgentRuntime runtime = new AgentRuntime()) {
- // 1. Deploy with two schedules.
- runtime.deploy(agent, List.of(
- Schedule.builder()
- .name("weekday-9am")
- .cron("0 0 9 * * MON-FRI")
- .timezone("America/Los_Angeles")
- .input(Map.of("channel", "#eng"))
- .description("Weekday morning digest")
- .build(),
- Schedule.builder()
- .name("friday-5pm")
- .cron("0 0 17 * * FRI")
- .timezone("America/Los_Angeles")
- .input(Map.of("channel", "#all-hands", "mode", "weekly"))
- .description("Weekly all-hands digest")
- .build()
- ));
+ // 1. Deploy the agent, then use the typed SchedulerClient directly.
+ runtime.deploy(agent);
+ SchedulerClient schedules = runtime.getSchedulerClient();
+ String weekdayName = agent.getName() + "_weekday";
+ String fridayName = agent.getName() + "_friday";
+ saveSchedule(
+ schedules,
+ weekdayName,
+ agent.getName(),
+ "0 0 9 * * MON-FRI",
+ Map.of("channel", "#eng"),
+ "Weekday morning digest");
+ saveSchedule(
+ schedules,
+ fridayName,
+ agent.getName(),
+ "0 0 17 * * FRI",
+ Map.of("channel", "#all-hands", "mode", "weekly"),
+ "Weekly all-hands digest");
System.out.printf("✓ Deployed '%s' with 2 schedules%n", agent.getName());
- var sched = runtime.schedules();
-
// 2. List schedules for this agent.
- List infos = sched.list(agent.getName());
+ List infos = schedules.getAllSchedules(agent.getName());
System.out.printf("%nSchedules (%d):%n", infos.size());
- for (ScheduleInfo s : infos) {
+ for (WorkflowSchedule s : infos) {
System.out.printf(" %s %s [%s]%n",
- s.getName(), s.getCron(), s.isPaused() ? "PAUSED" : "active");
+ s.getName(), s.getCronExpression(), s.isPaused() ? "PAUSED" : "active");
}
if (infos.size() < 2) {
@@ -84,39 +87,46 @@ public static void main(String[] args) throws Exception {
return;
}
- String weekdayName = infos.stream()
- .filter(s -> "weekday-9am".equals(s.getShortName()))
- .findFirst().orElseThrow().getName();
- String fridayName = infos.stream()
- .filter(s -> "friday-5pm".equals(s.getShortName()))
- .findFirst().orElseThrow().getName();
-
// 3. Pause the weekday schedule.
- sched.pause(weekdayName, "rate-limit cooldown demo");
- ScheduleInfo afterPause = sched.get(weekdayName);
+ schedules.pauseSchedule(weekdayName, "rate-limit cooldown demo");
+ WorkflowSchedule afterPause = schedules.getSchedule(weekdayName);
System.out.printf("%n✓ Paused '%s': paused=%b, reason=%s%n",
weekdayName, afterPause.isPaused(), afterPause.getPausedReason());
// 4. Resume it.
- sched.resume(weekdayName);
- ScheduleInfo afterResume = sched.get(weekdayName);
+ schedules.resumeSchedule(weekdayName);
+ WorkflowSchedule afterResume = schedules.getSchedule(weekdayName);
System.out.printf("✓ Resumed '%s': paused=%b%n", weekdayName, afterResume.isPaused());
- // 5. Ad-hoc run of the friday schedule.
- ScheduleInfo fridayInfo = sched.get(fridayName);
- String execId = sched.runNow(fridayInfo);
- System.out.printf("%n✓ runNow '%s' → execution id: %s%n", fridayName, execId);
-
- // 6. Preview next 5 fire times for the weekday cron.
- List nextFires = sched.previewNext("0 0 9 * * MON-FRI", 5);
- System.out.println("\nNext 5 fires for weekday-9am:");
+ // 5. Preview next 5 fire times for the weekday cron.
+ List nextFires = schedules.getNextFewSchedules("0 0 9 * * MON-FRI", null, null, 5);
+ System.out.println("\nNext 5 fires for weekday:");
for (int i = 0; i < nextFires.size(); i++) {
System.out.printf(" %d. %s%n", i + 1, new java.util.Date(nextFires.get(i)));
}
- // 7. Cleanup: redeploy with empty list to purge all schedules.
- runtime.deploy(agent, List.of());
+ // 6. Cleanup: remove each schedule explicitly.
+ schedules.deleteSchedule(weekdayName);
+ schedules.deleteSchedule(fridayName);
System.out.printf("%n✓ Purged all schedules for '%s'%n", agent.getName());
}
}
+
+ private static void saveSchedule(
+ SchedulerClient schedules,
+ String scheduleName,
+ String workflowName,
+ String cron,
+ Map input,
+ String description) {
+ StartWorkflowRequest workflow = new StartWorkflowRequest();
+ workflow.setName(workflowName);
+ workflow.setInput(input);
+ schedules.saveSchedule(new SaveScheduleRequest()
+ .name(scheduleName)
+ .cronExpression(cron)
+ .zoneId("America/Los_Angeles")
+ .startWorkflowRequest(workflow)
+ .description(description));
+ }
}
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Settings.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Settings.java
index 061f31941..067515dbe 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Settings.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/Settings.java
@@ -17,29 +17,29 @@
*
* Set these before running examples:
*
- * export AGENTSPAN_SERVER_URL=http://localhost:6767/api
- * export AGENTSPAN_LLM_MODEL=openai/gpt-4o
- * export AGENTSPAN_AUTH_KEY=your-key # optional
- * export AGENTSPAN_AUTH_SECRET=your-secret # optional
+ * export CONDUCTOR_SERVER_URL=http://localhost:6767/api
+ * export CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o
+ * export CONDUCTOR_AUTH_KEY=your-key # optional
+ * export CONDUCTOR_AUTH_SECRET=your-secret # optional
*
*/
public class Settings {
private static final java.util.Map ENV = System.getenv();
public static final String SERVER_URL =
- ENV.getOrDefault("AGENTSPAN_SERVER_URL", "http://localhost:6767/api");
+ ENV.getOrDefault("CONDUCTOR_SERVER_URL", "http://localhost:6767/api");
public static final String LLM_MODEL =
- ENV.getOrDefault("AGENTSPAN_LLM_MODEL", "openai/gpt-4o");
+ ENV.getOrDefault("CONDUCTOR_AGENT_LLM_MODEL", "openai/gpt-4o");
public static final String SECONDARY_LLM_MODEL =
- ENV.getOrDefault("AGENT_SECONDARY_LLM_MODEL", "anthropic/claude-sonnet-4-6");
+ ENV.getOrDefault("AGENT_SECONDARY_LLM_MODEL", "openai/gpt-4o");
public static final String AUTH_KEY =
- ENV.get("AGENTSPAN_AUTH_KEY");
+ ENV.get("CONDUCTOR_AUTH_KEY");
public static final String AUTH_SECRET =
- ENV.get("AGENTSPAN_AUTH_SECRET");
+ ENV.get("CONDUCTOR_AUTH_SECRET");
private Settings() {}
}
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/adk/Example00HelloWorld.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/adk/Example00HelloWorld.java
index 3c913bfde..86b60bef2 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/adk/Example00HelloWorld.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/adk/Example00HelloWorld.java
@@ -22,12 +22,12 @@
* Example Adk 00 — Hello World using the native Google ADK Java SDK.
*
* Defines a real {@link LlmAgent} with {@code com.google.adk.agents.LlmAgent.builder()},
- * and hands it directly to {@link org.conductoross.conductor.ai.Agentspan#run(Object, String)}
- * for execution on the durable Agentspan runtime.
+ * and hands it directly to {@link org.conductoross.conductor.ai.AgentRuntime#run(Object, String)}
+ * for execution on the durable Conductor runtime.
*
*
Requirements:
*
- * {@code AGENTSPAN_SERVER_URL=http://localhost:6767/api}
+ * {@code CONDUCTOR_SERVER_URL=http://localhost:6767/api}
* OpenAI/Gemini key configured in server credentials
*
*/
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/adk/Example01BasicAgent.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/adk/Example01BasicAgent.java
index 94fc7076b..6c1c90f7c 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/adk/Example01BasicAgent.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/adk/Example01BasicAgent.java
@@ -24,8 +24,8 @@
* Java port of sdk/python/examples/adk/01_basic_agent.py.
*
*
Demonstrates: the simplest Google ADK agent — defined via the
- * native {@link LlmAgent} builder and bridged to the Agentspan durable
- * runtime via {@link org.conductoross.conductor.ai.Agentspan#run(Object, String)}.
+ * native {@link LlmAgent} builder and bridged to the Conductor durable
+ * runtime via {@link org.conductoross.conductor.ai.AgentRuntime#run(Object, String)}.
*/
public class Example01BasicAgent {
public static void main(String[] args) {
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/adk/Example02FunctionTools.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/adk/Example02FunctionTools.java
index 6b5615606..aee575e0e 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/adk/Example02FunctionTools.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/adk/Example02FunctionTools.java
@@ -27,7 +27,7 @@
*
*
Tools are static methods annotated with {@code @Schema} — the idiomatic
* ADK pattern — and packaged via {@code FunctionTool.create(Class, "methodName")}.
- * No Agentspan-specific annotations.
+ * No Conductor-specific annotations.
*/
public class Example02FunctionTools {
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/adk/Example18OrderProcessing.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/adk/Example18OrderProcessing.java
index d146fec34..72452fe41 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/adk/Example18OrderProcessing.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/adk/Example18OrderProcessing.java
@@ -40,7 +40,13 @@ public class Example18OrderProcessing {
public static Map searchCatalog(
@Schema(name = "query", description = "Search query") String query,
@Schema(name = "category", description = "Product category") String category) {
- String cat = category == null || category.isEmpty() ? "all" : category;
+ String cat = category == null ? "all" : category.trim().toLowerCase();
+ // Models often use broad labels such as "electronics" or "computers". Treat an
+ // unknown category as an all-catalog search so a valid text query still produces a useful
+ // observation instead of sending the agent into a retry loop with an empty result.
+ if (!List.of("all", "laptops", "accessories", "monitors").contains(cat)) {
+ cat = "all";
+ }
List> catalog = List.of(
Map.of("sku", "LAP-001", "name", "ProBook Laptop 15\"", "category", "laptops", "price", 1299.99, "stock", 23),
Map.of("sku", "LAP-002", "name", "UltraSlim Notebook 13\"", "category", "laptops", "price", 899.99, "stock", 45),
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/adk/Example19SupplyChain.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/adk/Example19SupplyChain.java
index b42da311d..ad64bb8a2 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/adk/Example19SupplyChain.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/adk/Example19SupplyChain.java
@@ -34,6 +34,11 @@
*/
public class Example19SupplyChain {
+ @Schema(description = "List all the warehouses")
+ public static List listWarehouses() {
+ return List.of("east","west");
+ }
+
@Schema(description = "Get current inventory levels at a warehouse.")
public static Map getInventoryLevels(
@Schema(name = "warehouse", description = "Warehouse name") String warehouse) {
@@ -123,7 +128,8 @@ public static void main(String[] args) {
.instruction("Check inventory levels and supplier status. Flag items below reorder points.")
.tools(
FunctionTool.create(Example19SupplyChain.class, "getInventoryLevels"),
- FunctionTool.create(Example19SupplyChain.class, "checkSupplierStatus"))
+ FunctionTool.create(Example19SupplyChain.class, "checkSupplierStatus"),
+ FunctionTool.create(Example19SupplyChain.class, "listWarehouses"))
.build();
LlmAgent logisticsAgent = LlmAgent.builder()
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/adk/Example23Callbacks.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/adk/Example23Callbacks.java
index a51985399..1eadf8572 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/adk/Example23Callbacks.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/adk/Example23Callbacks.java
@@ -27,7 +27,7 @@
*
* Demonstrates: native ADK {@code beforeModelCallback} and
* {@code afterModelCallback} attached to an {@link LlmAgent}. The bridge
- * forwards both as Agentspan {@code CallbackHandler} workers.
+ * forwards both as Conductor {@code CallbackHandler} workers.
*
*
Server-side limitation (matches Python's
* {@code 14_callbacks.py} comment): the server's workflow compiler does
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/adk/Example34MlEngineering.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/adk/Example34MlEngineering.java
index fc58e134e..dfd02a912 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/adk/Example34MlEngineering.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/adk/Example34MlEngineering.java
@@ -26,7 +26,7 @@
*
Demonstrates: a multi-agent ML workflow combining sequential, parallel,
* and loop strategies. The Java port encodes the strategy semantics inline
* (sub-agents with instructions describing parallel/loop intent) since the
- * Agentspan {@link org.conductoross.conductor.ai.Agentspan#run(Object, String)} currently translates {@link LlmAgent}s with
+ * Conductor {@link org.conductoross.conductor.ai.AgentRuntime#run(Object, String)} currently translates {@link LlmAgent}s with
* sub-agents but does not extract {@code ParallelAgent}/{@code LoopAgent}
* primitives directly.
*/
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/adk/Example35RagAgent.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/adk/Example35RagAgent.java
index 7f0549158..f609c45ad 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/adk/Example35RagAgent.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/adk/Example35RagAgent.java
@@ -31,7 +31,7 @@
*
Java port of sdk/python/examples/adk/35_rag_agent.py.
*
*
Demonstrates: a RAG agent flow with index + search tools. The Python
- * source uses Agentspan's {@code search_tool} / {@code index_tool} factory
+ * source uses Conductor's {@code search_tool} / {@code index_tool} factory
* helpers (mapped to Conductor's LLM_INDEX_TEXT / LLM_SEARCH_INDEX system
* tasks).
*
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/adk/Example37DeployAndServe.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/adk/Example37DeployAndServe.java
index c8c6109c8..0b34f7a53 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/adk/Example37DeployAndServe.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/adk/Example37DeployAndServe.java
@@ -28,17 +28,17 @@
/**
* Example Adk 37 — deploy + serve + run round-trip
*
- *
Demonstrates the three Agentspan entry points working together with a
+ *
Demonstrates the three Conductor entry points working together with a
* native ADK {@link LlmAgent} and zero bridge calls in user code:
*
*
- * {@link Agentspan#deploy(Object...)} — register the agent on the
+ * {@link AgentRuntime#deploy(Object...)} — register the agent on the
* server. CI/CD step; idempotent; no workers polled.
- * {@link Agentspan#serve(Object...)} — register local worker handlers
+ * {@link AgentRuntime#serve(Object...)} — register local worker handlers
* and keep them polling. Long-running; normally a separate process.
* This example runs it on a daemon thread so a single JVM can play
* both client and worker roles.
- * {@link Agentspan#start(Object, String)} — trigger an execution
+ * {@link AgentRuntime#start(Object, String)} — trigger an execution
* against the deployed agent and stream events back.
*
*
@@ -52,7 +52,7 @@
* $ java -cp ... WorkerProcess // runtime.serve(rootAgent)
*
* # any caller, e.g. an HTTP handler
- * $ curl -X POST .../api/agent/start -d '{"agentName":"deploy_demo_agent",...}'
+ * $ curl -X POST .../api/agent/start -d '{"name":"deploy_demo_agent","prompt":"..."}'
* }
*/
public class Example37DeployAndServe {
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/adk/Example38AgentspanGuardrails.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/adk/Example38ConductorGuardrails.java
similarity index 91%
rename from agent-examples/src/main/java/org/conductoross/conductor/ai/examples/adk/Example38AgentspanGuardrails.java
rename to agent-examples/src/main/java/org/conductoross/conductor/ai/examples/adk/Example38ConductorGuardrails.java
index 1d5e14844..1ca1ba720 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/adk/Example38AgentspanGuardrails.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/adk/Example38ConductorGuardrails.java
@@ -27,25 +27,25 @@
import com.google.adk.agents.LlmAgent;
/**
- * Example Adk 38 — Agentspan guardrails on a native ADK agent
+ * Example ADK 38 — Conductor guardrails on a native ADK agent
*
* ADK itself doesn't ship a guardrail abstraction — its safety story is
* {@code beforeModelCallback} returning a short-circuit response, which the
- * server doesn't yet compile into hook tasks (see Example23). Agentspan
+ * server doesn't yet compile into hook tasks (see Example23). Conductor
* provides a separate, server-compiled guardrail mechanism that runs as a
* Conductor task inside the agent's loop. This example shows how to attach
- * Agentspan guardrails to a pure ADK agent without giving up the drop-in
+ * Conductor guardrails to a pure ADK agent without giving up the drop-in
* pattern.
*
*
Pattern:
*
* Build the native ADK {@link LlmAgent} exactly as you would for ADK.
- * Hand it to {@link AdkBridge#agentBuilder} (NOT {@code toAgentspan} —
+ * Hand it to {@link AdkBridge#agentBuilder} (NOT {@code toConductor} —
* the builder variant lets you decorate before building).
* Attach a {@link GuardrailDef} with a validation function that
* returns {@link GuardrailResult#pass()}, {@link GuardrailResult#fail
* fail(message)}, or {@link GuardrailResult#fix fix(rewrittenOutput)}.
- * {@link Agentspan#run} executes the agent with the guardrail running
+ * {@link AgentRuntime#run} executes the agent with the guardrail running
* server-side after each LLM turn; failures retry (up to
* {@code maxRetries}) or fix-substitute depending on {@link OnFail}.
*
@@ -54,7 +54,7 @@
* model's response using {@code OnFail.FIX} so the LLM doesn't have to
* regenerate — the guardrail rewrites the output in place.
*/
-public class Example38AgentspanGuardrails {
+public class Example38ConductorGuardrails {
private static final Pattern EMAIL = Pattern.compile(
"\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}\\b");
@@ -101,10 +101,10 @@ public static void main(String[] args) {
.position(Position.OUTPUT)
.onFail(OnFail.FIX)
.maxRetries(1)
- .func(Example38AgentspanGuardrails::redactPii)
+ .func(Example38ConductorGuardrails::redactPii)
.build();
- // Drop in the native ADK agent, then bolt Agentspan's server-side
+ // Drop in the native ADK agent, then bolt Conductor's server-side
// guardrail onto the bridged builder before .build().
Agent guarded = AdkBridge.agentBuilder(helper)
.guardrails(piiRedaction)
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example01HelloWorld.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example01HelloWorld.java
index fae71fd4e..98f15bb03 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example01HelloWorld.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example01HelloWorld.java
@@ -23,23 +23,23 @@
*
* Builds a real {@code dev.langchain4j.model.openai.OpenAiChatModel}
* (the canonical LangChain4j chat-model class) and hands it directly to
- * {@link Agentspan#run(ChatModel, String)} via the drop-in overload so the
- * agent runs on the durable Agentspan runtime.
+ * {@link AgentRuntime#run(ChatModel, String)} via the drop-in overload so the
+ * agent runs on the durable Conductor runtime.
*
*
Requirements:
*
- * {@code AGENTSPAN_SERVER_URL=http://localhost:6767/api}
- * Agentspan server with OpenAI credentials configured server-side.
+ * {@code CONDUCTOR_SERVER_URL=http://localhost:6767/api}
+ * Conductor server with OpenAI credentials configured server-side.
*
*/
public class Example01HelloWorld {
public static void main(String[] args) {
AgentRuntime runtime = new AgentRuntime();
- // apiKey is required by LangChain4j's builder but unused — Agentspan
+ // apiKey is required by LangChain4j's builder but unused — Conductor
// runs the LLM call on the server with server-registered credentials.
ChatModel model = OpenAiChatModel.builder()
- .apiKey("agentspan-server-handles-credentials")
+ .apiKey("conductor-server-handles-credentials")
.modelName("gpt-4o-mini")
.build();
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example02ReactWithTools.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example02ReactWithTools.java
index a2f234c4a..86d675b41 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example02ReactWithTools.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example02ReactWithTools.java
@@ -32,14 +32,14 @@
*
* Defining tools with {@link Tool @Tool} on a POJO
* Building a native {@link OpenAiChatModel} and passing it to
- * {@link Agentspan#run(ChatModel, String, Object...)} alongside the tool POJOs
+ * {@link AgentRuntime#run(ChatModel, String, Object...)} alongside the tool POJOs
* Calculator, string, and date utilities
*
*
* Requirements:
*
- * {@code AGENTSPAN_SERVER_URL=http://localhost:6767/api}
- * Agentspan server with OpenAI credentials configured server-side.
+ * {@code CONDUCTOR_SERVER_URL=http://localhost:6767/api}
+ * Conductor server with OpenAI credentials configured server-side.
*
*/
public class Example02ReactWithTools {
@@ -177,10 +177,10 @@ private double parsePrimary() {
public static void main(String[] args) {
AgentRuntime runtime = new AgentRuntime();
- // apiKey is required by LangChain4j's builder but unused — Agentspan
+ // apiKey is required by LangChain4j's builder but unused — Conductor
// runs the LLM call on the server with server-registered credentials.
ChatModel model = OpenAiChatModel.builder()
- .apiKey("agentspan-server-handles-credentials")
+ .apiKey("conductor-server-handles-credentials")
.modelName("gpt-4o-mini")
.build();
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example03CustomTools.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example03CustomTools.java
index 27973a8d1..2afc56219 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example03CustomTools.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example03CustomTools.java
@@ -40,8 +40,8 @@
*
* Requirements:
*
- * {@code AGENTSPAN_SERVER_URL=http://localhost:6767/api}
- * Agentspan server with OpenAI credentials configured server-side.
+ * {@code CONDUCTOR_SERVER_URL=http://localhost:6767/api}
+ * Conductor server with OpenAI credentials configured server-side.
*
*/
public class Example03CustomTools {
@@ -107,10 +107,10 @@ private static String trimNumber(double v) {
public static void main(String[] args) {
AgentRuntime runtime = new AgentRuntime();
- // apiKey is required by LangChain4j's builder but unused — Agentspan
+ // apiKey is required by LangChain4j's builder but unused — Conductor
// runs the LLM call on the server with server-registered credentials.
ChatModel model = OpenAiChatModel.builder()
- .apiKey("agentspan-server-handles-credentials")
+ .apiKey("conductor-server-handles-credentials")
.modelName("gpt-4o-mini")
.build();
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example04StructuredOutput.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example04StructuredOutput.java
index ed56dae08..f0c46ec58 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example04StructuredOutput.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example04StructuredOutput.java
@@ -34,7 +34,7 @@
*
* LangChain4j adaptation: there is no clean parity for
* {@code with_structured_output} when the {@code @Tool}-bearing POJO is executed
- * inside Agentspan's server-side LLM loop. The closest semantically-equivalent
+ * inside Conductor's server-side LLM loop. The closest semantically-equivalent
* shape is to have the tool itself return a structured JSON payload that matches
* the Pydantic schema — the LLM then receives the same structured-tool-output it
* would have under Pydantic. Field names, types, and ranges all match the
@@ -49,8 +49,8 @@
*
*
Requirements:
*
- * {@code AGENTSPAN_SERVER_URL=http://localhost:6767/api}
- * Agentspan server with OpenAI credentials configured server-side.
+ * {@code CONDUCTOR_SERVER_URL=http://localhost:6767/api}
+ * Conductor server with OpenAI credentials configured server-side.
*
*/
public class Example04StructuredOutput {
@@ -177,10 +177,10 @@ private static String escape(String s) {
public static void main(String[] args) {
AgentRuntime runtime = new AgentRuntime();
- // apiKey is required by LangChain4j's builder but unused — Agentspan
+ // apiKey is required by LangChain4j's builder but unused — Conductor
// runs the LLM call on the server with server-registered credentials.
ChatModel model = OpenAiChatModel.builder()
- .apiKey("agentspan-server-handles-credentials")
+ .apiKey("conductor-server-handles-credentials")
.modelName("gpt-4o-mini")
.build();
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example05PromptTemplates.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example05PromptTemplates.java
index 6e083fa64..73f16419a 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example05PromptTemplates.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example05PromptTemplates.java
@@ -39,8 +39,8 @@
*
* Requirements:
*
- * {@code AGENTSPAN_SERVER_URL=http://localhost:6767/api}
- * Agentspan server with OpenAI credentials configured server-side.
+ * {@code CONDUCTOR_SERVER_URL=http://localhost:6767/api}
+ * Conductor server with OpenAI credentials configured server-side.
*
*/
public class Example05PromptTemplates {
@@ -101,10 +101,10 @@ public String suggestSynonyms(@P("word") String word) {
public static void main(String[] args) {
AgentRuntime runtime = new AgentRuntime();
- // apiKey is required by LangChain4j's builder but unused — Agentspan
+ // apiKey is required by LangChain4j's builder but unused — Conductor
// runs the LLM call on the server with server-registered credentials.
ChatModel model = OpenAiChatModel.builder()
- .apiKey("agentspan-server-handles-credentials")
+ .apiKey("conductor-server-handles-credentials")
.modelName("gpt-4o-mini")
.build();
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example06ChatHistory.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example06ChatHistory.java
index fd670e576..d1be39d04 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example06ChatHistory.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example06ChatHistory.java
@@ -35,13 +35,13 @@
*
* LangChain4j adaptation: with the server-side LLM loop, there is
* no client-side {@code MessageWindowChatMemory} that survives across
- * {@link Agentspan#run} invocations. The closest semantically-equivalent
+ * {@link AgentRuntime#run} invocations. The closest semantically-equivalent
* shape is to mark the agent as {@code stateful(true)} so that the server
* persists conversation history across runs in a dedicated worker domain —
* multi-turn calls against the same stateful agent will see prior exchanges.
* The single-turn driver below mirrors the Python source exactly; toggling
* stateful demonstrates how the Java SDK surfaces persistent context. Because
- * {@code stateful(true)} is an Agentspan-side flag rather than a LangChain4j
+ * {@code stateful(true)} is an Conductor-side flag rather than a LangChain4j
* one, we use the advanced {@link LangChainBridge#agentBuilder} path so we
* can decorate the agent before {@code .build()}.
*
@@ -54,8 +54,8 @@
*
*
Requirements:
*
- * {@code AGENTSPAN_SERVER_URL=http://localhost:6767/api}
- * Agentspan server with OpenAI credentials configured server-side.
+ * {@code CONDUCTOR_SERVER_URL=http://localhost:6767/api}
+ * Conductor server with OpenAI credentials configured server-side.
*
*/
public class Example06ChatHistory {
@@ -82,16 +82,16 @@ public String recallFact(@P("topic") String topic) {
public static void main(String[] args) {
AgentRuntime runtime = new AgentRuntime();
- // apiKey is required by LangChain4j's builder but unused — Agentspan
+ // apiKey is required by LangChain4j's builder but unused — Conductor
// runs the LLM call on the server with server-registered credentials.
ChatModel model = OpenAiChatModel.builder()
- .apiKey("agentspan-server-handles-credentials")
+ .apiKey("conductor-server-handles-credentials")
.modelName("gpt-4o-mini")
.build();
// Use the advanced LangChainBridge.agentBuilder(...) path so we can
// mark the agent stateful(true) — server-side cross-run conversation
- // persistence is an Agentspan feature on top of LangChain4j.
+ // persistence is an Conductor feature on top of LangChain4j.
Agent agent = LangChainBridge.agentBuilder(
"chat_history_agent",
model,
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example07MemoryAgent.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example07MemoryAgent.java
index 0c32a06ae..e24af38f3 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example07MemoryAgent.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example07MemoryAgent.java
@@ -34,12 +34,12 @@
*
* LangChain4j adaptation: with the server-side LLM loop, there is
* no client-side {@code MessageWindowChatMemory} that survives across
- * {@link Agentspan#run} calls. The closest semantically-equivalent shape is
+ * {@link AgentRuntime#run} calls. The closest semantically-equivalent shape is
* to mark the agent as {@code stateful(true)} so that the server persists
* conversation history across runs in a dedicated worker domain. Subsequent
* runs against the same stateful agent then see prior exchanges. The
* single-turn driver below mirrors the Python source. Because
- * {@code stateful(true)} is an Agentspan-side flag, we use the advanced
+ * {@code stateful(true)} is an Conductor-side flag, we use the advanced
* {@link LangChainBridge#agentBuilder} path so we can decorate the agent
* before {@code .build()}.
*
@@ -52,8 +52,8 @@
*
*
Requirements:
*
- * {@code AGENTSPAN_SERVER_URL=http://localhost:6767/api}
- * Agentspan server with OpenAI credentials configured server-side.
+ * {@code CONDUCTOR_SERVER_URL=http://localhost:6767/api}
+ * Conductor server with OpenAI credentials configured server-side.
*
*/
public class Example07MemoryAgent {
@@ -82,16 +82,16 @@ public static void main(String[] args) {
String instructions =
"You are a helpful HR assistant. Remember information from earlier in the conversation.";
- // apiKey is required by LangChain4j's builder but unused — Agentspan
+ // apiKey is required by LangChain4j's builder but unused — Conductor
// runs the LLM call on the server with server-registered credentials.
ChatModel model = OpenAiChatModel.builder()
- .apiKey("agentspan-server-handles-credentials")
+ .apiKey("conductor-server-handles-credentials")
.modelName("gpt-4o-mini")
.build();
// Use the advanced LangChainBridge.agentBuilder(...) path so we can
// mark the agent stateful(true) — server-side cross-run conversation
- // persistence is an Agentspan feature on top of LangChain4j.
+ // persistence is an Conductor feature on top of LangChain4j.
Agent agent = LangChainBridge.agentBuilder(
"memory_agent",
model,
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example08MultiToolAgent.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example08MultiToolAgent.java
index fee9bdaef..15f84d920 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example08MultiToolAgent.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example08MultiToolAgent.java
@@ -39,8 +39,8 @@
*
* Requirements:
*
- * {@code AGENTSPAN_SERVER_URL=http://localhost:6767/api}
- * Agentspan server with OpenAI credentials configured server-side.
+ * {@code CONDUCTOR_SERVER_URL=http://localhost:6767/api}
+ * Conductor server with OpenAI credentials configured server-side.
*
*/
public class Example08MultiToolAgent {
@@ -104,10 +104,10 @@ public String getNewsHeadline(@P("topic") String topic) {
public static void main(String[] args) {
AgentRuntime runtime = new AgentRuntime();
- // apiKey is required by LangChain4j's builder but unused — Agentspan
+ // apiKey is required by LangChain4j's builder but unused — Conductor
// runs the LLM call on the server with server-registered credentials.
ChatModel model = OpenAiChatModel.builder()
- .apiKey("agentspan-server-handles-credentials")
+ .apiKey("conductor-server-handles-credentials")
.modelName("gpt-4o-mini")
.build();
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example09MathCalculator.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example09MathCalculator.java
index 99f9b59e2..a742e4054 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example09MathCalculator.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example09MathCalculator.java
@@ -244,10 +244,10 @@ private static String trimNum(double v) {
public static void main(String[] args) {
AgentRuntime runtime = new AgentRuntime();
- // apiKey is required by LangChain4j's builder but unused — Agentspan
+ // apiKey is required by LangChain4j's builder but unused — Conductor
// runs the LLM call on the server with server-registered credentials.
ChatModel model = OpenAiChatModel.builder()
- .apiKey("agentspan-server-handles-credentials")
+ .apiKey("conductor-server-handles-credentials")
.modelName("gpt-4o-mini")
.build();
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example10WebSearchAgent.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example10WebSearchAgent.java
index 96cc3e25b..4b9c91c86 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example10WebSearchAgent.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example10WebSearchAgent.java
@@ -121,10 +121,10 @@ public String summarizeResults(@dev.langchain4j.agent.tool.P("text") String text
public static void main(String[] args) {
AgentRuntime runtime = new AgentRuntime();
- // apiKey is required by LangChain4j's builder but unused — Agentspan
+ // apiKey is required by LangChain4j's builder but unused — Conductor
// runs the LLM call on the server with server-registered credentials.
ChatModel model = OpenAiChatModel.builder()
- .apiKey("agentspan-server-handles-credentials")
+ .apiKey("conductor-server-handles-credentials")
.modelName("gpt-4o-mini")
.build();
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example11CodeReviewAgent.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example11CodeReviewAgent.java
index 1dd53821b..099b1fc74 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example11CodeReviewAgent.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example11CodeReviewAgent.java
@@ -161,10 +161,10 @@ private static boolean isValidIdent(String s) {
public static void main(String[] args) {
AgentRuntime runtime = new AgentRuntime();
- // apiKey is required by LangChain4j's builder but unused — Agentspan
+ // apiKey is required by LangChain4j's builder but unused — Conductor
// runs the LLM call on the server with server-registered credentials.
ChatModel model = OpenAiChatModel.builder()
- .apiKey("agentspan-server-handles-credentials")
+ .apiKey("conductor-server-handles-credentials")
.modelName("gpt-4o-mini")
.build();
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example12DocumentSummarizer.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example12DocumentSummarizer.java
index 2c1690b4c..d2130c974 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example12DocumentSummarizer.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example12DocumentSummarizer.java
@@ -125,10 +125,10 @@ public String extractKeySentences(
public static void main(String[] args) {
AgentRuntime runtime = new AgentRuntime();
- // apiKey is required by LangChain4j's builder but unused — Agentspan
+ // apiKey is required by LangChain4j's builder but unused — Conductor
// runs the LLM call on the server with server-registered credentials.
ChatModel model = OpenAiChatModel.builder()
- .apiKey("agentspan-server-handles-credentials")
+ .apiKey("conductor-server-handles-credentials")
.modelName("gpt-4o-mini")
.build();
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example13CustomerServiceAgent.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example13CustomerServiceAgent.java
index c769fcfbf..f04b8df35 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example13CustomerServiceAgent.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example13CustomerServiceAgent.java
@@ -99,10 +99,10 @@ public String createSupportTicket(
public static void main(String[] args) {
AgentRuntime runtime = new AgentRuntime();
- // apiKey is required by LangChain4j's builder but unused — Agentspan
+ // apiKey is required by LangChain4j's builder but unused — Conductor
// runs the LLM call on the server with server-registered credentials.
ChatModel model = OpenAiChatModel.builder()
- .apiKey("agentspan-server-handles-credentials")
+ .apiKey("conductor-server-handles-credentials")
.modelName("gpt-4o-mini")
.build();
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example14ResearchAssistant.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example14ResearchAssistant.java
index e1932c4c7..d10662c72 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example14ResearchAssistant.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example14ResearchAssistant.java
@@ -114,10 +114,10 @@ public String getStatistics(@dev.langchain4j.agent.tool.P("domain") String domai
public static void main(String[] args) {
AgentRuntime runtime = new AgentRuntime();
- // apiKey is required by LangChain4j's builder but unused — Agentspan
+ // apiKey is required by LangChain4j's builder but unused — Conductor
// runs the LLM call on the server with server-registered credentials.
ChatModel model = OpenAiChatModel.builder()
- .apiKey("agentspan-server-handles-credentials")
+ .apiKey("conductor-server-handles-credentials")
.modelName("gpt-4o-mini")
.build();
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example15DataAnalyst.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example15DataAnalyst.java
index bba2a0396..7979bbecc 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example15DataAnalyst.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example15DataAnalyst.java
@@ -194,10 +194,10 @@ public String detectOutliers(
public static void main(String[] args) {
AgentRuntime runtime = new AgentRuntime();
- // apiKey is required by LangChain4j's builder but unused — Agentspan
+ // apiKey is required by LangChain4j's builder but unused — Conductor
// runs the LLM call on the server with server-registered credentials.
ChatModel model = OpenAiChatModel.builder()
- .apiKey("agentspan-server-handles-credentials")
+ .apiKey("conductor-server-handles-credentials")
.modelName("gpt-4o-mini")
.build();
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example16ContentWriter.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example16ContentWriter.java
index 2934e06fd..92ff055a5 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example16ContentWriter.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example16ContentWriter.java
@@ -146,10 +146,10 @@ private static String titleCase(String s) {
public static void main(String[] args) {
AgentRuntime runtime = new AgentRuntime();
- // apiKey is required by LangChain4j's builder but unused — Agentspan
+ // apiKey is required by LangChain4j's builder but unused — Conductor
// runs the LLM call on the server with server-registered credentials.
ChatModel model = OpenAiChatModel.builder()
- .apiKey("agentspan-server-handles-credentials")
+ .apiKey("conductor-server-handles-credentials")
.modelName("gpt-4o-mini")
.build();
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example17SqlAgent.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example17SqlAgent.java
index b0a573ceb..8735ba8f3 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example17SqlAgent.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example17SqlAgent.java
@@ -427,10 +427,10 @@ public static void main(String[] args) {
@SuppressWarnings("unused")
List _unused = Arrays.asList("ref");
- // apiKey is required by LangChain4j's builder but unused — Agentspan
+ // apiKey is required by LangChain4j's builder but unused — Conductor
// runs the LLM call on the server with server-registered credentials.
ChatModel model = OpenAiChatModel.builder()
- .apiKey("agentspan-server-handles-credentials")
+ .apiKey("conductor-server-handles-credentials")
.modelName("gpt-4o-mini")
.build();
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example18EmailDrafter.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example18EmailDrafter.java
index da344a7e8..662f3792a 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example18EmailDrafter.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example18EmailDrafter.java
@@ -113,10 +113,10 @@ public String formatEmailTemplate(
public static void main(String[] args) {
AgentRuntime runtime = new AgentRuntime();
- // apiKey is required by LangChain4j's builder but unused — Agentspan
+ // apiKey is required by LangChain4j's builder but unused — Conductor
// runs the LLM call on the server with server-registered credentials.
ChatModel model = OpenAiChatModel.builder()
- .apiKey("agentspan-server-handles-credentials")
+ .apiKey("conductor-server-handles-credentials")
.modelName("gpt-4o-mini")
.build();
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example19FactChecker.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example19FactChecker.java
index 74c89b921..589711d28 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example19FactChecker.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example19FactChecker.java
@@ -157,10 +157,10 @@ public String extractClaims(@dev.langchain4j.agent.tool.P("text") String text) {
public static void main(String[] args) {
AgentRuntime runtime = new AgentRuntime();
- // apiKey is required by LangChain4j's builder but unused — Agentspan
+ // apiKey is required by LangChain4j's builder but unused — Conductor
// runs the LLM call on the server with server-registered credentials.
ChatModel model = OpenAiChatModel.builder()
- .apiKey("agentspan-server-handles-credentials")
+ .apiKey("conductor-server-handles-credentials")
.modelName("gpt-4o-mini")
.build();
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example20TranslationAgent.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example20TranslationAgent.java
index 291d6e456..330b7f8c7 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example20TranslationAgent.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example20TranslationAgent.java
@@ -161,10 +161,10 @@ public String getLanguageFacts(@dev.langchain4j.agent.tool.P("language") String
public static void main(String[] args) {
AgentRuntime runtime = new AgentRuntime();
- // apiKey is required by LangChain4j's builder but unused — Agentspan
+ // apiKey is required by LangChain4j's builder but unused — Conductor
// runs the LLM call on the server with server-registered credentials.
ChatModel model = OpenAiChatModel.builder()
- .apiKey("agentspan-server-handles-credentials")
+ .apiKey("conductor-server-handles-credentials")
.modelName("gpt-4o-mini")
.build();
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example21SentimentAnalysis.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example21SentimentAnalysis.java
index 565ac0e28..3904fa4cf 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example21SentimentAnalysis.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example21SentimentAnalysis.java
@@ -163,10 +163,10 @@ private static int countIntersect(Set a, Set b) {
public static void main(String[] args) {
AgentRuntime runtime = new AgentRuntime();
- // apiKey is required by LangChain4j's builder but unused — Agentspan
+ // apiKey is required by LangChain4j's builder but unused — Conductor
// runs the LLM call on the server with server-registered credentials.
ChatModel model = OpenAiChatModel.builder()
- .apiKey("agentspan-server-handles-credentials")
+ .apiKey("conductor-server-handles-credentials")
.modelName("gpt-4o-mini")
.build();
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example22ClassificationAgent.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example22ClassificationAgent.java
index 489ff8cdd..0af1cbc80 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example22ClassificationAgent.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example22ClassificationAgent.java
@@ -154,10 +154,10 @@ public String getCategoryExamples(@dev.langchain4j.agent.tool.P("category") Stri
public static void main(String[] args) {
AgentRuntime runtime = new AgentRuntime();
- // apiKey is required by LangChain4j's builder but unused — Agentspan
+ // apiKey is required by LangChain4j's builder but unused — Conductor
// runs the LLM call on the server with server-registered credentials.
ChatModel model = OpenAiChatModel.builder()
- .apiKey("agentspan-server-handles-credentials")
+ .apiKey("conductor-server-handles-credentials")
.modelName("gpt-4o-mini")
.build();
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example23RecommendationAgent.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example23RecommendationAgent.java
index 87d1460f2..cb511ae85 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example23RecommendationAgent.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example23RecommendationAgent.java
@@ -189,10 +189,10 @@ private static Book findByTitle(String title) {
public static void main(String[] args) {
AgentRuntime runtime = new AgentRuntime();
- // apiKey is required by LangChain4j's builder but unused — Agentspan
+ // apiKey is required by LangChain4j's builder but unused — Conductor
// runs the LLM call on the server with server-registered credentials.
ChatModel model = OpenAiChatModel.builder()
- .apiKey("agentspan-server-handles-credentials")
+ .apiKey("conductor-server-handles-credentials")
.modelName("gpt-4o-mini")
.build();
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example24OutputParsers.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example24OutputParsers.java
index 6f16250d7..21a68b111 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example24OutputParsers.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example24OutputParsers.java
@@ -39,7 +39,7 @@
* {@code PydanticOutputParser}. The Java port uses prompt-based JSON return
* shapes plus consumer-side Jackson deserialization — LangChain4j's
* {@code AiServices} typed-return analog isn't applicable in
- * {@link Agentspan#run(ChatModel, String, Object...)} extraction mode
+ * {@link AgentRuntime#run(ChatModel, String, Object...)} extraction mode
* (server-side LLM loop).
*/
public class Example24OutputParsers {
@@ -163,10 +163,10 @@ public static void main(String[] args) {
@SuppressWarnings("unused")
ExtractedFields example = new ExtractedFields("2025-03-15", "$249.99", "billing@example.com");
- // apiKey is required by LangChain4j's builder but unused — Agentspan
+ // apiKey is required by LangChain4j's builder but unused — Conductor
// runs the LLM call on the server with server-registered credentials.
ChatModel model = OpenAiChatModel.builder()
- .apiKey("agentspan-server-handles-credentials")
+ .apiKey("conductor-server-handles-credentials")
.modelName("gpt-4o-mini")
.build();
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example25AdvancedOrchestration.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example25AdvancedOrchestration.java
index 632f5b643..f22a59197 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example25AdvancedOrchestration.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example25AdvancedOrchestration.java
@@ -233,10 +233,10 @@ private static Map parseSimpleJsonNumbers(String json) {
public static void main(String[] args) {
AgentRuntime runtime = new AgentRuntime();
- // apiKey is required by LangChain4j's builder but unused — Agentspan
+ // apiKey is required by LangChain4j's builder but unused — Conductor
// runs the LLM call on the server with server-registered credentials.
ChatModel model = OpenAiChatModel.builder()
- .apiKey("agentspan-server-handles-credentials")
+ .apiKey("conductor-server-handles-credentials")
.modelName("gpt-4o-mini")
.build();
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example26AgentspanGuardrails.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example26ConductorGuardrails.java
similarity index 89%
rename from agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example26AgentspanGuardrails.java
rename to agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example26ConductorGuardrails.java
index 826802446..904b55d4d 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example26AgentspanGuardrails.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/Example26ConductorGuardrails.java
@@ -27,25 +27,25 @@
import dev.langchain4j.model.openai.OpenAiChatModel;
/**
- * Example LangChain 26 — Agentspan guardrails on a native LangChain4j agent.
+ * Example LangChain4j 26 — Conductor guardrails on a native LangChain4j agent.
*
- * LangChain4j has no built-in guardrail abstraction. Agentspan provides a
+ *
LangChain4j has no built-in guardrail abstraction. Conductor provides a
* server-compiled guardrail mechanism that runs as a Conductor task inside the
* agent's loop. This example attaches a PII-redaction guardrail to a pure
* LangChain4j agent built from a native {@code ChatModel}.
*
*
Pattern (same as
- * {@code adk.Example38AgentspanGuardrails}):
+ * {@code adk.Example38ConductorGuardrails}):
*
* Build native LangChain4j {@code ChatModel}.
* Hand it to {@link LangChainBridge#agentBuilder} (the builder variant
* lets you decorate before building).
* Attach a {@link GuardrailDef} with {@link OnFail#FIX} so the
* guardrail rewrites the output in place.
- * {@link Agentspan#run}.
+ * {@link AgentRuntime#run}.
*
*/
-public class Example26AgentspanGuardrails {
+public class Example26ConductorGuardrails {
private static final Pattern EMAIL = Pattern.compile(
"\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}\\b");
@@ -64,10 +64,10 @@ private static GuardrailResult redactPii(String content) {
public static void main(String[] args) {
AgentRuntime runtime = new AgentRuntime();
- // apiKey is required by LangChain4j's builder but unused — Agentspan
+ // apiKey is required by LangChain4j's builder but unused — Conductor
// runs the LLM call on the server with server-registered credentials.
ChatModel model = OpenAiChatModel.builder()
- .apiKey("agentspan-server-handles-credentials")
+ .apiKey("conductor-server-handles-credentials")
.modelName("gpt-4o-mini")
.build();
@@ -76,7 +76,7 @@ public static void main(String[] args) {
.position(Position.OUTPUT)
.onFail(OnFail.FIX)
.maxRetries(1)
- .func(Example26AgentspanGuardrails::redactPii)
+ .func(Example26ConductorGuardrails::redactPii)
.build();
Agent guarded = LangChainBridge.agentBuilder(
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/ExampleCredentials.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/ExampleCredentials.java
index b2a3a5ce6..501981824 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/ExampleCredentials.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/ExampleCredentials.java
@@ -32,14 +32,14 @@
* Demonstrates mixing:
*
* Native LangChain4j {@code @Tool} methods that perform pure computation (no secrets)
- * An Agentspan {@link Tool}-annotated method that reads a credential injected
+ * An Conductor {@link Tool}-annotated method that reads a credential injected
* as an environment variable by the server (via the {@code credentials} field)
*
*
* Credential injection pattern:
*
* Declare credential names in {@code @Tool(credentials = {"MY_API_KEY"})}
- * Store the secret once via the CLI: {@code agentspan credentials set --name MY_API_KEY}
+ * Store the secret once via the CLI: {@code conductor credentials set --name MY_API_KEY}
* At runtime, the server resolves the credential and injects it as
* an environment variable before invoking the worker. Read it with
* {@code System.getenv("MY_API_KEY")}.
@@ -49,10 +49,10 @@
*
* Requirements:
*
- * {@code AGENTSPAN_SERVER_URL=http://localhost:6767}
+ * {@code CONDUCTOR_SERVER_URL=http://localhost:6767}
* langchain4j on the classpath (see examples/build.gradle)
- * Agentspan server with OpenAI credentials configured server-side.
- * Credential {@code WEATHER_API_KEY} registered in Agentspan (optional — example
+ * Conductor server with OpenAI credentials configured server-side.
+ * Credential {@code WEATHER_API_KEY} registered in Conductor (optional — example
* works without it, falling back to a stubbed response)
*
*/
@@ -79,14 +79,14 @@ public double fahrenheitToCelsius(@dev.langchain4j.agent.tool.P("fahrenheit") do
}
}
- // ── Agentspan @Tool class: reads a credential injected by the server ──────
+ // ── Conductor @Tool class: reads a credential injected by the server ──────
static class WeatherTools {
/**
* Fetches current weather for a city.
*
- * The server resolves {@code WEATHER_API_KEY} from the Agentspan credential
+ *
The server resolves {@code WEATHER_API_KEY} from the Conductor credential
* store and injects it as an environment variable before calling this worker.
* The tool reads it via {@code System.getenv("WEATHER_API_KEY")}.
*/
@@ -111,16 +111,16 @@ public String getWeather(String city) {
public static void main(String[] args) {
AgentRuntime runtime = new AgentRuntime();
- // apiKey is required by LangChain4j's builder but unused — Agentspan
+ // apiKey is required by LangChain4j's builder but unused — Conductor
// runs the LLM call on the server with server-registered credentials.
ChatModel model = OpenAiChatModel.builder()
- .apiKey("agentspan-server-handles-credentials")
+ .apiKey("conductor-server-handles-credentials")
.modelName("gpt-4o-mini")
.build();
// Build the LangChain4j-backed agent (unit conversion tools) via the
// advanced LangChainBridge.agentBuilder(...) path so we can merge in
- // Agentspan @Tool credential-aware tools before .build().
+ // Conductor @Tool credential-aware tools before .build().
Agent lc4jAgent = LangChainBridge.agentBuilder(
"lc4j_weather_agent",
model,
@@ -129,7 +129,7 @@ public static void main(String[] args) {
new UnitTools())
.build();
- // Agentspan @Tool tools (credential-aware) — build separately and merge in.
+ // Conductor @Tool tools (credential-aware) — build separately and merge in.
List credentialTools = ToolRegistry.fromInstance(new WeatherTools());
// Merge the credential-aware tool into the agent by rebuilding it.
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/ExamplePipeline.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/ExamplePipeline.java
index 3eb727e61..5e94f1636 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/ExamplePipeline.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langchain/ExamplePipeline.java
@@ -24,28 +24,28 @@
* Example Lc4j 04 — LangChain4j Agent in a Sequential Pipeline (native LangChain4j SDK)
*
* Demonstrates interoperability between a LangChain4j-backed agent and a
- * regular Agentspan agent inside a sequential pipeline (using {@link Agent#then}).
+ * regular Conductor agent inside a sequential pipeline (using {@link Agent#then}).
*
*
Pipeline stages:
*
* data_gatherer — built from native LangChain4j {@code @Tool} methods
* that look up product data. The LLM calls the tools and produces
* a structured data payload.
- * report_writer — a plain Agentspan agent (no tools) that
+ * report_writer — a plain Conductor agent (no tools) that
* receives the data payload as its input and writes a human-readable
* report.
*
*
* This pattern shows that
* {@link LangChainBridge#agentBuilder} returns a standard {@link Agent} via
- * {@code .build()} — it composes naturally with any other Agentspan agent or
+ * {@code .build()} — it composes naturally with any other Conductor agent or
* orchestration strategy.
*
*
Requirements:
*
- * {@code AGENTSPAN_SERVER_URL=http://localhost:6767}
+ * {@code CONDUCTOR_SERVER_URL=http://localhost:6767}
* langchain4j on the classpath (see examples/build.gradle)
- * Agentspan server with OpenAI credentials configured server-side.
+ * Conductor server with OpenAI credentials configured server-side.
*
*/
public class ExamplePipeline {
@@ -89,10 +89,10 @@ public java.util.Map getSalesStats(
public static void main(String[] args) {
AgentRuntime runtime = new AgentRuntime();
- // apiKey is required by LangChain4j's builder but unused — Agentspan
+ // apiKey is required by LangChain4j's builder but unused — Conductor
// runs the LLM call on the server with server-registered credentials.
ChatModel model = OpenAiChatModel.builder()
- .apiKey("agentspan-server-handles-credentials")
+ .apiKey("conductor-server-handles-credentials")
.modelName("gpt-4o-mini")
.build();
@@ -107,7 +107,7 @@ public static void main(String[] args) {
new ProductDataTools())
.build();
- // Stage 2: Plain Agentspan agent (no tools) — receives the data summary and writes a report.
+ // Stage 2: Plain Conductor agent (no tools) — receives the data summary and writes a report.
// Use the same provider/model string the bridge derived for stage 1.
Agent reportWriter = Agent.builder()
.name("report_writer")
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langgraph/Example01HelloWorld.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langgraph/Example01HelloWorld.java
index 3d8f94094..8a9b106ed 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langgraph/Example01HelloWorld.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langgraph/Example01HelloWorld.java
@@ -24,17 +24,17 @@
*
* Builds a real LangGraph4j {@code AgentExecutor.Builder} (the same builder
* the LangGraph4j docs use for the prebuilt ReAct agent) and hands it directly
- * to {@link Agentspan#run(AgentExecutor.Builder, String, Object...)} via the
- * drop-in overload so it runs on the durable Agentspan runtime.
+ * to {@link AgentRuntime#run(AgentExecutor.Builder, String, Object...)} via the
+ * drop-in overload so it runs on the durable Conductor runtime.
*/
public class Example01HelloWorld {
public static void main(String[] args) {
AgentRuntime runtime = new AgentRuntime();
- // apiKey is required by LangChain4j's builder but unused — Agentspan
+ // apiKey is required by LangChain4j's builder but unused — Conductor
// runs the LLM call on the server with server-registered credentials.
ChatModel model = OpenAiChatModel.builder()
- .apiKey("agentspan-server-handles-credentials")
+ .apiKey("conductor-server-handles-credentials")
.modelName("gpt-4o-mini")
.build();
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langgraph/Example02ReactWithTools.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langgraph/Example02ReactWithTools.java
index 770a6674f..40f69eac7 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langgraph/Example02ReactWithTools.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langgraph/Example02ReactWithTools.java
@@ -29,13 +29,13 @@
*
Java port (concepts) of
* sdk/python/examples/langgraph/02_react_with_tools.py. Builds a
* real LangGraph4j {@code AgentExecutor.Builder} (a ReAct {@code StateGraph})
- * and hands it straight to {@link Agentspan#run} via the drop-in overload.
+ * and hands it straight to {@link AgentRuntime#run} via the drop-in overload.
*
*
Demonstrates:
*
* Defining tools with native {@link Tool @Tool} on a POJO
* Passing the tool POJO straight to
- * {@link Agentspan#run(AgentExecutor.Builder, String, Object...)} via
+ * {@link AgentRuntime#run(AgentExecutor.Builder, String, Object...)} via
* the drop-in overload — internally LangGraph4j calls
* {@code toolsFromObject(...)}
* Calculator, word count, and date utilities
@@ -68,10 +68,10 @@ public String getToday() {
public static void main(String[] args) {
AgentRuntime runtime = new AgentRuntime();
- // apiKey is required by LangChain4j's builder but unused — Agentspan
+ // apiKey is required by LangChain4j's builder but unused — Conductor
// runs the LLM call on the server with server-registered credentials.
ChatModel model = OpenAiChatModel.builder()
- .apiKey("agentspan-server-handles-credentials")
+ .apiKey("conductor-server-handles-credentials")
.modelName("gpt-4o-mini")
.build();
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langgraph/Example03Memory.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langgraph/Example03Memory.java
index cb3f4b0ae..95a5da87d 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langgraph/Example03Memory.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langgraph/Example03Memory.java
@@ -24,15 +24,15 @@
*
* Inspired by sdk/python/examples/langgraph/03_memory.py which
* attaches a {@code MemorySaver} checkpointer to {@code create_agent}. In this
- * Java/Agentspan port we demonstrate the same surface — a single agent invoked
+ * Java/Conductor port we demonstrate the same surface — a single agent invoked
* three times in sequence — but without an in-process checkpointer. The history
* is passed back to the LLM directly inside the prompt for each turn, which is
- * the simplest portable pattern when running on the durable Agentspan runtime.
+ * the simplest portable pattern when running on the durable Conductor runtime.
*
*
Demonstrates:
*
* Reusing a single {@link AgentExecutor.Builder}-built agent for
- * multiple turns via the drop-in {@link Agentspan#run} overload
+ * multiple turns via the drop-in {@link AgentRuntime#run} overload
* Pure prompt-based history (no in-memory checkpointer required)
* How an LLM can recall facts when given prior context
*
@@ -41,10 +41,10 @@ public class Example03Memory {
public static void main(String[] args) {
AgentRuntime runtime = new AgentRuntime();
- // apiKey is required by LangChain4j's builder but unused — Agentspan
+ // apiKey is required by LangChain4j's builder but unused — Conductor
// runs the LLM call on the server with server-registered credentials.
ChatModel model = OpenAiChatModel.builder()
- .apiKey("agentspan-server-handles-credentials")
+ .apiKey("conductor-server-handles-credentials")
.modelName("gpt-4o-mini")
.build();
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langgraph/Example04SimpleStateGraph.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langgraph/Example04SimpleStateGraph.java
index f49e66e42..a090ca784 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langgraph/Example04SimpleStateGraph.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langgraph/Example04SimpleStateGraph.java
@@ -26,7 +26,7 @@
*
* Conceptually mirrors sdk/python/examples/langgraph/04_simple_stategraph.py
* which builds a 3-node {@code StateGraph} pipeline. In the LangGraph4j
- * agent-executor + Agentspan pattern, multi-node pipelines are most cleanly
+ * agent-executor + Conductor pattern, multi-node pipelines are most cleanly
* expressed by giving the ReAct loop a small set of pipeline-stage tools and
* letting the LLM walk through them in order — the agent still produces the
* same query -> refined -> answer flow as the Python pipeline.
@@ -37,7 +37,7 @@
* Tool-sequencing instructions folded into the user message
* (validate -> refine -> answer)
* Building the LangGraph4j {@code AgentExecutor.Builder} and handing it
- * straight to {@link Agentspan#run}
+ * straight to {@link AgentRuntime#run}
*
*/
public class Example04SimpleStateGraph {
@@ -69,10 +69,10 @@ public String recordAnswer(@P("answer") String answer) {
public static void main(String[] args) {
AgentRuntime runtime = new AgentRuntime();
- // apiKey is required by LangChain4j's builder but unused — Agentspan
+ // apiKey is required by LangChain4j's builder but unused — Conductor
// runs the LLM call on the server with server-registered credentials.
ChatModel model = OpenAiChatModel.builder()
- .apiKey("agentspan-server-handles-credentials")
+ .apiKey("conductor-server-handles-credentials")
.modelName("gpt-4o-mini")
.build();
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langgraph/Example05ToolNode.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langgraph/Example05ToolNode.java
index fd0ee6174..710b83e88 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langgraph/Example05ToolNode.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langgraph/Example05ToolNode.java
@@ -76,10 +76,10 @@ public String lookupPopulation(@P("country") String country) {
public static void main(String[] args) {
AgentRuntime runtime = new AgentRuntime();
- // apiKey is required by LangChain4j's builder but unused — Agentspan
+ // apiKey is required by LangChain4j's builder but unused — Conductor
// runs the LLM call on the server with server-registered credentials.
ChatModel model = OpenAiChatModel.builder()
- .apiKey("agentspan-server-handles-credentials")
+ .apiKey("conductor-server-handles-credentials")
.modelName("gpt-4o-mini")
.build();
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langgraph/Example06ConditionalRouting.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langgraph/Example06ConditionalRouting.java
index 3ff749988..b0985ff91 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langgraph/Example06ConditionalRouting.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langgraph/Example06ConditionalRouting.java
@@ -73,10 +73,10 @@ public String handleNeutral(@P("text") String text) {
public static void main(String[] args) {
AgentRuntime runtime = new AgentRuntime();
- // apiKey is required by LangChain4j's builder but unused — Agentspan
+ // apiKey is required by LangChain4j's builder but unused — Conductor
// runs the LLM call on the server with server-registered credentials.
ChatModel model = OpenAiChatModel.builder()
- .apiKey("agentspan-server-handles-credentials")
+ .apiKey("conductor-server-handles-credentials")
.modelName("gpt-4o-mini")
.build();
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langgraph/Example07SystemPrompt.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langgraph/Example07SystemPrompt.java
index 22cb05bc9..32940113e 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langgraph/Example07SystemPrompt.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langgraph/Example07SystemPrompt.java
@@ -24,7 +24,7 @@
*
* Mirrors sdk/python/examples/langgraph/07_system_prompt.py
* which passes a {@code system_prompt=...} to {@code create_agent}. The
- * drop-in {@link Agentspan#run} overload takes a single user prompt — fold
+ * drop-in {@link AgentRuntime#run} overload takes a single user prompt — fold
* the persona into that prompt as a leading section so it steers every reply.
*
*
Demonstrates:
@@ -53,10 +53,10 @@ public class Example07SystemPrompt {
public static void main(String[] args) {
AgentRuntime runtime = new AgentRuntime();
- // apiKey is required by LangChain4j's builder but unused — Agentspan
+ // apiKey is required by LangChain4j's builder but unused — Conductor
// runs the LLM call on the server with server-registered credentials.
ChatModel model = OpenAiChatModel.builder()
- .apiKey("agentspan-server-handles-credentials")
+ .apiKey("conductor-server-handles-credentials")
.modelName("gpt-4o-mini")
.build();
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langgraph/Example08StructuredOutput.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langgraph/Example08StructuredOutput.java
index 5f2758d00..cb0d22774 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langgraph/Example08StructuredOutput.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langgraph/Example08StructuredOutput.java
@@ -65,10 +65,10 @@ private static String escape(String s) {
public static void main(String[] args) {
AgentRuntime runtime = new AgentRuntime();
- // apiKey is required by LangChain4j's builder but unused — Agentspan
+ // apiKey is required by LangChain4j's builder but unused — Conductor
// runs the LLM call on the server with server-registered credentials.
ChatModel model = OpenAiChatModel.builder()
- .apiKey("agentspan-server-handles-credentials")
+ .apiKey("conductor-server-handles-credentials")
.modelName("gpt-4o-mini")
.build();
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langgraph/Example09MathAgent.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langgraph/Example09MathAgent.java
index ad3c3d003..e4d04223a 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langgraph/Example09MathAgent.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langgraph/Example09MathAgent.java
@@ -83,10 +83,10 @@ public String factorial(@P("n") int n) {
public static void main(String[] args) {
AgentRuntime runtime = new AgentRuntime();
- // apiKey is required by LangChain4j's builder but unused — Agentspan
+ // apiKey is required by LangChain4j's builder but unused — Conductor
// runs the LLM call on the server with server-registered credentials.
ChatModel model = OpenAiChatModel.builder()
- .apiKey("agentspan-server-handles-credentials")
+ .apiKey("conductor-server-handles-credentials")
.modelName("gpt-4o-mini")
.build();
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langgraph/Example10ResearchAgent.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langgraph/Example10ResearchAgent.java
index fde82cd6e..fb9da22fd 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langgraph/Example10ResearchAgent.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langgraph/Example10ResearchAgent.java
@@ -114,10 +114,10 @@ public String citeSource(@P("claim") String claim, @P("source_type") String sour
public static void main(String[] args) {
AgentRuntime runtime = new AgentRuntime();
- // apiKey is required by LangChain4j's builder but unused — Agentspan
+ // apiKey is required by LangChain4j's builder but unused — Conductor
// runs the LLM call on the server with server-registered credentials.
ChatModel model = OpenAiChatModel.builder()
- .apiKey("agentspan-server-handles-credentials")
+ .apiKey("conductor-server-handles-credentials")
.modelName("gpt-4o-mini")
.build();
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langgraph/Example11CustomerSupport.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langgraph/Example11CustomerSupport.java
index fc35d467d..f1c084a57 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langgraph/Example11CustomerSupport.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/langgraph/Example11CustomerSupport.java
@@ -84,10 +84,10 @@ public String handleGeneral(@P("user_message") String userMessage) {
public static void main(String[] args) {
AgentRuntime runtime = new AgentRuntime();
- // apiKey is required by LangChain4j's builder but unused — Agentspan
+ // apiKey is required by LangChain4j's builder but unused — Conductor
// runs the LLM call on the server with server-registered credentials.
ChatModel model = OpenAiChatModel.builder()
- .apiKey("agentspan-server-handles-credentials")
+ .apiKey("conductor-server-handles-credentials")
.modelName("gpt-4o-mini")
.build();
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/openai/Example01BasicAgent.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/openai/Example01BasicAgent.java
index 9f65c57c6..c252d54c8 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/openai/Example01BasicAgent.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/openai/Example01BasicAgent.java
@@ -24,14 +24,14 @@
*
Java port of sdk/python/examples/openai/01_basic_agent.py.
*
*
Demonstrates: the simplest possible OpenAI Agents SDK agent — no tools,
- * just a name + instructions + model — wired through the Agentspan
+ * just a name + instructions + model — wired through the Conductor
* {@link OpenAIAgent} factory so the server normalizes it into a Conductor
* workflow.
*
*
Requirements:
*
- * AGENTSPAN_SERVER_URL=http://localhost:6767/api
- * AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini
+ * CONDUCTOR_SERVER_URL=http://localhost:6767/api
+ * CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini
*
*/
public class Example01BasicAgent {
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/openai/Example02FunctionTools.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/openai/Example02FunctionTools.java
index ba9418c4d..c33fdde4d 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/openai/Example02FunctionTools.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/openai/Example02FunctionTools.java
@@ -29,19 +29,19 @@
*
* Demonstrates: an OpenAI Agents SDK-style agent that calls multiple
* function-tools (weather, calculator, population lookup). Each tool method
- * carries an Agentspan {@link Tool} annotation — the {@link OpenAIAgent}
- * reflection bridge wraps them as Agentspan worker tools.
+ * carries an Conductor {@link Tool} annotation — the {@link OpenAIAgent}
+ * reflection bridge wraps them as Conductor worker tools.
*
*
Note on annotation choice: the Python example uses
* {@code @function_tool}; in Java the {@code OpenAIAgent} factory accepts
* both {@code @org.conductoross.conductor.ai.annotations.Tool} and
- * {@code @dev.langchain4j.agent.tool.Tool}. We use the Agentspan annotation
+ * {@code @dev.langchain4j.agent.tool.Tool}. We use the Conductor annotation
* here because LangChain4j is only a {@code compileOnly} SDK dependency.
*
*
Requirements:
*
- * AGENTSPAN_SERVER_URL=http://localhost:6767/api
- * AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini
+ * CONDUCTOR_SERVER_URL=http://localhost:6767/api
+ * CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini
*
*/
public class Example02FunctionTools {
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/openai/Example03StructuredOutput.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/openai/Example03StructuredOutput.java
index 489d6ce13..296f2ff9b 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/openai/Example03StructuredOutput.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/openai/Example03StructuredOutput.java
@@ -47,8 +47,8 @@
*
* Requirements:
*
- * AGENTSPAN_SERVER_URL=http://localhost:6767/api
- * AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini
+ * CONDUCTOR_SERVER_URL=http://localhost:6767/api
+ * CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini
*
*/
public class Example03StructuredOutput {
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/openai/Example04Handoffs.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/openai/Example04Handoffs.java
index 0b764f7fd..9a2380194 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/openai/Example04Handoffs.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/openai/Example04Handoffs.java
@@ -34,8 +34,8 @@
*
* Requirements:
*
- * AGENTSPAN_SERVER_URL=http://localhost:6767/api
- * AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini
+ * CONDUCTOR_SERVER_URL=http://localhost:6767/api
+ * CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini
*
*/
public class Example04Handoffs {
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/openai/Example05Guardrails.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/openai/Example05Guardrails.java
index 5218de0b0..961944fdb 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/openai/Example05Guardrails.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/openai/Example05Guardrails.java
@@ -45,8 +45,8 @@
*
* Requirements:
*
- * AGENTSPAN_SERVER_URL=http://localhost:6767/api
- * AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini
+ * CONDUCTOR_SERVER_URL=http://localhost:6767/api
+ * CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini
*
*/
public class Example05Guardrails {
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/openai/Example06ModelSettings.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/openai/Example06ModelSettings.java
index 04d1828b4..dec96e71c 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/openai/Example06ModelSettings.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/openai/Example06ModelSettings.java
@@ -38,8 +38,8 @@
*
* Requirements:
*
- * AGENTSPAN_SERVER_URL=http://localhost:6767/api
- * AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini
+ * CONDUCTOR_SERVER_URL=http://localhost:6767/api
+ * CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini
*
*/
public class Example06ModelSettings {
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/openai/Example07Streaming.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/openai/Example07Streaming.java
index 0fd14d799..5a3d3ef96 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/openai/Example07Streaming.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/openai/Example07Streaming.java
@@ -34,8 +34,8 @@
*
* Requirements:
*
- * AGENTSPAN_SERVER_URL=http://localhost:6767/api
- * AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini
+ * CONDUCTOR_SERVER_URL=http://localhost:6767/api
+ * CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini
*
*/
public class Example07Streaming {
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/openai/Example08AgentAsTool.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/openai/Example08AgentAsTool.java
index beb607b7a..a6af30166 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/openai/Example08AgentAsTool.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/openai/Example08AgentAsTool.java
@@ -42,8 +42,8 @@
*
* Requirements:
*
- * AGENTSPAN_SERVER_URL=http://localhost:6767/api
- * AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini
+ * CONDUCTOR_SERVER_URL=http://localhost:6767/api
+ * CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini
*
*/
public class Example08AgentAsTool {
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/openai/Example09DynamicInstructions.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/openai/Example09DynamicInstructions.java
index 311006397..6aa03ed14 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/openai/Example09DynamicInstructions.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/openai/Example09DynamicInstructions.java
@@ -39,8 +39,8 @@
*
* Requirements:
*
- * AGENTSPAN_SERVER_URL=http://localhost:6767/api
- * AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini
+ * CONDUCTOR_SERVER_URL=http://localhost:6767/api
+ * CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini
*
*/
public class Example09DynamicInstructions {
diff --git a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/openai/Example10MultiModel.java b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/openai/Example10MultiModel.java
index efffcafd4..e33b3caf2 100644
--- a/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/openai/Example10MultiModel.java
+++ b/agent-examples/src/main/java/org/conductoross/conductor/ai/examples/openai/Example10MultiModel.java
@@ -44,8 +44,8 @@
*
* Requirements:
*
- * AGENTSPAN_SERVER_URL=http://localhost:6767/api
- * AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini
+ * CONDUCTOR_SERVER_URL=http://localhost:6767/api
+ * CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini
* AGENT_SECONDARY_LLM_MODEL=openai/gpt-4o
*
*/
diff --git a/conductor-client-ai-spring/src/main/java/org/conductoross/conductor/ai/spring/AgentAutoConfiguration.java b/conductor-client-ai-spring/src/main/java/org/conductoross/conductor/ai/spring/AgentAutoConfiguration.java
index 617a3f9ef..063fb0dd6 100644
--- a/conductor-client-ai-spring/src/main/java/org/conductoross/conductor/ai/spring/AgentAutoConfiguration.java
+++ b/conductor-client-ai-spring/src/main/java/org/conductoross/conductor/ai/spring/AgentAutoConfiguration.java
@@ -24,9 +24,9 @@
import io.orkes.conductor.client.spring.OrkesConductorClientAutoConfiguration;
/**
- * Spring Boot auto-configuration for the Agentspan SDK.
+ * Spring Boot auto-configuration for the Conductor agent SDK.
*
- * This configuration wires two beans from {@code agentspan.*} properties:
+ *
This configuration wires two beans from {@code conductor.agent.*} properties:
*
* {@link AgentConfig} — worker-runner tuning (poll interval, thread count)
* {@link AgentRuntime} — the SDK entry point
@@ -50,7 +50,7 @@ public class AgentAutoConfiguration {
@Bean
@ConditionalOnMissingBean
- public AgentConfig agentspanConfig(AgentProperties props) {
+ public AgentConfig conductorAgentConfig(AgentProperties props) {
return new AgentConfig(props.getWorkerPollIntervalMs(), props.getWorkerThreadCount());
}
diff --git a/conductor-client-ai-spring/src/main/java/org/conductoross/conductor/ai/spring/AgentProperties.java b/conductor-client-ai-spring/src/main/java/org/conductoross/conductor/ai/spring/AgentProperties.java
index cea805efb..421251a44 100644
--- a/conductor-client-ai-spring/src/main/java/org/conductoross/conductor/ai/spring/AgentProperties.java
+++ b/conductor-client-ai-spring/src/main/java/org/conductoross/conductor/ai/spring/AgentProperties.java
@@ -15,12 +15,12 @@
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
- * Agentspan-specific tuning knobs for the Spring Boot auto-configuration.
+ * Conductor agent tuning knobs for the Spring Boot auto-configuration.
*
* Server connectivity (URL, auth key/secret) is handled by the Conductor
* Java SDK's own Spring starter via {@code conductor.*} properties — see
* {@link io.orkes.conductor.client.spring.OrkesConductorClientAutoConfiguration}.
- * Only the Agentspan worker-runner settings live here.
+ * Only the Conductor agent worker-runner settings live here.
*
*
{@code
* # application.properties
@@ -30,12 +30,12 @@
* conductor.security.client.key-id=my-key # optional
* conductor.security.client.secret=my-secret # optional
*
- * # Agentspan worker tuning (this class):
- * agentspan.worker-poll-interval-ms=100
- * agentspan.worker-thread-count=1
+ * # Conductor agent worker tuning (this class):
+ * conductor.agent.worker-poll-interval-ms=100
+ * conductor.agent.worker-thread-count=1
* }
*/
-@ConfigurationProperties(prefix = "agentspan")
+@ConfigurationProperties(prefix = "conductor.agent")
public class AgentProperties {
private int workerPollIntervalMs = 100;
diff --git a/conductor-client-ai-spring/src/test/java/org/conductoross/conductor/ai/spring/AgentAutoConfigurationTest.java b/conductor-client-ai-spring/src/test/java/org/conductoross/conductor/ai/spring/AgentAutoConfigurationTest.java
index 55aaa5abf..0783a784c 100644
--- a/conductor-client-ai-spring/src/test/java/org/conductoross/conductor/ai/spring/AgentAutoConfigurationTest.java
+++ b/conductor-client-ai-spring/src/test/java/org/conductoross/conductor/ai/spring/AgentAutoConfigurationTest.java
@@ -41,12 +41,12 @@ private static ApiClient stubApiClient() {
@Test
void wiresConfigAndRuntimeWithDefaults() {
runner.run(ctx -> {
- assertTrue(ctx.containsBean("agentspanConfig"), "AgentConfig bean must be present");
+ assertTrue(ctx.containsBean("conductorAgentConfig"), "AgentConfig bean must be present");
assertTrue(ctx.containsBean("agentRuntime"), "AgentRuntime bean must be present");
- // No agentspanConductorClient bean — ApiClient comes from conductor-client-spring.
+ // No agent-specific client bean — ApiClient comes from conductor-client-spring.
assertFalse(
- ctx.containsBean("agentspanConductorClient"),
+ ctx.containsBean("conductorAgentClient"),
"auto-config must NOT create its own ApiClient — "
+ "that is OrkesConductorClientAutoConfiguration's job");
@@ -58,7 +58,7 @@ void wiresConfigAndRuntimeWithDefaults() {
@Test
void respectsWorkerTuningProperties() {
- runner.withPropertyValues("agentspan.worker-thread-count=4", "agentspan.worker-poll-interval-ms=250")
+ runner.withPropertyValues("conductor.agent.worker-thread-count=4", "conductor.agent.worker-poll-interval-ms=250")
.run(ctx -> {
AgentConfig config = ctx.getBean(AgentConfig.class);
assertEquals(4, config.getWorkerThreadCount());
@@ -68,9 +68,9 @@ void respectsWorkerTuningProperties() {
@Test
void serverUrlPropertiesAreNotAccepted() {
- // agentspan.server-url no longer exists — setting it must not cause an error
+ // conductor.agent.server-url does not exist — setting it must not cause an error
// (Spring ignores unknown properties by default) and must not affect the client.
- runner.withPropertyValues("agentspan.server-url=http://ignored:9090")
+ runner.withPropertyValues("conductor.agent.server-url=http://ignored:9090")
.run(ctx -> assertFalse(
ctx.getStartupFailure() != null, "unknown property must not break context startup"));
}
diff --git a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/AgentConfig.java b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/AgentConfig.java
index a54b8541b..8c849d12e 100644
--- a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/AgentConfig.java
+++ b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/AgentConfig.java
@@ -15,7 +15,7 @@
import java.util.function.Function;
/**
- * Worker-runner tuning for the Agentspan SDK.
+ * Worker-runner tuning for the Conductor agent SDK.
*
* Connection details (server URL, auth key/secret) are NOT here — those are
* transport concerns owned by the Conductor client
@@ -26,15 +26,15 @@
*
*
Environment variables (invalid or empty values fall back to the default):
*
- * {@code AGENTSPAN_WORKER_POLL_INTERVAL} — worker poll interval in ms (default: 100)
- * {@code AGENTSPAN_WORKER_THREADS} — worker thread count (default: 1)
- * {@code AGENTSPAN_AUTO_START_WORKERS} — register + start workers on run/start/stream (default: true)
- * {@code AGENTSPAN_DAEMON_WORKERS} — SDK-owned threads are daemons (default: true)
- * {@code AGENTSPAN_STREAMING_ENABLED} — use SSE for stream(); false = status polling (default: true)
- * {@code AGENTSPAN_LIVENESS_ENABLED} — monitor stateful runs for worker stalls (default: true)
- * {@code AGENTSPAN_LIVENESS_STALL_SECONDS} — seconds a task may sit unpolled before it counts
+ * {@code CONDUCTOR_AGENT_WORKER_POLL_INTERVAL} — worker poll interval in ms (default: 100)
+ * {@code CONDUCTOR_AGENT_WORKER_THREADS} — worker thread count (default: 1)
+ * {@code CONDUCTOR_AGENT_AUTO_START_WORKERS} — register + start workers on run/start/stream (default: true)
+ * {@code CONDUCTOR_AGENT_DAEMON_WORKERS} — SDK-owned threads are daemons (default: true)
+ * {@code CONDUCTOR_AGENT_STREAMING_ENABLED} — use SSE for stream(); false = status polling (default: true)
+ * {@code CONDUCTOR_AGENT_LIVENESS_ENABLED} — monitor stateful runs for worker stalls (default: true)
+ * {@code CONDUCTOR_AGENT_LIVENESS_STALL_SECONDS} — seconds a task may sit unpolled before it counts
* as a stall (default: 30.0)
- * {@code AGENTSPAN_LIVENESS_CHECK_INTERVAL_SECONDS} — seconds between liveness checks
+ * {@code CONDUCTOR_AGENT_LIVENESS_CHECK_INTERVAL_SECONDS} — seconds between liveness checks
* (default: 10.0)
*
*/
@@ -73,14 +73,14 @@ public static AgentConfig fromEnv() {
/** Env-seam variant so tests can exercise parsing without mutating process env. */
static AgentConfig fromEnv(Function env) {
AgentConfig config = new AgentConfig(
- intVar(env, "AGENTSPAN_WORKER_POLL_INTERVAL", 100),
- intVar(env, "AGENTSPAN_WORKER_THREADS", 1));
- config.autoStartWorkers = boolVar(env, "AGENTSPAN_AUTO_START_WORKERS", true);
- config.daemonWorkers = boolVar(env, "AGENTSPAN_DAEMON_WORKERS", true);
- config.streamingEnabled = boolVar(env, "AGENTSPAN_STREAMING_ENABLED", true);
- config.livenessEnabled = boolVar(env, "AGENTSPAN_LIVENESS_ENABLED", true);
- config.livenessStallSeconds = doubleVar(env, "AGENTSPAN_LIVENESS_STALL_SECONDS", 30.0);
- config.livenessCheckIntervalSeconds = doubleVar(env, "AGENTSPAN_LIVENESS_CHECK_INTERVAL_SECONDS", 10.0);
+ intVar(env, "CONDUCTOR_AGENT_WORKER_POLL_INTERVAL", 100),
+ intVar(env, "CONDUCTOR_AGENT_WORKER_THREADS", 1));
+ config.autoStartWorkers = boolVar(env, "CONDUCTOR_AGENT_AUTO_START_WORKERS", true);
+ config.daemonWorkers = boolVar(env, "CONDUCTOR_AGENT_DAEMON_WORKERS", true);
+ config.streamingEnabled = boolVar(env, "CONDUCTOR_AGENT_STREAMING_ENABLED", true);
+ config.livenessEnabled = boolVar(env, "CONDUCTOR_AGENT_LIVENESS_ENABLED", true);
+ config.livenessStallSeconds = doubleVar(env, "CONDUCTOR_AGENT_LIVENESS_STALL_SECONDS", 30.0);
+ config.livenessCheckIntervalSeconds = doubleVar(env, "CONDUCTOR_AGENT_LIVENESS_CHECK_INTERVAL_SECONDS", 10.0);
return config;
}
diff --git a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/AgentRuntime.java b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/AgentRuntime.java
index c89cef859..e0f2a6240 100644
--- a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/AgentRuntime.java
+++ b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/AgentRuntime.java
@@ -32,6 +32,7 @@
import org.conductoross.conductor.ai.execution.CliCommandExecutor;
import org.conductoross.conductor.ai.execution.CliConfig;
import org.conductoross.conductor.ai.internal.AgentConfigSerializer;
+import org.conductoross.conductor.ai.internal.GuardrailHandlerFactory;
import org.conductoross.conductor.ai.internal.ServerLivenessMonitor;
import org.conductoross.conductor.ai.internal.WorkerManager;
import org.conductoross.conductor.ai.model.AgentHandle;
@@ -39,11 +40,8 @@
import org.conductoross.conductor.ai.model.AgentStream;
import org.conductoross.conductor.ai.model.DeploymentInfo;
import org.conductoross.conductor.ai.model.GuardrailDef;
-import org.conductoross.conductor.ai.model.GuardrailResult;
import org.conductoross.conductor.ai.model.ToolDef;
import org.conductoross.conductor.ai.plans.Plan;
-import org.conductoross.conductor.ai.schedule.Schedule;
-import org.conductoross.conductor.ai.schedule.Schedules;
import org.conductoross.conductor.ai.skill.Skill;
import org.conductoross.conductor.ai.termination.AndTermination;
import org.conductoross.conductor.ai.termination.MaxMessageTermination;
@@ -58,6 +56,8 @@
import io.orkes.conductor.client.AgentClient;
import io.orkes.conductor.client.ApiClient;
+import io.orkes.conductor.client.OrkesClients;
+import io.orkes.conductor.client.SchedulerClient;
import io.orkes.conductor.client.SseClient;
import io.orkes.conductor.client.exceptions.SSEUnavailableException;
import io.orkes.conductor.client.http.OrkesAgentClient;
@@ -84,16 +84,16 @@ public class AgentRuntime implements AutoCloseable {
private final AgentConfig config;
/** Single native Conductor client (ApiClient) for the whole runtime — shared by every
- * typed client (AgentClient/WorkerManager/Schedules) and the SSE stream. */
+ * typed client (AgentClient/WorkerManager/SchedulerClient) and the SSE stream. */
private final ApiClient conductorClient;
/** Agent control-plane client (/api/agent/*) built on the shared Conductor client. */
private final AgentClient agentClient;
- /** Standard Conductor workflow client for /api/workflow/* — used by AgentHandle to
- * enrich results with token usage and tool calls after execution completes. */
+ /** Standard Conductor workflow client used by AgentHandle result handling. */
private final WorkflowClient workflowClient;
+ /** Typed scheduler transport shared by the runtime. */
+ private final SchedulerClient schedulerClient;
private final WorkerManager workerManager;
- private volatile Schedules schedules;
/** Create a runtime with a Conductor client and worker tuning both from environment. */
public AgentRuntime() {
@@ -122,10 +122,16 @@ public AgentRuntime(ApiClient conductorClient) {
* @param config worker-runner tuning
*/
public AgentRuntime(ApiClient conductorClient, AgentConfig config) {
+ this(conductorClient, config, new OrkesClients(conductorClient).getSchedulerClient());
+ }
+
+ /** Package-visible typed-client seam for deterministic runtime scheduling tests. */
+ AgentRuntime(ApiClient conductorClient, AgentConfig config, SchedulerClient schedulerClient) {
this.config = config;
this.conductorClient = conductorClient;
this.agentClient = new OrkesAgentClient(conductorClient);
this.workflowClient = new WorkflowClient(conductorClient);
+ this.schedulerClient = schedulerClient;
this.workerManager = new WorkerManager(config, conductorClient);
logger.info("AgentRuntime initialized: {}", conductorClient.getBasePath());
}
@@ -140,6 +146,17 @@ public AgentClient getClient() {
return agentClient;
}
+ /**
+ * Returns the typed client for workflow schedule management.
+ *
+ * Use this client directly with {@code SaveScheduleRequest} and
+ * {@code WorkflowSchedule}; the agent runtime does not add a separate
+ * schedule abstraction.
+ */
+ public SchedulerClient getSchedulerClient() {
+ return schedulerClient;
+ }
+
/**
* Environment-based bootstrap for the no-arg constructors. Construction and
* env resolution (spec R3 chain) live in the client layer —
@@ -313,6 +330,16 @@ public CompletableFuture startAsync(Agent agent, String prompt, Run
* {@link RunSettings} overrides (either may be null).
*/
public CompletableFuture startAsync(Agent agent, String prompt, Plan plan, RunSettings runSettings) {
+ // Capture execution metadata before scheduling the async request so a
+ // caller mutating a reused RunSettings instance cannot change this
+ // start's deduplication identity after startAsync returns.
+ final String configuredIdempotencyKey =
+ runSettings != null ? runSettings.getIdempotencyKey() : null;
+ final String idempotencyKey =
+ configuredIdempotencyKey == null || configuredIdempotencyKey.isBlank()
+ ? null
+ : configuredIdempotencyKey;
+
// Stateful agents get a per-execution domain UUID. The server uses it
// as taskToDomain for every worker task in this run; local workers are
// registered under the same domain so they poll the per-execution
@@ -338,6 +365,7 @@ public CompletableFuture startAsync(Agent agent, String prompt, Pla
.sessionId(sessionId != null && !sessionId.isEmpty() ? sessionId : null)
.runId(runId != null && !runId.isEmpty() ? runId : null)
.staticPlan(plan != null ? plan.toJson() : null)
+ .idempotencyKey(idempotencyKey)
.build());
String executionId = response.getExecutionId();
if (executionId == null) {
@@ -483,40 +511,6 @@ public CompletableFuture> deployAsync(Agent... agents) {
return CompletableFuture.supplyAsync(() -> deploy(agents));
}
- /**
- * Deploy a single agent and reconcile its cron schedules declaratively.
- *
- * {@code schedules} semantics:
- *
- * {@code null} → leave existing schedules untouched.
- * empty list → purge all schedules for this agent.
- * non-empty list → upsert these and prune any others for this agent.
- *
- *
- * @param agent the agent to deploy
- * @param schedules schedules to attach (tri-state semantics above)
- * @return the {@link DeploymentInfo}
- */
- public DeploymentInfo deploy(Agent agent, List schedules) {
- List infos = deploy(new Agent[] {agent});
- if (schedules != null) {
- schedules().reconcile(agent.getName(), schedules);
- }
- return infos.get(0);
- }
-
- /** Accessor for the cron-schedule lifecycle API. */
- public Schedules schedules() {
- if (schedules == null) {
- synchronized (this) {
- if (schedules == null) {
- schedules = new Schedules(conductorClient);
- }
- }
- }
- return schedules;
- }
-
/**
* Deploy agents, register their workers, and keep polling until interrupted.
*
@@ -563,7 +557,7 @@ public void serve(Boolean blocking, Agent... agents) {
return;
}
logger.info("Serving {} agent(s) — waiting for tasks (Ctrl+C to stop)", agents.length);
- Runtime.getRuntime().addShutdownHook(new Thread(workerManager::stop, "agentspan-serve-shutdown"));
+ Runtime.getRuntime().addShutdownHook(new Thread(workerManager::stop, "conductor-agent-serve-shutdown"));
try {
Thread.currentThread().join();
} catch (InterruptedException e) {
@@ -628,47 +622,47 @@ public void close() {
// ── Drop-in support for native framework agents (run / start / stream /
// deploy / serve / plan / resume all accept the raw native object) ──
- /** Drop-in: accepts a native ADK {@code BaseAgent} or any Agentspan {@link Agent}. */
+ /** Drop-in: accepts a native ADK {@code BaseAgent} or any Conductor {@link Agent}. */
public AgentResult run(Object agent, String prompt) {
return run(coerceAgent(agent), prompt);
}
- /** Drop-in: accepts a native ADK {@code BaseAgent} or any Agentspan {@link Agent}. */
+ /** Drop-in: accepts a native ADK {@code BaseAgent} or any Conductor {@link Agent}. */
public CompletableFuture runAsync(Object agent, String prompt) {
return runAsync(coerceAgent(agent), prompt);
}
- /** Drop-in: accepts a native ADK {@code BaseAgent} or any Agentspan {@link Agent}. */
+ /** Drop-in: accepts a native ADK {@code BaseAgent} or any Conductor {@link Agent}. */
public AgentHandle start(Object agent, String prompt) {
return start(coerceAgent(agent), prompt);
}
- /** Drop-in: accepts a native ADK {@code BaseAgent} or any Agentspan {@link Agent}. */
+ /** Drop-in: accepts a native ADK {@code BaseAgent} or any Conductor {@link Agent}. */
public CompletableFuture startAsync(Object agent, String prompt) {
return startAsync(coerceAgent(agent), prompt);
}
- /** Drop-in: accepts a native ADK {@code BaseAgent} or any Agentspan {@link Agent}. */
+ /** Drop-in: accepts a native ADK {@code BaseAgent} or any Conductor {@link Agent}. */
public AgentStream stream(Object agent, String prompt) {
return stream(coerceAgent(agent), prompt);
}
- /** Drop-in: accepts a native ADK {@code BaseAgent} or any Agentspan {@link Agent}. */
+ /** Drop-in: accepts a native ADK {@code BaseAgent} or any Conductor {@link Agent}. */
public CompletableFuture streamAsync(Object agent, String prompt) {
return streamAsync(coerceAgent(agent), prompt);
}
- /** Drop-in: accepts native ADK {@code BaseAgent} instances (or Agentspan {@link Agent}s). */
+ /** Drop-in: accepts native ADK {@code BaseAgent} instances (or Conductor {@link Agent}s). */
public List deploy(Object... agents) {
return deploy(coerceAgents(agents));
}
- /** Drop-in: accepts native ADK {@code BaseAgent} instances (or Agentspan {@link Agent}s). */
+ /** Drop-in: accepts native ADK {@code BaseAgent} instances (or Conductor {@link Agent}s). */
public CompletableFuture> deployAsync(Object... agents) {
return deployAsync(coerceAgents(agents));
}
- /** Drop-in: accepts native ADK {@code BaseAgent} instances (or Agentspan {@link Agent}s). */
+ /** Drop-in: accepts native ADK {@code BaseAgent} instances (or Conductor {@link Agent}s). */
public void serve(Object... agents) {
serve(true, coerceAgents(agents));
}
@@ -678,17 +672,17 @@ public void serve(Boolean blocking, Object... agents) {
serve(blocking, coerceAgents(agents));
}
- /** Drop-in: accepts a native ADK {@code BaseAgent} or any Agentspan {@link Agent}. */
+ /** Drop-in: accepts a native ADK {@code BaseAgent} or any Conductor {@link Agent}. */
public CompileResponse plan(Object agent) {
return plan(coerceAgent(agent));
}
- /** Drop-in: accepts a native ADK {@code BaseAgent} or any Agentspan {@link Agent}. */
+ /** Drop-in: accepts a native ADK {@code BaseAgent} or any Conductor {@link Agent}. */
public AgentHandle resume(String executionId, Object agent) {
return resume(executionId, coerceAgent(agent));
}
- /** Drop-in: accepts a native ADK {@code BaseAgent} or any Agentspan {@link Agent}. */
+ /** Drop-in: accepts a native ADK {@code BaseAgent} or any Conductor {@link Agent}. */
public CompletableFuture resumeAsync(String executionId, Object agent) {
return resumeAsync(executionId, coerceAgent(agent));
}
@@ -747,7 +741,7 @@ private static Object[] withoutRunSettings(Object[] tools) {
}
/**
- * Coerce a user-provided agent object to an Agentspan {@link Agent}.
+ * Coerce a user-provided agent object to a Conductor {@link Agent}.
*
* Supports {@link Agent} (returned as-is) and these native framework objects, each
* detected by fully-qualified-name so the core class never hard-references a
@@ -771,7 +765,7 @@ private static Agent coerceAgent(Object agent, Object[] tools) {
return a;
}
if (isInstanceOf(agent, "com.google.adk.agents.BaseAgent")) {
- return org.conductoross.conductor.ai.frameworks.AdkBridge.toAgentspan(
+ return org.conductoross.conductor.ai.frameworks.AdkBridge.toConductor(
(com.google.adk.agents.BaseAgent) agent);
}
if (isInstanceOf(agent, "dev.langchain4j.model.chat.ChatModel")) {
@@ -931,6 +925,7 @@ public void prepareWorkers(Agent agent) {
if ("agent_tool".equals(tool.getToolType()) && tool.getAgentRef() != null) {
prepareWorkers(tool.getAgentRef());
}
+ registerLocalGuardrailWorker(tool.getName(), tool.getGuardrails());
}
// Register callback workers (legacy single-function style)
@@ -997,44 +992,9 @@ public void prepareWorkers(Agent agent) {
}
}
- // Register combined guardrail worker per agent (matches Python: {agent_name}_output_guardrail)
- List customGuardrails =
- agent.getGuardrails().stream().filter(g -> g.getFunc() != null).collect(Collectors.toList());
- if (!customGuardrails.isEmpty()) {
- String taskName = agent.getName() + "_output_guardrail";
- workerManager.register(taskName, inputData -> {
- Object rawContent = inputData.get("content");
- String content = rawContent != null ? rawContent.toString() : "";
- int iteration = inputData.get("iteration") instanceof Number
- ? ((Number) inputData.get("iteration")).intValue()
- : 0;
- for (GuardrailDef g : customGuardrails) {
- GuardrailResult result = g.getFunc().apply(content);
- if (!result.isPassed()) {
- String onFail = g.getOnFail().toJsonValue();
- String fixedOutput = result.getFixedOutput();
- if ("retry".equals(onFail) && iteration >= g.getMaxRetries()) onFail = "raise";
- if ("fix".equals(onFail) && fixedOutput == null) onFail = "raise";
- Map out = new LinkedHashMap<>();
- out.put("passed", false);
- out.put("message", result.getMessage() != null ? result.getMessage() : "");
- out.put("on_fail", onFail);
- out.put("fixed_output", fixedOutput);
- out.put("guardrail_name", g.getName());
- out.put("should_continue", "retry".equals(onFail));
- return out;
- }
- }
- Map out = new LinkedHashMap<>();
- out.put("passed", true);
- out.put("message", "");
- out.put("on_fail", "pass");
- out.put("fixed_output", null);
- out.put("guardrail_name", "");
- out.put("should_continue", false);
- return out;
- });
- }
+ // A local function owns its guardrail worker; regex, LLM and external guardrails
+ // remain server- or externally-owned. Agent and tool workers share the same protocol.
+ registerLocalGuardrailWorker(agent.getName(), agent.getGuardrails());
// Register termination condition worker for agents that have one
if (agent.getTermination() != null) {
@@ -1110,6 +1070,24 @@ public void prepareWorkers(Agent agent) {
}
}
+ private void registerLocalGuardrailWorker(String scopeName, List guardrails) {
+ List localGuardrails = guardrails.stream()
+ .filter(guardrail -> guardrail.getFunc() != null)
+ .collect(Collectors.toList());
+ if (!localGuardrails.isEmpty()) {
+ workerManager.register(scopeName + "_output_guardrail", GuardrailHandlerFactory.create(localGuardrails));
+ }
+ }
+
+ // Package-visible test hooks. They expose registration state only, not worker mutation.
+ boolean isWorkerRegisteredForTest(String taskName) {
+ return workerManager.isRegistered(taskName);
+ }
+
+ String workerDomainForTest(String taskName) {
+ return workerManager.getRegisteredTaskDomain(taskName);
+ }
+
/**
* Evaluate a termination condition given the current LLM result and iteration count.
*
@@ -1368,7 +1346,7 @@ private Map executeCode(String language, String code, int timeou
interpreter = language;
}
// Write code to temp file
- File tmpFile = File.createTempFile("agentspan_code_", language.startsWith("python") ? ".py" : ".sh");
+ File tmpFile = File.createTempFile("conductor_agent_code_", language.startsWith("python") ? ".py" : ".sh");
tmpFile.deleteOnExit();
try (FileWriter fw = new FileWriter(tmpFile)) {
fw.write(code);
diff --git a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/RunSettings.java b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/RunSettings.java
index e00de4aa3..5fe0420b7 100644
--- a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/RunSettings.java
+++ b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/RunSettings.java
@@ -16,13 +16,14 @@
import java.util.Map;
/**
- * Per-invocation LLM overrides applied on top of an {@link Agent}'s settings.
+ * Per-invocation settings applied when an {@link Agent} is executed.
*
* Pass to {@code run}/{@code start}/{@code stream} (and their async
- * variants). Only non-{@code null} fields override the agent; everything else
- * is left as the agent defined it. Overrides mutate the serialized root
- * agent config before compile+register+start, so they flow into the LLM tasks
- * without a new server field — sub-agents keep their own settings.
+ * variants). LLM fields override the serialized root agent config before
+ * compile+register+start, while execution metadata such as {@link
+ * #idempotencyKey(String)} is sent at the top level of the start request.
+ * Unset fields leave the agent and request unchanged; sub-agents keep their own
+ * settings.
*
*
Example:
*
{@code
@@ -39,6 +40,7 @@ public class RunSettings {
private Integer maxTokens;
private String reasoningEffort;
private Integer thinkingBudgetTokens;
+ private String idempotencyKey;
/** Provider/model id (e.g. {@code "openai/gpt-4o"}). */
public RunSettings model(String model) {
@@ -70,6 +72,16 @@ public RunSettings thinkingBudgetTokens(Integer thinkingBudgetTokens) {
return this;
}
+ /**
+ * Stable key used by the server to deduplicate starts of the same logical
+ * execution. The runtime omits {@code null}, empty, and whitespace-only
+ * values; it never generates a key automatically.
+ */
+ public RunSettings idempotencyKey(String idempotencyKey) {
+ this.idempotencyKey = idempotencyKey;
+ return this;
+ }
+
public String getModel() {
return model;
}
@@ -90,11 +102,16 @@ public Integer getThinkingBudgetTokens() {
return thinkingBudgetTokens;
}
+ public String getIdempotencyKey() {
+ return idempotencyKey;
+ }
+
/**
- * Map the set fields to {@code agentConfig} wire keys — mirrors the Python
- * SDK's {@code RunSettings.to_config_overrides} so wire-key names match
- * across SDKs. Uses {@code != null} so {@code temperature(0.0)} and
- * {@code maxTokens(0)} are honored.
+ * Map only the LLM override fields to {@code agentConfig} wire keys —
+ * mirrors the Python SDK's {@code RunSettings.to_config_overrides} so
+ * wire-key names match across SDKs. Execution metadata such as the
+ * idempotency key is deliberately excluded. Uses {@code != null} so
+ * {@code temperature(0.0)} and {@code maxTokens(0)} are honored.
*/
public Map toConfigOverrides() {
Map overrides = new LinkedHashMap<>();
diff --git a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/enums/Framework.java b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/enums/Framework.java
index e58434834..ceb461ff6 100644
--- a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/enums/Framework.java
+++ b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/enums/Framework.java
@@ -22,7 +22,7 @@
*
* The server routes each framework through a matching {@code AgentConfigNormalizer}
* before compilation. Every value here corresponds to a {@code frameworkId()} in the
- * server's normalizer registry. Native Agentspan agents have no framework — their
+ * server's normalizer registry. Native Conductor agents have no framework — their
* config is sent as {@code agentConfig}.
*
*
Known server normalizers and their IDs:
diff --git a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/exceptions/CredentialNotFoundException.java b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/exceptions/CredentialNotFoundException.java
index 6597b3421..be2adbd89 100644
--- a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/exceptions/CredentialNotFoundException.java
+++ b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/exceptions/CredentialNotFoundException.java
@@ -14,7 +14,7 @@
import java.util.List;
-import io.orkes.conductor.client.exceptions.AgentspanException;
+import io.orkes.conductor.client.exceptions.AgentException;
/**
* One or more declared credentials could not be resolved from the server.
@@ -26,7 +26,7 @@
*
Mirrors Python's {@code CredentialNotFoundError} and .NET's
* {@code CredentialNotFoundException}.
*/
-public class CredentialNotFoundException extends AgentspanException {
+public class CredentialNotFoundException extends AgentException {
private final List missingNames;
diff --git a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/exceptions/WorkerStallError.java b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/exceptions/WorkerStallError.java
index 5a49df431..702d14d3a 100644
--- a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/exceptions/WorkerStallError.java
+++ b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/exceptions/WorkerStallError.java
@@ -12,7 +12,7 @@
*/
package org.conductoross.conductor.ai.exceptions;
-import io.orkes.conductor.client.exceptions.AgentspanException;
+import io.orkes.conductor.client.exceptions.AgentException;
/**
* A worker task in a stateful run sat {@code SCHEDULED} with zero polls beyond
@@ -21,7 +21,7 @@
* is a stateful run whose per-execution domain has no live worker (e.g. the
* owning process died or never registered under that domain).
*/
-public class WorkerStallError extends AgentspanException {
+public class WorkerStallError extends AgentException {
private final String taskReferenceName;
private final String executionId;
diff --git a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/execution/DockerCodeExecutor.java b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/execution/DockerCodeExecutor.java
index fad7cf54c..486826c76 100644
--- a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/execution/DockerCodeExecutor.java
+++ b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/execution/DockerCodeExecutor.java
@@ -55,7 +55,7 @@ public ExecutionResult execute(String code) {
Path tempFile = null;
try {
String extension = getExtension(language);
- tempFile = Files.createTempFile("agentspan_code_", extension);
+ tempFile = Files.createTempFile("conductor_agent_code_", extension);
Files.writeString(tempFile, code);
String containerPath = "/tmp/code" + extension;
diff --git a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/execution/LocalCodeExecutor.java b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/execution/LocalCodeExecutor.java
index 2ddce069d..e29edf3a3 100644
--- a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/execution/LocalCodeExecutor.java
+++ b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/execution/LocalCodeExecutor.java
@@ -75,7 +75,7 @@ public ExecutionResult execute(String code) {
Path tempFile = null;
try {
- tempFile = Files.createTempFile("agentspan_code_", fileExtension(language));
+ tempFile = Files.createTempFile("conductor_agent_code_", fileExtension(language));
Files.writeString(tempFile, code);
List command = new ArrayList<>(interpreter);
diff --git a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/frameworks/AdkBridge.java b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/frameworks/AdkBridge.java
index 81ade86e8..1182f8b32 100644
--- a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/frameworks/AdkBridge.java
+++ b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/frameworks/AdkBridge.java
@@ -47,7 +47,7 @@
/**
* Adapter that takes a native Google ADK {@link BaseAgent} and produces an
- * Agentspan {@link Agent} configured so the durable Agentspan runtime can
+ * Conductor {@link Agent} configured so the durable agent runtime can
* execute the agent server-side.
*
* This bridge extracts every field the server's
@@ -74,7 +74,7 @@
*
*
*
Symmetry with the Python {@code google.adk} serializer in
- * {@code sdk/python/src/agentspan/agents/frameworks/serializer.py} — both walk
+ * {@code sdk/python/src/agent_sdk/frameworks/serializer.py} — both walk
* the native agent tree and emit the same wire shape consumed by the server's
* {@code GoogleADKNormalizer}.
*/
@@ -99,15 +99,15 @@ private AdkBridge() {}
/**
* Convert any native ADK {@link BaseAgent} ({@code LlmAgent},
* {@code SequentialAgent}, {@code ParallelAgent}, {@code LoopAgent}, …)
- * into an Agentspan {@link Agent} ready for {@code runtime.run(...)}.
+ * into a Conductor {@link Agent} ready for {@code runtime.run(...)}.
*/
- public static Agent toAgentspan(BaseAgent adk) {
+ public static Agent toConductor(BaseAgent adk) {
return agentBuilder(adk).build();
}
/**
- * Same as {@link #toAgentspan} but returns the populated
- * {@link Agent.Builder} so callers can attach Agentspan-only features
+ * Same as {@link #toConductor} but returns the populated
+ * {@link Agent.Builder} so callers can attach Conductor-specific features
* (guardrails, gate, termination conditions, callbacks, …) on top of a
* native ADK agent before building.
*
@@ -125,7 +125,7 @@ public static Agent.Builder agentBuilder(BaseAgent adk) {
return agentBuilder(adk, new java.util.IdentityHashMap<>());
}
- private static Agent toAgentspan(BaseAgent adk, java.util.IdentityHashMap visited) {
+ private static Agent toConductor(BaseAgent adk, java.util.IdentityHashMap visited) {
return agentBuilder(adk, visited).build();
}
@@ -137,7 +137,7 @@ private static Agent.Builder agentBuilder(BaseAgent adk, java.util.IdentityHashM
Agent.Builder b = Agent.builder().name(adk.name()).framework("google_adk");
- // Model + instruction live at the Agentspan top level so the
+ // Model + instruction live at the Conductor agent top level so the
// worker poller / debug tools see them directly. Everything else
// goes into frameworkConfig (flattened by AgentConfigSerializer into
// the rawConfig the server consumes).
@@ -155,13 +155,13 @@ private static Agent.Builder agentBuilder(BaseAgent adk, java.util.IdentityHashM
// sub_agents Map list below.
List subAgentChildren = new ArrayList<>();
for (BaseAgent sub : safeSubAgents(adk)) {
- subAgentChildren.add(toAgentspan(sub, visited));
+ subAgentChildren.add(toConductor(sub, visited));
}
if (!subAgentChildren.isEmpty()) {
b.agents(subAgentChildren.toArray(new Agent[0]));
}
- // Callbacks: wrap ADK callbacks as an Agentspan CallbackHandler so the
+ // Callbacks: wrap ADK callbacks as a Conductor CallbackHandler so the
// runtime registers worker handlers and the server schedules hook tasks
// at the right positions. Best-effort — contexts are stubbed; see
// wrapCallbacks() for the constraint matrix.
@@ -273,7 +273,7 @@ private static Map buildRawConfig(
// Callbacks: emit `_worker_ref` placeholders for each non-empty
// callback list. The matching CallbackHandler attached on the
- // Agent.Builder (see toAgentspan) registers the local worker so
+ // Agent.Builder (see toConductor) registers the local worker so
// any server-scheduled hook task lands somewhere.
//
// KNOWN SERVER LIMITATION (matches Python — see python ADK
@@ -335,7 +335,7 @@ private static String extractInstruction(Instruction inst) {
/**
* Top-level tools — extracted from {@code LlmAgent.tools()} and wrapped as
- * {@link ToolDef} so the Agentspan worker poller registers handlers AND
+ * {@link ToolDef} so the Conductor worker poller registers handlers AND
* the serializer emits the expected {@code _worker_ref} / {@code _type:
* AgentTool} wire shape.
*/
@@ -482,9 +482,9 @@ private static ToolDef functionToolToDef(FunctionTool ft) {
private static ToolDef agentToolToDef(AgentTool at, java.util.IdentityHashMap visited) {
BaseAgent inner = at.getAgent();
- Agent childAgent = toAgentspan(inner, visited);
+ Agent childAgent = toConductor(inner, visited);
// AgentTool produces an empty input schema in ADK by default; the
- // Agentspan serializer's AgentTool path builds a stock {request:
+ // The Conductor serializer's AgentTool path builds a stock {request:
// string} schema for us.
return new ToolDef.Builder()
.name(at.name())
@@ -657,7 +657,7 @@ private static boolean callbackListIsNonEmpty(LlmAgent llm, String getter) {
/**
* Wrap any ADK callbacks attached to {@code llm} as a single
- * {@link CallbackHandler} that the Agentspan runtime can dispatch to.
+ * {@link CallbackHandler} that the Conductor agent runtime can dispatch to.
*
* Returns {@code null} if no callbacks are attached.
*
diff --git a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/frameworks/LangChain4jAgent.java b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/frameworks/LangChain4jAgent.java
index 4f8dc07eb..1888c513f 100644
--- a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/frameworks/LangChain4jAgent.java
+++ b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/frameworks/LangChain4jAgent.java
@@ -25,7 +25,7 @@
import org.conductoross.conductor.ai.model.ToolDef;
/**
- * Bridges LangChain4j tool objects to Agentspan {@link Agent}.
+ * Bridges LangChain4j tool objects to Conductor {@link Agent}.
*
*
Users who have existing POJOs annotated with
* {@code @dev.langchain4j.agent.tool.Tool} can hand them directly to
@@ -55,13 +55,13 @@ private LangChain4jAgent() {}
// ── Public API ───────────────────────────────────────────────────────────
/**
- * Create an Agentspan {@link Agent} from one or more LangChain4j tool objects.
+ * Create a Conductor {@link Agent} from one or more LangChain4j tool objects.
*
* @param name agent name (must match {@code ^[a-zA-Z_][a-zA-Z0-9_-]*$})
* @param model LLM model string, e.g. {@code "anthropic/claude-sonnet-4-6"}
* @param instructions system prompt / instructions for the agent
* @param toolObjects objects with {@code @dev.langchain4j.agent.tool.Tool} methods
- * @return an Agentspan Agent ready to pass to
+ * @return a Conductor Agent ready to pass to
* {@link org.conductoross.conductor.ai.AgentRuntime#plan(Agent)} or
* {@link org.conductoross.conductor.ai.AgentRuntime#run(Agent, String)}
*/
@@ -99,7 +99,7 @@ public static boolean isLangChain4jTools(Object obj) {
// ── Package-private helpers (visible to tests) ───────────────────────────
/**
- * Extract Agentspan {@link ToolDef} objects from an array of LangChain4j
+ * Extract Conductor {@link ToolDef} objects from an array of LangChain4j
* {@code @Tool}-annotated objects.
*
* @param toolObjects objects to inspect via reflection
diff --git a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/frameworks/LangChainBridge.java b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/frameworks/LangChainBridge.java
index da57d4209..309a6d88a 100644
--- a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/frameworks/LangChainBridge.java
+++ b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/frameworks/LangChainBridge.java
@@ -19,10 +19,10 @@
/**
* Adapter that takes native LangChain4j components (a {@link ChatModel} and
- * {@code @Tool}-annotated POJOs) and produces an Agentspan {@link Agent}.
+ * {@code @Tool}-annotated POJOs) and produces a Conductor {@link Agent}.
*
*
The model object is used only to extract the {@code provider/model} string
- * that the Agentspan server needs — Agentspan owns the LLM call and the
+ * that the Conductor server needs — it owns the LLM call and the
* credentials live on the server, so the client never invokes the local
* {@link ChatModel} directly.
*
@@ -34,9 +34,9 @@ public final class LangChainBridge {
private LangChainBridge() {}
/**
- * Build an Agentspan {@link Agent.Builder} from a native LangChain4j
+ * Build a Conductor {@link Agent.Builder} from a native LangChain4j
* {@link ChatModel} and {@code @Tool}-annotated POJOs. Returning the
- * Builder lets callers attach Agentspan-only features (guardrails,
+ * Builder lets callers attach Conductor-specific features (guardrails,
* gate, termination, callbacks) before {@code .build()}:
*
*
{@code
@@ -64,7 +64,7 @@ public static Agent.Builder agentBuilder(String name, ChatModel model, String sy
/**
* Map a LangChain4j {@link ChatModel} to the {@code provider/model} string
- * format expected by the Agentspan server (e.g. {@code anthropic/claude-sonnet-4-6}).
+ * format expected by the Conductor server (e.g. {@code anthropic/claude-sonnet-4-6}).
*
* The provider id is read from {@link ChatModel#provider()} and the model
* name from {@code defaultRequestParameters().modelName()}; both are part of
diff --git a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/frameworks/OpenAIAgent.java b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/frameworks/OpenAIAgent.java
index 061934e33..14ac08482 100644
--- a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/frameworks/OpenAIAgent.java
+++ b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/frameworks/OpenAIAgent.java
@@ -25,7 +25,7 @@
import org.conductoross.conductor.ai.model.ToolDef;
/**
- * Bridges the OpenAI Agents SDK shape to Agentspan {@link Agent}.
+ * Bridges the OpenAI Agents SDK shape to Conductor {@link Agent}.
*
*
Mirrors the Python pattern:
*
{@code
@@ -49,7 +49,7 @@
* without a provider prefix are auto-prefixed with {@code openai/} server-side.
*
* Local @Tool-annotated POJOs are wrapped using the same reflection bridge
- * as {@link LangChain4jAgent} — they are registered as Agentspan worker tools
+ * as {@link LangChain4jAgent} — they are registered as Conductor worker tools
* and the OpenAI Agents server-side runner calls them via the standard tool-call
* dispatch.
*/
@@ -84,7 +84,7 @@ public Builder instructions(String instructions) {
return this;
}
- /** Add @Tool-annotated POJO(s); each annotated method becomes an Agentspan worker tool. */
+ /** Add @Tool-annotated POJO(s); each annotated method becomes a Conductor worker tool. */
public Builder tools(Object... toolObjects) {
this.tools.addAll(extractTools(toolObjects));
return this;
diff --git a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/guardrail/Guardrail.java b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/guardrail/Guardrail.java
index eb4bdf8f6..6202d8e68 100644
--- a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/guardrail/Guardrail.java
+++ b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/guardrail/Guardrail.java
@@ -90,12 +90,6 @@ public Builder maxRetries(int maxRetries) {
}
public GuardrailDef build() {
- // on_fail=HUMAN is only valid for output guardrails — input guardrails
- // are client-side and cannot pause a workflow (parity with Python's ValueError).
- if (onFail == OnFail.HUMAN && position == Position.INPUT) {
- throw new IllegalArgumentException("onFail=HUMAN is only valid for position=OUTPUT "
- + "(input guardrails are client-side and cannot pause a workflow)");
- }
String guardrailType = isExternal ? "external" : "custom";
return GuardrailDef.builder()
.name(name)
diff --git a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/internal/AgentConfigSerializer.java b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/internal/AgentConfigSerializer.java
index 20927c8b1..34d7808db 100644
--- a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/internal/AgentConfigSerializer.java
+++ b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/internal/AgentConfigSerializer.java
@@ -120,7 +120,7 @@ private Map serializeAgent(Agent agent) {
map.put("tools", toolsList);
}
// Guardrails — emit so framework normalizers can preserve
- // Agentspan-side safety hooks. Without this, attaching
+ // Server-side safety hooks. Without this, attaching
// .guardrails(...) to a bridged ADK / OpenAI agent silently
// drops them at the wire layer.
if (agent.getGuardrails() != null && !agent.getGuardrails().isEmpty()) {
diff --git a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/internal/GuardrailHandlerFactory.java b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/internal/GuardrailHandlerFactory.java
new file mode 100644
index 000000000..6b1d525e0
--- /dev/null
+++ b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/internal/GuardrailHandlerFactory.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2025 Conductor Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+package org.conductoross.conductor.ai.internal;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+
+import org.conductoross.conductor.ai.model.GuardrailDef;
+import org.conductoross.conductor.ai.model.GuardrailResult;
+
+/** Creates the common worker handler used by agent- and tool-scoped local guardrails. */
+public final class GuardrailHandlerFactory {
+ private GuardrailHandlerFactory() {}
+
+ /**
+ * Creates a handler which evaluates local guardrails in declaration order and returns the
+ * first failure. Exceptions from user functions deliberately propagate to the worker runner,
+ * where they are reported as failed tasks.
+ */
+ public static Function, Object> create(List guardrails) {
+ List localGuardrails = List.copyOf(guardrails);
+ return inputData -> {
+ Object rawContent = inputData.get("content");
+ String content = rawContent != null ? rawContent.toString() : "";
+ int iteration = inputData.get("iteration") instanceof Number
+ ? ((Number) inputData.get("iteration")).intValue()
+ : 0;
+ for (GuardrailDef guardrail : localGuardrails) {
+ GuardrailResult result = guardrail.getFunc().apply(content);
+ if (result == null) {
+ throw new IllegalStateException("Guardrail '" + guardrail.getName() + "' returned null");
+ }
+ if (!result.isPassed()) {
+ String onFail = guardrail.getOnFail().toJsonValue();
+ String fixedOutput = result.getFixedOutput();
+ if ("retry".equals(onFail) && iteration >= guardrail.getMaxRetries()) onFail = "raise";
+ if ("fix".equals(onFail) && fixedOutput == null) onFail = "raise";
+ Map out = new LinkedHashMap<>();
+ out.put("passed", false);
+ out.put("message", result.getMessage() != null ? result.getMessage() : "");
+ out.put("on_fail", onFail);
+ out.put("fixed_output", fixedOutput);
+ out.put("guardrail_name", guardrail.getName());
+ out.put("should_continue", "retry".equals(onFail));
+ return out;
+ }
+ }
+ Map out = new LinkedHashMap<>();
+ out.put("passed", true);
+ out.put("message", "");
+ out.put("on_fail", "pass");
+ out.put("fixed_output", null);
+ out.put("guardrail_name", "");
+ out.put("should_continue", false);
+ return out;
+ };
+ }
+}
diff --git a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/internal/ServerLivenessMonitor.java b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/internal/ServerLivenessMonitor.java
index ff35c80d8..40f2d0b59 100644
--- a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/internal/ServerLivenessMonitor.java
+++ b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/internal/ServerLivenessMonitor.java
@@ -63,7 +63,7 @@ public ServerLivenessMonitor(
/** Start the background check thread (no-op transport errors, see class doc). */
public void start() {
- Thread t = new Thread(this::loop, "agentspan-liveness-" + executionId);
+ Thread t = new Thread(this::loop, "conductor-agent-liveness-" + executionId);
t.setDaemon(daemon);
t.start();
thread = t;
diff --git a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/internal/WorkerManager.java b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/internal/WorkerManager.java
index 3e51bc798..5f6db8b2d 100644
--- a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/internal/WorkerManager.java
+++ b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/internal/WorkerManager.java
@@ -44,7 +44,7 @@
* here returns {@code leaseExtendEnabled() == true}). A handler that blocks for
* minutes keeps its lease alive instead of being reclaimed and re-dispatched.
*
- * Agentspan registers workers incrementally (per run, sometimes under a
+ *
The agent runtime registers workers incrementally (per run, sometimes under a
* per-execution domain), whereas a {@link TaskRunnerConfigurer} is built from a
* fixed worker set. We bridge the two models by (re)building the configurer in
* {@link #startAll()} whenever a new task type has been registered since
@@ -60,7 +60,7 @@
*
*
Credentials ride the {@code runtimeMetadata} contract (spec R6): declared
* secret names are stamped on {@code TaskDef.runtimeMetadata} at registration;
- * a capable host (agentspan > 0.4.2, conductor-oss PR #1255) resolves them at
+ * a capable Conductor OSS host (PR #1255) resolves them at
* poll time and delivers the values on the wire-only
* {@code Task.runtimeMetadata} map. There is no fetch call, no execution token,
* and ambient process env is never read — a declared-but-undelivered name fails
@@ -172,6 +172,16 @@ String getTaskDomain(String taskName) {
return taskDomains.get(taskName);
}
+ /** Whether a handler has been registered for the supplied task type. */
+ public boolean isRegistered(String taskName) {
+ return handlers.containsKey(taskName);
+ }
+
+ /** Returns the worker domain assigned during registration, if any. */
+ public String getRegisteredTaskDomain(String taskName) {
+ return taskDomains.get(taskName);
+ }
+
/** Visible for testing: whether a runner (re)build is pending. */
boolean isWorkerSetChanged() {
synchronized (lifecycleLock) {
@@ -331,7 +341,7 @@ public void startAll() {
TaskRunnerConfigurer.Builder builder = new TaskRunnerConfigurer.Builder(taskClient, workers)
.withThreadCount(threadCount)
- .withWorkerNamePrefix("agentspan-worker-");
+ .withWorkerNamePrefix("conductor-agent-worker-");
if (!taskToDomain.isEmpty()) {
builder.withTaskToDomain(taskToDomain);
}
@@ -398,7 +408,7 @@ TaskResult executeHandler(String taskName, Task task) {
// (wire-only Task.runtimeMetadata) BEFORE invoking the handler. Fail closed:
// a declared-but-undelivered name is a terminal failure so Conductor doesn't
// burn retries on a config problem — ambient process env is NEVER read.
- // See docs/design/secret-injection-contract.md.
+ // See design/secret-injection-contract.md.
Map resolvedSecrets = Collections.emptyMap();
List declared = taskCredentials.getOrDefault(taskName, Collections.emptyList());
if (!declared.isEmpty()) {
@@ -421,7 +431,7 @@ TaskResult executeHandler(String taskName, Task task) {
result.setReasonForIncompletion("Missing credentials " + missing
+ ": not delivered by the server on this task. Ensure each secret is stored and "
+ "declared on the tool/agent, and that the server supports runtimeMetadata "
- + "delivery (agentspan > 0.4.2 / conductor-oss PR #1255).");
+ + "delivery (Conductor OSS with PR #1255).");
return result;
}
resolvedSecrets = resolved;
@@ -440,7 +450,7 @@ TaskResult executeHandler(String taskName, Task task) {
Object out = handler.apply(inputData);
result.setStatus(TaskResult.Status.COMPLETED);
result.setOutputData(buildOutput(out));
- logger.debug("Completed task {} ({})", taskName, task.getTaskId());
+ logger.trace("Completed task {} ({})", taskName, task.getTaskId());
} finally {
CredentialContext.clear();
}
diff --git a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/model/AgentEvent.java b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/model/AgentEvent.java
index 654e4ad68..1ac971dbf 100644
--- a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/model/AgentEvent.java
+++ b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/model/AgentEvent.java
@@ -154,7 +154,7 @@ public List getPendingToolCalls() {
*/
/** Internal keys injected by the server that should not be shown as tool arguments. */
private static final Set INTERNAL_KEYS =
- new HashSet<>(Arrays.asList("__agentspan_ctx__", "_agent_state", "method"));
+ new HashSet<>(Arrays.asList("_agent_state", "method"));
@SuppressWarnings("unchecked")
public static AgentEvent fromMap(Map data) {
diff --git a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/model/AgentHandle.java b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/model/AgentHandle.java
index 8c21961ea..9e631278e 100644
--- a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/model/AgentHandle.java
+++ b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/model/AgentHandle.java
@@ -398,7 +398,7 @@ private static TaskExtract extractFromTasks(Workflow workflow) {
}
// Tool worker task — capture name, input args (stripping
- // internal Agentspan context), and output result.
+ // internal runtime fields), and output result.
// referenceTaskName starts with "call_" for LLM-dispatched tool calls.
String refName = task.getReferenceTaskName();
if (refName != null && refName.startsWith("call_") && outputData != null) {
@@ -411,7 +411,6 @@ private static TaskExtract extractFromTasks(Workflow workflow) {
String k = e.getKey();
if (k.startsWith("_")
|| "method".equals(k)
- || "__agentspan_ctx__".equals(k)
|| "evaluatorType".equals(k)
|| "expression".equals(k)
|| "ctx".equals(k)
@@ -435,8 +434,7 @@ private static TaskExtract extractFromTasks(Workflow workflow) {
* Build an {@link AgentResult} from a terminal {@link Workflow}.
*
* Shared workflow → {@link AgentResult} extraction used by callers that
- * already hold a completed {@link Workflow} (e.g. the scheduler's
- * {@code runNowAndWait}). Maps the workflow status to an {@link AgentStatus},
+ * already hold a completed {@link Workflow}. Maps the workflow status to an {@link AgentStatus},
* normalizes the output map, surfaces {@code reasonForIncompletion} as the
* error for non-completed runs, and reuses {@link #extractFromTasks} for the
* token-usage and tool-call aggregation.
diff --git a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/model/GuardrailDef.java b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/model/GuardrailDef.java
index 6c739e062..de6dcbd1e 100644
--- a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/model/GuardrailDef.java
+++ b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/model/GuardrailDef.java
@@ -120,9 +120,15 @@ public Builder config(Map config) {
}
public GuardrailDef build() {
- if (name == null || name.isEmpty()) {
+ if (name == null || name.trim().isEmpty()) {
throw new IllegalArgumentException("GuardrailDef requires a name");
}
+ if (maxRetries < 0) {
+ throw new IllegalArgumentException("GuardrailDef maxRetries must be nonnegative");
+ }
+ if (onFail == OnFail.HUMAN && position != Position.OUTPUT) {
+ throw new IllegalArgumentException("onFail=HUMAN is only valid for position=OUTPUT");
+ }
return new GuardrailDef(this);
}
}
diff --git a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/model/ToolContext.java b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/model/ToolContext.java
index 372a199db..020cd2790 100644
--- a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/model/ToolContext.java
+++ b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/model/ToolContext.java
@@ -40,7 +40,7 @@
* {@code @Tool(credentials = {...})} and resolved by the runtime for this call. The
* credential map is an immutable per-call snapshot, so it is safe to read from threads
* the tool spawns — unlike a thread-local, the values remain valid for the lifetime of
- * this context object. See {@code docs/design/secret-injection-contract.md} for the
+ * this context object. See {@code design/secret-injection-contract.md} for the
* cross-SDK contract; Java's per-call context mirrors .NET's {@code IToolContext} and
* Python's contextvars accessor.
*/
diff --git a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/model/ToolDef.java b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/model/ToolDef.java
index 7174a4beb..e21924fe6 100644
--- a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/model/ToolDef.java
+++ b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/model/ToolDef.java
@@ -138,6 +138,33 @@ public boolean isStateful() {
return stateful;
}
+ /**
+ * Returns a copy of this tool with the supplied guardrails. Every execution and
+ * serialization field, including credentials, retries, statefulness, configuration and
+ * agent-tool references, is preserved.
+ */
+ public ToolDef withGuardrails(List guardrails) {
+ return builder()
+ .name(name)
+ .description(description)
+ .inputSchema(inputSchema)
+ .outputSchema(outputSchema)
+ .func(func)
+ .approvalRequired(approvalRequired)
+ .timeoutSeconds(timeoutSeconds)
+ .retryCount(retryCount)
+ .retryDelaySeconds(retryDelaySeconds)
+ .retryPolicy(retryPolicy)
+ .toolType(toolType)
+ .config(config)
+ .credentials(credentials)
+ .guardrails(List.copyOf(guardrails))
+ .maxCalls(maxCalls)
+ .agentRef(agentRef)
+ .stateful(stateful)
+ .build();
+ }
+
public static Builder builder() {
return new Builder();
}
diff --git a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/openai/GPTAssistantAgent.java b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/openai/GPTAssistantAgent.java
index 6527dbccb..079ac99eb 100644
--- a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/openai/GPTAssistantAgent.java
+++ b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/openai/GPTAssistantAgent.java
@@ -29,7 +29,7 @@
* An agent backed by the OpenAI Assistants API.
*
* Wraps an OpenAI Assistant (with its own instructions, tools, and file search
- * capabilities) as an Agentspan Agent. The assistant's execution is handled via the
+ * capabilities) as a Conductor Agent. The assistant's execution is handled via the
* Assistants API Threads and Runs.
*
*
{@code
diff --git a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/plans/Plan.java b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/plans/Plan.java
index 810d639c4..0323e5921 100644
--- a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/plans/Plan.java
+++ b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/plans/Plan.java
@@ -30,7 +30,7 @@
* and run a fully deterministic pipeline.
*
* The {@code toJson()} output is the wire format PAC consumes —
- * identical to what the Python {@code agentspan.agents.plans.Plan} and
+ * identical to the Python agent SDK's {@code Plan} and
* TypeScript {@code Plan} emit.
*/
public final class Plan {
diff --git a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/schedule/Schedule.java b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/schedule/Schedule.java
deleted file mode 100644
index a2810212d..000000000
--- a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/schedule/Schedule.java
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Copyright 2026 Conductor Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
- * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.conductoross.conductor.ai.schedule;
-
-import java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.Map;
-
-/**
- * A cron trigger attached to an agent. Mirrors {@code Schedule} in the Python /
- * TypeScript SDKs. See {@code docs/design/scheduling.md}.
- *
- *
One agent can carry multiple schedules; each is identified by a {@code name}
- * unique within that agent. The SDK auto-prefixes the wire name as
- * {@code {agent.name}-{name}} so Conductor's org-wide uniqueness is satisfied.
- *
- *
Construct via {@link #builder()}.
- */
-public final class Schedule {
-
- private final String name;
- private final String cron;
- private final String timezone;
- private final Map input;
- private final boolean catchup;
- private final boolean paused;
- private final Long startAt;
- private final Long endAt;
- private final String description;
-
- private Schedule(Builder b) {
- if (b.name == null || b.name.trim().isEmpty()) {
- throw new ScheduleException("Schedule.name is required and must be non-empty");
- }
- if (b.cron == null || b.cron.trim().isEmpty()) {
- throw new ScheduleException("Schedule.cron is required and must be non-empty");
- }
- if (b.startAt != null && b.endAt != null && b.startAt >= b.endAt) {
- throw new ScheduleException("Schedule.startAt must be < endAt");
- }
- this.name = b.name;
- this.cron = b.cron;
- this.timezone = b.timezone != null ? b.timezone : "UTC";
- this.input = b.input == null ? Collections.emptyMap() : new LinkedHashMap<>(b.input);
- this.catchup = b.catchup;
- this.paused = b.paused;
- this.startAt = b.startAt;
- this.endAt = b.endAt;
- this.description = b.description;
- }
-
- public static Builder builder() {
- return new Builder();
- }
-
- public String getName() {
- return name;
- }
-
- public String getCron() {
- return cron;
- }
-
- public String getTimezone() {
- return timezone;
- }
-
- public Map getInput() {
- return input;
- }
-
- public boolean isCatchup() {
- return catchup;
- }
-
- public boolean isPaused() {
- return paused;
- }
-
- public Long getStartAt() {
- return startAt;
- }
-
- public Long getEndAt() {
- return endAt;
- }
-
- public String getDescription() {
- return description;
- }
-
- public static final class Builder {
- private String name;
- private String cron;
- private String timezone;
- private Map input;
- private boolean catchup;
- private boolean paused;
- private Long startAt;
- private Long endAt;
- private String description;
-
- public Builder name(String v) {
- this.name = v;
- return this;
- }
-
- public Builder cron(String v) {
- this.cron = v;
- return this;
- }
-
- public Builder timezone(String v) {
- this.timezone = v;
- return this;
- }
-
- public Builder input(Map v) {
- this.input = v;
- return this;
- }
-
- public Builder catchup(boolean v) {
- this.catchup = v;
- return this;
- }
-
- public Builder paused(boolean v) {
- this.paused = v;
- return this;
- }
-
- public Builder startAt(Long v) {
- this.startAt = v;
- return this;
- }
-
- public Builder endAt(Long v) {
- this.endAt = v;
- return this;
- }
-
- public Builder description(String v) {
- this.description = v;
- return this;
- }
-
- public Schedule build() {
- return new Schedule(this);
- }
- }
-}
diff --git a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/schedule/ScheduleException.java b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/schedule/ScheduleException.java
deleted file mode 100644
index 75be2b8fc..000000000
--- a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/schedule/ScheduleException.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2026 Conductor Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
- * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.conductoross.conductor.ai.schedule;
-
-/** Base class for schedule errors. */
-public class ScheduleException extends RuntimeException {
- public ScheduleException(String message) {
- super(message);
- }
-
- public ScheduleException(String message, Throwable cause) {
- super(message, cause);
- }
-
- /** Two schedules in the same agent share a name. */
- public static class NameConflict extends ScheduleException {
- public NameConflict(String message) {
- super(message);
- }
- }
-
- /** No schedule matches the given name. */
- public static class NotFound extends ScheduleException {
- public NotFound(String message) {
- super(message);
- }
- }
-
- /** Server rejected the cron expression as malformed. */
- public static class InvalidCron extends ScheduleException {
- public InvalidCron(String message) {
- super(message);
- }
- }
-
- /** A {@code runNow(..., wait=true)} workflow did not finish within the timeout. */
- public static class Timeout extends ScheduleException {
- public Timeout(String message) {
- super(message);
- }
- }
-}
diff --git a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/schedule/ScheduleInfo.java b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/schedule/ScheduleInfo.java
deleted file mode 100644
index 0a9e5861f..000000000
--- a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/schedule/ScheduleInfo.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright 2026 Conductor Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
- * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.conductoross.conductor.ai.schedule;
-
-import java.util.Map;
-
-/** Server view of a schedule, returned by {@link Schedules#list(String)} / {@link Schedules#get(String)}. */
-public final class ScheduleInfo {
- private final String name;
- private final String shortName;
- private final String agent;
- private final String cron;
- private final String timezone;
- private final Map input;
- private final boolean paused;
- private final String pausedReason;
- private final boolean catchup;
- private final Long startAt;
- private final Long endAt;
- private final String description;
- private final Long nextRun;
- private final Long createTime;
- private final Long updateTime;
- private final String createdBy;
- private final String updatedBy;
-
- public ScheduleInfo(
- String name,
- String shortName,
- String agent,
- String cron,
- String timezone,
- Map input,
- boolean paused,
- String pausedReason,
- boolean catchup,
- Long startAt,
- Long endAt,
- String description,
- Long nextRun,
- Long createTime,
- Long updateTime,
- String createdBy,
- String updatedBy) {
- this.name = name;
- this.shortName = shortName;
- this.agent = agent;
- this.cron = cron;
- this.timezone = timezone;
- this.input = input;
- this.paused = paused;
- this.pausedReason = pausedReason;
- this.catchup = catchup;
- this.startAt = startAt;
- this.endAt = endAt;
- this.description = description;
- this.nextRun = nextRun;
- this.createTime = createTime;
- this.updateTime = updateTime;
- this.createdBy = createdBy;
- this.updatedBy = updatedBy;
- }
-
- public String getName() {
- return name;
- }
-
- public String getShortName() {
- return shortName;
- }
-
- public String getAgent() {
- return agent;
- }
-
- public String getCron() {
- return cron;
- }
-
- public String getTimezone() {
- return timezone;
- }
-
- public Map getInput() {
- return input;
- }
-
- public boolean isPaused() {
- return paused;
- }
-
- public String getPausedReason() {
- return pausedReason;
- }
-
- public boolean isCatchup() {
- return catchup;
- }
-
- public Long getStartAt() {
- return startAt;
- }
-
- public Long getEndAt() {
- return endAt;
- }
-
- public String getDescription() {
- return description;
- }
-
- public Long getNextRun() {
- return nextRun;
- }
-
- public Long getCreateTime() {
- return createTime;
- }
-
- public Long getUpdateTime() {
- return updateTime;
- }
-
- public String getCreatedBy() {
- return createdBy;
- }
-
- public String getUpdatedBy() {
- return updatedBy;
- }
-
- @Override
- public String toString() {
- return "ScheduleInfo{name=" + name + ", agent=" + agent + ", cron=" + cron + ", paused=" + paused + "}";
- }
-}
diff --git a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/schedule/Schedules.java b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/schedule/Schedules.java
deleted file mode 100644
index ea1100a1d..000000000
--- a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/schedule/Schedules.java
+++ /dev/null
@@ -1,384 +0,0 @@
-/*
- * Copyright 2026 Conductor Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
- * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.conductoross.conductor.ai.schedule;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.conductoross.conductor.ai.model.AgentHandle;
-import org.conductoross.conductor.ai.model.AgentResult;
-
-import com.netflix.conductor.client.exception.ConductorClientException;
-import com.netflix.conductor.client.http.ConductorClient;
-import com.netflix.conductor.client.http.ConductorClientRequest;
-import com.netflix.conductor.client.http.ConductorClientRequest.Method;
-import com.netflix.conductor.client.http.WorkflowClient;
-import com.netflix.conductor.common.metadata.workflow.StartWorkflowRequest;
-import com.netflix.conductor.common.run.Workflow;
-
-import io.orkes.conductor.client.exceptions.AgentAPIException;
-
-import com.fasterxml.jackson.core.type.TypeReference;
-
-/**
- * Lifecycle API for cron-based agent schedules. Obtained via {@code runtime.schedules()}.
- *
- *
All requests ride the shared native Conductor {@link ConductorClient}/ApiClient
- * (same HTTP + token-auth backend as every other client) — the scheduler CRUD via
- * {@link ConductorClientRequest}/{@link ConductorClient#execute} ({@code /api/scheduler/*},
- * for which the Conductor client ships no typed {@code SchedulerClient}), and
- * {@code runNow} via the typed {@link WorkflowClient}.
- *
- *
Operations are keyed by the wire name (prefixed with
- * {@code agent-}) returned by {@link #list(String)}. Use {@link Schedule} to
- * construct the user-facing short name; the SDK prefixes it at deploy time.
- */
-public class Schedules {
-
- private static final TypeReference> MAP_TYPE = new TypeReference>() {};
- private static final TypeReference>> LIST_MAP_TYPE =
- new TypeReference>>() {};
- private static final TypeReference> LIST_LONG_TYPE = new TypeReference>() {};
-
- private final ConductorClient client;
- /** Shared native Conductor client for starting workflows (runNow). */
- private final WorkflowClient workflowClient;
-
- public Schedules(ConductorClient conductorClient) {
- this.client = conductorClient;
- this.workflowClient = new WorkflowClient(conductorClient);
- }
-
- /** Test seam: inject a {@link WorkflowClient} so {@code runNow}/{@code runNowAndWait} can be unit-tested. */
- Schedules(ConductorClient conductorClient, WorkflowClient workflowClient) {
- this.client = conductorClient;
- this.workflowClient = workflowClient;
- }
-
- // ── CRUD ────────────────────────────────────────────────────────────
-
- public void save(Schedule schedule, String agentName) {
- Map body = toSaveRequest(schedule, agentName);
- execVoid(ConductorClientRequest.builder()
- .method(Method.POST)
- .path("/scheduler/schedules")
- .body(body)
- .build());
- }
-
- public ScheduleInfo get(String wireName) {
- Map resp = exec(
- ConductorClientRequest.builder()
- .method(Method.GET)
- .path("/scheduler/schedules/{name}")
- .addPathParam("name", wireName)
- .build(),
- MAP_TYPE);
- if (resp == null || resp.isEmpty() || resp.get("name") == null) {
- throw new ScheduleException.NotFound("Schedule '" + wireName + "' not found");
- }
- return fromWorkflowSchedule(resp, null);
- }
-
- public List list(String agentName) {
- List> resp = exec(
- ConductorClientRequest.builder()
- .method(Method.GET)
- .path("/scheduler/schedules")
- .addQueryParam("workflowName", agentName)
- .build(),
- LIST_MAP_TYPE);
- if (resp == null) return new ArrayList<>();
- List out = new ArrayList<>();
- for (Map item : resp) {
- if (item != null) out.add(fromWorkflowSchedule(item, agentName));
- }
- return out;
- }
-
- public void pause(String wireName) {
- pause(wireName, null);
- }
-
- public void pause(String wireName, String reason) {
- ConductorClientRequest.Builder b = ConductorClientRequest.builder()
- .method(Method.PUT)
- .path("/scheduler/schedules/{name}/pause")
- .addPathParam("name", wireName);
- if (reason != null) b.addQueryParam("reason", reason);
- execVoid(b.build());
- }
-
- public void resume(String wireName) {
- execVoid(ConductorClientRequest.builder()
- .method(Method.PUT)
- .path("/scheduler/schedules/{name}/resume")
- .addPathParam("name", wireName)
- .build());
- }
-
- public void delete(String wireName) {
- execVoid(ConductorClientRequest.builder()
- .method(Method.DELETE)
- .path("/scheduler/schedules/{name}")
- .addPathParam("name", wireName)
- .build());
- }
-
- /**
- * Start the scheduled agent's workflow immediately via the official Conductor
- * {@link WorkflowClient#startWorkflow} (returns the new workflowId).
- */
- public String runNow(ScheduleInfo info) {
- StartWorkflowRequest req = new StartWorkflowRequest();
- req.setName(info.getAgent());
- if (info.getInput() != null) req.setInput(info.getInput());
- return workflowClient.startWorkflow(req);
- }
-
- /** Default timeout (ms) for {@link #runNowAndWait}, mirroring Python's 600s default. */
- private static final long DEFAULT_WAIT_TIMEOUT_MS = 600_000L;
- /** Default poll interval (ms) for {@link #runNowAndWait}, mirroring Python's 1s default. */
- private static final long DEFAULT_POLL_INTERVAL_MS = 1_000L;
-
- /**
- * Fetch the schedule by its wire {@code name} and start its agent's workflow
- * immediately with the schedule's stored input. Returns the new workflowId.
- *
- * Name-keyed parity with the Python/TS {@code run_now(name)}.
- */
- public String runNow(String name) {
- return runNow(get(name));
- }
-
- /**
- * Fetch the schedule by its wire {@code name} and start its agent's workflow.
- *
- *
When {@code wait} is {@code false} (default behaviour) returns the
- * workflowId immediately. When {@code wait} is {@code true} this blocks until
- * the workflow reaches a terminal state and returns an {@link AgentResult}
- * built from the completed workflow (parity with Python's
- * {@code run_now(name, wait=True)} and the C#/TS SDKs, which return an
- * {@link AgentResult} from the wait variant).
- *
- * @return a {@link String} workflowId when {@code wait=false}, or an
- * {@link AgentResult} when {@code wait=true}
- */
- public Object runNow(String name, boolean wait) {
- if (!wait) {
- return runNow(name);
- }
- return runNowAndWait(name);
- }
-
- /**
- * Fetch the schedule by its wire {@code name}, start it, then poll until the
- * triggered workflow reaches a terminal state and return it as an
- * {@link AgentResult}.
- *
- * @throws ScheduleException.Timeout if the workflow has not finished within the timeout
- */
- public AgentResult runNowAndWait(String name) {
- return runNowAndWait(name, DEFAULT_WAIT_TIMEOUT_MS, DEFAULT_POLL_INTERVAL_MS);
- }
-
- /**
- * Fetch the schedule by its wire {@code name}, start it, then poll until the
- * triggered workflow reaches a terminal state and return it as an
- * {@link AgentResult}.
- *
- *
The completed {@link Workflow} is converted via the SDK's shared
- * workflow → {@link AgentResult} extraction ({@link AgentHandle#fromWorkflow})
- * — the same logic the {@code AgentHandle.waitForResult} path uses — so the
- * output, status, error, token usage, and tool calls match a direct run.
- *
- * @param name the schedule's wire name
- * @param timeoutMs maximum time to wait, in milliseconds
- * @param pollIntervalMs delay between status polls, in milliseconds
- * @throws ScheduleException.Timeout if the workflow has not finished within {@code timeoutMs}
- */
- public AgentResult runNowAndWait(String name, long timeoutMs, long pollIntervalMs) {
- String executionId = runNow(name);
- long deadline = System.currentTimeMillis() + timeoutMs;
- while (true) {
- Workflow wf = workflowClient.getWorkflow(executionId, true);
- if (isTerminal(wf)) {
- return AgentHandle.fromWorkflow(wf);
- }
- if (System.currentTimeMillis() >= deadline) {
- throw new ScheduleException.Timeout("runNow('" + name + "') did not finish within " + timeoutMs + "ms");
- }
- if (pollIntervalMs > 0) {
- try {
- Thread.sleep(pollIntervalMs);
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- throw new ScheduleException.Timeout("runNow('" + name + "') was interrupted while waiting");
- }
- }
- }
- }
-
- /** {@code true} if the workflow has reached a terminal state (completed/failed/terminated/timed-out). */
- static boolean isTerminal(Workflow wf) {
- Workflow.WorkflowStatus status = wf != null ? wf.getStatus() : null;
- return status != null && status.isTerminal();
- }
-
- public List previewNext(String cron, int n) {
- List resp = exec(
- ConductorClientRequest.builder()
- .method(Method.GET)
- .path("/scheduler/nextFewSchedules")
- .addQueryParam("cronExpression", cron)
- .addQueryParam("limit", Integer.valueOf(n))
- .build(),
- LIST_LONG_TYPE);
- return resp != null ? resp : new ArrayList<>();
- }
-
- // ── Declarative reconcile ───────────────────────────────────────────
-
- /**
- * Apply declarative scheduling semantics:
- *
- * {@code null} → no-op
- * empty list → purge all schedules whose workflow == agent
- * non-empty list → upsert listed, delete any other schedule for this agent
- *
- */
- public void reconcile(String agentName, List desired) {
- if (desired == null) return;
- checkUniqueNames(desired);
-
- Map existingWireByShort = new LinkedHashMap<>();
- for (ScheduleInfo info : list(agentName)) {
- existingWireByShort.put(info.getShortName(), info.getName());
- }
- Set desiredShort = new HashSet<>();
- for (Schedule s : desired) desiredShort.add(s.getName());
-
- for (Map.Entry entry : existingWireByShort.entrySet()) {
- if (!desiredShort.contains(entry.getKey())) {
- delete(entry.getValue());
- }
- }
- for (Schedule s : desired) {
- save(s, agentName);
- }
- }
-
- // ── Internals ───────────────────────────────────────────────────────
-
- static String prefix(String agentName, String shortName) {
- return agentName + "-" + shortName;
- }
-
- static String unprefix(String agentName, String wireName) {
- String p = agentName + "-";
- return wireName.startsWith(p) ? wireName.substring(p.length()) : wireName;
- }
-
- static void checkUniqueNames(List schedules) {
- Set seen = new HashSet<>();
- for (Schedule s : schedules) {
- if (!seen.add(s.getName())) {
- throw new ScheduleException.NameConflict(
- "Duplicate schedule name '" + s.getName() + "' — names must be unique per agent");
- }
- }
- }
-
- static Map toSaveRequest(Schedule s, String agentName) {
- Map swr = new LinkedHashMap<>();
- swr.put("name", agentName);
- swr.put("input", s.getInput() == null ? new LinkedHashMap<>() : new LinkedHashMap<>(s.getInput()));
-
- Map req = new LinkedHashMap<>();
- req.put("name", prefix(agentName, s.getName()));
- req.put("cronExpression", s.getCron());
- req.put("zoneId", s.getTimezone());
- req.put("runCatchupScheduleInstances", s.isCatchup());
- req.put("paused", s.isPaused());
- if (s.getStartAt() != null) req.put("scheduleStartTime", s.getStartAt());
- if (s.getEndAt() != null) req.put("scheduleEndTime", s.getEndAt());
- if (s.getDescription() != null) req.put("description", s.getDescription());
- req.put("startWorkflowRequest", swr);
- return req;
- }
-
- @SuppressWarnings("unchecked")
- static ScheduleInfo fromWorkflowSchedule(Map ws, String agentHint) {
- Map swr = (Map) ws.getOrDefault("startWorkflowRequest", new HashMap<>());
- String wireName = (String) ws.getOrDefault("name", "");
- String swrName = (String) swr.getOrDefault("name", "");
- String agent = agentHint != null ? agentHint : (swrName.isEmpty() ? "" : swrName);
-
- return new ScheduleInfo(
- wireName,
- unprefix(agent, wireName),
- swrName,
- (String) ws.getOrDefault("cronExpression", ""),
- (String) ws.getOrDefault("zoneId", "UTC"),
- (Map) swr.getOrDefault("input", new HashMap<>()),
- Boolean.TRUE.equals(ws.get("paused")),
- (String) ws.get("pausedReason"),
- Boolean.TRUE.equals(ws.get("runCatchupScheduleInstances")),
- longOrNull(ws.get("scheduleStartTime")),
- longOrNull(ws.get("scheduleEndTime")),
- (String) ws.get("description"),
- longOrNull(ws.get("nextRunTime")),
- longOrNull(ws.get("createTime")),
- longOrNull(ws.get("updatedTime")),
- (String) ws.get("createdBy"),
- (String) ws.get("updatedBy"));
- }
-
- private static Long longOrNull(Object o) {
- return o instanceof Number ? ((Number) o).longValue() : null;
- }
-
- /** Execute a scheduler request returning a typed body via the native Conductor client. */
- private T exec(ConductorClientRequest req, TypeReference type) {
- try {
- return client.execute(req, type).getData();
- } catch (ConductorClientException e) {
- throw mapException(e);
- }
- }
-
- /** Execute a scheduler request that returns no body. */
- private void execVoid(ConductorClientRequest req) {
- try {
- client.execute(req);
- } catch (ConductorClientException e) {
- throw mapException(e);
- }
- }
-
- /** Map Conductor's exception to the scheduler's typed exceptions (preserves the contract). */
- private static RuntimeException mapException(ConductorClientException e) {
- int status = e.getStatus();
- String msg = e.getMessage() != null ? e.getMessage() : "";
- if (status == 404) return new ScheduleException.NotFound(msg);
- if (status == 400 && msg.toLowerCase().contains("cron")) {
- return new ScheduleException.InvalidCron(msg);
- }
- return new AgentAPIException(status, msg);
- }
-}
diff --git a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/skill/Skill.java b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/skill/Skill.java
index 2df112c53..ca91ff236 100644
--- a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/skill/Skill.java
+++ b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/skill/Skill.java
@@ -37,7 +37,7 @@
import org.conductoross.conductor.ai.Agent;
/**
- * Load an Agent Skills directory as an Agentspan Agent.
+ * Load an Agent Skills directory as a Conductor Agent.
*
* A skill directory must contain a {@code SKILL.md} file with YAML frontmatter (including a
* {@code name} field) followed by the skill body. Optionally it may contain {@code *-agent.md}
diff --git a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/tools/McpTool.java b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/tools/McpTool.java
index 052a7f293..e771ff0d8 100644
--- a/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/tools/McpTool.java
+++ b/conductor-client-ai/src/main/java/org/conductoross/conductor/ai/tools/McpTool.java
@@ -111,7 +111,10 @@ public ToolDef build() {
}
Map config = new HashMap<>(additionalConfig);
- if (serverUrl != null) config.put("serverUrl", serverUrl);
+ // AgentSpan's MCP compiler consumes snake_case config keys, matching the
+ // cross-SDK agent schema. Using serverUrl silently drops the MCP server
+ // from the compiled workflow.
+ if (serverUrl != null) config.put("server_url", serverUrl);
if (toolName != null) config.put("toolName", toolName);
if (!headers.isEmpty()) config.put("headers", headers);
diff --git a/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/AgentConfigTest.java b/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/AgentConfigTest.java
index 7076ed3d9..7b90e7435 100644
--- a/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/AgentConfigTest.java
+++ b/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/AgentConfigTest.java
@@ -49,14 +49,14 @@ void defaultsWhenEnvEmpty() {
@Test
void parsesAllKnobsFromEnv() {
Map env = new HashMap<>();
- env.put("AGENTSPAN_WORKER_POLL_INTERVAL", "250");
- env.put("AGENTSPAN_WORKER_THREADS", "4");
- env.put("AGENTSPAN_AUTO_START_WORKERS", "false");
- env.put("AGENTSPAN_DAEMON_WORKERS", "false");
- env.put("AGENTSPAN_STREAMING_ENABLED", "false");
- env.put("AGENTSPAN_LIVENESS_ENABLED", "false");
- env.put("AGENTSPAN_LIVENESS_STALL_SECONDS", "45.5");
- env.put("AGENTSPAN_LIVENESS_CHECK_INTERVAL_SECONDS", "2.5");
+ env.put("CONDUCTOR_AGENT_WORKER_POLL_INTERVAL", "250");
+ env.put("CONDUCTOR_AGENT_WORKER_THREADS", "4");
+ env.put("CONDUCTOR_AGENT_AUTO_START_WORKERS", "false");
+ env.put("CONDUCTOR_AGENT_DAEMON_WORKERS", "false");
+ env.put("CONDUCTOR_AGENT_STREAMING_ENABLED", "false");
+ env.put("CONDUCTOR_AGENT_LIVENESS_ENABLED", "false");
+ env.put("CONDUCTOR_AGENT_LIVENESS_STALL_SECONDS", "45.5");
+ env.put("CONDUCTOR_AGENT_LIVENESS_CHECK_INTERVAL_SECONDS", "2.5");
AgentConfig config = fromEnv(env);
@@ -73,8 +73,8 @@ void parsesAllKnobsFromEnv() {
@Test
void invalidNumberFallsBackToDefault() {
AgentConfig config = fromEnv(Map.of(
- "AGENTSPAN_WORKER_POLL_INTERVAL", "not-a-number",
- "AGENTSPAN_LIVENESS_STALL_SECONDS", "soon"));
+ "CONDUCTOR_AGENT_WORKER_POLL_INTERVAL", "not-a-number",
+ "CONDUCTOR_AGENT_LIVENESS_STALL_SECONDS", "soon"));
assertEquals(
100,
@@ -86,8 +86,8 @@ void invalidNumberFallsBackToDefault() {
@Test
void emptyStringFallsBackToDefault() {
Map env = new HashMap<>();
- env.put("AGENTSPAN_WORKER_THREADS", "");
- env.put("AGENTSPAN_STREAMING_ENABLED", " ");
+ env.put("CONDUCTOR_AGENT_WORKER_THREADS", "");
+ env.put("CONDUCTOR_AGENT_STREAMING_ENABLED", " ");
AgentConfig config = fromEnv(env);
@@ -97,11 +97,11 @@ void emptyStringFallsBackToDefault() {
@Test
void booleanVariantsParse() {
- assertFalse(fromEnv(Map.of("AGENTSPAN_AUTO_START_WORKERS", "0")).isAutoStartWorkers());
- assertTrue(fromEnv(Map.of("AGENTSPAN_AUTO_START_WORKERS", "TRUE")).isAutoStartWorkers());
- assertFalse(fromEnv(Map.of("AGENTSPAN_AUTO_START_WORKERS", "no")).isAutoStartWorkers());
+ assertFalse(fromEnv(Map.of("CONDUCTOR_AGENT_AUTO_START_WORKERS", "0")).isAutoStartWorkers());
+ assertTrue(fromEnv(Map.of("CONDUCTOR_AGENT_AUTO_START_WORKERS", "TRUE")).isAutoStartWorkers());
+ assertFalse(fromEnv(Map.of("CONDUCTOR_AGENT_AUTO_START_WORKERS", "no")).isAutoStartWorkers());
assertTrue(
- fromEnv(Map.of("AGENTSPAN_AUTO_START_WORKERS", "banana")).isAutoStartWorkers(),
+ fromEnv(Map.of("CONDUCTOR_AGENT_AUTO_START_WORKERS", "banana")).isAutoStartWorkers(),
"unrecognized boolean falls back to the default");
}
diff --git a/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/AgentRuntimeRunSettingsWireTest.java b/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/AgentRuntimeRunSettingsWireTest.java
index 7a270f47f..91ec78244 100644
--- a/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/AgentRuntimeRunSettingsWireTest.java
+++ b/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/AgentRuntimeRunSettingsWireTest.java
@@ -80,11 +80,16 @@ private static MockResponse json(String body) {
}
@SuppressWarnings("unchecked")
- private Map takeStartAgentConfig() throws Exception {
+ private Map takeStartAgentBody() throws Exception {
RecordedRequest request = server.takeRequest(5, TimeUnit.SECONDS);
assertNotNull(request, "expected the start request to reach the stub server");
assertEquals("/api/agent/start", request.getPath());
- Map body = MAPPER.readValue(request.getBody().readUtf8(), Map.class);
+ return MAPPER.readValue(request.getBody().readUtf8(), Map.class);
+ }
+
+ @SuppressWarnings("unchecked")
+ private Map takeStartAgentConfig() throws Exception {
+ Map body = takeStartAgentBody();
Map agentConfig = (Map) body.get("agentConfig");
assertNotNull(agentConfig, "start payload must carry the serialized agentConfig");
return agentConfig;
@@ -119,10 +124,31 @@ void partialOverridesKeepAgentValues() throws Exception {
void noSettingsLeavesConfigUntouched() throws Exception {
runtime.start(agent, "hi");
- Map agentConfig = takeStartAgentConfig();
+ Map body = takeStartAgentBody();
+ @SuppressWarnings("unchecked")
+ Map agentConfig = (Map) body.get("agentConfig");
assertEquals("openai/gpt-4o", agentConfig.get("model"));
assertFalse(agentConfig.containsKey("thinkingConfig"));
assertFalse(agentConfig.containsKey("reasoningEffort"));
+ assertFalse(body.containsKey("idempotencyKey"));
+ }
+
+ @Test
+ void idempotencyKeyIsTopLevelExecutionMetadata() throws Exception {
+ runtime.start(agent, "hi", new RunSettings().idempotencyKey("logical-run-123"));
+
+ Map body = takeStartAgentBody();
+ assertEquals("logical-run-123", body.get("idempotencyKey"));
+ @SuppressWarnings("unchecked")
+ Map agentConfig = (Map) body.get("agentConfig");
+ assertFalse(agentConfig.containsKey("idempotencyKey"));
+ }
+
+ @Test
+ void blankIdempotencyKeyIsOmitted() throws Exception {
+ runtime.start(agent, "hi", new RunSettings().idempotencyKey(" "));
+
+ assertFalse(takeStartAgentBody().containsKey("idempotencyKey"));
}
@Test
@@ -137,9 +163,14 @@ void asyncVariantForwardsSettings() throws Exception {
void dropInVarargsExtractRunSettings() throws Exception {
// Through the Object drop-in, RunSettings arrives in the tools varargs —
// it must be applied as overrides, not coerced (and dropped) as a tool.
- runtime.start((Object) agent, "hi", new RunSettings().model("varargs/model"));
+ runtime.start((Object) agent, "hi", new RunSettings()
+ .model("varargs/model")
+ .idempotencyKey("varargs-run-123"));
- Map agentConfig = takeStartAgentConfig();
+ Map body = takeStartAgentBody();
+ @SuppressWarnings("unchecked")
+ Map agentConfig = (Map) body.get("agentConfig");
assertEquals("varargs/model", agentConfig.get("model"));
+ assertEquals("varargs-run-123", body.get("idempotencyKey"));
}
}
diff --git a/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/AgentRuntimeSchedulerClientTest.java b/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/AgentRuntimeSchedulerClientTest.java
new file mode 100644
index 000000000..62edc24ae
--- /dev/null
+++ b/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/AgentRuntimeSchedulerClientTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2026 Conductor Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+package org.conductoross.conductor.ai;
+
+import java.lang.reflect.Proxy;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.junit.jupiter.api.Test;
+
+import io.orkes.conductor.client.SchedulerClient;
+
+import okhttp3.mockwebserver.MockResponse;
+import okhttp3.mockwebserver.MockWebServer;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertSame;
+
+class AgentRuntimeSchedulerClientTest {
+
+ @Test
+ void getSchedulerClientReturnsTheSharedInjectedClient() {
+ SchedulerClient scheduler = (SchedulerClient) Proxy.newProxyInstance(
+ SchedulerClient.class.getClassLoader(),
+ new Class>[] {SchedulerClient.class},
+ (proxy, method, args) -> null);
+ AgentRuntime runtime = new AgentRuntime(
+ TestClients.forUrl("http://localhost:8080"), new AgentConfig(), scheduler);
+
+ assertSame(scheduler, runtime.getSchedulerClient());
+ }
+
+ @Test
+ void deployDoesNotManageSchedules() throws Exception {
+ AtomicInteger schedulerCalls = new AtomicInteger();
+ SchedulerClient scheduler = (SchedulerClient) Proxy.newProxyInstance(
+ SchedulerClient.class.getClassLoader(),
+ new Class>[] {SchedulerClient.class},
+ (proxy, method, args) -> {
+ schedulerCalls.incrementAndGet();
+ return null;
+ });
+
+ try (MockWebServer server = new MockWebServer()) {
+ server.start();
+ server.enqueue(new MockResponse()
+ .setHeader("Content-Type", "application/json")
+ .setBody("{\"agentName\":\"digest\",\"requiredWorkers\":[]}"));
+ AgentRuntime runtime = new AgentRuntime(
+ TestClients.forUrl(server.url("/").toString()), new AgentConfig(), scheduler);
+
+ runtime.deploy(Agent.builder().name("digest").model("openai/gpt-4o-mini").build());
+
+ assertEquals(0, schedulerCalls.get());
+ }
+ }
+}
diff --git a/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/RunSettingsTest.java b/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/RunSettingsTest.java
index e775bcb46..6a5b5c081 100644
--- a/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/RunSettingsTest.java
+++ b/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/RunSettingsTest.java
@@ -66,6 +66,14 @@ void emptySettingsProduceNoOverrides() {
assertTrue(new RunSettings().toConfigOverrides().isEmpty());
}
+ @Test
+ void idempotencyKeyIsExecutionMetadataNotConfigOverride() {
+ RunSettings settings = new RunSettings().idempotencyKey("logical-run-123");
+
+ assertEquals("logical-run-123", settings.getIdempotencyKey());
+ assertTrue(settings.toConfigOverrides().isEmpty());
+ }
+
@Test
void noTopPKnob() {
// The spec deliberately omits topP from RunSettings — guard against
diff --git a/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/ToolGuardrailRegistrationTest.java b/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/ToolGuardrailRegistrationTest.java
new file mode 100644
index 000000000..776280efc
--- /dev/null
+++ b/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/ToolGuardrailRegistrationTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2026 Conductor Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+package org.conductoross.conductor.ai;
+
+import java.util.List;
+
+import org.conductoross.conductor.ai.guardrail.Guardrail;
+import org.conductoross.conductor.ai.guardrail.LLMGuardrail;
+import org.conductoross.conductor.ai.guardrail.RegexGuardrail;
+import org.conductoross.conductor.ai.model.GuardrailDef;
+import org.conductoross.conductor.ai.model.GuardrailResult;
+import org.conductoross.conductor.ai.model.ToolDef;
+import org.junit.jupiter.api.Test;
+
+import io.orkes.conductor.client.ApiClient;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/** Regression coverage for local tool guardrail worker registration. */
+class ToolGuardrailRegistrationTest {
+ private static final String DOMAIN = "guardrail-test-domain";
+
+ @Test
+ void local_guardrail_is_registered_for_every_tool_type() {
+ AgentRuntime runtime = runtime();
+ try {
+ for (String toolType : List.of("worker", "http", "api", "mcp", "agent_tool", "human",
+ "pull_workflow_messages", "rag", "generate_pdf", "generate_image", "generate_audio", "generate_video")) {
+ ToolDef tool = ToolDef.builder().name("tool_" + toolType).toolType(toolType)
+ .guardrails(List.of(local("local_" + toolType))).build();
+ runtime.prepareWorkers(agent(tool), DOMAIN);
+ String taskName = tool.getName() + "_output_guardrail";
+ assertTrue(runtime.isWorkerRegisteredForTest(taskName), "local tool guardrail must have a worker");
+ assertEquals(DOMAIN, runtime.workerDomainForTest(taskName), "guardrail must poll the execution domain");
+ }
+ } finally {
+ runtime.shutdown();
+ }
+ }
+
+ @Test
+ void server_and_external_guardrails_do_not_create_local_workers() {
+ AgentRuntime runtime = runtime();
+ try {
+ ToolDef tool = ToolDef.builder().name("server_owned").toolType("http").guardrails(List.of(
+ RegexGuardrail.builder().name("regex").patterns("secret").build(),
+ LLMGuardrail.builder().name("judge").model("openai/gpt-4o-mini").policy("safe").build(),
+ Guardrail.external("external_guard").build())).build();
+ runtime.prepareWorkers(agent(tool), DOMAIN);
+ assertFalse(runtime.isWorkerRegisteredForTest("server_owned_output_guardrail"));
+ } finally {
+ runtime.shutdown();
+ }
+ }
+
+ private static GuardrailDef local(String name) {
+ return Guardrail.of(name, content -> GuardrailResult.pass()).build();
+ }
+
+ private static Agent agent(ToolDef tool) {
+ return Agent.builder().name("registration_agent_" + tool.getName()).model("openai/gpt-4o-mini")
+ .tools(List.of(tool)).build();
+ }
+
+ private static AgentRuntime runtime() {
+ return new AgentRuntime(ApiClient.builder().basePath("http://127.0.0.1:1/api").connectTimeout(1).readTimeout(1).build());
+ }
+}
diff --git a/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/exceptions/ExceptionsTest.java b/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/exceptions/ExceptionsTest.java
index 555ba00ea..08a156760 100644
--- a/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/exceptions/ExceptionsTest.java
+++ b/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/exceptions/ExceptionsTest.java
@@ -16,7 +16,7 @@
import org.junit.jupiter.api.Test;
-import io.orkes.conductor.client.exceptions.AgentspanException;
+import io.orkes.conductor.client.exceptions.AgentException;
import static org.junit.jupiter.api.Assertions.*;
@@ -41,7 +41,7 @@ void credentialNotFoundListsMissingNames() {
}
@Test
- void credentialNotFoundIsAnAgentspanException() {
- assertInstanceOf(AgentspanException.class, new CredentialNotFoundException("ONLY"));
+ void credentialNotFoundIsAnAgentException() {
+ assertInstanceOf(AgentException.class, new CredentialNotFoundException("ONLY"));
}
}
diff --git a/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/execution/CliCommandExecutorTest.java b/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/execution/CliCommandExecutorTest.java
index 877acad5d..66495eb16 100644
--- a/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/execution/CliCommandExecutorTest.java
+++ b/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/execution/CliCommandExecutorTest.java
@@ -155,7 +155,7 @@ void run_nonZeroExitReportsError() {
@DisabledOnOs(OS.WINDOWS)
void run_commandNotFound() {
Map result =
- CliCommandExecutor.run("agentspan_no_such_binary_xyz", null, null, false, List.of(), 30, null, false);
+ CliCommandExecutor.run("conductor_agent_no_such_binary_xyz", null, null, false, List.of(), 30, null, false);
assertEquals("error", result.get("status"));
assertTrue(((String) result.get("stderr")).contains("Command not found"), "stderr: " + result.get("stderr"));
}
diff --git a/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/frameworks/AdkBridgeTest.java b/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/frameworks/AdkBridgeTest.java
index f4b61546a..ba661ff96 100644
--- a/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/frameworks/AdkBridgeTest.java
+++ b/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/frameworks/AdkBridgeTest.java
@@ -27,7 +27,7 @@
import static org.junit.jupiter.api.Assertions.*;
/**
- * Server-free unit tests for the Google ADK bridge ({@link AdkBridge#toAgentspan}).
+ * Server-free unit tests for the Google ADK bridge ({@link AdkBridge#toConductor}).
*
* Mirrors how Python's framework e2e validates serialization (framework tagging,
* identity, tool extraction with a valid JSON Schema) without needing the model
@@ -54,8 +54,8 @@ private LlmAgent buildAdkAgent() {
}
@Test
- void toAgentspanTagsFrameworkAndCopiesIdentity() {
- Agent a = AdkBridge.toAgentspan(buildAdkAgent());
+ void toConductorTagsFrameworkAndCopiesIdentity() {
+ Agent a = AdkBridge.toConductor(buildAdkAgent());
assertEquals(
"google_adk",
a.getFramework(),
@@ -69,8 +69,8 @@ void toAgentspanTagsFrameworkAndCopiesIdentity() {
}
@Test
- void toAgentspanExtractsFunctionToolWithSchema() {
- Agent a = AdkBridge.toAgentspan(buildAdkAgent());
+ void toConductorExtractsFunctionToolWithSchema() {
+ Agent a = AdkBridge.toConductor(buildAdkAgent());
List tools = a.getTools();
assertNotNull(tools, "tools list must not be null");
assertFalse(
diff --git a/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/guardrail/GuardrailDefaultsTest.java b/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/guardrail/GuardrailDefaultsTest.java
index 399c40a81..33bd724f1 100644
--- a/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/guardrail/GuardrailDefaultsTest.java
+++ b/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/guardrail/GuardrailDefaultsTest.java
@@ -82,4 +82,10 @@ void human_output_guardrail_is_allowed() {
assertEquals(OnFail.HUMAN, g.getOnFail());
assertEquals(Position.OUTPUT, g.getPosition());
}
+
+ @Test
+ void core_builder_rejects_blank_names_and_negative_retries() {
+ assertThrows(IllegalArgumentException.class, () -> GuardrailDef.builder().name(" ").build());
+ assertThrows(IllegalArgumentException.class, () -> GuardrailDef.builder().name("g").maxRetries(-1).build());
+ }
}
diff --git a/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/internal/GuardrailHandlerFactoryTest.java b/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/internal/GuardrailHandlerFactoryTest.java
new file mode 100644
index 000000000..97e591003
--- /dev/null
+++ b/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/internal/GuardrailHandlerFactoryTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2026 Conductor Authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+package org.conductoross.conductor.ai.internal;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Function;
+
+import org.conductoross.conductor.ai.enums.OnFail;
+import org.conductoross.conductor.ai.model.GuardrailDef;
+import org.conductoross.conductor.ai.model.GuardrailResult;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/** Server-free contract tests for the shared local guardrail worker handler. */
+class GuardrailHandlerFactoryTest {
+
+ private static GuardrailDef guard(String name, OnFail onFail, Function function) {
+ return GuardrailDef.builder().name(name).onFail(onFail).func(function).build();
+ }
+
+ @Test
+ void passes_when_every_guardrail_passes() {
+ Map out = output(GuardrailHandlerFactory.create(List.of(
+ guard("first", OnFail.RAISE, value -> GuardrailResult.pass()))).apply(Map.of()));
+ assertTrue((Boolean) out.get("passed"));
+ assertEquals("pass", out.get("on_fail"));
+ }
+
+ @Test
+ void returns_first_failure_in_declaration_order() {
+ AtomicInteger secondCalls = new AtomicInteger();
+ Map out = output(GuardrailHandlerFactory.create(List.of(
+ guard("first", OnFail.RAISE, value -> GuardrailResult.fail("first failure")),
+ guard("second", OnFail.RAISE, value -> {
+ secondCalls.incrementAndGet();
+ return GuardrailResult.fail("second failure");
+ }))).apply(Map.of()));
+ assertFalse((Boolean) out.get("passed"));
+ assertEquals("first", out.get("guardrail_name"));
+ assertEquals(0, secondCalls.get());
+ }
+
+ @Test
+ void preserves_retry_fix_and_human_semantics() {
+ Map retry = output(GuardrailHandlerFactory.create(List.of(
+ guard("retry", OnFail.RETRY, value -> GuardrailResult.fail("no")))).apply(Map.of("content", "x", "iteration", 0)));
+ assertEquals("retry", retry.get("on_fail"));
+ assertTrue((Boolean) retry.get("should_continue"));
+
+ Map escalated = output(GuardrailHandlerFactory.create(List.of(
+ GuardrailDef.builder().name("retry").onFail(OnFail.RETRY).maxRetries(1)
+ .func(value -> GuardrailResult.fail("no")).build())).apply(Map.of("iteration", 1)));
+ assertEquals("raise", escalated.get("on_fail"));
+
+ Map fix = output(GuardrailHandlerFactory.create(List.of(
+ guard("fix", OnFail.FIX, value -> GuardrailResult.fix("clean")))).apply(Map.of()));
+ assertEquals("fix", fix.get("on_fail"));
+ assertEquals("clean", fix.get("fixed_output"));
+
+ Map human = output(GuardrailHandlerFactory.create(List.of(
+ guard("human", OnFail.HUMAN, value -> GuardrailResult.fail("review")))).apply(Map.of()));
+ assertEquals("human", human.get("on_fail"));
+ }
+
+ @Test
+ void thrown_or_null_guardrail_result_fails_the_task() {
+ assertThrows(RuntimeException.class, () -> GuardrailHandlerFactory.create(List.of(
+ guard("throws", OnFail.RAISE, value -> { throw new IllegalStateException("boom"); }))).apply(Map.of()));
+ assertThrows(IllegalStateException.class, () -> GuardrailHandlerFactory.create(List.of(
+ guard("null", OnFail.RAISE, value -> null))).apply(Map.of()));
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Map output(Object value) {
+ return (Map) value;
+ }
+}
diff --git a/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/internal/WorkerManagerCredentialsTest.java b/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/internal/WorkerManagerCredentialsTest.java
index 501aa2f43..ad0201400 100644
--- a/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/internal/WorkerManagerCredentialsTest.java
+++ b/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/internal/WorkerManagerCredentialsTest.java
@@ -193,7 +193,7 @@ void missingDeliveryFailsTerminallyAndNeverReadsAmbientEnv() {
assertFalse(handlerRan.get(), "the handler must not run without its declared credentials");
assertTrue(result.getReasonForIncompletion().contains("PATH"), "the missing name must be reported");
assertTrue(
- result.getReasonForIncompletion().contains("agentspan > 0.4.2"),
+ result.getReasonForIncompletion().contains("Conductor OSS with PR #1255"),
"the server capability requirement must be named: " + result.getReasonForIncompletion());
}
diff --git a/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/schedule/ScheduleIntegrationTest.java b/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/schedule/ScheduleIntegrationTest.java
deleted file mode 100644
index 7edd094f1..000000000
--- a/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/schedule/ScheduleIntegrationTest.java
+++ /dev/null
@@ -1,267 +0,0 @@
-/*
- * Copyright 2026 Conductor Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
- * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.conductoross.conductor.ai.schedule;
-
-import java.net.URI;
-import java.net.http.HttpClient;
-import java.net.http.HttpRequest;
-import java.net.http.HttpResponse;
-import java.time.Duration;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.UUID;
-
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.condition.EnabledIf;
-
-import com.netflix.conductor.client.http.ConductorClient;
-
-import io.orkes.conductor.client.ApiClient;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-/**
- * Integration tests for the Java schedule SDK against the live agentspan-runtime.
- * Skipped if the scheduler endpoint isn't reachable.
- */
-@EnabledIf("schedulerAvailable")
-class ScheduleIntegrationTest {
-
- // Base server URL WITHOUT a trailing "/api"; this suite appends "/api/..." itself.
- // AGENTSPAN_SERVER_URL conventionally INCLUDES "/api" (see BaseTest), so normalize it
- // away here — otherwise every URL gets a double "/api" and the scheduler probe 404s,
- // silently skipping the whole suite.
- private static final String SERVER =
- stripApiSuffix(System.getenv().getOrDefault("AGENTSPAN_SERVER_URL", "http://localhost:8080"));
-
- private static String stripApiSuffix(String url) {
- if (url.endsWith("/")) url = url.substring(0, url.length() - 1);
- if (url.endsWith("/api")) url = url.substring(0, url.length() - 4);
- return url;
- }
-
- private static final HttpClient HTTP =
- HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(5)).build();
-
- private static final String AGENT_NAME =
- "e2e_java_sched_noop_" + UUID.randomUUID().toString().substring(0, 8);
-
- private static Schedules schedules;
-
- static boolean schedulerAvailable() {
- try {
- HttpResponse r = HTTP.send(
- HttpRequest.newBuilder()
- .uri(URI.create(SERVER + "/api/scheduler/schedules"))
- .timeout(Duration.ofSeconds(3))
- .GET()
- .build(),
- HttpResponse.BodyHandlers.ofString());
- return r.statusCode() == 200;
- } catch (Exception e) {
- return false;
- }
- }
-
- @BeforeAll
- static void registerWorkflow() throws Exception {
- ConductorClient cc =
- new ApiClient((SERVER.endsWith("/") ? SERVER.substring(0, SERVER.length() - 1) : SERVER) + "/api");
- schedules = new Schedules(cc);
-
- String body = "{\"name\":\"" + AGENT_NAME + "\",\"version\":1,\"schemaVersion\":2,"
- + "\"ownerEmail\":\"e2e@agentspan.test\",\"timeoutSeconds\":60,\"timeoutPolicy\":\"TIME_OUT_WF\","
- + "\"tasks\":[{\"name\":\"noop_terminate\",\"taskReferenceName\":\"noop_terminate_ref\","
- + "\"type\":\"TERMINATE\",\"inputParameters\":{\"terminationStatus\":\"COMPLETED\","
- + "\"workflowOutput\":{\"ok\":true}}}]}";
-
- HttpResponse r = HTTP.send(
- HttpRequest.newBuilder()
- .uri(URI.create(SERVER + "/api/metadata/workflow"))
- .header("Content-Type", "application/json")
- .POST(HttpRequest.BodyPublishers.ofString(body))
- .build(),
- HttpResponse.BodyHandlers.ofString());
- if (r.statusCode() >= 400) {
- throw new RuntimeException("Workflow register failed: " + r.statusCode() + " " + r.body());
- }
- }
-
- @AfterAll
- static void unregisterWorkflow() throws Exception {
- if (schedules != null) {
- try {
- schedules.reconcile(AGENT_NAME, List.of());
- } catch (Exception ignored) {
- }
- }
- HTTP.send(
- HttpRequest.newBuilder()
- .uri(URI.create(SERVER + "/api/metadata/workflow/" + AGENT_NAME + "/1"))
- .DELETE()
- .build(),
- HttpResponse.BodyHandlers.ofString());
- }
-
- @AfterEach
- void clean() {
- try {
- schedules.reconcile(AGENT_NAME, List.of());
- } catch (Exception ignored) {
- }
- }
-
- @Test
- void reconcileCreatesSchedules() {
- Map input = new HashMap<>();
- input.put("k", 1);
- schedules.reconcile(
- AGENT_NAME,
- List.of(
- Schedule.builder()
- .name("daily")
- .cron("0 0 9 * * ?")
- .input(input)
- .build(),
- Schedule.builder().name("weekly").cron("0 0 9 * * MON").build()));
- List infos = schedules.list(AGENT_NAME);
- assertEquals(2, infos.size());
-
- ScheduleInfo daily = infos.stream()
- .filter(i -> "daily".equals(i.getShortName()))
- .findFirst()
- .orElseThrow();
- assertEquals(AGENT_NAME + "-daily", daily.getName());
- assertEquals("0 0 9 * * ?", daily.getCron());
- assertEquals(input, daily.getInput());
- assertEquals(AGENT_NAME, daily.getAgent());
- }
-
- @Test
- void upsertAndPrune() {
- schedules.reconcile(
- AGENT_NAME,
- List.of(
- Schedule.builder().name("a").cron("0 0 1 * * ?").build(),
- Schedule.builder().name("b").cron("0 0 2 * * ?").build()));
- schedules.reconcile(
- AGENT_NAME,
- List.of(
- Schedule.builder().name("a").cron("0 0 9 * * ?").build(),
- Schedule.builder().name("c").cron("0 0 17 * * ?").build()));
- List infos = schedules.list(AGENT_NAME);
- assertEquals(2, infos.size());
- ScheduleInfo a = infos.stream()
- .filter(i -> "a".equals(i.getShortName()))
- .findFirst()
- .orElseThrow();
- assertEquals("0 0 9 * * ?", a.getCron());
- }
-
- @Test
- void emptyListPurges() {
- schedules.reconcile(
- AGENT_NAME,
- List.of(Schedule.builder().name("x").cron("0 * * * * ?").build()));
- assertEquals(1, schedules.list(AGENT_NAME).size());
- schedules.reconcile(AGENT_NAME, List.of());
- assertTrue(schedules.list(AGENT_NAME).isEmpty());
- }
-
- @Test
- void nullPreserves() {
- schedules.reconcile(
- AGENT_NAME,
- List.of(Schedule.builder().name("x").cron("0 * * * * ?").build()));
- schedules.reconcile(AGENT_NAME, null);
- assertEquals(1, schedules.list(AGENT_NAME).size());
- }
-
- @Test
- void duplicateNameRaises() {
- assertThrows(
- ScheduleException.NameConflict.class,
- () -> schedules.reconcile(
- AGENT_NAME,
- List.of(
- Schedule.builder()
- .name("dup")
- .cron("0 * * * * ?")
- .build(),
- Schedule.builder()
- .name("dup")
- .cron("0 0 9 * * ?")
- .build())));
- assertTrue(schedules.list(AGENT_NAME).isEmpty());
- }
-
- @Test
- void pauseResume() {
- schedules.reconcile(
- AGENT_NAME,
- List.of(Schedule.builder().name("p").cron("0 0 9 * * ?").build()));
- String wire = AGENT_NAME + "-p";
- assertFalse(schedules.get(wire).isPaused());
- schedules.pause(wire, "rate limit");
- assertTrue(schedules.get(wire).isPaused());
- schedules.resume(wire);
- assertFalse(schedules.get(wire).isPaused());
- }
-
- @Test
- void pausedOnCreatePreservesState() {
- schedules.reconcile(
- AGENT_NAME,
- List.of(Schedule.builder()
- .name("silent")
- .cron("0 0 9 * * ?")
- .paused(true)
- .build()));
- assertTrue(schedules.get(AGENT_NAME + "-silent").isPaused());
- }
-
- @Test
- void deleteRemoves() {
- schedules.reconcile(
- AGENT_NAME,
- List.of(Schedule.builder().name("d").cron("0 * * * * ?").build()));
- schedules.delete(AGENT_NAME + "-d");
- assertTrue(schedules.list(AGENT_NAME).isEmpty());
- }
-
- @Test
- void getAfterDeleteRaises() {
- schedules.reconcile(
- AGENT_NAME,
- List.of(Schedule.builder().name("g").cron("0 * * * * ?").build()));
- String wire = AGENT_NAME + "-g";
- schedules.delete(wire);
- assertThrows(ScheduleException.NotFound.class, () -> schedules.get(wire));
- }
-
- @Test
- void previewNext() {
- List times = schedules.previewNext("0 0 9 * * ?", 3);
- assertEquals(3, times.size());
- for (int i = 1; i < times.size(); i++) {
- assertTrue(times.get(i) > times.get(i - 1));
- }
- }
-}
diff --git a/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/schedule/ScheduleRunNowTest.java b/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/schedule/ScheduleRunNowTest.java
deleted file mode 100644
index ccea66774..000000000
--- a/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/schedule/ScheduleRunNowTest.java
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Copyright 2026 Conductor Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
- * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.conductoross.conductor.ai.schedule;
-
-import java.util.Map;
-
-import org.conductoross.conductor.ai.enums.AgentStatus;
-import org.conductoross.conductor.ai.model.AgentResult;
-import org.junit.jupiter.api.Test;
-
-import com.netflix.conductor.client.http.WorkflowClient;
-import com.netflix.conductor.common.metadata.workflow.StartWorkflowRequest;
-import com.netflix.conductor.common.run.Workflow;
-import com.netflix.conductor.common.run.Workflow.WorkflowStatus;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-/**
- * Pure unit tests for the name-keyed {@code runNow} overloads (Fix 4).
- *
- *
No network: the {@link WorkflowClient} is subclassed to stub
- * {@code startWorkflow} / {@code getWorkflow}, and {@link Schedules} is
- * subclassed to stub the {@code get(name)} lookup.
- */
-class ScheduleRunNowTest {
-
- private static ScheduleInfo info(String agent) {
- return new ScheduleInfo(
- agent + "-daily",
- "daily",
- agent,
- "0 0 9 * * ?",
- "UTC",
- Map.of("k", "v"),
- false,
- null,
- false,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null);
- }
-
- /** A WorkflowClient that records start requests and serves canned getWorkflow results. */
- private static final class FakeWorkflowClient extends WorkflowClient {
- String startedName;
- Map startedInput;
- Workflow[] statusSequence;
- int polls = 0;
-
- FakeWorkflowClient() {
- // Avoid touching any real ConductorClient/network.
- super(new com.netflix.conductor.client.http.ConductorClient("http://localhost:0"));
- }
-
- @Override
- public String startWorkflow(StartWorkflowRequest req) {
- this.startedName = req.getName();
- this.startedInput = req.getInput();
- return "wf-123";
- }
-
- @Override
- public Workflow getWorkflow(String workflowId, boolean includeTasks) {
- Workflow wf = statusSequence[Math.min(polls, statusSequence.length - 1)];
- polls++;
- return wf;
- }
- }
-
- private static Workflow wf(WorkflowStatus status) {
- Workflow w = new Workflow();
- w.setStatus(status);
- w.setWorkflowId("wf-123");
- return w;
- }
-
- private static Workflow wfWithOutput(WorkflowStatus status, Map output) {
- Workflow w = wf(status);
- w.setOutput(output);
- return w;
- }
-
- /** Schedules subclass that returns a canned ScheduleInfo for get(name). */
- private static Schedules schedulesWith(FakeWorkflowClient fwc, ScheduleInfo canned) {
- return new Schedules(new com.netflix.conductor.client.http.ConductorClient("http://localhost:0"), fwc) {
- @Override
- public ScheduleInfo get(String wireName) {
- return canned;
- }
- };
- }
-
- @Test
- void runNowByName_startsWorkflowWithStoredInput_returnsExecutionId() {
- FakeWorkflowClient fwc = new FakeWorkflowClient();
- Schedules schedules = schedulesWith(fwc, info("my_agent"));
-
- String executionId = schedules.runNow("my_agent-daily");
-
- assertEquals("wf-123", executionId);
- assertEquals("my_agent", fwc.startedName, "must start the schedule's agent workflow");
- assertEquals(Map.of("k", "v"), fwc.startedInput, "must use the schedule's stored input");
- }
-
- @Test
- void runNowByName_noWait_returnsExecutionId() {
- FakeWorkflowClient fwc = new FakeWorkflowClient();
- Schedules schedules = schedulesWith(fwc, info("my_agent"));
-
- Object result = schedules.runNow("my_agent-daily", false);
- assertEquals("wf-123", result);
- assertEquals(0, fwc.polls, "non-wait must not poll for status");
- }
-
- @Test
- void runNowAndWait_pollsToTerminalAndReturnsAgentResult() {
- FakeWorkflowClient fwc = new FakeWorkflowClient();
- Workflow running = wf(WorkflowStatus.RUNNING);
- Workflow done = wfWithOutput(WorkflowStatus.COMPLETED, Map.of("result", "done"));
- fwc.statusSequence = new Workflow[] {running, running, done};
-
- Schedules schedules = schedulesWith(fwc, info("my_agent"));
-
- // 0ms poll interval keeps the test fast/deterministic.
- AgentResult result = schedules.runNowAndWait("my_agent-daily", 5_000L, 0L);
-
- assertEquals(3, fwc.polls, "must poll until terminal");
- assertEquals(AgentStatus.COMPLETED, result.getStatus(), "terminal workflow → COMPLETED");
- assertTrue(result.isSuccess());
- assertEquals("wf-123", result.getExecutionId());
- assertEquals(Map.of("result", "done"), result.getOutput(), "output carried from the workflow");
- }
-
- @Test
- void runNowAndWait_failedWorkflow_mapsToFailedResult() {
- FakeWorkflowClient fwc = new FakeWorkflowClient();
- Workflow failed = wf(WorkflowStatus.FAILED);
- failed.setReasonForIncompletion("boom");
- fwc.statusSequence = new Workflow[] {failed};
-
- Schedules schedules = schedulesWith(fwc, info("my_agent"));
-
- AgentResult result = schedules.runNowAndWait("my_agent-daily", 5_000L, 0L);
-
- assertEquals(AgentStatus.FAILED, result.getStatus());
- assertFalse(result.isSuccess());
- assertEquals("boom", result.getError());
- }
-
- @Test
- void runNowAndWait_timesOut() {
- FakeWorkflowClient fwc = new FakeWorkflowClient();
- fwc.statusSequence = new Workflow[] {wf(WorkflowStatus.RUNNING)};
-
- Schedules schedules = schedulesWith(fwc, info("my_agent"));
-
- assertThrows(
- ScheduleException.class,
- () -> schedules.runNowAndWait("my_agent-daily", 0L, 0L),
- "must raise once the deadline passes without a terminal state");
- }
-
- @Test
- void isTerminal_helper() {
- assertTrue(Schedules.isTerminal(wf(WorkflowStatus.COMPLETED)));
- assertTrue(Schedules.isTerminal(wf(WorkflowStatus.FAILED)));
- assertTrue(Schedules.isTerminal(wf(WorkflowStatus.TERMINATED)));
- assertTrue(Schedules.isTerminal(wf(WorkflowStatus.TIMED_OUT)));
- assertFalse(Schedules.isTerminal(wf(WorkflowStatus.RUNNING)));
- assertFalse(Schedules.isTerminal(wf(WorkflowStatus.PAUSED)));
- }
-}
diff --git a/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/schedule/ScheduleTest.java b/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/schedule/ScheduleTest.java
deleted file mode 100644
index eb03e4bcd..000000000
--- a/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/schedule/ScheduleTest.java
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * Copyright 2026 Conductor Authors.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
- * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations under the License.
- */
-package org.conductoross.conductor.ai.schedule;
-
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertNull;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-/** Unit tests for Schedule + Schedules helpers (no network). */
-class ScheduleTest {
-
- // ── Schedule construction ─────────────────────────────────────────
-
- @Test
- void minimal() {
- Schedule s = Schedule.builder().name("daily").cron("0 0 9 * * ?").build();
- assertEquals("daily", s.getName());
- assertEquals("UTC", s.getTimezone());
- assertFalse(s.isCatchup());
- assertFalse(s.isPaused());
- assertTrue(s.getInput().isEmpty());
- }
-
- @Test
- void full() {
- Map input = new HashMap<>();
- input.put("c", "#eng");
- Schedule s = Schedule.builder()
- .name("w")
- .cron("0 0 9 * * MON")
- .timezone("America/Los_Angeles")
- .input(input)
- .catchup(true)
- .paused(true)
- .startAt(1000L)
- .endAt(2000L)
- .description("desc")
- .build();
- assertEquals("America/Los_Angeles", s.getTimezone());
- assertEquals(input, s.getInput());
- assertTrue(s.isCatchup());
- assertTrue(s.isPaused());
- assertEquals(1000L, s.getStartAt());
- assertEquals(2000L, s.getEndAt());
- }
-
- @Test
- void rejectsEmptyName() {
- assertThrows(
- ScheduleException.class,
- () -> Schedule.builder().name("").cron("* * * * * ?").build());
- assertThrows(
- ScheduleException.class,
- () -> Schedule.builder().name(" ").cron("* * * * * ?").build());
- }
-
- @Test
- void rejectsEmptyCron() {
- assertThrows(
- ScheduleException.class,
- () -> Schedule.builder().name("x").cron("").build());
- }
-
- @Test
- void rejectsInvertedWindow() {
- assertThrows(ScheduleException.class, () -> Schedule.builder()
- .name("x")
- .cron("* * * * * ?")
- .startAt(2000L)
- .endAt(1000L)
- .build());
- assertThrows(ScheduleException.class, () -> Schedule.builder()
- .name("x")
- .cron("* * * * * ?")
- .startAt(1000L)
- .endAt(1000L)
- .build());
- }
-
- // ── Wire-name prefix/unprefix ─────────────────────────────────────
-
- @Test
- void prefixRoundtrips() {
- assertEquals("digest-daily", Schedules.prefix("digest", "daily"));
- assertEquals("daily", Schedules.unprefix("digest", "digest-daily"));
- }
-
- @Test
- void unprefixNoMatchReturnsInput() {
- assertEquals("unrelated", Schedules.unprefix("agent", "unrelated"));
- }
-
- @Test
- void agentNameWithHyphen() {
- String wire = Schedules.prefix("my-agent", "daily");
- assertEquals("my-agent-daily", wire);
- assertEquals("daily", Schedules.unprefix("my-agent", wire));
- }
-
- // ── Payload mapping ───────────────────────────────────────────────
-
- @Test
- void toSaveRequestMinimal() {
- Schedule s = Schedule.builder().name("daily").cron("0 0 9 * * ?").build();
- Map req = Schedules.toSaveRequest(s, "digest");
- assertEquals("digest-daily", req.get("name"));
- assertEquals("0 0 9 * * ?", req.get("cronExpression"));
- assertEquals("UTC", req.get("zoneId"));
- assertEquals(false, req.get("paused"));
- assertEquals(false, req.get("runCatchupScheduleInstances"));
- @SuppressWarnings("unchecked")
- Map swr = (Map) req.get("startWorkflowRequest");
- assertEquals("digest", swr.get("name"));
- assertTrue(((Map, ?>) swr.get("input")).isEmpty());
- }
-
- @Test
- void toSaveRequestFull() {
- Map input = new LinkedHashMap<>();
- input.put("c", "#eng");
- input.put("n", 42);
- Schedule s = Schedule.builder()
- .name("w")
- .cron("0 0 9 * * MON")
- .timezone("America/Los_Angeles")
- .input(input)
- .catchup(true)
- .paused(true)
- .startAt(1000L)
- .endAt(2000L)
- .description("desc")
- .build();
- Map req = Schedules.toSaveRequest(s, "digest");
- assertEquals("America/Los_Angeles", req.get("zoneId"));
- assertEquals(true, req.get("paused"));
- assertEquals(true, req.get("runCatchupScheduleInstances"));
- assertEquals(1000L, req.get("scheduleStartTime"));
- assertEquals(2000L, req.get("scheduleEndTime"));
- assertEquals("desc", req.get("description"));
- }
-
- @Test
- void inputCopiedNotShared() {
- Map original = new LinkedHashMap<>();
- original.put("a", 1);
- Schedule s =
- Schedule.builder().name("x").cron("* * * * * ?").input(original).build();
- Map req = Schedules.toSaveRequest(s, "agent");
- @SuppressWarnings("unchecked")
- Map swrInput =
- (Map) ((Map) req.get("startWorkflowRequest")).get("input");
- swrInput.put("mutated", true);
- assertNull(original.get("mutated"));
- }
-
- @Test
- void fromWorkflowScheduleBasic() {
- Map ws = new LinkedHashMap<>();
- ws.put("name", "digest-daily");
- ws.put("cronExpression", "0 0 9 * * ?");
- ws.put("zoneId", "UTC");
- ws.put("paused", false);
- Map swr = new LinkedHashMap<>();
- swr.put("name", "digest");
- Map input = new LinkedHashMap<>();
- input.put("c", "#eng");
- swr.put("input", input);
- ws.put("startWorkflowRequest", swr);
- ws.put("createTime", 111L);
- ws.put("createdBy", "alice");
-
- ScheduleInfo info = Schedules.fromWorkflowSchedule(ws, "digest");
- assertEquals("digest-daily", info.getName());
- assertEquals("daily", info.getShortName());
- assertEquals("digest", info.getAgent());
- assertEquals("0 0 9 * * ?", info.getCron());
- assertFalse(info.isPaused());
- assertEquals(input, info.getInput());
- assertEquals(111L, info.getCreateTime());
- assertEquals("alice", info.getCreatedBy());
- }
-
- @Test
- void fromWorkflowScheduleDerivesAgentWhenOmitted() {
- Map ws = new LinkedHashMap<>();
- ws.put("name", "digest-daily");
- Map swr = new LinkedHashMap<>();
- swr.put("name", "digest");
- ws.put("startWorkflowRequest", swr);
- ScheduleInfo info = Schedules.fromWorkflowSchedule(ws, null);
- assertEquals("digest", info.getAgent());
- assertEquals("daily", info.getShortName());
- }
-
- // ── Unique-name validation ────────────────────────────────────────
-
- @Test
- void distinctNamesOk() {
- Schedules.checkUniqueNames(List.of(
- Schedule.builder().name("a").cron("* * * * * ?").build(),
- Schedule.builder().name("b").cron("* * * * * ?").build()));
- }
-
- @Test
- void duplicateNameRaises() {
- assertThrows(
- ScheduleException.NameConflict.class,
- () -> Schedules.checkUniqueNames(List.of(
- Schedule.builder().name("a").cron("* * * * * ?").build(),
- Schedule.builder().name("a").cron("0 0 9 * * ?").build())));
- }
-}
diff --git a/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/tools/ToolsTest.java b/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/tools/ToolsTest.java
index d85df3e2f..3c174dd99 100644
--- a/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/tools/ToolsTest.java
+++ b/conductor-client-ai/src/test/java/org/conductoross/conductor/ai/tools/ToolsTest.java
@@ -12,6 +12,12 @@
*/
package org.conductoross.conductor.ai.tools;
+import java.util.List;
+import java.util.Map;
+
+import org.conductoross.conductor.ai.Agent;
+import org.conductoross.conductor.ai.guardrail.Guardrail;
+import org.conductoross.conductor.ai.model.GuardrailResult;
import org.conductoross.conductor.ai.model.ToolDef;
import org.junit.jupiter.api.Test;
@@ -48,6 +54,7 @@ void mcpToolShape() {
.build();
assertEquals("mcp", t.getToolType());
assertEquals("m", t.getName());
+ assertEquals("http://mcp", t.getConfig().get("server_url"));
}
@Test
@@ -74,4 +81,49 @@ void imageToolShape() {
assertEquals("img", t.getName());
assertNotNull(t.getToolType());
}
+
+ @Test
+ void withGuardrails_preserves_every_tool_field() {
+ Agent child = Agent.builder().name("child").model("openai/gpt-4o-mini").build();
+ ToolDef original = ToolDef.builder()
+ .name("complete")
+ .description("original description")
+ .inputSchema(Map.of("type", "object"))
+ .outputSchema(Map.of("type", "string"))
+ .func(input -> "ok")
+ .approvalRequired(true)
+ .timeoutSeconds(42)
+ .retryCount(5)
+ .retryDelaySeconds(7)
+ .retryPolicy("exponential_backoff")
+ .toolType("agent_tool")
+ .config(Map.of("endpoint", "https://example.test"))
+ .credentials(List.of("API_KEY"))
+ .maxCalls(3)
+ .agentRef(child)
+ .stateful(true)
+ .build();
+
+ ToolDef guarded = original.withGuardrails(List.of(
+ Guardrail.of("safe", content -> GuardrailResult.pass()).build()));
+
+ assertEquals(original.getName(), guarded.getName());
+ assertEquals(original.getDescription(), guarded.getDescription());
+ assertEquals(original.getInputSchema(), guarded.getInputSchema());
+ assertEquals(original.getOutputSchema(), guarded.getOutputSchema());
+ assertSame(original.getFunc(), guarded.getFunc());
+ assertEquals(original.isApprovalRequired(), guarded.isApprovalRequired());
+ assertEquals(original.getTimeoutSeconds(), guarded.getTimeoutSeconds());
+ assertEquals(original.getRetryCount(), guarded.getRetryCount());
+ assertEquals(original.getRetryDelaySeconds(), guarded.getRetryDelaySeconds());
+ assertEquals(original.getRetryPolicy(), guarded.getRetryPolicy());
+ assertEquals(original.getToolType(), guarded.getToolType());
+ assertEquals(original.getConfig(), guarded.getConfig());
+ assertEquals(original.getCredentials(), guarded.getCredentials());
+ assertEquals(original.getMaxCalls(), guarded.getMaxCalls());
+ assertSame(original.getAgentRef(), guarded.getAgentRef());
+ assertEquals(original.isStateful(), guarded.isStateful());
+ assertEquals(1, guarded.getGuardrails().size());
+ assertThrows(UnsupportedOperationException.class, () -> guarded.getGuardrails().add(null));
+ }
}
diff --git a/conductor-client-metrics/README.md b/conductor-client-metrics/README.md
index 9ef6e767a..f23958d8a 100644
--- a/conductor-client-metrics/README.md
+++ b/conductor-client-metrics/README.md
@@ -6,9 +6,11 @@ The `conductor-client-metrics` module provides Prometheus metrics for Java SDK c
Add the metrics module to the worker application:
+Use the published version from [Maven Central](https://search.maven.org/artifact/org.conductoross/conductor-client-metrics).
+
```groovy
dependencies {
- implementation 'org.conductoross:conductor-client-metrics:5.1.0'
+ implementation 'org.conductoross:conductor-client-metrics:'
}
```
diff --git a/conductor-client-spring-boot4/README.md b/conductor-client-spring-boot4/README.md
index 9ed7e8ec8..7ecce3727 100644
--- a/conductor-client-spring-boot4/README.md
+++ b/conductor-client-spring-boot4/README.md
@@ -1,28 +1,54 @@
-# Conductor Client Spring (Spring Boot 4)
+# Conductor Client Spring for Spring Boot 4
-Provides Spring Boot 4 / Spring Framework 7 auto-configurations for the Conductor client and SDK.
+Spring Boot 4 / Spring Framework 7 auto-configuration for the Conductor core client, workflow SDK, and Java workers.
-For Spring Boot 3 consumers, use `org.conductoross:conductor-client-spring` instead.
+**Prerequisites:** Java 21+, Spring Boot 4, and a running [OSS or Orkes Conductor server](../docs/connection-authentication.md). For Spring Boot 3, use [the Boot 3 module](../conductor-client-spring/README.md).
-## Getting Started
+## Install
-### Prerequisites
-- Java 21 or higher
-- A Spring Boot 4 project
-- A running Conductor server (local or remote)
-
-### Usage
-
-Add the dependency:
+Use the published version from [Maven Central](https://search.maven.org/artifact/org.conductoross/conductor-client-spring-boot4).
```groovy
-implementation 'org.conductoross:conductor-client-spring-boot4:'
+implementation 'org.conductoross:conductor-client-spring-boot4:'
```
-The auto-configurations are registered via `META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports` and discovered automatically by `@SpringBootApplication`. No `@ComponentScan` is required.
+```xml
+
+ org.conductoross
+ conductor-client-spring-boot4
+ <VERSION>
+
+```
-Configure the client:
+## Configure
```properties
conductor.client.root-uri=http://localhost:8080/api
+conductor.client.verifying-ssl=true
```
+
+For Orkes, inject the endpoint and credentials with your platform secret manager; see [connection and authentication](../docs/connection-authentication.md). Never commit credentials to an application configuration file.
+
+## What auto-configuration provides
+
+The module is registered in `META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports`. A normal `@SpringBootApplication` discovers it automatically; no manual `@ComponentScan` is needed.
+
+When `conductor.client.root-uri` or `conductor.client.base-path` is configured, it provides a `ConductorClient`, `TaskClient`, `WorkflowClient`, `WorkflowExecutor`, `AnnotatedWorkerExecutor`, and a `TaskRunnerConfigurer` whose lifecycle is managed by Spring. `Worker` beans and public `@WorkerTask` methods on `@Component` beans are registered for polling.
+
+```java
+@Component
+class GreetingTasks {
+ @WorkerTask("greet")
+ public @OutputParam("greeting") String greet(@InputParam("name") String name) {
+ return "Hello, " + name;
+ }
+}
+```
+
+**Expected result:** a registered `greet` task is polled by the Spring-managed worker and completes. Set `conductor.worker.greet.threadCount` to control its worker concurrency, and make external effects idempotent.
+
+## Migration from Boot 3
+
+Switch only the dependency artifact from `conductor-client-spring` to `conductor-client-spring-boot4`; the `conductor.client.*` configuration and worker annotations are intentionally the same. Verify Java 21 and your Boot 4 dependency set first. If you supplied custom client/runner beans, retain them: `@ConditionalOnMissingBean` keeps your implementations in control.
+
+Next: [Spring integration selector](../docs/spring-boot.md), [workers](../docs/workers.md), and [deployment/scaling](../docs/deployment-scaling.md).
diff --git a/conductor-client-spring/README.md b/conductor-client-spring/README.md
index d7ffc57da..023b860e0 100644
--- a/conductor-client-spring/README.md
+++ b/conductor-client-spring/README.md
@@ -1,50 +1,56 @@
-# Conductor Client Spring
+# Conductor Client Spring for Spring Boot 3
-Provides Spring framework configurations, simplifying the use of Conductor client
-in Spring-based applications.
+Spring Boot 3 auto-configuration for the Conductor core client, workflow SDK, and Java workers.
-## Getting Started
+**Prerequisites:** Java 21+, Spring Boot 3, and a running [OSS or Orkes Conductor server](../docs/connection-authentication.md). For Spring Boot 4, use [the Boot 4 module](../conductor-client-spring-boot4/README.md).
-### Prerequisites
-- Java 17 or higher
-- A Spring boot Project Gradle properly setup with Gradle or Maven
-- A running Conductor server (local or remote)
+## Install
-### Using Conductor Client Spring
+Use the published version from [Maven Central](https://search.maven.org/artifact/org.conductoross/conductor-client-spring).
-1. **Add `conductor-client-spring` dependency to your project**
-
-For Gradle:
```groovy
-implementation 'org.conductoross:conductor-client-spring:4.0.0'
+implementation 'org.conductoross:conductor-client-spring:'
```
-For Maven:
```xml
org.conductoross
conductor-client-spring
- 4.0.0
+ <VERSION>
```
-2. Add `com.netflix.conductor` to the component scan packages, e.g.:
+## Configure
-```java
-import org.springframework.boot.autoconfigure.SpringBootApplication;;
-import org.springframework.context.annotation.ComponentScan;
+```properties
+conductor.client.root-uri=http://localhost:8080/api
+conductor.client.verifying-ssl=true
+```
+
+For Orkes, provide the endpoint and credentials through your deployment's environment/secret manager; see [connection and authentication](../docs/connection-authentication.md). Never store auth credentials in `application.properties` committed to source control.
+
+## What auto-configuration provides
+
+With `@SpringBootApplication`, the module discovers its auto-configurations automatically. It creates a `ConductorClient`, `TaskClient`, `WorkflowClient`, `WorkflowExecutor`, `AnnotatedWorkerExecutor`, and a managed `TaskRunnerConfigurer` when the required beans are available. No `@ComponentScan` for `com.netflix.conductor` is required.
-@SpringBootApplication
-@ComponentScan(basePackages = {"com.netflix.conductor"})
-public class MyApp {
-
+Spring discovers `Worker` beans and `@Component` beans with public `@WorkerTask` methods. The task runner starts with the application and calls `shutdown()` during graceful Spring shutdown.
+
+```java
+@Component
+class GreetingTasks {
+ @WorkerTask("greet")
+ public @OutputParam("greeting") String greet(@InputParam("name") String name) {
+ return "Hello, " + name;
+ }
}
```
-3. Configure the client in `application.properties`
+**Expected result:** when a registered workflow reaches `greet`, the managed worker polls it and completes the task. Set per-task worker thread counts with `conductor.worker..threadCount`; keep side effects idempotent.
-```properties
-conductor.client.rootUri=http://localhost:8080/api
-```
+## Customization and migration
+
+Define your own `ConductorClient`, `TaskClient`, `WorkflowClient`, `WorkflowExecutor`, or `TaskRunnerConfigurer` bean to replace the corresponding default. Use `conductor.client.base-path` as an alternative to `root-uri`; `root-uri` wins when both are present.
+
+Older documentation showed Java 17 and a fixed `4.0.0` coordinate. This module now follows the Java 21 SDK baseline and uses `` from Maven Central. It also uses Boot auto-configuration, so remove legacy manual component scanning unless it serves your own application components.
-> **Note:** We are improving the Spring module to make the integration seamless. SEE: [[Java Client v4] Improve Spring module with auto-configuration](https://github.com/conductor-oss/conductor/issues/285)
\ No newline at end of file
+Next: [Spring integration selector](../docs/spring-boot.md), [workers](../docs/workers.md), and [reliability](../docs/reliability.md).
diff --git a/conductor-client-spring/src/main/java/com/netflix/conductor/client/spring/ConductorClientAutoConfiguration.java b/conductor-client-spring/src/main/java/com/netflix/conductor/client/spring/ConductorClientAutoConfiguration.java
index 28e2150e9..0dc00aa29 100644
--- a/conductor-client-spring/src/main/java/com/netflix/conductor/client/spring/ConductorClientAutoConfiguration.java
+++ b/conductor-client-spring/src/main/java/com/netflix/conductor/client/spring/ConductorClientAutoConfiguration.java
@@ -85,7 +85,6 @@ public TaskRunnerConfigurer taskRunnerConfigurer(Environment env,
TaskClient taskClient,
ClientProperties clientProperties,
List workers,
- Optional fileClient,
Optional metricsCollector) {
Map taskThreadCount = new HashMap<>();
for (Worker worker : workers) {
@@ -109,7 +108,6 @@ public TaskRunnerConfigurer taskRunnerConfigurer(Environment env,
.withTaskToDomain(clientProperties.getTaskToDomain())
.withShutdownGracePeriodSeconds(clientProperties.getShutdownGracePeriodSeconds())
.withTaskPollTimeout(clientProperties.getTaskPollTimeout());
- fileClient.ifPresent(builder::withFileClient);
metricsCollector.ifPresent(builder::withMetricsCollector);
return builder.build();
}
@@ -140,6 +138,6 @@ public FileClientProperties fileClientProperties() {
@ConditionalOnBean(ConductorClient.class)
@ConditionalOnMissingBean
public FileClient fileClient(ConductorClient client, FileClientProperties fileClientProperties) {
- return new FileClient(client, fileClientProperties, null);
+ return new FileClient(client, fileClientProperties);
}
}
diff --git a/conductor-client-spring/src/main/java/io/orkes/conductor/client/spring/OrkesConductorClientAutoConfiguration.java b/conductor-client-spring/src/main/java/io/orkes/conductor/client/spring/OrkesConductorClientAutoConfiguration.java
index 6e23f48ff..9f8d0a53f 100644
--- a/conductor-client-spring/src/main/java/io/orkes/conductor/client/spring/OrkesConductorClientAutoConfiguration.java
+++ b/conductor-client-spring/src/main/java/io/orkes/conductor/client/spring/OrkesConductorClientAutoConfiguration.java
@@ -143,6 +143,6 @@ public FileClientProperties fileClientProperties() {
@ConditionalOnBean(ApiClient.class)
@ConditionalOnMissingBean
public FileClient fileClient(ApiClient client, FileClientProperties fileClientProperties) {
- return new FileClient(client, fileClientProperties, null);
+ return new FileClient(client, fileClientProperties);
}
}
diff --git a/conductor-client-spring/src/test/java/com/netflix/conductor/client/spring/ConductorClientAutoConfigurationTest.java b/conductor-client-spring/src/test/java/com/netflix/conductor/client/spring/ConductorClientAutoConfigurationTest.java
index 0a1c10342..ddf0623a0 100644
--- a/conductor-client-spring/src/test/java/com/netflix/conductor/client/spring/ConductorClientAutoConfigurationTest.java
+++ b/conductor-client-spring/src/test/java/com/netflix/conductor/client/spring/ConductorClientAutoConfigurationTest.java
@@ -32,20 +32,20 @@ void zeroConfigYieldsWorkingFileClientBean() {
.run(context -> {
assertThat(context).hasSingleBean(FileClient.class);
FileClientProperties properties = context.getBean(FileClientProperties.class);
- assertThat(properties.getLocalCacheDirectory())
- .startsWith(System.getProperty("java.io.tmpdir"));
+ assertThat(properties.getMultipartThreshold())
+ .isEqualTo(100L * 1024 * 1024);
});
}
@Test
- void cacheDirectoryOverrideIsRespected() {
+ void multipartThresholdOverrideIsRespected() {
contextRunner
.withPropertyValues(
"conductor.client.base-path=http://localhost:8080/api",
- "conductor.file-client.local-cache-directory=/tmp/custom-cache")
+ "conductor.file-client.multipart-threshold=4096")
.run(context -> {
FileClientProperties properties = context.getBean(FileClientProperties.class);
- assertThat(properties.getLocalCacheDirectory()).isEqualTo("/tmp/custom-cache");
+ assertThat(properties.getMultipartThreshold()).isEqualTo(4096);
});
}
diff --git a/conductor-client/README.md b/conductor-client/README.md
index 18be60b21..f711c1c4d 100644
--- a/conductor-client/README.md
+++ b/conductor-client/README.md
@@ -5,17 +5,19 @@ This module provides the core client library to interact with Conductor through
## Getting Started
### Prerequisites
-- Java 11 or higher
+- Java 21 or higher
- A Gradle or Maven project properly set up
-- A running Conductor server (local or remote)
+- A running Conductor server ([start one locally with the CLI](../docs/server-setup.md), or use a remote server)
### Using Conductor Client
+Use the published version from [Maven Central](https://search.maven.org/artifact/org.conductoross/conductor-client).
+
1. **Add `conductor-client` dependency to your project**
For Gradle:
```groovy
-implementation 'org.conductoross:conductor-client:4.0.0'
+implementation 'org.conductoross:conductor-client:'
```
For Maven:
@@ -23,7 +25,7 @@ For Maven:
org.conductoross
conductor-client
- 4.0.0
+ <VERSION>
```
@@ -89,4 +91,17 @@ public class HelloWorker implements Worker {
}
```
-> **Note:** The full code for the above examples can be found [here](../examples/src/main/java/com/netflix/conductor/gettingstarted).
+> **Note:** For a maintained end-to-end worker example, see [Hello World](../examples/basics/hello-world/).
+
+## File uploads and downloads
+
+`FileClient` explicitly transfers workflow-scoped files and returns opaque `conductor://file/` strings:
+
+```java
+FileClient files = new FileClient(client);
+String handle = files.upload(workflowId, Path.of("input.csv"));
+FileMetadata metadata = files.getMetadata(workflowId, handle);
+Path local = files.download(workflowId, handle, Path.of("work/input.csv"));
+```
+
+For path metadata, caller-owned streams, automatic multipart, atomic replacement, raw workers, and annotated workers, see the [FileClient guide](../docs/file-client.md) and [media-transcoder example](../examples/file-storage/media-transcoder/).
diff --git a/conductor-client/build.gradle b/conductor-client/build.gradle
index 5ca0b419f..7d8b6d2d1 100644
--- a/conductor-client/build.gradle
+++ b/conductor-client/build.gradle
@@ -15,7 +15,7 @@ ext {
apply plugin: 'publish-config'
dependencies {
- implementation "com.squareup.okhttp3:okhttp:${versions.okHttp}"
+ api "com.squareup.okhttp3:okhttp:${versions.okHttp}"
// test dependencies
testImplementation "org.junit.jupiter:junit-jupiter-api:${versions.junit}"
diff --git a/conductor-client/src/main/java/com/netflix/conductor/client/automator/TaskRunner.java b/conductor-client/src/main/java/com/netflix/conductor/client/automator/TaskRunner.java
index 2ffc1dada..e8b70f28b 100644
--- a/conductor-client/src/main/java/com/netflix/conductor/client/automator/TaskRunner.java
+++ b/conductor-client/src/main/java/com/netflix/conductor/client/automator/TaskRunner.java
@@ -14,7 +14,6 @@
import java.io.PrintWriter;
import java.io.StringWriter;
-import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
@@ -35,11 +34,6 @@
import java.util.function.Function;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
-import org.conductoross.conductor.client.FileClient;
-import org.conductoross.conductor.sdk.file.FileHandler;
-import org.conductoross.conductor.sdk.file.FileUploadOptions;
-import org.conductoross.conductor.sdk.file.LocalFileHandler;
-import org.conductoross.conductor.sdk.file.WorkflowFileClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -89,7 +83,6 @@ class TaskRunner {
private final EventDispatcher eventDispatcher;
private final LinkedBlockingQueue tasksTobeExecuted;
private final boolean enableUpdateV2;
- private final FileClient fileClient;
private static final int LEASE_EXTEND_RETRY_COUNT = 3;
private static final double LEASE_EXTEND_DURATION_FACTOR = 0.8;
private final ScheduledExecutorService leaseExtendExecutorService;
@@ -106,11 +99,9 @@ class TaskRunner {
int taskPollTimeout,
List pollFilters,
EventDispatcher eventDispatcher,
- boolean useVirtualThreads,
- FileClient fileClient) {
+ boolean useVirtualThreads) {
this.worker = worker;
this.taskClient = taskClient;
- this.fileClient = fileClient;
this.updateRetryCount = updateRetryCount;
this.taskPollTimeout = taskPollTimeout;
this.pollingIntervalInMillis = worker.getPollingInterval();
@@ -299,7 +290,7 @@ private List pollTasksForWorker() {
permits.release(pollCount - tasks.size()); //release extra permits
stopwatch.stop();
long elapsed = stopwatch.elapsed(TimeUnit.MILLISECONDS);
- LOGGER.debug("Time taken to poll {} task with a batch size of {} is {} ms", taskType, tasks.size(), elapsed);
+ LOGGER.trace("Time taken to poll {} task with a batch size of {} is {} ms", taskType, tasks.size(), elapsed);
eventDispatcher.publish(new PollCompleted(taskType, elapsed));
} catch (Throwable e) {
permits.release(pollCount - tasks.size());
@@ -408,11 +399,6 @@ private void executeTask(Worker worker, Task task) {
return;
}
- // Set FileClient on task for file storage support
- if (fileClient != null) {
- task.setWorkflowFileClient(new WorkflowFileClient(fileClient, task.getWorkflowInstanceId()));
- }
-
// Calculate inbound network latency
try {
if(task.getExecutionMetadata().getServerSendTime() != null ){
@@ -435,7 +421,6 @@ private void executeTask(Worker worker, Task task) {
worker.getIdentity());
result = worker.execute(task);
stopwatch.stop();
- uploadFilesToFileStorage(result, task.getWorkflowInstanceId(), task.getTaskId());
eventDispatcher.publish(new TaskExecutionCompleted(taskType, task.getTaskId(), worker.getIdentity(), stopwatch.elapsed(TimeUnit.MILLISECONDS)));
// record execution end time in task
task.getExecutionMetadata().setExecutionEndTime(System.currentTimeMillis());
@@ -537,26 +522,6 @@ private void updateTaskResult(int count, Task task, TaskResult result, Worker wo
}
}
- @VisibleForTesting
- void uploadFilesToFileStorage(TaskResult result, String workflowId, String taskId) {
- if (fileClient == null || result.getOutputData() == null) {
- return;
- }
- for (var entry : result.getOutputData().entrySet()) {
- if (!(entry.getValue() instanceof FileHandler fh)) {
- continue;
- }
- if (fh.getFileHandleId() == null) {
- Path path = ((LocalFileHandler) fh).getPath();
- FileUploadOptions options = new FileUploadOptions()
- .setContentType(fh.getContentType())
- .setTaskId(taskId);
- FileHandler uploaded = fileClient.upload(workflowId, path, options);
- entry.setValue(uploaded);
- }
- }
- }
-
private Optional upload(TaskResult result, String taskType) {
try {
return taskClient.evaluateAndUploadLargePayload(result.getOutputData(), taskType);
diff --git a/conductor-client/src/main/java/com/netflix/conductor/client/automator/TaskRunnerConfigurer.java b/conductor-client/src/main/java/com/netflix/conductor/client/automator/TaskRunnerConfigurer.java
index c198792eb..030246917 100644
--- a/conductor-client/src/main/java/com/netflix/conductor/client/automator/TaskRunnerConfigurer.java
+++ b/conductor-client/src/main/java/com/netflix/conductor/client/automator/TaskRunnerConfigurer.java
@@ -21,7 +21,6 @@
import java.util.function.Consumer;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
-import org.conductoross.conductor.client.FileClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -56,7 +55,6 @@ public class TaskRunnerConfigurer {
private final EventDispatcher eventDispatcher;
private final MetricsCollector metricsCollector;
private final boolean useVirtualThreads;
- private final FileClient fileClient;
/**
* @see TaskRunnerConfigurer.Builder
@@ -79,7 +77,6 @@ private TaskRunnerConfigurer(TaskRunnerConfigurer.Builder builder) {
this.eventDispatcher = builder.eventDispatcher;
this.metricsCollector = builder.metricsCollector;
this.useVirtualThreads = builder.useVirtualThreads;
- this.fileClient = builder.fileClient;
builder.workers.forEach(this.workers::add);
taskRunners = new LinkedList<>();
}
@@ -182,8 +179,7 @@ private void startWorker(Worker worker) {
taskPollTimeout,
pollFilters,
eventDispatcher,
- useVirtualThreads,
- fileClient);
+ useVirtualThreads);
// startWorker(worker) is executed by several threads.
// taskRunners.add(taskRunner) without synchronization could lead to a race condition and unpredictable behavior,
// including potential null values being inserted or corrupted state.
@@ -216,7 +212,6 @@ public static class Builder {
private final List pollFilters = new LinkedList<>();
private final EventDispatcher eventDispatcher = new EventDispatcher<>();
private boolean useVirtualThreads;
- private FileClient fileClient;
/**
* Returns the event dispatcher used by this builder, allowing direct
@@ -388,9 +383,5 @@ public Builder withUseVirtualThreads(boolean useVirtualThreads) {
return this;
}
- public Builder withFileClient(FileClient fileClient) {
- this.fileClient = fileClient;
- return this;
- }
}
}
diff --git a/conductor-client/src/main/java/com/netflix/conductor/client/http/ConductorClient.java b/conductor-client/src/main/java/com/netflix/conductor/client/http/ConductorClient.java
index 90bd776b8..aaa3073d3 100644
--- a/conductor-client/src/main/java/com/netflix/conductor/client/http/ConductorClient.java
+++ b/conductor-client/src/main/java/com/netflix/conductor/client/http/ConductorClient.java
@@ -735,14 +735,13 @@ protected void validateAndAssignDefaults() {
/**
* Resolves the server base path from environment: {@code CONDUCTOR_SERVER_URL}
- * → {@code AGENTSPAN_SERVER_URL} (legacy fallback) → {@code http://localhost:8080/api}.
+ * → {@code http://localhost:8080/api}.
* Blank values are treated as unset. The resolved URL is normalized to end in
* {@code /api}, so both {@code http://host:8080} and {@code http://host:8080/api}
* forms are accepted.
*/
protected void applyEnvVariables() {
- String serverUrl = envOrDefault("CONDUCTOR_SERVER_URL",
- envOrDefault("AGENTSPAN_SERVER_URL", null));
+ String serverUrl = envOrDefault("CONDUCTOR_SERVER_URL", null);
this.basePath(normalizeServerUrl(serverUrl));
}
diff --git a/conductor-client/src/main/java/com/netflix/conductor/client/worker/Worker.java b/conductor-client/src/main/java/com/netflix/conductor/client/worker/Worker.java
index cfeda2603..bc5a7cb8d 100644
--- a/conductor-client/src/main/java/com/netflix/conductor/client/worker/Worker.java
+++ b/conductor-client/src/main/java/com/netflix/conductor/client/worker/Worker.java
@@ -86,7 +86,7 @@ default String getIdentity() {
serverId = System.getenv("HOSTNAME");
}
- LoggerHolder.logger.debug("Setting worker id to {}", serverId);
+ LoggerHolder.logger.trace("Setting worker id to {}", serverId);
return serverId;
}
diff --git a/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/Task.java b/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/Task.java
index 56efdf3be..720d8df23 100644
--- a/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/Task.java
+++ b/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/Task.java
@@ -18,16 +18,10 @@
import java.util.Optional;
import org.apache.commons.lang3.StringUtils;
-import org.conductoross.conductor.sdk.file.FileHandler;
-import org.conductoross.conductor.sdk.file.FileStorageException;
-import org.conductoross.conductor.sdk.file.FileUploader;
-import org.conductoross.conductor.sdk.file.ManagedFileHandler;
-import org.conductoross.conductor.sdk.file.WorkflowFileClient;
import com.netflix.conductor.common.metadata.workflow.WorkflowTask;
import com.netflix.conductor.common.run.tasks.TypedTask;
-import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.*;
@Data
@@ -186,38 +180,12 @@ public boolean isRetriable() {
private long firstStartTime;
/**
- * Secret values delivered by a capable host (agentspan > 0.4.2, conductor-oss
- * PR #1255) for the names declared on {@code TaskDef.runtimeMetadata} — resolved
+ * Secret values delivered by a Conductor OSS host with PR #1255 for the names declared on
+ * {@code TaskDef.runtimeMetadata} — resolved
* at poll time, wire-only, never persisted to task input.
*/
private Map runtimeMetadata;
- @JsonIgnore
- private transient WorkflowFileClient workflowFileClient;
-
- @JsonIgnore
- public FileUploader getFileUploader() {
- return workflowFileClient;
- }
-
- public void setWorkflowFileClient(WorkflowFileClient workflowFileClient) {
- this.workflowFileClient = workflowFileClient;
- }
-
- public FileHandler getInputFileHandler(String key) {
- if (workflowFileClient == null) {
- throw new FileStorageException("FileClient is not configured; cannot access file input for key '" + key + "'");
- }
- Object value = getInputData().get(key);
- String fileHandleId = FileHandler.extractFileHandleId(value);
- if (FileHandler.isFileHandleId(fileHandleId)) {
- return new ManagedFileHandler(fileHandleId, workflowFileClient);
- }
- throw new FileStorageException(
- "Expected " + FileHandler.PREFIX
- + " reference for key '" + key + "', got: " + value);
- }
-
public void setInputData(Map inputData) {
if (inputData == null) {
inputData = new HashMap<>();
diff --git a/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskDef.java b/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskDef.java
index 1dd5ae994..b41968572 100644
--- a/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskDef.java
+++ b/conductor-client/src/main/java/com/netflix/conductor/common/metadata/tasks/TaskDef.java
@@ -109,8 +109,8 @@ public enum RetryLogic {
private long totalTimeoutSeconds;
/**
- * Declared secret names for the task's worker. Capable hosts (agentspan > 0.4.2,
- * conductor-oss PR #1255) resolve these at poll time and deliver the values on the
+ * Declared secret names for the task's worker. Conductor OSS hosts with PR #1255 resolve
+ * these at poll time and deliver the values on the
* wire-only {@code Task.runtimeMetadata} map — never persisted to task input.
*/
private List runtimeMetadata;
@@ -180,4 +180,4 @@ public boolean equals(Object o) {
public int hashCode() {
return Objects.hash(getName(), getDescription(), getRetryCount(), getTimeoutSeconds(), getInputKeys(), getOutputKeys(), getTimeoutPolicy(), getRetryLogic(), getRetryDelaySeconds(), getBackoffScaleFactor(), getResponseTimeoutSeconds(), getConcurrentExecLimit(), getRateLimitPerFrequency(), getInputTemplate(), getIsolationGroupId(), getExecutionNameSpace(), getOwnerEmail(), getBaseType(), getInputSchema(), getOutputSchema(), getTotalTimeoutSeconds());
}
-}
\ No newline at end of file
+}
diff --git a/conductor-client/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorker.java b/conductor-client/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorker.java
index 7339a0ab5..3a1b5bfd6 100644
--- a/conductor-client/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorker.java
+++ b/conductor-client/src/main/java/com/netflix/conductor/sdk/workflow/executor/task/AnnotatedWorker.java
@@ -21,11 +21,6 @@
import java.lang.reflect.Type;
import java.util.*;
-import org.conductoross.conductor.sdk.file.FileHandler;
-import org.conductoross.conductor.sdk.file.FileHandlerDeserializer;
-import org.conductoross.conductor.sdk.file.FileStorageException;
-import org.conductoross.conductor.sdk.file.ManagedFileHandler;
-
import com.netflix.conductor.client.worker.Worker;
import com.netflix.conductor.common.config.ObjectMapperProvider;
import com.netflix.conductor.common.metadata.tasks.Task;
@@ -140,7 +135,7 @@ private Object[] getParameters(Task task, Class>[] parameterTypes, Parameter[]
Class> parameterType = parameterTypes[i];
values[i] = getInputValue(task, parameterType, type, paramAnnotation);
} else {
- values[i] = convertWithContext(task.getInputData(), parameterTypes[i], task);
+ values[i] = convertValue(task.getInputData(), parameterTypes[i]);
}
}
@@ -166,7 +161,7 @@ private Object getInputValue(
InputParam ip = findInputParamAnnotation(paramAnnotation);
if (ip == null) {
- return convertWithContext(task.getInputData(), parameterType, task);
+ return convertValue(task.getInputData(), parameterType);
}
final String name = ip.value();
@@ -175,16 +170,6 @@ private Object getInputValue(
return null;
}
- if (parameterType == FileHandler.class) {
- String fileHandleId = FileHandler.extractFileHandleId(value);
- if (FileHandler.isFileHandleId(fileHandleId)) {
- return new ManagedFileHandler(fileHandleId, task.getWorkflowFileClient());
- }
- throw new FileStorageException(
- "Expected " + FileHandler.PREFIX
- + " reference for param '" + name + "', got: " + value);
- }
-
if (List.class.isAssignableFrom(parameterType)) {
List> list = om.convertValue(value, List.class);
if (type instanceof ParameterizedType) {
@@ -192,7 +177,7 @@ private Object getInputValue(
Class> typeOfParameter = (Class>) parameterizedType.getActualTypeArguments()[0];
List parameterizedList = new ArrayList<>();
for (Object item : list) {
- parameterizedList.add(convertWithContext(item, typeOfParameter, task));
+ parameterizedList.add(convertValue(item, typeOfParameter));
}
return parameterizedList;
@@ -200,18 +185,14 @@ private Object getInputValue(
return list;
}
} else {
- return convertWithContext(value, parameterType, task);
+ return convertValue(value, parameterType);
}
}
- private Object convertWithContext(Object source, Class> targetType, Task task) {
+ private Object convertValue(Object source, Class> targetType) {
JsonNode tree = om.valueToTree(source);
try (JsonParser parser = tree.traverse(om)) {
- return om.readerFor(targetType)
- .withAttribute(
- FileHandlerDeserializer.WORKFLOW_FILE_CLIENT_ATTR,
- task.getWorkflowFileClient())
- .readValue(parser);
+ return om.readerFor(targetType).readValue(parser);
} catch (IOException e) {
throw new RuntimeException("Failed to bind task input to " + targetType.getName(), e);
}
@@ -232,15 +213,6 @@ private TaskResult setValue(Object invocationResult, TaskResult result) {
return result;
}
- if (invocationResult instanceof FileHandler fh) {
- OutputParam opAnn =
- workerMethod.getAnnotatedReturnType().getAnnotation(OutputParam.class);
- String key = opAnn != null ? opAnn.value() : "result";
- result.getOutputData().put(key, fh);
- result.setStatus(TaskResult.Status.COMPLETED);
- return result;
- }
-
OutputParam opAnnotation =
workerMethod.getAnnotatedReturnType().getAnnotation(OutputParam.class);
if (opAnnotation != null) {
diff --git a/conductor-client/src/main/java/io/orkes/conductor/client/AgentClient.java b/conductor-client/src/main/java/io/orkes/conductor/client/AgentClient.java
index 33f6d94f4..363bad79b 100644
--- a/conductor-client/src/main/java/io/orkes/conductor/client/AgentClient.java
+++ b/conductor-client/src/main/java/io/orkes/conductor/client/AgentClient.java
@@ -70,6 +70,17 @@ public interface AgentClient extends AutoCloseable {
/** {@code POST /api/agent/{executionId}/stop} — graceful deterministic stop. */
void stopAgent(String executionId);
+ /**
+ * {@code DELETE /api/agent/{executionId}/cancel} — immediately cancel an execution and
+ * optionally record a reason.
+ *
+ * The default preserves compatibility for custom {@code AgentClient} implementations
+ * compiled before cancellation was added. Transport implementations should override it.
+ */
+ default void cancelAgent(String executionId, String reason) {
+ throw new UnsupportedOperationException("Agent cancellation is not supported");
+ }
+
/** {@code POST /api/agent/{executionId}/signal} — inject persistent context. */
void signalAgent(String executionId, String message);
diff --git a/conductor-client/src/main/java/io/orkes/conductor/client/ApiClient.java b/conductor-client/src/main/java/io/orkes/conductor/client/ApiClient.java
index 3252a7ec9..191ad2178 100644
--- a/conductor-client/src/main/java/io/orkes/conductor/client/ApiClient.java
+++ b/conductor-client/src/main/java/io/orkes/conductor/client/ApiClient.java
@@ -44,7 +44,7 @@
* users of orkes-conductor-client v2.
*/
@Slf4j
-public final class ApiClient extends ConductorClient {
+public class ApiClient extends ConductorClient {
private final OrkesAuthentication authentication;
@@ -209,19 +209,15 @@ public ApiClient build() {
/**
* Resolves credentials from environment on top of the base-path chain:
- * {@code CONDUCTOR_AUTH_KEY} → {@code CONDUCTOR_SERVER_AUTH_KEY} →
- * {@code AGENTSPAN_AUTH_KEY} (legacy fallback), same order for the secret.
+ * {@code CONDUCTOR_AUTH_KEY} → {@code CONDUCTOR_SERVER_AUTH_KEY}, same order for the secret.
*/
@Override
protected void applyEnvVariables() {
super.applyEnvVariables();
- String authKey = envOrDefault("CONDUCTOR_AUTH_KEY",
- envOrDefault("CONDUCTOR_SERVER_AUTH_KEY",
- envOrDefault("AGENTSPAN_AUTH_KEY", null)));
- String authSecret = envOrDefault("CONDUCTOR_AUTH_SECRET",
- envOrDefault("CONDUCTOR_SERVER_AUTH_SECRET",
- envOrDefault("AGENTSPAN_AUTH_SECRET", null)));
+ String authKey = envOrDefault("CONDUCTOR_AUTH_KEY", envOrDefault("CONDUCTOR_SERVER_AUTH_KEY", null));
+ String authSecret = envOrDefault(
+ "CONDUCTOR_AUTH_SECRET", envOrDefault("CONDUCTOR_SERVER_AUTH_SECRET", null));
if (authKey != null && authSecret != null) {
this.credentials(authKey, authSecret);
diff --git a/conductor-client/src/main/java/io/orkes/conductor/client/SchedulerClient.java b/conductor-client/src/main/java/io/orkes/conductor/client/SchedulerClient.java
index 878176fd6..9568d1c1a 100644
--- a/conductor-client/src/main/java/io/orkes/conductor/client/SchedulerClient.java
+++ b/conductor-client/src/main/java/io/orkes/conductor/client/SchedulerClient.java
@@ -92,6 +92,19 @@ public interface SchedulerClient {
*/
void pauseSchedule(String name);
+ /**
+ * Pauses a specific schedule and records an optional human-readable reason.
+ *
+ *
The default preserves source and binary compatibility for custom client
+ * implementations that only support pausing without a reason.
+ *
+ * @param name the name of the schedule to pause
+ * @param reason optional reason for pausing the schedule
+ */
+ default void pauseSchedule(String name, String reason) {
+ pauseSchedule(name);
+ }
+
/**
* Resumes a paused schedule, allowing new workflow executions.
*
diff --git a/conductor-client/src/main/java/io/orkes/conductor/client/exceptions/AgentAPIException.java b/conductor-client/src/main/java/io/orkes/conductor/client/exceptions/AgentAPIException.java
index b969320ac..ad52105b6 100644
--- a/conductor-client/src/main/java/io/orkes/conductor/client/exceptions/AgentAPIException.java
+++ b/conductor-client/src/main/java/io/orkes/conductor/client/exceptions/AgentAPIException.java
@@ -13,9 +13,9 @@
package io.orkes.conductor.client.exceptions;
/**
- * Thrown when the Agentspan server returns a non-2xx HTTP response.
+ * Thrown when the Conductor agent server returns a non-2xx HTTP response.
*/
-public class AgentAPIException extends AgentspanException {
+public class AgentAPIException extends AgentException {
private final int statusCode;
private final String responseBody;
diff --git a/conductor-client/src/main/java/io/orkes/conductor/client/exceptions/AgentspanException.java b/conductor-client/src/main/java/io/orkes/conductor/client/exceptions/AgentException.java
similarity index 72%
rename from conductor-client/src/main/java/io/orkes/conductor/client/exceptions/AgentspanException.java
rename to conductor-client/src/main/java/io/orkes/conductor/client/exceptions/AgentException.java
index 288d55a8f..505020681 100644
--- a/conductor-client/src/main/java/io/orkes/conductor/client/exceptions/AgentspanException.java
+++ b/conductor-client/src/main/java/io/orkes/conductor/client/exceptions/AgentException.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2025 Conductor Authors.
+ * Copyright 2026 Conductor Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
@@ -12,16 +12,14 @@
*/
package io.orkes.conductor.client.exceptions;
-/**
- * Base exception for all Agentspan SDK errors.
- */
-public class AgentspanException extends RuntimeException {
+/** Base exception for Conductor agent SDK errors. */
+public class AgentException extends RuntimeException {
- public AgentspanException(String message) {
+ public AgentException(String message) {
super(message);
}
- public AgentspanException(String message, Throwable cause) {
+ public AgentException(String message, Throwable cause) {
super(message, cause);
}
}
diff --git a/conductor-client/src/main/java/io/orkes/conductor/client/exceptions/SSEUnavailableException.java b/conductor-client/src/main/java/io/orkes/conductor/client/exceptions/SSEUnavailableException.java
index b09c845af..75f15219c 100644
--- a/conductor-client/src/main/java/io/orkes/conductor/client/exceptions/SSEUnavailableException.java
+++ b/conductor-client/src/main/java/io/orkes/conductor/client/exceptions/SSEUnavailableException.java
@@ -18,7 +18,7 @@
* Python SDK. Callers should degrade to status polling rather than treating
* the stream as silently empty.
*/
-public class SSEUnavailableException extends AgentspanException {
+public class SSEUnavailableException extends AgentException {
public SSEUnavailableException(String message) {
super(message);
diff --git a/conductor-client/src/main/java/io/orkes/conductor/client/http/OrkesAgentClient.java b/conductor-client/src/main/java/io/orkes/conductor/client/http/OrkesAgentClient.java
index a96a26c17..202c75540 100644
--- a/conductor-client/src/main/java/io/orkes/conductor/client/http/OrkesAgentClient.java
+++ b/conductor-client/src/main/java/io/orkes/conductor/client/http/OrkesAgentClient.java
@@ -135,6 +135,18 @@ public void stopAgent(String executionId) {
execute(req);
}
+ @Override
+ public void cancelAgent(String executionId, String reason) {
+ ConductorClientRequest.Builder builder = ConductorClientRequest.builder()
+ .method(Method.DELETE)
+ .path("/agent/{executionId}/cancel")
+ .addPathParam("executionId", executionId);
+ if (reason != null && !reason.isBlank()) {
+ builder.addQueryParam("reason", reason);
+ }
+ execute(builder.build());
+ }
+
@Override
public void signalAgent(String executionId, String message) {
ConductorClientRequest req = ConductorClientRequest.builder()
diff --git a/conductor-client/src/main/java/io/orkes/conductor/client/http/OrkesAuthentication.java b/conductor-client/src/main/java/io/orkes/conductor/client/http/OrkesAuthentication.java
index 27f9182f6..d9e7acf13 100644
--- a/conductor-client/src/main/java/io/orkes/conductor/client/http/OrkesAuthentication.java
+++ b/conductor-client/src/main/java/io/orkes/conductor/client/http/OrkesAuthentication.java
@@ -48,6 +48,7 @@ public class OrkesAuthentication implements HeaderSupplier {
// Guarded by refreshLock.
private int tokenRefreshFailures = 0;
private long lastTokenRefreshAttempt = 0;
+ private volatile boolean authenticationDisabled;
private TokenResource tokenResource;
@@ -95,18 +96,28 @@ public void init(ConductorClient client) {
@Override
public Map get(String method, String path) {
- if ("/token".equalsIgnoreCase(path)) {
+ if (authenticationDisabled || "/token".equalsIgnoreCase(path)) {
return Map.of();
}
- return Map.of("X-Authorization", getToken());
+ String token = getToken();
+ return token == null ? Map.of() : Map.of("X-Authorization", token);
}
public String getToken() {
+ if (authenticationDisabled) {
+ return null;
+ }
+
try {
return tokenCache.get(TOKEN_CACHE_KEY, this::refreshToken);
} catch (ExecutionException e) {
return null;
+ } catch (RuntimeException e) {
+ if (authenticationDisabled) {
+ return null;
+ }
+ throw e;
}
}
@@ -120,13 +131,24 @@ public String getToken() {
*/
public String refreshIfStale(String staleToken) {
synchronized (refreshLock) {
+ if (authenticationDisabled) {
+ return null;
+ }
+
String current = tokenCache.getIfPresent(TOKEN_CACHE_KEY);
if (current != null && !current.equals(staleToken)) {
return current; // another thread already rotated it
}
- String fresh = refreshToken();
- tokenCache.put(TOKEN_CACHE_KEY, fresh); // resets TTL and unblocks others
- return fresh;
+ try {
+ String fresh = refreshToken();
+ tokenCache.put(TOKEN_CACHE_KEY, fresh); // resets TTL and unblocks others
+ return fresh;
+ } catch (RuntimeException e) {
+ if (authenticationDisabled) {
+ return null;
+ }
+ throw e;
+ }
}
}
@@ -171,6 +193,12 @@ private String refreshToken() {
tokenRefreshFailures = 0;
return response.getToken();
} catch (RuntimeException e) {
+ if (isUnsupportedTokenEndpoint(e)) {
+ authenticationDisabled = true;
+ tokenRefreshFailures = 0;
+ LOGGER.warn("Token endpoint is unavailable (404); disabling key/secret authentication for this client");
+ throw e;
+ }
tokenRefreshFailures++;
LOGGER.error("Failed to refresh authentication token (attempt {}): {}",
tokenRefreshFailures, e.getMessage());
@@ -178,4 +206,14 @@ private String refreshToken() {
}
}
}
+
+ private boolean isUnsupportedTokenEndpoint(Throwable error) {
+ for (Throwable cause = error; cause != null; cause = cause.getCause()) {
+ if (cause instanceof ConductorClientException
+ && ((ConductorClientException) cause).getStatus() == 404) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/conductor-client/src/main/java/io/orkes/conductor/client/http/OrkesSchedulerClient.java b/conductor-client/src/main/java/io/orkes/conductor/client/http/OrkesSchedulerClient.java
index bacbbdaaf..2778e4deb 100644
--- a/conductor-client/src/main/java/io/orkes/conductor/client/http/OrkesSchedulerClient.java
+++ b/conductor-client/src/main/java/io/orkes/conductor/client/http/OrkesSchedulerClient.java
@@ -64,7 +64,12 @@ public void pauseAllSchedules() {
@Override
public void pauseSchedule(String name) {
- schedulerResource.pauseSchedule(name);
+ pauseSchedule(name, null);
+ }
+
+ @Override
+ public void pauseSchedule(String name, String reason) {
+ schedulerResource.pauseSchedule(name, reason);
}
@Override
diff --git a/conductor-client/src/main/java/io/orkes/conductor/client/http/SchedulerResource.java b/conductor-client/src/main/java/io/orkes/conductor/client/http/SchedulerResource.java
index 2506c0df1..fe9d7db38 100644
--- a/conductor-client/src/main/java/io/orkes/conductor/client/http/SchedulerResource.java
+++ b/conductor-client/src/main/java/io/orkes/conductor/client/http/SchedulerResource.java
@@ -15,6 +15,7 @@
import java.util.List;
import java.util.Map;
+import com.netflix.conductor.client.exception.ConductorClientException;
import com.netflix.conductor.client.http.ConductorClient;
import com.netflix.conductor.client.http.ConductorClientRequest;
import com.netflix.conductor.client.http.ConductorClientRequest.Method;
@@ -104,13 +105,24 @@ public Map pauseAllSchedules() {
}
public void pauseSchedule(String name) {
- ConductorClientRequest request = ConductorClientRequest.builder()
+ pauseSchedule(name, null);
+ }
+
+ public void pauseSchedule(String name, String reason) {
+ ConductorClientRequest getRequest = ConductorClientRequest.builder()
.method(Method.GET)
.path("/scheduler/schedules/{name}/pause")
.addPathParam("name", name)
+ .addQueryParam("reason", reason)
+ .build();
+ ConductorClientRequest putRequest = ConductorClientRequest.builder()
+ .method(Method.PUT)
+ .path("/scheduler/schedules/{name}/pause")
+ .addPathParam("name", name)
+ .addQueryParam("reason", reason)
.build();
- client.execute(request);
+ executeGetThenPutOnMethodNotAllowed(getRequest, putRequest);
}
public Map requeueAllExecutionRecords() {
@@ -138,13 +150,35 @@ public Map resumeAllSchedules() {
}
public void resumeSchedule(String name) {
- ConductorClientRequest request = ConductorClientRequest.builder()
+ ConductorClientRequest getRequest = ConductorClientRequest.builder()
.method(Method.GET)
.path("/scheduler/schedules/{name}/resume")
.addPathParam("name", name)
.build();
+ ConductorClientRequest putRequest = ConductorClientRequest.builder()
+ .method(Method.PUT)
+ .path("/scheduler/schedules/{name}/resume")
+ .addPathParam("name", name)
+ .build();
- client.execute(request);
+ executeGetThenPutOnMethodNotAllowed(getRequest, putRequest);
+ }
+
+ /**
+ * Enterprise scheduler endpoints accept GET while OSS accepts PUT. Retry only
+ * a method-not-allowed response so application and authentication failures
+ * retain their original behavior.
+ */
+ private void executeGetThenPutOnMethodNotAllowed(
+ ConductorClientRequest getRequest, ConductorClientRequest putRequest) {
+ try {
+ client.execute(getRequest);
+ } catch (ConductorClientException e) {
+ if (e.getStatus() != 405) {
+ throw e;
+ }
+ client.execute(putRequest);
+ }
}
public void saveSchedule(SaveScheduleRequest saveScheduleRequest) {
diff --git a/conductor-client/src/main/java/io/orkes/conductor/client/model/SaveScheduleRequest.java b/conductor-client/src/main/java/io/orkes/conductor/client/model/SaveScheduleRequest.java
index c1190ce99..b413dbea7 100644
--- a/conductor-client/src/main/java/io/orkes/conductor/client/model/SaveScheduleRequest.java
+++ b/conductor-client/src/main/java/io/orkes/conductor/client/model/SaveScheduleRequest.java
@@ -104,4 +104,9 @@ public SaveScheduleRequest zoneId(String zoneId) {
return this;
}
-}
\ No newline at end of file
+ public SaveScheduleRequest description(String description) {
+ this.description = description;
+ return this;
+ }
+
+}
diff --git a/conductor-client/src/main/java/io/orkes/conductor/client/model/WorkflowSchedule.java b/conductor-client/src/main/java/io/orkes/conductor/client/model/WorkflowSchedule.java
index f29e1c958..a12cba8ce 100644
--- a/conductor-client/src/main/java/io/orkes/conductor/client/model/WorkflowSchedule.java
+++ b/conductor-client/src/main/java/io/orkes/conductor/client/model/WorkflowSchedule.java
@@ -56,6 +56,8 @@ public class WorkflowSchedule {
private String description;
+ private Long nextRunTime;
+
public WorkflowSchedule createTime(Long createTime) {
this.createTime = createTime;
return this;
@@ -123,4 +125,9 @@ public WorkflowSchedule zoneId(String zoneId) {
this.zoneId = zoneId;
return this;
}
-}
\ No newline at end of file
+
+ public WorkflowSchedule nextRunTime(Long nextRunTime) {
+ this.nextRunTime = nextRunTime;
+ return this;
+ }
+}
diff --git a/conductor-client/src/main/java/io/orkes/conductor/client/model/agent/AgentRequest.java b/conductor-client/src/main/java/io/orkes/conductor/client/model/agent/AgentRequest.java
index a3b4bab4b..0f2de9bcf 100644
--- a/conductor-client/src/main/java/io/orkes/conductor/client/model/agent/AgentRequest.java
+++ b/conductor-client/src/main/java/io/orkes/conductor/client/model/agent/AgentRequest.java
@@ -17,6 +17,8 @@
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+import lombok.ToString;
/**
* Request payload for {@code POST /api/agent/compile}, {@code /deploy}, and {@code /start}.
@@ -24,62 +26,70 @@
* All three endpoints share the same server-side {@code StartRequest} DTO. The agent
* definition arrives pre-serialized as a JSON-ready map — domain serialization is owned by
* the agent SDK ({@code conductor-client-ai}), keeping this transport DTO free of agent types.
- * Native agents carry it under {@code "agentConfig"}; framework-backed agents under
- * {@code "framework"} + {@code "rawConfig"}.
+ * Deployed agents carry {@code "name"} + optional {@code "version"}; native agents carry their
+ * definition under {@code "agentConfig"}; framework-backed agents use {@code "framework"} plus
+ * {@code "rawConfig"} or {@code "skillRef"}.
*
- *
Build via {@link #nativeAgent(Object)} or {@link #frameworkAgent(String, Object)},
- * then chain builder methods for execution-specific fields. Unset ({@code null}) fields
- * are omitted from the JSON body.
+ *
Build via {@link #deployedAgent(String, Integer)}, {@link #nativeAgent(Object)}, or {@link
+ * #frameworkAgent(String, Object)}, then chain builder methods for execution-specific fields.
+ * Unset ({@code null}) fields are omitted from the JSON body.
*/
@JsonInclude(JsonInclude.Include.NON_NULL)
+@Data
+@ToString(onlyExplicitlyIncluded = true)
public final class AgentRequest {
// ── Agent definition (mutually exclusive shapes) ─────────────────────
+ /** Name of an already-deployed agent; {@code null} for inline definitions. */
+ private final String name;
+
+ /** Optional version of an already-deployed agent. */
+ private final Integer version;
+
/** Serialized agent definition for native agents; {@code null} on the framework path. */
- @JsonProperty("agentConfig")
private final Object agentConfig;
/** Framework wire name (e.g. {@code "openai"}); {@code null} on the native path. */
- @JsonProperty("framework")
private final String framework;
/** Serialized agent definition for framework-backed agents; {@code null} on the native path. */
- @JsonProperty("rawConfig")
private final Object rawConfig;
+ /** Optional model override understood by the server's agent control plane. */
+ private final String model;
+
+ /** Skill reference for framework-backed agents that do not provide {@code rawConfig}. */
+ private final Map skillRef;
+
// ── Execution fields (only meaningful for /start) ────────────────────
- @JsonProperty("prompt")
private final String prompt;
- @JsonProperty("sessionId")
private final String sessionId;
- @JsonProperty("runId")
private final String runId;
@JsonProperty("static_plan")
private final Object staticPlan;
// ── Optional fields ──────────────────────────────────────────────────
- @JsonProperty("media")
private final List media;
- @JsonProperty("context")
private final Map context;
- @JsonProperty("idempotencyKey")
private final String idempotencyKey;
- @JsonProperty("credentials")
private final List credentials;
- @JsonProperty("timeoutSeconds")
private final Integer timeoutSeconds;
private AgentRequest(Builder b) {
+ this.name = b.name;
+ this.version = b.version;
this.agentConfig = b.agentConfig;
this.framework = b.framework;
this.rawConfig = b.rawConfig;
+ this.model = b.model;
+ this.skillRef = b.skillRef;
this.prompt = b.prompt;
this.sessionId = b.sessionId;
this.runId = b.runId;
@@ -91,22 +101,31 @@ private AgentRequest(Builder b) {
this.timeoutSeconds = b.timeoutSeconds;
}
+ /** Build a request that starts an already-deployed agent by name and optional version. */
+ public static Builder deployedAgent(String name, Integer version) {
+ return new Builder(name, version, null, null, null);
+ }
+
/** Build a request for a native (non-framework) agent from its serialized definition. */
public static Builder nativeAgent(Object agentConfig) {
- return new Builder(agentConfig, null, null);
+ return new Builder(null, null, agentConfig, null, null);
}
/** Build a request for a framework-backed agent (OpenAI, ADK, Skill) from its serialized definition. */
public static Builder frameworkAgent(String framework, Object rawConfig) {
- return new Builder(null, framework, rawConfig);
+ return new Builder(null, null, null, framework, rawConfig);
}
// ── Builder ──────────────────────────────────────────────────────────
public static final class Builder {
+ private final String name;
+ private final Integer version;
private final Object agentConfig;
private final String framework;
private final Object rawConfig;
+ private String model;
+ private Map skillRef;
private String prompt;
private String sessionId;
private String runId;
@@ -117,12 +136,29 @@ public static final class Builder {
private List credentials;
private Integer timeoutSeconds;
- private Builder(Object agentConfig, String framework, Object rawConfig) {
+ private Builder(
+ String name,
+ Integer version,
+ Object agentConfig,
+ String framework,
+ Object rawConfig) {
+ this.name = name;
+ this.version = version;
this.agentConfig = agentConfig;
this.framework = framework;
this.rawConfig = rawConfig;
}
+ public Builder model(String v) {
+ this.model = v;
+ return this;
+ }
+
+ public Builder skillRef(Map