Skip to content

Commit 0a95a6f

Browse files
Rename Agent Orchestrations to Agent Factories
Renames the authoring surface from "orchestration" to "factory" per the updated product name "agent factories": - defineOrchestration -> defineFactory; OrchestrationContext/Definition/Handle/ Meta/Limits -> Factory*; SessionOrchestrationApi -> SessionFactoryApi (session.factory); OrchestrationRunError -> FactoryRunError. - joinSession({ orchestrations }) -> joinSession({ factories }). - orchestration.ts -> factory.ts (and its test); generated/rpc.ts regenerated from the renamed runtime contract (orchestration.* -> factory.*). The context primitives (step, agent, parallel, pipeline, phase, log, run) keep their names. Companion runtime rename lands in github/copilot-agent-runtime#12953.
1 parent 6ad6604 commit 0a95a6f

9 files changed

Lines changed: 1003 additions & 1003 deletions

File tree

nodejs/src/client.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ import type {
8585
TypedSessionLifecycleHandler,
8686
} from "./types.js";
8787
import { defaultJoinSessionPermissionHandler } from "./types.js";
88-
import type { OrchestrationHandle } from "./orchestration.js";
88+
import type { FactoryHandle } from "./factory.js";
8989

9090
/**
9191
* Minimum protocol version this SDK can communicate with.
@@ -1671,15 +1671,15 @@ export class CopilotClient {
16711671
async resumeSessionForExtension(
16721672
sessionId: string,
16731673
config: ResumeSessionConfig,
1674-
orchestrations?: OrchestrationHandle[]
1674+
factories?: FactoryHandle[]
16751675
): Promise<CopilotSession> {
1676-
return this.resumeSessionInternal(sessionId, config, orchestrations);
1676+
return this.resumeSessionInternal(sessionId, config, factories);
16771677
}
16781678

16791679
private async resumeSessionInternal(
16801680
sessionId: string,
16811681
config: ResumeSessionConfig,
1682-
orchestrations?: OrchestrationHandle[]
1682+
factories?: FactoryHandle[]
16831683
): Promise<CopilotSession> {
16841684
if (!this.connection) {
16851685
await this.start();
@@ -1697,7 +1697,7 @@ export class CopilotClient {
16971697
session.registerTools(config.tools);
16981698
session.registerCanvases(config.canvases);
16991699
session.registerCommands(config.commands);
1700-
session.registerOrchestrations(orchestrations);
1700+
session.registerFactories(factories);
17011701
const {
17021702
wireProvider: bearerWireProvider,
17031703
wireProviders: bearerWireProviders,
@@ -1769,7 +1769,7 @@ export class CopilotClient {
17691769
})),
17701770
toolSearch: config.toolSearch,
17711771
canvases: config.canvases?.map((canvas) => canvas.declaration),
1772-
orchestrations: orchestrations?.map((orchestration) => orchestration.meta),
1772+
factories: factories?.map((factory) => factory.meta),
17731773
requestCanvasRenderer: config.requestCanvasRenderer,
17741774
requestExtensions: config.requestExtensions,
17751775
extensionSdkPath: config.extensionSdkPath,

nodejs/src/extension.ts

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
type PermissionHandler,
1010
type ResumeSessionConfig,
1111
} from "./types.js";
12-
import type { OrchestrationHandle } from "./orchestration.js";
12+
import type { FactoryHandle } from "./factory.js";
1313

1414
export {
1515
Canvas,
@@ -28,28 +28,28 @@ export type JoinSessionConfig = Omit<
2828
> & {
2929
onPermissionRequest?: PermissionHandler;
3030
/**
31-
* Orchestration handles to register when the extension joins the session.
31+
* Factory handles to register when the extension joins the session.
3232
*
33-
* @experimental Part of the experimental Agent Orchestrations surface and may
33+
* @experimental Part of the experimental Agent Factories surface and may
3434
* change or be removed in future SDK or CLI releases.
3535
*/
36-
orchestrations?: OrchestrationHandle[];
36+
factories?: FactoryHandle[];
3737
};
3838

39-
export type { ExtensionInfo, OrchestrationLimits, OrchestrationMeta } from "./types.js";
39+
export type { ExtensionInfo, FactoryLimits, FactoryMeta } from "./types.js";
4040
export {
41-
defineOrchestration,
42-
OrchestrationRunError,
41+
defineFactory,
42+
FactoryRunError,
4343
type RunOptions,
44-
type SessionOrchestrationApi,
45-
type OrchestrationAgentOptions,
46-
type OrchestrationContext,
47-
type OrchestrationDefinition,
48-
type OrchestrationHandle,
49-
type OrchestrationJsonSchema,
50-
type OrchestrationPipelineStage,
51-
type OrchestrationStepOptions,
52-
} from "./orchestration.js";
44+
type SessionFactoryApi,
45+
type FactoryAgentOptions,
46+
type FactoryContext,
47+
type FactoryDefinition,
48+
type FactoryHandle,
49+
type FactoryJsonSchema,
50+
type FactoryPipelineStage,
51+
type FactoryStepOptions,
52+
} from "./factory.js";
5353

5454
/**
5555
* Joins the current foreground session.
@@ -80,7 +80,7 @@ export async function joinSession(config: JoinSessionConfig = {}): Promise<Copil
8080
// already been forked by the host with the SDK the host chose.
8181
const {
8282
extensionSdkPath: _stripped,
83-
orchestrations,
83+
factories,
8484
...rest
8585
} = config as JoinSessionConfig & {
8686
extensionSdkPath?: string;
@@ -94,6 +94,6 @@ export async function joinSession(config: JoinSessionConfig = {}): Promise<Copil
9494
onPermissionRequest: config.onPermissionRequest ?? defaultJoinSessionPermissionHandler,
9595
suppressResumeEvent: config.suppressResumeEvent ?? true,
9696
},
97-
orchestrations
97+
factories
9898
);
9999
}

nodejs/src/factory.ts

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
*--------------------------------------------------------------------------------------------*/
4+
5+
import type { FactoryRunResult } from "./generated/rpc.js";
6+
import type { CopilotSession } from "./session.js";
7+
import type { FactoryMeta } from "./types.js";
8+
9+
declare const factoryHandleBrand: unique symbol;
10+
11+
/**
12+
* Conservative JSON shape language accepted for structured factory agent output.
13+
*
14+
* This is a best-effort structural guard used to decide whether a subagent's
15+
* structured output should be accepted or retried — **not** a full JSON Schema
16+
* validator. Only these keywords are honored: `type`, `required`, `enum`,
17+
* `const`, recursive `properties`/`items`, and `anyOf`/`oneOf`/`allOf`.
18+
*
19+
* Everything else is **ignored, not enforced**. In particular, string
20+
* constraints (`pattern`, `minLength`, `maxLength`, `format`), numeric ranges
21+
* (`minimum`, `maximum`), `additionalProperties`, and boolean (`true`/`false`)
22+
* schemas do not reject non-conforming output. `oneOf` is treated like `anyOf`
23+
* (at least one branch must match) rather than strict exactly-one. Author
24+
* schemas within this subset; do not rely on unsupported constraints for
25+
* correctness.
26+
*
27+
* @experimental Part of the experimental Agent Factories surface and may
28+
* change or be removed in future SDK or CLI releases.
29+
*/
30+
export type FactoryJsonSchema = Record<string, unknown>;
31+
32+
/**
33+
* Options for one factory-scoped subagent call.
34+
*
35+
* @experimental Part of the experimental Agent Factories surface and may
36+
* change or be removed in future SDK or CLI releases.
37+
*/
38+
export interface FactoryAgentOptions {
39+
label?: string;
40+
schema?: FactoryJsonSchema;
41+
model?: string;
42+
}
43+
44+
/**
45+
* Options for a durable factory step.
46+
*
47+
* @experimental Part of the experimental Agent Factories surface and may
48+
* change or be removed in future SDK or CLI releases.
49+
*/
50+
export interface FactoryStepOptions {
51+
/** Skip the journal and always invoke the producer. */
52+
volatile?: boolean;
53+
}
54+
55+
/**
56+
* One stage in a per-item factory pipeline.
57+
*
58+
* @experimental Part of the experimental Agent Factories surface and may
59+
* change or be removed in future SDK or CLI releases.
60+
*/
61+
export type FactoryPipelineStage<TInput = unknown, TResult = unknown> = (
62+
previous: TInput,
63+
item: unknown,
64+
index: number
65+
) => Promise<TResult> | TResult;
66+
67+
/**
68+
* Context passed to an extension-authored factory body.
69+
*
70+
* @experimental Part of the experimental Agent Factories surface and may
71+
* change or be removed in future SDK or CLI releases.
72+
*/
73+
export interface FactoryContext<TArgs = unknown> {
74+
/** Spawn and await one factory-scoped subagent. */
75+
agent(prompt: string, options?: FactoryAgentOptions): Promise<unknown>;
76+
/** Memoize an arbitrary producer under a stable author-supplied key. */
77+
step<TResult>(
78+
key: string,
79+
producer: () => Promise<TResult> | TResult,
80+
options?: FactoryStepOptions
81+
): Promise<TResult>;
82+
/** Run thunks concurrently, returning null for a thunk that throws. */
83+
parallel<TResult>(
84+
thunks: Array<() => Promise<TResult> | TResult>
85+
): Promise<Array<TResult | null>>;
86+
/** Run each item through every stage without barriers between stages. */
87+
pipeline(items: unknown[], ...stages: FactoryPipelineStage[]): Promise<unknown[]>;
88+
/** Start a named factory progress phase. */
89+
phase(title: string): void;
90+
/** Emit a factory progress line. */
91+
log(message: string): void;
92+
/** Reject because nested factories are not supported. */
93+
factory(name: string, args?: unknown): Promise<unknown>;
94+
/** Caller-supplied input, forwarded verbatim. */
95+
args: TArgs;
96+
/** The same full session instance returned by `joinSession`. */
97+
session: CopilotSession;
98+
/** Cooperative cancellation signal for the current factory run. */
99+
signal: AbortSignal;
100+
}
101+
102+
/**
103+
* Definition accepted by {@link defineFactory}.
104+
*
105+
* @experimental Part of the experimental Agent Factories surface and may
106+
* change or be removed in future SDK or CLI releases.
107+
*/
108+
export interface FactoryDefinition<TArgs = unknown, TResult = unknown> {
109+
meta: FactoryMeta;
110+
run(context: FactoryContext<TArgs>): Promise<TResult>;
111+
}
112+
113+
/**
114+
* Opaque reusable reference to a defined factory.
115+
*
116+
* @experimental Part of the experimental Agent Factories surface and may
117+
* change or be removed in future SDK or CLI releases.
118+
*/
119+
export interface FactoryHandle<TArgs = unknown, TResult = unknown> {
120+
readonly meta: FactoryMeta;
121+
readonly [factoryHandleBrand]: {
122+
readonly args: TArgs;
123+
readonly result: TResult;
124+
};
125+
}
126+
127+
/**
128+
* Options for invoking a factory.
129+
*
130+
* @experimental Part of the experimental Agent Factories surface and may
131+
* change or be removed in future SDK or CLI releases.
132+
*/
133+
export interface RunOptions<TArgs = unknown> {
134+
/** Input surfaced as `context.args`. */
135+
args?: TArgs;
136+
/** Return once the approved run starts instead of awaiting completion. */
137+
background?: boolean;
138+
/** Prior run whose journal and progress should seed this run. */
139+
resumeFromRunId?: string;
140+
}
141+
142+
/**
143+
* Friendly factory API exposed on a session.
144+
*
145+
* @experimental Part of the experimental Agent Factories surface and may
146+
* change or be removed in future SDK or CLI releases.
147+
*/
148+
export interface SessionFactoryApi {
149+
run(name: string, options: RunOptions & { background: true }): Promise<FactoryRunResult>;
150+
run<TResult = unknown>(
151+
name: string,
152+
options?: RunOptions & { background?: false }
153+
): Promise<TResult>;
154+
run<TResult = unknown>(
155+
name: string,
156+
options?: RunOptions
157+
): Promise<TResult | FactoryRunResult>;
158+
run<TArgs, TResult>(
159+
factory: FactoryHandle<TArgs, TResult>,
160+
options: RunOptions<TArgs> & { background: true }
161+
): Promise<FactoryRunResult>;
162+
run<TArgs, TResult>(
163+
factory: FactoryHandle<TArgs, TResult>,
164+
options?: RunOptions<TArgs> & { background?: false }
165+
): Promise<TResult>;
166+
run<TArgs, TResult>(
167+
factory: FactoryHandle<TArgs, TResult>,
168+
options?: RunOptions<TArgs>
169+
): Promise<TResult | FactoryRunResult>;
170+
/** Read the latest durable envelope for a factory run. */
171+
getRun(runId: string): Promise<FactoryRunResult>;
172+
/** Cancel a factory run and return its terminal envelope. */
173+
cancel(runId: string): Promise<FactoryRunResult>;
174+
}
175+
176+
/**
177+
* Error thrown when a foreground factory run does not complete successfully.
178+
*
179+
* @experimental Part of the experimental Agent Factories surface and may
180+
* change or be removed in future SDK or CLI releases.
181+
*/
182+
export class FactoryRunError extends Error {
183+
constructor(public readonly envelope: FactoryRunResult) {
184+
super(
185+
envelope.error ??
186+
envelope.reason ??
187+
`Factory run "${envelope.runId}" ended with status "${envelope.status}"`
188+
);
189+
this.name = "FactoryRunError";
190+
}
191+
}
192+
193+
interface StoredFactory {
194+
meta: FactoryMeta;
195+
run(context: FactoryContext<unknown>): Promise<unknown>;
196+
}
197+
198+
const factoryHandles = new WeakMap<object, StoredFactory>();
199+
200+
function validateLimits(meta: FactoryMeta): void {
201+
const limits = meta.limits;
202+
if (!limits) {
203+
return;
204+
}
205+
206+
for (const field of ["maxConcurrentSubagents", "maxTotalSubagents"] as const) {
207+
const value = limits[field];
208+
if (value !== undefined && (!Number.isInteger(value) || value <= 0)) {
209+
throw new Error(`Factory limit "${field}" must be a positive integer`);
210+
}
211+
}
212+
213+
if (limits.timeout !== undefined && (!Number.isFinite(limits.timeout) || limits.timeout <= 0)) {
214+
throw new Error('Factory limit "timeout" must be a positive number of milliseconds');
215+
}
216+
}
217+
218+
/**
219+
* Defines an extension-authored factory and returns an opaque registration handle.
220+
*
221+
* @experimental Part of the experimental Agent Factories surface and may
222+
* change or be removed in future SDK or CLI releases.
223+
*/
224+
export function defineFactory<TArgs = unknown, TResult = unknown>(
225+
definition: FactoryDefinition<TArgs, TResult>
226+
): FactoryHandle<TArgs, TResult> {
227+
validateLimits(definition.meta);
228+
229+
const stored: StoredFactory = {
230+
meta: definition.meta,
231+
run: definition.run as StoredFactory["run"],
232+
};
233+
const handle = Object.freeze({ meta: definition.meta }) as FactoryHandle<TArgs, TResult>;
234+
235+
factoryHandles.set(handle, stored);
236+
return handle;
237+
}
238+
239+
/** @internal */
240+
export function getFactoryDefinition(handle: FactoryHandle): StoredFactory {
241+
const definition = factoryHandles.get(handle);
242+
if (!definition) {
243+
throw new Error("Invalid factory handle");
244+
}
245+
return definition;
246+
}

0 commit comments

Comments
 (0)