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.
- package/dist/api-exports.d.ts +5 -3
- package/dist/api-exports.js +634 -297
- package/dist/api-exports.js.map +1 -1
- package/dist/index.js +1443 -923
- package/dist/index.js.map +1 -1
- package/dist/workflow-builder.d.ts +5 -3
- package/dist/workflow-builder.js +426 -256
- package/dist/workflow-builder.js.map +1 -1
- package/docs/README.md +2 -2
- package/docs/api/LuaWorkflow.md +1 -1
- package/docs/workflows/approvals.md +2 -0
- package/docs/workflows/recovery.md +1 -1
- package/docs/workflows/schedules.md +13 -4
- package/package.json +4 -4
- package/template/examples/workflows/research-brief.ts +29 -16
- package/template/package.json +1 -1
package/dist/workflow-builder.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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`,
|
|
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`,
|
|
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 ===
|
|
3601
|
+
if (node.type === "workflow" && (typeof node.workflowId !== "string" || node.workflowId.length === 0)) {
|
|
3281
3602
|
checkId(node.id, path);
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
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" &&
|
|
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
|
-
|
|
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 (
|
|
3355
|
-
err("editable-path-invalid", "`editablePaths`
|
|
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
|
|
4224
|
-
const stepId = nodeIdOf(step22);
|
|
4537
|
+
function inlineContainerArm(mapping, step22) {
|
|
4225
4538
|
return {
|
|
4226
|
-
|
|
4227
|
-
|
|
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(
|
|
4237
|
-
__name3(
|
|
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
|
|
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
|
-
|
|
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 (
|
|
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])
|
|
6518
|
-
|
|
6519
|
-
|
|
6520
|
-
|
|
6521
|
-
n2.
|
|
6522
|
-
|
|
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) {
|