Skip to content

feat: consolidate agent control-plane onto AgentClient; fix worker credential delivery#421

Open
v1r3n wants to merge 10 commits into
mainfrom
feat/agent-client-worker-credentials
Open

feat: consolidate agent control-plane onto AgentClient; fix worker credential delivery#421
v1r3n wants to merge 10 commits into
mainfrom
feat/agent-client-worker-credentials

Conversation

@v1r3n

@v1r3n v1r3n commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Summary

Consolidates the agent runtime's /agent/* access onto the standard client stack and fixes worker credential resolution to the server's runtimeMetadata contract (conductor-oss PR #1255). Also removes the now-unused server auto-start and fixes a CLI-tool spawn-safety bug.

Verified end-to-end against a live agentspan server; unit + e2e suites pass.

AgentClient — interface + Orkes impl

  • New conductor.client.agent_client.AgentClient (ABC) + conductor.client.orkes.orkes_agent_client.OrkesAgentClient, following the WorkflowClient/OrkesWorkflowClient pattern. Built on the shared ApiClient (sync) / AsyncApiClient (async), so JWT minting, X-Authorization, TTL refresh and 401-retry are reused, not re-implemented. SSE reuses the ApiClient token via a new public get_authentication_headers().
  • OrkesClients.get_agent_client() returns it.
  • Deleted the duplicate async transport (client/ai/agent_api_client.py) and the DX wrapper (ai/agents/runtime/http_client.py). AgentRuntime now uses the one client for all /agent/* calls (start/deploy/compile/status/execution/respond/stop/signal/SSE), sync + async.

AgentRuntime + Configuration

  • AgentRuntime(configuration: Configuration = None, *, settings: AgentConfig = None) — server config comes solely from the standard Configuration (same object ApiClient uses); AgentConfig carries runtime behaviour only. Removed the custom to_conductor_configuration() bridge and the private log-level poke.
  • Configuration host resolution falls back CONDUCTOR_SERVER_URLAGENTSPAN_SERVER_URL.

Worker credentials via runtimeMetadata

Fixes the "Required credentials not found … No execution token available" failure. The server resolves a worker's declared TaskDef.runtimeMetadata secret names at poll time and delivers values on the wire-only Task.runtimeMetadata; the SDK was still on the retired execution-token / POST /workers/secrets path and was wiping TaskDef.runtimeMetadata on registration.

  • Add Task.runtime_metadata and TaskDef.runtime_metadata model fields.
  • Stamp TaskDef.runtimeMetadata from declared credentials at registration (native @tool, framework, and passthrough workers) so overwrite-registration no longer clobbers the server-compiled value.
  • Worker dispatch + the langchain/langgraph/claude_agent_sdk wrappers read secrets off Task.runtimeMetadata. Retired WorkerCredentialFetcher, _extract_execution_token, _get_credential_fetcher; resolve_credentials(task, names) now reads runtimeMetadata.

Remove server auto-start

  • Deleted runtime/server.py and the auto_start_server / AGENTSPAN_AUTO_START_SERVER config surface. The runtime no longer probes or launches a local server.

CLI tools spawn-safety

  • cli_config._make_cli_tool now backs run_command with a module-level _CliCommandRunner instance instead of a <locals> closure, so the registration spawn-safety probe passes (cli_allowed_commands agents register + run).

Tests / docs / examples

  • New unit tests: test_orkes_agent_client, task/taskdef runtimeMetadata serde, runtime_metadata registration, CLI spawn-safety regression.
  • New e2e test_suite26_worker_credentials (credential injection via runtimeMetadata, TaskDef.runtimeMetadata stamping, env isolation, CLI spawn-safety) — stores secrets via REST /api/secrets (no CLI dependency).
  • Updated credential/dispatch/runtime/sse/signals unit tests to the new contracts; rewrote e2e suite24 for the transport-only client + runtime DX.
  • Docs (api-reference, advanced, getting-started) and examples updated (GH_TOKEN, Configuration-based AgentRuntime).

Verification

  • Full tests/unit + tests/backwardcompatibility + tests/serdesertest: green.
  • e2e against a live server: suite26 (4), suite24 (5), suite1 (9) pass. (The CLI-credential suites suite2/suite3 are blocked by an unrelated local CLI↔server API skew — /api/credentials vs /api/secrets — not by these changes.)

Notes

  • runtime.client is now the transport AgentClient; the old control-plane DX (runtime.client.run/start/deploy/schedule) was removed — use the runtime's own run/start/deploy/schedules_client().

…edential delivery

Refactors the agent runtime's server access onto the standard client stack and
fixes worker credential resolution to the server's runtimeMetadata contract.

AgentClient (interface + Orkes impl)
- Add `conductor.client.agent_client.AgentClient` (ABC) + `OrkesAgentClient`
  following the WorkflowClient/OrkesWorkflowClient pattern, built on the shared
  ApiClient (sync) / AsyncApiClient (async) so JWT mint / X-Authorization / TTL
  refresh / 401-retry are reused, not re-implemented. SSE reuses the ApiClient
  token via a new public `get_authentication_headers()`.
- `OrkesClients.get_agent_client()` returns the new client.
- Delete the duplicate transport (`client/ai/agent_api_client.py`) and the DX
  wrapper (`ai/agents/runtime/http_client.py`); AgentRuntime uses the one client
  for all /agent/* calls (start/deploy/compile/status/execution/respond/stop/
  signal/SSE), sync and async.

AgentRuntime / Configuration
- `AgentRuntime(configuration: Configuration = None, *, settings: AgentConfig = None)`:
  server config comes solely from the standard Configuration; AgentConfig carries
  runtime behaviour only. Remove the custom `to_conductor_configuration()` bridge
  and the private log-level poke.
- `Configuration` host resolution falls back CONDUCTOR_SERVER_URL -> AGENTSPAN_SERVER_URL.

Worker credentials via runtimeMetadata (conductor-oss PR #1255 contract)
- Add `Task.runtime_metadata` and `TaskDef.runtime_metadata` model fields.
- Stamp `TaskDef.runtimeMetadata` from a tool/agent's declared credentials at
  registration so the SDK's overwrite-registration no longer wipes the value the
  server compiles.
- Worker dispatch (+ langchain/langgraph/claude_agent_sdk wrappers) reads the
  host-resolved secrets off `Task.runtimeMetadata` instead of the retired
  execution-token / POST /workers/secrets path. Retire WorkerCredentialFetcher,
  `_extract_execution_token`, `_get_credential_fetcher`; `resolve_credentials(task, names)`
  now reads runtimeMetadata.

Remove server auto-start
- Delete `runtime/server.py` and the `auto_start_server` / AGENTSPAN_AUTO_START_SERVER
  config surface; runtime no longer probes or launches a local server.

CLI tools spawn-safety
- `cli_config._make_cli_tool` builds the `run_command` worker from a module-level
  `_CliCommandRunner` instance (not a <locals> closure), so registration's
  spawn-safety probe passes.

Tests / docs / examples
- New: orkes_agent_client, task/taskdef runtimeMetadata serde, runtime_metadata
  registration, e2e suite26 (worker credentials + CLI spawn-safety) unit/e2e tests.
- Update credential/dispatch/runtime/sse/signals tests to the new contracts;
  rewrite e2e suite24 for the transport-only client + runtime DX.
- Docs + examples updated (GH_TOKEN, Configuration-based AgentRuntime).
@codecov

codecov Bot commented Jul 11, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 74.07407% with 147 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/conductor/client/orkes/orkes_agent_client.py 56.79% 105 Missing ⚠️
...conductor/ai/agents/frameworks/claude_agent_sdk.py 26.08% 17 Missing ⚠️
src/conductor/ai/agents/runtime/runtime.py 71.92% 16 Missing ⚠️
src/conductor/ai/agents/ext.py 95.91% 2 Missing ⚠️
src/conductor/ai/agents/frameworks/langgraph.py 33.33% 2 Missing ⚠️
src/conductor/client/http/models/task_def.py 85.71% 2 Missing ⚠️
src/conductor/ai/agents/frameworks/langchain.py 66.66% 1 Missing ⚠️
src/conductor/ai/agents/run_settings.py 96.87% 1 Missing ⚠️
src/conductor/ai/agents/runtime/tool_registry.py 0.00% 1 Missing ⚠️
Files with missing lines Coverage Δ
src/conductor/ai/agents/_internal/agent_http.py 100.00% <100.00%> (ø)
src/conductor/ai/agents/_internal/token_utils.py 100.00% <ø> (+6.52%) ⬆️
src/conductor/ai/agents/cli_config.py 88.09% <100.00%> (+1.08%) ⬆️
src/conductor/ai/agents/run.py 89.28% <100.00%> (+0.53%) ⬆️
src/conductor/ai/agents/runtime/_dispatch.py 86.97% <100.00%> (+1.99%) ⬆️
src/conductor/ai/agents/runtime/config.py 100.00% <100.00%> (+1.66%) ⬆️
src/conductor/client/agent_client.py 100.00% <100.00%> (ø)
...rc/conductor/client/configuration/configuration.py 97.80% <100.00%> (+1.21%) ⬆️
src/conductor/client/http/api_client.py 96.87% <ø> (ø)
src/conductor/client/http/models/task.py 98.97% <100.00%> (+0.02%) ⬆️
... and 10 more

... and 3 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

v1r3n added 9 commits July 11, 2026 15:11
CI sets CONDUCTOR_AUTH_KEY/SECRET (a service account). The new
Configuration-based client correctly reads them, which broke two tests that
assumed an anonymous environment:

- test_runtime: assert settings.auth_key is *ignored* (rt._auth_key != "ignored")
  instead of asserting anonymity — env-independent.
- test_sse_client TestStreamSSEAuth: autouse fixture clears ambient auth env so
  "no auth configured -> no X-Authorization" holds regardless of CI env.

Verified: full tests/unit + serdesertest + backwardcompatibility on py3.12 with
CONDUCTOR_AUTH_* set -> 3549 passed.
…credential tests

- suite23: build the respond URL via runtime.client._agent_url() (the removed
  runtime._agent_api_url helper).
- suite26: function-scoped runtime fixture so each credential test re-registers
  the worker TaskDef (with overwrite), making TaskDef.runtimeMetadata reflect
  the test's own code path instead of a value left by a prior test in the module.
  Verified via fail-first mutation: removing the registration stamping now fails
  both credential tests together (previously the module-scoped runtime masked it).
…support

Released agentspan servers (through v0.4.2) do not implement the
TaskDef.runtimeMetadata credential-delivery contract (conductor-oss PR #1255) —
they drop the field — so worker credential injection cannot work there, and the
credential-delivery e2e tests would fail for a server-version reason unrelated to
the SDK.

Add a server-capability probe (does a registered TaskDef.runtimeMetadata persist?)
and a `requires_runtime_metadata` fixture that skips the credential-delivery tests
when unsupported. Applied to suite26 TestWorkerCredentials, suite2
test_credential_lifecycle, suite3 test_cli_credential_lifecycle. The CLI
spawn-safety test is left unguarded (server-independent).

Verified both ways: against the dev server (supports it) the tests RUN and pass;
against v0.4.2 (does not) they SKIP with a TODO to bump AGENTSPAN_VERSION once a
release ships runtimeMetadata.
…vel on Configuration

Connection/auth (server_url, api_key, auth_key, auth_secret) and the dead llm_retry_count / secret_strict_mode fields are removed from AgentConfig — connection and auth belong to the conductor Configuration. log_level moves onto Configuration (settable param + CONDUCTOR_LOG_LEVEL/AGENTSPAN_LOG_LEVEL env) so it is SDK-wide; the runtime reads it from there. run.configure() now takes a Configuration for connection. AgentConfig keeps worker + liveness settings only. Unit tests updated.
…e parallel token cache

Framework workers (claude_agent_sdk, langchain, langgraph) posted to Agentspan /agent/* endpoints with raw requests + a standalone token mint/cache in token_utils, a second token authority parallel to ApiClient. New _internal/agent_http.py reconstructs a cached ApiClient per (server_url, auth_key) inside the spawned worker and posts via ApiClient.call_api, reusing the SDK's token management (mint/cache/TTL-refresh/401-retry). _task_client folds into the same cache so /tasks and /agent/* share one token. token_utils is reduced to decode_jwt_exp. Adds test_agent_http.py (in-process server; mint-once-and-cache guard); test_token_utils trimmed to the decoder.
…RLs; add run-all harness

GPTAssistantAgent's assistant tool becomes a module-level picklable callable (spawn-safe). Examples: add if-__main__ guards to stateful/looping workers (75/76/82/83/84), hoist <locals> tools to module level (92, 16g) and make 82 spawn-safe (env-shared IPC + client rebuild), use settings.llm_model in 58, read server URL from env in the schedule examples, add missing 'import sys' (72/73). Adds run_all_examples.py harness and gitignores its generated report + coding-agent output.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant