lua-cli 3.33.0 → 3.35.0

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.
@@ -100,6 +100,14 @@ function isAllowedReviewableExecuteTool(tool) {
100
100
  }
101
101
  __name(isAllowedReviewableExecuteTool, "isAllowedReviewableExecuteTool");
102
102
  __name2(isAllowedReviewableExecuteTool, "isAllowedReviewableExecuteTool");
103
+ var REVIEWABLE_STANDING_START_TOOLS = [
104
+ "scheduleWorkflow"
105
+ ];
106
+ function isReviewableStandingStartTool(tool) {
107
+ return REVIEWABLE_STANDING_START_TOOLS.includes(tool);
108
+ }
109
+ __name(isReviewableStandingStartTool, "isReviewableStandingStartTool");
110
+ __name2(isReviewableStandingStartTool, "isReviewableStandingStartTool");
103
111
  var REVIEWABLE_MCP_SEND_TOOL_SUFFIX = "_create_messaging_message";
104
112
  function isReviewableMcpSendTool(tool) {
105
113
  return tool.length > REVIEWABLE_MCP_SEND_TOOL_SUFFIX.length && tool.endsWith(REVIEWABLE_MCP_SEND_TOOL_SUFFIX);
@@ -152,7 +160,7 @@ function mcpSendSiblingForDraftTool(tool, availableToolIds) {
152
160
  __name(mcpSendSiblingForDraftTool, "mcpSendSiblingForDraftTool");
153
161
  __name2(mcpSendSiblingForDraftTool, "mcpSendSiblingForDraftTool");
154
162
  function isReviewableExecuteTool(tool) {
155
- return isAllowedReviewableExecuteTool(tool) || isReviewableMcpSendTool(tool);
163
+ return isAllowedReviewableExecuteTool(tool) || isReviewableMcpSendTool(tool) || isReviewableStandingStartTool(tool);
156
164
  }
157
165
  __name(isReviewableExecuteTool, "isReviewableExecuteTool");
158
166
  __name2(isReviewableExecuteTool, "isReviewableExecuteTool");
@@ -312,7 +320,10 @@ function transformChatHistoryContentParts(parts) {
312
320
  content.push({
313
321
  type: "file",
314
322
  data: part.data,
315
- mediaType
323
+ mediaType,
324
+ ...part.filename && {
325
+ filename: part.filename
326
+ }
316
327
  });
317
328
  }
318
329
  }
@@ -1206,6 +1217,70 @@ function normalizeLuaJobExecutionTimeoutSeconds(timeout) {
1206
1217
  }
1207
1218
  __name(normalizeLuaJobExecutionTimeoutSeconds, "normalizeLuaJobExecutionTimeoutSeconds");
1208
1219
  __name2(normalizeLuaJobExecutionTimeoutSeconds, "normalizeLuaJobExecutionTimeoutSeconds");
1220
+ var KEYS = /* @__PURE__ */ new Set([
1221
+ "v",
1222
+ "kind",
1223
+ "agentId",
1224
+ "jobId",
1225
+ "scheduledTime",
1226
+ "triggerId"
1227
+ ]);
1228
+ var MAX_BODY_BYTES = 4096;
1229
+ var MAX_ID_LENGTH = 512;
1230
+ var UTC_ISO = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/;
1231
+ function isId(value3) {
1232
+ return typeof value3 === "string" && value3.length > 0 && value3.length <= MAX_ID_LENGTH;
1233
+ }
1234
+ __name(isId, "isId");
1235
+ __name2(isId, "isId");
1236
+ function parseScheduledJobFire(raw) {
1237
+ if (new TextEncoder().encode(raw).byteLength > MAX_BODY_BYTES) {
1238
+ throw new Error("Invalid scheduled job fire: body is too large");
1239
+ }
1240
+ let value3;
1241
+ try {
1242
+ value3 = JSON.parse(raw);
1243
+ } catch {
1244
+ throw new Error("Invalid scheduled job fire: body is not JSON");
1245
+ }
1246
+ if (!value3 || typeof value3 !== "object" || Array.isArray(value3)) {
1247
+ throw new Error("Invalid scheduled job fire: body must be an object");
1248
+ }
1249
+ const record = value3;
1250
+ if (Object.keys(record).some((key) => !KEYS.has(key))) {
1251
+ throw new Error("Invalid scheduled job fire: unknown field");
1252
+ }
1253
+ if (record.v !== 1 || record.kind !== "scheduled-job-fire") {
1254
+ throw new Error("Invalid scheduled job fire: unsupported contract");
1255
+ }
1256
+ if (!isId(record.agentId)) {
1257
+ throw new Error("Invalid scheduled job fire: agentId is required");
1258
+ }
1259
+ if (!isId(record.jobId)) {
1260
+ throw new Error("Invalid scheduled job fire: jobId is required");
1261
+ }
1262
+ if (typeof record.scheduledTime !== "string" || !UTC_ISO.test(record.scheduledTime) || !Number.isFinite(Date.parse(record.scheduledTime)) || ![
1263
+ new Date(record.scheduledTime).toISOString(),
1264
+ new Date(record.scheduledTime).toISOString().replace(".000Z", "Z")
1265
+ ].includes(record.scheduledTime)) {
1266
+ throw new Error("Invalid scheduled job fire: scheduledTime must be ISO 8601");
1267
+ }
1268
+ if (record.triggerId !== void 0 && !isId(record.triggerId)) {
1269
+ throw new Error("Invalid scheduled job fire: triggerId must be a non-empty string");
1270
+ }
1271
+ return {
1272
+ v: 1,
1273
+ kind: "scheduled-job-fire",
1274
+ agentId: record.agentId,
1275
+ jobId: record.jobId,
1276
+ scheduledTime: record.scheduledTime,
1277
+ ...typeof record.triggerId === "string" ? {
1278
+ triggerId: record.triggerId
1279
+ } : {}
1280
+ };
1281
+ }
1282
+ __name(parseScheduledJobFire, "parseScheduledJobFire");
1283
+ __name2(parseScheduledJobFire, "parseScheduledJobFire");
1209
1284
  var TEMPLATE_TRIGGER_URL_ENV_PREFIX = "LUA_TRIGGER_URL__";
1210
1285
  function triggerUrlEnvKey(triggerKey) {
1211
1286
  const upper = triggerKey.trim().replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "").toUpperCase();
@@ -1550,7 +1625,20 @@ var ProjectedResourceSchema = z3.object({
1550
1625
  "platform-allowlist"
1551
1626
  ]),
1552
1627
  /** Product data only. A roster row confers nothing. */
1553
- rostered: z3.boolean()
1628
+ rostered: z3.boolean(),
1629
+ /**
1630
+ * Ownership, product data only (PRO-1754). `createdBy` is the creating
1631
+ * user's id straight off the sub-agent document; `owner` is that id
1632
+ * resolved to a display identity after the list is decided. Both are
1633
+ * absent when the document records no creator or the lookup fails —
1634
+ * they never influence which resources are listed.
1635
+ */
1636
+ createdBy: z3.string().min(1).max(256).optional(),
1637
+ owner: z3.object({
1638
+ id: z3.string().min(1).max(256),
1639
+ name: z3.string().optional(),
1640
+ email: z3.string().optional()
1641
+ }).optional()
1554
1642
  }).passthrough();
1555
1643
  var CapabilityProfilesSchema = z3.record(z3.string().min(1).max(64), z3.array(ProjectedScopeSchema));
1556
1644
  var RoleCatalogSchema = z3.record(z3.string().min(1).max(128), z3.object({
@@ -3078,6 +3166,169 @@ var fromKnowledge = /* @__PURE__ */ __name4((k) => ({
3078
3166
  }), "fromKnowledge");
3079
3167
  var SideEffectsSchema = z4.enum(WORKFLOW_SIDE_EFFECTS);
3080
3168
  var JobResourcesSchema = z4.enum(WORKFLOW_JOB_RESOURCES);
3169
+ var WORKFLOW_MODEL_TEMPLATE_ROOT = "initData";
3170
+ var WORKFLOW_MODEL_TEMPLATE_RE = /^\$\{\s*initData\.([A-Za-z0-9_.\-[\]]+)\s*(?:\|([^}]*))?\}$/;
3171
+ var PATH_SEGMENT_RE = /^[A-Za-z0-9_-]+(?:\[(?:0|[1-9]\d*)\])*$/;
3172
+ function looksLikeModelTemplate(model) {
3173
+ return typeof model === "string" && model.includes("${");
3174
+ }
3175
+ __name(looksLikeModelTemplate, "looksLikeModelTemplate");
3176
+ __name4(looksLikeModelTemplate, "looksLikeModelTemplate");
3177
+ function parseModelTemplate(model) {
3178
+ if (!looksLikeModelTemplate(model)) return {
3179
+ kind: "static"
3180
+ };
3181
+ const trimmed = model.trim();
3182
+ const m = WORKFLOW_MODEL_TEMPLATE_RE.exec(trimmed);
3183
+ if (!m) {
3184
+ const inner = /^\$\{([^}]*)\}$/.exec(trimmed)?.[1];
3185
+ const root = inner?.split(/[.|]/, 1)[0];
3186
+ const why = inner !== void 0 && root !== void 0 && root !== WORKFLOW_MODEL_TEMPLATE_ROOT ? `only the \`${WORKFLOW_MODEL_TEMPLATE_ROOT}\` root is allowed (got \`${root || "(empty)"}\`)` : inner !== void 0 && (inner === WORKFLOW_MODEL_TEMPLATE_ROOT || inner.startsWith(`${WORKFLOW_MODEL_TEMPLATE_ROOT}.`)) ? "the path after `initData.` is empty" : "the whole value must be exactly one placeholder \u2014 no prefix, suffix or second placeholder";
3187
+ return {
3188
+ kind: "invalid",
3189
+ message: modelTemplateInvalidMessage(model, why)
3190
+ };
3191
+ }
3192
+ const path = m[1];
3193
+ if (!path.split(".").every((seg) => PATH_SEGMENT_RE.test(seg))) {
3194
+ return {
3195
+ kind: "invalid",
3196
+ message: modelTemplateInvalidMessage(model, `the path \`${path}\` is not a dotted path of names and canonical [n] indexes`)
3197
+ };
3198
+ }
3199
+ if (m[2] !== void 0) {
3200
+ const dflt = m[2].trim();
3201
+ if (!dflt) return {
3202
+ kind: "invalid",
3203
+ message: modelTemplateInvalidMessage(model, "the default after `|` is empty")
3204
+ };
3205
+ if (dflt.includes("|")) {
3206
+ return {
3207
+ kind: "invalid",
3208
+ message: modelTemplateInvalidMessage(model, "a placeholder names ONE default \u2014 a second `|` is not a fallback chain")
3209
+ };
3210
+ }
3211
+ return {
3212
+ kind: "template",
3213
+ template: {
3214
+ placeholder: trimmed,
3215
+ path,
3216
+ default: dflt
3217
+ }
3218
+ };
3219
+ }
3220
+ return {
3221
+ kind: "template",
3222
+ template: {
3223
+ placeholder: trimmed,
3224
+ path
3225
+ }
3226
+ };
3227
+ }
3228
+ __name(parseModelTemplate, "parseModelTemplate");
3229
+ __name4(parseModelTemplate, "parseModelTemplate");
3230
+ function modelTemplateInvalidMessage(model, why) {
3231
+ return `model "${model.trim()}" is not a valid run-input-bound model placeholder \u2014 ${why}; the forms are \${initData.<path>} and \${initData.<path>|<provider/model default>}`;
3232
+ }
3233
+ __name(modelTemplateInvalidMessage, "modelTemplateInvalidMessage");
3234
+ __name4(modelTemplateInvalidMessage, "modelTemplateInvalidMessage");
3235
+ function modelTemplateDefaultRequiredMessage(model) {
3236
+ return `model "${model}" runs on the Job tier, so its placeholder needs a default \u2014 the harness / provider gates (harness:'claude-code' needs an Anthropic model) classify it at push; write ${model.replace(/\}$/, "|<provider/model>}")}`;
3237
+ }
3238
+ __name(modelTemplateDefaultRequiredMessage, "modelTemplateDefaultRequiredMessage");
3239
+ __name4(modelTemplateDefaultRequiredMessage, "modelTemplateDefaultRequiredMessage");
3240
+ function staticModelPin(model) {
3241
+ if (typeof model !== "string") return void 0;
3242
+ const parsed = parseModelTemplate(model);
3243
+ if (parsed.kind === "static") return model;
3244
+ if (parsed.kind === "template") return parsed.template.default;
3245
+ return void 0;
3246
+ }
3247
+ __name(staticModelPin, "staticModelPin");
3248
+ __name4(staticModelPin, "staticModelPin");
3249
+ function readPath(root, path) {
3250
+ let cur = root;
3251
+ for (const seg of path.split(".")) {
3252
+ const name = seg.replace(/\[(?:0|[1-9]\d*)\]/g, "");
3253
+ const indexes = [
3254
+ ...seg.matchAll(/\[(0|[1-9]\d*)\]/g)
3255
+ ].map((x) => Number(x[1]));
3256
+ if (cur === null || typeof cur !== "object") return void 0;
3257
+ cur = cur[name];
3258
+ for (const i of indexes) {
3259
+ if (!Array.isArray(cur)) return void 0;
3260
+ cur = cur[i];
3261
+ }
3262
+ }
3263
+ return cur;
3264
+ }
3265
+ __name(readPath, "readPath");
3266
+ __name4(readPath, "readPath");
3267
+ function renderModelTemplate(model, initData) {
3268
+ if (model === void 0 || !looksLikeModelTemplate(model)) return {
3269
+ ok: true,
3270
+ model,
3271
+ source: "static"
3272
+ };
3273
+ const parsed = parseModelTemplate(model);
3274
+ if (parsed.kind === "invalid") {
3275
+ return {
3276
+ ok: false,
3277
+ placeholder: model.trim(),
3278
+ path: "",
3279
+ reason: "invalid",
3280
+ message: parsed.message
3281
+ };
3282
+ }
3283
+ if (parsed.kind === "static") return {
3284
+ ok: true,
3285
+ model,
3286
+ source: "static"
3287
+ };
3288
+ const { placeholder, path } = parsed.template;
3289
+ if (initData !== void 0 && initData !== null && (typeof initData !== "object" || Array.isArray(initData))) {
3290
+ return {
3291
+ ok: false,
3292
+ placeholder,
3293
+ path,
3294
+ reason: "input-not-object",
3295
+ message: modelTemplateInputNotObjectMessage(placeholder, initData)
3296
+ };
3297
+ }
3298
+ const value22 = readPath(initData, path);
3299
+ const reason = value22 === void 0 || value22 === null ? "unbound" : typeof value22 !== "string" ? "not-a-string" : value22.trim() ? null : "empty";
3300
+ if (reason === null) return {
3301
+ ok: true,
3302
+ model: value22.trim(),
3303
+ source: "initData"
3304
+ };
3305
+ if (parsed.template.default !== void 0) return {
3306
+ ok: true,
3307
+ model: parsed.template.default,
3308
+ source: "default"
3309
+ };
3310
+ return {
3311
+ ok: false,
3312
+ placeholder,
3313
+ path,
3314
+ reason,
3315
+ message: modelTemplateUnboundMessage(placeholder, path, reason)
3316
+ };
3317
+ }
3318
+ __name(renderModelTemplate, "renderModelTemplate");
3319
+ __name4(renderModelTemplate, "renderModelTemplate");
3320
+ function modelTemplateUnboundMessage(placeholder, path, reason) {
3321
+ const what = reason === "unbound" ? `the run input has no ${WORKFLOW_MODEL_TEMPLATE_ROOT}.${path}` : reason === "empty" ? `${WORKFLOW_MODEL_TEMPLATE_ROOT}.${path} is empty on the run input` : `${WORKFLOW_MODEL_TEMPLATE_ROOT}.${path} on the run input is not a string`;
3322
+ return `model placeholder "${placeholder}" is unbound \u2014 ${what} and the placeholder declares no default; pass one in the run input or write \${${WORKFLOW_MODEL_TEMPLATE_ROOT}.${path}|<provider/model>}`;
3323
+ }
3324
+ __name(modelTemplateUnboundMessage, "modelTemplateUnboundMessage");
3325
+ __name4(modelTemplateUnboundMessage, "modelTemplateUnboundMessage");
3326
+ function modelTemplateInputNotObjectMessage(placeholder, initData) {
3327
+ const type = Array.isArray(initData) ? "an array" : `a ${typeof initData}`;
3328
+ return `model placeholder "${placeholder}" cannot be rendered \u2014 the run input is ${type}, not an object; start the run with an object input (a JSON string must be parsed before it is passed)`;
3329
+ }
3330
+ __name(modelTemplateInputNotObjectMessage, "modelTemplateInputNotObjectMessage");
3331
+ __name4(modelTemplateInputNotObjectMessage, "modelTemplateInputNotObjectMessage");
3081
3332
  var APPROVER_SPEC_MAX_USERS = 20;
3082
3333
  var ESCALATION_MAX_HOPS = 3;
3083
3334
  var TemplateBindingSchema = z22.object({
@@ -3618,6 +3869,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
3618
3869
  }
3619
3870
  }
3620
3871
  const declaredKeys = new Set(opts.connectionKeys ?? []);
3872
+ const ownKeys = /* @__PURE__ */ new Set();
3621
3873
  if (g.connections !== void 0 && !Array.isArray(g.connections)) {
3622
3874
  err("connection-declaration-invalid", "`connections` must be an array of { key, integrationType }", "connections");
3623
3875
  }
@@ -3629,7 +3881,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
3629
3881
  err("connection-declaration-invalid", `connections[${i}].key must match ${WORKFLOW_CONNECTION_KEY_RE}`, `${path}.key`);
3630
3882
  return;
3631
3883
  }
3632
- if (declaredKeys.has(key)) {
3884
+ if (ownKeys.has(key)) {
3633
3885
  err("connection-declaration-invalid", `connections[${i}].key "${key}" is declared twice`, `${path}.key`);
3634
3886
  return;
3635
3887
  }
@@ -3637,6 +3889,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
3637
3889
  err("connection-declaration-invalid", `connections[${i}] ("${key}") needs an integrationType (the catalog slug, e.g. 'github')`, `${path}.integrationType`);
3638
3890
  return;
3639
3891
  }
3892
+ ownKeys.add(key);
3640
3893
  declaredKeys.add(key);
3641
3894
  });
3642
3895
  const undeclaredKey = /* @__PURE__ */ __name4((ref) => typeof ref === "string" && !declaredKeys.has(ref) && isConnectionKeyShaped(ref) && opts.connectionIds?.has(ref) !== true, "undeclaredKey");
@@ -3763,7 +4016,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
3763
4016
  if (node.harness !== void 0 && node.tier !== "job") {
3764
4017
  err("harness-requires-job-tier", "`harness` is only legal on a tier:'job' agent step", `${path}.harness`, id);
3765
4018
  }
3766
- const provider = node.type === "agent" ? classifyModelProvider(node.model) : null;
4019
+ const provider = node.type === "agent" ? classifyModelProvider(staticModelPin(node.model)) : null;
3767
4020
  if (node.harness === "claude-code" && provider !== null && provider !== "anthropic") {
3768
4021
  err("harness-provider-mismatch", `harness:'claude-code' needs an Anthropic model (model "${node.type === "agent" ? node.model : ""}" is ${provider})`, `${path}.harness`, id);
3769
4022
  }
@@ -3773,18 +4026,32 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
3773
4026
  }, "checkTier");
3774
4027
  const checkModel = /* @__PURE__ */ __name4((node, path) => {
3775
4028
  if (node.type !== "agent" || typeof node.model !== "string") return;
4029
+ const id = singleId(node);
4030
+ const parsed = parseModelTemplate(node.model);
4031
+ if (parsed.kind === "invalid") {
4032
+ err("model-template-invalid", parsed.message, `${path}.model`, id);
4033
+ return;
4034
+ }
4035
+ if (parsed.kind === "template" && parsed.template.default === void 0) {
4036
+ if (isJobTier(node)) {
4037
+ err("model-template-default-required", modelTemplateDefaultRequiredMessage(node.model), `${path}.model`, id);
4038
+ }
4039
+ return;
4040
+ }
4041
+ const pin = parsed.kind === "template" ? parsed.template.default : node.model;
4042
+ if (pin === void 0) return;
4043
+ const inContext = /* @__PURE__ */ __name4((message) => parsed.kind === "template" ? `${message} (the default of ${parsed.template.placeholder})` : message, "inContext");
3776
4044
  const registry = opts.approvedModels;
3777
4045
  if (registry === void 0) return;
3778
- const id = singleId(node);
3779
4046
  if (registry === "unavailable") {
3780
- const pin = node.model.trim();
3781
- if (pin && !normalizeModelId(pin, []).ok) {
3782
- 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);
4047
+ const trimmed = pin.trim();
4048
+ if (trimmed && !normalizeModelId(trimmed, []).ok) {
4049
+ warn("model-unresolved", inContext(`model "${trimmed}" 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);
3783
4050
  }
3784
4051
  return;
3785
4052
  }
3786
- const resolved = normalizeModelId(node.model, registry);
3787
- if (!resolved.ok) err("model-unresolved", modelUnresolvedMessage(resolved), `${path}.model`, id);
4053
+ const resolved = normalizeModelId(pin, registry);
4054
+ if (!resolved.ok) err("model-unresolved", inContext(modelUnresolvedMessage(resolved)), `${path}.model`, id);
3788
4055
  }, "checkModel");
3789
4056
  const checkWorkspace = /* @__PURE__ */ __name4((node, path) => {
3790
4057
  const id = singleId(node);
@@ -5764,7 +6031,16 @@ var STEP_ERROR_DETAIL_KEYS = [
5764
6031
  "secretName",
5765
6032
  "executionId",
5766
6033
  "credentialsExecutionId",
5767
- "expired"
6034
+ "expired",
6035
+ // Run-input-bound model pins (the Principal Engineer stage models): the Job tier's `MODEL_ERROR` refusals name the
6036
+ // pin they judged (`requested`), the declared harness, the provider the classifier read and the precedence leg the
6037
+ // pin came from (`source`: initData / default / node / env / org / stamp), and `provider_unsupported`'s fleet
6038
+ // (`allowed`, the LUA_WF_JOB_PROVIDERS list) — the members the PR body promises.
6039
+ "requested",
6040
+ "harness",
6041
+ "provider",
6042
+ "source",
6043
+ "allowed"
5768
6044
  ];
5769
6045
  var STEP_ERROR_DETAIL_MAX_BYTES = 8 * 1024;
5770
6046
  var DETAIL_MAX_DEPTH = 4;
@@ -6766,6 +7042,42 @@ function needsInheritedWorkspace(graph) {
6766
7042
  }
6767
7043
  __name(needsInheritedWorkspace, "needsInheritedWorkspace");
6768
7044
  __name4(needsInheritedWorkspace, "needsInheritedWorkspace");
7045
+ function armEntry2(arm) {
7046
+ return Array.isArray(arm) ? arm[arm.length - 1] : arm;
7047
+ }
7048
+ __name(armEntry2, "armEntry2");
7049
+ __name4(armEntry2, "armEntry");
7050
+ function entryHasJobTier(entry) {
7051
+ const n2 = armEntry2(entry);
7052
+ if (!n2 || typeof n2 !== "object") return false;
7053
+ const node = n2;
7054
+ if (node.tier === "job") return true;
7055
+ const ws = node.workspace;
7056
+ if (ws !== void 0 && ws !== "inherit") return true;
7057
+ switch (node.type) {
7058
+ case "parallel":
7059
+ return Array.isArray(node.steps) && node.steps.some(entryHasJobTier);
7060
+ case "conditional":
7061
+ return Array.isArray(node.steps) && node.steps.some(entryHasJobTier) || node.otherwise !== void 0 && entryHasJobTier(node.otherwise);
7062
+ case "foreach":
7063
+ case "loop":
7064
+ return entryHasJobTier(node.step);
7065
+ default:
7066
+ return false;
7067
+ }
7068
+ }
7069
+ __name(entryHasJobTier, "entryHasJobTier");
7070
+ __name4(entryHasJobTier, "entryHasJobTier");
7071
+ function graphHasJobTierStep(envelopeOrGraph) {
7072
+ if (!envelopeOrGraph || typeof envelopeOrGraph !== "object") return false;
7073
+ const env = envelopeOrGraph;
7074
+ const envWorkspace = env.workspace;
7075
+ if (envWorkspace !== void 0 && envWorkspace !== "inherit") return true;
7076
+ const graph = Array.isArray(envelopeOrGraph) ? envelopeOrGraph : env.definition?.graph ?? [];
7077
+ return Array.isArray(graph) && graph.some(entryHasJobTier);
7078
+ }
7079
+ __name(graphHasJobTierStep, "graphHasJobTierStep");
7080
+ __name4(graphHasJobTierStep, "graphHasJobTierStep");
6769
7081
 
6770
7082
  // src/types/workflow.ts
6771
7083
  function createStep(s) {