lua-cli 3.32.2 → 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.
@@ -353,6 +353,84 @@ function isDesktopFileCommandName(value3) {
353
353
  function isDesktopFileSessionId(value3) {
354
354
  return typeof value3 === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value3);
355
355
  }
356
+ function isModelIdSentinel(input) {
357
+ const lower = (input ?? "").trim().toLowerCase();
358
+ return lower === "auto" || lower.startsWith("auto/");
359
+ }
360
+ function normalizeModelId(input, registry) {
361
+ const requested = typeof input === "string" ? input.trim() : "";
362
+ if (!requested) return {
363
+ ok: false,
364
+ reason: "empty",
365
+ requested,
366
+ candidates: []
367
+ };
368
+ const lower = requested.toLowerCase();
369
+ if (isModelIdSentinel(lower)) return {
370
+ ok: true,
371
+ id: lower,
372
+ form: "sentinel"
373
+ };
374
+ const slash = requested.indexOf("/");
375
+ const malformed = slash === 0;
376
+ const provider = slash > 0 ? lower.slice(0, slash) : void 0;
377
+ if (provider && MODEL_ID_BYOK_PROVIDERS.includes(provider)) {
378
+ return {
379
+ ok: true,
380
+ id: requested,
381
+ form: "byok"
382
+ };
383
+ }
384
+ const bareId = slash >= 0 ? lower.slice(slash + 1) : lower;
385
+ const lastSegment = bareId.slice(bareId.lastIndexOf("/") + 1);
386
+ const exact = /* @__PURE__ */ new Set();
387
+ const hints = /* @__PURE__ */ new Set();
388
+ for (const code of registry) {
389
+ if (typeof code !== "string" || !code) continue;
390
+ const codeLower = code.toLowerCase();
391
+ if (!malformed && codeLower === lower) return {
392
+ ok: true,
393
+ id: code,
394
+ form: provider ? "canonical" : "bare"
395
+ };
396
+ const i = codeLower.indexOf("/");
397
+ if (i < 0) continue;
398
+ const codeBare = codeLower.slice(i + 1);
399
+ if (bareId && codeBare === bareId) exact.add(code);
400
+ else if (lastSegment && codeBare.slice(codeBare.lastIndexOf("/") + 1) === lastSegment) hints.add(code);
401
+ }
402
+ const sorted = [
403
+ ...exact
404
+ ].sort();
405
+ if (!provider && !malformed && sorted.length === 1) return {
406
+ ok: true,
407
+ id: sorted[0],
408
+ form: "bare"
409
+ };
410
+ if (!provider && !malformed && sorted.length > 1) {
411
+ return {
412
+ ok: false,
413
+ reason: "ambiguous",
414
+ requested,
415
+ candidates: sorted
416
+ };
417
+ }
418
+ return {
419
+ ok: false,
420
+ reason: "unknown",
421
+ requested,
422
+ candidates: sorted.length ? sorted : [
423
+ ...hints
424
+ ].sort()
425
+ };
426
+ }
427
+ function modelUnresolvedMessage(r) {
428
+ if (r.reason === "empty") return "model pin is empty \u2014 pin an approved model (provider/model) or omit `model`";
429
+ if (r.reason === "ambiguous") {
430
+ return `model "${r.requested}" does not resolve to one approved model \u2014 it names ${r.candidates.length}; pin one of: ${r.candidates.join(", ")}`;
431
+ }
432
+ 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`;
433
+ }
356
434
  function isImplicitModelSelectionSource(source) {
357
435
  return source !== void 0 && IMPLICIT_MODEL_SELECTION_SOURCES.includes(source);
358
436
  }
@@ -653,6 +731,48 @@ function scheduledTimeKey(scheduledTime) {
653
731
  function scheduledWorkflowRunIdForTime(jobId, scheduledTime) {
654
732
  return scheduledWorkflowRunId(jobId, scheduledTime);
655
733
  }
734
+ function workflowRetryMaxAttemptsMessage(got) {
735
+ const tail = got === void 0 ? "" : ` (got ${JSON.stringify(got)})`;
736
+ return `\`retry.maxAttempts\` must be an integer ${WORKFLOW_RETRY_MIN_ATTEMPTS}..${WORKFLOW_RETRY_MAX_ATTEMPTS}${tail}`;
737
+ }
738
+ function isWithinWorkflowRetryAttempts(value3) {
739
+ return typeof value3 === "number" && Number.isInteger(value3) && value3 >= WORKFLOW_RETRY_MIN_ATTEMPTS && value3 <= WORKFLOW_RETRY_MAX_ATTEMPTS;
740
+ }
741
+ function workflowRetryUnknownMembersMessage(keys) {
742
+ const named = keys.map((k) => {
743
+ const engine = WORKFLOW_RETRY_ENGINE_KEYS.includes(k);
744
+ return `\`${k}\`${engine ? " (engine-owned \u2014 stamped by resetAttempts, never authored)" : ""}`;
745
+ });
746
+ return `\`retry\` has no member ${named.join(", ")}; members: ${WORKFLOW_RETRY_POLICY_KEYS.join(", ")}`;
747
+ }
748
+ function unknownWorkflowRetryMembers(retry) {
749
+ if (!retry || typeof retry !== "object" || Array.isArray(retry)) return [];
750
+ return Object.keys(retry).filter((k) => !WORKFLOW_RETRY_POLICY_KEYS.includes(k));
751
+ }
752
+ function authoredRetryPolicy(retry) {
753
+ if (!retry || typeof retry !== "object" || Array.isArray(retry)) return void 0;
754
+ const src = retry;
755
+ const out = {};
756
+ for (const k of WORKFLOW_RETRY_POLICY_KEYS) if (src[k] !== void 0) out[k] = src[k];
757
+ return Object.keys(out).length ? out : void 0;
758
+ }
759
+ function retryBudgetBaseAttempt(row) {
760
+ const base = row.retry?.budgetBaseAttempt;
761
+ return typeof base === "number" && Number.isSafeInteger(base) && base > 0 ? base : 0;
762
+ }
763
+ function retryBudgetAttempt(row) {
764
+ return Math.max(0, row.attempt - retryBudgetBaseAttempt(row));
765
+ }
766
+ function retryBudgetMaxAttempts(row) {
767
+ const max = row.retry?.maxAttempts;
768
+ return typeof max === "number" && Number.isFinite(max) && max >= 1 ? max : 1;
769
+ }
770
+ function retryBudgetRemaining(row) {
771
+ return retryBudgetAttempt(row) < retryBudgetMaxAttempts(row);
772
+ }
773
+ function retriesRemaining(row) {
774
+ return Math.max(0, retryBudgetMaxAttempts(row) - retryBudgetAttempt(row));
775
+ }
656
776
  function isWithinWorkflowJobRange(member, value3) {
657
777
  const { min, max } = WORKFLOW_JOB_RANGES[member];
658
778
  return typeof value3 === "number" && Number.isInteger(value3) && value3 >= min && value3 <= max;
@@ -840,7 +960,66 @@ ${PREAMBLE}
840
960
 
841
961
  ${items.join("\n\n")}`;
842
962
  }
843
- var __defProp2, __name2, CHANNEL_SEND_CHANNELS, REVIEWABLE_ACTION_EXECUTE_TOOL_ALLOWLIST, REVIEWABLE_MCP_SEND_TOOL_SUFFIX, MCP_TOOL_READ_VERB_RE, MCP_DRAFT_CREATE_VERBS, NON_INTERACTIVE_CHANNELS, RICH_PARTS_MESSAGE_ID_PREFIX, SCREENSHOT_MESSAGE_ID_PREFIX, BROWSER_COMMANDS, BROWSER_COMMAND_NAMES, DESKTOP_FILE_COMMANDS, DESKTOP_FILE_COMMAND_SET, REASONING_EFFORT_VALUES, IMPLICIT_MODEL_SELECTION_SOURCES, PLATFORM_FALLBACK_MODEL_SOURCE, AGENT_NAME_TOKEN, DEFAULT_PERSONA_GUIDE, PERSONAL_SPACE_STARTING_PERSONA, CORE_DRAINING_CODE, CORE_DRAINING_DEFAULT_RETRY_MS, CORE_DRAINING_MAX_RETRY_MS, VoiceNameSchema, PluginProviderSchema, RealtimeProviderSchema, PluginClassSchema, ModelDescriptorSchema, InferenceModelSchema, PluginModelSchema, RealtimeModelSchema, LuaVoiceModelSchema, TurnDetectionSchema, InterruptionSchema, BuiltinAudioClipSchema, AudioConfigSchema, BackgroundAudioEntrySchema, BackgroundAudioSchema, LuaVoiceConfigInnerSchema, LuaVoiceConfigSchema, LuaVoiceRefSchema, LUA_JOB_DEFAULT_TIMEOUT_SECONDS, LUA_JOB_MIN_TIMEOUT_SECONDS, LUA_JOB_MAX_TIMEOUT_SECONDS, TEMPLATE_TRIGGER_URL_ENV_PREFIX, SUBJECT_TYPES, SubjectTypeSchema, CREDENTIAL_TYPES, CredentialTypeSchema, DEVICE_OPERATIONS, DeviceOperationSchema, DEVICE_SCOPE_BY_OPERATION, DeviceBindingSchema, IdSchema, PrincipalDescriptorSchema, ActorDescriptorSchema, PrincipalOwnerSchema, CredentialLifecycleSchema, GeneralCredentialDescriptorSchema, DeviceCredentialDescriptorSchema, GeneralPrincipalContextSchema, DeviceCredentialPrincipalContextSchema, RawPrincipalContextSchema, PrincipalContextSchema, DeviceCredentialClaimSchema, LUA_CLIENT_HEADER, LUA_CLIENT_APPS, SEMVER_PATTERN, WEB_RELEASE_PATTERN, CLIENT_HEADER_PATTERN, AUTHZ_PROJECTION_VERSION, ProjectedScopeSchema, DisplayRoleSchema, AuthorizationPrincipalSchema, CredentialContextSchema, ProjectionAnomalySchema, ProjectedOrgSchema, ProjectedResourceSchema, CapabilityProfilesSchema, RoleCatalogSchema, EffectiveAuthorizationSchema, ResourcePageSchema, SYSTEM_USER_PREFIX, WORKFLOW_RUN_IN_FLIGHT, WORKFLOW_RUN_IDLE, WORKFLOW_RUN_TERMINAL, WORKFLOW_RUN_STATUSES, WORKFLOW_STEP_STATUSES, WORKFLOW_STEP_IN_FLIGHT, ARCHIVE_WINDOW_MARGIN_DAYS, WORKFLOW_ORG_PURGING_TTL_S, WORKFLOW_ORG_PURGE_FORCE_AFTER_MS, IDEMPOTENCY_HOLDING_STATUSES, WORKFLOW_SCHEDULED_RUN_ID_PREFIX, CLOUD_TASK_RUN_ID_PREFIX, WORKFLOW_OPERATION_ID_PREFIX, WORKFLOW_CONNECTION_KEY_RE, WORKFLOW_SIGNAL_PAYLOAD_MAX_BYTES, WORKFLOW_RESOLVE_OUTPUT_MAX_BYTES, WORKFLOW_RETRY_BACKOFFS, WORKFLOW_JOB_RESOURCES, WORKFLOW_SIDE_EFFECTS, WORKFLOW_JOB_RANGES, WORKFLOW_JOB_RANGE_MEMBERS, WORKFLOW_SINGLE_STEP_TYPES, WORKFLOW_HITL_ENTRY_TYPES, WORKFLOW_ARM_ENTRY_TYPES, WORKFLOW_HITL_ARM_CONTAINERS, WORKFLOW_GRAPH_ENTRY_STEP_KINDS, WORKFLOW_ARM_ENTRY_STEP_KINDS, WORKFLOW_BUDGET_MAX_DURATION_SECONDS, REDACTED_PLACEHOLDER, PROVIDER_MESSAGE_MAX_CHARS, ERROR_MESSAGE_MAX_CHARS, SECRET_LITERAL_PATTERNS, SECRET_NAME, SECRET_PAIR_PATTERNS, GROUP_COUNT, WORKFLOW_SECRET_KEY_RE, WORKFLOW_RESERVED_SECRET_KEYS, SCRUB_INPUT_MAX_CHARS, SCRUB_CUT_BACKOFF_CHARS, WORKFLOW_AUDIT_EVENTS, WORKFLOW_AUDIT_METADATA_MAX_BYTES, INDENT, WRAP_WIDTH, NOUNS, GET_TOOL_NAMES, PREAMBLE;
963
+ function workflowApprovalDecisionOf(resumeData) {
964
+ if (resumeData.timedOut === true) return "timed_out";
965
+ return resumeData.approved === true ? "approved" : "denied";
966
+ }
967
+ function workflowApprovalOutput(resumeData) {
968
+ const decision = typeof resumeData.decision === "string" ? resumeData.decision : workflowApprovalDecisionOf(resumeData);
969
+ const note = typeof resumeData.note === "string" && resumeData.note.trim() !== "" ? resumeData.note : void 0;
970
+ const text = typeof resumeData.text === "string" ? resumeData.text : note ?? decision;
971
+ return {
972
+ ...resumeData,
973
+ decision,
974
+ text
975
+ };
976
+ }
977
+ function isWorkflowApprovalOutput(value3) {
978
+ if (typeof value3 !== "object" || value3 === null || Array.isArray(value3)) return false;
979
+ const v = value3;
980
+ return typeof v.approved === "boolean" && WORKFLOW_APPROVAL_OUTPUT_DECISIONS.includes(v.decision) && typeof v.text === "string";
981
+ }
982
+ function extractSingleJsonValue(text) {
983
+ const trimmed = (text ?? "").trim();
984
+ if (!trimmed) return {
985
+ reason: "reply is empty"
986
+ };
987
+ const fenced = [
988
+ ...trimmed.matchAll(JSON_FENCE_RE)
989
+ ];
990
+ if (fenced.length > 1) return {
991
+ reason: "reply carries more than one fenced block"
992
+ };
993
+ const candidate = fenced.length === 1 ? fenced[0][1].trim() : trimmed;
994
+ try {
995
+ return {
996
+ value: JSON.parse(candidate)
997
+ };
998
+ } catch {
999
+ if (fenced.length === 1) return {
1000
+ reason: "fenced block is not valid JSON"
1001
+ };
1002
+ }
1003
+ const opens = [
1004
+ trimmed.indexOf("{"),
1005
+ trimmed.indexOf("[")
1006
+ ].filter((i) => i !== -1);
1007
+ const start = opens.length ? Math.min(...opens) : -1;
1008
+ const end = Math.max(trimmed.lastIndexOf("}"), trimmed.lastIndexOf("]"));
1009
+ if (start === -1 || end <= start) return {
1010
+ reason: "reply is not JSON"
1011
+ };
1012
+ try {
1013
+ return {
1014
+ value: JSON.parse(trimmed.slice(start, end + 1))
1015
+ };
1016
+ } catch {
1017
+ return {
1018
+ reason: "reply does not contain a single JSON value"
1019
+ };
1020
+ }
1021
+ }
1022
+ var __defProp2, __name2, CHANNEL_SEND_CHANNELS, REVIEWABLE_ACTION_EXECUTE_TOOL_ALLOWLIST, REVIEWABLE_MCP_SEND_TOOL_SUFFIX, MCP_TOOL_READ_VERB_RE, MCP_DRAFT_CREATE_VERBS, NON_INTERACTIVE_CHANNELS, RICH_PARTS_MESSAGE_ID_PREFIX, SCREENSHOT_MESSAGE_ID_PREFIX, BROWSER_COMMANDS, BROWSER_COMMAND_NAMES, DESKTOP_FILE_COMMANDS, DESKTOP_FILE_COMMAND_SET, MODEL_ID_BYOK_PROVIDERS, REASONING_EFFORT_VALUES, IMPLICIT_MODEL_SELECTION_SOURCES, PLATFORM_FALLBACK_MODEL_SOURCE, AGENT_NAME_TOKEN, DEFAULT_PERSONA_GUIDE, PERSONAL_SPACE_STARTING_PERSONA, CORE_DRAINING_CODE, CORE_DRAINING_DEFAULT_RETRY_MS, CORE_DRAINING_MAX_RETRY_MS, VoiceNameSchema, PluginProviderSchema, RealtimeProviderSchema, PluginClassSchema, ModelDescriptorSchema, InferenceModelSchema, PluginModelSchema, RealtimeModelSchema, LuaVoiceModelSchema, TurnDetectionSchema, InterruptionSchema, BuiltinAudioClipSchema, AudioConfigSchema, BackgroundAudioEntrySchema, BackgroundAudioSchema, LuaVoiceConfigInnerSchema, LuaVoiceConfigSchema, LuaVoiceRefSchema, LUA_JOB_DEFAULT_TIMEOUT_SECONDS, LUA_JOB_MIN_TIMEOUT_SECONDS, LUA_JOB_MAX_TIMEOUT_SECONDS, TEMPLATE_TRIGGER_URL_ENV_PREFIX, SUBJECT_TYPES, SubjectTypeSchema, CREDENTIAL_TYPES, CredentialTypeSchema, DEVICE_OPERATIONS, DeviceOperationSchema, DEVICE_SCOPE_BY_OPERATION, DeviceBindingSchema, IdSchema, PrincipalDescriptorSchema, ActorDescriptorSchema, PrincipalOwnerSchema, CredentialLifecycleSchema, GeneralCredentialDescriptorSchema, DeviceCredentialDescriptorSchema, GeneralPrincipalContextSchema, DeviceCredentialPrincipalContextSchema, RawPrincipalContextSchema, PrincipalContextSchema, DeviceCredentialClaimSchema, LUA_CLIENT_HEADER, LUA_CLIENT_APPS, SEMVER_PATTERN, WEB_RELEASE_PATTERN, CLIENT_HEADER_PATTERN, AUTHZ_PROJECTION_VERSION, ProjectedScopeSchema, DisplayRoleSchema, AuthorizationPrincipalSchema, CredentialContextSchema, ProjectionAnomalySchema, ProjectedOrgSchema, ProjectedResourceSchema, CapabilityProfilesSchema, RoleCatalogSchema, EffectiveAuthorizationSchema, ResourcePageSchema, SYSTEM_USER_PREFIX, WORKFLOW_RUN_IN_FLIGHT, WORKFLOW_RUN_IDLE, WORKFLOW_RUN_TERMINAL, WORKFLOW_RUN_STATUSES, WORKFLOW_STEP_STATUSES, WORKFLOW_STEP_IN_FLIGHT, ARCHIVE_WINDOW_MARGIN_DAYS, WORKFLOW_ORG_PURGING_TTL_S, WORKFLOW_ORG_PURGE_FORCE_AFTER_MS, IDEMPOTENCY_HOLDING_STATUSES, WORKFLOW_SCHEDULED_RUN_ID_PREFIX, CLOUD_TASK_RUN_ID_PREFIX, WORKFLOW_OPERATION_ID_PREFIX, WORKFLOW_CONNECTION_KEY_RE, WORKFLOW_SIGNAL_PAYLOAD_MAX_BYTES, WORKFLOW_RESOLVE_OUTPUT_MAX_BYTES, WORKFLOW_RETRY_BACKOFFS, WORKFLOW_RETRY_MIN_ATTEMPTS, WORKFLOW_RETRY_POLICY_KEYS, WORKFLOW_RETRY_MAX_ATTEMPTS, WORKFLOW_RETRY_ENGINE_KEYS, WORKFLOW_JOB_RESOURCES, WORKFLOW_SIDE_EFFECTS, WORKFLOW_JOB_RANGES, WORKFLOW_JOB_RANGE_MEMBERS, WORKFLOW_SINGLE_STEP_TYPES, WORKFLOW_HITL_ENTRY_TYPES, WORKFLOW_ARM_ENTRY_TYPES, WORKFLOW_HITL_ARM_CONTAINERS, WORKFLOW_GRAPH_ENTRY_STEP_KINDS, WORKFLOW_ARM_ENTRY_STEP_KINDS, WORKFLOW_BUDGET_MAX_DURATION_SECONDS, REDACTED_PLACEHOLDER, PROVIDER_MESSAGE_MAX_CHARS, ERROR_MESSAGE_MAX_CHARS, SECRET_LITERAL_PATTERNS, SECRET_NAME, SECRET_PAIR_PATTERNS, GROUP_COUNT, WORKFLOW_SECRET_KEY_RE, WORKFLOW_RESERVED_SECRET_KEYS, SCRUB_INPUT_MAX_CHARS, SCRUB_CUT_BACKOFF_CHARS, WORKFLOW_AUDIT_EVENTS, WORKFLOW_AUDIT_METADATA_MAX_BYTES, INDENT, WRAP_WIDTH, NOUNS, GET_TOOL_NAMES, PREAMBLE, WORKFLOW_APPROVAL_OUTPUT_DECISIONS, WORKFLOW_APPROVAL_OUTPUT_SCHEMA, JSON_FENCE_RE;
844
1023
  var init_dist = __esm({
845
1024
  "../shared-types/dist/index.mjs"() {
846
1025
  "use strict";
@@ -1157,6 +1336,16 @@ var init_dist = __esm({
1157
1336
  __name2(isDesktopFileCommandName, "isDesktopFileCommandName");
1158
1337
  __name(isDesktopFileSessionId, "isDesktopFileSessionId");
1159
1338
  __name2(isDesktopFileSessionId, "isDesktopFileSessionId");
1339
+ MODEL_ID_BYOK_PROVIDERS = [
1340
+ "azure",
1341
+ "bedrock"
1342
+ ];
1343
+ __name(isModelIdSentinel, "isModelIdSentinel");
1344
+ __name2(isModelIdSentinel, "isModelIdSentinel");
1345
+ __name(normalizeModelId, "normalizeModelId");
1346
+ __name2(normalizeModelId, "normalizeModelId");
1347
+ __name(modelUnresolvedMessage, "modelUnresolvedMessage");
1348
+ __name2(modelUnresolvedMessage, "modelUnresolvedMessage");
1160
1349
  REASONING_EFFORT_VALUES = [
1161
1350
  "off",
1162
1351
  "minimal",
@@ -1928,6 +2117,37 @@ This text is who you are for this person. As you learn them, their name, their w
1928
2117
  "fixed",
1929
2118
  "exponential"
1930
2119
  ];
2120
+ WORKFLOW_RETRY_MIN_ATTEMPTS = 1;
2121
+ WORKFLOW_RETRY_POLICY_KEYS = [
2122
+ "maxAttempts",
2123
+ "backoffSeconds",
2124
+ "backoff",
2125
+ "maxBackoffSeconds"
2126
+ ];
2127
+ WORKFLOW_RETRY_MAX_ATTEMPTS = 20;
2128
+ __name(workflowRetryMaxAttemptsMessage, "workflowRetryMaxAttemptsMessage");
2129
+ __name2(workflowRetryMaxAttemptsMessage, "workflowRetryMaxAttemptsMessage");
2130
+ __name(isWithinWorkflowRetryAttempts, "isWithinWorkflowRetryAttempts");
2131
+ __name2(isWithinWorkflowRetryAttempts, "isWithinWorkflowRetryAttempts");
2132
+ WORKFLOW_RETRY_ENGINE_KEYS = [
2133
+ "budgetBaseAttempt"
2134
+ ];
2135
+ __name(workflowRetryUnknownMembersMessage, "workflowRetryUnknownMembersMessage");
2136
+ __name2(workflowRetryUnknownMembersMessage, "workflowRetryUnknownMembersMessage");
2137
+ __name(unknownWorkflowRetryMembers, "unknownWorkflowRetryMembers");
2138
+ __name2(unknownWorkflowRetryMembers, "unknownWorkflowRetryMembers");
2139
+ __name(authoredRetryPolicy, "authoredRetryPolicy");
2140
+ __name2(authoredRetryPolicy, "authoredRetryPolicy");
2141
+ __name(retryBudgetBaseAttempt, "retryBudgetBaseAttempt");
2142
+ __name2(retryBudgetBaseAttempt, "retryBudgetBaseAttempt");
2143
+ __name(retryBudgetAttempt, "retryBudgetAttempt");
2144
+ __name2(retryBudgetAttempt, "retryBudgetAttempt");
2145
+ __name(retryBudgetMaxAttempts, "retryBudgetMaxAttempts");
2146
+ __name2(retryBudgetMaxAttempts, "retryBudgetMaxAttempts");
2147
+ __name(retryBudgetRemaining, "retryBudgetRemaining");
2148
+ __name2(retryBudgetRemaining, "retryBudgetRemaining");
2149
+ __name(retriesRemaining, "retriesRemaining");
2150
+ __name2(retriesRemaining, "retriesRemaining");
1931
2151
  WORKFLOW_JOB_RESOURCES = [
1932
2152
  "small",
1933
2153
  "medium",
@@ -2200,6 +2420,8 @@ This text is who you are for this person. As you learn them, their name, their w
2200
2420
  "workflow.goal.resumed",
2201
2421
  "workflow.goal.done",
2202
2422
  "workflow.goal.closed",
2423
+ // LUA-760: an ended goal's cadence Job retired (deleted) — inline on done / closed, by R21 / R28, or by sweep #30
2424
+ "workflow.goal.job_retired",
2203
2425
  // --- org policy (02 §2.10 / 09 R23) ---
2204
2426
  "workflow.policy.retention_changed",
2205
2427
  "workflow.policy.pacing_changed",
@@ -2249,6 +2471,79 @@ listed here; never invent a target.`;
2249
2471
  __name2(reachingIt, "reachingIt");
2250
2472
  __name(renderTargetsBlock, "renderTargetsBlock");
2251
2473
  __name2(renderTargetsBlock, "renderTargetsBlock");
2474
+ WORKFLOW_APPROVAL_OUTPUT_DECISIONS = [
2475
+ "approved",
2476
+ "denied",
2477
+ "timed_out"
2478
+ ];
2479
+ WORKFLOW_APPROVAL_OUTPUT_SCHEMA = {
2480
+ type: "object",
2481
+ properties: {
2482
+ approved: {
2483
+ type: "boolean"
2484
+ },
2485
+ decision: {
2486
+ type: "string",
2487
+ enum: [
2488
+ ...WORKFLOW_APPROVAL_OUTPUT_DECISIONS
2489
+ ]
2490
+ },
2491
+ /** the approver's note when given, else the decision word — what `${stepResults.<id>.text}` reads */
2492
+ text: {
2493
+ type: "string"
2494
+ },
2495
+ note: {
2496
+ type: "string"
2497
+ },
2498
+ editedPayload: {},
2499
+ editRevision: {
2500
+ type: "integer"
2501
+ },
2502
+ decidedBy: {
2503
+ type: "object",
2504
+ properties: {
2505
+ id: {
2506
+ type: "string"
2507
+ },
2508
+ kind: {
2509
+ type: "string"
2510
+ }
2511
+ }
2512
+ },
2513
+ timedOut: {
2514
+ type: "boolean"
2515
+ },
2516
+ escalations: {
2517
+ type: "integer"
2518
+ },
2519
+ evidence: {
2520
+ type: "array",
2521
+ items: {
2522
+ type: "string"
2523
+ }
2524
+ },
2525
+ items: {
2526
+ type: "array",
2527
+ items: {
2528
+ type: "object"
2529
+ }
2530
+ }
2531
+ },
2532
+ required: [
2533
+ "approved",
2534
+ "decision",
2535
+ "text"
2536
+ ]
2537
+ };
2538
+ __name(workflowApprovalDecisionOf, "workflowApprovalDecisionOf");
2539
+ __name2(workflowApprovalDecisionOf, "workflowApprovalDecisionOf");
2540
+ __name(workflowApprovalOutput, "workflowApprovalOutput");
2541
+ __name2(workflowApprovalOutput, "workflowApprovalOutput");
2542
+ __name(isWorkflowApprovalOutput, "isWorkflowApprovalOutput");
2543
+ __name2(isWorkflowApprovalOutput, "isWorkflowApprovalOutput");
2544
+ JSON_FENCE_RE = /```(?:json)?[ \t]*\r?\n([\s\S]*?)\r?\n?```/g;
2545
+ __name(extractSingleJsonValue, "extractSingleJsonValue");
2546
+ __name2(extractSingleJsonValue, "extractSingleJsonValue");
2252
2547
  }
2253
2548
  });
2254
2549
 
@@ -2257,139 +2552,466 @@ import { createHash } from "crypto";
2257
2552
  import { z as z4 } from "zod";
2258
2553
  import { z as z22 } from "zod";
2259
2554
  import { createHash as createHash2 } from "crypto";
2260
- function workspaceTemplatePath(template22) {
2261
- const key = template22.trim();
2262
- const expr = WORKSPACE_TEMPLATE_EXPR_RE.exec(key);
2263
- if (expr) return expr[1].split(".");
2264
- if (key.includes("${")) return void 0;
2265
- return key.replace(/^(?:input|initData)\./, "").split(".");
2555
+ function isMapConfigObject(v) {
2556
+ return typeof v === "object" && v !== null && !Array.isArray(v);
2266
2557
  }
2267
- function retryBackoffs() {
2268
- if (!Array.isArray(WORKFLOW_RETRY_BACKOFFS)) {
2269
- throw new Error("@lua/shared-types.WORKFLOW_RETRY_BACKOFFS is not a tuple \u2014 a jest.mock('@lua/shared-types') must spread jest.requireActual('@lua/shared-types')");
2558
+ function parseMapConfig(raw, stepId) {
2559
+ if (isMapConfigObject(raw)) return raw;
2560
+ if (typeof raw !== "string") {
2561
+ throw new Error(`Stored mapping step "${stepId}" has a mapConfig that is neither a JSON string nor an object.`);
2562
+ }
2563
+ try {
2564
+ return JSON.parse(raw);
2565
+ } catch (e) {
2566
+ throw new Error(`Stored mapping step "${stepId}" has invalid JSON mapConfig: ${e.message}`);
2270
2567
  }
2271
- return WORKFLOW_RETRY_BACKOFFS;
2272
2568
  }
2273
- function sleepUntilUnsupportedMessage(id) {
2274
- return `the engine does not execute \`sleepUntil\` yet (node "${id}") \u2014 replace it with a \`sleep\` node with a \`duration\` in ms, e.g. { type: 'sleep', id: '${id}', duration: ${SLEEP_UNTIL_REPLACEMENT.duration} }`;
2569
+ function mapConfigWire(raw) {
2570
+ if (typeof raw === "string") return raw;
2571
+ if (isMapConfigObject(raw)) return canonicalJson(raw);
2572
+ return void 0;
2275
2573
  }
2276
- function fillPolicy(node, defaultTimeout) {
2277
- if (node.tier === void 0 && node.workspace !== void 0 && node.workspace !== "inherit") node.tier = "job";
2278
- if (node.timeoutSeconds === void 0) node.timeoutSeconds = node.tier === "job" ? WORKFLOW_JOB_DEFAULT_TIMEOUT_SECONDS : defaultTimeout;
2279
- if (node.retry === void 0) node.retry = {
2280
- maxAttempts: 1
2281
- };
2282
- if (node.onError === void 0) node.onError = "fail";
2283
- if ((node.type === "step" || node.type === "tool") && node.sideEffects === void 0) node.sideEffects = "none";
2574
+ function describeBadPlaceholder(template22, idx, rawExpr) {
2575
+ return `Template placeholder #${idx} (\${${rawExpr}}) in '${template22}'`;
2284
2576
  }
2285
- function fillSingle(node) {
2286
- switch (node.type) {
2287
- case "step": {
2288
- fillPolicy(node, WORKFLOW_STEP_DEFAULT_TIMEOUT_SECONDS);
2289
- const s = node;
2290
- if (s.resumeTimeoutHours === void 0) s.resumeTimeoutHours = WORKFLOW_SUSPEND_DEFAULT_TIMEOUT_HOURS;
2291
- if (s.onSuspendTimeout === void 0) s.onSuspendTimeout = "fail";
2292
- return;
2293
- }
2294
- case "agent":
2295
- fillPolicy(node, WORKFLOW_AGENT_DEFAULT_TIMEOUT_SECONDS);
2296
- return;
2297
- case "tool":
2298
- fillPolicy(node, WORKFLOW_STEP_DEFAULT_TIMEOUT_SECONDS);
2299
- return;
2300
- case "workflow":
2301
- if (node.workflowId === WORKFLOW_ARM_SUBRUN_ID && Array.isArray(node.graph) && node.graph[1]) fillSingle(node.graph[1]);
2302
- return;
2303
- }
2577
+ function parseTemplatePlaceholder(rawExpr) {
2578
+ const dot = rawExpr.indexOf(".");
2579
+ return {
2580
+ scope: dot === -1 ? rawExpr : rawExpr.slice(0, dot),
2581
+ rest: dot === -1 ? "" : rawExpr.slice(dot + 1)
2582
+ };
2304
2583
  }
2305
- function fillHitl(node) {
2306
- if (node.type === "approval") {
2307
- const a = node;
2308
- if (a.approver === void 0) a.approver = "creator";
2309
- if (a.timeoutHours === void 0) a.timeoutHours = WORKFLOW_SUSPEND_DEFAULT_TIMEOUT_HOURS;
2310
- if (a.onTimeout === void 0) a.onTimeout = "deny";
2311
- if (a.onDeny === void 0) a.onDeny = "continue";
2312
- if (a.excludeInitiator === void 0) a.excludeInitiator = false;
2313
- if (a.editable === void 0) a.editable = false;
2314
- return;
2584
+ function traverseMappingPath(root, path3, errorLabel) {
2585
+ if (path3 === "" || path3 === ".") return root;
2586
+ const parts = path3.split(".");
2587
+ let value22 = root;
2588
+ for (const part of parts) {
2589
+ if (typeof value22 === "object" && value22 !== null) value22 = value22[part];
2590
+ else throw new WorkflowTemplateError(`Invalid path ${path3} in ${errorLabel}`, path3);
2315
2591
  }
2316
- const w = node;
2317
- if (w.timeoutHours === void 0) w.timeoutHours = WORKFLOW_SUSPEND_DEFAULT_TIMEOUT_HOURS;
2318
- if (w.onTimeout === void 0) w.onTimeout = "fail";
2319
- if (w.acceptedSources === void 0) w.acceptedSources = [
2320
- ...WORKFLOW_SIGNAL_DEFAULT_SOURCES
2321
- ];
2322
- }
2323
- function fillArm(arm) {
2324
- if (arm.type === "mapping") return;
2325
- if (isHitlNode(arm)) fillHitl(arm);
2326
- else fillSingle(arm);
2592
+ return value22;
2327
2593
  }
2328
- function fillEntry(entry) {
2329
- switch (entry.type) {
2330
- case "step":
2331
- case "agent":
2332
- case "tool":
2333
- case "workflow":
2334
- fillSingle(entry);
2335
- return;
2336
- case "parallel":
2337
- entry.steps.forEach(fillArm);
2338
- return;
2339
- case "conditional": {
2340
- const c = entry;
2341
- if (c.exclusive === void 0) c.exclusive = false;
2342
- c.steps.forEach(fillArm);
2343
- if (c.otherwise) fillArm(c.otherwise);
2344
- return;
2345
- }
2346
- case "foreach": {
2347
- const f = entry;
2348
- f.opts = f.opts ?? {};
2349
- if (f.opts.concurrency === void 0) f.opts.concurrency = WORKFLOW_FOREACH_DEFAULT_CONCURRENCY;
2350
- if (f.opts.maxItems === void 0) f.opts.maxItems = WORKFLOW_FOREACH_DEFAULT_MAX_ITEMS;
2351
- fillArm(f.step);
2352
- return;
2353
- }
2354
- case "loop": {
2355
- const l = entry;
2356
- if (l.maxIterations === void 0) l.maxIterations = WORKFLOW_LOOP_DEFAULT_MAX_ITERATIONS;
2357
- fillArm(l.step);
2358
- return;
2594
+ function stringifyTemplateValue(v, template22, idx, rawExpr) {
2595
+ if (v === null || v === void 0) return "";
2596
+ if (typeof v === "object") {
2597
+ try {
2598
+ return JSON.stringify(v);
2599
+ } catch (err) {
2600
+ throw new WorkflowTemplateError(`${describeBadPlaceholder(template22, idx, rawExpr)} resolved to a value that could not be JSON-stringified (${err.message}).`, rawExpr);
2359
2601
  }
2360
- case "approval":
2361
- case "waitForSignal":
2362
- fillHitl(entry);
2363
- return;
2364
- case "mapping":
2365
- case "sleep":
2366
- case "sleepUntil":
2367
- return;
2368
2602
  }
2603
+ return String(v);
2369
2604
  }
2370
- function withDefaultsFilled(g) {
2371
- const out = clone(g);
2372
- out.definition.graph.forEach(fillEntry);
2373
- return out;
2374
- }
2375
- function isConnectionKeyShaped(value22) {
2376
- return WORKFLOW_CONNECTION_KEY_RE.test(value22) && !CONNECTION_ID_HEX_RE.test(value22);
2377
- }
2378
- function connectionKeyUndeclaredMessage(path3, key) {
2379
- return `${path3} '${key}' is neither a connection id nor a declared connections[].key \u2014 declare it: connections: [{ key: '${key}', integrationType: '<catalog slug, e.g. github>' }] and it resolves on any agent`;
2605
+ function escapeFence(content) {
2606
+ return content.replace(/<\/lua-data/g, "<\\/lua-data");
2380
2607
  }
2381
- function classifyModelProvider(model) {
2382
- const m = (model ?? "").trim().toLowerCase();
2383
- if (!m) return null;
2384
- if (/^(anthropic\/|claude)/.test(m)) return "anthropic";
2385
- if (/^(openai\/|gpt-|o[1-9](-|$)|chatgpt)/.test(m)) return "openai";
2386
- if (/^(google\/|gemini)/.test(m)) return "google";
2387
- return null;
2608
+ function fenceBlock(name, source, content) {
2609
+ return `<lua-data name="${name}" source="${source}" untrusted="true">${escapeFence(content)}</lua-data>`;
2388
2610
  }
2389
- function schemaAtPath(schema, path3) {
2390
- let cur = schema;
2391
- if (!cur || typeof cur !== "object") return void 0;
2392
- for (const seg of path3.split(".").filter(Boolean)) {
2611
+ function renderTemplate(template22, ctx, opts) {
2612
+ let idx = 0;
2613
+ return template22.replace(TEMPLATE_PLACEHOLDER, (_match, rawExpr) => {
2614
+ idx += 1;
2615
+ const { scope, rest } = parseTemplatePlaceholder(rawExpr);
2616
+ const label = describeBadPlaceholder(template22, idx, rawExpr);
2617
+ let rendered;
2618
+ let source;
2619
+ switch (scope) {
2620
+ case "initData":
2621
+ rendered = stringifyTemplateValue(traverseMappingPath(ctx.initData, rest, label), template22, idx, rawExpr);
2622
+ source = "initData";
2623
+ break;
2624
+ case "state":
2625
+ rendered = stringifyTemplateValue(traverseMappingPath(ctx.state, rest, label), template22, idx, rawExpr);
2626
+ source = "state";
2627
+ break;
2628
+ case "requestContext":
2629
+ rendered = stringifyTemplateValue(traverseMappingPath(ctx.requestContext, rest, label), template22, idx, rawExpr);
2630
+ source = "requestContext";
2631
+ break;
2632
+ case "stepResults": {
2633
+ const innerDot = rest.indexOf(".");
2634
+ const stepId = innerDot === -1 ? rest : rest.slice(0, innerDot);
2635
+ const subPath = innerDot === -1 ? "" : rest.slice(innerDot + 1);
2636
+ if (!stepId) throw new WorkflowTemplateError(`${label} must name a step: \${stepResults.<stepId>.<path>}.`, rawExpr);
2637
+ if (!(stepId in ctx.stepResults) || ctx.stepResults[stepId] == null) {
2638
+ throw new WorkflowTemplateError(`${label} references stepResults.${stepId} but step "${stepId}" has no resolvable output (not an ancestor, not run, failed, or produced no output).`, rawExpr);
2639
+ }
2640
+ rendered = stringifyTemplateValue(traverseMappingPath(ctx.stepResults[stepId], subPath, label), template22, idx, rawExpr);
2641
+ source = `step:${stepId}`;
2642
+ break;
2643
+ }
2644
+ default:
2645
+ throw new WorkflowTemplateError(`${label} references unknown namespace "${scope}". Use one of: ${TEMPLATE_NAMESPACES.join(", ")}.`, rawExpr);
2646
+ }
2647
+ return opts.fenced ? fenceBlock(rawExpr, source, rendered) : rendered;
2648
+ });
2649
+ }
2650
+ function isMapDescriptor(v) {
2651
+ if (v === null || typeof v !== "object" || Array.isArray(v)) return false;
2652
+ const d = v;
2653
+ const keys = Object.keys(d);
2654
+ const only = /* @__PURE__ */ __name3((...allowed) => keys.every((k) => allowed.includes(k)), "only");
2655
+ if ("value" in d) return keys.length === 1;
2656
+ if ("template" in d) return keys.length === 1 && typeof d.template === "string";
2657
+ if ("requestContextPath" in d) return keys.length === 1 && typeof d.requestContextPath === "string";
2658
+ if ("knowledge" in d) return keys.length === 1 && typeof d.knowledge === "object" && d.knowledge !== null;
2659
+ if ("initData" in d) return d.initData === true && typeof d.path === "string" && only("initData", "path");
2660
+ if ("step" in d) {
2661
+ const stepOk = typeof d.step === "string" || Array.isArray(d.step) && d.step.every((x) => typeof x === "string");
2662
+ return stepOk && typeof d.path === "string" && only("step", "path", "rows");
2663
+ }
2664
+ return false;
2665
+ }
2666
+ function malformedMapMembers(cfg) {
2667
+ if (!cfg || typeof cfg !== "object" || Array.isArray(cfg)) return [];
2668
+ const out = [];
2669
+ for (const [member, v] of Object.entries(cfg)) {
2670
+ if (!v || typeof v !== "object" || Array.isArray(v) || isMapDescriptor(v)) continue;
2671
+ const keys = Object.keys(v).filter((k) => MAP_DESCRIPTOR_KEYS.includes(k));
2672
+ if (keys.length > 0) out.push({
2673
+ member,
2674
+ keys
2675
+ });
2676
+ }
2677
+ return out;
2678
+ }
2679
+ function mapMemberMalformedMessage(id, m) {
2680
+ const keys = m.keys.map((k) => `\`${k}\``).join(", ");
2681
+ return `"${id}".${m.member} carries descriptor key${m.keys.length === 1 ? "" : "s"} ${keys} but is not an exact binding form ({initData:true, path} | {step, path[, rows]} | {value} | {template} | {requestContextPath} | {knowledge}) \u2014 it is passed to the step verbatim as a literal; fix the descriptor, or wrap it in {value: \u2026} if the literal is intended`;
2682
+ }
2683
+ function resolveDescriptor(key, m, ctx) {
2684
+ if (!isMapDescriptor(m)) return {
2685
+ value: m
2686
+ };
2687
+ try {
2688
+ if ("value" in m) return {
2689
+ value: m.value
2690
+ };
2691
+ if ("template" in m && typeof m.template === "string") {
2692
+ return {
2693
+ value: renderTemplate(m.template, ctx, {
2694
+ fenced: false
2695
+ })
2696
+ };
2697
+ }
2698
+ if ("knowledge" in m || "rows" in m && m.rows !== void 0) {
2699
+ return {
2700
+ error: "binding_unresolved",
2701
+ key
2702
+ };
2703
+ }
2704
+ if ("requestContextPath" in m) {
2705
+ const label = `requestContext path for key "${key}"`;
2706
+ return {
2707
+ value: traverseMappingPath(ctx.requestContext, m.requestContextPath, label)
2708
+ };
2709
+ }
2710
+ if ("path" in m) {
2711
+ const source = "initData" in m && m.initData ? "initData" : "step";
2712
+ if (source === "initData") {
2713
+ return {
2714
+ value: traverseMappingPath(ctx.initData, m.path, `initData for key "${key}"`)
2715
+ };
2716
+ }
2717
+ const stepRef = m.step;
2718
+ const candidates = Array.isArray(stepRef) ? stepRef : [
2719
+ stepRef
2720
+ ];
2721
+ const stepId = candidates.find((s) => ctx.stepResults[s] !== void 0 && ctx.stepResults[s] !== null);
2722
+ if (stepId === void 0) return {
2723
+ error: "binding_unresolved",
2724
+ key
2725
+ };
2726
+ return {
2727
+ value: traverseMappingPath(ctx.stepResults[stepId], m.path, `step ${candidates.join("|")} for key "${key}"`)
2728
+ };
2729
+ }
2730
+ return {
2731
+ error: "binding_unresolved",
2732
+ key
2733
+ };
2734
+ } catch (err) {
2735
+ if (err instanceof WorkflowTemplateError) return {
2736
+ error: "binding_unresolved",
2737
+ key
2738
+ };
2739
+ throw err;
2740
+ }
2741
+ }
2742
+ function resolveMapping(cfg, ctx) {
2743
+ const keys = Object.keys(cfg);
2744
+ if (keys.length === 1 && keys[0] === "") {
2745
+ return resolveDescriptor("", cfg[""], ctx);
2746
+ }
2747
+ const result = {};
2748
+ for (const key of keys) {
2749
+ const resolved = resolveDescriptor(key, cfg[key], ctx);
2750
+ if ("error" in resolved) return resolved;
2751
+ result[key] = resolved.value;
2752
+ }
2753
+ return {
2754
+ value: result
2755
+ };
2756
+ }
2757
+ function describeApproverSpecRefusal(spec) {
2758
+ const raw = spec === void 0 ? "undefined" : JSON.stringify(spec) ?? String(spec);
2759
+ const written = raw.length > APPROVER_WRITTEN_MAX ? `${raw.slice(0, APPROVER_WRITTEN_MAX - 1)}\u2026` : raw;
2760
+ const users = typeof spec === "object" && spec !== null ? spec.users : void 0;
2761
+ const approver = typeof users === "string" && USER_ID_SHAPED_RE.test(users) ? {
2762
+ users: [
2763
+ users
2764
+ ]
2765
+ } : "creator";
2766
+ 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)}`);
2767
+ return {
2768
+ approver,
2769
+ written,
2770
+ message
2771
+ };
2772
+ }
2773
+ function bindingRootsOk(template22) {
2774
+ const refs = [
2775
+ ...template22.matchAll(/\$\{\s*([A-Za-z_$][\w$]*)/g)
2776
+ ].map((m) => m[1]);
2777
+ return refs.length > 0 && refs.every((r) => BINDING_ROOTS.includes(r));
2778
+ }
2779
+ function isTemplateBinding(v) {
2780
+ return typeof v === "object" && v !== null && typeof v.template === "string";
2781
+ }
2782
+ function approvalEditable(node) {
2783
+ if (node.editable === true) return true;
2784
+ if (node.editable === false) return false;
2785
+ return Array.isArray(node.editablePaths) && node.editablePaths.length > 0;
2786
+ }
2787
+ function validateApproverBlock(node, opts = {
2788
+ path: "approval"
2789
+ }) {
2790
+ const issues = [];
2791
+ const push = /* @__PURE__ */ __name3((code, path3, message, severity = "error") => issues.push({
2792
+ code,
2793
+ path: path3,
2794
+ severity,
2795
+ message
2796
+ }), "push");
2797
+ const checkSpec = /* @__PURE__ */ __name3((spec, path3) => {
2798
+ const r = ApproverSpecSchema.safeParse(spec);
2799
+ if (!r.success) {
2800
+ const users = spec?.users;
2801
+ if (Array.isArray(users) && users.length > APPROVER_SPEC_MAX_USERS) push("cap-exceeded", path3, `at most ${APPROVER_SPEC_MAX_USERS} users`);
2802
+ else {
2803
+ const refusal = describeApproverSpecRefusal(spec);
2804
+ issues.push({
2805
+ code: "approver-invalid",
2806
+ path: path3,
2807
+ severity: "error",
2808
+ message: refusal.message,
2809
+ repair: {
2810
+ approver: refusal.approver,
2811
+ written: refusal.written
2812
+ }
2813
+ });
2814
+ }
2815
+ return;
2816
+ }
2817
+ const s = r.data;
2818
+ if (typeof s === "object") {
2819
+ if ("governance" in s && !opts.governanceEnabled) push("approver-governance-unavailable", path3, "governance reviewer routing is not enabled for this deployment");
2820
+ if ("group" in s && typeof s.group === "string" && !opts.scimEnabled && opts.idpGroups?.includes(s.group)) push("approver-idp-group-unavailable", path3, "IdP-group approvers are not enabled for this deployment");
2821
+ const binding = "users" in s ? s.users : "role" in s ? s.role : "group" in s ? s.group : void 0;
2822
+ if (isTemplateBinding(binding)) {
2823
+ if (!bindingRootsOk(binding.template)) push("approver-binding-invalid", `${path3}.template`, "binding root must be initData / stepResults / requestContext / state");
2824
+ if ("users" in s && opts.customerReachable) push("approver-binding-customer-reachable", `${path3}.users`, "a customer-reachable workflow may not bind its approver list");
2825
+ }
2826
+ }
2827
+ }, "checkSpec");
2828
+ if (node.approver !== void 0) checkSpec(node.approver, `${opts.path}.approver`);
2829
+ if (node.fourEyes !== void 0) {
2830
+ const r = FourEyesSchema.safeParse(node.fourEyes);
2831
+ if (!r.success) push("approver-invalid", `${opts.path}.fourEyes`, "fourEyes needs { edit, approve } approver specs");
2832
+ else {
2833
+ checkSpec(r.data.edit, `${opts.path}.fourEyes.edit`);
2834
+ checkSpec(r.data.approve, `${opts.path}.fourEyes.approve`);
2835
+ }
2836
+ if (!approvalEditable(node)) push("four-eyes-requires-editable", `${opts.path}.fourEyes`, "fourEyes requires editable:true");
2837
+ if (node.approver !== void 0) push("four-eyes-overrides-approver", `${opts.path}.approver`, "fourEyes replaces approver", "warning");
2838
+ if (node.itemsPath) push("four-eyes-items-unsupported", `${opts.path}.fourEyes`, "fourEyes cannot combine with itemsPath");
2839
+ }
2840
+ 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");
2841
+ if (Array.isArray(node.onTimeout)) {
2842
+ const chain = node.onTimeout;
2843
+ const hops = chain.filter((m) => typeof m === "object" && m !== null && "escalateTo" in m);
2844
+ if (hops.length > ESCALATION_MAX_HOPS) push("escalation-chain-too-long", `${opts.path}.onTimeout`, `at most ${ESCALATION_MAX_HOPS} hops`);
2845
+ const last = chain[chain.length - 1];
2846
+ if (typeof last === "object" && last !== null) push("escalation-chain-not-terminal", `${opts.path}.onTimeout`, "a chain must end in deny | cancel-run | fail");
2847
+ hops.forEach((h, i) => checkSpec(h.escalateTo, `${opts.path}.onTimeout[${i}].escalateTo`));
2848
+ } else if (typeof node.onTimeout === "object" && node.onTimeout !== null) {
2849
+ checkSpec(node.onTimeout.escalateTo, `${opts.path}.onTimeout.escalateTo`);
2850
+ }
2851
+ return issues;
2852
+ }
2853
+ function liftRenderedApprover(row, rendered) {
2854
+ const text = (rendered ?? "").trim();
2855
+ if (!text) return null;
2856
+ if (row === "users") {
2857
+ let members = null;
2858
+ if (text.startsWith("[")) {
2859
+ try {
2860
+ members = JSON.parse(text);
2861
+ } catch {
2862
+ return null;
2863
+ }
2864
+ } else members = text.split(",").map((s) => s.trim());
2865
+ if (!Array.isArray(members) || members.length === 0 || members.length > APPROVER_SPEC_MAX_USERS) return null;
2866
+ if (!members.every((m) => typeof m === "string" && m.length > 0 && m.length <= 128)) return null;
2867
+ return {
2868
+ users: [
2869
+ ...new Set(members)
2870
+ ].sort()
2871
+ };
2872
+ }
2873
+ if (text.length > 128 || text.startsWith("[") || text.startsWith("{")) return null;
2874
+ return row === "role" ? {
2875
+ role: text
2876
+ } : {
2877
+ group: text
2878
+ };
2879
+ }
2880
+ function workspaceTemplatePath(template22) {
2881
+ const key = template22.trim();
2882
+ const expr = WORKSPACE_TEMPLATE_EXPR_RE.exec(key);
2883
+ if (expr) return expr[1].split(".");
2884
+ if (key.includes("${")) return void 0;
2885
+ return key.replace(/^(?:input|initData)\./, "").split(".");
2886
+ }
2887
+ function retryBackoffs() {
2888
+ if (!Array.isArray(WORKFLOW_RETRY_BACKOFFS)) {
2889
+ throw new Error("@lua/shared-types.WORKFLOW_RETRY_BACKOFFS is not a tuple \u2014 a jest.mock('@lua/shared-types') must spread jest.requireActual('@lua/shared-types')");
2890
+ }
2891
+ return WORKFLOW_RETRY_BACKOFFS;
2892
+ }
2893
+ function sleepUntilUnsupportedMessage(id) {
2894
+ return `the engine does not execute \`sleepUntil\` yet (node "${id}") \u2014 replace it with a \`sleep\` node with a \`duration\` in ms, e.g. { type: 'sleep', id: '${id}', duration: ${SLEEP_UNTIL_REPLACEMENT.duration} }`;
2895
+ }
2896
+ function armSubrunUnsupportedMessage(id, workflowId) {
2897
+ 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)`;
2898
+ }
2899
+ function fillPolicy(node, defaultTimeout) {
2900
+ if (node.tier === void 0 && node.workspace !== void 0 && node.workspace !== "inherit") node.tier = "job";
2901
+ if (node.timeoutSeconds === void 0) node.timeoutSeconds = node.tier === "job" ? WORKFLOW_JOB_DEFAULT_TIMEOUT_SECONDS : defaultTimeout;
2902
+ if (node.retry === void 0) node.retry = {
2903
+ maxAttempts: 1
2904
+ };
2905
+ if (node.onError === void 0) node.onError = "fail";
2906
+ if ((node.type === "step" || node.type === "tool") && node.sideEffects === void 0) node.sideEffects = "none";
2907
+ }
2908
+ function fillSingle(node) {
2909
+ switch (node.type) {
2910
+ case "step": {
2911
+ fillPolicy(node, WORKFLOW_STEP_DEFAULT_TIMEOUT_SECONDS);
2912
+ const s = node;
2913
+ if (s.resumeTimeoutHours === void 0) s.resumeTimeoutHours = WORKFLOW_SUSPEND_DEFAULT_TIMEOUT_HOURS;
2914
+ if (s.onSuspendTimeout === void 0) s.onSuspendTimeout = "fail";
2915
+ return;
2916
+ }
2917
+ case "agent":
2918
+ fillPolicy(node, WORKFLOW_AGENT_DEFAULT_TIMEOUT_SECONDS);
2919
+ return;
2920
+ case "tool":
2921
+ fillPolicy(node, WORKFLOW_STEP_DEFAULT_TIMEOUT_SECONDS);
2922
+ return;
2923
+ case "workflow":
2924
+ return;
2925
+ }
2926
+ }
2927
+ function fillHitl(node) {
2928
+ if (node.type === "approval") {
2929
+ const a = node;
2930
+ if (a.approver === void 0) a.approver = "creator";
2931
+ if (a.timeoutHours === void 0) a.timeoutHours = WORKFLOW_SUSPEND_DEFAULT_TIMEOUT_HOURS;
2932
+ if (a.onTimeout === void 0) a.onTimeout = "deny";
2933
+ if (a.onDeny === void 0) a.onDeny = "continue";
2934
+ if (a.excludeInitiator === void 0) a.excludeInitiator = false;
2935
+ if (a.editable === void 0) a.editable = false;
2936
+ return;
2937
+ }
2938
+ const w = node;
2939
+ if (w.timeoutHours === void 0) w.timeoutHours = WORKFLOW_SUSPEND_DEFAULT_TIMEOUT_HOURS;
2940
+ if (w.onTimeout === void 0) w.onTimeout = "fail";
2941
+ if (w.acceptedSources === void 0) w.acceptedSources = [
2942
+ ...WORKFLOW_SIGNAL_DEFAULT_SOURCES
2943
+ ];
2944
+ }
2945
+ function fillArm(arm) {
2946
+ if (arm.type === "mapping") return;
2947
+ if (isHitlNode(arm)) fillHitl(arm);
2948
+ else fillSingle(arm);
2949
+ }
2950
+ function fillEntry(entry) {
2951
+ switch (entry.type) {
2952
+ case "step":
2953
+ case "agent":
2954
+ case "tool":
2955
+ case "workflow":
2956
+ fillSingle(entry);
2957
+ return;
2958
+ case "parallel":
2959
+ entry.steps.forEach(fillArm);
2960
+ return;
2961
+ case "conditional": {
2962
+ const c = entry;
2963
+ if (c.exclusive === void 0) c.exclusive = false;
2964
+ c.steps.forEach(fillArm);
2965
+ if (c.otherwise) fillArm(c.otherwise);
2966
+ return;
2967
+ }
2968
+ case "foreach": {
2969
+ const f = entry;
2970
+ f.opts = f.opts ?? {};
2971
+ if (f.opts.concurrency === void 0) f.opts.concurrency = WORKFLOW_FOREACH_DEFAULT_CONCURRENCY;
2972
+ if (f.opts.maxItems === void 0) f.opts.maxItems = WORKFLOW_FOREACH_DEFAULT_MAX_ITEMS;
2973
+ fillArm(f.step);
2974
+ return;
2975
+ }
2976
+ case "loop": {
2977
+ const l = entry;
2978
+ if (l.maxIterations === void 0) l.maxIterations = WORKFLOW_LOOP_DEFAULT_MAX_ITERATIONS;
2979
+ fillArm(l.step);
2980
+ return;
2981
+ }
2982
+ case "approval":
2983
+ case "waitForSignal":
2984
+ fillHitl(entry);
2985
+ return;
2986
+ case "mapping":
2987
+ case "sleep":
2988
+ case "sleepUntil":
2989
+ return;
2990
+ }
2991
+ }
2992
+ function withDefaultsFilled(g) {
2993
+ const out = clone(g);
2994
+ out.definition.graph.forEach(fillEntry);
2995
+ return out;
2996
+ }
2997
+ function isConnectionKeyShaped(value22) {
2998
+ return WORKFLOW_CONNECTION_KEY_RE.test(value22) && !CONNECTION_ID_HEX_RE.test(value22);
2999
+ }
3000
+ function connectionKeyUndeclaredMessage(path3, key) {
3001
+ return `${path3} '${key}' is neither a connection id nor a declared connections[].key \u2014 declare it: connections: [{ key: '${key}', integrationType: '<catalog slug, e.g. github>' }] and it resolves on any agent`;
3002
+ }
3003
+ function classifyModelProvider(model) {
3004
+ const m = (model ?? "").trim().toLowerCase();
3005
+ if (!m) return null;
3006
+ if (/^(anthropic\/|claude)/.test(m)) return "anthropic";
3007
+ if (/^(openai\/|gpt-|o[1-9](-|$)|chatgpt)/.test(m)) return "openai";
3008
+ if (/^(google\/|gemini)/.test(m)) return "google";
3009
+ return null;
3010
+ }
3011
+ function schemaAtPath(schema, path3) {
3012
+ let cur = schema;
3013
+ if (!cur || typeof cur !== "object") return void 0;
3014
+ for (const seg of path3.split(".").filter(Boolean)) {
2393
3015
  const props = cur.properties;
2394
3016
  const next = props?.[seg];
2395
3017
  if (!next || typeof next !== "object") return void 0;
@@ -2417,12 +3039,11 @@ function mapConfigStepRefs(raw) {
2417
3039
  if (!cfg) return [];
2418
3040
  const ids = [];
2419
3041
  for (const d of Object.values(cfg)) {
2420
- if (!d || typeof d !== "object") continue;
2421
- const desc = d;
2422
- if (desc.step !== void 0) ids.push(...Array.isArray(desc.step) ? desc.step : [
2423
- desc.step
3042
+ if (!isMapDescriptor(d)) continue;
3043
+ if ("step" in d) ids.push(...Array.isArray(d.step) ? d.step : [
3044
+ d.step
2424
3045
  ]);
2425
- if (typeof desc.template === "string") ids.push(...templateStepRefs(desc.template));
3046
+ if ("template" in d) ids.push(...templateStepRefs(d.template));
2426
3047
  }
2427
3048
  return ids;
2428
3049
  }
@@ -2431,8 +3052,14 @@ function nodeStepRefs(entry) {
2431
3052
  case "agent": {
2432
3053
  const a = entry;
2433
3054
  const p = a.promptTemplate;
2434
- return typeof p === "string" ? templateStepRefs(p) : p && "template" in p ? templateStepRefs(p.template) : [];
3055
+ const prompt = typeof p === "string" ? templateStepRefs(p) : p && "template" in p ? templateStepRefs(p.template) : [];
3056
+ return [
3057
+ ...prompt,
3058
+ ...mapConfigStepRefs(a.input)
3059
+ ];
2435
3060
  }
3061
+ case "step":
3062
+ return mapConfigStepRefs(entry.input);
2436
3063
  case "tool":
2437
3064
  return mapConfigStepRefs(entry.input);
2438
3065
  case "workflow":
@@ -2547,6 +3174,12 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
2547
3174
  const r = node.retry;
2548
3175
  if (!r) return;
2549
3176
  const id = singleId(node);
3177
+ const unknown = unknownWorkflowRetryMembers(r);
3178
+ if (unknown.length) err("invalid-envelope", workflowRetryUnknownMembersMessage(unknown), `${path3}.retry`, id);
3179
+ if (r.maxAttempts !== void 0 && !isWithinWorkflowRetryAttempts(r.maxAttempts)) {
3180
+ const over = typeof r.maxAttempts === "number" && r.maxAttempts > WORKFLOW_RETRY_MAX_ATTEMPTS;
3181
+ err(over ? "cap-exceeded" : "invalid-envelope", workflowRetryMaxAttemptsMessage(r.maxAttempts), `${path3}.retry.maxAttempts`, id);
3182
+ }
2550
3183
  const backoffs = retryBackoffs();
2551
3184
  if (r.backoff !== void 0 && !backoffs.includes(r.backoff)) {
2552
3185
  const list = backoffs.map((b) => `'${b}'`).join(" | ");
@@ -2643,6 +3276,21 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
2643
3276
  err("job-tier-provider-unsupported", `model provider '${provider}' is outside LUA_WF_JOB_PROVIDERS [${opts.policy.jobProviders.join(", ")}]`, `${path3}.model`, id);
2644
3277
  }
2645
3278
  }, "checkTier");
3279
+ const checkModel = /* @__PURE__ */ __name3((node, path3) => {
3280
+ if (node.type !== "agent" || typeof node.model !== "string") return;
3281
+ const registry = opts.approvedModels;
3282
+ if (registry === void 0) return;
3283
+ const id = singleId(node);
3284
+ if (registry === "unavailable") {
3285
+ const pin = node.model.trim();
3286
+ if (pin && !normalizeModelId(pin, []).ok) {
3287
+ 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)`, `${path3}.model`, id);
3288
+ }
3289
+ return;
3290
+ }
3291
+ const resolved = normalizeModelId(node.model, registry);
3292
+ if (!resolved.ok) err("model-unresolved", modelUnresolvedMessage(resolved), `${path3}.model`, id);
3293
+ }, "checkModel");
2646
3294
  const checkWorkspace = /* @__PURE__ */ __name3((node, path3) => {
2647
3295
  const id = singleId(node);
2648
3296
  const ws = workspaceOf(node);
@@ -2689,35 +3337,43 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
2689
3337
  const schema = node.type === "step" ? node.step.outputSchema : node.type === "agent" ? node.outputSchema : void 0;
2690
3338
  if (schema !== void 0) outputSchemas.set(singleId(node), schema);
2691
3339
  }, "recordOutputSchema");
3340
+ const checkMapMembers = /* @__PURE__ */ __name3((cfg, basePath, id) => {
3341
+ for (const m of malformedMapMembers(cfg)) {
3342
+ warn(MAP_MEMBER_MALFORMED_CODE, mapMemberMalformedMessage(id, m), `${basePath}.${m.member}`, id);
3343
+ }
3344
+ }, "checkMapMembers");
3345
+ const checkInputShape = /* @__PURE__ */ __name3((node, path3) => {
3346
+ const input = node.input;
3347
+ if (input === void 0) return;
3348
+ const id = singleId(node);
3349
+ if (input !== null && typeof input === "object" && !Array.isArray(input)) {
3350
+ checkMapMembers(input, `${path3}.input`, id);
3351
+ return;
3352
+ }
3353
+ 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)})`, `${path3}.input`, id);
3354
+ }, "checkInputShape");
3355
+ const checkBodyInput = /* @__PURE__ */ __name3((body, path3, container) => {
3356
+ if (body.type === "workflow" || body.input === void 0) return;
3357
+ 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`, `${path3}.input`, singleId(body));
3358
+ }, "checkBodyInput");
2692
3359
  const checkSingle = /* @__PURE__ */ __name3((node, path3, depth) => {
2693
3360
  recordOutputSchema(node);
2694
- if (node.type === "workflow" && node.workflowId === WORKFLOW_ARM_SUBRUN_ID) {
3361
+ if (node.type === "workflow" && (typeof node.workflowId !== "string" || node.workflowId.length === 0)) {
2695
3362
  checkId(node.id, path3);
2696
- const body = node.graph;
2697
- if (!Array.isArray(body) || body.length !== 2 || body[0]?.type !== "mapping" || !body[1]) {
2698
- err("container-arm-empty", "an implicit arm subrun needs a [mapping, step] body", `${path3}.graph`, node.id);
2699
- return;
2700
- }
2701
- if (body[1].type === "mapping") {
2702
- err("container-arm-empty", "a bare mapping arm has nothing to run", `${path3}.graph.1`, node.id);
2703
- return;
2704
- }
2705
- const inner = body[1];
2706
- if (isHitlNode(inner)) {
2707
- err("node-type-unsupported-in-container", workflowHitlArmShapeMessage(inner.type, inner.id, "mapped-arm"), `${path3}.graph.1`, inner.id);
2708
- return;
2709
- }
2710
- upstream.add(singleId(body[1]));
2711
- checkArm(body[0], `${path3}.graph.0`, depth, "parallel");
2712
- checkSingle(body[1], `${path3}.graph.1`, depth);
2713
- upstream.add(body[0].id);
2714
- upstream.add(singleId(body[1]));
3363
+ err("invalid-envelope", `\`workflowId\` must be a non-empty string naming the workflow to start (got ${JSON.stringify(node.workflowId)})`, `${path3}.workflowId`, node.id);
3364
+ return;
3365
+ }
3366
+ if (node.type === "workflow" && (node.workflowId.startsWith("$") || Array.isArray(node.graph))) {
3367
+ checkId(node.id, path3);
3368
+ err("node-type-unsupported-by-engine", armSubrunUnsupportedMessage(node.id, node.workflowId), path3, node.id);
2715
3369
  return;
2716
3370
  }
2717
3371
  checkId(singleId(node), path3);
2718
3372
  checkPolicyEnums(node, path3);
3373
+ checkInputShape(node, path3);
2719
3374
  checkTimeout(node, path3);
2720
3375
  checkTier(node, path3);
3376
+ checkModel(node, path3);
2721
3377
  checkRetry(node, path3);
2722
3378
  checkWorkspace(node, path3);
2723
3379
  if (!opts.static) {
@@ -2740,7 +3396,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
2740
3396
  if (node.type === "workflow" && node.kind === "subrun" && depth > caps.maxNestingDepth) {
2741
3397
  err("cap-exceeded", `nesting depth ${depth} exceeds ${caps.maxNestingDepth}`, path3, node.id);
2742
3398
  }
2743
- if (node.type === "workflow" && node.workflowId !== WORKFLOW_ARM_SUBRUN_ID && typeof g.definition?.id === "string" && node.workflowId === g.definition.id) {
3399
+ if (node.type === "workflow" && typeof g.definition?.id === "string" && node.workflowId === g.definition.id) {
2744
3400
  err("subrun-cycle", `"${node.id}" starts "${node.workflowId}", which is this workflow itself`, path3, node.id);
2745
3401
  }
2746
3402
  for (const ref of nodeStepRefs(node)) {
@@ -2761,11 +3417,14 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
2761
3417
  if (a.approver === "creator" && a.excludeInitiator === true) {
2762
3418
  err("approver-excludes-only-candidate", "approver:'creator' with excludeInitiator:true always excludes the only candidate", path3, a.id);
2763
3419
  }
2764
- if (a.fourEyes !== void 0 && a.editable !== true) {
3420
+ const editable = approvalEditable(a);
3421
+ if (a.fourEyes !== void 0 && !editable) {
2765
3422
  err("four-eyes-requires-editable", "`fourEyes` requires editable:true", `${path3}.fourEyes`, a.id);
2766
3423
  }
2767
- if ((a.editablePaths !== void 0 || a.editedPayloadSchema !== void 0) && a.editable !== true) {
2768
- err("editable-path-invalid", "`editablePaths` / `editedPayloadSchema` require editable:true", `${path3}.editablePaths`, a.id);
3424
+ if (a.editable === false && Array.isArray(a.editablePaths) && a.editablePaths.length > 0) {
3425
+ err("editable-path-invalid", "`editablePaths` beside editable:false is contradictory \u2014 drop the paths or set editable:true", `${path3}.editablePaths`, a.id);
3426
+ } else if ((a.editablePaths !== void 0 || a.editedPayloadSchema !== void 0) && !editable) {
3427
+ err("editable-path-invalid", "`editablePaths` / `editedPayloadSchema` require editable:true (a non-empty editablePaths implies it)", `${path3}.editablePaths`, a.id);
2769
3428
  }
2770
3429
  for (const p of a.editablePaths ?? []) {
2771
3430
  if (!EDITABLE_PATH_RE.test(p)) err("editable-path-invalid", `editablePaths entry "${p}" is outside the seg(.seg)*[*]/[n] grammar`, `${path3}.editablePaths`, a.id);
@@ -2796,6 +3455,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
2796
3455
  const checkArm = /* @__PURE__ */ __name3((arm, path3, depth, container) => {
2797
3456
  if (arm.type === "mapping") {
2798
3457
  checkId(arm.id, path3);
3458
+ checkMapMembers(readMapConfig(arm.mapConfig), `${path3}.mapConfig`, arm.id);
2799
3459
  for (const ref of nodeStepRefs(arm)) {
2800
3460
  if (!upstream.has(ref)) err("template-reference-unresolved", `"${arm.id}" references stepResults.${ref}, which is not upstream`, path3, arm.id);
2801
3461
  }
@@ -2820,6 +3480,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
2820
3480
  break;
2821
3481
  case "mapping":
2822
3482
  checkId(entry.id, path3);
3483
+ checkMapMembers(readMapConfig(entry.mapConfig), `${path3}.mapConfig`, entry.id);
2823
3484
  for (const ref of nodeStepRefs(entry)) {
2824
3485
  if (!upstream.has(ref)) err("template-reference-unresolved", `"${entry.id}" references stepResults.${ref}, which is not upstream`, path3, entry.id);
2825
3486
  }
@@ -2944,6 +3605,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
2944
3605
  } else checkHitlArm(f.step, `${path3}.step`, "foreach");
2945
3606
  declared.push(f.step.id);
2946
3607
  } else {
3608
+ checkBodyInput(f.step, `${path3}.step`, "foreach");
2947
3609
  checkSingle(f.step, `${path3}.step`, o.chunk ? 2 : 1);
2948
3610
  declared.push(singleId(f.step));
2949
3611
  }
@@ -2964,6 +3626,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
2964
3626
  checkHitlArm(l.step, `${path3}.step`, "loop");
2965
3627
  declared.push(l.step.id);
2966
3628
  } else {
3629
+ checkBodyInput(l.step, `${path3}.step`, "loop");
2967
3630
  checkSingle(l.step, `${path3}.step`, 1);
2968
3631
  declared.push(singleId(l.step));
2969
3632
  }
@@ -3358,230 +4021,64 @@ function renderPredicate(pred) {
3358
4021
  return `${renderRef(pred.value)} in ${JSON.stringify(pred.set)}`;
3359
4022
  case "notIn":
3360
4023
  return `${renderRef(pred.value)} not in ${JSON.stringify(pred.set)}`;
3361
- case "eq":
3362
- return `${renderRef(pred.left)} == ${renderRef(pred.right)}`;
3363
- case "ne":
3364
- return `${renderRef(pred.left)} != ${renderRef(pred.right)}`;
3365
- case "lt":
3366
- return `${renderRef(pred.left)} < ${renderRef(pred.right)}`;
3367
- case "lte":
3368
- return `${renderRef(pred.left)} <= ${renderRef(pred.right)}`;
3369
- case "gt":
3370
- return `${renderRef(pred.left)} > ${renderRef(pred.right)}`;
3371
- case "gte":
3372
- return `${renderRef(pred.left)} >= ${renderRef(pred.right)}`;
3373
- }
3374
- }
3375
- function wrapLabel(child, rendered) {
3376
- return child.op === "and" || child.op === "or" || child.op === "not" ? `(${rendered})` : rendered;
3377
- }
3378
- function renderRef(ref) {
3379
- if ("literal" in ref) return JSON.stringify(ref.literal);
3380
- return ref.path;
3381
- }
3382
- function step(s) {
3383
- const id = stepIdOf(s);
3384
- return {
3385
- path: /* @__PURE__ */ __name3((p) => ({
3386
- path: p === "" ? `stepResults.${id}` : `stepResults.${id}.${p}`
3387
- }), "path")
3388
- };
3389
- }
3390
- function stepOf(id) {
3391
- return step(id);
3392
- }
3393
- function init(path3) {
3394
- return {
3395
- path: path3 === "" ? "initData" : `initData.${path3}`
3396
- };
3397
- }
3398
- function state(path3) {
3399
- return {
3400
- path: path3 === "" ? "state" : `state.${path3}`
3401
- };
3402
- }
3403
- function lit(v) {
3404
- return {
3405
- literal: v
3406
- };
3407
- }
3408
- function toPathOrLiteral(v) {
3409
- if (typeof v === "object" && v !== null) {
3410
- if ("path" in v) return {
3411
- path: v.path
3412
- };
3413
- if ("literal" in v) return {
3414
- literal: v.literal
3415
- };
3416
- }
3417
- return {
3418
- literal: v
3419
- };
3420
- }
3421
- function isMapConfigObject(v) {
3422
- return typeof v === "object" && v !== null && !Array.isArray(v);
3423
- }
3424
- function parseMapConfig(raw, stepId) {
3425
- if (isMapConfigObject(raw)) return raw;
3426
- if (typeof raw !== "string") {
3427
- throw new Error(`Stored mapping step "${stepId}" has a mapConfig that is neither a JSON string nor an object.`);
3428
- }
3429
- try {
3430
- return JSON.parse(raw);
3431
- } catch (e) {
3432
- throw new Error(`Stored mapping step "${stepId}" has invalid JSON mapConfig: ${e.message}`);
3433
- }
3434
- }
3435
- function mapConfigWire(raw) {
3436
- if (typeof raw === "string") return raw;
3437
- if (isMapConfigObject(raw)) return canonicalJson(raw);
3438
- return void 0;
3439
- }
3440
- function describeBadPlaceholder(template22, idx, rawExpr) {
3441
- return `Template placeholder #${idx} (\${${rawExpr}}) in '${template22}'`;
3442
- }
3443
- function parseTemplatePlaceholder(rawExpr) {
3444
- const dot = rawExpr.indexOf(".");
3445
- return {
3446
- scope: dot === -1 ? rawExpr : rawExpr.slice(0, dot),
3447
- rest: dot === -1 ? "" : rawExpr.slice(dot + 1)
3448
- };
3449
- }
3450
- function traverseMappingPath(root, path3, errorLabel) {
3451
- if (path3 === "" || path3 === ".") return root;
3452
- const parts = path3.split(".");
3453
- let value22 = root;
3454
- for (const part of parts) {
3455
- if (typeof value22 === "object" && value22 !== null) value22 = value22[part];
3456
- else throw new WorkflowTemplateError(`Invalid path ${path3} in ${errorLabel}`, path3);
3457
- }
3458
- return value22;
3459
- }
3460
- function stringifyTemplateValue(v, template22, idx, rawExpr) {
3461
- if (v === null || v === void 0) return "";
3462
- if (typeof v === "object") {
3463
- try {
3464
- return JSON.stringify(v);
3465
- } catch (err) {
3466
- throw new WorkflowTemplateError(`${describeBadPlaceholder(template22, idx, rawExpr)} resolved to a value that could not be JSON-stringified (${err.message}).`, rawExpr);
3467
- }
3468
- }
3469
- return String(v);
3470
- }
3471
- function escapeFence(content) {
3472
- return content.replace(/<\/lua-data/g, "<\\/lua-data");
3473
- }
3474
- function fenceBlock(name, source, content) {
3475
- return `<lua-data name="${name}" source="${source}" untrusted="true">${escapeFence(content)}</lua-data>`;
3476
- }
3477
- function renderTemplate(template22, ctx, opts) {
3478
- let idx = 0;
3479
- return template22.replace(TEMPLATE_PLACEHOLDER, (_match, rawExpr) => {
3480
- idx += 1;
3481
- const { scope, rest } = parseTemplatePlaceholder(rawExpr);
3482
- const label = describeBadPlaceholder(template22, idx, rawExpr);
3483
- let rendered;
3484
- let source;
3485
- switch (scope) {
3486
- case "initData":
3487
- rendered = stringifyTemplateValue(traverseMappingPath(ctx.initData, rest, label), template22, idx, rawExpr);
3488
- source = "initData";
3489
- break;
3490
- case "state":
3491
- rendered = stringifyTemplateValue(traverseMappingPath(ctx.state, rest, label), template22, idx, rawExpr);
3492
- source = "state";
3493
- break;
3494
- case "requestContext":
3495
- rendered = stringifyTemplateValue(traverseMappingPath(ctx.requestContext, rest, label), template22, idx, rawExpr);
3496
- source = "requestContext";
3497
- break;
3498
- case "stepResults": {
3499
- const innerDot = rest.indexOf(".");
3500
- const stepId = innerDot === -1 ? rest : rest.slice(0, innerDot);
3501
- const subPath = innerDot === -1 ? "" : rest.slice(innerDot + 1);
3502
- if (!stepId) throw new WorkflowTemplateError(`${label} must name a step: \${stepResults.<stepId>.<path>}.`, rawExpr);
3503
- if (!(stepId in ctx.stepResults) || ctx.stepResults[stepId] == null) {
3504
- throw new WorkflowTemplateError(`${label} references stepResults.${stepId} but step "${stepId}" has no resolvable output (not an ancestor, not run, failed, or produced no output).`, rawExpr);
3505
- }
3506
- rendered = stringifyTemplateValue(traverseMappingPath(ctx.stepResults[stepId], subPath, label), template22, idx, rawExpr);
3507
- source = `step:${stepId}`;
3508
- break;
3509
- }
3510
- default:
3511
- throw new WorkflowTemplateError(`${label} references unknown namespace "${scope}". Use one of: ${TEMPLATE_NAMESPACES.join(", ")}.`, rawExpr);
3512
- }
3513
- return opts.fenced ? fenceBlock(rawExpr, source, rendered) : rendered;
3514
- });
3515
- }
3516
- function resolveDescriptor(key, m, ctx) {
3517
- try {
3518
- if ("value" in m) return {
3519
- value: m.value
3520
- };
3521
- if ("template" in m && typeof m.template === "string") {
3522
- return {
3523
- value: renderTemplate(m.template, ctx, {
3524
- fenced: false
3525
- })
3526
- };
3527
- }
3528
- if ("knowledge" in m || "rows" in m && m.rows !== void 0) {
3529
- return {
3530
- error: "binding_unresolved",
3531
- key
3532
- };
3533
- }
3534
- if ("requestContextPath" in m) {
3535
- const label = `requestContext path for key "${key}"`;
3536
- return {
3537
- value: traverseMappingPath(ctx.requestContext, m.requestContextPath, label)
3538
- };
3539
- }
3540
- if ("path" in m) {
3541
- const source = "initData" in m && m.initData ? "initData" : "step";
3542
- if (source === "initData") {
3543
- return {
3544
- value: traverseMappingPath(ctx.initData, m.path, `initData for key "${key}"`)
3545
- };
3546
- }
3547
- const stepRef = m.step;
3548
- const candidates = Array.isArray(stepRef) ? stepRef : [
3549
- stepRef
3550
- ];
3551
- const stepId = candidates.find((s) => ctx.stepResults[s] !== void 0 && ctx.stepResults[s] !== null);
3552
- if (stepId === void 0) return {
3553
- error: "binding_unresolved",
3554
- key
3555
- };
3556
- return {
3557
- value: traverseMappingPath(ctx.stepResults[stepId], m.path, `step ${candidates.join("|")} for key "${key}"`)
3558
- };
3559
- }
3560
- return {
3561
- error: "binding_unresolved",
3562
- key
3563
- };
3564
- } catch (err) {
3565
- if (err instanceof WorkflowTemplateError) return {
3566
- error: "binding_unresolved",
3567
- key
3568
- };
3569
- throw err;
4024
+ case "eq":
4025
+ return `${renderRef(pred.left)} == ${renderRef(pred.right)}`;
4026
+ case "ne":
4027
+ return `${renderRef(pred.left)} != ${renderRef(pred.right)}`;
4028
+ case "lt":
4029
+ return `${renderRef(pred.left)} < ${renderRef(pred.right)}`;
4030
+ case "lte":
4031
+ return `${renderRef(pred.left)} <= ${renderRef(pred.right)}`;
4032
+ case "gt":
4033
+ return `${renderRef(pred.left)} > ${renderRef(pred.right)}`;
4034
+ case "gte":
4035
+ return `${renderRef(pred.left)} >= ${renderRef(pred.right)}`;
3570
4036
  }
3571
4037
  }
3572
- function resolveMapping(cfg, ctx) {
3573
- const keys = Object.keys(cfg);
3574
- if (keys.length === 1 && keys[0] === "") {
3575
- return resolveDescriptor("", cfg[""], ctx);
3576
- }
3577
- const result = {};
3578
- for (const key of keys) {
3579
- const resolved = resolveDescriptor(key, cfg[key], ctx);
3580
- if ("error" in resolved) return resolved;
3581
- result[key] = resolved.value;
4038
+ function wrapLabel(child, rendered) {
4039
+ return child.op === "and" || child.op === "or" || child.op === "not" ? `(${rendered})` : rendered;
4040
+ }
4041
+ function renderRef(ref) {
4042
+ if ("literal" in ref) return JSON.stringify(ref.literal);
4043
+ return ref.path;
4044
+ }
4045
+ function step(s) {
4046
+ const id = stepIdOf(s);
4047
+ return {
4048
+ path: /* @__PURE__ */ __name3((p) => ({
4049
+ path: p === "" ? `stepResults.${id}` : `stepResults.${id}.${p}`
4050
+ }), "path")
4051
+ };
4052
+ }
4053
+ function stepOf(id) {
4054
+ return step(id);
4055
+ }
4056
+ function init(path3) {
4057
+ return {
4058
+ path: path3 === "" ? "initData" : `initData.${path3}`
4059
+ };
4060
+ }
4061
+ function state(path3) {
4062
+ return {
4063
+ path: path3 === "" ? "state" : `state.${path3}`
4064
+ };
4065
+ }
4066
+ function lit(v) {
4067
+ return {
4068
+ literal: v
4069
+ };
4070
+ }
4071
+ function toPathOrLiteral(v) {
4072
+ if (typeof v === "object" && v !== null) {
4073
+ if ("path" in v) return {
4074
+ path: v.path
4075
+ };
4076
+ if ("literal" in v) return {
4077
+ literal: v.literal
4078
+ };
3582
4079
  }
3583
4080
  return {
3584
- value: result
4081
+ literal: v
3585
4082
  };
3586
4083
  }
3587
4084
  function continuedFailureValue(error, killReason) {
@@ -3603,17 +4100,10 @@ function isContinuedFailureValue(v) {
3603
4100
  const err = o.error;
3604
4101
  return o.__lua_workflow === CONTINUED_FAILURE_TAG && o.failed === true && o.text === "" && err !== null && typeof err === "object" && typeof err.code === "string" && typeof err.message === "string";
3605
4102
  }
3606
- function lowerContainerArm(mapping, step22) {
3607
- const stepId = nodeIdOf(step22);
4103
+ function inlineContainerArm(mapping, step22) {
3608
4104
  return {
3609
- type: "workflow",
3610
- id: `${stepId}_arm`,
3611
- workflowId: WORKFLOW_ARM_SUBRUN_ID,
3612
- kind: "subrun",
3613
- graph: [
3614
- mapping,
3615
- step22
3616
- ]
4105
+ ...step22,
4106
+ input: parseMapConfig(mapping.mapConfig, mapping.id)
3617
4107
  };
3618
4108
  }
3619
4109
  function entryIds(entry) {
@@ -3683,6 +4173,27 @@ function resolvePlacements(calls) {
3683
4173
  break;
3684
4174
  }
3685
4175
  });
4176
+ const armMapPlacementIssue = /* @__PURE__ */ __name3((node, ref, i, container) => {
4177
+ if (!ref.armMap || node.type === "mapping" || isHitlNode2(node)) return void 0;
4178
+ const id = nodeIdOf(node);
4179
+ if ((container === "foreach" || container === "loop") && node.type !== "workflow") {
4180
+ return {
4181
+ code: "mapping-placement",
4182
+ 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`,
4183
+ callIndex: i,
4184
+ stepId: id
4185
+ };
4186
+ }
4187
+ if (node.input !== void 0) {
4188
+ return {
4189
+ code: "mapping-placement",
4190
+ 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)`,
4191
+ callIndex: i,
4192
+ stepId: id
4193
+ };
4194
+ }
4195
+ return void 0;
4196
+ }, "armMapPlacementIssue");
3686
4197
  const hitlPlacementIssue = /* @__PURE__ */ __name3((node, ref, i, container) => {
3687
4198
  if (!isHitlNode2(node)) return void 0;
3688
4199
  const id = node.id;
@@ -3709,7 +4220,7 @@ function resolvePlacements(calls) {
3709
4220
  if (ref.node.type === "mapping" && !allowMapping) {
3710
4221
  issues.push({
3711
4222
  code: "mapping-placement",
3712
- message: `mapping "${ref.node.id}" cannot be a container arm \u2014 chain it as [map, step]`,
4223
+ 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`,
3713
4224
  callIndex: i,
3714
4225
  stepId: ref.node.id
3715
4226
  });
@@ -3730,7 +4241,7 @@ function resolvePlacements(calls) {
3730
4241
  if (d.node.type === "mapping" && !allowMapping) {
3731
4242
  issues.push({
3732
4243
  code: "mapping-placement",
3733
- message: `map "${ref.ref}" cannot be a parallel/foreach/loop arm \u2014 chain it as [map, step]`,
4244
+ 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`,
3734
4245
  callIndex: i,
3735
4246
  stepId: ref.ref
3736
4247
  });
@@ -3741,6 +4252,11 @@ function resolvePlacements(calls) {
3741
4252
  issues.push(hitl);
3742
4253
  return void 0;
3743
4254
  }
4255
+ const mapped = armMapPlacementIssue(d.node, ref, i, container);
4256
+ if (mapped) {
4257
+ issues.push(mapped);
4258
+ return void 0;
4259
+ }
3744
4260
  const prior = placedBy.get(ref.ref);
3745
4261
  if (prior !== void 0 && prior !== i) {
3746
4262
  issues.push({
@@ -3761,6 +4277,10 @@ function resolvePlacements(calls) {
3761
4277
  }
3762
4278
  const hitl = hitlPlacementIssue(ref.node, ref, i, container);
3763
4279
  if (hitl) issues.push(hitl);
4280
+ else {
4281
+ const mapped = armMapPlacementIssue(ref.node, ref, i, container);
4282
+ if (mapped) issues.push(mapped);
4283
+ }
3764
4284
  }, "claim");
3765
4285
  calls.forEach((call, i) => {
3766
4286
  switch (call.kind) {
@@ -3788,7 +4308,7 @@ function resolvePlacements(calls) {
3788
4308
  const lookup = /* @__PURE__ */ __name3((ref) => {
3789
4309
  const n2 = "node" in ref ? ref.node : declared.get(ref.ref)?.node;
3790
4310
  if (!n2 || !ref.armMap || n2.type === "mapping" || isHitlNode2(n2)) return n2;
3791
- return lowerContainerArm(ref.armMap, n2);
4311
+ return inlineContainerArm(ref.armMap, n2);
3792
4312
  }, "lookup");
3793
4313
  calls.forEach((call, i) => {
3794
4314
  switch (call.kind) {
@@ -4173,6 +4693,27 @@ function isTerminalRunStatus(status) {
4173
4693
  function pruneUndefined(o) {
4174
4694
  return Object.fromEntries(Object.entries(o).filter(([, v]) => v !== void 0));
4175
4695
  }
4696
+ function runOrigin(run) {
4697
+ if (run.goalId) return "goal";
4698
+ if (run.jobId || run.trigger === "schedule") return "schedule";
4699
+ if (run.dynamic === true) return run.tags?.includes(WORKFLOW_INLINE_RUN_TAG) ? "inline" : "compose";
4700
+ return "definition";
4701
+ }
4702
+ function runErrorIssues(issues) {
4703
+ if (!Array.isArray(issues)) return void 0;
4704
+ const out = [];
4705
+ for (const raw of issues.slice(0, RUN_ERROR_ISSUES_MAX)) {
4706
+ if (!raw || typeof raw !== "object") continue;
4707
+ const o = raw;
4708
+ if (typeof o.code !== "string" || !o.code) continue;
4709
+ out.push(pruneUndefined({
4710
+ code: o.code,
4711
+ path: typeof o.path === "string" ? o.path : void 0,
4712
+ message: typeof o.message === "string" ? o.message : void 0
4713
+ }));
4714
+ }
4715
+ return out.length ? out : void 0;
4716
+ }
4176
4717
  function runNextAction(run) {
4177
4718
  if (isTerminalRunStatus(run.status)) return "none";
4178
4719
  if (run.status === "suspended" && run.gate?.kind === "budget") return "raise_budget";
@@ -4218,15 +4759,64 @@ function runCounts(counts) {
4218
4759
  pending: n(c.pending) + n(c.ready) + n(c.waiting)
4219
4760
  };
4220
4761
  }
4221
- function runUsage(run) {
4762
+ function isPricedStepReceipt(receipt) {
4763
+ return typeof receipt?.multiplier === "number" && Number.isFinite(receipt.multiplier);
4764
+ }
4765
+ function receiptEngine(engine) {
4766
+ if (engine === "actions") return "seat";
4767
+ if (engine === "credits") return "legacy";
4768
+ return void 0;
4769
+ }
4770
+ function receiptTier(tier) {
4771
+ return tier === "light" || tier === "standard" || tier === "heavy" ? tier : void 0;
4772
+ }
4773
+ function stepBillingView(receipt) {
4774
+ const engine = receiptEngine(receipt?.engine);
4775
+ if (!receipt || engine === void 0) return void 0;
4776
+ return pruneUndefined({
4777
+ engine,
4778
+ attempt: typeof receipt.attempt === "number" ? receipt.attempt : void 0,
4779
+ credits: typeof receipt.credits === "number" ? receipt.credits : void 0,
4780
+ actions: typeof receipt.actionsEstimate === "number" ? receipt.actionsEstimate : void 0,
4781
+ model: typeof receipt.model === "string" ? receipt.model : void 0,
4782
+ tier: receiptTier(receipt.tier),
4783
+ multiplier: typeof receipt.multiplier === "number" ? receipt.multiplier : void 0,
4784
+ byok: typeof receipt.byok === "boolean" ? receipt.byok : void 0,
4785
+ calibrated: typeof receipt.calibrated === "boolean" ? receipt.calibrated : void 0
4786
+ });
4787
+ }
4788
+ function runUsage(run, receipts) {
4789
+ const actions = n(run.budget?.spent?.actionsEstimate);
4790
+ const stamped = run.budget?.engine;
4791
+ const priced = (receipts ?? []).filter(isPricedStepReceipt);
4792
+ const engine = stamped === "seat" || stamped === "legacy" ? stamped : actions > 0 || priced.some((r) => r.engine === "actions") ? "seat" : priced.some((r) => r.engine === "credits") ? "legacy" : void 0;
4793
+ const seat = engine === "seat";
4794
+ const metering = engine ? "priced" : "flat";
4222
4795
  return {
4223
4796
  creditsUsed: run.budget?.spent?.credits ?? 0,
4224
4797
  actionsEstimate: run.budget?.spent?.actionsEstimate ?? 0,
4798
+ ...seat ? {
4799
+ actionsUsed: actions
4800
+ } : {},
4801
+ metering,
4802
+ ...engine ? {
4803
+ engine
4804
+ } : {},
4225
4805
  steps: run.budget?.spent?.steps ?? 0,
4226
4806
  inputTokens: run.usage?.inputTokens ?? 0,
4227
4807
  outputTokens: run.usage?.outputTokens ?? 0
4228
4808
  };
4229
4809
  }
4810
+ function runBudgetCap(budget) {
4811
+ const cap = budget?.maxCredits;
4812
+ return typeof cap === "number" && Number.isFinite(cap) && cap > 0 ? cap : void 0;
4813
+ }
4814
+ function runBudgetRemaining(budget) {
4815
+ const cap = runBudgetCap(budget);
4816
+ if (cap === void 0) return void 0;
4817
+ const spent = budget?.spent;
4818
+ return Math.max(0, cap - n(spent?.credits) - n(spent?.actionsEstimate) - n(budget?.reserved));
4819
+ }
4230
4820
  function runCancelView(cancel) {
4231
4821
  if (!cancel) return void 0;
4232
4822
  return {
@@ -4293,6 +4883,7 @@ function toWorkflowRunSummary(run) {
4293
4883
  repairOf: run.repairOf,
4294
4884
  repairRunIds: run.repairRunIds,
4295
4885
  trigger: run.trigger ?? "api",
4886
+ origin: runOrigin(run),
4296
4887
  createdBy: {
4297
4888
  subjectType: principal?.subjectType ?? "system",
4298
4889
  subjectId: principal?.subjectId ?? run.userId ?? ""
@@ -4310,11 +4901,13 @@ function toWorkflowRunSummary(run) {
4310
4901
  usage: runUsage(run),
4311
4902
  // LUA-697: a row persisted before the write seams (#2406 / #2465 / the script tier) leaves scrubbed here too —
4312
4903
  // idempotent on a scrubbed message, bounded input; an empty message falls back to the code.
4313
- error: run.error ? {
4904
+ error: run.error ? pruneUndefined({
4314
4905
  code: run.error.code ?? "error",
4315
4906
  message: scrubStepErrorMessage(run.error.message) ?? run.error.code ?? "error",
4316
- stepId: run.error.stepId
4317
- } : void 0,
4907
+ stepId: run.error.stepId,
4908
+ // LUA-784 (item 3): the unattended pre-start failure's refusal rows (`input_schema_invalid` and kin).
4909
+ issues: runErrorIssues(run.error.issues)
4910
+ }) : void 0,
4318
4911
  kind: "run",
4319
4912
  aclHash: run.aclHash,
4320
4913
  migration: run.migration,
@@ -4753,208 +5346,90 @@ function applyJsonPatch(doc, ops) {
4753
5346
  if (Array.isArray(parent) && typeof s === "number") parent = parent[s];
4754
5347
  else if (typeof parent === "object" && parent !== null && typeof s === "string") parent = parent[s];
4755
5348
  else parent = void 0;
4756
- if (parent === void 0) return {
4757
- ok: false,
4758
- code: "PATH_NOT_FOUND",
4759
- index: i,
4760
- path: op.path,
4761
- message: "path not found"
4762
- };
4763
- }
4764
- if (Array.isArray(parent)) {
4765
- if (last === "-") {
4766
- if (op.op !== "add") return {
4767
- ok: false,
4768
- code: "PATCH_INVALID",
4769
- index: i,
4770
- path: op.path,
4771
- message: "'-' only with add"
4772
- };
4773
- parent.push(structuredClone(op.value));
4774
- continue;
4775
- }
4776
- if (typeof last !== "number") return {
4777
- ok: false,
4778
- code: "PATCH_INVALID",
4779
- index: i,
4780
- path: op.path,
4781
- message: "array index expected"
4782
- };
4783
- if (op.op === "add") {
4784
- if (last > parent.length) return {
4785
- ok: false,
4786
- code: "PATH_NOT_FOUND",
4787
- index: i,
4788
- path: op.path,
4789
- message: "index out of range"
4790
- };
4791
- parent.splice(last, 0, structuredClone(op.value));
4792
- } else if (last >= parent.length) {
4793
- return {
4794
- ok: false,
4795
- code: "PATH_NOT_FOUND",
4796
- index: i,
4797
- path: op.path,
4798
- message: "index out of range"
4799
- };
4800
- } else if (op.op === "replace") parent[last] = structuredClone(op.value);
4801
- else parent.splice(last, 1);
4802
- continue;
4803
- }
4804
- if (typeof parent !== "object" || parent === null || typeof last !== "string" && typeof last !== "number") return {
4805
- ok: false,
4806
- code: "PATH_NOT_FOUND",
4807
- index: i,
4808
- path: op.path,
4809
- message: "path not found"
4810
- };
4811
- const obj = parent;
4812
- const key = String(last);
4813
- if (key === "__proto__" || key === "constructor" || key === "prototype") return {
4814
- ok: false,
4815
- code: "PATCH_INVALID",
4816
- index: i,
4817
- path: op.path,
4818
- message: "path not allowed"
4819
- };
4820
- if (op.op === "add") obj[key] = structuredClone(op.value);
4821
- else if (!(key in obj)) return {
4822
- ok: false,
4823
- code: "PATH_NOT_FOUND",
4824
- index: i,
4825
- path: op.path,
4826
- message: "path not found"
4827
- };
4828
- else if (op.op === "replace") obj[key] = structuredClone(op.value);
4829
- else delete obj[key];
4830
- }
4831
- return {
4832
- ok: true,
4833
- value: value22
4834
- };
4835
- }
4836
- function rebaseItemPointer(pointer, itemsPath, index) {
4837
- const base = `/${itemsPath.split(".").map(escapePointer).join("/")}/${index}`;
4838
- return pointer === "/" || pointer === "" ? base : `${base}${pointer}`;
4839
- }
4840
- function describeApproverSpecRefusal(spec) {
4841
- const raw = spec === void 0 ? "undefined" : JSON.stringify(spec) ?? String(spec);
4842
- const written = raw.length > APPROVER_WRITTEN_MAX ? `${raw.slice(0, APPROVER_WRITTEN_MAX - 1)}\u2026` : raw;
4843
- const users = typeof spec === "object" && spec !== null ? spec.users : void 0;
4844
- const approver = typeof users === "string" && USER_ID_SHAPED_RE.test(users) ? {
4845
- users: [
4846
- users
4847
- ]
4848
- } : "creator";
4849
- 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)}`);
4850
- return {
4851
- approver,
4852
- written,
4853
- message
4854
- };
4855
- }
4856
- function bindingRootsOk(template22) {
4857
- const refs = [
4858
- ...template22.matchAll(/\$\{\s*([A-Za-z_$][\w$]*)/g)
4859
- ].map((m) => m[1]);
4860
- return refs.length > 0 && refs.every((r) => BINDING_ROOTS.includes(r));
4861
- }
4862
- function isTemplateBinding(v) {
4863
- return typeof v === "object" && v !== null && typeof v.template === "string";
4864
- }
4865
- function validateApproverBlock(node, opts = {
4866
- path: "approval"
4867
- }) {
4868
- const issues = [];
4869
- const push = /* @__PURE__ */ __name3((code, path3, message, severity = "error") => issues.push({
4870
- code,
4871
- path: path3,
4872
- severity,
4873
- message
4874
- }), "push");
4875
- const checkSpec = /* @__PURE__ */ __name3((spec, path3) => {
4876
- const r = ApproverSpecSchema.safeParse(spec);
4877
- if (!r.success) {
4878
- const users = spec?.users;
4879
- if (Array.isArray(users) && users.length > APPROVER_SPEC_MAX_USERS) push("cap-exceeded", path3, `at most ${APPROVER_SPEC_MAX_USERS} users`);
4880
- else {
4881
- const refusal = describeApproverSpecRefusal(spec);
4882
- issues.push({
4883
- code: "approver-invalid",
4884
- path: path3,
4885
- severity: "error",
4886
- message: refusal.message,
4887
- repair: {
4888
- approver: refusal.approver,
4889
- written: refusal.written
4890
- }
4891
- });
4892
- }
4893
- return;
4894
- }
4895
- const s = r.data;
4896
- if (typeof s === "object") {
4897
- if ("governance" in s && !opts.governanceEnabled) push("approver-governance-unavailable", path3, "governance reviewer routing is not enabled for this deployment");
4898
- if ("group" in s && typeof s.group === "string" && !opts.scimEnabled && opts.idpGroups?.includes(s.group)) push("approver-idp-group-unavailable", path3, "IdP-group approvers are not enabled for this deployment");
4899
- const binding = "users" in s ? s.users : "role" in s ? s.role : "group" in s ? s.group : void 0;
4900
- if (isTemplateBinding(binding)) {
4901
- if (!bindingRootsOk(binding.template)) push("approver-binding-invalid", `${path3}.template`, "binding root must be initData / stepResults / requestContext / state");
4902
- if ("users" in s && opts.customerReachable) push("approver-binding-customer-reachable", `${path3}.users`, "a customer-reachable workflow may not bind its approver list");
4903
- }
4904
- }
4905
- }, "checkSpec");
4906
- if (node.approver !== void 0) checkSpec(node.approver, `${opts.path}.approver`);
4907
- if (node.fourEyes !== void 0) {
4908
- const r = FourEyesSchema.safeParse(node.fourEyes);
4909
- if (!r.success) push("approver-invalid", `${opts.path}.fourEyes`, "fourEyes needs { edit, approve } approver specs");
4910
- else {
4911
- checkSpec(r.data.edit, `${opts.path}.fourEyes.edit`);
4912
- checkSpec(r.data.approve, `${opts.path}.fourEyes.approve`);
5349
+ if (parent === void 0) return {
5350
+ ok: false,
5351
+ code: "PATH_NOT_FOUND",
5352
+ index: i,
5353
+ path: op.path,
5354
+ message: "path not found"
5355
+ };
4913
5356
  }
4914
- if (!node.editable) push("four-eyes-requires-editable", `${opts.path}.fourEyes`, "fourEyes requires editable:true");
4915
- if (node.approver !== void 0) push("four-eyes-overrides-approver", `${opts.path}.approver`, "fourEyes replaces approver", "warning");
4916
- if (node.itemsPath) push("four-eyes-items-unsupported", `${opts.path}.fourEyes`, "fourEyes cannot combine with itemsPath");
4917
- }
4918
- 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");
4919
- if (Array.isArray(node.onTimeout)) {
4920
- const chain = node.onTimeout;
4921
- const hops = chain.filter((m) => typeof m === "object" && m !== null && "escalateTo" in m);
4922
- if (hops.length > ESCALATION_MAX_HOPS) push("escalation-chain-too-long", `${opts.path}.onTimeout`, `at most ${ESCALATION_MAX_HOPS} hops`);
4923
- const last = chain[chain.length - 1];
4924
- if (typeof last === "object" && last !== null) push("escalation-chain-not-terminal", `${opts.path}.onTimeout`, "a chain must end in deny | cancel-run | fail");
4925
- hops.forEach((h, i) => checkSpec(h.escalateTo, `${opts.path}.onTimeout[${i}].escalateTo`));
4926
- } else if (typeof node.onTimeout === "object" && node.onTimeout !== null) {
4927
- checkSpec(node.onTimeout.escalateTo, `${opts.path}.onTimeout.escalateTo`);
4928
- }
4929
- return issues;
4930
- }
4931
- function liftRenderedApprover(row, rendered) {
4932
- const text = (rendered ?? "").trim();
4933
- if (!text) return null;
4934
- if (row === "users") {
4935
- let members = null;
4936
- if (text.startsWith("[")) {
4937
- try {
4938
- members = JSON.parse(text);
4939
- } catch {
4940
- return null;
5357
+ if (Array.isArray(parent)) {
5358
+ if (last === "-") {
5359
+ if (op.op !== "add") return {
5360
+ ok: false,
5361
+ code: "PATCH_INVALID",
5362
+ index: i,
5363
+ path: op.path,
5364
+ message: "'-' only with add"
5365
+ };
5366
+ parent.push(structuredClone(op.value));
5367
+ continue;
4941
5368
  }
4942
- } else members = text.split(",").map((s) => s.trim());
4943
- if (!Array.isArray(members) || members.length === 0 || members.length > APPROVER_SPEC_MAX_USERS) return null;
4944
- if (!members.every((m) => typeof m === "string" && m.length > 0 && m.length <= 128)) return null;
4945
- return {
4946
- users: [
4947
- ...new Set(members)
4948
- ].sort()
5369
+ if (typeof last !== "number") return {
5370
+ ok: false,
5371
+ code: "PATCH_INVALID",
5372
+ index: i,
5373
+ path: op.path,
5374
+ message: "array index expected"
5375
+ };
5376
+ if (op.op === "add") {
5377
+ if (last > parent.length) return {
5378
+ ok: false,
5379
+ code: "PATH_NOT_FOUND",
5380
+ index: i,
5381
+ path: op.path,
5382
+ message: "index out of range"
5383
+ };
5384
+ parent.splice(last, 0, structuredClone(op.value));
5385
+ } else if (last >= parent.length) {
5386
+ return {
5387
+ ok: false,
5388
+ code: "PATH_NOT_FOUND",
5389
+ index: i,
5390
+ path: op.path,
5391
+ message: "index out of range"
5392
+ };
5393
+ } else if (op.op === "replace") parent[last] = structuredClone(op.value);
5394
+ else parent.splice(last, 1);
5395
+ continue;
5396
+ }
5397
+ if (typeof parent !== "object" || parent === null || typeof last !== "string" && typeof last !== "number") return {
5398
+ ok: false,
5399
+ code: "PATH_NOT_FOUND",
5400
+ index: i,
5401
+ path: op.path,
5402
+ message: "path not found"
5403
+ };
5404
+ const obj = parent;
5405
+ const key = String(last);
5406
+ if (key === "__proto__" || key === "constructor" || key === "prototype") return {
5407
+ ok: false,
5408
+ code: "PATCH_INVALID",
5409
+ index: i,
5410
+ path: op.path,
5411
+ message: "path not allowed"
5412
+ };
5413
+ if (op.op === "add") obj[key] = structuredClone(op.value);
5414
+ else if (!(key in obj)) return {
5415
+ ok: false,
5416
+ code: "PATH_NOT_FOUND",
5417
+ index: i,
5418
+ path: op.path,
5419
+ message: "path not found"
4949
5420
  };
5421
+ else if (op.op === "replace") obj[key] = structuredClone(op.value);
5422
+ else delete obj[key];
4950
5423
  }
4951
- if (text.length > 128 || text.startsWith("[") || text.startsWith("{")) return null;
4952
- return row === "role" ? {
4953
- role: text
4954
- } : {
4955
- group: text
5424
+ return {
5425
+ ok: true,
5426
+ value: value22
4956
5427
  };
4957
5428
  }
5429
+ function rebaseItemPointer(pointer, itemsPath, index) {
5430
+ const base = `/${itemsPath.split(".").map(escapePointer).join("/")}/${index}`;
5431
+ return pointer === "/" || pointer === "" ? base : `${base}${pointer}`;
5432
+ }
4958
5433
  function collectEnvTemplateKeys(value22) {
4959
5434
  const keys = /* @__PURE__ */ new Set();
4960
5435
  const walk22 = /* @__PURE__ */ __name3((v) => {
@@ -5203,7 +5678,6 @@ function* singleStepsOf(entry) {
5203
5678
  return;
5204
5679
  case "workflow":
5205
5680
  yield entry;
5206
- if (Array.isArray(entry.graph)) yield* singleStepsOf(entry.graph[1]);
5207
5681
  return;
5208
5682
  case "parallel":
5209
5683
  case "conditional":
@@ -5245,7 +5719,7 @@ function needsInheritedWorkspace(graph) {
5245
5719
  }
5246
5720
  return false;
5247
5721
  }
5248
- var __defProp3, __name3, SideEffectsSchema, JobResourcesSchema, WORKFLOW_ARM_SUBRUN_ID, WORKSPACE_TEMPLATE_EXPR_RE, SLEEP_UNTIL_REPLACEMENT, WORKFLOW_CAPS_DEFAULT, WORKFLOW_STEP_DEFAULT_TIMEOUT_SECONDS, WORKFLOW_AGENT_DEFAULT_TIMEOUT_SECONDS, WORKFLOW_JOB_DEFAULT_TIMEOUT_SECONDS, WORKFLOW_FOREACH_DEFAULT_CONCURRENCY, WORKFLOW_FOREACH_DEFAULT_MAX_ITEMS, WORKFLOW_LOOP_DEFAULT_MAX_ITERATIONS, WORKFLOW_SUSPEND_DEFAULT_TIMEOUT_HOURS, WORKFLOW_SIGNAL_DEFAULT_SOURCES, clone, CONNECTION_ID_HEX_RE, WORKFLOW_JOB_TOOLS, WORKFLOW_JOB_MAX_WORKTREE_ARMS, workspaceOf, mountsWorkspace, isJobTier, jobToolsOf, schemaIsArray, isHitlNode, isSingleStep, singleId, armId, TEMPLATE_STEP_REF, EDITABLE_PATH_RE, PREDICATE_OPS, isPredicateScalar, GRAPH_HASH_PREFIX, WorkflowPlanError, isArmStep, armStepId, armStepKind, joinIdOf, containerIdOf, PATH_PLACEHOLDER, MISSING, stepIdOf, cmp, eq, ne, gt, gte, lt, lte, inSet, notIn, exists, notExists, truthy, falsy, and, or, not, WorkflowTemplateError, TEMPLATE_PLACEHOLDER, TEMPLATE_NAMESPACES, fromInit, fromStep, value, template, fromRequest, rows, fromKnowledge, CONTINUED_FAILURE_TAG, CONTINUED_FAILURE_DEFAULT_CODE, CONTINUED_FAILURE_OUTPUT_SCHEMA, CONTINUED_FAILURE_LEAF_PATHS, isHitlNode2, nodeIdOf, GOAL_JUDGE_STEP_ID, NON_LEAF_KINDS, CONDITIONAL_JOIN_ID, branchArmId, canonical, sortKeys, JOIN, entryOfJoin, FORCE_CANCEL_STALE_MS, TERMINAL, IN_FLIGHT, n, STEP_ERROR_DETAIL_KEYS, STEP_ERROR_DETAIL_MAX_BYTES, DETAIL_MAX_DEPTH, DETAIL_MAX_ITEMS, MAX_HOLIDAYS, MAX_WALK_DAYS, HHMM, YMD, MS_PER_MIN, MS_PER_DAY, MON_FRI, supportedTz, fmtCache, WEEKDAYS, JSON_PATCH_OPS, JSON_PATCH_MAX_OPS, JSON_PATCH_MAX_VALUE_BYTES, JSON_PATCH_MAX_TOTAL_BYTES, SEGMENT_RE, APPROVER_SPEC_MAX_USERS, ESCALATION_MAX_HOPS, TemplateBindingSchema, ApproverSpecSchema, FourEyesSchema, EscalationHopSchema, TerminalOutcomeSchema, ApprovalOnTimeoutSchema, APPROVER_SPEC_SHAPES, APPROVER_WRITTEN_MAX, USER_ID_SHAPED_RE, BINDING_ROOTS, WORKFLOW_ENV_OVERLAY_MAX_KEYS, WORKFLOW_ENV_OVERLAY_MAX_VALUE_BYTES, WORKFLOW_ENV_TEMPLATE_SECRET_KEY_RE, isEnvRef, looksLikeEmbeddedJson, ZERO, isRecord2;
5722
+ var __defProp3, __name3, WorkflowTemplateError, TEMPLATE_PLACEHOLDER, TEMPLATE_NAMESPACES, MAP_DESCRIPTOR_KEYS, MAP_MEMBER_MALFORMED_CODE, fromInit, fromStep, value, template, fromRequest, rows, fromKnowledge, SideEffectsSchema, JobResourcesSchema, APPROVER_SPEC_MAX_USERS, ESCALATION_MAX_HOPS, TemplateBindingSchema, ApproverSpecSchema, FourEyesSchema, EscalationHopSchema, TerminalOutcomeSchema, ApprovalOnTimeoutSchema, APPROVER_SPEC_SHAPES, APPROVER_WRITTEN_MAX, USER_ID_SHAPED_RE, BINDING_ROOTS, WORKSPACE_TEMPLATE_EXPR_RE, SLEEP_UNTIL_REPLACEMENT, WORKFLOW_CAPS_DEFAULT, WORKFLOW_STEP_DEFAULT_TIMEOUT_SECONDS, WORKFLOW_AGENT_DEFAULT_TIMEOUT_SECONDS, WORKFLOW_JOB_DEFAULT_TIMEOUT_SECONDS, WORKFLOW_FOREACH_DEFAULT_CONCURRENCY, WORKFLOW_FOREACH_DEFAULT_MAX_ITEMS, WORKFLOW_LOOP_DEFAULT_MAX_ITERATIONS, WORKFLOW_SUSPEND_DEFAULT_TIMEOUT_HOURS, WORKFLOW_SIGNAL_DEFAULT_SOURCES, clone, CONNECTION_ID_HEX_RE, WORKFLOW_JOB_TOOLS, WORKFLOW_JOB_MAX_WORKTREE_ARMS, workspaceOf, mountsWorkspace, isJobTier, jobToolsOf, schemaIsArray, isHitlNode, isSingleStep, singleId, armId, TEMPLATE_STEP_REF, EDITABLE_PATH_RE, PREDICATE_OPS, isPredicateScalar, GRAPH_HASH_PREFIX, WorkflowPlanError, isArmStep, armStepId, armStepKind, joinIdOf, containerIdOf, PATH_PLACEHOLDER, MISSING, stepIdOf, cmp, eq, ne, gt, gte, lt, lte, inSet, notIn, exists, notExists, truthy, falsy, and, or, not, CONTINUED_FAILURE_TAG, CONTINUED_FAILURE_DEFAULT_CODE, CONTINUED_FAILURE_OUTPUT_SCHEMA, CONTINUED_FAILURE_LEAF_PATHS, isHitlNode2, nodeIdOf, GOAL_JUDGE_STEP_ID, NON_LEAF_KINDS, CONDITIONAL_JOIN_ID, branchArmId, canonical, sortKeys, JOIN, entryOfJoin, FORCE_CANCEL_STALE_MS, TERMINAL, WORKFLOW_INLINE_RUN_TAG, RUN_ERROR_ISSUES_MAX, IN_FLIGHT, n, STEP_ERROR_DETAIL_KEYS, STEP_ERROR_DETAIL_MAX_BYTES, DETAIL_MAX_DEPTH, DETAIL_MAX_ITEMS, MAX_HOLIDAYS, MAX_WALK_DAYS, HHMM, YMD, MS_PER_MIN, MS_PER_DAY, MON_FRI, supportedTz, fmtCache, WEEKDAYS, JSON_PATCH_OPS, JSON_PATCH_MAX_OPS, JSON_PATCH_MAX_VALUE_BYTES, JSON_PATCH_MAX_TOTAL_BYTES, SEGMENT_RE, WORKFLOW_ENV_OVERLAY_MAX_KEYS, WORKFLOW_ENV_OVERLAY_MAX_VALUE_BYTES, WORKFLOW_ENV_TEMPLATE_SECRET_KEY_RE, isEnvRef, looksLikeEmbeddedJson, ZERO, isRecord2;
5249
5723
  var init_dist2 = __esm({
5250
5724
  "../workflow-graph/dist/index.mjs"() {
5251
5725
  "use strict";
@@ -5258,9 +5732,178 @@ var init_dist2 = __esm({
5258
5732
  init_dist();
5259
5733
  __defProp3 = Object.defineProperty;
5260
5734
  __name3 = /* @__PURE__ */ __name((target, value22) => __defProp3(target, "name", { value: value22, configurable: true }), "__name");
5735
+ WorkflowTemplateError = class extends Error {
5736
+ static {
5737
+ __name(this, "WorkflowTemplateError");
5738
+ }
5739
+ static {
5740
+ __name3(this, "WorkflowTemplateError");
5741
+ }
5742
+ placeholder;
5743
+ constructor(message, placeholder) {
5744
+ super(message), this.placeholder = placeholder;
5745
+ this.name = "WorkflowTemplateError";
5746
+ }
5747
+ };
5748
+ __name(isMapConfigObject, "isMapConfigObject");
5749
+ __name3(isMapConfigObject, "isMapConfigObject");
5750
+ __name(parseMapConfig, "parseMapConfig");
5751
+ __name3(parseMapConfig, "parseMapConfig");
5752
+ __name(mapConfigWire, "mapConfigWire");
5753
+ __name3(mapConfigWire, "mapConfigWire");
5754
+ TEMPLATE_PLACEHOLDER = /\$\{([^}]*)\}/g;
5755
+ TEMPLATE_NAMESPACES = [
5756
+ "initData",
5757
+ "state",
5758
+ "requestContext",
5759
+ "stepResults"
5760
+ ];
5761
+ __name(describeBadPlaceholder, "describeBadPlaceholder");
5762
+ __name3(describeBadPlaceholder, "describeBadPlaceholder");
5763
+ __name(parseTemplatePlaceholder, "parseTemplatePlaceholder");
5764
+ __name3(parseTemplatePlaceholder, "parseTemplatePlaceholder");
5765
+ __name(traverseMappingPath, "traverseMappingPath");
5766
+ __name3(traverseMappingPath, "traverseMappingPath");
5767
+ __name(stringifyTemplateValue, "stringifyTemplateValue");
5768
+ __name3(stringifyTemplateValue, "stringifyTemplateValue");
5769
+ __name(escapeFence, "escapeFence");
5770
+ __name3(escapeFence, "escapeFence");
5771
+ __name(fenceBlock, "fenceBlock");
5772
+ __name3(fenceBlock, "fenceBlock");
5773
+ __name(renderTemplate, "renderTemplate");
5774
+ __name3(renderTemplate, "renderTemplate");
5775
+ __name(isMapDescriptor, "isMapDescriptor");
5776
+ __name3(isMapDescriptor, "isMapDescriptor");
5777
+ MAP_DESCRIPTOR_KEYS = [
5778
+ "step",
5779
+ "path",
5780
+ "initData",
5781
+ "value",
5782
+ "template",
5783
+ "requestContextPath",
5784
+ "knowledge"
5785
+ ];
5786
+ MAP_MEMBER_MALFORMED_CODE = "map-member-malformed";
5787
+ __name(malformedMapMembers, "malformedMapMembers");
5788
+ __name3(malformedMapMembers, "malformedMapMembers");
5789
+ __name(mapMemberMalformedMessage, "mapMemberMalformedMessage");
5790
+ __name3(mapMemberMalformedMessage, "mapMemberMalformedMessage");
5791
+ __name(resolveDescriptor, "resolveDescriptor");
5792
+ __name3(resolveDescriptor, "resolveDescriptor");
5793
+ __name(resolveMapping, "resolveMapping");
5794
+ __name3(resolveMapping, "resolveMapping");
5795
+ fromInit = /* @__PURE__ */ __name3((path3) => ({
5796
+ initData: true,
5797
+ path: path3
5798
+ }), "fromInit");
5799
+ fromStep = /* @__PURE__ */ __name3((s, path3 = "") => {
5800
+ const idOf = /* @__PURE__ */ __name3((x) => typeof x === "string" ? x : x.id, "idOf");
5801
+ return {
5802
+ step: Array.isArray(s) ? s.map(idOf) : idOf(s),
5803
+ path: path3
5804
+ };
5805
+ }, "fromStep");
5806
+ value = /* @__PURE__ */ __name3((v) => ({
5807
+ value: v
5808
+ }), "value");
5809
+ template = /* @__PURE__ */ __name3((s) => ({
5810
+ template: s
5811
+ }), "template");
5812
+ fromRequest = /* @__PURE__ */ __name3((path3) => ({
5813
+ requestContextPath: path3
5814
+ }), "fromRequest");
5815
+ rows = /* @__PURE__ */ __name3((s, path3, page) => ({
5816
+ step: typeof s === "string" ? s : s.id,
5817
+ path: path3,
5818
+ rows: page
5819
+ }), "rows");
5820
+ fromKnowledge = /* @__PURE__ */ __name3((k) => ({
5821
+ knowledge: k
5822
+ }), "fromKnowledge");
5261
5823
  SideEffectsSchema = z4.enum(WORKFLOW_SIDE_EFFECTS);
5262
5824
  JobResourcesSchema = z4.enum(WORKFLOW_JOB_RESOURCES);
5263
- WORKFLOW_ARM_SUBRUN_ID = "$arm";
5825
+ APPROVER_SPEC_MAX_USERS = 20;
5826
+ ESCALATION_MAX_HOPS = 3;
5827
+ TemplateBindingSchema = z22.object({
5828
+ template: z22.string().min(1).max(2048)
5829
+ }).strict();
5830
+ ApproverSpecSchema = z22.union([
5831
+ z22.literal("creator"),
5832
+ z22.literal("org-admins"),
5833
+ z22.object({
5834
+ users: z22.union([
5835
+ z22.array(z22.string().min(1).max(128)).min(1).max(APPROVER_SPEC_MAX_USERS),
5836
+ TemplateBindingSchema
5837
+ ])
5838
+ }).strict(),
5839
+ z22.object({
5840
+ role: z22.union([
5841
+ z22.string().min(1).max(128),
5842
+ TemplateBindingSchema
5843
+ ])
5844
+ }).strict(),
5845
+ z22.object({
5846
+ group: z22.union([
5847
+ z22.string().min(1).max(128),
5848
+ TemplateBindingSchema
5849
+ ])
5850
+ }).strict(),
5851
+ z22.object({
5852
+ governance: z22.object({
5853
+ policyId: z22.string().min(1).max(128)
5854
+ }).strict()
5855
+ }).strict()
5856
+ ]);
5857
+ FourEyesSchema = z22.object({
5858
+ edit: ApproverSpecSchema,
5859
+ approve: ApproverSpecSchema
5860
+ }).strict();
5861
+ EscalationHopSchema = z22.object({
5862
+ escalateTo: ApproverSpecSchema,
5863
+ timeoutHours: z22.number().finite().min(1).max(720)
5864
+ }).strict();
5865
+ TerminalOutcomeSchema = z22.enum([
5866
+ "deny",
5867
+ "cancel-run",
5868
+ "fail",
5869
+ "continue"
5870
+ ]);
5871
+ ApprovalOnTimeoutSchema = z22.union([
5872
+ TerminalOutcomeSchema,
5873
+ EscalationHopSchema,
5874
+ z22.array(z22.union([
5875
+ TerminalOutcomeSchema,
5876
+ EscalationHopSchema
5877
+ ])).min(1).max(ESCALATION_MAX_HOPS + 1)
5878
+ ]);
5879
+ APPROVER_SPEC_SHAPES = [
5880
+ "'creator'",
5881
+ "'org-admins'",
5882
+ "{users:[userId, \u2026]}",
5883
+ "{role:roleName}",
5884
+ "{group:groupName}",
5885
+ "{governance:{policyId}}"
5886
+ ];
5887
+ APPROVER_WRITTEN_MAX = 120;
5888
+ USER_ID_SHAPED_RE = /^[^\s@]{1,128}$/;
5889
+ __name(describeApproverSpecRefusal, "describeApproverSpecRefusal");
5890
+ __name3(describeApproverSpecRefusal, "describeApproverSpecRefusal");
5891
+ BINDING_ROOTS = [
5892
+ "initData",
5893
+ "stepResults",
5894
+ "requestContext",
5895
+ "state"
5896
+ ];
5897
+ __name(bindingRootsOk, "bindingRootsOk");
5898
+ __name3(bindingRootsOk, "bindingRootsOk");
5899
+ __name(isTemplateBinding, "isTemplateBinding");
5900
+ __name3(isTemplateBinding, "isTemplateBinding");
5901
+ __name(approvalEditable, "approvalEditable");
5902
+ __name3(approvalEditable, "approvalEditable");
5903
+ __name(validateApproverBlock, "validateApproverBlock");
5904
+ __name3(validateApproverBlock, "validateApproverBlock");
5905
+ __name(liftRenderedApprover, "liftRenderedApprover");
5906
+ __name3(liftRenderedApprover, "liftRenderedApprover");
5264
5907
  WORKSPACE_TEMPLATE_EXPR_RE = /^\$\{\s*(?:initData|input)\.([^}]+?)\s*\}$/;
5265
5908
  __name(workspaceTemplatePath, "workspaceTemplatePath");
5266
5909
  __name3(workspaceTemplatePath, "workspaceTemplatePath");
@@ -5272,6 +5915,8 @@ var init_dist2 = __esm({
5272
5915
  });
5273
5916
  __name(sleepUntilUnsupportedMessage, "sleepUntilUnsupportedMessage");
5274
5917
  __name3(sleepUntilUnsupportedMessage, "sleepUntilUnsupportedMessage");
5918
+ __name(armSubrunUnsupportedMessage, "armSubrunUnsupportedMessage");
5919
+ __name3(armSubrunUnsupportedMessage, "armSubrunUnsupportedMessage");
5275
5920
  WORKFLOW_CAPS_DEFAULT = Object.freeze({
5276
5921
  maxParallelArms: 16,
5277
5922
  maxForeachConcurrency: 16,
@@ -5501,78 +6146,6 @@ var init_dist2 = __esm({
5501
6146
  op: "not",
5502
6147
  arg
5503
6148
  }), "not");
5504
- WorkflowTemplateError = class extends Error {
5505
- static {
5506
- __name(this, "WorkflowTemplateError");
5507
- }
5508
- static {
5509
- __name3(this, "WorkflowTemplateError");
5510
- }
5511
- placeholder;
5512
- constructor(message, placeholder) {
5513
- super(message), this.placeholder = placeholder;
5514
- this.name = "WorkflowTemplateError";
5515
- }
5516
- };
5517
- __name(isMapConfigObject, "isMapConfigObject");
5518
- __name3(isMapConfigObject, "isMapConfigObject");
5519
- __name(parseMapConfig, "parseMapConfig");
5520
- __name3(parseMapConfig, "parseMapConfig");
5521
- __name(mapConfigWire, "mapConfigWire");
5522
- __name3(mapConfigWire, "mapConfigWire");
5523
- TEMPLATE_PLACEHOLDER = /\$\{([^}]*)\}/g;
5524
- TEMPLATE_NAMESPACES = [
5525
- "initData",
5526
- "state",
5527
- "requestContext",
5528
- "stepResults"
5529
- ];
5530
- __name(describeBadPlaceholder, "describeBadPlaceholder");
5531
- __name3(describeBadPlaceholder, "describeBadPlaceholder");
5532
- __name(parseTemplatePlaceholder, "parseTemplatePlaceholder");
5533
- __name3(parseTemplatePlaceholder, "parseTemplatePlaceholder");
5534
- __name(traverseMappingPath, "traverseMappingPath");
5535
- __name3(traverseMappingPath, "traverseMappingPath");
5536
- __name(stringifyTemplateValue, "stringifyTemplateValue");
5537
- __name3(stringifyTemplateValue, "stringifyTemplateValue");
5538
- __name(escapeFence, "escapeFence");
5539
- __name3(escapeFence, "escapeFence");
5540
- __name(fenceBlock, "fenceBlock");
5541
- __name3(fenceBlock, "fenceBlock");
5542
- __name(renderTemplate, "renderTemplate");
5543
- __name3(renderTemplate, "renderTemplate");
5544
- __name(resolveDescriptor, "resolveDescriptor");
5545
- __name3(resolveDescriptor, "resolveDescriptor");
5546
- __name(resolveMapping, "resolveMapping");
5547
- __name3(resolveMapping, "resolveMapping");
5548
- fromInit = /* @__PURE__ */ __name3((path3) => ({
5549
- initData: true,
5550
- path: path3
5551
- }), "fromInit");
5552
- fromStep = /* @__PURE__ */ __name3((s, path3 = "") => {
5553
- const idOf = /* @__PURE__ */ __name3((x) => typeof x === "string" ? x : x.id, "idOf");
5554
- return {
5555
- step: Array.isArray(s) ? s.map(idOf) : idOf(s),
5556
- path: path3
5557
- };
5558
- }, "fromStep");
5559
- value = /* @__PURE__ */ __name3((v) => ({
5560
- value: v
5561
- }), "value");
5562
- template = /* @__PURE__ */ __name3((s) => ({
5563
- template: s
5564
- }), "template");
5565
- fromRequest = /* @__PURE__ */ __name3((path3) => ({
5566
- requestContextPath: path3
5567
- }), "fromRequest");
5568
- rows = /* @__PURE__ */ __name3((s, path3, page) => ({
5569
- step: typeof s === "string" ? s : s.id,
5570
- path: path3,
5571
- rows: page
5572
- }), "rows");
5573
- fromKnowledge = /* @__PURE__ */ __name3((k) => ({
5574
- knowledge: k
5575
- }), "fromKnowledge");
5576
6149
  CONTINUED_FAILURE_TAG = "continued_failure";
5577
6150
  CONTINUED_FAILURE_DEFAULT_CODE = "step_failed";
5578
6151
  CONTINUED_FAILURE_OUTPUT_SCHEMA = Object.freeze({
@@ -5625,8 +6198,8 @@ var init_dist2 = __esm({
5625
6198
  __name(isContinuedFailureValue, "isContinuedFailureValue");
5626
6199
  __name3(isContinuedFailureValue, "isContinuedFailureValue");
5627
6200
  isHitlNode2 = /* @__PURE__ */ __name3((n2) => isWorkflowHitlEntryType(n2.type), "isHitlNode");
5628
- __name(lowerContainerArm, "lowerContainerArm");
5629
- __name3(lowerContainerArm, "lowerContainerArm");
6201
+ __name(inlineContainerArm, "inlineContainerArm");
6202
+ __name3(inlineContainerArm, "inlineContainerArm");
5630
6203
  nodeIdOf = /* @__PURE__ */ __name3((n2) => n2.type === "step" ? n2.step.id : n2.id, "nodeIdOf");
5631
6204
  __name(entryIds, "entryIds");
5632
6205
  __name3(entryIds, "entryIds");
@@ -5686,6 +6259,12 @@ var init_dist2 = __esm({
5686
6259
  __name3(isTerminalRunStatus, "isTerminalRunStatus");
5687
6260
  __name(pruneUndefined, "pruneUndefined");
5688
6261
  __name3(pruneUndefined, "pruneUndefined");
6262
+ WORKFLOW_INLINE_RUN_TAG = "inline";
6263
+ __name(runOrigin, "runOrigin");
6264
+ __name3(runOrigin, "runOrigin");
6265
+ RUN_ERROR_ISSUES_MAX = 20;
6266
+ __name(runErrorIssues, "runErrorIssues");
6267
+ __name3(runErrorIssues, "runErrorIssues");
5689
6268
  __name(runNextAction, "runNextAction");
5690
6269
  __name3(runNextAction, "runNextAction");
5691
6270
  IN_FLIGHT = new Set(WORKFLOW_STEP_IN_FLIGHT);
@@ -5698,8 +6277,20 @@ var init_dist2 = __esm({
5698
6277
  n = /* @__PURE__ */ __name3((v) => typeof v === "number" && Number.isFinite(v) ? v : 0, "n");
5699
6278
  __name(runCounts, "runCounts");
5700
6279
  __name3(runCounts, "runCounts");
6280
+ __name(isPricedStepReceipt, "isPricedStepReceipt");
6281
+ __name3(isPricedStepReceipt, "isPricedStepReceipt");
6282
+ __name(receiptEngine, "receiptEngine");
6283
+ __name3(receiptEngine, "receiptEngine");
6284
+ __name(receiptTier, "receiptTier");
6285
+ __name3(receiptTier, "receiptTier");
6286
+ __name(stepBillingView, "stepBillingView");
6287
+ __name3(stepBillingView, "stepBillingView");
5701
6288
  __name(runUsage, "runUsage");
5702
6289
  __name3(runUsage, "runUsage");
6290
+ __name(runBudgetCap, "runBudgetCap");
6291
+ __name3(runBudgetCap, "runBudgetCap");
6292
+ __name(runBudgetRemaining, "runBudgetRemaining");
6293
+ __name3(runBudgetRemaining, "runBudgetRemaining");
5703
6294
  __name(runCancelView, "runCancelView");
5704
6295
  __name3(runCancelView, "runCancelView");
5705
6296
  __name(runWorkspaceView, "runWorkspaceView");
@@ -5834,86 +6425,6 @@ var init_dist2 = __esm({
5834
6425
  __name3(applyJsonPatch, "applyJsonPatch");
5835
6426
  __name(rebaseItemPointer, "rebaseItemPointer");
5836
6427
  __name3(rebaseItemPointer, "rebaseItemPointer");
5837
- APPROVER_SPEC_MAX_USERS = 20;
5838
- ESCALATION_MAX_HOPS = 3;
5839
- TemplateBindingSchema = z22.object({
5840
- template: z22.string().min(1).max(2048)
5841
- }).strict();
5842
- ApproverSpecSchema = z22.union([
5843
- z22.literal("creator"),
5844
- z22.literal("org-admins"),
5845
- z22.object({
5846
- users: z22.union([
5847
- z22.array(z22.string().min(1).max(128)).min(1).max(APPROVER_SPEC_MAX_USERS),
5848
- TemplateBindingSchema
5849
- ])
5850
- }).strict(),
5851
- z22.object({
5852
- role: z22.union([
5853
- z22.string().min(1).max(128),
5854
- TemplateBindingSchema
5855
- ])
5856
- }).strict(),
5857
- z22.object({
5858
- group: z22.union([
5859
- z22.string().min(1).max(128),
5860
- TemplateBindingSchema
5861
- ])
5862
- }).strict(),
5863
- z22.object({
5864
- governance: z22.object({
5865
- policyId: z22.string().min(1).max(128)
5866
- }).strict()
5867
- }).strict()
5868
- ]);
5869
- FourEyesSchema = z22.object({
5870
- edit: ApproverSpecSchema,
5871
- approve: ApproverSpecSchema
5872
- }).strict();
5873
- EscalationHopSchema = z22.object({
5874
- escalateTo: ApproverSpecSchema,
5875
- timeoutHours: z22.number().finite().min(1).max(720)
5876
- }).strict();
5877
- TerminalOutcomeSchema = z22.enum([
5878
- "deny",
5879
- "cancel-run",
5880
- "fail",
5881
- "continue"
5882
- ]);
5883
- ApprovalOnTimeoutSchema = z22.union([
5884
- TerminalOutcomeSchema,
5885
- EscalationHopSchema,
5886
- z22.array(z22.union([
5887
- TerminalOutcomeSchema,
5888
- EscalationHopSchema
5889
- ])).min(1).max(ESCALATION_MAX_HOPS + 1)
5890
- ]);
5891
- APPROVER_SPEC_SHAPES = [
5892
- "'creator'",
5893
- "'org-admins'",
5894
- "{users:[userId, \u2026]}",
5895
- "{role:roleName}",
5896
- "{group:groupName}",
5897
- "{governance:{policyId}}"
5898
- ];
5899
- APPROVER_WRITTEN_MAX = 120;
5900
- USER_ID_SHAPED_RE = /^[^\s@]{1,128}$/;
5901
- __name(describeApproverSpecRefusal, "describeApproverSpecRefusal");
5902
- __name3(describeApproverSpecRefusal, "describeApproverSpecRefusal");
5903
- BINDING_ROOTS = [
5904
- "initData",
5905
- "stepResults",
5906
- "requestContext",
5907
- "state"
5908
- ];
5909
- __name(bindingRootsOk, "bindingRootsOk");
5910
- __name3(bindingRootsOk, "bindingRootsOk");
5911
- __name(isTemplateBinding, "isTemplateBinding");
5912
- __name3(isTemplateBinding, "isTemplateBinding");
5913
- __name(validateApproverBlock, "validateApproverBlock");
5914
- __name3(validateApproverBlock, "validateApproverBlock");
5915
- __name(liftRenderedApprover, "liftRenderedApprover");
5916
- __name3(liftRenderedApprover, "liftRenderedApprover");
5917
6428
  WORKFLOW_ENV_OVERLAY_MAX_KEYS = 64;
5918
6429
  WORKFLOW_ENV_OVERLAY_MAX_VALUE_BYTES = 4096;
5919
6430
  WORKFLOW_ENV_TEMPLATE_SECRET_KEY_RE = /(SECRET|TOKEN|KEY|PASSWORD)$/;
@@ -6016,14 +6527,13 @@ function stepNodeOf(s) {
6016
6527
  }
6017
6528
  function materializeEntry(entry, steps) {
6018
6529
  const single = /* @__PURE__ */ __name((n2) => {
6019
- if (n2.type === "step" && steps[n2.step.id]) return stepNodeOf(steps[n2.step.id]);
6020
- if (n2.type === "workflow" && n2.workflowId === WORKFLOW_ARM_SUBRUN_ID && n2.graph) return {
6021
- ...n2,
6022
- graph: [
6023
- n2.graph[0],
6024
- single(n2.graph[1])
6025
- ]
6026
- };
6530
+ if (n2.type === "step" && steps[n2.step.id]) {
6531
+ const node = stepNodeOf(steps[n2.step.id]);
6532
+ return n2.input !== void 0 ? {
6533
+ ...node,
6534
+ input: n2.input
6535
+ } : node;
6536
+ }
6027
6537
  return n2;
6028
6538
  }, "single");
6029
6539
  switch (entry.type) {
@@ -6181,6 +6691,10 @@ var init_workflow = __esm({
6181
6691
  }, "assertPredicate");
6182
6692
  assertRetry = /* @__PURE__ */ __name((r, id) => {
6183
6693
  if (!r) return;
6694
+ if (r.maxAttempts !== void 0 && !isWithinWorkflowRetryAttempts(r.maxAttempts)) {
6695
+ const over = typeof r.maxAttempts === "number" && r.maxAttempts > WORKFLOW_RETRY_MAX_ATTEMPTS;
6696
+ throw new LuaWorkflowBuildError(over ? "cap-exceeded" : "invalid-envelope", `"${id}": ${workflowRetryMaxAttemptsMessage(r.maxAttempts)}`);
6697
+ }
6184
6698
  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(" | ")}`);
6185
6699
  if (r.maxBackoffSeconds !== void 0) {
6186
6700
  if (r.backoff !== "exponential") throw new LuaWorkflowBuildError("backoff-invalid", `"${id}": retry.maxBackoffSeconds is only meaningful with backoff:'exponential'`);
@@ -6950,6 +7464,197 @@ var init_auth_error = __esm({
6950
7464
  }
6951
7465
  });
6952
7466
 
7467
+ // src/errors/cli.error.ts
7468
+ function isAccessDeniedError(error) {
7469
+ if (CliError.isCliError(error)) return error.statusCode === 403;
7470
+ return error instanceof Error && error.message.startsWith("Access denied (403)");
7471
+ }
7472
+ function authHint(error) {
7473
+ if (error.suppressDefaultRemediation) return void 0;
7474
+ if (error.reason === "no_agent_access") {
7475
+ return [
7476
+ "Your API key is valid, but it does not have access to the agentId in lua.skill.yaml \u2014 the agent belongs",
7477
+ "to another account or organization, was deleted or transferred, or the yaml was copied from another project.",
7478
+ "Check the configured agent and switch if needed:",
7479
+ " lua agents (list agents you have access to)",
7480
+ " lua init (re-select the agent for this project)"
7481
+ ].join("\n");
7482
+ }
7483
+ return "Re-authenticate or check your API key: lua auth configure \xB7 https://admin.heylua.ai";
7484
+ }
7485
+ function numericStatus(error) {
7486
+ const candidate = error.statusCode ?? error.status;
7487
+ return typeof candidate === "number" && Number.isFinite(candidate) ? candidate : void 0;
7488
+ }
7489
+ function classifyCliError(error) {
7490
+ if (CliError.isCliError(error)) {
7491
+ return {
7492
+ code: error.code,
7493
+ exitCode: error.exitCode,
7494
+ message: error.message,
7495
+ hint: error.hint
7496
+ };
7497
+ }
7498
+ if (AuthenticationError.isAuthenticationError(error)) {
7499
+ return {
7500
+ code: "auth",
7501
+ exitCode: CLI_EXIT.AUTH,
7502
+ message: error.message,
7503
+ hint: authHint(error)
7504
+ };
7505
+ }
7506
+ const e = typeof error === "object" && error !== null ? error : {};
7507
+ const message = typeof e.message === "string" && e.message.length > 0 ? e.message : error instanceof Error ? error.name : String(error ?? "Unknown error");
7508
+ if (e.name === "WorkflowLocalUsageError" || typeof e.code === "string" && e.code.startsWith("commander.")) {
7509
+ return {
7510
+ code: "usage",
7511
+ exitCode: CLI_EXIT.USAGE,
7512
+ message
7513
+ };
7514
+ }
7515
+ const status = numericStatus(e);
7516
+ if (status !== void 0) {
7517
+ if (status === 401) return {
7518
+ code: "auth",
7519
+ exitCode: CLI_EXIT.AUTH,
7520
+ message
7521
+ };
7522
+ if (status === 403) return {
7523
+ code: "forbidden",
7524
+ exitCode: CLI_EXIT.FORBIDDEN,
7525
+ message
7526
+ };
7527
+ if (status === 404) return {
7528
+ code: "not_found",
7529
+ exitCode: CLI_EXIT.NOT_FOUND,
7530
+ message
7531
+ };
7532
+ if (status >= 400 && status < 500) return {
7533
+ code: `http_${status}`,
7534
+ exitCode: CLI_EXIT.FORBIDDEN,
7535
+ message
7536
+ };
7537
+ if (status >= 500 || status === 0) return {
7538
+ code: "unavailable",
7539
+ exitCode: CLI_EXIT.UNAVAILABLE,
7540
+ message
7541
+ };
7542
+ }
7543
+ const causeCode = e.cause?.code;
7544
+ if (typeof e.code === "string" && NETWORK_ERRNO.has(e.code) || typeof causeCode === "string" && NETWORK_ERRNO.has(causeCode) || e.name === "AbortError" || e.name === "TimeoutError" || NETWORK_MESSAGE.test(message)) {
7545
+ return {
7546
+ code: "unavailable",
7547
+ exitCode: CLI_EXIT.UNAVAILABLE,
7548
+ message,
7549
+ hint: UNAVAILABLE_HINT
7550
+ };
7551
+ }
7552
+ return {
7553
+ code: "error",
7554
+ exitCode: CLI_EXIT.ERROR,
7555
+ message
7556
+ };
7557
+ }
7558
+ var CLI_EXIT, CliError, NETWORK_ERRNO, NETWORK_MESSAGE, UNAVAILABLE_HINT;
7559
+ var init_cli_error = __esm({
7560
+ "src/errors/cli.error.ts"() {
7561
+ "use strict";
7562
+ init_auth_error();
7563
+ CLI_EXIT = {
7564
+ OK: 0,
7565
+ ERROR: 1,
7566
+ USAGE: 2,
7567
+ NOT_FOUND: 3,
7568
+ AUTH: 9,
7569
+ FORBIDDEN: 10,
7570
+ UNAVAILABLE: 11
7571
+ };
7572
+ CliError = class _CliError extends Error {
7573
+ static {
7574
+ __name(this, "CliError");
7575
+ }
7576
+ isCliError = true;
7577
+ code;
7578
+ exitCode;
7579
+ hint;
7580
+ statusCode;
7581
+ constructor(code, message, options = {}) {
7582
+ super(message);
7583
+ this.name = "CliError";
7584
+ this.code = code;
7585
+ this.exitCode = options.exitCode ?? CLI_EXIT.ERROR;
7586
+ this.hint = options.hint;
7587
+ this.statusCode = options.statusCode;
7588
+ if (Error.captureStackTrace) Error.captureStackTrace(this, _CliError);
7589
+ }
7590
+ /** Bad arguments, an unknown action, no project — exit 2. */
7591
+ static usage(message, hint) {
7592
+ return new _CliError("usage", message, {
7593
+ exitCode: CLI_EXIT.USAGE,
7594
+ hint
7595
+ });
7596
+ }
7597
+ /** The named thing does not exist — exit 3. */
7598
+ static notFound(message, hint) {
7599
+ return new _CliError("not_found", message, {
7600
+ exitCode: CLI_EXIT.NOT_FOUND,
7601
+ hint,
7602
+ statusCode: 404
7603
+ });
7604
+ }
7605
+ /** The credential may not do this — exit 10. */
7606
+ static forbidden(message, hint) {
7607
+ return new _CliError("forbidden", message, {
7608
+ exitCode: CLI_EXIT.FORBIDDEN,
7609
+ hint,
7610
+ statusCode: 403
7611
+ });
7612
+ }
7613
+ /**
7614
+ * An API refusal the site already holds the status of (LUA-766) — classified by the same table the top-level
7615
+ * classifier applies to an untyped error: 401 auth · 403 forbidden · 404 not_found · other 4xx `http_<status>`
7616
+ * (10) · 5xx / 0 unavailable (11, with the network hint unless the site gives its own) · no status `error` (1).
7617
+ * A command that reads `response.error.statusCode` throws through here, so `lua logs` on a 503 exits 11 like
7618
+ * every other verb instead of printing the message itself and then throwing an exit-1 `Error`.
7619
+ */
7620
+ static fromStatus(statusCode, message, hint) {
7621
+ const reported = classifyCliError(Object.assign(new Error(message), {
7622
+ statusCode
7623
+ }));
7624
+ const classHint = reported.exitCode === CLI_EXIT.UNAVAILABLE ? UNAVAILABLE_HINT : reported.hint;
7625
+ return new _CliError(reported.code, message, {
7626
+ exitCode: reported.exitCode,
7627
+ hint: hint ?? classHint,
7628
+ statusCode
7629
+ });
7630
+ }
7631
+ static isCliError(error) {
7632
+ return error instanceof _CliError || typeof error === "object" && error !== null && error.isCliError === true;
7633
+ }
7634
+ };
7635
+ __name(isAccessDeniedError, "isAccessDeniedError");
7636
+ NETWORK_ERRNO = /* @__PURE__ */ new Set([
7637
+ "ECONNREFUSED",
7638
+ "ECONNRESET",
7639
+ "ENOTFOUND",
7640
+ "ETIMEDOUT",
7641
+ "EAI_AGAIN",
7642
+ "EPIPE",
7643
+ "EHOSTUNREACH",
7644
+ "ENETUNREACH",
7645
+ "UND_ERR_CONNECT_TIMEOUT",
7646
+ "UND_ERR_HEADERS_TIMEOUT",
7647
+ "UND_ERR_BODY_TIMEOUT",
7648
+ "UND_ERR_SOCKET"
7649
+ ]);
7650
+ NETWORK_MESSAGE = /fetch failed|socket hang up|network request failed|request timeout|ECONNREFUSED|ENOTFOUND/i;
7651
+ UNAVAILABLE_HINT = "The Lua API could not be reached \u2014 check your network and https://status.heylua.ai, then retry.";
7652
+ __name(authHint, "authHint");
7653
+ __name(numericStatus, "numericStatus");
7654
+ __name(classifyCliError, "classifyCliError");
7655
+ }
7656
+ });
7657
+
6953
7658
  // src/utils/package-root.ts
6954
7659
  import { readFileSync, existsSync } from "fs";
6955
7660
  import { fileURLToPath, pathToFileURL } from "url";
@@ -7476,6 +8181,10 @@ async function* parseSseStream(body, signal) {
7476
8181
  if (frame) yield frame;
7477
8182
  }
7478
8183
  } finally {
8184
+ try {
8185
+ await reader.cancel();
8186
+ } catch {
8187
+ }
7479
8188
  try {
7480
8189
  reader.releaseLock();
7481
8190
  } catch {
@@ -7505,6 +8214,7 @@ var init_http_client = __esm({
7505
8214
  "use strict";
7506
8215
  init_dist();
7507
8216
  init_auth_error();
8217
+ init_cli_error();
7508
8218
  init_lua_fetch();
7509
8219
  init_request_credential();
7510
8220
  DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
@@ -7568,7 +8278,7 @@ var init_http_client = __esm({
7568
8278
  if (AuthenticationError.isAuthenticationError(error)) {
7569
8279
  throw error;
7570
8280
  }
7571
- if (error instanceof Error && error.message.startsWith("Access denied (403)")) {
8281
+ if (isAccessDeniedError(error)) {
7572
8282
  throw error;
7573
8283
  }
7574
8284
  if (error instanceof DOMException && error.name === "AbortError") {
@@ -7616,8 +8326,11 @@ var init_http_client = __esm({
7616
8326
  }
7617
8327
  if (response.status === 403) {
7618
8328
  const detail = errorData.message || "You do not have permission to access this resource.";
7619
- throw new Error(`Access denied (403): ${detail}
7620
- Check that your Lua login has access to this agent or organization.`);
8329
+ throw new CliError("forbidden", `Access denied (403): ${detail}`, {
8330
+ exitCode: CLI_EXIT.FORBIDDEN,
8331
+ statusCode: 403,
8332
+ hint: "Check that your Lua login has access to this agent or organization."
8333
+ });
7621
8334
  }
7622
8335
  return {
7623
8336
  success: false,
@@ -7859,6 +8572,7 @@ var init_auth = __esm({
7859
8572
  init_auth_api_service();
7860
8573
  init_constants();
7861
8574
  init_auth_error();
8575
+ init_cli_error();
7862
8576
  }
7863
8577
  });
7864
8578
 
@@ -8086,6 +8800,9 @@ function buildSourceArchive(files) {
8086
8800
  const gz = zlib.gzipSync(Buffer.from(json, "utf-8"));
8087
8801
  return gz.toString("base64");
8088
8802
  }
8803
+ function getPrimitivesByKind(manifest, kind) {
8804
+ return manifest.primitives.filter((p) => p.kind === kind);
8805
+ }
8089
8806
  function findPrimitive(manifest, name, kind) {
8090
8807
  return manifest.primitives.find((p) => {
8091
8808
  if (kind && p.kind !== kind) return false;
@@ -8106,6 +8823,7 @@ var init_artifact_loader = __esm({
8106
8823
  __name(loadOriginalSource, "loadOriginalSource");
8107
8824
  __name(normalizeEntryFile, "normalizeEntryFile");
8108
8825
  __name(buildSourceArchive, "buildSourceArchive");
8826
+ __name(getPrimitivesByKind, "getPrimitivesByKind");
8109
8827
  __name(findPrimitive, "findPrimitive");
8110
8828
  }
8111
8829
  });
@@ -8752,6 +9470,7 @@ var init_base_handler = __esm({
8752
9470
  init_bundle_upload();
8753
9471
  init_semver();
8754
9472
  init_auth_error();
9473
+ init_cli_error();
8755
9474
  DEFAULT_VERSION = SKILL_DEFAULTS.VERSION;
8756
9475
  BaseVersionedHandler = class {
8757
9476
  static {
@@ -8833,7 +9552,7 @@ var init_base_handler = __esm({
8833
9552
  serverItems
8834
9553
  };
8835
9554
  } catch (error) {
8836
- if (AuthenticationError.isAuthenticationError(error)) throw error;
9555
+ if (AuthenticationError.isAuthenticationError(error) || isAccessDeniedError(error)) throw error;
8837
9556
  return {
8838
9557
  serverItems: null,
8839
9558
  fetchError: error instanceof Error ? error.message : String(error)
@@ -8863,13 +9582,24 @@ var init_base_handler = __esm({
8863
9582
  }
8864
9583
  const yamlItems = this.getFromYaml(config);
8865
9584
  const { yamlById, yamlByName, serverByName } = this.buildMaps(serverData.serverItems, yamlItems);
9585
+ const idField = this.yamlConfig.idField;
9586
+ const manifestNames = manifest ? new Set(getPrimitivesByKind(manifest, this.kind).map((p) => p.name)) : /* @__PURE__ */ new Set();
9587
+ for (const stale of this.staleYamlRows(yamlItems, serverData.serverItems, manifestNames)) {
9588
+ const idx = yamlItems.indexOf(stale);
9589
+ if (idx < 0) continue;
9590
+ const { [idField]: goneId, ...rest } = stale;
9591
+ yamlItems[idx] = rest;
9592
+ yamlUpdated = true;
9593
+ const msg = `\u2139\uFE0F ${this.displayName} "${stale.name}" (${goneId}) no longer exists on the server \u2014 re-registering it`;
9594
+ messages.push(msg);
9595
+ console.log(msg);
9596
+ }
8866
9597
  const orphans = serverData.serverItems.filter((item) => {
8867
9598
  const id = item.id;
8868
9599
  const name = item.name;
8869
9600
  return !yamlById.has(id) && !yamlByName.has(name) && this.isActive(item) && this.shouldConsiderForOrphan(item);
8870
9601
  });
8871
9602
  if (orphans.length > 0) {
8872
- const idField = this.yamlConfig.idField;
8873
9603
  const stubs = orphans.map((item) => this.cleanItem({
8874
9604
  name: item.name,
8875
9605
  version: this.getActiveVersion(item) || DEFAULT_VERSION,
@@ -8912,6 +9642,7 @@ var init_base_handler = __esm({
8912
9642
  console.log(`\u2705 Server ${this.displayNamePlural} and YAML are fully in sync`);
8913
9643
  }
8914
9644
  } catch (error) {
9645
+ if (AuthenticationError.isAuthenticationError(error) || isAccessDeniedError(error)) throw error;
8915
9646
  console.error(`\u274C Error syncing server ${this.displayNamePlural}:`, error);
8916
9647
  }
8917
9648
  return {
@@ -8968,6 +9699,7 @@ var init_base_handler = __esm({
8968
9699
  console.error(` \u274C Failed to create "${item.name}" - no ID returned`);
8969
9700
  }
8970
9701
  } catch (error) {
9702
+ if (AuthenticationError.isAuthenticationError(error) || isAccessDeniedError(error)) throw error;
8971
9703
  console.error(` \u274C Failed to create "${item.name}": ${error instanceof Error ? error.message : error}`);
8972
9704
  }
8973
9705
  }
@@ -9102,6 +9834,15 @@ var init_base_handler = __esm({
9102
9834
  getItemId(item) {
9103
9835
  return item[this.yamlConfig.idField] || "";
9104
9836
  }
9837
+ /**
9838
+ * LUA-750: the yaml rows whose server id is gone (deleted server-side) and which `applySyncToYaml` should
9839
+ * re-register. Default none — a handler whose `fetchFromServer` lists EVERY live row of its kind for the
9840
+ * agent overrides this (a kind whose list omits inactive rows must not, or it would duplicate them).
9841
+ * `manifestNames` is the set of primitives in local code: only those are worth re-creating.
9842
+ */
9843
+ staleYamlRows(_yamlItems, _serverItems, _manifestNames) {
9844
+ return [];
9845
+ }
9105
9846
  buildMaps(serverItems, yamlItems) {
9106
9847
  const yamlById = /* @__PURE__ */ new Map();
9107
9848
  const yamlByName = /* @__PURE__ */ new Map();
@@ -9500,6 +10241,7 @@ var init_cli = __esm({
9500
10241
  "src/utils/cli.ts"() {
9501
10242
  "use strict";
9502
10243
  init_auth_error();
10244
+ init_cli_error();
9503
10245
  init_version_check();
9504
10246
  init_package_root();
9505
10247
  init_analytics();
@@ -9519,6 +10261,7 @@ var init_command_utils = __esm({
9519
10261
  init_request_credential();
9520
10262
  init_files();
9521
10263
  init_cli();
10264
+ init_cli_error();
9522
10265
  __name(requireAuth, "requireAuth");
9523
10266
  }
9524
10267
  });
@@ -12990,6 +13733,14 @@ var init_workflow_api_service = __esm({
12990
13733
  async getVersionEnvOverlay(workflowId, version) {
12991
13734
  return this.httpGet(`${this.base}/${workflowId}/versions/${encodeURIComponent(version)}/env-overlay`, await this.auth());
12992
13735
  }
13736
+ /**
13737
+ * WF-403 (13 §13.14; LUA-752) — `GET …/:workflowId/export?version=`: the active (or named) version as pushable
13738
+ * files (`{ form, version, files:[{ path, contents }], warnings }`). `lua workflows export` writes them to disk.
13739
+ */
13740
+ async exportWorkflowFiles(workflowId, version) {
13741
+ const qs = version ? `?version=${encodeURIComponent(version)}` : "";
13742
+ return this.httpGet(`${this.base}/${workflowId}/export${qs}`, await this.auth());
13743
+ }
12993
13744
  async getWorkflowVersions(workflowId) {
12994
13745
  return this.httpGet(`${this.base}/${workflowId}/versions`, await this.auth());
12995
13746
  }
@@ -13067,7 +13818,10 @@ var init_workflow_api_service = __esm({
13067
13818
  async getRunReplayBundle(runId) {
13068
13819
  return this.httpGet(`${this.runs}/${runId}/journal?format=replay`, await this.auth());
13069
13820
  }
13070
- /** R11 — cancel (`mode:'request'` default; `'force'` after `forceAvailableAt`, 409 `FORCE_NOT_YET_AVAILABLE` before). */
13821
+ /**
13822
+ * R11 — cancel (`mode:'request'` default). A `'force'` before `forceAvailableAt` is the 200 verdict
13823
+ * `{ transitioned:false, nextAction:'cancel_again', forceAvailableAt }` (PRO-979) — never a 409 (LUA-748).
13824
+ */
13071
13825
  async cancelRun(runId, data = {}) {
13072
13826
  return this.httpPost(`${this.runs}/${runId}/cancel`, data, await this.auth());
13073
13827
  }
@@ -13083,6 +13837,34 @@ var init_workflow_api_service = __esm({
13083
13837
  async retryStep(runId, stepId, data = {}) {
13084
13838
  return this.httpPost(`${this.runs}/${runId}/steps/${pathId(stepId)}/retry`, data, await this.auth());
13085
13839
  }
13840
+ /**
13841
+ * R37 (LUA-752) — a human decides a parked step: `skip` it, `complete` it with the output it would have produced,
13842
+ * or `fail` it (the step's onError policy applies). 400 `VALIDATION_FAILED{output-required}` / `RESOLVE_OUTPUT_INVALID`,
13843
+ * 403 `APPROVAL_REQUIRES_HUMAN` / `NOT_RUN_CREATOR`, 404 `RUN_NOT_FOUND` / `STEP_NOT_FOUND`, 409 `STEP_NOT_PARKED` /
13844
+ * `RUN_TERMINAL`, 413 `OUTPUT_TOO_LARGE`; the CAS loser is a 200 `{ resolved:false, reason, recorded }`.
13845
+ */
13846
+ async resolveStep(runId, stepId, data) {
13847
+ return this.httpPost(`${this.runs}/${runId}/steps/${pathId(stepId)}/resolve`, data, await this.auth());
13848
+ }
13849
+ /**
13850
+ * R45 (LUA-752) — raise a parked run's budget (`maxCredits` / `maxSteps` / `maxJobSeconds` / `maxDurationSeconds`;
13851
+ * increases only). 400 `VALIDATION_FAILED` / `CAP_EXCEEDED`, 403 `NOT_RUN_CREATOR`, 409 `BUDGET_NOT_RAISABLE`.
13852
+ */
13853
+ async raiseBudget(runId, data) {
13854
+ return this.httpPost(`${this.runs}/${runId}/budget`, data, await this.auth());
13855
+ }
13856
+ /**
13857
+ * R39 (LUA-752) — the current approval payload with its `payloadFingerprint` / `editRevision` (what
13858
+ * `approve --edit --fingerprint` echoes). `path` pages one array of a large payload.
13859
+ */
13860
+ async getApprovalPayload(runId, approvalId, query = {}) {
13861
+ const q = new URLSearchParams();
13862
+ if (query.path) q.append("path", query.path);
13863
+ if (query.cursor) q.append("cursor", query.cursor);
13864
+ if (query.limit !== void 0) q.append("limit", String(query.limit));
13865
+ const qs = q.toString();
13866
+ return this.httpGet(`${this.runs}/${runId}/approvals/${pathId(approvalId)}/payload${qs ? `?${qs}` : ""}`, await this.auth());
13867
+ }
13086
13868
  /** R13 — resolve an approval (human; `expectedFingerprint` guards against an edited payload — 409 `PAYLOAD_MISMATCH`). */
13087
13869
  async resolveApproval(runId, approvalId, data) {
13088
13870
  return this.httpPost(`${this.runs}/${runId}/approvals/${pathId(approvalId)}/resolve`, data, await this.auth());
@@ -13146,6 +13928,14 @@ var init_workflow_api_service = __esm({
13146
13928
  async closeGoal(goalId, data = {}) {
13147
13929
  return this.httpPost(`${this.goals}/${encodeURIComponent(goalId)}/close`, data, await this.auth());
13148
13930
  }
13931
+ /** LUA-749 R63 — edit (objective / judge / cadence / caps / note); a `budget` / `max_runs` park re-arms when the cap clears (`rearmed:true`). */
13932
+ async updateGoal(goalId, data) {
13933
+ return this.httpPatch(`${this.goals}/${encodeURIComponent(goalId)}`, data, await this.auth());
13934
+ }
13935
+ /** LUA-749 R64 — raise `maxTotalCredits` / `maxRuns` (increases only; 400 `GOAL_RAISE_BELOW_SPENT{field, value, spent}`). */
13936
+ async raiseGoal(goalId, data) {
13937
+ return this.httpPost(`${this.goals}/${encodeURIComponent(goalId)}/raise`, data, await this.auth());
13938
+ }
13149
13939
  // ─── Schedules (R4-MF-2 list/get + R28 delete — `/workflows/:agentId/schedules`; LUA-627 stanza) ───
13150
13940
  /** Schedule tree (09 §9.5 — the write-only R27/R56/R28 family plus the R4-MF-2 read rows). */
13151
13941
  get schedules() {
@@ -13159,7 +13949,15 @@ var init_workflow_api_service = __esm({
13159
13949
  async getSchedule(jobId) {
13160
13950
  return this.httpGet(`${this.schedules}/${encodeURIComponent(jobId)}`, await this.auth());
13161
13951
  }
13162
- /** R28delete a schedule Job (404 `SCHEDULE_NOT_FOUND`). The CLI refuses a goal-owned job BEFORE this call (`goal_schedule`). */
13952
+ /** R27 (LUA-752) create or replace the plain schedule Job of a workflow (201; 400 `VALIDATION_FAILED` / `WORKFLOW_NOT_ON_AGENT`, 404 `WORKFLOW_NOT_FOUND`, 409 `SCHEDULE_CAP`). */
13953
+ async createSchedule(data) {
13954
+ return this.httpPost(this.schedules, data, await this.auth());
13955
+ }
13956
+ /** R56 (LUA-752) — pause / resume a schedule, persist `backfillOnEnable`, one-shot `backfillNow` (404 `SCHEDULE_NOT_FOUND`, 400 `VALIDATION_FAILED{issues}`). */
13957
+ async updateSchedule(jobId, data) {
13958
+ return this.httpPatch(`${this.schedules}/${encodeURIComponent(jobId)}`, data, await this.auth());
13959
+ }
13960
+ /** R28 — delete a schedule Job (404 `SCHEDULE_NOT_FOUND`). The CLI refuses a LIVE goal's job BEFORE this call (`goal_schedule`); an ended goal's lingering Job is retired here (LUA-760). */
13163
13961
  async deleteSchedule(jobId) {
13164
13962
  return this.httpDelete(`${this.schedules}/${encodeURIComponent(jobId)}`, await this.auth());
13165
13963
  }