Skip to content

Add Agent Orchestrations Authoring Surface#2009

Open
MRayermannMSFT wants to merge 13 commits into
mainfrom
dev/mrayermannmsft/other/workflowsproto-1
Open

Add Agent Orchestrations Authoring Surface#2009
MRayermannMSFT wants to merge 13 commits into
mainfrom
dev/mrayermannmsft/other/workflowsproto-1

Conversation

@MRayermannMSFT

@MRayermannMSFT MRayermannMSFT commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

What

Adds the SDK authoring surface for Agent Orchestrations. Extensions can define a orchestration with defineOrchestration({ meta, run }), register it by passing orchestration handles to joinSession({ orchestrations }), and run it from a session through session.orchestration.run(...), with getRun and cancel for run management. The orchestration body receives a context that exposes the durable orchestration primitives (agent, step, parallel, pipeline, phase, log) alongside the caller-supplied args and the full session. The entire hand-authored surface is annotated @experimental so consumers know it may change.

Why

The runtime exposes Agent Orchestrations over a JSON-RPC contract, but extension authors need an ergonomic, typed TypeScript surface to define and run orchestrations rather than hand-writing wire calls. This adds that friendly layer and the orchestration context primitives that let a multi-agent orchestration read as ordinary async code. Marking the surface experimental sets clear expectations while the feature stabilizes alongside the runtime.

Companion PR: github/copilot-agent-runtime#12953

Regenerate SDK wire types (nodejs/src/generated/rpc.ts) from the updated runtime schema for the new workflow.* methods and DTOs, and mirror WorkflowMeta/WorkflowLimits in the SDK public types (nodejs/src/types.ts) with exports via index.ts/extension.ts. No behavior wired (contract skeleton only).

Verification: cd nodejs && npm run build (Node 22) exit 0. Deviations: none.
Add the SDK-side Dynamic Workflows authoring surface: defineWorkflow (opaque frozen WorkflowHandle, module-local name->{meta,run} map, optional limits that reject non-positive values), workflows?: WorkflowHandle[] on the extension JoinSessionConfig only (serializing just WorkflowMeta on the wire, never the run closure), the workflow.execute reverse handler (dispatch-by-name with a stub ctx of args+log-noop+return, structured error on unknown name) and workflow.abort handler (per-run AbortController keyed by runId), both registered before session.resume, and the session.workflow.run friendly wrapper (name/handle overloads; foreground unwraps completed result and throws exported WorkflowRunError on non-completed; background returns the envelope). Exports from index.ts and extension.ts.

SDK-only; runtime untouched; no generated files edited.

Verification (Node 22, exit 0): npm run build; npm test -- test/workflow.test.ts (12 passed); typecheck; targeted ESLint. Deviations: none.
Build the real run() WorkflowContext in the SDK workflow.execute handler (replacing the Phase 2 stub): populate args; set ctx.session to the identical CopilotSession instance joinSession returned (strict === identity, no wrapper); expose ctx.signal as the per-run AbortController's AbortSignal; and implement log()/phase() as synchronous calls that buffer each line with a monotonic seq and flush incrementally over workflow.log (at await boundaries, a short unref'd timer, and a guaranteed finally-flush so no buffered narration is lost on a throwing/aborted body).

SDK side of Phase 4; runtime workflow.log handler is the companion commit.

Verification (Node 22, exit 0): npm run build; npm test -- test/workflow.test.ts (16 passed); typecheck; lint. Deviations: none.
Implement ctx.agent in the SDK run() context: it calls workflow.agent carrying the closed-over workflowRunId and the model, returns the subagent result text, and trims opts to label/schema/model only (effort/isolation/agentType/tool-narrowing are not v1). Schema handling arrives in Phase 6.

Verification (Node 22, exit 0): npm run build; npm test -- test/workflow.test.ts (17 passed, asserts effort is dropped). Deviations: none.
Document the supported schema subset on the WorkflowContext agent() schema option (examon validator subset, not arbitrary JSON Schema) so authors do not expect unsupported keywords. Companion to the runtime Rust parse/validate.

Verification (Node 22, exit 0): npm run build; npm run typecheck; npm test -- test/workflow.test.ts. Deviations: none.
Implement ctx.parallel(thunks) as a barrier fan-out (awaits all; throwing thunk -> null) and ctx.pipeline(items, ...stages) as per-item staged flow with no barrier (throwing stage nulls that item and skips its remaining stages). Both are pure SDK control flow holding no slot; concurrency is enforced by the runtime per-run limiter (each leaf ctx.agent is a workflow.agent RPC that queues until admitted). Enforce the non-configurable 4096 per-fan-out item cap, and validate element types up front so passing already-invoked promises surfaces a clear pass-functions-not-promises diagnostic instead of silently resolving to all-nulls.

Verification (Node 22, exit 0): npm run build; npm test -- test/workflow.test.ts (22 passed); typecheck. Deviations: none.
SDK-side durable step(): ctx.step(key, producer) does get-then-put across the RPC gap (workflow.journal.get -> run producer on miss -> workflow.journal.put), with a cached JSON null served as a hit and producer failures left un-journaled (retried on resume). Adds getRun() forwarding and resume integration wiring. Pairs with the runtime SQL journal/run store.

Verified (workflows-scoped, exit 0): npm run build; test/workflow.test.ts (24 passed, incl. step-once + cached-null + failures-not-cached + getRun forwarding). Deviations: none.
SDK-side cancellation support for the Phase-9 limits/timeout/cancel machinery: workflow.abort trips the per-run AbortController so in-flight ctx awaits reject with an AbortError and a well-behaved body unwinds cooperatively; session.workflow.cancel forwards the runId. Pairs with the runtime enforcement, approval/feedback gate, and halt->continue flow.

Verified (workflows-scoped, exit 0): npm run build; test/workflow.test.ts (both new Phase-9 tests pass: cancel forwards runId; workflow.abort rejects the in-flight await with AbortError). The lone unrelated failure is the documented win32-arm64 platform-package env wrinkle. Deviations: none.
Strengthens the SDK workflow-context unit coverage to lock the full-session trust-tier guarantee: asserts ctx.session is identical to the joinSession result (context.session === joinSessionResult and context.session.rpc === joinSessionResult.rpc, proving no facade/pruning) and that session RPC is reachable from the context (invokes context.session.rpc.tasks.list()). Test-only; pairs with the runtime E2E.

Verified (workflows-scoped, exit 0): npm run build; test/workflow.test.ts 26/26. Deviations: none.
…ame identity

Makes name-based resolution the primary path in session.workflow.run (the workflow-handle overload becomes local sugar), matching how tools/commands/subagents resolve by name, and removes a now-dead module-global workflowDefinitions map (workflow.execute resolves via the session-scoped registry instead). Adds unit coverage that name resolution selects the right registered workflow and that run-by-name forwards {name, args}. Duplicate-name rejection continues to rely on the Phase-3 registration policy.

Verified (workflows-scoped, exit 0): npm run build; test/workflow.test.ts (26 passed). Deviations: none.
Nested workflows are not supported: ctx.workflow(name, args) now throws a clear "nested workflows are not supported" error synchronously at the call site and forwards NO workflow.runNested request (no RPC leaves the SDK). The parentRunId/rootRunId contract scaffolding is untouched. Pairs with the runtime workflow.runNested structured rejection. (Phase reshaped from "Nested workflows" to "Forbid workflow nesting" per a human-authorized plan amendment.)

Verified (workflows-scoped, exit 0): npm run build; test/workflow.test.ts 27/27 (incl. ctx.workflow-throws-without-forwarding test asserting sendRequest is not called). Deviations: none.
Add @experimental JSDoc tags to the hand-authored Dynamic Workflows
surface, matching the Canvas prior art. Covers the exported symbols in
workflow.ts (defineWorkflow, WorkflowContext, WorkflowHandle,
SessionWorkflowApi, WorkflowRunError, and the related option and type
aliases), the WorkflowLimits and WorkflowMeta types in types.ts, the
readonly workflow property on the session, and the workflows option on
JoinSessionConfig.

The generated wire types in generated/rpc.ts already carry @experimental
because every workflow RPC method is marked stability: "experimental" in
the runtime contract, so no generated files change here. This commit only
annotates the hand-written ergonomic surface so consumers are warned the
API may change or be removed in a future SDK or CLI release.
@github-actions

This comment has been minimized.

@MRayermannMSFT MRayermannMSFT changed the title Add Dynamic Workflows Authoring Surface Add Agent Orchestrations Authoring Surface Jul 16, 2026
@github-actions

This comment has been minimized.

@MRayermannMSFT MRayermannMSFT marked this pull request as ready for review July 17, 2026 00:01
@MRayermannMSFT MRayermannMSFT requested a review from a team as a code owner July 17, 2026 00:01
Copilot AI review requested due to automatic review settings July 17, 2026 00:01

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds the experimental TypeScript authoring surface for defining, registering, executing, and managing Agent Orchestrations.

Changes:

  • Adds orchestration definitions, handles, context primitives, metadata, and run APIs.
  • Integrates orchestration registration and reverse-RPC execution into extension sessions.
  • Adds generated RPC contracts and comprehensive unit tests.
Show a summary per file
File Description
nodejs/src/orchestration.ts Defines the public orchestration API.
nodejs/src/session.ts Implements execution, primitives, progress, and cancellation.
nodejs/src/client.ts Registers orchestrations during extension resume.
nodejs/src/extension.ts Exposes orchestration registration through joinSession.
nodejs/src/index.ts Exports the new public surface.
nodejs/src/types.ts Adds orchestration metadata and limits.
nodejs/src/generated/rpc.ts Adds orchestration RPC contracts and handlers.
nodejs/test/orchestration.test.ts Tests orchestration behavior and integration.

Review details

  • Files reviewed: 7/8 changed files
  • Comments generated: 7
  • Review effort level: Medium

Comment thread nodejs/src/session.ts
thunks.map((thunk) =>
Promise.resolve()
.then(() => thunk())
.catch(() => null)
Comment thread nodejs/src/session.ts
Comment on lines +156 to +159
try {
previous = await stage(previous, item, index);
} catch {
return null;
Comment thread nodejs/src/session.ts
Comment on lines +1193 to +1194
const result = await definition.run(context);
return { result } as OrchestrationExecuteResult;
Comment on lines +219 to +223
const stored: StoredOrchestration = {
meta: definition.meta,
run: definition.run as StoredOrchestration["run"],
};
const handle = Object.freeze({ meta: definition.meta }) as OrchestrationHandle<TArgs, TResult>;
Comment thread nodejs/src/session.ts
Comment on lines +1095 to +1098
for (const handle of orchestrations) {
const definition = getOrchestrationDefinition(handle);
this.orchestrations.set(definition.meta.name, definition);
}
Comment thread nodejs/src/index.ts
Comment on lines +164 to +174
export type {
RunOptions,
SessionOrchestrationApi,
OrchestrationAgentOptions,
OrchestrationContext,
OrchestrationDefinition,
OrchestrationHandle,
OrchestrationJsonSchema,
OrchestrationPipelineStage,
OrchestrationStepOptions,
} from "./orchestration.js";
Comment thread nodejs/src/session.ts
Comment on lines +337 to +341
const envelope = await this.rpc.orchestration.run({
name,
args: (options?.args === undefined ? {} : options.args) as Parameters<
typeof this.rpc.orchestration.run
>[0]["args"],
Renames the authoring surface from "workflow" to "orchestration" to match
the new product name "agent orchestrations":

- defineWorkflow -> defineOrchestration; WorkflowContext/Definition/Handle/
  Meta/Limits -> Orchestration*; SessionWorkflowApi -> SessionOrchestrationApi
  (session.orchestration); WorkflowRunError -> OrchestrationRunError.
- joinSession({ workflows }) -> joinSession({ orchestrations }).
- workflow.ts -> orchestration.ts (and its test); generated/rpc.ts regenerated
  from the renamed runtime contract (workflow.* -> orchestration.*).

The context primitives (step, agent, parallel, pipeline, phase, log, run) keep
their names. Companion runtime rename lands in github/copilot-agent-runtime#12953.
@MRayermannMSFT MRayermannMSFT force-pushed the dev/mrayermannmsft/other/workflowsproto-1 branch from e60aeab to e290d1b Compare July 17, 2026 00:16
@github-actions

Copy link
Copy Markdown
Contributor

Cross-SDK Consistency Review

This PR adds the Agent Orchestrations authoring surface exclusively to the Node.js/TypeScript SDK. The feature is correctly marked @experimental.

Current State

The following SDKs do not yet have an equivalent orchestration API:

  • 🐍 Python (python/copilot/)
  • 🐹 Go (go/)
  • 🔷 .NET (dotnet/src/)
  • Java (java/src/main/java/)
  • 🦀 Rust (rust/src/)

Assessment

Since this is a new experimental feature, shipping it in one SDK first while the API stabilizes is reasonable. However, for long-term SDK consistency, the following public API surface will eventually need to be ported to all other SDKs:

Concept Node.js API Expected equivalents
Define an orchestration defineOrchestration({ meta, run }) define_orchestration / DefineOrchestration
Register on join joinSession({ orchestrations: [handle] }) equivalent orchestrations param
Run session.orchestration.run(name, options?) session.orchestration.run / session.Orchestration.Run
Get run status session.orchestration.getRun(runId) get_run / GetRun
Cancel a run session.orchestration.cancel(runId) cancel / Cancel
Orchestration context OrchestrationContext (agent, step, parallel, pipeline, phase, log) equivalent context object

Recommendation

No blocking issues for this PR — shipping the experimental surface in Node.js first is acceptable. I suggest tracking cross-language parity as follow-up work once the feature graduates from @experimental (or sooner, if the runtime contract is considered stable). Consider filing a tracking issue for porting to Python, Go, .NET, Java, and Rust.


This review was generated by the SDK Consistency Review Agent.

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • awmgmcpg

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "awmgmcpg"

See Network Configuration for more information.

Generated by SDK Consistency Review Agent for #2009 · 54.2 AIC · ⌖ 7.66 AIC · ⊞ 5K ·

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.

2 participants