Repository navigation
Add Conductor-Agents to Rust SDK - #10
Merged
Merged
Conversation
Guardrail, TerminationCondition, SwarmTransition, CallbackHandler, ConversationMemory, and Credentials, ported from python-sdk's conductor.ai.agents. Wired into agents::mod and the crate's top-level re-exports; not yet consumed by AgentDef/AgentConfigSerializer (Wave 2). Also fixes a clippy::drain_collect warning in TaskHandler::stop, and adds the CredentialNotFound error variant plus the exhaustive-match arm it required in events/exception.rs. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Guardrail: full field + builder + AgentConfigSerializer wire support, including a new GuardrailCheck::guardrail_type_fields() method so each concrete guardrail type owns its own wire discriminant/fields. TerminationCondition: field + with_termination() builder + tests on AgentDef; serializer wiring still outstanding. Also includes incidental cargo-fmt reformatting of callback.rs, credentials.rs, memory.rs, and termination.rs picked up from a misdirected worktree run (no logic changes). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Completes Agents parity Wave 2: every composition type added in Wave 1 (Guardrail, TerminationCondition, SwarmTransition, CallbackHandler, ConversationMemory) is now wired into AgentDef and AgentConfigSerializer, and all three previously-deferred Strategy variants are unlocked: - Router: requires a router sub-agent (with_router), serializes recursively. - Swarm: requires swarm_transitions (with_swarm_transition), serializes under the "handoffs" wire key for cross-SDK compatibility; OnCondition's unserializable closure mirrors python's taskName-reference convention. - PlanExecute: requires a planner (with_planner), with optional fallback/ fallback_max_turns/planner_context/synthesize. - CallbackHandler: registered via with_callback but deliberately not serialized (no AgentRuntime yet to give it a real task-name reference). - ConversationMemory: wired with_memory + full message/tool-call serialization matching python's wire shape. requires_deferred_composition() is removed since nothing is deferred anymore; with_strategy() now validates Router/PlanExecute's required composition field directly. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…810e-97fd167641bc:baseline] Conductor-Original-Branch: CCOR-13389-propp Conductor-Original-Head: e6e6947
…-13389-propp-a99bc3b9
…bb74-292f08938b1f:baseline] Conductor-Original-Branch: CCOR-13389-propp Conductor-Original-Head: 7cecbcf
AgentHandle::stream() and AgentStream::new() were built by separate Wave 4 subtasks (agent-handle, agent-stream-transport) against different assumptions: stream() called AgentStream::new(client, execution_id) synchronously, but AgentStream::new takes a raw reqwest::Response. Make stream() async and have it open the SSE response via AgentClient::stream() first. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Wave 4 (runtime): agent-runtime-types, agent-runtime-core, agent-handle, agent-stream-transport. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
TerminationCondition doctest imported it via the private agents::termination module (E0603); the module is private, only re-exported as agents::TerminationCondition. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…b30e-3386f825439d:baseline] Conductor-Original-Branch: CCOR-13389-propp Conductor-Original-Head: cd5da7d
The def-output-type and serializer-output-type code_parallel subtasks built the OutputType wiring against different assumed builder shapes: def.rs's with_output_type(class_name, schema) two-arg builder vs. a test in serializer.rs calling it with a single OutputType value (only caught by cargo test, not cargo build, since #[cfg(test)] code isn't compiled by build). Fix the test call site and check off the output_type line in development-waves.md, which the same run left unchecked despite implementing it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…b91c-9595c7444afd:baseline] Conductor-Original-Branch: CCOR-13389-propp Conductor-Original-Head: ae59c59
…heck Verifies every registered worker's polling task actually reached and completed a first real poll within a timeout, catching an early panic during setup or a spawned task that never reaches the network call -- the rust-native analog of python's fork()-failure check, since tokio::spawn has no equivalent silent-failure mode to guard against. Built on a new TaskRunner::poll_attempt_count() counter plus each worker's JoinHandle::is_finished(). Not agent-specific (lives on the core TaskHandler) and not auto-wired into AgentRuntime::serve/resume (new task_handler() accessor lets callers opt in explicitly). python's own LocalLivenessCheck turned out to be unreachable dead code -- never called from runtime.py/worker_manager.py, no config field, no test -- so this is a from-scratch rust-native design for the same underlying problem, not a port of working python behavior. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
OpenAI's Assistants API (client.beta.assistants.*/threads.*, what GPTAssistantAgent and its _AssistantCall wrap) was sunset on 2026-08-26 with no extension -- every endpoint it calls now errors unconditionally. Porting it would ship a feature that never works. No architectural gap either way: it was always just a native Agent with one custom tool, not a framework-adapter case needing new machinery. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Comment on lines
+1
to
+11
| # Debugging Incidents | ||
|
|
||
| Start with safe evidence: workflow ID, task reference name, status, retry count, and | ||
| `reason_for_incompletion`. Confirm server reachability and authentication before changing | ||
| application code. | ||
|
|
||
| | Symptom | First check | | ||
| |---|---| | ||
| | Connection error | `CONDUCTOR_SERVER_URL` includes `/api` and the server is healthy (`curl $CONDUCTOR_SERVER_URL/../health`). | | ||
| | Task remains `SCHEDULED` | A worker is polling the exact task type (and `domain`, if set). | | ||
| | Authentication failure | `CONDUCTOR_AUTH_KEY`/`CONDUCTOR_AUTH_SECRET` target the active server. | |
Contributor
There was a problem hiding this comment.
Let's remove all of the .md files.
We should have at-most 1 .md file explaining how to use agents
Contributor
|
@propp-orkes , can you bring in github action against common action to prove that playback is working in CI? Should be able to reference feature branch conductor-oss/conductor#1614 |
dfont-orkes
reviewed
Sep 18, 2026
dfont-orkes
left a comment
There was a problem hiding this comment.
this isn't a full review of the PR. if you're able to can you help organize some of the longer docstring comments into design docs where it makes sense?
Merge the 7 design docs (README, python-sdk-reference, rust-sdk-design, parity-plan, examples, framework-support, secrets-and-credentials) and the top-level Agent_Parity_checklist.txt into one implementation-focused README, now that the feature has shipped and the old planning docs' python/java-sdk comparisons are no longer needed. Update every doc cross-reference in source comments and the other top-level docs to point at the merged file.
Cut every doc comment (///, //!) that was explaining design rationale, porting history, or cross-referencing python-sdk/java-sdk/docs/agents/ files, since the feature has already shipped and none of that context is useful for someone reading the code today. Kept a short description per item plus any genuinely load-bearing usage note (fail-closed behavior, panics, valid ranges, thread-safety), and left every doctest code example untouched.
…dings Runs every sdk_playback_* example against a Conductor server built from conductor-oss/conductor#1614 (LLM recording/playback support, not yet merged — pinned to a commit) in mock-LLM mode, then verifies each shared recording was actually replayed via that PR's check-playback action. Non-blocking for now: two of the shared recordings fail LLM-request matching because the mcp-testkit test-tool server's tool definitions have drifted since they were captured, a known upstream gap rather than an SDK regression.
…ocally The prior comment blamed mcp-testkit tool-definition drift for 04_http_and_mcp_tools and one 16e_credentials_http_tool recording. Re-running the full suite with the Python version actually pinned in this job (3.12, not the local default 3.14) shows 04_http_and_mcp_tools fully passes — the earlier failure was this session's own test-setup mistake, not a real gap. The one genuine, permanent gap is 16e_credentials_http_tool's 2nd recording, unplayable because GitHub scopes its response ETag to the requesting identity/token.
cargo clippy --all-features -D warnings was failing on two doc comments missing backticks around ZeroMQ/UUIDv4 (jupyter_executor.rs, semantic_memory.rs) — both were in module-level //! headers on private modules, so fixed by demoting them (see below) rather than just adding backticks. Every submodule under src/agents/ is a private `mod`, reachable only through `pub use` re-exports in mod.rs; only the crate's top-level `pub mod agents` is genuinely public. A //! or /// on anything not actually reachable from outside the crate was never doing real public-API-documentation work, so this converts those to plain // comments: every private module's //! header, and every /// on a private/non-reexported item, private method, or test-only helper. Kept /// wherever it's on a genuinely re-exported item (or a pub member of one), and always preserved `# Errors`/`# Panics` sections on anything staying public, since this crate runs clippy::pedantic at -D warnings (missing_errors_doc/missing_panics_doc/doc_markdown). Left every existing doctest untouched, including guardrail.rs's module-doc example. Verified: cargo clippy --lib and --tests --examples --all-features -D warnings clean, cargo fmt --check clean, cargo doc --document-private-items with RUSTDOCFLAGS=-D warnings clean (no broken intra-doc links from the demoted comments), 625 lib tests and 9 doctests pass (same doctest count as before).
python-sdk was the source of truth while this crate's implementation was being built; now that it's at parity, most of those references were just leftover porting-process narrative with no ongoing value (exact python file/class/method citations, "matching python-sdk's X" asides, "ported from python's Y" framing) rather than something a reader of this crate today actually needs. Removed those across agent_client.rs, ai_orchestrator.rs, service_registry_client.rs, worker_config.rs, error.rs, ai_integration.rs, workflow_def.rs, task_handler.rs, task_runner.rs, and examples/support/mod.rs, keeping the substance of each comment (the actual behavior/rationale) intact. Also fixed one doc comment in agent_client.rs that had gone stale independently, claiming AgentStatus/AgentResult didn't exist yet. Left references alone where they're still doing real work: naming- compatibility aliases (ConductorClient/OrkesClients, the *_client() aliases on ConductorClient) genuinely exist for Python SDK familiarity, not just history; multi-SDK parity statements (Java/Go/Python together, e.g. metrics bucket definitions) describe an ongoing cross-SDK contract, not one-off porting notes; and the sdk_playback_* examples' comments about matching python's exact behavior are load-bearing, since those examples replay LLM recordings captured from real python-sdk runs. Left root-level comparison docs (SDK_COMPARISON.md, WORKER_COMPARISON.md, EXAMPLES_COMPARISON.md, DESIGN.md) and Cargo.toml's dependency-choice comments untouched -- out of scope for a source-comment cleanup pass.
Same cleanup as the rest of the python-sdk reference pass: these get_*_client() aliases are still worth documenting as aliases, but citing Python SDK naming as the reason doesn't add anything a reader needs today.
…hrasing Continuation of the python-sdk reference cleanup. Kept the substance of each comment (metrics stay cross-SDK-consistent by design; TaskContext still gets the same one-line description) without naming specific other-language SDKs as the reason.
dfont-orkes
reviewed
Sep 23, 2026
NicholasDCole
force-pushed
the
CCOR-13389-propp
branch
from
September 23, 2026 21:49
161bea3 to
a7d0686
Compare
NicholasDCole
marked this pull request as ready for review
September 23, 2026 22:42
NicholasDCole
force-pushed
the
CCOR-13389-propp
branch
from
September 23, 2026 22:42
da79473 to
34b6576
Compare
dfont-orkes
reviewed
Sep 23, 2026
dfont-orkes
reviewed
Sep 23, 2026
dfont-orkes
reviewed
Sep 23, 2026
dfont-orkes
reviewed
Sep 23, 2026
v1r3n
approved these changes
Sep 24, 2026
- Task: redact runtime_metadata values in Debug (may hold secrets, same as Credentials::Debug already does), and replace the long internal-design field doc with a short one pointing at Credentials::from_task. - task_runner.rs: replace the hand-rolled AbortOnDrop with tokio_util::task::AbortOnDropHandle, which already does the same thing; adds tokio-util as a direct dependency. - schedule.rs: reconcile() now saves every desired schedule before deleting pruned ones, so a transient error partway through doesn't risk deleting everything while creating nothing (save_schedule is an upsert, so this ordering is strictly safer). - schedule.rs: documented the wire_name collision risk dfont-orkes flagged (non-blocking, matches every other Conductor SDK's wire format, not fixable on the Rust side alone). - Fixed 3 dangling docs/agents/README.md references left over from that file's removal in a prior commit.
… SDKs Splits the single docs/agents/README.md into a python-sdk/java-sdk-style tree: a short README index, getting-started.md, concepts/ (one file per topic: agents, tools, guardrails, termination, multi-agent, callbacks, stateful, deploy-serve-run, streaming-hitl, scheduling), and reference/ (agent-definition, client, runtime). Content is re-derived from the current src/agents/ source rather than carried over from the old single-file version, which had drifted (e.g. it still documented a plan() method on AgentRuntime that no longer exists). Scoped to what this SDK actually ships today -- no frameworks/ directory, since no framework adapters exist here, and no references to other SDKs in the text itself.
Reconciles the closed-and-remerged PR's HTTP-client rework (PUT-with- GET-fallback for schedule pause/resume, get_text/put_no_body, the GrantedAccessResponse envelope fix, and the queue-config HashMap shape fix) with this branch's agents work. Keeps both sides' additions where they don't overlap (e.g. get_stream stays for agent streaming), takes main's stricter/fixed test suite for authorization_client_tests.rs and orkes_client_tests.rs, and re-adds `# Errors` doc sections main's history lacks so this branch's pedantic clippy gate still passes. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Brings the Rust SDK's Agents feature (
agentsCargo feature) to comprehensive parity withpython-sdk's
conductor.ai.agents, plus a handful of core (non-agents) improvements found andfixed along the way. Delivered in 8 waves, tracked in detail in
docs/agents/development-waves.md— that file is theauthoritative, itemized record of what shipped, what was deliberately left out (and why), and
what's still open; this description summarizes it.
What's included
Core agent composition & runtime (Waves 1–4)
Guardrail/RegexGuardrail/LlmGuardrail,TerminationCondition,SwarmTransition,CallbackHandler,ConversationMemory,Credentials.AgentDef: guardrails, termination, router/swarm/plan-execute strategies,callbacks, memory, structured
output_type.TaskDef.runtime_metadataatregistration,
#[tool]macro support for a&Credentialsparameter, per-tool/per-agentcredential scoping.
AgentRuntime(compile/deploy/start/run/serve/resume),AgentHandle(
join/stream/approve/reject/respond),AgentEvent/AgentStream(SSE),AgentStatus/AgentResult.Framework adapters (Wave 5)
FrameworkAgenttrait + fallible conversion toAgentDef.async-openaiadapter (OpenAI Agents SDK tool shape).stream-jsontransport.GraphAgentDef— a typed, Rust-native graph-declaration API for the LangGraph-shaped need(not a literal LangGraph port; see "Decisions" below for why).
Worker infrastructure (Wave 6, benefits every
TaskHandleruser, not just agents)lease_extend_enabled/lease_extend_threshold) so long-runningtasks don't get reassigned mid-execution.
TaskHandler::verify_workers_started— confirms every registered worker actually reached itsfirst real poll within a timeout, catching an early panic or a starved task before it becomes
a silent "nobody's listening" stall.
Docs (Wave 7) — 14 docs checked against actual capability (not rewritten from assumption);
two turned out to be real code gaps and were implemented rather than just flagged: Workflow
Message Queue (
WorkflowClient::send_message,PULL_WORKFLOW_MESSAGEStask support) and aTestWorkflowRequest/TaskMockwire-format fix (verified against the server's actual Java DTO).Parity gaps found in a full-source review, all resolved (Wave 8)
AgentHandle::join(
ConductorError::WorkerStall,StallPolicy::Warn/Raise) plus the local startup checkabove.
AgentRuntime::resume— reattach to an existing execution after a process restart.mock_run/ScriptedEvent+expect(result)assertions).TaskTypevariants (GenerateImage/GenerateAudio/LlmSearchEmbeddings) withpython-matching defaults.
ServiceRegistryClient— full HTTP/gRPC service registry + circuit-breaker management (14methods, verified against python's generated REST client).
AgentRuntime::deploy_with_schedules— upsert/prune an agent's cron schedules in the samedeploy()call (found and fixed two real wire-format bugs in the schedule models along theway).
AiOrchestrator— typed convenience layer for registering LLM/vector-DB integrations(
add_ai_integration/add_vector_store/test_prompt_template/get_token_used)."opus"→"claude-opus-4-6", etc.).AgentEventfixed to match the real server SSE contract. This was the most consequentialfinding: 2 of the previously-shipped 5 event variants (
Message/Progress) matched no realserver event at all, and 2 more (
Waiting/Error) had the wrong field shape entirely.Re-derived from the actual server source (
AgentSSEEvent.java/AgentEventListener.java), notpython — the corrected 12-variant set now covers 3 event kinds python's own SDK doesn't even
know about (
ContextCondensed/SubagentStart/SubagentStop).Also enabled
clippy::pedantic+clippy::restrictionacross the crate, fixing orconsciously triaging every finding (many turns of work, not part of the agents effort per se,
but needed to keep CI green while iterating).
Parity confidence
Yes, with specific, itemized exceptions — no blanket "100% parity" claim:
— the dominant Rust LLM crate (
rig/rig-agent) can't be adapted viaFrameworkAgent(itstool discovery is async/prompt-dependent, no plain model-string getter); worth revisiting as a
passthrough adapter if a concrete user asks.
GPTAssistantAgent: not ported — the OpenAIAssistants API it wraps was fully sunset by OpenAI on 2026-08-26, so a port would ship a
feature that fails on every call.
semantic assertions, per-strategy structural validators, and the LLM-backed correctness eval
runner are not ported (each needs infrastructure — an LLM client, a recording format — this
crate doesn't have yet).
AgentEventhas no forward-compatible catch-all variant — a future 13th server event kindwould error out
AgentStream::nextrather than degrade gracefully. Pre-existing property ofthis design, documented, not fixed in this PR.
worker_isolation.py,_worker_entries.py, the pre-harmonization metrics collector) are confirmed N/A — rust'sworker model has no equivalent problem to solve.
Full itemized list, including every "why not" with its reasoning on the record, is in
docs/agents/development-waves.md.Verification
scenario from
conductor-oss/conductorPR #1614's shared cross-SDKllm-recordings/(93recordings across 19 scenarios) and replayed each end-to-end against a real server. 92/93 play
back correctly — the one gap is an externally-unfixable recording artifact (a GitHub response
ETag scoped to the original recording session), not a bug here. Found and fixed 5 real bugs
this way that unit tests alone hadn't caught.
(
ServiceRegistryClient,AiOrchestrator, agent liveness/stall detection, schedulereconciliation, worker-startup verification).
clippy::pedantic/clippy::restrictionclean,cargo fmtclean, rustdoc clean(
-D warnings), license headers clean, MSRV (1.85) clean.shared remote dev server (
sdkdev.orkesconductor.io) — confirmed transient/environmentalacross multiple pushes (different test fails each time, none touching files this PR changed),
not addressable from this repo.