Files
Viren Baraiya cde7b8e934 feat: Durable A2A (Agent2Agent) protocol — client, server, observability (#1195)
* feat(a2a): durable A2A protocol — client, server, observability

Add Agent2Agent (A2A) protocol support to the AI module, in both directions.

Client: CALL_AGENT / GET_AGENT_CARD / CANCEL_AGENT_TASK system tasks call remote
A2A agents over JSON-RPC with poll, streaming (SSE), and push modes. Durable by
design — deterministic messageId, state in the execution (not a thread), liveness
guards, and a push backstop. SSRF-guarded (IPv6 ULA + cloud-metadata; redirects off).

Server: expose any workflow as an A2A agent (one agent per workflow), idempotent
message/send -> startWorkflow (RETURN_EXISTING), tasks/get / tasks/cancel, and
multi-turn resume (a follow-up message/send completes the paused HUMAN/WAIT task
instead of starting a duplicate).

Observability: Micrometer counters via the shared Monitors registry and MDC
correlation keys across the A2A code paths.

Gated by conductor.integrations.ai.enabled (client) and conductor.a2a.server.enabled
(server).

* test(a2a): unit, real-agent interop, and real-engine durability tests

- Unit/wire tests (MockWebServer), embedded-agent e2e, push callback, server
  JSON-RPC dispatch + multi-turn resume, mapper/worker, and observability.
- A2ASdkInteropTest: drives the client against the official a2a-sdk reference
  agent launched as a subprocess (discovery, send, poll, streaming, message-mode);
  self-skips when no Python with a2a-sdk is available.
- A2ADurableEngineEndToEndTest (test-harness): CALL_AGENT through the real decider
  + AsyncSystemTaskExecutor + Redis, proving crash/restart resume from persistence.

* docs(a2a): integration guide, examples, UI task types, design notes

- docs/devguide/ai/a2a-integration.md (AI Cookbook nav) with worked examples:
  call/expose, multi-turn resume request/response, and push end-to-end.
- ai/examples: call / get-card / server + streaming / push / multi-turn / cancel,
  indexed in the examples README; runnable interop + durable demos under test
  resources.
- ui-next: register CALL_AGENT / GET_AGENT_CARD / CANCEL_AGENT_TASK task types.
- design/a2a: protocol study, durability proposal, and server design notes.

Note: a CI step to install a2a-sdk (so A2ASdkInteropTest runs in the build job)
is left as a follow-up — it needs a token with the `workflow` scope to land.

* refactor(a2a): move inbound server auth to enterprise (OSS open by default)

The A2A server's optional shared-secret api-key (conductor.a2a.server.api-key)
is removed from OSS: the server is now open by default, matching OSS Conductor
REST. Inbound authentication (API keys, OAuth/OIDC, mTLS, per-skill scopes,
signed Agent Cards) belongs to the enterprise build; front OSS with a
gateway/firewall.

Unchanged and kept in OSS as safe-by-default guards: client SSRF protection,
push-callback token auth, cross-agent execution isolation, and client→remote
per-call auth headers.

Removes authorized()/apiKey + the two api-key tests; docs and design notes
updated.

* docs(a2a): error-handling/troubleshooting, multi-agent + client multi-turn + LLM-pick examples

Docs: add an error-handling & retries section (FAILED vs FAILED_WITH_TERMINAL_ERROR
mapping for HTTP/JSON-RPC/SSRF/liveness) + a troubleshooting table; flesh out the
client multi-turn section with a worked SWITCH-on-input-required snippet; add an
"orchestrating multiple agents" subsection.

Examples (validated by ExampleWorkflowValidationTest):
- 27-a2a-multi-agent: FORK_JOIN calling agents in parallel → JOIN.
- 28-a2a-llm-pick-skill: GET_AGENT_CARD → LLM_CHAT_COMPLETE → CALL_AGENT.
- 29-a2a-client-multi-turn: SWITCH on input-required, re-call with same context/taskId.

* refactor(a2a)!: rename CALL_AGENT→AGENT, CANCEL_AGENT_TASK→CANCEL_AGENT + add agentType

Generalize the agent task types ahead of multi-runtime support:
- CALL_AGENT → AGENT (class CallAgentTask → AgentTask), CANCEL_AGENT_TASK →
  CANCEL_AGENT. GET_AGENT_CARD kept (discovery/"Agent Card" is A2A-specific).
- New input field `agentType` (default "a2a") on all three tasks — the extension
  point for native runtimes (langgraph, openai, …). Unknown values are rejected
  with a clear error; only "a2a" is implemented today.

Updated across enum, handlers, request models, mapper, callback, tests,
test-harness, examples, docs, design notes, and the ui-next task registration.

BREAKING CHANGE: workflows using type "CALL_AGENT"/"CANCEL_AGENT_TASK" must switch
to "AGENT"/"CANCEL_AGENT".

* docs(a2a): clean up post-rename residue (grammar, example names, phrasing)

Fix 'A AGENT'→'An AGENT' grammar (A2AMetrics, design), reword example descriptions and a doc sentence left awkward by the mechanical rename, and update legacy example workflow names (a2a_call_agent_* → a2a_agent_*).

* feat(a2a): server-side message/stream (SSE)

Exposed workflow-agents now support A2A message/stream. The endpoint returns an
SSE stream driven off a dedicated daemon pool: the initial Task, status-update
events as the workflow's A2A state changes, artifact-update events as output is
produced, and a final status-update at a terminal/input-required state (or when
the stream window elapses). The agent advertises capabilities.streaming=true.

A web-agnostic A2AStreamSink keeps SseEmitter out of A2AWorkflowAgent so the
stream driver stays unit-testable. Config: conductor.a2a.server.stream-poll-
interval-millis (500), stream-max-duration-seconds (300).

Tests: a mocked unit test asserting the task → artifact-update → final
status-update sequence, and a real-HTTP loopback (our client message/stream
against the server's SSE) aggregating to a completed task with artifacts.

Docs + design notes updated (streaming moves from follow-up to shipped).

* Update A2ADurableEngineEndToEndTest.java

* Update A2ADurableEngineEndToEndTest.java

* refactor(a2a): simplify client/server task code (no behavior change)

- AgentTask: replace hand-rolled isBlank/firstNonBlank/stripTrailingSlash/
  asInt/asLong helpers with commons-lang3 StringUtils + Number-only reads;
  reuse parseRequest().getPollIntervalSeconds() for pollInterval; dedup
  stateOf via A2AResults; fix the stale {@link #isAsyncComplete} javadoc to
  describe the real backstop-poll mechanism
- A2ACallbackResource: drop the deprecated query-param token path (untested,
  no legacy clients on a feature branch); StringUtils for isBlank
- A2AServerResource/A2AWorkflowAgent: delete the duplicate basePath()/URL
  building (delegate to the agent's agentUrl/new agentCardUrl); drop the
  no-op `id == null ? null : id` ternaries
- remove dead fields A2AMessage.referenceTaskIds and PushNotificationConfig.id

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(a2a): add mermaid use-case diagrams (client & server directions)

Diagrams rendering both A2A directions — Conductor as client (workflow calls
remote agents via the AGENT task) and as server (a workflow exposed as an A2A
agent):

- docs/devguide/ai/a2a-integration.md: both-directions overview flowchart,
  AGENT lifecycle sequence (poll/push/stream), FORK_JOIN→JOIN multi-agent
  fan-out, and the server message/send→workflow→tasks/get sequence
- design/a2a/08: overview flowchart + a sequence per direction (A/B)
- design/a2a/10: server sequence emphasizing the streaming and multi-turn
  resume paths

Rendered via pymdownx.superfences (mkdocs) and natively on GitHub.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(ai)!: rename package ai.models -> ai.model (singular)

Align the AI module with conductor-oss package conventions: every model
package in the codebase (com.netflix.conductor.common.model, core.model,
grpc/proto/model, ai.a2a.model) is singular `model` — `ai.models` was the
only plural `models` package in the entire repo, and sat one letter away
from the singular ai.a2a.model, which read as an inconsistency.

Pure mechanical rename: git mv of the package dir (history preserved) plus
package-decl/import/FQN updates across the module (main + test) and one doc
reference in ai/CONTRIBUTING.md. No type or behavior changes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix /a2a endpoint not being reachable

* style: spotlessApply

* fix and upgrade version

* refactor for agentspan

* feat: add SQLite (sqlite-vec) vector database backend

Add an embedded, zero-infrastructure VectorDB backend implemented with the
sqlite-vec extension, alongside the existing pgvector/MongoDB/Pinecone backends.

- SqliteVectorDB stores embeddings in a vec0 virtual table and runs KNN via the
  sqlite-vec MATCH operator (l2/cosine/l1).
- The native vec0 binaries (linux/macos/windows) are downloaded + checksum-verified
  at build time (downloadSqliteVec) and bundled as jar resources; SqliteVecExtensions
  extracts the right one at runtime.
- SqliteVectorDBAutoConfiguration registers a "default" instance automatically when
  conductor.db.type=sqlite and conductor.integrations.ai.enabled=true, so the
  all-in-one SQLite server gets a working vector store with no external dependency.
- Adds unit tests, a real end-to-end round-trip test, a RAG example
  (30-rag-sqlite-vec.json), and documentation.

Note: a dedicated Linux e2e CI job (.github/workflows/ci.yml) is intentionally not
included here because the push token lacks the GitHub `workflow` scope; it must be
committed separately with a workflow-scoped token.

* test: enable workflow execution lock in integration profile to fix flaky specs

The integration-test profile disabled conductor.app.workflow-execution-lock-enabled
(since 2021), so acquireLock() was a no-op and the background WorkflowSweeper could
decide a workflow concurrently with specs' manual sweep()/asyncSystemTaskExecutor
calls. Two simultaneous decides could schedule duplicate tasks, intermittently
failing exact task-count assertions (FailureWorkflowSpec, and
HierarchicalForkJoinSubworkflowRetrySpec).

Enable the lock (matching production defaults; local_only lock is already configured)
so decides are serialized. Full :conductor-test-harness:test passes (252 tests).

* fix(build): wire downloadSqliteVec as a resource source so all consumers depend on it

The generated sqlite-vec resource directory is consumed by sourcesJar (and other
resource consumers), but only processResources depended on downloadSqliteVec. Gradle's
task-dependency validation (run as part of `build`) failed sourcesJar with an implicit
dependency error. Register the task provider itself as the resource srcDir so every
consumer depends on it automatically.

* fix(ai): harden sqlite-vec backend after review

Follow-up fixes from a deep review of the SQLite/sqlite-vec backend:

- SqliteVectorDB.upsertEmbeddings: restore autoCommit(true) in a finally block
  so connections are never returned to the Hikari pool in manual-commit mode.
- SqliteVectorDB.getSqliteClient: validate the extension path against an
  allowlist before interpolating it into load_extension(...).
- SqliteVectorDBAutoConfiguration.resolveDbPath: strip JDBC URL query params
  (e.g. ?busy_timeout=15000&journal_mode=WAL) so the derived *_vectordb.db path
  is a valid filename.
- SqliteVecExtensions: re-extract the bundled binary if the cached temp file was
  deleted between calls (stale-cache guard).
- VectorDBProvider: warn when an auto-registered default is skipped because an
  explicit instance with the same name already exists.

Adds SqliteVectorDBAutoConfigurationTest (resolveDbPath cases incl. query
params), SqliteVecExtensionsTest (platform detection), and a rollback/
autocommit-restore test in SqliteVectorDBTest.

* feat(ui-next): config forms for all AI / agentic task types

Wire up the workflow-editor task form dispatcher, Add-Task menu, icons, task
generators and JSON schemas for every AI task type exposed by the `ai/` module,
so each gets a real config form instead of the generic JSON editor.

- New bespoke forms: AGENT, GET_AGENT_CARD, CANCEL_AGENT, LIST_MCP_TOOLS,
  CALL_MCP_TOOL, GENERATE_IMAGE, GENERATE_AUDIO, GENERATE_VIDEO, GENERATE_PDF,
  LLM_SEARCH_EMBEDDINGS.
- Wire the existing-but-unwired LLM forms (chat/text complete, embeddings,
  index text/document, search index, get document) into TaskFormContent.
- LLM_CHAT_COMPLETE: OSS plain "Instructions" textarea (enterprise prompt-name
  picker remains pluggable via the plugin registry).
- A2A forms: agentType as a fixed A2A/Conductor radio; multi-row headers via the
  shared HTTP headers editor; pushNotification is a boolean toggle.
- CALL_MCP_TOOL arguments accept inline JSON or a ${variable} reference.
- Media-gen + search-embeddings reuse the field-driven LLMFormFields for
  provider/model/vectorDB/embedding selection (server-backed dropdowns).
- Full field coverage vs the backend input models; numeric fields use coerceTo
  so the workflow JSON stores real numbers while still accepting ${variables}.
- A2A tasks added to the Agentic Orchestration quick-add section.

* fix(ui-next): address PR review on AI task forms

- Schemas.ts: add the new AI/agentic task types to genericSchema's type enum so
  the code (JSON) editor no longer warns "type is not one of ..." for AGENT,
  GENERATE_*, *_MCP_*, LLM_SEARCH_EMBEDDINGS, CHUNK_TEXT, LIST_FILES, PARSE_DOCUMENT.
- AgentTaskForm: fix spacing so the "Push backstop poll" field's floating label
  no longer collides with the push-notification toggle (group switches, separate
  the input row).
- LLMSearchEmbeddingsTaskForm: add spacing so the helper text no longer collides
  with the Query field's floating label.
- QuickAddMenu: fill the two empty Agentic Orchestration slots (Call MCP Tool,
  Generate Image) for a clean two-row grid.

* fix(ui-next): snug node height for AI/agentic task cards

AI task cards render header-only content (no custom body), but fell through to
the DEFAULT node height (100px), leaving empty space below the label on the
canvas. Size the AI/agentic header-only task family to 80px (matching the
header-only FORK_JOIN node) so the cards fit their content.

---------

Co-authored-by: Nicholas Cole <68611647+NicholasDCole@users.noreply.github.com>
Co-authored-by: nicholascole <nicholas.colesd@gmail.com>
2026-06-29 23:41:58 -07:00

199 lines
7.9 KiB
YAML

site_name: Durable Execution for workflows and agents
site_description: Conductor is an open-source durable code execution engine and agentic workflow engine. Orchestrate distributed workflows with saga pattern compensation, at-least-once task delivery, and polyglot workers in Java, Python, Go, and more.
repo_url: https://github.com/conductor-oss/conductor
site_url: https://conductor-oss.github.io/conductor
edit_uri: ''
strict: false
use_directory_urls: false
nav:
- Home: index.md
- Quickstart:
- quickstart/index.md
- Concepts: devguide/concepts/index.md
- Workflows: devguide/concepts/workflows.md
- Tasks: devguide/concepts/tasks.md
- Workers: devguide/concepts/workers.md
- Durable Execution: architecture/durable-execution.md
- JSON + Code Native: architecture/json-native.md
- Task Lifecycle: devguide/architecture/tasklifecycle.md
- Guides:
- Build with AI Agents: devguide/how-tos/conductor-skills.md
- devguide/concepts/conductor.md
- Workflows:
- devguide/how-tos/Workflows/creating-workflows.md
- devguide/how-tos/Workflows/starting-workflows.md
- devguide/how-tos/Workflows/handling-errors.md
- devguide/how-tos/Workflows/debugging-workflows.md
- devguide/how-tos/Workflows/versioning-workflows.md
- devguide/how-tos/Workflows/searching-workflows.md
- devguide/how-tos/Workflows/viewing-workflow-executions.md
- devguide/how-tos/Workflows/scheduling-workflows.md
- Tasks:
- devguide/how-tos/Tasks/creating-tasks.md
- devguide/how-tos/Tasks/task-inputs.md
- devguide/how-tos/Tasks/choosing-tasks.md
- devguide/how-tos/Workers/scaling-workers.md
- Event Bus Orchestration: devguide/how-tos/event-bus.md
- Best Practices: devguide/bestpractices.md
- FAQ: devguide/faq.md
- Cookbook:
- devguide/cookbook/index.md
- Microservice Orchestration: devguide/cookbook/microservice-orchestration.md
- Dynamic Parallelism: devguide/cookbook/dynamic-parallelism.md
- Wait & Timer Patterns: devguide/cookbook/wait-and-timers.md
- Task Timeouts & Retries: devguide/cookbook/task-timeouts-and-retries.md
- Event-Driven Recipes: devguide/cookbook/event-driven.md
- AI & LLM Recipes: devguide/cookbook/ai-llm.md
- Scheduled Workflows: devguide/cookbook/workflow-scheduling.md
- Dynamic Workflows in Code: devguide/cookbook/dynamic-workflows.md
- AI Cookbook:
- devguide/ai/index.md
- Build Your First AI Agent: devguide/ai/first-ai-agent.md
- AI & LLM Recipes: devguide/cookbook/ai-llm.md
- LLM Orchestration: devguide/ai/llm-orchestration.md
- MCP Integration: devguide/ai/mcp-guide.md
- A2A Integration: devguide/ai/a2a-integration.md
- Production Agent Architecture: devguide/ai/production-agent-architecture.md
- Failure Semantics: devguide/ai/failure-semantics.md
- Why Conductor for Agents: devguide/ai/why-conductor.md
- Durable Agents: devguide/ai/durable-agents.md
- Human-in-the-Loop: devguide/ai/human-in-the-loop.md
- Dynamic Workflows: devguide/ai/dynamic-workflows.md
- Token Efficiency: devguide/ai/token-efficiency.md
- SDKs:
- documentation/clientsdks/index.md
- Java: documentation/clientsdks/java-sdk.md
- Python: documentation/clientsdks/python-sdk.md
- Go: documentation/clientsdks/go-sdk.md
- JavaScript: documentation/clientsdks/js-sdk.md
- C#: documentation/clientsdks/csharp-sdk.md
- Ruby: documentation/clientsdks/ruby-sdk.md
- Rust: documentation/clientsdks/rust-sdk.md
- Reference:
- API:
- documentation/api/index.md
- documentation/api/metadata.md
- documentation/api/startworkflow.md
- documentation/api/workflow.md
- documentation/api/task.md
- documentation/api/files.md
- documentation/api/bulk.md
- documentation/api/eventhandlers.md
- documentation/api/taskdomains.md
- documentation/api/scheduler.md
- Workflow Definition:
- documentation/configuration/workflowdef/index.md
- System Tasks:
- documentation/configuration/workflowdef/systemtasks/index.md
- documentation/configuration/workflowdef/systemtasks/http-task.md
- documentation/configuration/workflowdef/systemtasks/inline-task.md
- documentation/configuration/workflowdef/systemtasks/event-task.md
- documentation/configuration/workflowdef/systemtasks/human-task.md
- documentation/configuration/workflowdef/systemtasks/json-jq-transform-task.md
- documentation/configuration/workflowdef/systemtasks/kafka-publish-task.md
- documentation/configuration/workflowdef/systemtasks/noop-task.md
- documentation/configuration/workflowdef/systemtasks/jdbc-task.md
- documentation/configuration/workflowdef/systemtasks/wait-task.md
- Operators:
- documentation/configuration/workflowdef/operators/index.md
- documentation/configuration/workflowdef/operators/fork-task.md
- documentation/configuration/workflowdef/operators/join-task.md
- documentation/configuration/workflowdef/operators/switch-task.md
- documentation/configuration/workflowdef/operators/do-while-task.md
- documentation/configuration/workflowdef/operators/dynamic-task.md
- documentation/configuration/workflowdef/operators/dynamic-fork-task.md
- documentation/configuration/workflowdef/operators/sub-workflow-task.md
- documentation/configuration/workflowdef/operators/start-workflow-task.md
- documentation/configuration/workflowdef/operators/set-variable-task.md
- documentation/configuration/workflowdef/operators/terminate-task.md
- Task Definition: documentation/configuration/taskdef.md
- Event Handlers: documentation/configuration/eventhandlers.md
- Configuration: documentation/configuration/appconf.md
- Metrics:
- Server Metrics: documentation/metrics/server.md
- Client Metrics: documentation/metrics/client.md
- Deploy:
- Docker: devguide/running/deploy.md
- From Source: devguide/running/source.md
- Hosted: devguide/running/hosted.md
- devguide/architecture/index.md
- Advanced:
- documentation/advanced/extend.md
- documentation/advanced/isolationgroups.md
- documentation/advanced/archival-of-workflows.md
- documentation/advanced/externalpayloadstorage.md
- documentation/advanced/file-storage.md
- documentation/advanced/redis.md
- documentation/advanced/postgresql.md
- documentation/advanced/opensearch.md
theme:
name: material
logo: img/logo.svg
custom_dir: docs/overrides
palette:
- scheme: default
primary: custom
toggle:
icon: material/brightness-7
name: Switch to dark mode
- scheme: slate
primary: custom
toggle:
icon: material/brightness-4
name: Switch to light mode
font: false
features:
- navigation.tabs
- navigation.tabs.sticky
- navigation.indexes
- navigation.footer
- navigation.sections
- navigation.expand
- navigation.top
- search.highlight
- search.suggest
- content.code.copy
- content.code.annotate
- content.tabs.link
- toc.follow
extra:
generator: false
social:
- icon: fontawesome/brands/github
link: https://github.com/conductor-oss/conductor
- icon: fontawesome/brands/slack
link: https://join.slack.com/t/orkes-conductor/shared_invite/zt-3dpcskdyd-W895bJDm8psAV7viYG3jFA
- icon: fontawesome/brands/youtube
link: https://www.youtube.com/@orkesio
extra_css:
- css/custom.css
plugins:
- search
- macros
markdown_extensions:
- admonition
- codehilite
- attr_list
- md_in_html
- tables
- pymdownx.highlight:
anchor_linenums: true
- pymdownx.inlinehilite
- pymdownx.snippets
- pymdownx.superfences:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_code_format
- pymdownx.tabbed:
alternate_style: true
- pymdownx.details
copyright: Conductor authors