Skip to content

Add Agent Factories Authoring Surface#2009

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

Add Agent Factories Authoring Surface#2009
MRayermannMSFT wants to merge 22 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 Factories. Extensions can define a factory with defineFactory({ meta, run }), register it by passing factory handles to joinSession({ factories }), and run it from a session through session.factory.run(name, options), with getRun and cancel for run management. RunOptions accepts per-invocation ad-hoc limits and a resumeFromRunId so a caller can retry the same run under a raised, user-approved budget after a limit failure. The factory body receives a context that exposes the durable factory 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 Factories over a JSON-RPC contract, but extension authors need an ergonomic, typed TypeScript surface to define and run factories rather than hand-writing wire calls. This adds that friendly layer and the factory context primitives that let a multi-agent factory 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

@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 Outdated
Comment thread nodejs/src/session.ts
Comment thread nodejs/src/session.ts Outdated
Comment thread nodejs/src/orchestration.ts Outdated
Comment thread nodejs/src/session.ts Outdated
Comment thread nodejs/src/index.ts Outdated
Comment thread nodejs/src/session.ts Outdated
@MRayermannMSFT
MRayermannMSFT force-pushed the dev/mrayermannmsft/other/workflowsproto-1 branch from e60aeab to e290d1b Compare July 17, 2026 00:16
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@MRayermannMSFT MRayermannMSFT changed the title Add Agent Orchestrations Authoring Surface Add Agent Factories Authoring Surface Jul 17, 2026
@github-actions

This comment has been minimized.

@MRayermannMSFT
MRayermannMSFT force-pushed the dev/mrayermannmsft/other/workflowsproto-1 branch from e512ad2 to 0a95a6f Compare July 17, 2026 02:30
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@MRayermannMSFT
MRayermannMSFT force-pushed the dev/mrayermannmsft/other/workflowsproto-1 branch from e33c993 to 34fbf49 Compare July 17, 2026 13:13
@github-actions

This comment has been minimized.

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.
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.
Address Copilot review feedback on the agent orchestrations PR (SDK side):

- Document the restricted structured-output schema subset at the contract
  boundary on OrchestrationJsonSchema: only a conservative keyword set is
  honored (type/required/enum/const/properties/items/anyOf/oneOf/allOf);
  unsupported constraints (pattern, minLength, format, additionalProperties,
  numeric ranges, boolean schemas) are ignored, and oneOf is treated like
  anyOf. It is a best-effort accept-or-retry guard, not full JSON Schema.
- Regenerate rpc.ts from the updated runtime contract, dropping the removed
  orchestration.runNested method and OrchestrationRunNestedRequest (nesting
  is forbidden; the context-level rejection is covered by orchestration.test.ts).
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.
Address Copilot review feedback on the agent factories PR (SDK side):

- Reject a factory `timeout` limit above 2^31-1 ms in defineFactory's limit
  validation. Node clamps setTimeout delays above that to ~1ms, which would
  make a large declared timeout halt the run almost immediately. Adds a test.
- Regenerate rpc.ts to drop the removed `parentRunId` field from
  FactoryExecuteRequest (factory nesting is forbidden; the field was never
  populated by the runtime).
Fix prettier formatting on factory.ts, session.ts, and factory.test.ts
(collapse multi-line signatures/expressions to Prettier print width).
Update joinSession onPermissionRequest tests to spy on
resumeSessionForExtension (the method joinSession now calls to thread
factories). The old spy on resumeSession no longer intercepts, so the real
method ran and hung to a 30s timeout once the prettier gate stopped
short-circuiting the suite.
Address CCR review feedback (clear/low-churn items):
- Reject duplicate factory names in registerFactories so reverse dispatch is
  deterministic (silently overwriting a name left the runtime advertising two
  declarations for one closure). Adds a test.
- Re-export FactoryRunResult from factory.ts, the package root, and the
  extension entry point so consumers can name the type returned by
  SessionFactoryApi methods and carried on FactoryRunError.envelope.
Deviation-review CR-5 (SDK side): the parallel/pipeline combinators mapped
every thunk/stage error to null, including the AbortError raised when a run is
cancelled mid-agent(). That let an aborted run report success with null
results instead of propagating cancellation. Re-throw abort-shaped failures
from both combinators (via an isFactoryAbortError guard) so cancellation
bubbles out; ordinary per-item failures still map to null. Adds a test that a
parallel-wrapped aborted agent() call rejects with AbortError.
Phase 3 (SDK side): ad-hoc limits and resume consent.

Adds limits to the public RunOptions (typed as the public FactoryLimits) and
forwards it plus resumeFromRunId in the session.factory.run request copy;
refactors validateFactoryLimits to take a limits object; regenerates the wire
types (FactoryRunLimits, factory_resume_declined). Paired with runtime Shift 3.

Verification (Node 22): npm run build (0); npm test -- factory (31).
Ships-dark: generated types track the runtime schema; SDK CI codegen is
expectedly red until the runtime publishes and the @github/copilot pin bumps.
Phase 4 (SDK side): remove background execution mode.

Removes background from the public RunOptions, the run() overloads, and the
session.factory.run request copy; regenerates the wire types. Paired with
runtime Shift 4.

Verification (Node 22): npm run build (0); factory tests (31). Ships-dark CI
note applies.
@MRayermannMSFT
MRayermannMSFT force-pushed the dev/mrayermannmsft/other/workflowsproto-1 branch from 34fbf49 to 372f740 Compare July 18, 2026 01:59
@github-actions

Copy link
Copy Markdown
Contributor

Cross-SDK Consistency Review

Summary: This PR adds the Agent Factories authoring surface exclusively to the Node.js/TypeScript SDK. No equivalent feature exists in Python, Go, .NET, Java, or Rust.

Finding: New experimental feature, Node.js only ⚠️

The PR introduces a significant new API surface:

  • defineFactory({ meta, run }) — define a factory
  • joinSession({ factories }) — register factories at session join
  • session.factory.run(...), .getRun(runId), .cancel(runId) — invoke and manage factory runs
  • FactoryContext primitives: agent, step, parallel, pipeline, phase, log

None of these exist in the other five SDK implementations (Python, Go, .NET, Java, Rust).

Assessment: Expected gap — no action required right now

This is an intentional inconsistency for the following reasons:

  1. Marked @experimental throughout — every exported type and function carries an @experimental JSDoc tag noting it may change or be removed. This signals the API is not yet stable for cross-language rollout.
  2. Companion runtime PR — the PR description references a companion runtime PR (github/copilot-agent-runtime#12953), indicating this is a staged rollout where the Node.js SDK serves as the initial authoring surface.
  3. TypeScript-first pattern — the SDK already has TypeScript-only experimental APIs. This follows that established pattern.

Suggested follow-up (non-blocking)

When the Agent Factories feature graduates from @experimental status, the following should be considered for cross-SDK parity:

SDK Equivalent surface needed
Python define_factory(), join_session(factories=[...]), session.factory.run()
Go DefineFactory(), JoinSession with Factories option, session.Factory.Run()
.NET DefineFactory(), JoinSession with Factories property, session.Factory.RunAsync()
Java defineFactory(), joinSession with factories list, session.factory().run()
Rust define_factory(), session factory API

No changes are required at this time. The PR is internally consistent with the Node.js SDK's established patterns for experimental features.

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 · 40.3 AIC · ⌖ 7.69 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