@bastani/atomic 0.8.23-0 → 0.8.24-alpha.1
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.
- package/CHANGELOG.md +21 -0
- package/dist/builtin/intercom/CHANGELOG.md +13 -0
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/mcp/CHANGELOG.md +13 -0
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +28 -0
- package/dist/builtin/subagents/README.md +16 -0
- package/dist/builtin/subagents/agents/code-simplifier.md +2 -3
- package/dist/builtin/subagents/agents/codebase-analyzer.md +2 -3
- package/dist/builtin/subagents/agents/codebase-locator.md +2 -3
- package/dist/builtin/subagents/agents/codebase-online-researcher.md +2 -3
- package/dist/builtin/subagents/agents/codebase-pattern-finder.md +2 -3
- package/dist/builtin/subagents/agents/codebase-research-analyzer.md +2 -3
- package/dist/builtin/subagents/agents/codebase-research-locator.md +2 -3
- package/dist/builtin/subagents/agents/debugger.md +2 -3
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/subagents/skills/subagent/SKILL.md +6 -0
- package/dist/builtin/subagents/src/agents/agent-serializer.ts +3 -0
- package/dist/builtin/subagents/src/agents/agents.ts +20 -1
- package/dist/builtin/subagents/src/runs/background/async-execution.ts +1 -1
- package/dist/builtin/subagents/src/runs/background/subagent-runner.ts +3 -1
- package/dist/builtin/subagents/src/runs/foreground/chain-clarify.ts +7 -7
- package/dist/builtin/subagents/src/runs/foreground/execution.ts +5 -1
- package/dist/builtin/subagents/src/runs/shared/model-fallback.ts +9 -10
- package/dist/builtin/subagents/src/shared/types.ts +1 -0
- package/dist/builtin/web-access/CHANGELOG.md +13 -0
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +31 -0
- package/dist/builtin/workflows/README.md +38 -41
- package/dist/builtin/workflows/builtin/deep-research-codebase.d.ts +35 -0
- package/dist/builtin/workflows/builtin/deep-research-codebase.ts +11 -14
- package/dist/builtin/workflows/builtin/goal.d.ts +46 -0
- package/dist/builtin/workflows/builtin/goal.ts +10 -12
- package/dist/builtin/workflows/builtin/index.d.ts +136 -0
- package/dist/builtin/workflows/builtin/open-claude-design.d.ts +44 -0
- package/dist/builtin/workflows/builtin/open-claude-design.ts +19 -20
- package/dist/builtin/workflows/builtin/ralph.d.ts +36 -0
- package/dist/builtin/workflows/builtin/ralph.ts +20 -24
- package/dist/builtin/workflows/package.json +15 -5
- package/dist/builtin/workflows/src/authoring.ts +410 -0
- package/dist/builtin/workflows/src/extension/workflow-module-loader.ts +6 -12
- package/dist/builtin/workflows/src/extension/workflow-schema.ts +3 -2
- package/dist/builtin/workflows/src/index.ts +0 -5
- package/dist/builtin/workflows/src/runs/foreground/executor.ts +23 -9
- package/dist/builtin/workflows/src/runs/foreground/stage-runner.ts +33 -5
- package/dist/builtin/workflows/src/runs/shared/model-fallback.ts +61 -10
- package/dist/builtin/workflows/src/sdk-surface.ts +12 -2
- package/dist/builtin/workflows/src/shared/authoring-contract.ts +660 -0
- package/dist/builtin/workflows/src/shared/render-inputs-schema.ts +1 -1
- package/dist/builtin/workflows/src/shared/types.ts +65 -350
- package/dist/builtin/workflows/src/workflows/define-workflow.ts +59 -44
- package/dist/core/atomic-guide-command.d.ts.map +1 -1
- package/dist/core/atomic-guide-command.js +1 -1
- package/dist/core/atomic-guide-command.js.map +1 -1
- package/dist/modes/interactive/components/chat-message-renderer.d.ts +1 -0
- package/dist/modes/interactive/components/chat-message-renderer.d.ts.map +1 -1
- package/dist/modes/interactive/components/chat-message-renderer.js +13 -1
- package/dist/modes/interactive/components/chat-message-renderer.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +1 -1
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/utils/changelog.d.ts.map +1 -1
- package/dist/utils/changelog.js +23 -16
- package/dist/utils/changelog.js.map +1 -1
- package/docs/extensions.md +2 -2
- package/docs/packages.md +8 -4
- package/docs/subagents.md +30 -0
- package/docs/workflows.md +39 -21
- package/package.json +1 -1
- 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
|
-
|
|
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
|
|
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("
|
|
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 {
|
|
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
|
|
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,36 @@ 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
|
|
105
|
+
return makeCandidate(baseModel, baseModel, level);
|
|
78
106
|
}
|
|
79
107
|
|
|
80
108
|
const models = uniqueByFullId(availableModels);
|
|
81
|
-
const explicit = models.find((model) => model.fullId ===
|
|
109
|
+
const explicit = models.find((model) => model.fullId === baseModel);
|
|
82
110
|
if (explicit !== undefined) {
|
|
83
|
-
return
|
|
111
|
+
return makeCandidate(explicit.fullId, explicit.model ?? explicit.fullId, level);
|
|
84
112
|
}
|
|
85
113
|
|
|
86
|
-
if (
|
|
114
|
+
if (baseModel.includes("/")) {
|
|
87
115
|
return { input, reason: "not available" };
|
|
88
116
|
}
|
|
89
117
|
|
|
90
|
-
const byBareId = models.filter((model) => model.id ===
|
|
118
|
+
const byBareId = models.filter((model) => model.id === baseModel);
|
|
91
119
|
if (byBareId.length === 0) {
|
|
92
120
|
return { input, reason: "not available" };
|
|
93
121
|
}
|
|
94
122
|
if (byBareId.length === 1) {
|
|
95
123
|
const only = byBareId[0]!;
|
|
96
|
-
return
|
|
124
|
+
return makeCandidate(only.fullId, only.model ?? only.fullId, level);
|
|
97
125
|
}
|
|
98
126
|
|
|
99
127
|
const preferred = preferredProvider === undefined
|
|
100
128
|
? undefined
|
|
101
129
|
: byBareId.find((model) => model.provider === preferredProvider);
|
|
102
130
|
if (preferred !== undefined) {
|
|
103
|
-
return
|
|
131
|
+
return makeCandidate(preferred.fullId, preferred.model ?? preferred.fullId, level);
|
|
104
132
|
}
|
|
105
133
|
|
|
106
134
|
return {
|
|
@@ -127,13 +155,30 @@ function isFailure(value: WorkflowResolvedModelCandidate | ModelResolutionFailur
|
|
|
127
155
|
export function buildModelCandidates(input: {
|
|
128
156
|
readonly primaryModel?: WorkflowModelValue;
|
|
129
157
|
readonly fallbackModels?: readonly string[];
|
|
158
|
+
readonly fallbackThinkingLevels?: readonly string[];
|
|
130
159
|
readonly currentModel?: WorkflowModelValue;
|
|
131
160
|
readonly availableModels?: readonly WorkflowModelInfo[];
|
|
132
161
|
readonly preferredProvider?: string;
|
|
133
162
|
}): WorkflowResolvedModelCandidate[] {
|
|
134
163
|
const rawValues: WorkflowModelValue[] = [];
|
|
135
164
|
if (input.primaryModel !== undefined) rawValues.push(input.primaryModel);
|
|
136
|
-
|
|
165
|
+
for (const [index, fallback] of (input.fallbackModels ?? []).entries()) {
|
|
166
|
+
// Trim once up front so the suffix split, the validation error input, and the
|
|
167
|
+
// compat concatenation all operate on the same value. Concatenating the raw
|
|
168
|
+
// (untrimmed) fallback would push trailing whitespace into the interior of
|
|
169
|
+
// `id:level`, which `resolveStringModel` can no longer trim away.
|
|
170
|
+
const trimmedFallback = fallback.trim();
|
|
171
|
+
const split = splitReasoningSuffix(trimmedFallback);
|
|
172
|
+
const compatLevel = input.fallbackThinkingLevels?.[index];
|
|
173
|
+
if (split.level === undefined && compatLevel !== undefined) {
|
|
174
|
+
if (!WORKFLOW_THINKING_LEVEL_SET.has(compatLevel)) {
|
|
175
|
+
throw new WorkflowModelValidationError([{ input: trimmedFallback, reason: `invalid fallbackThinkingLevels[${index}] "${compatLevel}"; expected one of ${WORKFLOW_THINKING_LEVELS.join(", ")}` }]);
|
|
176
|
+
}
|
|
177
|
+
rawValues.push(`${trimmedFallback}:${compatLevel}`);
|
|
178
|
+
} else {
|
|
179
|
+
rawValues.push(trimmedFallback);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
137
182
|
if (input.currentModel !== undefined) rawValues.push(input.currentModel);
|
|
138
183
|
|
|
139
184
|
const failures: ModelResolutionFailure[] = [];
|
|
@@ -145,8 +190,9 @@ export function buildModelCandidates(input: {
|
|
|
145
190
|
failures.push(resolved);
|
|
146
191
|
continue;
|
|
147
192
|
}
|
|
148
|
-
|
|
149
|
-
seen.
|
|
193
|
+
const key = candidateKey(resolved);
|
|
194
|
+
if (seen.has(key)) continue;
|
|
195
|
+
seen.add(key);
|
|
150
196
|
candidates.push(resolved);
|
|
151
197
|
}
|
|
152
198
|
|
|
@@ -165,6 +211,7 @@ function catalogUnavailableWarning(): string {
|
|
|
165
211
|
export async function buildModelCandidatesFromCatalog(input: {
|
|
166
212
|
readonly primaryModel?: WorkflowModelValue;
|
|
167
213
|
readonly fallbackModels?: readonly string[];
|
|
214
|
+
readonly fallbackThinkingLevels?: readonly string[];
|
|
168
215
|
readonly catalog?: WorkflowModelCatalogPort;
|
|
169
216
|
}): Promise<WorkflowResolvedModelCandidate[]> {
|
|
170
217
|
const hasExplicitModel = input.primaryModel !== undefined || (input.fallbackModels?.length ?? 0) > 0;
|
|
@@ -174,6 +221,7 @@ export async function buildModelCandidatesFromCatalog(input: {
|
|
|
174
221
|
return buildModelCandidates({
|
|
175
222
|
primaryModel: input.primaryModel,
|
|
176
223
|
fallbackModels: input.fallbackModels,
|
|
224
|
+
fallbackThinkingLevels: input.fallbackThinkingLevels,
|
|
177
225
|
});
|
|
178
226
|
}
|
|
179
227
|
|
|
@@ -182,6 +230,7 @@ export async function buildModelCandidatesFromCatalog(input: {
|
|
|
182
230
|
return buildModelCandidates({
|
|
183
231
|
primaryModel: input.primaryModel,
|
|
184
232
|
fallbackModels: input.fallbackModels,
|
|
233
|
+
fallbackThinkingLevels: input.fallbackThinkingLevels,
|
|
185
234
|
currentModel: input.catalog.currentModel,
|
|
186
235
|
availableModels,
|
|
187
236
|
preferredProvider: input.catalog.preferredProvider,
|
|
@@ -199,6 +248,7 @@ export async function validateWorkflowModels(input: {
|
|
|
199
248
|
readonly requests: readonly {
|
|
200
249
|
readonly model?: WorkflowModelValue;
|
|
201
250
|
readonly fallbackModels?: readonly string[];
|
|
251
|
+
readonly fallbackThinkingLevels?: readonly string[];
|
|
202
252
|
}[];
|
|
203
253
|
readonly catalog?: WorkflowModelCatalogPort;
|
|
204
254
|
}): Promise<readonly string[]> {
|
|
@@ -230,6 +280,7 @@ export async function validateWorkflowModels(input: {
|
|
|
230
280
|
buildModelCandidates({
|
|
231
281
|
primaryModel: request.model,
|
|
232
282
|
fallbackModels: request.fallbackModels,
|
|
283
|
+
fallbackThinkingLevels: request.fallbackThinkingLevels,
|
|
233
284
|
currentModel: input.catalog?.currentModel,
|
|
234
285
|
availableModels,
|
|
235
286
|
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
|
|
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";
|