Conductor agents (embedded runtime)
The AGENT task selects its runtime with an agentType input:
agentType: "a2a"(default) — call a remote Agent2Agent endpoint over HTTP. See A2A integration.agentType: "conductor"— run an agent on the embedded agentspan runtime in-process. This page.
Both values drive the same AGENT task type with one consistent input/output contract; the branch is chosen per task by agentType.
What it is
With agentType: "conductor", the AGENT task runs a registered agent on Conductor's embedded agentspan runtime instead of calling out to a remote agent. Like the A2A branch, it is non-blocking: a fast reply completes immediately; a long-running run moves to IN_PROGRESS and is polled at a cadence (no worker thread is held), so the call survives a server crash, restart, or redeploy and resumes from persisted state.
This branch requires the embedded runtime, enabled with:
On a deployment without it, the runtime bean is absent and any agentType: "conductor" task fails terminally with:
Task input
The conductor branch parses its task input as a ConductorAgentRequest — an AgentStartRequest (the same DTO POST /api/agent/start takes) plus four AGENT-task-only orchestration fields for resuming a run and bounding how long it polls. Fields specific to the A2A branch (agentUrl, streaming, pushNotification, headers, contextId, taskId, parts, message, …) don't exist on this contract and are ignored here.
| Field | Type | Meaning |
|---|---|---|
agentType |
String | Must be "conductor" to select this branch. |
name |
String | Required on a fresh start. Name of a previously deployed agent definition. |
version |
Integer | Optional deployed agent version. The latest version is used when omitted. |
prompt |
String | Required on a fresh start. The single prompt field — no fallback chain. |
sessionId |
String | Optional. Associates the run with an existing conversation/session. |
runId |
String | Per-execution isolation key for stateful agents. When set, every worker tool task is routed to this domain so concurrent instances of the same agent don't cross-talk. |
context |
Map | Extra context values passed to the run. |
media |
List\<String> | Media references attached to the prompt. |
agentConfig |
AgentConfig | Inline agent construction details. Mutually exclusive with identifying the agent by name/version. |
framework |
String | Framework identifier for foreign agents (e.g. "openai", "google_adk"). Null for native agents. |
rawConfig |
Map | Raw framework-specific agent config. Used when framework is non-null. |
skillRef |
Map | Reference to a server-registered skill package. Used with framework="skill" when the caller wants the server to resolve the raw skill config from the skill registry instead of sending it inline. |
timeoutSeconds |
Integer | Per-call timeout override (seconds). Applied server-side to the workflow definition. |
idempotencyKey |
String | Client-supplied idempotency key. If omitted on a fresh start, the conductor branch fills in a deterministic, restart-stable key itself (see Durability). |
static_plan |
Map | Optional deterministic plan for Strategy.PLAN_EXECUTE harnesses — replays a recorded plan instead of running an LLM planner. Note the wire key is static_plan (snake_case), not staticPlan. |
executionId |
String | When set, resume an in-flight run instead of starting a new one (see Human-in-the-loop). |
pollIntervalSeconds |
Integer | Poll cadence while the run is not terminal. Default 5. |
maxDurationSeconds |
Integer | Absolute deadline (seconds) for the run to reach a terminal state. Default 86400 (24h). |
maxPollFailures |
Integer | Consecutive transient poll failures (executor unreachable) tolerated before failing terminally. Default 30. |
Task output
The task writes these keys to its output (ConductorAgentResults):
| Output key | Meaning |
|---|---|
executionId |
Runtime-assigned execution id — carry it into a follow-up AGENT call to resume. |
agentName |
Name of the executed agent. |
sessionId |
Session id the execution belongs to. |
state |
Normalized execution state (uppercase): RUNNING, WAITING, COMPLETED, FAILED, CANCELED. |
waiting |
true when the run paused for external input (human answer / tool result). |
pendingTool |
The pending tool/human request surfaced while waiting. |
text |
Latest / final text emitted by the agent. |
output |
Structured output of a completed run. |
The agent's execution state maps onto the Conductor task status as follows:
state |
Conductor task status | Notes |
|---|---|---|
RUNNING |
IN_PROGRESS |
Keep polling at the evaluation cadence. |
WAITING |
COMPLETED |
Sets waiting=true and surfaces pendingTool / text. Resume with a new AGENT call carrying executionId. |
COMPLETED |
COMPLETED |
Surfaces output + text. |
FAILED |
FAILED |
Sets reasonForIncompletion. |
CANCELED |
CANCELED |
Sets reasonForIncompletion when present. |
Downstream tasks read these with ${agent.output.text}, ${agent.output.executionId}, etc.
Minimal workflow
A single AGENT task that runs an embedded agent to completion:
{
"name": "conductor_agent_basic",
"version": 1,
"schemaVersion": 2,
"tasks": [
{
"name": "run_agent",
"taskReferenceName": "agent",
"type": "AGENT",
"inputParameters": {
"agentType": "conductor",
"name": "${workflow.input.name}",
"prompt": "${workflow.input.prompt}",
"pollIntervalSeconds": 5
}
}
]
}
Register and run it (the embedded runtime must be enabled — agentspan.embedded=true):
# register
curl -X POST 'http://localhost:8080/api/metadata/workflow' \
-H 'Content-Type: application/json' \
-d @conductor_agent_basic.json
# run
curl -X POST 'http://localhost:8080/api/workflow/conductor_agent_basic' \
-H 'Content-Type: application/json' \
-d '{"name":"my-agent","prompt":"Summarize the latest release notes"}'
Human-in-the-loop / resume
When the agent pauses for external input (a human answer or a tool result), it reaches WAITING. The AGENT task then completes with waiting=true and surfaces the pending request in pendingTool (and any text), rather than holding a thread open.
The workflow branches on that state and resumes by issuing a second AGENT task carrying the same executionId — the resume feeds the caller's message back into the waiting execution as the pending tool/human result:
{
"name": "resume_agent",
"taskReferenceName": "resume",
"type": "AGENT",
"inputParameters": {
"agentType": "conductor",
"executionId": "${agent.output.executionId}",
"prompt": "${workflow.input.answer}"
}
}
name is not required on a resume — the executionId identifies the in-flight run. Full example: ai/examples/32-conductor-agent-human-in-loop.json.
Durability
The conductor branch mirrors the A2A branch's guards; the run's state lives in the persisted task output, not a thread.
-
Deterministic idempotency key. A fresh start uses a restart-stable key so a re-issued start (after a retry or restart) is deduped by the runtime:
It is built from retry-stable identity — not
taskId, which changes per retry attempt. - Absolute deadline. Anchored once at start; the task fails terminally aftermaxDurationSeconds(default 86400) if the run never reaches a terminal state. The abandoned child execution is also given a best-effortterminateWorkflowcall so it doesn't keep running orphaned. - Poll-failure cap. Consecutive transient poll failures are counted and reset to 0 on any success; the task fails terminally atmaxPollFailures(default 30), with the same best-effort child termination.
maxDurationSeconds/maxPollFailures are this branch's own liveness guards, independent of the Conductor engine's standard task-level timeout (taskDefinition.timeoutSeconds/responseTimeoutSeconds/timeoutPolicy, set inline on the AGENT WorkflowTask — not as an inputParameters field). Note: as of this writing, the engine does not invoke a system task's cancel() hook when the task's own TaskDef timeout fires (it only does so for still-running sibling tasks once the whole workflow becomes terminal) — a general gap that also affects SUB_WORKFLOW. Until that's addressed at the engine level, maxDurationSeconds is the reliable way to guarantee the child agent execution is cleaned up.
Examples
Runnable workflow definitions live in ai/examples/:
| File | Shows |
|---|---|
31-conductor-agent-basic.json |
Run an embedded agent to completion (poll mode) |
32-conductor-agent-human-in-loop.json |
WAITING → resume with the same executionId |
33-conductor-agent-multi-agent.json |
FORK_JOIN running two independent AGENT (conductor) tasks concurrently |
34-conductor-agent-cancel.json |
Canceling an in-flight run via TERMINATE maps to CANCELED on the AGENT task |