@bastani/atomic 0.8.23 → 0.8.24-alpha.2

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.
Files changed (72) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/dist/builtin/intercom/CHANGELOG.md +13 -0
  3. package/dist/builtin/intercom/package.json +1 -1
  4. package/dist/builtin/mcp/CHANGELOG.md +13 -0
  5. package/dist/builtin/mcp/package.json +1 -1
  6. package/dist/builtin/subagents/CHANGELOG.md +28 -0
  7. package/dist/builtin/subagents/README.md +16 -0
  8. package/dist/builtin/subagents/agents/code-simplifier.md +2 -3
  9. package/dist/builtin/subagents/agents/codebase-analyzer.md +2 -3
  10. package/dist/builtin/subagents/agents/codebase-locator.md +2 -3
  11. package/dist/builtin/subagents/agents/codebase-online-researcher.md +2 -3
  12. package/dist/builtin/subagents/agents/codebase-pattern-finder.md +2 -3
  13. package/dist/builtin/subagents/agents/codebase-research-analyzer.md +2 -3
  14. package/dist/builtin/subagents/agents/codebase-research-locator.md +2 -3
  15. package/dist/builtin/subagents/agents/debugger.md +2 -3
  16. package/dist/builtin/subagents/package.json +1 -1
  17. package/dist/builtin/subagents/skills/subagent/SKILL.md +6 -0
  18. package/dist/builtin/subagents/src/agents/agent-serializer.ts +3 -0
  19. package/dist/builtin/subagents/src/agents/agents.ts +20 -1
  20. package/dist/builtin/subagents/src/runs/background/async-execution.ts +1 -1
  21. package/dist/builtin/subagents/src/runs/background/subagent-runner.ts +3 -1
  22. package/dist/builtin/subagents/src/runs/foreground/chain-clarify.ts +7 -7
  23. package/dist/builtin/subagents/src/runs/foreground/execution.ts +5 -1
  24. package/dist/builtin/subagents/src/runs/shared/model-fallback.ts +9 -10
  25. package/dist/builtin/subagents/src/shared/types.ts +1 -0
  26. package/dist/builtin/web-access/CHANGELOG.md +13 -0
  27. package/dist/builtin/web-access/package.json +1 -1
  28. package/dist/builtin/workflows/CHANGELOG.md +35 -0
  29. package/dist/builtin/workflows/README.md +38 -41
  30. package/dist/builtin/workflows/ambient.d.ts +36 -0
  31. package/dist/builtin/workflows/builtin/deep-research-codebase.d.ts +35 -0
  32. package/dist/builtin/workflows/builtin/deep-research-codebase.ts +11 -14
  33. package/dist/builtin/workflows/builtin/goal.d.ts +46 -0
  34. package/dist/builtin/workflows/builtin/goal.ts +10 -12
  35. package/dist/builtin/workflows/builtin/index.d.ts +136 -0
  36. package/dist/builtin/workflows/builtin/open-claude-design.d.ts +44 -0
  37. package/dist/builtin/workflows/builtin/open-claude-design.ts +19 -20
  38. package/dist/builtin/workflows/builtin/ralph.d.ts +36 -0
  39. package/dist/builtin/workflows/builtin/ralph.ts +20 -24
  40. package/dist/builtin/workflows/package.json +15 -5
  41. package/dist/builtin/workflows/src/authoring.d.ts +197 -0
  42. package/dist/builtin/workflows/src/extension/workflow-module-loader.ts +6 -12
  43. package/dist/builtin/workflows/src/extension/workflow-schema.ts +3 -2
  44. package/dist/builtin/workflows/src/index.ts +0 -5
  45. package/dist/builtin/workflows/src/runs/foreground/executor.ts +23 -9
  46. package/dist/builtin/workflows/src/runs/foreground/stage-runner.ts +33 -5
  47. package/dist/builtin/workflows/src/runs/shared/model-fallback.ts +72 -11
  48. package/dist/builtin/workflows/src/sdk-surface.ts +12 -2
  49. package/dist/builtin/workflows/src/shared/authoring-contract.d.ts +523 -0
  50. package/dist/builtin/workflows/src/shared/render-inputs-schema.ts +1 -1
  51. package/dist/builtin/workflows/src/shared/types.ts +65 -350
  52. package/dist/builtin/workflows/src/workflows/define-workflow.ts +59 -44
  53. package/dist/core/atomic-guide-command.d.ts.map +1 -1
  54. package/dist/core/atomic-guide-command.js +1 -1
  55. package/dist/core/atomic-guide-command.js.map +1 -1
  56. package/dist/index.d.ts +1 -0
  57. package/dist/modes/interactive/components/chat-message-renderer.d.ts +1 -0
  58. package/dist/modes/interactive/components/chat-message-renderer.d.ts.map +1 -1
  59. package/dist/modes/interactive/components/chat-message-renderer.js +13 -1
  60. package/dist/modes/interactive/components/chat-message-renderer.js.map +1 -1
  61. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  62. package/dist/modes/interactive/interactive-mode.js +1 -1
  63. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  64. package/dist/utils/changelog.d.ts.map +1 -1
  65. package/dist/utils/changelog.js +23 -16
  66. package/dist/utils/changelog.js.map +1 -1
  67. package/docs/extensions.md +2 -2
  68. package/docs/packages.md +8 -4
  69. package/docs/subagents.md +30 -0
  70. package/docs/workflows.md +62 -21
  71. package/package.json +20 -1
  72. package/dist/builtin/workflows/src/runs/shared/workflow-runner.ts +0 -335
@@ -7,6 +7,7 @@ import { mkdir, writeFile } from "node:fs/promises";
7
7
  import { basename, dirname, extname, isAbsolute, join, resolve } from "node:path";
8
8
  import { CONFIG_DIR_NAME, createAskUserQuestionToolDefinition, isCodexFastModeCandidateModelId } from "@bastani/atomic";
9
9
  import { stageUiBroker } from "../../shared/stage-ui-broker.js";
10
+ import { isBrandedWorkflowDefinition, stampWorkflowDefinition } from "../../workflows/define-workflow.js";
10
11
  import { buildStagePromptAdapter } from "../../shared/stage-prompt.js";
11
12
  import type {
12
13
  WorkflowDefinition,
@@ -43,6 +44,7 @@ import type {
43
44
  WorkflowSerializableValue,
44
45
  } from "../../shared/types.js";
45
46
  import type { InternalStageContext, StageAdapters } from "./stage-runner.js";
47
+ import type * as AuthoringContract from "../../shared/authoring-contract.js";
46
48
  import type {
47
49
  RunStatus,
48
50
  StageNotice,
@@ -102,7 +104,7 @@ export interface RunContinuationOpts {
102
104
  readonly resumeFromStageId: string;
103
105
  }
104
106
 
105
- export interface RunOpts {
107
+ export interface RunOpts extends Omit<AuthoringContract.RunOpts, "adapters" | "store" | "cancellation" | "overlay" | "registry" | "stageControlRegistry" | "continuation" | "onRunStart" | "onStageStart" | "onStageEnd" | "onRunEnd"> {
106
108
  adapters?: StageAdapters;
107
109
  /** Invocation working directory exposed to workflow definitions as ctx.cwd. */
108
110
  cwd?: string;
@@ -1152,7 +1154,7 @@ function defineDirectWorkflow(
1152
1154
  name: string,
1153
1155
  runFn: WorkflowDefinition["run"],
1154
1156
  ): WorkflowDefinition {
1155
- return Object.freeze({
1157
+ const definition = {
1156
1158
  __piWorkflow: true,
1157
1159
  name,
1158
1160
  normalizedName: name,
@@ -1160,7 +1162,10 @@ function defineDirectWorkflow(
1160
1162
  inputs: Object.freeze({}),
1161
1163
  outputs: DIRECT_WORKFLOW_OUTPUTS,
1162
1164
  run: runFn,
1163
- });
1165
+ } as WorkflowDefinition;
1166
+ // Stamp before freezing so the WeakSet brand can be attached.
1167
+ stampWorkflowDefinition(definition);
1168
+ return Object.freeze(definition);
1164
1169
  }
1165
1170
 
1166
1171
  /**
@@ -1805,19 +1810,24 @@ function workflowChildSerializationMessage(err: unknown): string {
1805
1810
  }
1806
1811
 
1807
1812
  function isWorkflowDefinition(value: unknown): value is WorkflowDefinition {
1808
- if (value === null || typeof value !== "object") return false;
1813
+ if (!isBrandedWorkflowDefinition(value)) return false;
1809
1814
  const record = value as Partial<WorkflowDefinition>;
1810
1815
  return record.__piWorkflow === true &&
1811
1816
  typeof record.name === "string" && record.name.trim().length > 0 &&
1812
1817
  typeof record.normalizedName === "string" && record.normalizedName.trim().length > 0 &&
1813
1818
  typeof record.run === "function" &&
1814
- // Compiled definitions always set `inputs: {}`; guard it so a handcrafted
1815
- // object that passes the sentinel still fails here with the clear "requires
1816
- // a compiled workflow definition" error rather than crashing later inside
1817
- // resolveAndValidateInputs(child.inputs, ...) on `Object.entries(undefined)`.
1818
1819
  typeof record.inputs === "object" && record.inputs !== null;
1819
1820
  }
1820
1821
 
1822
+ function workflowDefinitionRequirementMessage(callSite: string, value: unknown): string {
1823
+ // isWorkflowDefinition already failed; this extra sentinel check narrows the
1824
+ // diagnostic for forged legacy literals versus unrelated values.
1825
+ if (value !== null && typeof value === "object" && (value as { __piWorkflow?: unknown }).__piWorkflow === true) {
1826
+ return `atomic-workflows: ${callSite} requires a compiled workflow definition produced by defineWorkflow(...).compile(); hand-rolled __piWorkflow objects are not supported`;
1827
+ }
1828
+ return `atomic-workflows: ${callSite} requires a compiled workflow definition`;
1829
+ }
1830
+
1821
1831
  function cloneWorkflowChildReplaySnapshot(snapshot: WorkflowChildReplaySnapshot): WorkflowChildReplaySnapshot {
1822
1832
  return {
1823
1833
  alias: snapshot.alias,
@@ -1859,6 +1869,10 @@ export async function run<TInputs extends WorkflowInputValues>(
1859
1869
  inputs: Readonly<Record<string, unknown>>,
1860
1870
  opts: RunOpts = {},
1861
1871
  ): Promise<RunResult> {
1872
+ if (!isWorkflowDefinition(def)) {
1873
+ throw new Error(workflowDefinitionRequirementMessage("run(definition, inputs)", def));
1874
+ }
1875
+
1862
1876
  const activeStore = opts.store ?? defaultStore;
1863
1877
  const adapters = opts.adapters ?? {};
1864
1878
  if (opts.usePromptNodesForUi === true && opts.ui !== undefined) {
@@ -3221,7 +3235,7 @@ export async function run<TInputs extends WorkflowInputValues>(
3221
3235
  // declared output contract is validated dynamically by the child run and
3222
3236
  // selectWorkflowOutputs, so the typed result is reconstructed via casts.
3223
3237
  if (!isWorkflowDefinition(child)) {
3224
- throw new Error("atomic-workflows: ctx.workflow(definition) requires a compiled workflow definition");
3238
+ throw new Error(workflowDefinitionRequirementMessage("ctx.workflow(definition)", child));
3225
3239
  }
3226
3240
  const childName = child.normalizedName;
3227
3241
  const boundaryName = options.stageName ?? `workflow:${childName}`;
@@ -64,7 +64,7 @@ export interface StageSessionRuntime {
64
64
  getLastAssistantText?: () => string | undefined;
65
65
  }
66
66
 
67
- export type StageSessionCreateOptions = CreateAgentSessionOptions & Pick<StageOptions, "mcp" | "fallbackModels">;
67
+ export type StageSessionCreateOptions = CreateAgentSessionOptions & Pick<StageOptions, "mcp" | "fallbackModels" | "fallbackThinkingLevels">;
68
68
 
69
69
  type WorkflowFastModeSettings = {
70
70
  readonly chat: boolean;
@@ -169,6 +169,7 @@ function stripWorkflowOnlyOptions(options: StageOptions | undefined): CreateAgen
169
169
  const {
170
170
  mcp: _mcp,
171
171
  fallbackModels: _fallbackModels,
172
+ fallbackThinkingLevels: _fallbackThinkingLevels,
172
173
  context,
173
174
  forkFromSessionFile,
174
175
  sessionDir,
@@ -549,6 +550,7 @@ export function createStageContext(opts: StageRunnerOpts): InternalStageContext
549
550
  candidatesPromise = buildModelCandidatesFromCatalog({
550
551
  primaryModel: stageOptions?.model,
551
552
  fallbackModels: stageOptions?.fallbackModels,
553
+ fallbackThinkingLevels: stageOptions?.fallbackThinkingLevels,
552
554
  catalog: modelCatalog,
553
555
  });
554
556
  }
@@ -557,7 +559,13 @@ export function createStageContext(opts: StageRunnerOpts): InternalStageContext
557
559
 
558
560
  function stageOptionsForCandidate(candidate: WorkflowResolvedModelCandidate | undefined): StageOptions | undefined {
559
561
  if (candidate === undefined) return stageOptions;
560
- return { ...(stageOptions ?? {}), model: candidate.value, fallbackModels: undefined };
562
+ return {
563
+ ...(stageOptions ?? {}),
564
+ model: candidate.value,
565
+ ...(candidate.reasoningLevel !== undefined ? { thinkingLevel: candidate.reasoningLevel } : {}),
566
+ fallbackModels: undefined,
567
+ fallbackThinkingLevels: undefined,
568
+ };
561
569
  }
562
570
 
563
571
  let sessionSettingsManager: WorkflowFastModeSettingsManager | undefined;
@@ -591,6 +599,25 @@ export function createStageContext(opts: StageRunnerOpts): InternalStageContext
591
599
  return { session: created };
592
600
  }
593
601
 
602
+ function effectiveCandidateReasoning(candidate: WorkflowResolvedModelCandidate): StageOptions["thinkingLevel"] | undefined {
603
+ return candidate.reasoningLevel ?? stageOptions?.thinkingLevel;
604
+ }
605
+
606
+ function modelAttemptReasoning(candidate: WorkflowResolvedModelCandidate): Pick<WorkflowModelAttempt, "reasoningLevel"> {
607
+ const reasoningLevel = effectiveCandidateReasoning(candidate);
608
+ return reasoningLevel !== undefined ? { reasoningLevel } : {};
609
+ }
610
+
611
+ function applyCandidateThinking(candidate: WorkflowResolvedModelCandidate | undefined): void {
612
+ pendingThinkingLevel = candidate === undefined
613
+ ? stageOptions?.thinkingLevel
614
+ : effectiveCandidateReasoning(candidate);
615
+ }
616
+
617
+ function candidateLabel(candidate: WorkflowResolvedModelCandidate): string {
618
+ return candidate.reasoningLevel !== undefined ? `${candidate.id}:${candidate.reasoningLevel}` : candidate.id;
619
+ }
620
+
594
621
  function attachSession(created: StageSessionRuntime | StageSessionCreateResult): StageSessionRuntime {
595
622
  const result = normalizeSessionCreateResult(created);
596
623
  session = result.session;
@@ -612,6 +639,7 @@ export function createStageContext(opts: StageRunnerOpts): InternalStageContext
612
639
  candidate: WorkflowResolvedModelCandidate | undefined,
613
640
  consumer: AgentSessionConsumer,
614
641
  ): Promise<StageSessionRuntime> {
642
+ applyCandidateThinking(candidate);
615
643
  const created = adapters.agentSession
616
644
  ? await adapters.agentSession.create(stripWorkflowOnlyOptions(stageOptionsForCandidate(candidate)) as StageSessionCreateOptions, {
617
645
  ...meta,
@@ -716,16 +744,16 @@ export function createStageContext(opts: StageRunnerOpts): InternalStageContext
716
744
  notifyModelFallbackMetaChange();
717
745
  try {
718
746
  await promptWithPauseResume(activeSession, text, sdkOptions);
719
- modelAttempts.push({ model: candidate.id, success: true });
747
+ modelAttempts.push({ model: candidate.id, success: true, ...modelAttemptReasoning(candidate) });
720
748
  return;
721
749
  } catch (err) {
722
750
  const message = errorMessage(err);
723
- modelAttempts.push({ model: candidate.id, success: false, error: message });
751
+ modelAttempts.push({ model: candidate.id, success: false, ...modelAttemptReasoning(candidate), error: message });
724
752
  if (signal?.aborted || !isRetryableModelFailure(message) || index === candidates.length - 1) {
725
753
  throw err;
726
754
  }
727
755
  const nextCandidate = candidates[index + 1]!;
728
- modelWarnings.push(`[fallback] ${candidate.id} failed: ${message}. Retrying with ${nextCandidate.id}.`);
756
+ modelWarnings.push(`[fallback] ${candidateLabel(candidate)} failed: ${message}. Retrying with ${candidateLabel(nextCandidate)}.`);
729
757
  await disposeCurrentSession();
730
758
  index += 1;
731
759
  }
@@ -3,11 +3,38 @@ import type {
3
3
  WorkflowModelCatalogPort,
4
4
  WorkflowModelInfo,
5
5
  WorkflowModelValue,
6
+ WorkflowThinkingLevel,
6
7
  } from "../../shared/types.js";
7
8
 
8
9
  export interface WorkflowResolvedModelCandidate {
9
10
  readonly id: string;
10
11
  readonly value: WorkflowModelValue;
12
+ readonly reasoningLevel?: WorkflowThinkingLevel;
13
+ }
14
+
15
+ function makeCandidate(
16
+ id: string,
17
+ value: WorkflowModelValue,
18
+ level: WorkflowThinkingLevel | undefined,
19
+ ): WorkflowResolvedModelCandidate {
20
+ return level !== undefined ? { id, value, reasoningLevel: level } : { id, value };
21
+ }
22
+
23
+ const WORKFLOW_THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh"] as const satisfies readonly WorkflowThinkingLevel[];
24
+ const WORKFLOW_THINKING_LEVEL_SET: ReadonlySet<string> = new Set(WORKFLOW_THINKING_LEVELS);
25
+
26
+ export function splitReasoningSuffix(model: string): { readonly baseModel: string; readonly level?: WorkflowThinkingLevel } {
27
+ const index = model.lastIndexOf(":");
28
+ if (index < 0) return { baseModel: model };
29
+ const suffix = model.slice(index + 1);
30
+ if (WORKFLOW_THINKING_LEVEL_SET.has(suffix)) {
31
+ return { baseModel: model.slice(0, index), level: suffix as WorkflowThinkingLevel };
32
+ }
33
+ return { baseModel: model };
34
+ }
35
+
36
+ function candidateKey(candidate: WorkflowResolvedModelCandidate): string {
37
+ return `${candidate.id}::${candidate.reasoningLevel ?? ""}`;
11
38
  }
12
39
 
13
40
  interface ModelResolutionFailure {
@@ -72,35 +99,46 @@ function resolveStringModel(
72
99
  ): WorkflowResolvedModelCandidate | ModelResolutionFailure {
73
100
  const input = rawInput.trim();
74
101
  if (!input) return { input: rawInput, reason: "empty model id" };
102
+ const { baseModel, level } = splitReasoningSuffix(input);
75
103
 
76
104
  if (availableModels === undefined) {
77
- return { id: input, value: input };
105
+ return makeCandidate(baseModel, baseModel, level);
78
106
  }
79
107
 
80
108
  const models = uniqueByFullId(availableModels);
81
- const explicit = models.find((model) => model.fullId === input);
109
+ const explicit = models.find((model) => model.fullId === baseModel);
82
110
  if (explicit !== undefined) {
83
- return { id: explicit.fullId, value: explicit.model ?? explicit.fullId };
111
+ return makeCandidate(explicit.fullId, explicit.model ?? explicit.fullId, level);
84
112
  }
85
113
 
86
- if (input.includes("/")) {
87
- return { input, reason: "not available" };
114
+ if (baseModel.includes("/")) {
115
+ // Trust an explicit provider/model id even when the live catalog does not
116
+ // list it, mirroring the subagent resolver (resolveModelCandidate's
117
+ // `if (model.includes("/")) return model;`). The workflow catalog
118
+ // (ctx.modelRegistry.getAvailable()) can legitimately be a partial view
119
+ // (auth/provider gating, freshly added models), so treating an absent
120
+ // fully-qualified id as a hard failure made buildModelCandidates throw and
121
+ // collapse the whole ordered candidate list down to just the user's
122
+ // currentModel — discarding the workflow's defined primary and fallbacks.
123
+ // Pass it through with the reasoning suffix split off; the runtime fallback
124
+ // loop skips it only if the SDK genuinely cannot create a session for it.
125
+ return makeCandidate(baseModel, baseModel, level);
88
126
  }
89
127
 
90
- const byBareId = models.filter((model) => model.id === input);
128
+ const byBareId = models.filter((model) => model.id === baseModel);
91
129
  if (byBareId.length === 0) {
92
130
  return { input, reason: "not available" };
93
131
  }
94
132
  if (byBareId.length === 1) {
95
133
  const only = byBareId[0]!;
96
- return { id: only.fullId, value: only.model ?? only.fullId };
134
+ return makeCandidate(only.fullId, only.model ?? only.fullId, level);
97
135
  }
98
136
 
99
137
  const preferred = preferredProvider === undefined
100
138
  ? undefined
101
139
  : byBareId.find((model) => model.provider === preferredProvider);
102
140
  if (preferred !== undefined) {
103
- return { id: preferred.fullId, value: preferred.model ?? preferred.fullId };
141
+ return makeCandidate(preferred.fullId, preferred.model ?? preferred.fullId, level);
104
142
  }
105
143
 
106
144
  return {
@@ -127,13 +165,30 @@ function isFailure(value: WorkflowResolvedModelCandidate | ModelResolutionFailur
127
165
  export function buildModelCandidates(input: {
128
166
  readonly primaryModel?: WorkflowModelValue;
129
167
  readonly fallbackModels?: readonly string[];
168
+ readonly fallbackThinkingLevels?: readonly string[];
130
169
  readonly currentModel?: WorkflowModelValue;
131
170
  readonly availableModels?: readonly WorkflowModelInfo[];
132
171
  readonly preferredProvider?: string;
133
172
  }): WorkflowResolvedModelCandidate[] {
134
173
  const rawValues: WorkflowModelValue[] = [];
135
174
  if (input.primaryModel !== undefined) rawValues.push(input.primaryModel);
136
- rawValues.push(...(input.fallbackModels ?? []));
175
+ for (const [index, fallback] of (input.fallbackModels ?? []).entries()) {
176
+ // Trim once up front so the suffix split, the validation error input, and the
177
+ // compat concatenation all operate on the same value. Concatenating the raw
178
+ // (untrimmed) fallback would push trailing whitespace into the interior of
179
+ // `id:level`, which `resolveStringModel` can no longer trim away.
180
+ const trimmedFallback = fallback.trim();
181
+ const split = splitReasoningSuffix(trimmedFallback);
182
+ const compatLevel = input.fallbackThinkingLevels?.[index];
183
+ if (split.level === undefined && compatLevel !== undefined) {
184
+ if (!WORKFLOW_THINKING_LEVEL_SET.has(compatLevel)) {
185
+ throw new WorkflowModelValidationError([{ input: trimmedFallback, reason: `invalid fallbackThinkingLevels[${index}] "${compatLevel}"; expected one of ${WORKFLOW_THINKING_LEVELS.join(", ")}` }]);
186
+ }
187
+ rawValues.push(`${trimmedFallback}:${compatLevel}`);
188
+ } else {
189
+ rawValues.push(trimmedFallback);
190
+ }
191
+ }
137
192
  if (input.currentModel !== undefined) rawValues.push(input.currentModel);
138
193
 
139
194
  const failures: ModelResolutionFailure[] = [];
@@ -145,8 +200,9 @@ export function buildModelCandidates(input: {
145
200
  failures.push(resolved);
146
201
  continue;
147
202
  }
148
- if (seen.has(resolved.id)) continue;
149
- seen.add(resolved.id);
203
+ const key = candidateKey(resolved);
204
+ if (seen.has(key)) continue;
205
+ seen.add(key);
150
206
  candidates.push(resolved);
151
207
  }
152
208
 
@@ -165,6 +221,7 @@ function catalogUnavailableWarning(): string {
165
221
  export async function buildModelCandidatesFromCatalog(input: {
166
222
  readonly primaryModel?: WorkflowModelValue;
167
223
  readonly fallbackModels?: readonly string[];
224
+ readonly fallbackThinkingLevels?: readonly string[];
168
225
  readonly catalog?: WorkflowModelCatalogPort;
169
226
  }): Promise<WorkflowResolvedModelCandidate[]> {
170
227
  const hasExplicitModel = input.primaryModel !== undefined || (input.fallbackModels?.length ?? 0) > 0;
@@ -174,6 +231,7 @@ export async function buildModelCandidatesFromCatalog(input: {
174
231
  return buildModelCandidates({
175
232
  primaryModel: input.primaryModel,
176
233
  fallbackModels: input.fallbackModels,
234
+ fallbackThinkingLevels: input.fallbackThinkingLevels,
177
235
  });
178
236
  }
179
237
 
@@ -182,6 +240,7 @@ export async function buildModelCandidatesFromCatalog(input: {
182
240
  return buildModelCandidates({
183
241
  primaryModel: input.primaryModel,
184
242
  fallbackModels: input.fallbackModels,
243
+ fallbackThinkingLevels: input.fallbackThinkingLevels,
185
244
  currentModel: input.catalog.currentModel,
186
245
  availableModels,
187
246
  preferredProvider: input.catalog.preferredProvider,
@@ -199,6 +258,7 @@ export async function validateWorkflowModels(input: {
199
258
  readonly requests: readonly {
200
259
  readonly model?: WorkflowModelValue;
201
260
  readonly fallbackModels?: readonly string[];
261
+ readonly fallbackThinkingLevels?: readonly string[];
202
262
  }[];
203
263
  readonly catalog?: WorkflowModelCatalogPort;
204
264
  }): Promise<readonly string[]> {
@@ -230,6 +290,7 @@ export async function validateWorkflowModels(input: {
230
290
  buildModelCandidates({
231
291
  primaryModel: request.model,
232
292
  fallbackModels: request.fallbackModels,
293
+ fallbackThinkingLevels: request.fallbackThinkingLevels,
233
294
  currentModel: input.catalog?.currentModel,
234
295
  availableModels,
235
296
  preferredProvider: input.catalog?.preferredProvider,
@@ -2,12 +2,22 @@
2
2
  * Non-cyclic public SDK surface for @bastani/workflows.
3
3
  *
4
4
  * Keep public runtime exports here when they are safe to load during workflow
5
- * discovery. The package root re-exports this module and adds runWorkflow,
6
- * which is intentionally excluded because workflow-runner imports discovery.ts.
5
+ * discovery. The package root re-exports this module directly.
7
6
  */
8
7
 
9
8
  export { defineWorkflow } from "./workflows/define-workflow.js";
10
9
 
10
+ const REMOVED_RUN_WORKFLOW_MESSAGE =
11
+ "@bastani/workflows no longer exports runWorkflow; author workflows with defineWorkflow(...).compile()";
12
+
13
+ /**
14
+ * @deprecated Removed imperative workflow API. Kept as a runtime migration
15
+ * stub so older workflow modules fail at the call site with a clear message.
16
+ */
17
+ export const runWorkflow: never = (() => {
18
+ throw new Error(REMOVED_RUN_WORKFLOW_MESSAGE);
19
+ }) as never;
20
+
11
21
  // TypeBox authoring surface so jiti-loaded workflows can `import { Type } from
12
22
  // "@bastani/workflows"` (the virtual module is built from this file).
13
23
  export { Type } from "typebox";