@sweny-ai/core 0.1.102 → 0.1.103
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/dist/browser.d.ts +3 -2
- package/dist/browser.js +6 -1
- package/dist/claude.d.ts +66 -3
- package/dist/claude.js +334 -29
- package/dist/cli/check.d.ts +7 -0
- package/dist/cli/check.js +91 -5
- package/dist/cli/config-file.js +5 -1
- package/dist/cli/config.d.ts +11 -0
- package/dist/cli/config.js +19 -1
- package/dist/cli/e2e.d.ts +14 -1
- package/dist/cli/e2e.js +40 -6
- package/dist/cli/main.d.ts +3 -1
- package/dist/cli/main.js +26 -16
- package/dist/cli/new.js +7 -0
- package/dist/eval/value.js +12 -1
- package/dist/executor.d.ts +16 -0
- package/dist/executor.js +81 -18
- package/dist/index.d.ts +8 -4
- package/dist/index.js +10 -2
- package/dist/inputs.js +18 -0
- package/dist/loader.js +15 -1
- package/dist/model.d.ts +11 -0
- package/dist/model.js +19 -0
- package/dist/retry.d.ts +5 -0
- package/dist/retry.js +2 -2
- package/dist/schema.d.ts +163 -6
- package/dist/schema.js +125 -10
- package/dist/skills/custom-loader.d.ts +3 -3
- package/dist/skills/custom-loader.js +50 -14
- package/dist/skills/github.js +8 -2
- package/dist/skills/linear.js +26 -0
- package/dist/skills/notification.d.ts +17 -0
- package/dist/skills/notification.js +67 -5
- package/dist/source-resolver.js +231 -6
- package/dist/sources.d.ts +33 -0
- package/dist/types.d.ts +29 -1
- package/dist/types.js +17 -0
- package/package.json +4 -4
package/dist/executor.js
CHANGED
|
@@ -17,7 +17,15 @@ import { resolveSources } from "./source-resolver.js";
|
|
|
17
17
|
import { evaluateAll, aggregateEval } from "./eval/index.js";
|
|
18
18
|
import { evaluateRequires } from "./requires.js";
|
|
19
19
|
import { buildRetryPreamble } from "./retry.js";
|
|
20
|
+
import { resolveExecutionModel } from "./model.js";
|
|
20
21
|
import { buildToolAliases } from "./skills/index.js";
|
|
22
|
+
/**
|
|
23
|
+
* Default workflow-level step budget. Counts total node executions across the
|
|
24
|
+
* whole run (loop iterations included). Generous enough for legitimate deep
|
|
25
|
+
* workflows with controlled cycles, tight enough to terminate a runaway loop
|
|
26
|
+
* before it burns meaningful model spend.
|
|
27
|
+
*/
|
|
28
|
+
export const DEFAULT_MAX_STEPS = 200;
|
|
21
29
|
/**
|
|
22
30
|
* Execute a workflow from entry to completion.
|
|
23
31
|
*
|
|
@@ -31,11 +39,13 @@ export async function execute(workflow, input, options) {
|
|
|
31
39
|
// Inline skills (instruction/mcp only) become Skill objects with empty tools/config.
|
|
32
40
|
// The caller's skill map takes precedence — inline skills only fill gaps.
|
|
33
41
|
const skills = mergeInlineSkills(options.skills, workflow.skills);
|
|
34
|
-
const config = resolveConfig(skills, options.config);
|
|
42
|
+
const config = resolveConfig(skills, options.config, options.env ?? process.env);
|
|
35
43
|
const logger = options.logger ?? consoleLogger;
|
|
36
44
|
const results = new Map();
|
|
37
45
|
const edgeCounts = new Map(); // "from→to" → times followed
|
|
38
46
|
const nodeRunCounts = new Map(); // node → times executed
|
|
47
|
+
const maxSteps = options.max_steps ?? DEFAULT_MAX_STEPS;
|
|
48
|
+
let stepCount = 0; // total node executions across the run (loop iterations included)
|
|
39
49
|
const trace = { steps: [], edges: [], sources: {} };
|
|
40
50
|
validate(workflow, skills);
|
|
41
51
|
// Build an eval-time alias table from the loaded skills. Each skill owns
|
|
@@ -86,6 +96,17 @@ export async function execute(workflow, input, options) {
|
|
|
86
96
|
const node = workflow.nodes[currentId];
|
|
87
97
|
if (!node)
|
|
88
98
|
throw new Error(`Unknown node: "${currentId}"`);
|
|
99
|
+
// Workflow-level hard cap. Per-edge max_iterations bounds individual
|
|
100
|
+
// back-edges, but a self-loop (or LLM route) with no max_iterations is
|
|
101
|
+
// otherwise unbounded. This budget terminates the run loudly instead of
|
|
102
|
+
// hanging CI / the action and burning model spend.
|
|
103
|
+
stepCount++;
|
|
104
|
+
if (stepCount > maxSteps) {
|
|
105
|
+
throw new Error(`step budget exceeded: workflow '${workflow.id}' ran ${stepCount} steps (max_steps: ${maxSteps}). ` +
|
|
106
|
+
`This usually means an unbounded cycle, e.g. a back-edge with no 'max_iterations' or a route that keeps ` +
|
|
107
|
+
`looping. Add 'max_iterations' to the offending edge or raise 'max_steps' if the workflow legitimately ` +
|
|
108
|
+
`needs more steps.`);
|
|
109
|
+
}
|
|
89
110
|
const iteration = (nodeRunCounts.get(currentId) ?? 0) + 1;
|
|
90
111
|
nodeRunCounts.set(currentId, iteration);
|
|
91
112
|
const resolvedInstruction = resolvedSources[`nodes.${currentId}.instruction`].content;
|
|
@@ -159,6 +180,9 @@ export async function execute(workflow, input, options) {
|
|
|
159
180
|
let result;
|
|
160
181
|
let currentInstruction = instruction;
|
|
161
182
|
const retry = node.retry;
|
|
183
|
+
// Per-node execution model: node.model ?? workflow.model. When undefined,
|
|
184
|
+
// claude.run falls back to its own client default (then Claude Code's).
|
|
185
|
+
const nodeModel = resolveExecutionModel(node, workflow);
|
|
162
186
|
while (true) {
|
|
163
187
|
result = await claude.run({
|
|
164
188
|
instruction: currentInstruction,
|
|
@@ -167,6 +191,7 @@ export async function execute(workflow, input, options) {
|
|
|
167
191
|
outputSchema: node.output,
|
|
168
192
|
maxTurns: node.max_turns,
|
|
169
193
|
disallowedTools: node.disallowed_tools,
|
|
194
|
+
model: nodeModel,
|
|
170
195
|
onProgress: (message) => {
|
|
171
196
|
safeObserve(observer, { type: "node:progress", node: currentId, message }, logger);
|
|
172
197
|
},
|
|
@@ -187,25 +212,35 @@ export async function execute(workflow, input, options) {
|
|
|
187
212
|
// validation. Type mismatches, formats, additionalProperties, etc.
|
|
188
213
|
// stay the workflow author's problem and don't have the same
|
|
189
214
|
// routing-correctness blast radius.
|
|
215
|
+
// A missing required field is a contract violation handled on the same
|
|
216
|
+
// footing as an eval failure: an otherwise-recoverable one-off omission
|
|
217
|
+
// should self-heal on a second attempt when the node has `retry`
|
|
218
|
+
// configured. We synthesize an eval-shaped failure so the retry preamble
|
|
219
|
+
// and trace bookkeeping below treat it identically. When no retry is
|
|
220
|
+
// configured (or the budget is exhausted), it still hard-fails the node.
|
|
190
221
|
const missingRequired = findMissingRequiredFields(result.data, node.output);
|
|
222
|
+
let outcome;
|
|
191
223
|
if (missingRequired.length > 0) {
|
|
192
224
|
const msg = `output schema violation: required field(s) missing from emitted data: ${missingRequired.join(", ")}`;
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
225
|
+
const failure = { name: "output_required", kind: "value", pass: false, reasoning: msg };
|
|
226
|
+
outcome = { pass: false, error: msg, failures: [failure] };
|
|
227
|
+
// Still record the synthesized failure as the node's eval result so
|
|
228
|
+
// observers and downstream context see the contract violation.
|
|
229
|
+
result.evals = [failure];
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
const evalResults = await evaluateAll(node.eval, result, {
|
|
233
|
+
aliases: toolAliases,
|
|
234
|
+
claude,
|
|
235
|
+
node,
|
|
236
|
+
workflow,
|
|
237
|
+
});
|
|
238
|
+
result.evals = evalResults;
|
|
239
|
+
outcome = aggregateEval(evalResults, node.eval_policy ?? "all_pass");
|
|
197
240
|
}
|
|
198
|
-
const evalResults = await evaluateAll(node.eval, result, {
|
|
199
|
-
aliases: toolAliases,
|
|
200
|
-
claude,
|
|
201
|
-
node,
|
|
202
|
-
workflow,
|
|
203
|
-
});
|
|
204
|
-
result.evals = evalResults;
|
|
205
|
-
const outcome = aggregateEval(evalResults, node.eval_policy ?? "all_pass");
|
|
206
241
|
if (outcome.pass)
|
|
207
242
|
break;
|
|
208
|
-
// Apply
|
|
243
|
+
// Apply the failure to the result.
|
|
209
244
|
result.status = "failed";
|
|
210
245
|
result.data = { ...result.data, error: outcome.error };
|
|
211
246
|
if (!retry || attempt >= retry.max) {
|
|
@@ -225,6 +260,7 @@ export async function execute(workflow, input, options) {
|
|
|
225
260
|
claude,
|
|
226
261
|
logger,
|
|
227
262
|
context,
|
|
263
|
+
model: nodeModel,
|
|
228
264
|
});
|
|
229
265
|
currentInstruction = `${preamble}\n\n---\n\n${instruction}`;
|
|
230
266
|
safeObserve(observer, { type: "node:retry", node: currentId, attempt: attempt + 1, reason: outcome.error ?? "eval failed", preamble }, logger);
|
|
@@ -562,13 +598,18 @@ function resolveSkillInstructions(skillIds, skills) {
|
|
|
562
598
|
/**
|
|
563
599
|
* Resolve config values: check explicit overrides first, then env vars.
|
|
564
600
|
* Throws if a required field is missing.
|
|
601
|
+
*
|
|
602
|
+
* `env` is the environment map to read `field.env` from. Callers thread
|
|
603
|
+
* `options.env ?? process.env` so an explicit env map is honored consistently
|
|
604
|
+
* with Source resolution (which reads the same map). Defaults to
|
|
605
|
+
* `process.env` when omitted.
|
|
565
606
|
*/
|
|
566
|
-
function resolveConfig(skills, overrides) {
|
|
607
|
+
function resolveConfig(skills, overrides, env = process.env) {
|
|
567
608
|
const config = {};
|
|
568
609
|
const missing = [];
|
|
569
610
|
for (const skill of skills.values()) {
|
|
570
611
|
for (const [key, field] of Object.entries(skill.config)) {
|
|
571
|
-
const value = overrides?.[key] ?? (field.env ?
|
|
612
|
+
const value = overrides?.[key] ?? (field.env ? env[field.env] : undefined);
|
|
572
613
|
if (value) {
|
|
573
614
|
config[key] = value;
|
|
574
615
|
}
|
|
@@ -691,14 +732,36 @@ async function resolveNext(workflow, current, results, input, claude, observer,
|
|
|
691
732
|
context,
|
|
692
733
|
choices,
|
|
693
734
|
});
|
|
694
|
-
// Validate that Claude returned a valid target
|
|
735
|
+
// Validate that Claude returned a valid target.
|
|
695
736
|
const validTargets = new Set(outEdges.map((e) => e.to));
|
|
696
|
-
|
|
737
|
+
if (!validTargets.has(chosen)) {
|
|
738
|
+
// The evaluator returned a target that is not a live out-edge. If there's
|
|
739
|
+
// an explicit default (unconditional) edge, take it as the documented
|
|
740
|
+
// fallback. Otherwise this is a hard error: silently jumping to
|
|
741
|
+
// `outEdges[0]` (often the loop-back edge) launders garbage model output
|
|
742
|
+
// into a plausible-looking route and, combined with an unbounded cycle,
|
|
743
|
+
// produces an infinite loop. Stop loudly instead.
|
|
744
|
+
if (!defaultEdge) {
|
|
745
|
+
logger?.warn(` route eval: evaluator returned invalid target '${chosen}' for node '${current}' and there is no ` +
|
|
746
|
+
`default (unconditional) edge; terminating this branch. Valid targets: ${[...validTargets].join(", ")}`, { node: current, chosen, validTargets: [...validTargets] });
|
|
747
|
+
safeObserve(observer, {
|
|
748
|
+
type: "route",
|
|
749
|
+
from: current,
|
|
750
|
+
to: "(end)",
|
|
751
|
+
reason: `invalid route target '${chosen}' with no default edge`,
|
|
752
|
+
}, logger);
|
|
753
|
+
return null;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
const resolved = validTargets.has(chosen) ? chosen : defaultEdge.to;
|
|
697
757
|
// Track edge usage for max_iterations
|
|
698
758
|
if (edgeCounts) {
|
|
699
759
|
const key = `${current}→${resolved}`;
|
|
700
760
|
edgeCounts.set(key, (edgeCounts.get(key) ?? 0) + 1);
|
|
701
761
|
}
|
|
762
|
+
// Report the matched choice's description. The `?? "default"` fallback now
|
|
763
|
+
// only fires when a default edge genuinely exists (the no-default invalid
|
|
764
|
+
// case returned null above), so it can no longer mislabel a garbage route.
|
|
702
765
|
safeObserve(observer, {
|
|
703
766
|
type: "route",
|
|
704
767
|
from: current,
|
package/dist/index.d.ts
CHANGED
|
@@ -21,18 +21,22 @@
|
|
|
21
21
|
* })
|
|
22
22
|
* ```
|
|
23
23
|
*/
|
|
24
|
-
export type { Skill, SkillCategory, SkillDefinition, Tool, ToolContext, ConfigField, JSONSchema, Workflow, Node, Edge, NodeResult, ToolCall, ExecutionEvent, Observer, Claude, Logger, TraceStep, TraceEdge, ExecutionTrace, ExecutionResult, Source, ResolvedSource, SourceKind, SourceResolutionMap, WorkflowInputs, WorkflowInputField, WorkflowInputType, InputValidationError, InputValidationResult, } from "./types.js";
|
|
24
|
+
export type { Skill, SkillCategory, SkillDefinition, Tool, ToolContext, ConfigField, JSONSchema, Workflow, Node, Edge, NodeResult, ToolCall, ExecutionEvent, Observer, Claude, Logger, TraceStep, TraceEdge, ExecutionTrace, ExecutionResult, Source, ResolvedSource, SourceKind, SourceResolutionMap, WorkflowInputs, WorkflowInputField, WorkflowInputType, InputValidationError, InputValidationResult, Evaluator, EvaluatorRule, EvalResult, EvaluatorKind, NodeRequires, NodeRetry, OutputMatch, NodeSources, EvalPolicy, RequiresOnFail, McpTransport, WorkflowType, SkillHarnessKey, } from "./types.js";
|
|
25
25
|
export { WORKFLOW_INPUT_TYPES } from "./types.js";
|
|
26
|
+
export { EVALUATOR_KINDS, EVAL_POLICIES, REQUIRES_ON_FAIL, MCP_TRANSPORTS, SKILL_CATEGORIES, SKILL_HARNESSES, SKILL_ID_PATTERN, SKILL_ID_MAX_LENGTH, isValidSkillId, } from "./types.js";
|
|
26
27
|
export { consoleLogger } from "./types.js";
|
|
27
28
|
export { execute } from "./executor.js";
|
|
28
29
|
export type { ExecuteOptions } from "./executor.js";
|
|
29
|
-
export { ClaudeClient } from "./claude.js";
|
|
30
|
-
export type { ClaudeClientOptions } from "./claude.js";
|
|
30
|
+
export { ClaudeClient, resolveAuthEnv } from "./claude.js";
|
|
31
|
+
export type { ClaudeClientOptions, SwenyAuthMode, ResolveAuthEnvOpts } from "./claude.js";
|
|
32
|
+
export { resolveExecutionModel } from "./model.js";
|
|
31
33
|
export { github, linear, slack, sentry, datadog, notification, supabase, builtinSkills, createSkillMap, allSkills, isSkillConfigured, configuredBuiltinSkills, validateWorkflowSkills, } from "./skills/index.js";
|
|
32
34
|
export type { SkillValidationResult } from "./skills/index.js";
|
|
33
35
|
export { loadCustomSkills, discoverSkills, configuredSkills } from "./skills/custom-loader.js";
|
|
34
|
-
export { workflowZ, nodeZ, edgeZ, skillZ, mcpServerConfigZ, skillDefinitionZ, sourceZ, workflowInputsZ, parseWorkflow, validateWorkflow, workflowJsonSchema, } from "./schema.js";
|
|
36
|
+
export { workflowZ, nodeZ, edgeZ, skillZ, mcpServerConfigZ, skillDefinitionZ, sourceZ, workflowInputsZ, parseWorkflow, validateWorkflow, workflowJsonSchema, skillJsonSchema, } from "./schema.js";
|
|
35
37
|
export type { WorkflowError } from "./schema.js";
|
|
38
|
+
export { loadAndValidateWorkflow, validateParsed } from "./loader.js";
|
|
39
|
+
export type { LoaderResult, LoaderError, LoaderOptions } from "./loader.js";
|
|
36
40
|
export { validateRuntimeInput, summarizeInputShape } from "./inputs.js";
|
|
37
41
|
export { buildAutoMcpServers, buildSkillMcpServers, buildProviderContext } from "./mcp.js";
|
|
38
42
|
export type { ProviderContextOptions, SkillMcpOptions } from "./mcp.js";
|
package/dist/index.js
CHANGED
|
@@ -22,17 +22,25 @@
|
|
|
22
22
|
* ```
|
|
23
23
|
*/
|
|
24
24
|
export { WORKFLOW_INPUT_TYPES } from "./types.js";
|
|
25
|
+
// Runtime enum constants + skill-id helpers
|
|
26
|
+
export { EVALUATOR_KINDS, EVAL_POLICIES, REQUIRES_ON_FAIL, MCP_TRANSPORTS, SKILL_CATEGORIES, SKILL_HARNESSES, SKILL_ID_PATTERN, SKILL_ID_MAX_LENGTH, isValidSkillId, } from "./types.js";
|
|
25
27
|
export { consoleLogger } from "./types.js";
|
|
26
28
|
// Executor
|
|
27
29
|
export { execute } from "./executor.js";
|
|
28
30
|
// Claude client
|
|
29
|
-
export { ClaudeClient } from "./claude.js";
|
|
31
|
+
export { ClaudeClient, resolveAuthEnv } from "./claude.js";
|
|
32
|
+
// Execution model resolution
|
|
33
|
+
export { resolveExecutionModel } from "./model.js";
|
|
30
34
|
// Skills
|
|
31
35
|
export { github, linear, slack, sentry, datadog, notification, supabase, builtinSkills, createSkillMap, allSkills, isSkillConfigured, configuredBuiltinSkills, validateWorkflowSkills, } from "./skills/index.js";
|
|
32
36
|
// Node-only skill discovery (filesystem-based)
|
|
33
37
|
export { loadCustomSkills, discoverSkills, configuredSkills } from "./skills/custom-loader.js";
|
|
34
38
|
// Schema & validation
|
|
35
|
-
export { workflowZ, nodeZ, edgeZ, skillZ, mcpServerConfigZ, skillDefinitionZ, sourceZ, workflowInputsZ, parseWorkflow, validateWorkflow, workflowJsonSchema, } from "./schema.js";
|
|
39
|
+
export { workflowZ, nodeZ, edgeZ, skillZ, mcpServerConfigZ, skillDefinitionZ, sourceZ, workflowInputsZ, parseWorkflow, validateWorkflow, workflowJsonSchema, skillJsonSchema, } from "./schema.js";
|
|
40
|
+
// Loader — canonical read → parse → structural-validate pipeline (the path
|
|
41
|
+
// the CLI uses for `workflow run`, `workflow validate`, and `publish`).
|
|
42
|
+
// Node-only: loader.ts imports `node:fs`, so this is NOT mirrored in browser.ts.
|
|
43
|
+
export { loadAndValidateWorkflow, validateParsed } from "./loader.js";
|
|
36
44
|
// Workflow input validation
|
|
37
45
|
export { validateRuntimeInput, summarizeInputShape } from "./inputs.js";
|
|
38
46
|
// MCP auto-injection
|
package/dist/inputs.js
CHANGED
|
@@ -161,7 +161,16 @@ export function validateRuntimeInput(declared, raw) {
|
|
|
161
161
|
// and the cascade `rules`/`context` mechanism inject extra keys into
|
|
162
162
|
// the input bag. The declaration narrows the contract for documented
|
|
163
163
|
// fields without locking down the whole object.
|
|
164
|
+
//
|
|
165
|
+
// Prototype-pollution guard: `raw` is `unknown` and callers (Studio,
|
|
166
|
+
// programmatic scripts) may hand us hand-built objects, or `JSON.parse`
|
|
167
|
+
// output containing an enumerable own `__proto__` key. Writing such a
|
|
168
|
+
// key with `out[k] = v` would reassign the result's prototype. Skip the
|
|
169
|
+
// three dangerous keys so untrusted input can never touch the prototype
|
|
170
|
+
// chain. `out` stays a normal object literal for downstream consumers.
|
|
164
171
|
for (const [k, v] of Object.entries(provided)) {
|
|
172
|
+
if (isUnsafeKey(k))
|
|
173
|
+
continue;
|
|
165
174
|
if (!Object.prototype.hasOwnProperty.call(declared, k)) {
|
|
166
175
|
out[k] = v;
|
|
167
176
|
}
|
|
@@ -195,6 +204,15 @@ export function summarizeInputShape(declared, resolved) {
|
|
|
195
204
|
return shape;
|
|
196
205
|
}
|
|
197
206
|
// ─── Internals ──────────────────────────────────────────────────
|
|
207
|
+
/**
|
|
208
|
+
* Keys that must never be copied from untrusted input onto a plain object,
|
|
209
|
+
* because assigning them mutates the prototype chain rather than adding an
|
|
210
|
+
* own property. `JSON.parse('{"__proto__":{...}}')` produces an enumerable
|
|
211
|
+
* own `__proto__` key that `Object.entries` surfaces.
|
|
212
|
+
*/
|
|
213
|
+
function isUnsafeKey(key) {
|
|
214
|
+
return key === "__proto__" || key === "constructor" || key === "prototype";
|
|
215
|
+
}
|
|
198
216
|
function checkValueType(value, type) {
|
|
199
217
|
switch (type) {
|
|
200
218
|
case "string":
|
package/dist/loader.js
CHANGED
|
@@ -13,7 +13,7 @@ import * as fs from "node:fs";
|
|
|
13
13
|
import * as path from "node:path";
|
|
14
14
|
import { parse as parseYaml } from "yaml";
|
|
15
15
|
import { ZodError } from "zod";
|
|
16
|
-
import { workflowZ, validateWorkflow } from "./schema.js";
|
|
16
|
+
import { workflowZ, validateWorkflow, preflightLegacyVerify } from "./schema.js";
|
|
17
17
|
/**
|
|
18
18
|
* Load a workflow YAML or JSON file, parse with the canonical Zod schema,
|
|
19
19
|
* and run structural validation. All three phases contribute to `errors`.
|
|
@@ -47,6 +47,20 @@ export function validateParsed(raw, options = {}) {
|
|
|
47
47
|
const kind = raw === null ? "null" : Array.isArray(raw) ? "array" : typeof raw;
|
|
48
48
|
return { ok: false, errors: [{ message: `Expected a workflow object, got ${kind}`, code: "SCHEMA" }] };
|
|
49
49
|
}
|
|
50
|
+
// Detect legacy `verify:` blocks (renamed to `eval:` in v0.2.0) before the
|
|
51
|
+
// Zod parse. workflowZ is intentionally non-strict at the top level, so a
|
|
52
|
+
// top-level `verify:` would be silently STRIPPED instead of surfacing the
|
|
53
|
+
// migration message. parseWorkflow runs this preflight already; validateParsed
|
|
54
|
+
// (the entrypoint for loadAndValidateWorkflow / Studio import) must too. The
|
|
55
|
+
// loader contract is "return errors, do not throw", so map the throw to a
|
|
56
|
+
// SCHEMA LoaderError rather than letting it propagate.
|
|
57
|
+
try {
|
|
58
|
+
preflightLegacyVerify(raw);
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
62
|
+
return { ok: false, errors: [{ message: msg, code: "SCHEMA" }] };
|
|
63
|
+
}
|
|
50
64
|
let parsed;
|
|
51
65
|
try {
|
|
52
66
|
parsed = workflowZ.parse(raw);
|
package/dist/model.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Node, Workflow } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Resolve the execution model for a node: `node.model ?? workflow.model`.
|
|
4
|
+
*
|
|
5
|
+
* Returns `undefined` when neither is set. The client default (if any) is
|
|
6
|
+
* applied one layer down, inside `ClaudeClient.run` (`model ?? this.model`),
|
|
7
|
+
* so the full three-layer cascade is node → workflow → client default →
|
|
8
|
+
* Claude Code default. Callers SHOULD pass `undefined` through to `run`
|
|
9
|
+
* rather than substituting a hardcoded model.
|
|
10
|
+
*/
|
|
11
|
+
export declare function resolveExecutionModel(node: Node | undefined, workflow: Workflow | undefined): string | undefined;
|
package/dist/model.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// ─── Execution model resolution ─────────────────────────────────────
|
|
2
|
+
//
|
|
3
|
+
// Mirrors the judge-model cascade in eval/index.ts, but for the model that
|
|
4
|
+
// executes a node (not the model that evaluates it). Unlike the judge
|
|
5
|
+
// resolver, there is NO package-level default: when no layer specifies a
|
|
6
|
+
// model, this returns undefined so the executor emits no SDK `model` option
|
|
7
|
+
// and Claude Code's own default applies.
|
|
8
|
+
/**
|
|
9
|
+
* Resolve the execution model for a node: `node.model ?? workflow.model`.
|
|
10
|
+
*
|
|
11
|
+
* Returns `undefined` when neither is set. The client default (if any) is
|
|
12
|
+
* applied one layer down, inside `ClaudeClient.run` (`model ?? this.model`),
|
|
13
|
+
* so the full three-layer cascade is node → workflow → client default →
|
|
14
|
+
* Claude Code default. Callers SHOULD pass `undefined` through to `run`
|
|
15
|
+
* rather than substituting a hardcoded model.
|
|
16
|
+
*/
|
|
17
|
+
export function resolveExecutionModel(node, workflow) {
|
|
18
|
+
return node?.model ?? workflow?.model;
|
|
19
|
+
}
|
package/dist/retry.d.ts
CHANGED
|
@@ -12,6 +12,11 @@ export interface BuildRetryPreambleOptions {
|
|
|
12
12
|
claude: Claude;
|
|
13
13
|
logger: Logger;
|
|
14
14
|
context: Record<string, unknown>;
|
|
15
|
+
/**
|
|
16
|
+
* Per-node execution model. The reflection reasons about this node, so it
|
|
17
|
+
* runs on the node's model. When undefined, the client default applies.
|
|
18
|
+
*/
|
|
19
|
+
model?: string;
|
|
15
20
|
}
|
|
16
21
|
/**
|
|
17
22
|
* Build the retry preamble for a single retry attempt.
|
package/dist/retry.js
CHANGED
|
@@ -21,7 +21,7 @@ const DEFAULT_PREAMBLE_HEADER = "## Previous attempt failed evaluation";
|
|
|
21
21
|
* default static preamble and logs a warning.
|
|
22
22
|
*/
|
|
23
23
|
export async function buildRetryPreamble(opts) {
|
|
24
|
-
const { retry, evalFailures, toolCalls, nodeInstruction, claude, logger, context } = opts;
|
|
24
|
+
const { retry, evalFailures, toolCalls, nodeInstruction, claude, logger, context, model } = opts;
|
|
25
25
|
const failureList = formatEvalFailures(evalFailures);
|
|
26
26
|
const inst = retry.instruction;
|
|
27
27
|
if (typeof inst === "string") {
|
|
@@ -31,7 +31,7 @@ export async function buildRetryPreamble(opts) {
|
|
|
31
31
|
const reflectPrompt = "reflect" in inst && typeof inst.reflect === "string" ? inst.reflect : DEFAULT_REFLECTION_PROMPT;
|
|
32
32
|
const askInstruction = buildReflectionPrompt(reflectPrompt, nodeInstruction, failureList, toolCalls);
|
|
33
33
|
try {
|
|
34
|
-
const diagnosis = await claude.ask({ instruction: askInstruction, context });
|
|
34
|
+
const diagnosis = await claude.ask({ instruction: askInstruction, context, model });
|
|
35
35
|
const trimmed = diagnosis.trim();
|
|
36
36
|
if (trimmed.length > 0) {
|
|
37
37
|
return `## Reflection on previous attempt\n\n${trimmed}\n\n${DEFAULT_PREAMBLE_HEADER}\n\n${failureList}`;
|