lua-cli 3.32.3 → 3.32.4

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.
@@ -647,6 +647,94 @@ function isDesktopFileSessionId(value3) {
647
647
  }
648
648
  __name(isDesktopFileSessionId, "isDesktopFileSessionId");
649
649
  __name2(isDesktopFileSessionId, "isDesktopFileSessionId");
650
+ var MODEL_ID_BYOK_PROVIDERS = [
651
+ "azure",
652
+ "bedrock"
653
+ ];
654
+ function isModelIdSentinel(input) {
655
+ const lower = (input ?? "").trim().toLowerCase();
656
+ return lower === "auto" || lower.startsWith("auto/");
657
+ }
658
+ __name(isModelIdSentinel, "isModelIdSentinel");
659
+ __name2(isModelIdSentinel, "isModelIdSentinel");
660
+ function normalizeModelId(input, registry) {
661
+ const requested = typeof input === "string" ? input.trim() : "";
662
+ if (!requested) return {
663
+ ok: false,
664
+ reason: "empty",
665
+ requested,
666
+ candidates: []
667
+ };
668
+ const lower = requested.toLowerCase();
669
+ if (isModelIdSentinel(lower)) return {
670
+ ok: true,
671
+ id: lower,
672
+ form: "sentinel"
673
+ };
674
+ const slash = requested.indexOf("/");
675
+ const malformed = slash === 0;
676
+ const provider = slash > 0 ? lower.slice(0, slash) : void 0;
677
+ if (provider && MODEL_ID_BYOK_PROVIDERS.includes(provider)) {
678
+ return {
679
+ ok: true,
680
+ id: requested,
681
+ form: "byok"
682
+ };
683
+ }
684
+ const bareId = slash >= 0 ? lower.slice(slash + 1) : lower;
685
+ const lastSegment = bareId.slice(bareId.lastIndexOf("/") + 1);
686
+ const exact = /* @__PURE__ */ new Set();
687
+ const hints = /* @__PURE__ */ new Set();
688
+ for (const code of registry) {
689
+ if (typeof code !== "string" || !code) continue;
690
+ const codeLower = code.toLowerCase();
691
+ if (!malformed && codeLower === lower) return {
692
+ ok: true,
693
+ id: code,
694
+ form: provider ? "canonical" : "bare"
695
+ };
696
+ const i = codeLower.indexOf("/");
697
+ if (i < 0) continue;
698
+ const codeBare = codeLower.slice(i + 1);
699
+ if (bareId && codeBare === bareId) exact.add(code);
700
+ else if (lastSegment && codeBare.slice(codeBare.lastIndexOf("/") + 1) === lastSegment) hints.add(code);
701
+ }
702
+ const sorted = [
703
+ ...exact
704
+ ].sort();
705
+ if (!provider && !malformed && sorted.length === 1) return {
706
+ ok: true,
707
+ id: sorted[0],
708
+ form: "bare"
709
+ };
710
+ if (!provider && !malformed && sorted.length > 1) {
711
+ return {
712
+ ok: false,
713
+ reason: "ambiguous",
714
+ requested,
715
+ candidates: sorted
716
+ };
717
+ }
718
+ return {
719
+ ok: false,
720
+ reason: "unknown",
721
+ requested,
722
+ candidates: sorted.length ? sorted : [
723
+ ...hints
724
+ ].sort()
725
+ };
726
+ }
727
+ __name(normalizeModelId, "normalizeModelId");
728
+ __name2(normalizeModelId, "normalizeModelId");
729
+ function modelUnresolvedMessage(r) {
730
+ if (r.reason === "empty") return "model pin is empty \u2014 pin an approved model (provider/model) or omit `model`";
731
+ if (r.reason === "ambiguous") {
732
+ return `model "${r.requested}" does not resolve to one approved model \u2014 it names ${r.candidates.length}; pin one of: ${r.candidates.join(", ")}`;
733
+ }
734
+ return r.candidates.length ? `model "${r.requested}" does not resolve to an approved model \u2014 accepted forms: ${r.candidates.join(", ")}` : `model "${r.requested}" does not resolve to an approved model \u2014 accepted forms are the registry's provider-prefixed ids (provider/model) or a bare id that names exactly one of them`;
735
+ }
736
+ __name(modelUnresolvedMessage, "modelUnresolvedMessage");
737
+ __name2(modelUnresolvedMessage, "modelUnresolvedMessage");
650
738
  var IMPLICIT_MODEL_SELECTION_SOURCES = [
651
739
  "workspace-default",
652
740
  "platform-default"
@@ -2724,7 +2812,211 @@ var fromKnowledge = /* @__PURE__ */ __name3((k) => ({
2724
2812
  }), "fromKnowledge");
2725
2813
  var SideEffectsSchema = z4.enum(WORKFLOW_SIDE_EFFECTS);
2726
2814
  var JobResourcesSchema = z4.enum(WORKFLOW_JOB_RESOURCES);
2727
- var WORKFLOW_ARM_SUBRUN_ID = "$arm";
2815
+ var APPROVER_SPEC_MAX_USERS = 20;
2816
+ var ESCALATION_MAX_HOPS = 3;
2817
+ var TemplateBindingSchema = z22.object({
2818
+ template: z22.string().min(1).max(2048)
2819
+ }).strict();
2820
+ var ApproverSpecSchema = z22.union([
2821
+ z22.literal("creator"),
2822
+ z22.literal("org-admins"),
2823
+ z22.object({
2824
+ users: z22.union([
2825
+ z22.array(z22.string().min(1).max(128)).min(1).max(APPROVER_SPEC_MAX_USERS),
2826
+ TemplateBindingSchema
2827
+ ])
2828
+ }).strict(),
2829
+ z22.object({
2830
+ role: z22.union([
2831
+ z22.string().min(1).max(128),
2832
+ TemplateBindingSchema
2833
+ ])
2834
+ }).strict(),
2835
+ z22.object({
2836
+ group: z22.union([
2837
+ z22.string().min(1).max(128),
2838
+ TemplateBindingSchema
2839
+ ])
2840
+ }).strict(),
2841
+ z22.object({
2842
+ governance: z22.object({
2843
+ policyId: z22.string().min(1).max(128)
2844
+ }).strict()
2845
+ }).strict()
2846
+ ]);
2847
+ var FourEyesSchema = z22.object({
2848
+ edit: ApproverSpecSchema,
2849
+ approve: ApproverSpecSchema
2850
+ }).strict();
2851
+ var EscalationHopSchema = z22.object({
2852
+ escalateTo: ApproverSpecSchema,
2853
+ timeoutHours: z22.number().finite().min(1).max(720)
2854
+ }).strict();
2855
+ var TerminalOutcomeSchema = z22.enum([
2856
+ "deny",
2857
+ "cancel-run",
2858
+ "fail",
2859
+ "continue"
2860
+ ]);
2861
+ var ApprovalOnTimeoutSchema = z22.union([
2862
+ TerminalOutcomeSchema,
2863
+ EscalationHopSchema,
2864
+ z22.array(z22.union([
2865
+ TerminalOutcomeSchema,
2866
+ EscalationHopSchema
2867
+ ])).min(1).max(ESCALATION_MAX_HOPS + 1)
2868
+ ]);
2869
+ var APPROVER_SPEC_SHAPES = [
2870
+ "'creator'",
2871
+ "'org-admins'",
2872
+ "{users:[userId, \u2026]}",
2873
+ "{role:roleName}",
2874
+ "{group:groupName}",
2875
+ "{governance:{policyId}}"
2876
+ ];
2877
+ var APPROVER_WRITTEN_MAX = 120;
2878
+ var USER_ID_SHAPED_RE = /^[^\s@]{1,128}$/;
2879
+ function describeApproverSpecRefusal(spec) {
2880
+ const raw = spec === void 0 ? "undefined" : JSON.stringify(spec) ?? String(spec);
2881
+ const written = raw.length > APPROVER_WRITTEN_MAX ? `${raw.slice(0, APPROVER_WRITTEN_MAX - 1)}\u2026` : raw;
2882
+ const users = typeof spec === "object" && spec !== null ? spec.users : void 0;
2883
+ const approver = typeof users === "string" && USER_ID_SHAPED_RE.test(users) ? {
2884
+ users: [
2885
+ users
2886
+ ]
2887
+ } : "creator";
2888
+ const message = `approver ${written} is not an approver \u2014 legal: ${APPROVER_SPEC_SHAPES.join(" | ")}. 'creator' is the person who started the run: write approver:'creator' for "ask me" / "I approve"; {users:[\u2026]} takes user ids, never emails, names or {type:'user'}` + (approver === "creator" ? "" : `; here: approver:${JSON.stringify(approver)}`);
2889
+ return {
2890
+ approver,
2891
+ written,
2892
+ message
2893
+ };
2894
+ }
2895
+ __name(describeApproverSpecRefusal, "describeApproverSpecRefusal");
2896
+ __name3(describeApproverSpecRefusal, "describeApproverSpecRefusal");
2897
+ var BINDING_ROOTS = [
2898
+ "initData",
2899
+ "stepResults",
2900
+ "requestContext",
2901
+ "state"
2902
+ ];
2903
+ function bindingRootsOk(template22) {
2904
+ const refs = [
2905
+ ...template22.matchAll(/\$\{\s*([A-Za-z_$][\w$]*)/g)
2906
+ ].map((m) => m[1]);
2907
+ return refs.length > 0 && refs.every((r) => BINDING_ROOTS.includes(r));
2908
+ }
2909
+ __name(bindingRootsOk, "bindingRootsOk");
2910
+ __name3(bindingRootsOk, "bindingRootsOk");
2911
+ function isTemplateBinding(v) {
2912
+ return typeof v === "object" && v !== null && typeof v.template === "string";
2913
+ }
2914
+ __name(isTemplateBinding, "isTemplateBinding");
2915
+ __name3(isTemplateBinding, "isTemplateBinding");
2916
+ function approvalEditable(node) {
2917
+ if (node.editable === true) return true;
2918
+ if (node.editable === false) return false;
2919
+ return Array.isArray(node.editablePaths) && node.editablePaths.length > 0;
2920
+ }
2921
+ __name(approvalEditable, "approvalEditable");
2922
+ __name3(approvalEditable, "approvalEditable");
2923
+ function validateApproverBlock(node, opts = {
2924
+ path: "approval"
2925
+ }) {
2926
+ const issues = [];
2927
+ const push = /* @__PURE__ */ __name3((code, path, message, severity = "error") => issues.push({
2928
+ code,
2929
+ path,
2930
+ severity,
2931
+ message
2932
+ }), "push");
2933
+ const checkSpec = /* @__PURE__ */ __name3((spec, path) => {
2934
+ const r = ApproverSpecSchema.safeParse(spec);
2935
+ if (!r.success) {
2936
+ const users = spec?.users;
2937
+ if (Array.isArray(users) && users.length > APPROVER_SPEC_MAX_USERS) push("cap-exceeded", path, `at most ${APPROVER_SPEC_MAX_USERS} users`);
2938
+ else {
2939
+ const refusal = describeApproverSpecRefusal(spec);
2940
+ issues.push({
2941
+ code: "approver-invalid",
2942
+ path,
2943
+ severity: "error",
2944
+ message: refusal.message,
2945
+ repair: {
2946
+ approver: refusal.approver,
2947
+ written: refusal.written
2948
+ }
2949
+ });
2950
+ }
2951
+ return;
2952
+ }
2953
+ const s = r.data;
2954
+ if (typeof s === "object") {
2955
+ if ("governance" in s && !opts.governanceEnabled) push("approver-governance-unavailable", path, "governance reviewer routing is not enabled for this deployment");
2956
+ if ("group" in s && typeof s.group === "string" && !opts.scimEnabled && opts.idpGroups?.includes(s.group)) push("approver-idp-group-unavailable", path, "IdP-group approvers are not enabled for this deployment");
2957
+ const binding = "users" in s ? s.users : "role" in s ? s.role : "group" in s ? s.group : void 0;
2958
+ if (isTemplateBinding(binding)) {
2959
+ if (!bindingRootsOk(binding.template)) push("approver-binding-invalid", `${path}.template`, "binding root must be initData / stepResults / requestContext / state");
2960
+ if ("users" in s && opts.customerReachable) push("approver-binding-customer-reachable", `${path}.users`, "a customer-reachable workflow may not bind its approver list");
2961
+ }
2962
+ }
2963
+ }, "checkSpec");
2964
+ if (node.approver !== void 0) checkSpec(node.approver, `${opts.path}.approver`);
2965
+ if (node.fourEyes !== void 0) {
2966
+ const r = FourEyesSchema.safeParse(node.fourEyes);
2967
+ if (!r.success) push("approver-invalid", `${opts.path}.fourEyes`, "fourEyes needs { edit, approve } approver specs");
2968
+ else {
2969
+ checkSpec(r.data.edit, `${opts.path}.fourEyes.edit`);
2970
+ checkSpec(r.data.approve, `${opts.path}.fourEyes.approve`);
2971
+ }
2972
+ if (!approvalEditable(node)) push("four-eyes-requires-editable", `${opts.path}.fourEyes`, "fourEyes requires editable:true");
2973
+ if (node.approver !== void 0) push("four-eyes-overrides-approver", `${opts.path}.approver`, "fourEyes replaces approver", "warning");
2974
+ if (node.itemsPath) push("four-eyes-items-unsupported", `${opts.path}.fourEyes`, "fourEyes cannot combine with itemsPath");
2975
+ }
2976
+ if (node.excludeInitiator && (node.approver === void 0 || node.approver === "creator") && !node.fourEyes) push("approver-excludes-only-candidate", `${opts.path}.excludeInitiator`, "'creator' with excludeInitiator leaves no approver");
2977
+ if (Array.isArray(node.onTimeout)) {
2978
+ const chain = node.onTimeout;
2979
+ const hops = chain.filter((m) => typeof m === "object" && m !== null && "escalateTo" in m);
2980
+ if (hops.length > ESCALATION_MAX_HOPS) push("escalation-chain-too-long", `${opts.path}.onTimeout`, `at most ${ESCALATION_MAX_HOPS} hops`);
2981
+ const last = chain[chain.length - 1];
2982
+ if (typeof last === "object" && last !== null) push("escalation-chain-not-terminal", `${opts.path}.onTimeout`, "a chain must end in deny | cancel-run | fail");
2983
+ hops.forEach((h, i) => checkSpec(h.escalateTo, `${opts.path}.onTimeout[${i}].escalateTo`));
2984
+ } else if (typeof node.onTimeout === "object" && node.onTimeout !== null) {
2985
+ checkSpec(node.onTimeout.escalateTo, `${opts.path}.onTimeout.escalateTo`);
2986
+ }
2987
+ return issues;
2988
+ }
2989
+ __name(validateApproverBlock, "validateApproverBlock");
2990
+ __name3(validateApproverBlock, "validateApproverBlock");
2991
+ function liftRenderedApprover(row, rendered) {
2992
+ const text = (rendered ?? "").trim();
2993
+ if (!text) return null;
2994
+ if (row === "users") {
2995
+ let members = null;
2996
+ if (text.startsWith("[")) {
2997
+ try {
2998
+ members = JSON.parse(text);
2999
+ } catch {
3000
+ return null;
3001
+ }
3002
+ } else members = text.split(",").map((s) => s.trim());
3003
+ if (!Array.isArray(members) || members.length === 0 || members.length > APPROVER_SPEC_MAX_USERS) return null;
3004
+ if (!members.every((m) => typeof m === "string" && m.length > 0 && m.length <= 128)) return null;
3005
+ return {
3006
+ users: [
3007
+ ...new Set(members)
3008
+ ].sort()
3009
+ };
3010
+ }
3011
+ if (text.length > 128 || text.startsWith("[") || text.startsWith("{")) return null;
3012
+ return row === "role" ? {
3013
+ role: text
3014
+ } : {
3015
+ group: text
3016
+ };
3017
+ }
3018
+ __name(liftRenderedApprover, "liftRenderedApprover");
3019
+ __name3(liftRenderedApprover, "liftRenderedApprover");
2728
3020
  var WORKSPACE_TEMPLATE_EXPR_RE = /^\$\{\s*(?:initData|input)\.([^}]+?)\s*\}$/;
2729
3021
  function workspaceTemplatePath(template22) {
2730
3022
  const key = template22.trim();
@@ -2752,6 +3044,11 @@ function sleepUntilUnsupportedMessage(id) {
2752
3044
  }
2753
3045
  __name(sleepUntilUnsupportedMessage, "sleepUntilUnsupportedMessage");
2754
3046
  __name3(sleepUntilUnsupportedMessage, "sleepUntilUnsupportedMessage");
3047
+ function armSubrunUnsupportedMessage(id, workflowId) {
3048
+ return `the engine does not execute the implicit \`${workflowId}\` arm subrun (node "${id}") \u2014 a [map, step] container arm is the step itself with the map as its \`input\` since lua-cli 3.32.4; re-run \`lua compile\` with the current CLI (a hand-written artifact: put the map on the arm node's \`input\` and drop the \`workflow\` wrapper)`;
3049
+ }
3050
+ __name(armSubrunUnsupportedMessage, "armSubrunUnsupportedMessage");
3051
+ __name3(armSubrunUnsupportedMessage, "armSubrunUnsupportedMessage");
2755
3052
  var WORKFLOW_CAPS_DEFAULT = Object.freeze({
2756
3053
  maxParallelArms: 16,
2757
3054
  maxForeachConcurrency: 16,
@@ -2804,7 +3101,6 @@ function fillSingle(node) {
2804
3101
  fillPolicy(node, WORKFLOW_STEP_DEFAULT_TIMEOUT_SECONDS);
2805
3102
  return;
2806
3103
  case "workflow":
2807
- if (node.workflowId === WORKFLOW_ARM_SUBRUN_ID && Array.isArray(node.graph) && node.graph[1]) fillSingle(node.graph[1]);
2808
3104
  return;
2809
3105
  }
2810
3106
  }
@@ -2994,8 +3290,14 @@ function nodeStepRefs(entry) {
2994
3290
  case "agent": {
2995
3291
  const a = entry;
2996
3292
  const p = a.promptTemplate;
2997
- return typeof p === "string" ? templateStepRefs(p) : p && "template" in p ? templateStepRefs(p.template) : [];
3293
+ const prompt = typeof p === "string" ? templateStepRefs(p) : p && "template" in p ? templateStepRefs(p.template) : [];
3294
+ return [
3295
+ ...prompt,
3296
+ ...mapConfigStepRefs(a.input)
3297
+ ];
2998
3298
  }
3299
+ case "step":
3300
+ return mapConfigStepRefs(entry.input);
2999
3301
  case "tool":
3000
3302
  return mapConfigStepRefs(entry.input);
3001
3303
  case "workflow":
@@ -3214,6 +3516,21 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
3214
3516
  err("job-tier-provider-unsupported", `model provider '${provider}' is outside LUA_WF_JOB_PROVIDERS [${opts.policy.jobProviders.join(", ")}]`, `${path}.model`, id);
3215
3517
  }
3216
3518
  }, "checkTier");
3519
+ const checkModel = /* @__PURE__ */ __name3((node, path) => {
3520
+ if (node.type !== "agent" || typeof node.model !== "string") return;
3521
+ const registry = opts.approvedModels;
3522
+ if (registry === void 0) return;
3523
+ const id = singleId(node);
3524
+ if (registry === "unavailable") {
3525
+ const pin = node.model.trim();
3526
+ if (pin && !normalizeModelId(pin, []).ok) {
3527
+ warn("model-unresolved", `model "${pin}" could not be checked against the approved-model registry (unavailable at push) \u2014 it dispatches only if it resolves there (a provider/model registry code, or a bare id exactly one approved model carries)`, `${path}.model`, id);
3528
+ }
3529
+ return;
3530
+ }
3531
+ const resolved = normalizeModelId(node.model, registry);
3532
+ if (!resolved.ok) err("model-unresolved", modelUnresolvedMessage(resolved), `${path}.model`, id);
3533
+ }, "checkModel");
3217
3534
  const checkWorkspace = /* @__PURE__ */ __name3((node, path) => {
3218
3535
  const id = singleId(node);
3219
3536
  const ws = workspaceOf(node);
@@ -3266,38 +3583,29 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
3266
3583
  }
3267
3584
  }, "checkMapMembers");
3268
3585
  const checkInputShape = /* @__PURE__ */ __name3((node, path) => {
3269
- if (node.type !== "tool" && node.type !== "workflow") return;
3270
3586
  const input = node.input;
3271
3587
  if (input === void 0) return;
3588
+ const id = singleId(node);
3272
3589
  if (input !== null && typeof input === "object" && !Array.isArray(input)) {
3273
- checkMapMembers(input, `${path}.input`, node.id);
3590
+ checkMapMembers(input, `${path}.input`, id);
3274
3591
  return;
3275
3592
  }
3276
- err("invalid-envelope", `\`input\` must be an object map \u2014 each member a binding descriptor ({initData:true, path} | {step, path} | {value} | {template} | {requestContextPath}) or a JSON literal (got ${JSON.stringify(input)})`, `${path}.input`, node.id);
3593
+ err("invalid-envelope", `\`input\` must be an object map \u2014 each member a binding descriptor ({initData:true, path} | {step, path} | {value} | {template} | {requestContextPath}) or a JSON literal (got ${JSON.stringify(input)})`, `${path}.input`, id);
3277
3594
  }, "checkInputShape");
3595
+ const checkBodyInput = /* @__PURE__ */ __name3((body, path, container) => {
3596
+ if (body.type === "workflow" || body.input === void 0) return;
3597
+ err("arm-input-unsupported", container === "foreach" ? `a foreach body receives each item as its input \u2014 drop \`input\` on "${singleId(body)}" and map the items before the foreach instead` : `a loop body receives the previous output as its input \u2014 drop \`input\` on "${singleId(body)}" and put the map before the loop instead`, `${path}.input`, singleId(body));
3598
+ }, "checkBodyInput");
3278
3599
  const checkSingle = /* @__PURE__ */ __name3((node, path, depth) => {
3279
3600
  recordOutputSchema(node);
3280
- if (node.type === "workflow" && node.workflowId === WORKFLOW_ARM_SUBRUN_ID) {
3601
+ if (node.type === "workflow" && (typeof node.workflowId !== "string" || node.workflowId.length === 0)) {
3281
3602
  checkId(node.id, path);
3282
- const body = node.graph;
3283
- if (!Array.isArray(body) || body.length !== 2 || body[0]?.type !== "mapping" || !body[1]) {
3284
- err("container-arm-empty", "an implicit arm subrun needs a [mapping, step] body", `${path}.graph`, node.id);
3285
- return;
3286
- }
3287
- if (body[1].type === "mapping") {
3288
- err("container-arm-empty", "a bare mapping arm has nothing to run", `${path}.graph.1`, node.id);
3289
- return;
3290
- }
3291
- const inner = body[1];
3292
- if (isHitlNode(inner)) {
3293
- err("node-type-unsupported-in-container", workflowHitlArmShapeMessage(inner.type, inner.id, "mapped-arm"), `${path}.graph.1`, inner.id);
3294
- return;
3295
- }
3296
- upstream.add(singleId(body[1]));
3297
- checkArm(body[0], `${path}.graph.0`, depth, "parallel");
3298
- checkSingle(body[1], `${path}.graph.1`, depth);
3299
- upstream.add(body[0].id);
3300
- upstream.add(singleId(body[1]));
3603
+ err("invalid-envelope", `\`workflowId\` must be a non-empty string naming the workflow to start (got ${JSON.stringify(node.workflowId)})`, `${path}.workflowId`, node.id);
3604
+ return;
3605
+ }
3606
+ if (node.type === "workflow" && (node.workflowId.startsWith("$") || Array.isArray(node.graph))) {
3607
+ checkId(node.id, path);
3608
+ err("node-type-unsupported-by-engine", armSubrunUnsupportedMessage(node.id, node.workflowId), path, node.id);
3301
3609
  return;
3302
3610
  }
3303
3611
  checkId(singleId(node), path);
@@ -3305,6 +3613,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
3305
3613
  checkInputShape(node, path);
3306
3614
  checkTimeout(node, path);
3307
3615
  checkTier(node, path);
3616
+ checkModel(node, path);
3308
3617
  checkRetry(node, path);
3309
3618
  checkWorkspace(node, path);
3310
3619
  if (!opts.static) {
@@ -3327,7 +3636,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
3327
3636
  if (node.type === "workflow" && node.kind === "subrun" && depth > caps.maxNestingDepth) {
3328
3637
  err("cap-exceeded", `nesting depth ${depth} exceeds ${caps.maxNestingDepth}`, path, node.id);
3329
3638
  }
3330
- if (node.type === "workflow" && node.workflowId !== WORKFLOW_ARM_SUBRUN_ID && typeof g.definition?.id === "string" && node.workflowId === g.definition.id) {
3639
+ if (node.type === "workflow" && typeof g.definition?.id === "string" && node.workflowId === g.definition.id) {
3331
3640
  err("subrun-cycle", `"${node.id}" starts "${node.workflowId}", which is this workflow itself`, path, node.id);
3332
3641
  }
3333
3642
  for (const ref of nodeStepRefs(node)) {
@@ -3348,11 +3657,14 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
3348
3657
  if (a.approver === "creator" && a.excludeInitiator === true) {
3349
3658
  err("approver-excludes-only-candidate", "approver:'creator' with excludeInitiator:true always excludes the only candidate", path, a.id);
3350
3659
  }
3351
- if (a.fourEyes !== void 0 && a.editable !== true) {
3660
+ const editable = approvalEditable(a);
3661
+ if (a.fourEyes !== void 0 && !editable) {
3352
3662
  err("four-eyes-requires-editable", "`fourEyes` requires editable:true", `${path}.fourEyes`, a.id);
3353
3663
  }
3354
- if ((a.editablePaths !== void 0 || a.editedPayloadSchema !== void 0) && a.editable !== true) {
3355
- err("editable-path-invalid", "`editablePaths` / `editedPayloadSchema` require editable:true", `${path}.editablePaths`, a.id);
3664
+ if (a.editable === false && Array.isArray(a.editablePaths) && a.editablePaths.length > 0) {
3665
+ err("editable-path-invalid", "`editablePaths` beside editable:false is contradictory \u2014 drop the paths or set editable:true", `${path}.editablePaths`, a.id);
3666
+ } else if ((a.editablePaths !== void 0 || a.editedPayloadSchema !== void 0) && !editable) {
3667
+ err("editable-path-invalid", "`editablePaths` / `editedPayloadSchema` require editable:true (a non-empty editablePaths implies it)", `${path}.editablePaths`, a.id);
3356
3668
  }
3357
3669
  for (const p of a.editablePaths ?? []) {
3358
3670
  if (!EDITABLE_PATH_RE.test(p)) err("editable-path-invalid", `editablePaths entry "${p}" is outside the seg(.seg)*[*]/[n] grammar`, `${path}.editablePaths`, a.id);
@@ -3533,6 +3845,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
3533
3845
  } else checkHitlArm(f.step, `${path}.step`, "foreach");
3534
3846
  declared.push(f.step.id);
3535
3847
  } else {
3848
+ checkBodyInput(f.step, `${path}.step`, "foreach");
3536
3849
  checkSingle(f.step, `${path}.step`, o.chunk ? 2 : 1);
3537
3850
  declared.push(singleId(f.step));
3538
3851
  }
@@ -3553,6 +3866,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
3553
3866
  checkHitlArm(l.step, `${path}.step`, "loop");
3554
3867
  declared.push(l.step.id);
3555
3868
  } else {
3869
+ checkBodyInput(l.step, `${path}.step`, "loop");
3556
3870
  checkSingle(l.step, `${path}.step`, 1);
3557
3871
  declared.push(singleId(l.step));
3558
3872
  }
@@ -4220,21 +4534,14 @@ function isContinuedFailureValue(v) {
4220
4534
  __name(isContinuedFailureValue, "isContinuedFailureValue");
4221
4535
  __name3(isContinuedFailureValue, "isContinuedFailureValue");
4222
4536
  var isHitlNode2 = /* @__PURE__ */ __name3((n2) => isWorkflowHitlEntryType(n2.type), "isHitlNode");
4223
- function lowerContainerArm(mapping, step22) {
4224
- const stepId = nodeIdOf(step22);
4537
+ function inlineContainerArm(mapping, step22) {
4225
4538
  return {
4226
- type: "workflow",
4227
- id: `${stepId}_arm`,
4228
- workflowId: WORKFLOW_ARM_SUBRUN_ID,
4229
- kind: "subrun",
4230
- graph: [
4231
- mapping,
4232
- step22
4233
- ]
4539
+ ...step22,
4540
+ input: parseMapConfig(mapping.mapConfig, mapping.id)
4234
4541
  };
4235
4542
  }
4236
- __name(lowerContainerArm, "lowerContainerArm");
4237
- __name3(lowerContainerArm, "lowerContainerArm");
4543
+ __name(inlineContainerArm, "inlineContainerArm");
4544
+ __name3(inlineContainerArm, "inlineContainerArm");
4238
4545
  var nodeIdOf = /* @__PURE__ */ __name3((n2) => n2.type === "step" ? n2.step.id : n2.id, "nodeIdOf");
4239
4546
  function entryIds(entry) {
4240
4547
  switch (entry.type) {
@@ -4305,6 +4612,27 @@ function resolvePlacements(calls) {
4305
4612
  break;
4306
4613
  }
4307
4614
  });
4615
+ const armMapPlacementIssue = /* @__PURE__ */ __name3((node, ref, i, container) => {
4616
+ if (!ref.armMap || node.type === "mapping" || isHitlNode2(node)) return void 0;
4617
+ const id = nodeIdOf(node);
4618
+ if ((container === "foreach" || container === "loop") && node.type !== "workflow") {
4619
+ return {
4620
+ code: "mapping-placement",
4621
+ message: container === "foreach" ? `foreach body "${id}": a [map, step] body is not supported \u2014 the body receives each item as its input; map the items before the foreach instead (foreach(step, { items: \u2026 }) or a .map() before it)` : `loop body "${id}": a [map, step] body is not supported \u2014 the body receives the previous output as its input; put the .map() before the loop instead`,
4622
+ callIndex: i,
4623
+ stepId: id
4624
+ };
4625
+ }
4626
+ if (node.input !== void 0) {
4627
+ return {
4628
+ code: "mapping-placement",
4629
+ message: `"${id}": the [map, step] arm mapping and the node's own input map would both bind its input \u2014 keep one (drop the arm map, or the \`input\` on the declaration)`,
4630
+ callIndex: i,
4631
+ stepId: id
4632
+ };
4633
+ }
4634
+ return void 0;
4635
+ }, "armMapPlacementIssue");
4308
4636
  const hitlPlacementIssue = /* @__PURE__ */ __name3((node, ref, i, container) => {
4309
4637
  if (!isHitlNode2(node)) return void 0;
4310
4638
  const id = node.id;
@@ -4331,7 +4659,7 @@ function resolvePlacements(calls) {
4331
4659
  if (ref.node.type === "mapping" && !allowMapping) {
4332
4660
  issues.push({
4333
4661
  code: "mapping-placement",
4334
- message: `mapping "${ref.node.id}" cannot be a container arm \u2014 chain it as [map, step]`,
4662
+ message: `mapping "${ref.node.id}" cannot be a container arm \u2014 chain it as [map, step] in a parallel / conditional arm, or place the .map() before the container`,
4335
4663
  callIndex: i,
4336
4664
  stepId: ref.node.id
4337
4665
  });
@@ -4352,7 +4680,7 @@ function resolvePlacements(calls) {
4352
4680
  if (d.node.type === "mapping" && !allowMapping) {
4353
4681
  issues.push({
4354
4682
  code: "mapping-placement",
4355
- message: `map "${ref.ref}" cannot be a parallel/foreach/loop arm \u2014 chain it as [map, step]`,
4683
+ message: `map "${ref.ref}" cannot be a parallel/foreach/loop arm \u2014 chain it as [map, step] in a parallel arm, or place the .map() before the container`,
4356
4684
  callIndex: i,
4357
4685
  stepId: ref.ref
4358
4686
  });
@@ -4363,6 +4691,11 @@ function resolvePlacements(calls) {
4363
4691
  issues.push(hitl);
4364
4692
  return void 0;
4365
4693
  }
4694
+ const mapped = armMapPlacementIssue(d.node, ref, i, container);
4695
+ if (mapped) {
4696
+ issues.push(mapped);
4697
+ return void 0;
4698
+ }
4366
4699
  const prior = placedBy.get(ref.ref);
4367
4700
  if (prior !== void 0 && prior !== i) {
4368
4701
  issues.push({
@@ -4383,6 +4716,10 @@ function resolvePlacements(calls) {
4383
4716
  }
4384
4717
  const hitl = hitlPlacementIssue(ref.node, ref, i, container);
4385
4718
  if (hitl) issues.push(hitl);
4719
+ else {
4720
+ const mapped = armMapPlacementIssue(ref.node, ref, i, container);
4721
+ if (mapped) issues.push(mapped);
4722
+ }
4386
4723
  }, "claim");
4387
4724
  calls.forEach((call, i) => {
4388
4725
  switch (call.kind) {
@@ -4410,7 +4747,7 @@ function resolvePlacements(calls) {
4410
4747
  const lookup = /* @__PURE__ */ __name3((ref) => {
4411
4748
  const n2 = "node" in ref ? ref.node : declared.get(ref.ref)?.node;
4412
4749
  if (!n2 || !ref.armMap || n2.type === "mapping" || isHitlNode2(n2)) return n2;
4413
- return lowerContainerArm(ref.armMap, n2);
4750
+ return inlineContainerArm(ref.armMap, n2);
4414
4751
  }, "lookup");
4415
4752
  calls.forEach((call, i) => {
4416
4753
  switch (call.kind) {
@@ -4851,6 +5188,33 @@ function pruneUndefined(o) {
4851
5188
  }
4852
5189
  __name(pruneUndefined, "pruneUndefined");
4853
5190
  __name3(pruneUndefined, "pruneUndefined");
5191
+ var WORKFLOW_INLINE_RUN_TAG = "inline";
5192
+ function runOrigin(run) {
5193
+ if (run.goalId) return "goal";
5194
+ if (run.jobId || run.trigger === "schedule") return "schedule";
5195
+ if (run.dynamic === true) return run.tags?.includes(WORKFLOW_INLINE_RUN_TAG) ? "inline" : "compose";
5196
+ return "definition";
5197
+ }
5198
+ __name(runOrigin, "runOrigin");
5199
+ __name3(runOrigin, "runOrigin");
5200
+ var RUN_ERROR_ISSUES_MAX = 20;
5201
+ function runErrorIssues(issues) {
5202
+ if (!Array.isArray(issues)) return void 0;
5203
+ const out = [];
5204
+ for (const raw of issues.slice(0, RUN_ERROR_ISSUES_MAX)) {
5205
+ if (!raw || typeof raw !== "object") continue;
5206
+ const o = raw;
5207
+ if (typeof o.code !== "string" || !o.code) continue;
5208
+ out.push(pruneUndefined({
5209
+ code: o.code,
5210
+ path: typeof o.path === "string" ? o.path : void 0,
5211
+ message: typeof o.message === "string" ? o.message : void 0
5212
+ }));
5213
+ }
5214
+ return out.length ? out : void 0;
5215
+ }
5216
+ __name(runErrorIssues, "runErrorIssues");
5217
+ __name3(runErrorIssues, "runErrorIssues");
4854
5218
  function runNextAction(run) {
4855
5219
  if (isTerminalRunStatus(run.status)) return "none";
4856
5220
  if (run.status === "suspended" && run.gate?.kind === "budget") return "raise_budget";
@@ -5050,6 +5414,7 @@ function toWorkflowRunSummary(run) {
5050
5414
  repairOf: run.repairOf,
5051
5415
  repairRunIds: run.repairRunIds,
5052
5416
  trigger: run.trigger ?? "api",
5417
+ origin: runOrigin(run),
5053
5418
  createdBy: {
5054
5419
  subjectType: principal?.subjectType ?? "system",
5055
5420
  subjectId: principal?.subjectId ?? run.userId ?? ""
@@ -5067,11 +5432,13 @@ function toWorkflowRunSummary(run) {
5067
5432
  usage: runUsage(run),
5068
5433
  // LUA-697: a row persisted before the write seams (#2406 / #2465 / the script tier) leaves scrubbed here too —
5069
5434
  // idempotent on a scrubbed message, bounded input; an empty message falls back to the code.
5070
- error: run.error ? {
5435
+ error: run.error ? pruneUndefined({
5071
5436
  code: run.error.code ?? "error",
5072
5437
  message: scrubStepErrorMessage(run.error.message) ?? run.error.code ?? "error",
5073
- stepId: run.error.stepId
5074
- } : void 0,
5438
+ stepId: run.error.stepId,
5439
+ // LUA-784 (item 3): the unattended pre-start failure's refusal rows (`input_schema_invalid` and kin).
5440
+ issues: runErrorIssues(run.error.issues)
5441
+ }) : void 0,
5075
5442
  kind: "run",
5076
5443
  aclHash: run.aclHash,
5077
5444
  migration: run.migration,
@@ -5724,204 +6091,6 @@ function rebaseItemPointer(pointer, itemsPath, index) {
5724
6091
  }
5725
6092
  __name(rebaseItemPointer, "rebaseItemPointer");
5726
6093
  __name3(rebaseItemPointer, "rebaseItemPointer");
5727
- var APPROVER_SPEC_MAX_USERS = 20;
5728
- var ESCALATION_MAX_HOPS = 3;
5729
- var TemplateBindingSchema = z22.object({
5730
- template: z22.string().min(1).max(2048)
5731
- }).strict();
5732
- var ApproverSpecSchema = z22.union([
5733
- z22.literal("creator"),
5734
- z22.literal("org-admins"),
5735
- z22.object({
5736
- users: z22.union([
5737
- z22.array(z22.string().min(1).max(128)).min(1).max(APPROVER_SPEC_MAX_USERS),
5738
- TemplateBindingSchema
5739
- ])
5740
- }).strict(),
5741
- z22.object({
5742
- role: z22.union([
5743
- z22.string().min(1).max(128),
5744
- TemplateBindingSchema
5745
- ])
5746
- }).strict(),
5747
- z22.object({
5748
- group: z22.union([
5749
- z22.string().min(1).max(128),
5750
- TemplateBindingSchema
5751
- ])
5752
- }).strict(),
5753
- z22.object({
5754
- governance: z22.object({
5755
- policyId: z22.string().min(1).max(128)
5756
- }).strict()
5757
- }).strict()
5758
- ]);
5759
- var FourEyesSchema = z22.object({
5760
- edit: ApproverSpecSchema,
5761
- approve: ApproverSpecSchema
5762
- }).strict();
5763
- var EscalationHopSchema = z22.object({
5764
- escalateTo: ApproverSpecSchema,
5765
- timeoutHours: z22.number().finite().min(1).max(720)
5766
- }).strict();
5767
- var TerminalOutcomeSchema = z22.enum([
5768
- "deny",
5769
- "cancel-run",
5770
- "fail",
5771
- "continue"
5772
- ]);
5773
- var ApprovalOnTimeoutSchema = z22.union([
5774
- TerminalOutcomeSchema,
5775
- EscalationHopSchema,
5776
- z22.array(z22.union([
5777
- TerminalOutcomeSchema,
5778
- EscalationHopSchema
5779
- ])).min(1).max(ESCALATION_MAX_HOPS + 1)
5780
- ]);
5781
- var APPROVER_SPEC_SHAPES = [
5782
- "'creator'",
5783
- "'org-admins'",
5784
- "{users:[userId, \u2026]}",
5785
- "{role:roleName}",
5786
- "{group:groupName}",
5787
- "{governance:{policyId}}"
5788
- ];
5789
- var APPROVER_WRITTEN_MAX = 120;
5790
- var USER_ID_SHAPED_RE = /^[^\s@]{1,128}$/;
5791
- function describeApproverSpecRefusal(spec) {
5792
- const raw = spec === void 0 ? "undefined" : JSON.stringify(spec) ?? String(spec);
5793
- const written = raw.length > APPROVER_WRITTEN_MAX ? `${raw.slice(0, APPROVER_WRITTEN_MAX - 1)}\u2026` : raw;
5794
- const users = typeof spec === "object" && spec !== null ? spec.users : void 0;
5795
- const approver = typeof users === "string" && USER_ID_SHAPED_RE.test(users) ? {
5796
- users: [
5797
- users
5798
- ]
5799
- } : "creator";
5800
- const message = `approver ${written} is not an approver \u2014 legal: ${APPROVER_SPEC_SHAPES.join(" | ")}. 'creator' is the person who started the run: write approver:'creator' for "ask me" / "I approve"; {users:[\u2026]} takes user ids, never emails, names or {type:'user'}` + (approver === "creator" ? "" : `; here: approver:${JSON.stringify(approver)}`);
5801
- return {
5802
- approver,
5803
- written,
5804
- message
5805
- };
5806
- }
5807
- __name(describeApproverSpecRefusal, "describeApproverSpecRefusal");
5808
- __name3(describeApproverSpecRefusal, "describeApproverSpecRefusal");
5809
- var BINDING_ROOTS = [
5810
- "initData",
5811
- "stepResults",
5812
- "requestContext",
5813
- "state"
5814
- ];
5815
- function bindingRootsOk(template22) {
5816
- const refs = [
5817
- ...template22.matchAll(/\$\{\s*([A-Za-z_$][\w$]*)/g)
5818
- ].map((m) => m[1]);
5819
- return refs.length > 0 && refs.every((r) => BINDING_ROOTS.includes(r));
5820
- }
5821
- __name(bindingRootsOk, "bindingRootsOk");
5822
- __name3(bindingRootsOk, "bindingRootsOk");
5823
- function isTemplateBinding(v) {
5824
- return typeof v === "object" && v !== null && typeof v.template === "string";
5825
- }
5826
- __name(isTemplateBinding, "isTemplateBinding");
5827
- __name3(isTemplateBinding, "isTemplateBinding");
5828
- function validateApproverBlock(node, opts = {
5829
- path: "approval"
5830
- }) {
5831
- const issues = [];
5832
- const push = /* @__PURE__ */ __name3((code, path, message, severity = "error") => issues.push({
5833
- code,
5834
- path,
5835
- severity,
5836
- message
5837
- }), "push");
5838
- const checkSpec = /* @__PURE__ */ __name3((spec, path) => {
5839
- const r = ApproverSpecSchema.safeParse(spec);
5840
- if (!r.success) {
5841
- const users = spec?.users;
5842
- if (Array.isArray(users) && users.length > APPROVER_SPEC_MAX_USERS) push("cap-exceeded", path, `at most ${APPROVER_SPEC_MAX_USERS} users`);
5843
- else {
5844
- const refusal = describeApproverSpecRefusal(spec);
5845
- issues.push({
5846
- code: "approver-invalid",
5847
- path,
5848
- severity: "error",
5849
- message: refusal.message,
5850
- repair: {
5851
- approver: refusal.approver,
5852
- written: refusal.written
5853
- }
5854
- });
5855
- }
5856
- return;
5857
- }
5858
- const s = r.data;
5859
- if (typeof s === "object") {
5860
- if ("governance" in s && !opts.governanceEnabled) push("approver-governance-unavailable", path, "governance reviewer routing is not enabled for this deployment");
5861
- if ("group" in s && typeof s.group === "string" && !opts.scimEnabled && opts.idpGroups?.includes(s.group)) push("approver-idp-group-unavailable", path, "IdP-group approvers are not enabled for this deployment");
5862
- const binding = "users" in s ? s.users : "role" in s ? s.role : "group" in s ? s.group : void 0;
5863
- if (isTemplateBinding(binding)) {
5864
- if (!bindingRootsOk(binding.template)) push("approver-binding-invalid", `${path}.template`, "binding root must be initData / stepResults / requestContext / state");
5865
- if ("users" in s && opts.customerReachable) push("approver-binding-customer-reachable", `${path}.users`, "a customer-reachable workflow may not bind its approver list");
5866
- }
5867
- }
5868
- }, "checkSpec");
5869
- if (node.approver !== void 0) checkSpec(node.approver, `${opts.path}.approver`);
5870
- if (node.fourEyes !== void 0) {
5871
- const r = FourEyesSchema.safeParse(node.fourEyes);
5872
- if (!r.success) push("approver-invalid", `${opts.path}.fourEyes`, "fourEyes needs { edit, approve } approver specs");
5873
- else {
5874
- checkSpec(r.data.edit, `${opts.path}.fourEyes.edit`);
5875
- checkSpec(r.data.approve, `${opts.path}.fourEyes.approve`);
5876
- }
5877
- if (!node.editable) push("four-eyes-requires-editable", `${opts.path}.fourEyes`, "fourEyes requires editable:true");
5878
- if (node.approver !== void 0) push("four-eyes-overrides-approver", `${opts.path}.approver`, "fourEyes replaces approver", "warning");
5879
- if (node.itemsPath) push("four-eyes-items-unsupported", `${opts.path}.fourEyes`, "fourEyes cannot combine with itemsPath");
5880
- }
5881
- if (node.excludeInitiator && (node.approver === void 0 || node.approver === "creator") && !node.fourEyes) push("approver-excludes-only-candidate", `${opts.path}.excludeInitiator`, "'creator' with excludeInitiator leaves no approver");
5882
- if (Array.isArray(node.onTimeout)) {
5883
- const chain = node.onTimeout;
5884
- const hops = chain.filter((m) => typeof m === "object" && m !== null && "escalateTo" in m);
5885
- if (hops.length > ESCALATION_MAX_HOPS) push("escalation-chain-too-long", `${opts.path}.onTimeout`, `at most ${ESCALATION_MAX_HOPS} hops`);
5886
- const last = chain[chain.length - 1];
5887
- if (typeof last === "object" && last !== null) push("escalation-chain-not-terminal", `${opts.path}.onTimeout`, "a chain must end in deny | cancel-run | fail");
5888
- hops.forEach((h, i) => checkSpec(h.escalateTo, `${opts.path}.onTimeout[${i}].escalateTo`));
5889
- } else if (typeof node.onTimeout === "object" && node.onTimeout !== null) {
5890
- checkSpec(node.onTimeout.escalateTo, `${opts.path}.onTimeout.escalateTo`);
5891
- }
5892
- return issues;
5893
- }
5894
- __name(validateApproverBlock, "validateApproverBlock");
5895
- __name3(validateApproverBlock, "validateApproverBlock");
5896
- function liftRenderedApprover(row, rendered) {
5897
- const text = (rendered ?? "").trim();
5898
- if (!text) return null;
5899
- if (row === "users") {
5900
- let members = null;
5901
- if (text.startsWith("[")) {
5902
- try {
5903
- members = JSON.parse(text);
5904
- } catch {
5905
- return null;
5906
- }
5907
- } else members = text.split(",").map((s) => s.trim());
5908
- if (!Array.isArray(members) || members.length === 0 || members.length > APPROVER_SPEC_MAX_USERS) return null;
5909
- if (!members.every((m) => typeof m === "string" && m.length > 0 && m.length <= 128)) return null;
5910
- return {
5911
- users: [
5912
- ...new Set(members)
5913
- ].sort()
5914
- };
5915
- }
5916
- if (text.length > 128 || text.startsWith("[") || text.startsWith("{")) return null;
5917
- return row === "role" ? {
5918
- role: text
5919
- } : {
5920
- group: text
5921
- };
5922
- }
5923
- __name(liftRenderedApprover, "liftRenderedApprover");
5924
- __name3(liftRenderedApprover, "liftRenderedApprover");
5925
6094
  var WORKFLOW_ENV_OVERLAY_MAX_KEYS = 64;
5926
6095
  var WORKFLOW_ENV_OVERLAY_MAX_VALUE_BYTES = 4096;
5927
6096
  var WORKFLOW_ENV_TEMPLATE_SECRET_KEY_RE = /(SECRET|TOKEN|KEY|PASSWORD)$/;
@@ -6205,7 +6374,6 @@ function* singleStepsOf(entry) {
6205
6374
  return;
6206
6375
  case "workflow":
6207
6376
  yield entry;
6208
- if (Array.isArray(entry.graph)) yield* singleStepsOf(entry.graph[1]);
6209
6377
  return;
6210
6378
  case "parallel":
6211
6379
  case "conditional":
@@ -6364,7 +6532,10 @@ var assertPredicate = /* @__PURE__ */ __name((p, where) => {
6364
6532
  }, "assertPredicate");
6365
6533
  var assertRetry = /* @__PURE__ */ __name((r, id) => {
6366
6534
  if (!r) return;
6367
- if (typeof r.maxAttempts === "number" && r.maxAttempts > WORKFLOW_RETRY_MAX_ATTEMPTS) throw new LuaWorkflowBuildError("cap-exceeded", `"${id}": ${workflowRetryMaxAttemptsMessage(r.maxAttempts)}`);
6535
+ if (r.maxAttempts !== void 0 && !isWithinWorkflowRetryAttempts(r.maxAttempts)) {
6536
+ const over = typeof r.maxAttempts === "number" && r.maxAttempts > WORKFLOW_RETRY_MAX_ATTEMPTS;
6537
+ throw new LuaWorkflowBuildError(over ? "cap-exceeded" : "invalid-envelope", `"${id}": ${workflowRetryMaxAttemptsMessage(r.maxAttempts)}`);
6538
+ }
6368
6539
  if (r.backoff !== void 0 && !WORKFLOW_RETRY_BACKOFFS.includes(r.backoff)) throw new LuaWorkflowBuildError("backoff-invalid", `"${id}": retry.backoff must be ${WORKFLOW_RETRY_BACKOFFS.map((b) => `'${b}'`).join(" | ")}`);
6369
6540
  if (r.maxBackoffSeconds !== void 0) {
6370
6541
  if (r.backoff !== "exponential") throw new LuaWorkflowBuildError("backoff-invalid", `"${id}": retry.maxBackoffSeconds is only meaningful with backoff:'exponential'`);
@@ -6514,14 +6685,13 @@ function stepNodeOf(s) {
6514
6685
  __name(stepNodeOf, "stepNodeOf");
6515
6686
  function materializeEntry(entry, steps) {
6516
6687
  const single = /* @__PURE__ */ __name((n2) => {
6517
- if (n2.type === "step" && steps[n2.step.id]) return stepNodeOf(steps[n2.step.id]);
6518
- if (n2.type === "workflow" && n2.workflowId === WORKFLOW_ARM_SUBRUN_ID && n2.graph) return {
6519
- ...n2,
6520
- graph: [
6521
- n2.graph[0],
6522
- single(n2.graph[1])
6523
- ]
6524
- };
6688
+ if (n2.type === "step" && steps[n2.step.id]) {
6689
+ const node = stepNodeOf(steps[n2.step.id]);
6690
+ return n2.input !== void 0 ? {
6691
+ ...node,
6692
+ input: n2.input
6693
+ } : node;
6694
+ }
6525
6695
  return n2;
6526
6696
  }, "single");
6527
6697
  switch (entry.type) {