@bastani/atomic 0.5.0-5 → 0.5.1-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,7 +7,7 @@
7
7
  * `bun run executor.ts --run <args>`
8
8
  * 3. The CLI then attaches to the tmux session (user sees it live)
9
9
  * 4. The orchestrator pane calls `definition.run(workflowCtx)` — the
10
- * user's callback uses `ctx.session()` to spawn agent sessions
10
+ * user's callback uses `ctx.stage()` to spawn agent sessions
11
11
  */
12
12
 
13
13
  import { join, resolve } from "path";
@@ -24,6 +24,8 @@ import type {
24
24
  Transcript,
25
25
  SavedMessage,
26
26
  SaveTranscript,
27
+ StageClientOptions,
28
+ StageSessionOptions,
27
29
  } from "../types.ts";
28
30
  import type { SessionEvent } from "@github/copilot-sdk";
29
31
  import type { SessionPromptResponse } from "@opencode-ai/sdk/v2";
@@ -31,8 +33,13 @@ import type { SessionMessage } from "@anthropic-ai/claude-agent-sdk";
31
33
  import * as tmux from "./tmux.ts";
32
34
  import { getMuxBinary } from "./tmux.ts";
33
35
  import { WorkflowLoader } from "./loader.ts";
34
- import { clearClaudeSession } from "../providers/claude.ts";
36
+ import {
37
+ clearClaudeSession,
38
+ ClaudeClientWrapper,
39
+ ClaudeSessionWrapper,
40
+ } from "../providers/claude.ts";
35
41
  import { OrchestratorPanel } from "./panel.tsx";
42
+ import { GraphFrontierTracker } from "./graph-inference.ts";
36
43
 
37
44
  /** Maximum time (ms) to wait for an agent's server to become reachable. */
38
45
  const SERVER_WAIT_TIMEOUT_MS = 60_000;
@@ -98,11 +105,7 @@ interface SessionResult {
98
105
  interface ActiveSession {
99
106
  name: string;
100
107
  paneId: string;
101
- /**
102
- * Settles when the session finishes. Resolves on success, rejects with the
103
- * callback's error on failure. Dependent sessions awaiting via `dependsOn`
104
- * block on this promise.
105
- */
108
+ /** Settles when the session finishes. Resolves on success, rejects on failure. */
106
109
  done: Promise<void>;
107
110
  }
108
111
 
@@ -410,7 +413,7 @@ function resolveRef(ref: SessionRef): string {
410
413
  }
411
414
 
412
415
  // ============================================================================
413
- // Session runner — implements ctx.session() lifecycle
416
+ // Session runner — implements ctx.stage() lifecycle
414
417
  // ============================================================================
415
418
 
416
419
  /** Shared state passed to session runners by the orchestrator. */
@@ -427,23 +430,118 @@ interface SharedRunnerState {
427
430
  }
428
431
 
429
432
  /**
430
- * Create a `ctx.session()` function bound to a parent name for graph edges.
433
+ * Create the provider-specific client and session for a stage.
434
+ * Called by the session runner after server readiness is confirmed.
435
+ */
436
+ async function initProviderClientAndSession(
437
+ agent: AgentType,
438
+ serverUrl: string,
439
+ paneId: string,
440
+ sessionId: string,
441
+ clientOpts: StageClientOptions<AgentType>,
442
+ sessionOpts: StageSessionOptions<AgentType>,
443
+ ): Promise<{ client: unknown; session: unknown }> {
444
+ switch (agent) {
445
+ case "copilot": {
446
+ const { CopilotClient, approveAll } = await import("@github/copilot-sdk");
447
+ const copilotClientOpts = clientOpts as StageClientOptions<"copilot">;
448
+ const copilotSessionOpts = sessionOpts as StageSessionOptions<"copilot">;
449
+ const client = new CopilotClient({ cliUrl: serverUrl, ...copilotClientOpts });
450
+ await client.start();
451
+ const session = await client.createSession({
452
+ onPermissionRequest: approveAll,
453
+ ...copilotSessionOpts,
454
+ });
455
+ await client.setForegroundSessionId(session.sessionId);
456
+ return { client, session };
457
+ }
458
+ case "opencode": {
459
+ const { createOpencodeClient } = await import("@opencode-ai/sdk/v2");
460
+ const ocClientOpts = clientOpts as StageClientOptions<"opencode">;
461
+ const ocSessionOpts = sessionOpts as StageSessionOptions<"opencode">;
462
+ const client = createOpencodeClient({ baseUrl: serverUrl, ...ocClientOpts });
463
+ const sessionResult = await client.session.create(ocSessionOpts);
464
+ await client.tui.selectSession({ sessionID: sessionResult.data!.id });
465
+ return { client, session: sessionResult.data! };
466
+ }
467
+ case "claude": {
468
+ const claudeClientOpts = clientOpts as StageClientOptions<"claude">;
469
+ const claudeSessionOpts = sessionOpts as StageSessionOptions<"claude">;
470
+ const client = new ClaudeClientWrapper(paneId, claudeClientOpts);
471
+ await client.start();
472
+ const session = new ClaudeSessionWrapper(paneId, sessionId, claudeSessionOpts);
473
+ return { client, session };
474
+ }
475
+ }
476
+ }
477
+
478
+ /**
479
+ * Clean up provider-specific resources after a stage callback completes.
480
+ * Errors are silently caught — cleanup must not mask callback errors.
481
+ */
482
+ async function cleanupProvider(
483
+ agent: AgentType,
484
+ providerClient: unknown,
485
+ providerSession: unknown,
486
+ paneId: string,
487
+ ): Promise<void> {
488
+ switch (agent) {
489
+ case "copilot": {
490
+ const { CopilotSession: CopilotSessionClass } = await import("@github/copilot-sdk");
491
+ try {
492
+ if (providerSession instanceof CopilotSessionClass) {
493
+ await providerSession.disconnect();
494
+ }
495
+ } catch {}
496
+ try {
497
+ const { CopilotClient: CopilotClientClass } = await import("@github/copilot-sdk");
498
+ if (providerClient instanceof CopilotClientClass) {
499
+ await providerClient.stop();
500
+ }
501
+ } catch {}
502
+ break;
503
+ }
504
+ case "opencode":
505
+ // Stateless HTTP client — no cleanup needed
506
+ break;
507
+ case "claude":
508
+ clearClaudeSession(paneId);
509
+ break;
510
+ }
511
+ }
512
+
513
+ /**
514
+ * Create a `ctx.stage()` function bound to a parent name for graph edges.
515
+ *
516
+ * Graph topology is auto-inferred from JavaScript's execution order:
517
+ * - **Sequential** (`await`): the completed stage is in the frontier when the
518
+ * next stage spawns → parent-child edge.
519
+ * - **Parallel** (`Promise.all`): both calls fire in the same synchronous
520
+ * frame → frontier is empty for the second call → sibling edges.
521
+ * - **Fan-in**: after `Promise.all` resolves, all parallel stages are in the
522
+ * frontier → the next stage depends on all of them.
523
+ *
431
524
  * The returned function manages the full session lifecycle:
432
- * spawn → run callback → flush saves → complete/error → cleanup.
525
+ * spawn → init client/session → run callback → flush saves → cleanup → complete/error.
433
526
  */
434
527
  function createSessionRunner(
435
528
  shared: SharedRunnerState,
436
529
  parentName: string,
437
530
  ): <T = void>(
438
531
  options: SessionRunOptions,
532
+ clientOpts: StageClientOptions<AgentType>,
533
+ sessionOpts: StageSessionOptions<AgentType>,
439
534
  run: (ctx: SessionContext) => Promise<T>,
440
535
  ) => Promise<SessionHandle<T>> {
536
+ const graphTracker = new GraphFrontierTracker(parentName);
537
+
441
538
  return async <T = void>(
442
539
  options: SessionRunOptions,
540
+ clientOpts: StageClientOptions<AgentType>,
541
+ sessionOpts: StageSessionOptions<AgentType>,
443
542
  run: (ctx: SessionContext) => Promise<T>,
444
543
  ): Promise<SessionHandle<T>> => {
445
544
  const { name } = options;
446
- const deps = options.dependsOn ?? [];
447
545
 
448
546
  // ── 1. Validate name uniqueness (synchronous, before any await) ──
449
547
  if (!name || name.trim() === "") {
@@ -453,23 +551,8 @@ function createSessionRunner(
453
551
  throw new Error(`Duplicate session name: "${name}"`);
454
552
  }
455
553
 
456
- // ── 2. Validate dependsOn (synchronous, before any await) ──
457
- if (deps.length > 0) {
458
- if (deps.includes(name)) {
459
- throw new Error(`Session "${name}" cannot depend on itself.`);
460
- }
461
- const unknown = deps.filter(
462
- (d) =>
463
- !shared.activeRegistry.has(d) && !shared.completedRegistry.has(d),
464
- );
465
- if (unknown.length > 0) {
466
- throw new Error(
467
- `Session "${name}" dependsOn unknown session(s): ${unknown.join(
468
- ", ",
469
- )}. Dependencies must be spawned before the dependent session.`,
470
- );
471
- }
472
- }
554
+ // ── 2. Auto-infer graph parents from frontier (synchronous) ──
555
+ const graphParents = graphTracker.onSpawn();
473
556
 
474
557
  // ── 3. Create done promise so dependent sessions can await this one ──
475
558
  let resolveDone!: () => void;
@@ -487,22 +570,8 @@ function createSessionRunner(
487
570
 
488
571
  const sessionId = generateId();
489
572
  let paneId = "";
490
- // Graph parents: explicit deps if provided, otherwise the enclosing scope.
491
- const graphParents = deps.length > 0 ? [...deps] : [parentName];
492
573
 
493
574
  try {
494
- // ── 5. Wait for dependsOn sessions to finish ──
495
- // Active deps block; completed deps resolve immediately. If a dep
496
- // rejects (its session failed), this throws and aborts the dependent.
497
- if (deps.length > 0) {
498
- await Promise.all(
499
- deps.map((d) => {
500
- const active = shared.activeRegistry.get(d);
501
- if (active) return active.done;
502
- return Promise.resolve();
503
- }),
504
- );
505
- }
506
575
 
507
576
  // ── 6. Allocate port ──
508
577
  const port = await getRandomPort();
@@ -652,9 +721,21 @@ function createSessionRunner(
652
721
  return JSON.parse(raw) as SavedMessage[];
653
722
  };
654
723
 
655
- // ── 12. Construct SessionContext ──
724
+ // ── 12. Auto-create provider client and session ──
725
+ const { client: providerClient, session: providerSession } =
726
+ await initProviderClientAndSession(
727
+ shared.agent,
728
+ serverUrl,
729
+ paneId,
730
+ sessionId,
731
+ clientOpts,
732
+ sessionOpts,
733
+ );
734
+
735
+ // ── 13. Construct SessionContext ──
656
736
  const ctx: SessionContext = {
657
- serverUrl,
737
+ client: providerClient as SessionContext["client"],
738
+ session: providerSession as SessionContext["session"],
658
739
  userPrompt: shared.prompt,
659
740
  agent: shared.agent,
660
741
  sessionDir,
@@ -663,7 +744,7 @@ function createSessionRunner(
663
744
  save,
664
745
  transcript: transcriptFn,
665
746
  getMessages: getMessagesFn,
666
- session: createSessionRunner(shared, name),
747
+ stage: createSessionRunner(shared, name) as SessionContext["stage"],
667
748
  };
668
749
 
669
750
  // ── Write session metadata ──
@@ -684,7 +765,7 @@ function createSessionRunner(
684
765
  ),
685
766
  );
686
767
 
687
- // ── 13. Run user callback ──
768
+ // ── 14. Run user callback ──
688
769
  let callbackResult: T;
689
770
  try {
690
771
  callbackResult = await run(ctx);
@@ -697,27 +778,29 @@ function createSessionRunner(
697
778
  );
698
779
  shared.panel.sessionError(name, message);
699
780
  throw error;
781
+ } finally {
782
+ // ── 14a. Auto-cleanup provider resources ──
783
+ await cleanupProvider(shared.agent, providerClient, providerSession, paneId);
700
784
  }
701
785
 
702
- // ── 14. Mark session complete ──
786
+ // ── 15. Mark session complete ──
703
787
  shared.panel.sessionSuccess(name);
704
788
  const result: SessionResult = { name, sessionId, sessionDir, paneId };
705
789
  shared.completedRegistry.set(name, result);
706
790
  shared.activeRegistry.delete(name);
707
791
  resolveDone();
708
792
 
793
+ // Update frontier so the next stage in this scope chains from us.
794
+ graphTracker.onSettle(name);
709
795
  return { name, id: sessionId, result: callbackResult! };
710
796
  } catch (error) {
711
- // Ensure the done promise settles and the active entry is cleared so
712
- // dependents fail fast instead of hanging forever on a ghost dep.
797
+ // Ensure the done promise settles and the active entry is cleared.
713
798
  shared.activeRegistry.delete(name);
714
799
  rejectDone(error);
800
+ // Update frontier even on failure — if the caller catches and
801
+ // continues, the next stage should still chain from this one.
802
+ graphTracker.onSettle(name);
715
803
  throw error;
716
- } finally {
717
- // ── 15. Cleanup (Claude session state) ──
718
- if (shared.agent === "claude" && paneId) {
719
- clearClaudeSession(paneId);
720
- }
721
804
  }
722
805
  };
723
806
  }
@@ -831,7 +914,7 @@ async function runOrchestrator(): Promise<void> {
831
914
  const workflowCtx: WorkflowContext = {
832
915
  userPrompt: prompt,
833
916
  agent,
834
- session: sessionRunner,
917
+ stage: sessionRunner as WorkflowContext["stage"],
835
918
  transcript: async (ref: SessionRef): Promise<Transcript> => {
836
919
  const refName = resolveRef(ref);
837
920
  const prev = shared.completedRegistry.get(refName);
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Frontier-based graph inference for workflow stage topology.
3
+ *
4
+ * Automatically infers parent-child edges from JavaScript's execution order:
5
+ * - **Sequential** (`await`): completed stages are in the frontier when the
6
+ * next stage spawns → parent-child edge.
7
+ * - **Parallel** (`Promise.all`): both calls fire in the same synchronous
8
+ * frame → frontier is empty for the second call → sibling edges.
9
+ * - **Fan-in**: after `Promise.all` resolves, all parallel stages are in the
10
+ * frontier → the next stage depends on all of them.
11
+ */
12
+ export class GraphFrontierTracker {
13
+ /**
14
+ * Stages that completed since the last stage was spawned in this scope.
15
+ * When non-empty at spawn time, the new stage is sequential (depends on frontier).
16
+ */
17
+ private frontier: string[] = [];
18
+
19
+ /**
20
+ * The parent set for the current parallel batch — a snapshot of the frontier
21
+ * at the point the first sibling consumed it.
22
+ */
23
+ private parallelAncestors: string[];
24
+
25
+ constructor(parentName: string) {
26
+ this.parallelAncestors = [parentName];
27
+ }
28
+
29
+ /**
30
+ * Called synchronously when a new stage is spawned.
31
+ * Returns the inferred graph parents for this stage.
32
+ */
33
+ onSpawn(): string[] {
34
+ if (this.frontier.length > 0) {
35
+ // Sequential: previous stage(s) completed → new wave
36
+ this.parallelAncestors = [...this.frontier];
37
+ this.frontier = [];
38
+ }
39
+ // Parallel sibling, first stage, or sequential → same ancestors
40
+ return [...this.parallelAncestors];
41
+ }
42
+
43
+ /**
44
+ * Called when a stage settles (completes or fails).
45
+ * Adds the stage to the frontier so the next spawn can chain from it.
46
+ */
47
+ onSettle(name: string): void {
48
+ this.frontier.push(name);
49
+ }
50
+ }
package/src/sdk/types.ts CHANGED
@@ -8,9 +8,95 @@ import type { SessionEvent } from "@github/copilot-sdk";
8
8
  import type { SessionPromptResponse } from "@opencode-ai/sdk/v2";
9
9
  import type { SessionMessage } from "@anthropic-ai/claude-agent-sdk";
10
10
 
11
+ // Provider SDK types for the type maps
12
+ import type {
13
+ CopilotClient,
14
+ CopilotClientOptions,
15
+ CopilotSession,
16
+ SessionConfig as CopilotSessionConfig,
17
+ } from "@github/copilot-sdk";
18
+ import type {
19
+ OpencodeClient,
20
+ Session as OpencodeSession,
21
+ } from "@opencode-ai/sdk/v2";
22
+ import type {
23
+ ClaudeClientWrapper,
24
+ ClaudeSessionWrapper,
25
+ ClaudeQueryDefaults,
26
+ } from "./providers/claude.ts";
27
+
11
28
  /** Supported agent types */
12
29
  export type AgentType = "copilot" | "opencode" | "claude";
13
30
 
31
+ // ─── Provider type maps ─────────────────────────────────────────────────────
32
+
33
+ /**
34
+ * Maps each agent to the client init options the user passes to `ctx.stage()`.
35
+ * Auto-injected fields (`cliUrl`, `baseUrl`, `paneId`) are omitted.
36
+ */
37
+ type ClientOptionsMap = {
38
+ opencode: { directory?: string; experimental_workspaceID?: string };
39
+ copilot: Omit<CopilotClientOptions, "cliUrl">;
40
+ claude: { chatFlags?: string[]; readyTimeoutMs?: number };
41
+ };
42
+
43
+ /**
44
+ * Maps each agent to the session create options the user passes to `ctx.stage()`.
45
+ * - OpenCode: `client.session.create()` body params
46
+ * - Copilot: `client.createSession()` config (onPermissionRequest defaults to approveAll)
47
+ * - Claude: `claudeQuery()` defaults for subsequent queries
48
+ */
49
+ type SessionOptionsMap = {
50
+ opencode: {
51
+ parentID?: string;
52
+ title?: string;
53
+ workspaceID?: string;
54
+ };
55
+ copilot: Partial<CopilotSessionConfig>;
56
+ claude: ClaudeQueryDefaults;
57
+ };
58
+
59
+ /** Maps each agent to the `s.client` type provided in the stage callback. */
60
+ type ClientMap = {
61
+ opencode: OpencodeClient;
62
+ copilot: CopilotClient;
63
+ claude: ClaudeClientWrapper;
64
+ };
65
+
66
+ /** Maps each agent to the `s.session` type provided in the stage callback. */
67
+ type SessionMap = {
68
+ opencode: OpencodeSession;
69
+ copilot: CopilotSession;
70
+ claude: ClaudeSessionWrapper;
71
+ };
72
+
73
+ /** Client init options for `ctx.stage()`, resolved by agent type. */
74
+ export type StageClientOptions<A extends AgentType> = ClientOptionsMap[A];
75
+
76
+ /** Session create options for `ctx.stage()`, resolved by agent type. */
77
+ export type StageSessionOptions<A extends AgentType> = SessionOptionsMap[A];
78
+
79
+ /** The `s.client` type in a stage callback, resolved by agent type. */
80
+ export type ProviderClient<A extends AgentType> = ClientMap[A];
81
+
82
+ /** The `s.session` type in a stage callback, resolved by agent type. */
83
+ export type ProviderSession<A extends AgentType> = SessionMap[A];
84
+
85
+ // Re-export provider types for convenience
86
+ export type {
87
+ CopilotClient,
88
+ CopilotClientOptions,
89
+ CopilotSession,
90
+ CopilotSessionConfig,
91
+ OpencodeClient,
92
+ OpencodeSession,
93
+ ClaudeClientWrapper,
94
+ ClaudeSessionWrapper,
95
+ ClaudeQueryDefaults,
96
+ };
97
+
98
+ // ─── Core types ─────────────────────────────────────────────────────────────
99
+
14
100
  /**
15
101
  * A transcript from a completed session.
16
102
  * Provides both the file path and rendered text content.
@@ -34,7 +120,7 @@ export type SavedMessage =
34
120
  /**
35
121
  * Save native message objects from the provider SDK.
36
122
  *
37
- * - **Copilot**: `s.save(await session.getMessages())`
123
+ * - **Copilot**: `s.save(await s.session.getMessages())`
38
124
  * - **OpenCode**: `s.save(result.data)` — the full `{ info, parts }` response
39
125
  * - **Claude**: `s.save(sessionId)` — auto-reads via `getSessionMessages()`
40
126
  */
@@ -51,7 +137,7 @@ export interface SaveTranscript {
51
137
  export type SessionRef = string | SessionHandle<unknown>;
52
138
 
53
139
  /**
54
- * Handle returned by `ctx.session()`. Used for type-safe transcript references
140
+ * Handle returned by `ctx.stage()`. Used for type-safe transcript references
55
141
  * and carries the callback's return value.
56
142
  */
57
143
  export interface SessionHandle<T = void> {
@@ -64,45 +150,29 @@ export interface SessionHandle<T = void> {
64
150
  }
65
151
 
66
152
  /**
67
- * Options for spawning a session via `ctx.session()`.
153
+ * Options for spawning a session via `ctx.stage()`.
68
154
  */
69
155
  export interface SessionRunOptions {
70
156
  /** Unique name for this session (used for transcript references and graph display) */
71
157
  name: string;
72
158
  /** Human-readable description */
73
159
  description?: string;
74
- /**
75
- * Names of sessions this one depends on. Serves two purposes:
76
- *
77
- * 1. **Graph rendering** — each named session becomes a parent edge in the
78
- * graph, so chains and fan-ins show up as real topology instead of
79
- * sibling-under-root.
80
- * 2. **Runtime ordering** — at spawn time, the runtime waits for every
81
- * named dep to finish before starting. This makes dependency-driven
82
- * `Promise.all([...])` patterns safe: you can kick off many sessions
83
- * concurrently and let `dependsOn` serialize only the edges that matter.
84
- *
85
- * Each name must refer to a session that has already been spawned (either
86
- * active or completed) at the time the dependent session is created.
87
- * Unknown names throw a clear error.
88
- *
89
- * When omitted, the session falls back to the default parent (the
90
- * enclosing `ctx.session()` scope, or `orchestrator` at the top level).
91
- */
92
- dependsOn?: string[];
93
160
  }
94
161
 
95
162
  /**
96
163
  * Context provided to each session's callback.
97
- * Created by `ctx.session(opts, fn)` — the callback receives this as its argument.
164
+ * Created by `ctx.stage(opts, clientOpts, sessionOpts, fn)` — the callback
165
+ * receives this as its argument with pre-initialized `client` and `session`.
98
166
  */
99
- export interface SessionContext {
100
- /** The agent's server URL (Copilot --ui-server / OpenCode built-in server) */
101
- serverUrl: string;
167
+ export interface SessionContext<A extends AgentType = AgentType> {
168
+ /** Provider-specific SDK client (auto-created by runtime) */
169
+ client: ProviderClient<A>;
170
+ /** Provider-specific session (auto-created by runtime) */
171
+ session: ProviderSession<A>;
102
172
  /** The original user prompt from the CLI invocation */
103
173
  userPrompt: string;
104
174
  /** Which agent is running */
105
- agent: AgentType;
175
+ agent: A;
106
176
  /**
107
177
  * Get a completed session's transcript as rendered text.
108
178
  * Accepts a SessionHandle (recommended) or session name string.
@@ -129,29 +199,34 @@ export interface SessionContext {
129
199
  * The sub-session is a child of this session in the graph.
130
200
  * The callback's return value is available as `handle.result`.
131
201
  */
132
- session<T = void>(
202
+ stage<T = void>(
133
203
  options: SessionRunOptions,
134
- run: (ctx: SessionContext) => Promise<T>,
204
+ clientOpts: StageClientOptions<A>,
205
+ sessionOpts: StageSessionOptions<A>,
206
+ run: (ctx: SessionContext<A>) => Promise<T>,
135
207
  ): Promise<SessionHandle<T>>;
136
208
  }
137
209
 
138
210
  /**
139
211
  * Top-level context provided to the workflow's `.run()` callback.
140
- * Does not have session-specific fields (serverUrl, paneId, save, etc.).
212
+ * Does not have session-specific fields (paneId, save, etc.).
141
213
  */
142
- export interface WorkflowContext {
214
+ export interface WorkflowContext<A extends AgentType = AgentType> {
143
215
  /** The original user prompt from the CLI invocation */
144
216
  userPrompt: string;
145
217
  /** Which agent is running */
146
- agent: AgentType;
218
+ agent: A;
147
219
  /**
148
220
  * Spawn a session with its own tmux window and graph node.
149
- * The runtime manages the full lifecycle: startrun callback complete/error.
150
- * The callback's return value is available as `handle.result`.
221
+ * The runtime manages the full lifecycle: create client create session
222
+ * run callback → cleanup. The callback's return value is available as
223
+ * `handle.result`.
151
224
  */
152
- session<T = void>(
225
+ stage<T = void>(
153
226
  options: SessionRunOptions,
154
- run: (ctx: SessionContext) => Promise<T>,
227
+ clientOpts: StageClientOptions<A>,
228
+ sessionOpts: StageSessionOptions<A>,
229
+ run: (ctx: SessionContext<A>) => Promise<T>,
155
230
  ): Promise<SessionHandle<T>>;
156
231
  /**
157
232
  * Get a completed session's transcript as rendered text.
@@ -178,10 +253,10 @@ export interface WorkflowOptions {
178
253
  /**
179
254
  * A compiled workflow definition — the sealed output of defineWorkflow().compile().
180
255
  */
181
- export interface WorkflowDefinition {
256
+ export interface WorkflowDefinition<A extends AgentType = AgentType> {
182
257
  readonly __brand: "WorkflowDefinition";
183
258
  readonly name: string;
184
259
  readonly description: string;
185
260
  /** The workflow's entry point. Called by the executor with a WorkflowContext. */
186
- readonly run: (ctx: WorkflowContext) => Promise<void>;
261
+ readonly run: (ctx: WorkflowContext<A>) => Promise<void>;
187
262
  }
@@ -2,7 +2,7 @@
2
2
  * atomic/workflows
3
3
  *
4
4
  * Workflow SDK for defining dynamic agent workflows.
5
- * Workflows use defineWorkflow().run().compile() with ctx.session()
5
+ * Workflows use defineWorkflow().run().compile() with ctx.stage()
6
6
  * for spawning agent sessions using native TypeScript control flow.
7
7
  */
8
8
 
@@ -20,6 +20,19 @@ export type {
20
20
  WorkflowContext,
21
21
  WorkflowOptions,
22
22
  WorkflowDefinition,
23
+ StageClientOptions,
24
+ StageSessionOptions,
25
+ ProviderClient,
26
+ ProviderSession,
27
+ CopilotClient,
28
+ CopilotClientOptions,
29
+ CopilotSession,
30
+ CopilotSessionConfig,
31
+ OpencodeClient,
32
+ OpencodeSession,
33
+ ClaudeClientWrapper,
34
+ ClaudeSessionWrapper,
35
+ ClaudeQueryDefaults,
23
36
  } from "./types.ts";
24
37
 
25
38
  // Re-export native SDK types for convenience