Skip to content

Commit 389cafa

Browse files
Kowserclaude
andcommitted
feat(sdk): AgentClient + agent WorkflowClient live in sdk/clients, exposed via OrkesClients
Moves the agent control-plane client and the agent-flavored workflow client from src/agents/ to src/sdk/clients/agent/ (names kept — nothing on the root surface collides: Conductor's workflow client is WorkflowExecutor). OrkesClients gains getAgentClient() and getAgentWorkflowClient(); AgentClient's constructor accepts an optional pre-built client ((AgentConfigOptions & {client?}) | AgentConfig) which pre-seeds the memoized promise, so the factory path reuses one Conductor client across workflow + schedule surfaces. The /agent/* raw transport and JWT mint path are unchanged; decodeJwtExp stays exported. Cycle guard: the moved files import createConductorClient and the agent domain modules via deep paths, never the sdk barrel; madge reports no new cycles (the AgentClient↔WorkflowClient type-only edge predates the move). The ./agents subpath re-exports everything from the new homes — its surface is unchanged. The enriched ConductorClient type is not re-exported at root (the deprecated bare alias keeps that name). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 7af89ab commit 389cafa

8 files changed

Lines changed: 108 additions & 27 deletions

File tree

src/agents/__tests__/agent-client-auth.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, it, expect, jest, beforeEach, afterEach } from "@jest/globals";
2-
import { AgentClient, decodeJwtExp } from "../agent-client.js";
2+
import { AgentClient, decodeJwtExp } from "../../sdk/clients/agent/AgentClient.js";
33

44
/** Build a minimal JWT with the given `exp` (epoch seconds). */
55
function makeJwt(exp: number): string {

src/agents/index.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -146,10 +146,10 @@ export {
146146
} from "./runtime.js";
147147

148148
// ── Control-plane / Workflow clients ────────────────────
149-
export type { ClientHandle } from "./agent-client.js";
150-
export { AgentClient, decodeJwtExp } from "./agent-client.js";
151-
export type { WorkflowExecution, WorkflowTokenUsage } from "./workflow-client.js";
152-
export { WorkflowClient } from "./workflow-client.js";
149+
export type { ClientHandle, AgentClientOptions } from "../sdk/clients/agent/AgentClient.js";
150+
export { AgentClient, decodeJwtExp } from "../sdk/clients/agent/AgentClient.js";
151+
export type { WorkflowExecution, WorkflowTokenUsage } from "../sdk/clients/agent/WorkflowClient.js";
152+
export { WorkflowClient } from "../sdk/clients/agent/WorkflowClient.js";
153153

154154
// ── Scheduling ──────────────────────────────────────────
155155
export {

src/agents/runtime.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ import type { HandoffContext } from "./handoff.js";
2424
import { detectFramework } from "./frameworks/detect.js";
2525
import type { Schedule } from "../sdk/clients/agent/schedule.js";
2626
import type { SchedulerClient } from "../sdk/clients/scheduler/SchedulerClient.js";
27-
import { AgentClient } from "./agent-client.js";
28-
import { WorkflowClient } from "./workflow-client.js";
27+
import { AgentClient } from "../sdk/clients/agent/AgentClient.js";
28+
import { WorkflowClient } from "../sdk/clients/agent/WorkflowClient.js";
2929
import { serializeFrameworkAgent } from "./frameworks/serializer.js";
3030
import { serializeLangGraph } from "./frameworks/langgraph-serializer.js";
3131
import { serializeLangChain } from "./frameworks/langchain-serializer.js";

src/sdk/OrkesClients.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import type { Client } from "../open-api";
22
import type { OrkesApiConfig } from "./types";
33
import { createConductorClient } from "./createConductorClient";
4+
import { AgentClient } from "./clients/agent/AgentClient";
5+
import type { ConductorClient } from "./clients/agent/AgentClient";
6+
import { WorkflowClient as AgentWorkflowClient } from "./clients/agent/WorkflowClient";
47
import { ApplicationClient } from "./clients/application";
58
import { AuthorizationClient } from "./clients/authorization";
69
import { EventClient } from "./clients/event";
@@ -64,6 +67,25 @@ export class OrkesClients {
6467
return new SchedulerClient(this._client);
6568
}
6669

70+
/**
71+
* Agent control-plane client (`/agent/*`: run/deploy/schedule/status),
72+
* reusing this factory's Conductor client for the Conductor-side calls.
73+
* The client must have been built by `createConductorClient` (always true
74+
* for `OrkesClients.from(...)`).
75+
*/
76+
getAgentClient(): AgentClient {
77+
return new AgentClient({ client: this._client as ConductorClient });
78+
}
79+
80+
/**
81+
* Agent-flavored workflow reads (agent-execution 404 fallback + token
82+
* rollup) — the same instance `getAgentClient().workflows` returns. For
83+
* general workflow operations use `getWorkflowClient()` (WorkflowExecutor).
84+
*/
85+
getAgentWorkflowClient(): AgentWorkflowClient {
86+
return this.getAgentClient().workflows;
87+
}
88+
6789
getSecretClient(): SecretClient {
6890
return new SecretClient(this._client);
6991
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { describe, expect, it } from "@jest/globals";
2+
import { OrkesClients } from "../OrkesClients";
3+
import { AgentClient } from "../clients/agent/AgentClient";
4+
import { WorkflowClient } from "../clients/agent/WorkflowClient";
5+
import { SchedulerClient } from "../clients/scheduler/SchedulerClient";
6+
import type { Client } from "../../open-api";
7+
8+
const fakeClient = { getConfig: () => ({}) } as unknown as Client;
9+
10+
describe("OrkesClients agent getters", () => {
11+
it("getAgentClient returns an AgentClient reusing the factory's client", async () => {
12+
const clients = new OrkesClients(fakeClient);
13+
const agentClient = clients.getAgentClient();
14+
15+
expect(agentClient).toBeInstanceOf(AgentClient);
16+
// The injected client is pre-seeded — getClient() must resolve to the
17+
// exact instance, not build a fresh one via createConductorClient.
18+
await expect(agentClient.getClient()).resolves.toBe(fakeClient);
19+
});
20+
21+
it("getAgentWorkflowClient matches getAgentClient().workflows behavior", () => {
22+
const clients = new OrkesClients(fakeClient);
23+
const viaFactory = clients.getAgentWorkflowClient();
24+
const viaAgentClient = clients.getAgentClient().workflows;
25+
26+
expect(viaFactory).toBeInstanceOf(WorkflowClient);
27+
expect(viaAgentClient).toBeInstanceOf(WorkflowClient);
28+
});
29+
30+
it("agent schedules ride the injected client too", () => {
31+
const clients = new OrkesClients(fakeClient);
32+
expect(clients.getAgentClient().schedules).toBeInstanceOf(SchedulerClient);
33+
});
34+
});
Lines changed: 37 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,22 +20,22 @@
2020
* carry that JWT as `X-Authorization` (see {@link _authHeaders}).
2121
*/
2222

23-
import { createConductorClient } from "../sdk";
24-
import type { AgentResult, AgentStatus, DeploymentInfo, RunOptions } from "./types.js";
25-
import { AgentAPIError } from "./errors.js";
26-
import { AgentConfig } from "./config.js";
27-
import type { AgentConfigOptions } from "./config.js";
28-
import { Agent } from "./agent.js";
29-
import { AgentConfigSerializer } from "./serializer.js";
30-
import { detectFramework } from "./frameworks/detect.js";
31-
import { serializeFrameworkAgent } from "./frameworks/serializer.js";
32-
import { serializeLangGraph } from "./frameworks/langgraph-serializer.js";
33-
import { serializeLangChain } from "./frameworks/langchain-serializer.js";
34-
import { Schedule } from "../sdk/clients/agent/schedule.js";
35-
import { SchedulerClient } from "../sdk/clients/scheduler/SchedulerClient.js";
36-
import { WorkflowClient } from "./workflow-client.js";
37-
import { makeAgentResult, TERMINAL_STATUSES } from "./result.js";
38-
import { AgentStream } from "./stream.js";
23+
import { createConductorClient } from "../../createConductorClient";
24+
import type { AgentResult, AgentStatus, DeploymentInfo, RunOptions } from "../../../agents/types.js";
25+
import { AgentAPIError } from "../../../agents/errors.js";
26+
import { AgentConfig } from "../../../agents/config.js";
27+
import type { AgentConfigOptions } from "../../../agents/config.js";
28+
import { Agent } from "../../../agents/agent.js";
29+
import { AgentConfigSerializer } from "../../../agents/serializer.js";
30+
import { detectFramework } from "../../../agents/frameworks/detect.js";
31+
import { serializeFrameworkAgent } from "../../../agents/frameworks/serializer.js";
32+
import { serializeLangGraph } from "../../../agents/frameworks/langgraph-serializer.js";
33+
import { serializeLangChain } from "../../../agents/frameworks/langchain-serializer.js";
34+
import { Schedule } from "./schedule.js";
35+
import { SchedulerClient } from "../scheduler/SchedulerClient.js";
36+
import { WorkflowClient } from "./WorkflowClient.js";
37+
import { makeAgentResult, TERMINAL_STATUSES } from "../../../agents/result.js";
38+
import { AgentStream } from "../../../agents/stream.js";
3939

4040
/**
4141
* The resource client returned by `createConductorClient`. The package's
@@ -77,6 +77,16 @@ export function decodeJwtExp(token: string): number {
7777
/** Default client-side ceiling for {@link AgentClient.run}/`wait()` when no `timeoutSeconds` is given. */
7878
const DEFAULT_WAIT_MS = 600_000; // 10 min — mirrors the C# SDK's HttpClient cap
7979

80+
/**
81+
* {@link AgentClient} construction options: the agent config plus an optional
82+
* pre-built Conductor client to reuse (as handed out by `OrkesClients`). The
83+
* injected client must originate from `createConductorClient` — it carries
84+
* the attached `*Resource` members the workflow client reads.
85+
*/
86+
export type AgentClientOptions = AgentConfigOptions & {
87+
client?: ConductorClient;
88+
};
89+
8090
export class AgentClient {
8191
readonly config: AgentConfig;
8292

@@ -90,8 +100,16 @@ export class AgentClient {
90100
private _tokenExp = 0; // epoch seconds; 0 == "no decodable expiry" (not cached)
91101
private _mintPromise?: Promise<string>; // single-flight guard for concurrent mints
92102

93-
constructor(options?: AgentConfigOptions | AgentConfig) {
94-
this.config = options instanceof AgentConfig ? options : new AgentConfig(options);
103+
constructor(options?: AgentClientOptions | AgentConfig) {
104+
if (options instanceof AgentConfig) {
105+
this.config = options;
106+
} else {
107+
const { client, ...configOptions } = options ?? {};
108+
this.config = new AgentConfig(configOptions);
109+
// Pre-seed the memoized promise; getClient() then reuses the injected
110+
// client instead of building its own via createConductorClient.
111+
if (client) this._clientPromise = Promise.resolve(client);
112+
}
95113
this.serializer = new AgentConfigSerializer();
96114
}
97115

@@ -362,7 +380,7 @@ export class AgentClient {
362380
if (opts?.credentials) payload.credentials = opts.credentials;
363381
if (opts?.context) payload.context = opts.context;
364382
if (opts?.plan !== undefined) {
365-
const { coercePlan } = await import("./plans.js");
383+
const { coercePlan } = await import("../../../agents/plans.js");
366384
payload.static_plan = coercePlan(opts.plan as Parameters<typeof coercePlan>[0]);
367385
}
368386

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
* shared Conductor client rather than ad-hoc HTTP on the runtime.
99
*/
1010

11-
import type { ConductorClient } from "./agent-client.js";
11+
import type { ConductorClient } from "./AgentClient.js";
1212

1313
/** Conductor workflow shape (subset we read). */
1414
export interface WorkflowExecution {

src/sdk/clients/agent/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
export { AgentClient, decodeJwtExp } from "./AgentClient";
2+
// NOTE: the enriched `ConductorClient` type stays on the module (import it
3+
// from "./AgentClient") — re-exporting it here would collide with the
4+
// deprecated bare `ConductorClient = Client` alias on the root barrel.
5+
export type { AgentClientOptions, ClientHandle } from "./AgentClient";
6+
export { WorkflowClient } from "./WorkflowClient";
7+
export type { WorkflowExecution, WorkflowTokenUsage } from "./WorkflowClient";
18
export {
29
Schedule,
310
ScheduleError,

0 commit comments

Comments
 (0)