lua-cli 3.32.1 → 3.32.3

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.
@@ -90,9 +90,9 @@ function mcpActionTokens(action) {
90
90
  return action.replace(/([a-z0-9])([A-Z])/g, "$1 $2").toLowerCase().split(/[^a-z0-9]+/).filter(Boolean);
91
91
  }
92
92
  function isReviewableMcpDraftTool(tool) {
93
- const sep = tool.indexOf("_");
94
- if (sep <= 0 || sep >= tool.length - 1) return false;
95
- const action = tool.slice(sep + 1);
93
+ const sep4 = tool.indexOf("_");
94
+ if (sep4 <= 0 || sep4 >= tool.length - 1) return false;
95
+ const action = tool.slice(sep4 + 1);
96
96
  const tokens = mcpActionTokens(action);
97
97
  if (!tokens.includes("draft") && !tokens.includes("drafts")) return false;
98
98
  return !MCP_TOOL_READ_VERB_RE.test(action);
@@ -356,6 +356,9 @@ function isDesktopFileSessionId(value3) {
356
356
  function isImplicitModelSelectionSource(source) {
357
357
  return source !== void 0 && IMPLICIT_MODEL_SELECTION_SOURCES.includes(source);
358
358
  }
359
+ function isPlatformFallbackModelSource(source) {
360
+ return source === PLATFORM_FALLBACK_MODEL_SOURCE;
361
+ }
359
362
  function resolveRequireToolApproval(rules) {
360
363
  const raw = rules?.requireToolApproval ?? rules?.requireApproval;
361
364
  if (raw === void 0 || raw === null) return void 0;
@@ -395,14 +398,14 @@ async function readCoreDrainingRetryDelayMs(response) {
395
398
  return coreDrainingRetryDelayMs(response.headers.get("Retry-After"));
396
399
  }
397
400
  function waitForCoreDrain(delayMs, signal) {
398
- return new Promise((resolve, reject) => {
401
+ return new Promise((resolve3, reject) => {
399
402
  if (signal?.aborted) {
400
403
  reject(signal.reason ?? new DOMException("Aborted", "AbortError"));
401
404
  return;
402
405
  }
403
406
  const timer = setTimeout(() => {
404
407
  signal?.removeEventListener("abort", onAbort);
405
- resolve();
408
+ resolve3();
406
409
  }, delayMs);
407
410
  function onAbort() {
408
411
  clearTimeout(timer);
@@ -535,6 +538,75 @@ function isKnownProfile(profiles, profileId) {
535
538
  function hasCapability(profiles, profileId, required) {
536
539
  return capabilitiesFor(profiles, profileId).includes(required);
537
540
  }
541
+ function workflowJobId(id) {
542
+ switch (id.kind) {
543
+ case "start":
544
+ return `wft_${id.runId}_0`;
545
+ case "start-redrive":
546
+ return `wft_${id.runId}_start_r${id.now}`;
547
+ case "tick":
548
+ return `wft_${id.runId}_${id.seq}`;
549
+ case "timer":
550
+ return `wft_${id.runId}_timer_${id.stepId}_${id.attempt}`;
551
+ case "timer-remainder":
552
+ return `wft_${id.runId}_timer_${id.stepId}_${id.attempt}_r${id.now}`;
553
+ case "timer-deferral":
554
+ return `wft_${id.runId}_timer_${id.stepId}_${id.attempt}_d${id.now}`;
555
+ case "timer-sibling":
556
+ return `wft_${id.runId}_timer_${id.stepId}_${id.attempt}_w${id.now}`;
557
+ case "retry":
558
+ return `wft_${id.runId}_retry_${id.stepId}_${id.attempt}`;
559
+ case "respawn":
560
+ return `wft_${id.runId}_respawn_${id.stepId}_${id.attempt}_s${id.segment}_r${id.now}`;
561
+ case "redrive":
562
+ return `wft_${id.runId}_redrive_${id.stepId}_${id.attempt}_r${id.now}`;
563
+ case "redispatch":
564
+ return `wft_${id.runId}_redispatch_${id.stepId}_${id.attempt}_x${id.executionId ?? "none"}_r${id.now}`;
565
+ case "expiry":
566
+ return `wft_${id.runId}_expire_${id.stepId}_${id.attempt}_t${id.suspendedAt}${id.hop > 0 ? `_h${id.hop}` : ""}`;
567
+ case "step-terminal":
568
+ return `wft_${id.runId}_term_${id.stepId}_${id.attempt}_r${id.now}`;
569
+ case "handback":
570
+ return `wft_${id.runId}_handback_${id.stepId}_${id.attempt}_${id.now}`;
571
+ case "foreach-rate":
572
+ return `wft_${id.runId}_fe_${id.stepId}_${id.attempt}_${id.bucket}`;
573
+ case "budget-expiry":
574
+ return `wft_${id.runId}_expiry_budget_r${id.now}`;
575
+ case "budget-resume":
576
+ return `wft_${id.runId}_resume_budget_${id.now}`;
577
+ case "deadline":
578
+ return `wft_${id.runId}_deadline_${id.deadlineAt}`;
579
+ case "deadline-remainder":
580
+ return `wft_${id.runId}_deadline_${id.deadlineAt}_r${id.now}`;
581
+ case "readmit":
582
+ return `wft_${id.runId}_readmit_${id.now}`;
583
+ case "cancel":
584
+ return `wft_${id.runId}_cancel`;
585
+ case "reconcile":
586
+ return `wft_${id.runId}_reconcile_${id.now}`;
587
+ case "replay":
588
+ return `wft_${id.runId}_replay_g${id.leaseGeneration}`;
589
+ case "migration":
590
+ return `wfm_${id.migrationId}`;
591
+ default:
592
+ return assertNever(id);
593
+ }
594
+ }
595
+ function assertNever(id) {
596
+ throw new Error(`workflowJobId: unknown wake kind ${JSON.stringify(id)}`);
597
+ }
598
+ function agentStepJobId(msg) {
599
+ return `${msg.runId}_${msg.stepId}_${msg.attempt}_x${msg.executionId}`;
600
+ }
601
+ function heavyStepJobId(msg) {
602
+ return `${msg.runId}_${msg.stepId}_${msg.attempt}_x${msg.executionId}`;
603
+ }
604
+ function jobSpawnJobId(msg) {
605
+ return `${msg.runId}_${msg.stepId}_${msg.attempt}_s${msg.segment}_${msg.executionId}`;
606
+ }
607
+ function exportAutoJobId(exportId, suffix) {
608
+ return suffix ? `${exportId}:${suffix}` : exportId;
609
+ }
538
610
  function isSystemRun(identity) {
539
611
  return identity.userId.startsWith(SYSTEM_USER_PREFIX);
540
612
  }
@@ -581,6 +653,75 @@ function scheduledTimeKey(scheduledTime) {
581
653
  function scheduledWorkflowRunIdForTime(jobId, scheduledTime) {
582
654
  return scheduledWorkflowRunId(jobId, scheduledTime);
583
655
  }
656
+ function workflowRetryMaxAttemptsMessage(got) {
657
+ const tail = got === void 0 ? "" : ` (got ${JSON.stringify(got)})`;
658
+ return `\`retry.maxAttempts\` must be an integer ${WORKFLOW_RETRY_MIN_ATTEMPTS}..${WORKFLOW_RETRY_MAX_ATTEMPTS}${tail}`;
659
+ }
660
+ function isWithinWorkflowRetryAttempts(value3) {
661
+ return typeof value3 === "number" && Number.isInteger(value3) && value3 >= WORKFLOW_RETRY_MIN_ATTEMPTS && value3 <= WORKFLOW_RETRY_MAX_ATTEMPTS;
662
+ }
663
+ function workflowRetryUnknownMembersMessage(keys) {
664
+ const named = keys.map((k) => {
665
+ const engine = WORKFLOW_RETRY_ENGINE_KEYS.includes(k);
666
+ return `\`${k}\`${engine ? " (engine-owned \u2014 stamped by resetAttempts, never authored)" : ""}`;
667
+ });
668
+ return `\`retry\` has no member ${named.join(", ")}; members: ${WORKFLOW_RETRY_POLICY_KEYS.join(", ")}`;
669
+ }
670
+ function unknownWorkflowRetryMembers(retry) {
671
+ if (!retry || typeof retry !== "object" || Array.isArray(retry)) return [];
672
+ return Object.keys(retry).filter((k) => !WORKFLOW_RETRY_POLICY_KEYS.includes(k));
673
+ }
674
+ function authoredRetryPolicy(retry) {
675
+ if (!retry || typeof retry !== "object" || Array.isArray(retry)) return void 0;
676
+ const src = retry;
677
+ const out = {};
678
+ for (const k of WORKFLOW_RETRY_POLICY_KEYS) if (src[k] !== void 0) out[k] = src[k];
679
+ return Object.keys(out).length ? out : void 0;
680
+ }
681
+ function retryBudgetBaseAttempt(row) {
682
+ const base = row.retry?.budgetBaseAttempt;
683
+ return typeof base === "number" && Number.isSafeInteger(base) && base > 0 ? base : 0;
684
+ }
685
+ function retryBudgetAttempt(row) {
686
+ return Math.max(0, row.attempt - retryBudgetBaseAttempt(row));
687
+ }
688
+ function retryBudgetMaxAttempts(row) {
689
+ const max = row.retry?.maxAttempts;
690
+ return typeof max === "number" && Number.isFinite(max) && max >= 1 ? max : 1;
691
+ }
692
+ function retryBudgetRemaining(row) {
693
+ return retryBudgetAttempt(row) < retryBudgetMaxAttempts(row);
694
+ }
695
+ function retriesRemaining(row) {
696
+ return Math.max(0, retryBudgetMaxAttempts(row) - retryBudgetAttempt(row));
697
+ }
698
+ function isWithinWorkflowJobRange(member, value3) {
699
+ const { min, max } = WORKFLOW_JOB_RANGES[member];
700
+ return typeof value3 === "number" && Number.isInteger(value3) && value3 >= min && value3 <= max;
701
+ }
702
+ function workflowJobRangeMessage(member) {
703
+ const { min, max } = WORKFLOW_JOB_RANGES[member];
704
+ return `\`${member}\` must be an integer ${min}..${max}`;
705
+ }
706
+ function isWorkflowSingleStepType(type) {
707
+ return typeof type === "string" && WORKFLOW_SINGLE_STEP_TYPES.includes(type);
708
+ }
709
+ function isWorkflowHitlEntryType(type) {
710
+ return typeof type === "string" && WORKFLOW_HITL_ENTRY_TYPES.includes(type);
711
+ }
712
+ function isWorkflowArmEntryType(type) {
713
+ return typeof type === "string" && WORKFLOW_ARM_ENTRY_TYPES.includes(type);
714
+ }
715
+ function workflowContainerRunsHitlArm(container) {
716
+ return typeof container === "string" && WORKFLOW_HITL_ARM_CONTAINERS.includes(container);
717
+ }
718
+ function workflowHitlArmUnsupportedMessage(type, id, container) {
719
+ const legal = WORKFLOW_HITL_ARM_CONTAINERS.map((c) => `\`${c}\``).join(" / ");
720
+ return `the engine does not run \`${type}\` inside a \`${container}\` yet (node "${id}") \u2014 place it at the top level, as a ${legal} arm, or inside a child \`workflow\``;
721
+ }
722
+ function workflowHitlArmShapeMessage(type, id, shape) {
723
+ return shape === "mapped-arm" ? `\`${type}\` arm "${id}" takes the previous output as its payload \u2014 it cannot head a [mapping, step] chain; map before the container instead` : `a chunked foreach hands each child a slice of items, not one \u2014 \`${type}\` body "${id}" takes one item; drop \`chunk\``;
724
+ }
584
725
  function groupCount(re) {
585
726
  let n2 = GROUP_COUNT.get(re);
586
727
  if (n2 === void 0) {
@@ -589,6 +730,9 @@ function groupCount(re) {
589
730
  }
590
731
  return n2;
591
732
  }
733
+ function isWorkflowSecretKey(key) {
734
+ return WORKFLOW_SECRET_KEY_RE.test(key);
735
+ }
592
736
  function applyPatterns(text, patterns) {
593
737
  let out = text;
594
738
  for (const { re, suffix } of patterns) {
@@ -611,8 +755,15 @@ function scrubSecretText(text) {
611
755
  if (typeof text !== "string" || text.length < 4) return text;
612
756
  return applyPatterns(applyPatterns(text, SECRET_LITERAL_PATTERNS), SECRET_PAIR_PATTERNS);
613
757
  }
758
+ function boundScrubInput(text, max = SCRUB_INPUT_MAX_CHARS) {
759
+ if (typeof text !== "string" || text.length <= max) return text;
760
+ const window = text.slice(Math.max(0, max - SCRUB_CUT_BACKOFF_CHARS), max);
761
+ const ws = window.search(/\s\S*$/);
762
+ const cut = ws >= 0 ? max - window.length + ws : max;
763
+ return `${text.slice(0, cut)}\u2026`;
764
+ }
614
765
  function scrubSecretLines(lines) {
615
- return lines.map((l) => typeof l === "string" ? scrubSecretText(l) : l);
766
+ return lines.map((l) => typeof l === "string" ? scrubSecretText(boundScrubInput(l)) : l);
616
767
  }
617
768
  function messageText(value3) {
618
769
  if (typeof value3 === "string") return value3;
@@ -629,11 +780,14 @@ function messageText(value3) {
629
780
  return value3 === void 0 || value3 === null ? "" : String(value3);
630
781
  }
631
782
  function scrubProviderMessage(raw, max = PROVIDER_MESSAGE_MAX_CHARS) {
632
- const text = messageText(raw).replace(/\s+/g, " ").trim();
783
+ const text = boundScrubInput(messageText(raw)).replace(/\s+/g, " ").trim();
633
784
  if (!text) return void 0;
634
785
  const out = scrubSecretText(text);
635
786
  return out.length > max ? `${out.slice(0, max - 1)}\u2026` : out;
636
787
  }
788
+ function scrubStepErrorMessage(raw) {
789
+ return scrubProviderMessage(raw, ERROR_MESSAGE_MAX_CHARS);
790
+ }
637
791
  function isWorkflowAuditEvent(action) {
638
792
  return typeof action === "string" && WORKFLOW_AUDIT_EVENTS.includes(action);
639
793
  }
@@ -728,7 +882,66 @@ ${PREAMBLE}
728
882
 
729
883
  ${items.join("\n\n")}`;
730
884
  }
731
- 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, 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, REDACTED_PLACEHOLDER, PROVIDER_MESSAGE_MAX_CHARS, SECRET_LITERAL_PATTERNS, SECRET_NAME, SECRET_PAIR_PATTERNS, GROUP_COUNT, WORKFLOW_AUDIT_EVENTS, WORKFLOW_AUDIT_METADATA_MAX_BYTES, INDENT, WRAP_WIDTH, NOUNS, GET_TOOL_NAMES, PREAMBLE;
885
+ function workflowApprovalDecisionOf(resumeData) {
886
+ if (resumeData.timedOut === true) return "timed_out";
887
+ return resumeData.approved === true ? "approved" : "denied";
888
+ }
889
+ function workflowApprovalOutput(resumeData) {
890
+ const decision = typeof resumeData.decision === "string" ? resumeData.decision : workflowApprovalDecisionOf(resumeData);
891
+ const note = typeof resumeData.note === "string" && resumeData.note.trim() !== "" ? resumeData.note : void 0;
892
+ const text = typeof resumeData.text === "string" ? resumeData.text : note ?? decision;
893
+ return {
894
+ ...resumeData,
895
+ decision,
896
+ text
897
+ };
898
+ }
899
+ function isWorkflowApprovalOutput(value3) {
900
+ if (typeof value3 !== "object" || value3 === null || Array.isArray(value3)) return false;
901
+ const v = value3;
902
+ return typeof v.approved === "boolean" && WORKFLOW_APPROVAL_OUTPUT_DECISIONS.includes(v.decision) && typeof v.text === "string";
903
+ }
904
+ function extractSingleJsonValue(text) {
905
+ const trimmed = (text ?? "").trim();
906
+ if (!trimmed) return {
907
+ reason: "reply is empty"
908
+ };
909
+ const fenced = [
910
+ ...trimmed.matchAll(JSON_FENCE_RE)
911
+ ];
912
+ if (fenced.length > 1) return {
913
+ reason: "reply carries more than one fenced block"
914
+ };
915
+ const candidate = fenced.length === 1 ? fenced[0][1].trim() : trimmed;
916
+ try {
917
+ return {
918
+ value: JSON.parse(candidate)
919
+ };
920
+ } catch {
921
+ if (fenced.length === 1) return {
922
+ reason: "fenced block is not valid JSON"
923
+ };
924
+ }
925
+ const opens = [
926
+ trimmed.indexOf("{"),
927
+ trimmed.indexOf("[")
928
+ ].filter((i) => i !== -1);
929
+ const start = opens.length ? Math.min(...opens) : -1;
930
+ const end = Math.max(trimmed.lastIndexOf("}"), trimmed.lastIndexOf("]"));
931
+ if (start === -1 || end <= start) return {
932
+ reason: "reply is not JSON"
933
+ };
934
+ try {
935
+ return {
936
+ value: JSON.parse(trimmed.slice(start, end + 1))
937
+ };
938
+ } catch {
939
+ return {
940
+ reason: "reply does not contain a single JSON value"
941
+ };
942
+ }
943
+ }
944
+ 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_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;
732
945
  var init_dist = __esm({
733
946
  "../shared-types/dist/index.mjs"() {
734
947
  "use strict";
@@ -1059,6 +1272,9 @@ var init_dist = __esm({
1059
1272
  ];
1060
1273
  __name(isImplicitModelSelectionSource, "isImplicitModelSelectionSource");
1061
1274
  __name2(isImplicitModelSelectionSource, "isImplicitModelSelectionSource");
1275
+ PLATFORM_FALLBACK_MODEL_SOURCE = "platform-fallback";
1276
+ __name(isPlatformFallbackModelSource, "isPlatformFallbackModelSource");
1277
+ __name2(isPlatformFallbackModelSource, "isPlatformFallbackModelSource");
1062
1278
  __name(resolveRequireToolApproval, "resolveRequireToolApproval");
1063
1279
  __name2(resolveRequireToolApproval, "resolveRequireToolApproval");
1064
1280
  AGENT_NAME_TOKEN = "[Your Agent Name]";
@@ -1711,6 +1927,18 @@ This text is who you are for this person. As you learn them, their name, their w
1711
1927
  __name2(isKnownProfile, "isKnownProfile");
1712
1928
  __name(hasCapability, "hasCapability");
1713
1929
  __name2(hasCapability, "hasCapability");
1930
+ __name(workflowJobId, "workflowJobId");
1931
+ __name2(workflowJobId, "workflowJobId");
1932
+ __name(assertNever, "assertNever");
1933
+ __name2(assertNever, "assertNever");
1934
+ __name(agentStepJobId, "agentStepJobId");
1935
+ __name2(agentStepJobId, "agentStepJobId");
1936
+ __name(heavyStepJobId, "heavyStepJobId");
1937
+ __name2(heavyStepJobId, "heavyStepJobId");
1938
+ __name(jobSpawnJobId, "jobSpawnJobId");
1939
+ __name2(jobSpawnJobId, "jobSpawnJobId");
1940
+ __name(exportAutoJobId, "exportAutoJobId");
1941
+ __name2(exportAutoJobId, "exportAutoJobId");
1714
1942
  SYSTEM_USER_PREFIX = "system:";
1715
1943
  __name(isSystemRun, "isSystemRun");
1716
1944
  __name2(isSystemRun, "isSystemRun");
@@ -1796,8 +2024,130 @@ This text is who you are for this person. As you learn them, their name, their w
1796
2024
  __name(scheduledWorkflowRunIdForTime, "scheduledWorkflowRunIdForTime");
1797
2025
  __name2(scheduledWorkflowRunIdForTime, "scheduledWorkflowRunIdForTime");
1798
2026
  WORKFLOW_SIGNAL_PAYLOAD_MAX_BYTES = 64 * 1024;
2027
+ WORKFLOW_RESOLVE_OUTPUT_MAX_BYTES = 256 * 1024;
2028
+ WORKFLOW_RETRY_BACKOFFS = [
2029
+ "fixed",
2030
+ "exponential"
2031
+ ];
2032
+ WORKFLOW_RETRY_MIN_ATTEMPTS = 1;
2033
+ WORKFLOW_RETRY_POLICY_KEYS = [
2034
+ "maxAttempts",
2035
+ "backoffSeconds",
2036
+ "backoff",
2037
+ "maxBackoffSeconds"
2038
+ ];
2039
+ WORKFLOW_RETRY_MAX_ATTEMPTS = 20;
2040
+ __name(workflowRetryMaxAttemptsMessage, "workflowRetryMaxAttemptsMessage");
2041
+ __name2(workflowRetryMaxAttemptsMessage, "workflowRetryMaxAttemptsMessage");
2042
+ __name(isWithinWorkflowRetryAttempts, "isWithinWorkflowRetryAttempts");
2043
+ __name2(isWithinWorkflowRetryAttempts, "isWithinWorkflowRetryAttempts");
2044
+ WORKFLOW_RETRY_ENGINE_KEYS = [
2045
+ "budgetBaseAttempt"
2046
+ ];
2047
+ __name(workflowRetryUnknownMembersMessage, "workflowRetryUnknownMembersMessage");
2048
+ __name2(workflowRetryUnknownMembersMessage, "workflowRetryUnknownMembersMessage");
2049
+ __name(unknownWorkflowRetryMembers, "unknownWorkflowRetryMembers");
2050
+ __name2(unknownWorkflowRetryMembers, "unknownWorkflowRetryMembers");
2051
+ __name(authoredRetryPolicy, "authoredRetryPolicy");
2052
+ __name2(authoredRetryPolicy, "authoredRetryPolicy");
2053
+ __name(retryBudgetBaseAttempt, "retryBudgetBaseAttempt");
2054
+ __name2(retryBudgetBaseAttempt, "retryBudgetBaseAttempt");
2055
+ __name(retryBudgetAttempt, "retryBudgetAttempt");
2056
+ __name2(retryBudgetAttempt, "retryBudgetAttempt");
2057
+ __name(retryBudgetMaxAttempts, "retryBudgetMaxAttempts");
2058
+ __name2(retryBudgetMaxAttempts, "retryBudgetMaxAttempts");
2059
+ __name(retryBudgetRemaining, "retryBudgetRemaining");
2060
+ __name2(retryBudgetRemaining, "retryBudgetRemaining");
2061
+ __name(retriesRemaining, "retriesRemaining");
2062
+ __name2(retriesRemaining, "retriesRemaining");
2063
+ WORKFLOW_JOB_RESOURCES = [
2064
+ "small",
2065
+ "medium",
2066
+ "large"
2067
+ ];
2068
+ WORKFLOW_SIDE_EFFECTS = [
2069
+ "none",
2070
+ "external"
2071
+ ];
2072
+ WORKFLOW_JOB_RANGES = Object.freeze({
2073
+ /** Coding-turn cap (`claude -p --max-turns`); absent ⇒ the cluster default (LUA_WF_JOB_MAX_TURNS). */
2074
+ maxTurns: Object.freeze({
2075
+ min: 1,
2076
+ max: 500
2077
+ }),
2078
+ /** Harness messages per attempt (one per content block); absent ⇒ the cluster default (400). */
2079
+ maxMessages: Object.freeze({
2080
+ min: 1,
2081
+ max: 5e3
2082
+ }),
2083
+ /** Input-side tokens per attempt (prompt + cache); absent ⇒ the cluster default (4M — LUA-708, was 30M). */
2084
+ maxInputTokens: Object.freeze({
2085
+ min: 1e6,
2086
+ max: 5e8
2087
+ })
2088
+ });
2089
+ WORKFLOW_JOB_RANGE_MEMBERS = Object.keys(WORKFLOW_JOB_RANGES);
2090
+ __name(isWithinWorkflowJobRange, "isWithinWorkflowJobRange");
2091
+ __name2(isWithinWorkflowJobRange, "isWithinWorkflowJobRange");
2092
+ __name(workflowJobRangeMessage, "workflowJobRangeMessage");
2093
+ __name2(workflowJobRangeMessage, "workflowJobRangeMessage");
2094
+ WORKFLOW_SINGLE_STEP_TYPES = [
2095
+ "agent",
2096
+ "tool",
2097
+ "workflow",
2098
+ "step"
2099
+ ];
2100
+ WORKFLOW_HITL_ENTRY_TYPES = [
2101
+ "approval",
2102
+ "waitForSignal"
2103
+ ];
2104
+ WORKFLOW_ARM_ENTRY_TYPES = [
2105
+ ...WORKFLOW_SINGLE_STEP_TYPES,
2106
+ ...WORKFLOW_HITL_ENTRY_TYPES
2107
+ ];
2108
+ WORKFLOW_HITL_ARM_CONTAINERS = [
2109
+ "parallel",
2110
+ "conditional",
2111
+ "foreach"
2112
+ ];
2113
+ __name(isWorkflowSingleStepType, "isWorkflowSingleStepType");
2114
+ __name2(isWorkflowSingleStepType, "isWorkflowSingleStepType");
2115
+ __name(isWorkflowHitlEntryType, "isWorkflowHitlEntryType");
2116
+ __name2(isWorkflowHitlEntryType, "isWorkflowHitlEntryType");
2117
+ __name(isWorkflowArmEntryType, "isWorkflowArmEntryType");
2118
+ __name2(isWorkflowArmEntryType, "isWorkflowArmEntryType");
2119
+ __name(workflowContainerRunsHitlArm, "workflowContainerRunsHitlArm");
2120
+ __name2(workflowContainerRunsHitlArm, "workflowContainerRunsHitlArm");
2121
+ __name(workflowHitlArmUnsupportedMessage, "workflowHitlArmUnsupportedMessage");
2122
+ __name2(workflowHitlArmUnsupportedMessage, "workflowHitlArmUnsupportedMessage");
2123
+ __name(workflowHitlArmShapeMessage, "workflowHitlArmShapeMessage");
2124
+ __name2(workflowHitlArmShapeMessage, "workflowHitlArmShapeMessage");
2125
+ WORKFLOW_GRAPH_ENTRY_STEP_KINDS = Object.freeze({
2126
+ agent: "agent",
2127
+ tool: "tool",
2128
+ workflow: "subrun",
2129
+ step: "code",
2130
+ mapping: "map",
2131
+ sleep: "sleep",
2132
+ sleepUntil: "sleepUntil",
2133
+ parallel: null,
2134
+ conditional: "branch",
2135
+ foreach: "foreach",
2136
+ loop: "loop",
2137
+ approval: "approval",
2138
+ waitForSignal: "signal"
2139
+ });
2140
+ WORKFLOW_ARM_ENTRY_STEP_KINDS = Object.freeze(Object.fromEntries(WORKFLOW_ARM_ENTRY_TYPES.map((t) => [
2141
+ t,
2142
+ WORKFLOW_GRAPH_ENTRY_STEP_KINDS[t]
2143
+ ])));
2144
+ WORKFLOW_BUDGET_MAX_DURATION_SECONDS = Object.freeze({
2145
+ min: 60,
2146
+ max: 2592e3
2147
+ });
1799
2148
  REDACTED_PLACEHOLDER = "[REDACTED]";
1800
2149
  PROVIDER_MESSAGE_MAX_CHARS = 300;
2150
+ ERROR_MESSAGE_MAX_CHARS = 2e3;
1801
2151
  SECRET_LITERAL_PATTERNS = [
1802
2152
  {
1803
2153
  re: /\b(github_pat_)[A-Za-z0-9_]{16,}/g
@@ -1808,6 +2158,10 @@ This text is who you are for this person. As you learn them, their name, their w
1808
2158
  {
1809
2159
  re: /\b(glpat-)[A-Za-z0-9_-]{16,}/g
1810
2160
  },
2161
+ // LUA-696 review (3): the npm granular / classic token (`npm_` + 36 alphanumerics).
2162
+ {
2163
+ re: /\b(npm_)[A-Za-z0-9]{36}\b/g
2164
+ },
1811
2165
  {
1812
2166
  re: /\b(sk-ant-)[A-Za-z0-9_-]{16,}/g
1813
2167
  },
@@ -1844,8 +2198,15 @@ This text is who you are for this person. As you learn them, their name, their w
1844
2198
  // `FOO_TOKEN=x`, `secretKey=x`, `password=x`. Groups: the char before the name, the name, the separator
1845
2199
  // (with its quotes), the scheme word — all kept; the value goes. A value a literal rule already replaced
1846
2200
  // (`x-access-token:[REDACTED]@host`) is left alone so the host after it survives.
2201
+ // LUA-696: the second lookahead keeps a scrubbed `Authorization: Bearer [REDACTED]` as it is — without it the
2202
+ // optional scheme group backtracks to empty and `Bearer` itself becomes the value (`[REDACTED] [REDACTED]`).
2203
+ // The scrub is applied more than once on purpose (the Job site at its copy, the outcome table at the row
2204
+ // write, lua-api on the way out), so it must be idempotent.
2205
+ // LUA-696 review (HIGH): the identifier prefix is BOUNDED (`{1,64}`) — unbounded, `[A-Za-z0-9-]+` made the
2206
+ // scan quadratic on `-`-heavy text (100 KB of `-` took 12.7 s on the lua-core event loop; a worker-tier step
2207
+ // can throw that). No identifier that names a credential is longer; the probe list is unchanged.
1847
2208
  {
1848
- re: new RegExp(`(^|[^A-Za-z0-9])((?:[A-Za-z0-9-]+[_-])?${SECRET_NAME}|[A-Za-z0-9-]+_key)(["']?\\s*[:=]\\s*["']?)((?:basic\\s+|bearer\\s+|token\\s+)?)(?!\\[REDACTED\\])[^\\s"',;)}&]{4,}`, "gi")
2209
+ re: new RegExp(`(^|[^A-Za-z0-9])((?:[A-Za-z0-9-]{1,64}[_-])?${SECRET_NAME}|[A-Za-z0-9-]{1,64}_key)(["']?\\s*[:=]\\s*["']?)((?:basic\\s+|bearer\\s+|token\\s+)?)(?!\\[REDACTED\\]|(?:basic|bearer|token)\\s+\\[REDACTED\\])[^\\s"',;)}&]{4,}`, "gi")
1849
2210
  },
1850
2211
  // `?token=x`, `&key=x`, `&X-Amz-Signature=x`, `&sig=x`
1851
2212
  {
@@ -1864,16 +2225,47 @@ This text is who you are for this person. As you learn them, their name, their w
1864
2225
  GROUP_COUNT = /* @__PURE__ */ new WeakMap();
1865
2226
  __name(groupCount, "groupCount");
1866
2227
  __name2(groupCount, "groupCount");
2228
+ WORKFLOW_SECRET_KEY_RE = /(^|[_\-.\s])(secret|token|password|passwd|pwd|passphrase|api[_-]?key|apikey|access[_-]?key|secret[_-]?key|private[_-]?key|client[_-]?secret|authorization|auth[_-]?token|access[_-]?token|id[_-]?token|refresh[_-]?token|session[_-]?key|credential(s)?)([_\-.\s]|$)|^(secret|token|password|passwd|pwd|passphrase|apikey|authorization|credentials?)$/i;
2229
+ WORKFLOW_RESERVED_SECRET_KEYS = Object.freeze([
2230
+ "secret",
2231
+ "token",
2232
+ "password",
2233
+ "passwd",
2234
+ "pwd",
2235
+ "passphrase",
2236
+ "api_key",
2237
+ "apikey",
2238
+ "access_key",
2239
+ "secret_key",
2240
+ "private_key",
2241
+ "client_secret",
2242
+ "authorization",
2243
+ "auth_token",
2244
+ "access_token",
2245
+ "id_token",
2246
+ "refresh_token",
2247
+ "session_key",
2248
+ "credential",
2249
+ "credentials"
2250
+ ]);
2251
+ __name(isWorkflowSecretKey, "isWorkflowSecretKey");
2252
+ __name2(isWorkflowSecretKey, "isWorkflowSecretKey");
1867
2253
  __name(applyPatterns, "applyPatterns");
1868
2254
  __name2(applyPatterns, "applyPatterns");
1869
2255
  __name(scrubSecretText, "scrubSecretText");
1870
2256
  __name2(scrubSecretText, "scrubSecretText");
2257
+ SCRUB_INPUT_MAX_CHARS = 16 * 1024;
2258
+ SCRUB_CUT_BACKOFF_CHARS = 256;
2259
+ __name(boundScrubInput, "boundScrubInput");
2260
+ __name2(boundScrubInput, "boundScrubInput");
1871
2261
  __name(scrubSecretLines, "scrubSecretLines");
1872
2262
  __name2(scrubSecretLines, "scrubSecretLines");
1873
2263
  __name(messageText, "messageText");
1874
2264
  __name2(messageText, "messageText");
1875
2265
  __name(scrubProviderMessage, "scrubProviderMessage");
1876
2266
  __name2(scrubProviderMessage, "scrubProviderMessage");
2267
+ __name(scrubStepErrorMessage, "scrubStepErrorMessage");
2268
+ __name2(scrubStepErrorMessage, "scrubStepErrorMessage");
1877
2269
  WORKFLOW_AUDIT_EVENTS = [
1878
2270
  // --- definitions / versions / templates (13, 11 §11.11.5) ---
1879
2271
  "workflow.published",
@@ -1940,6 +2332,8 @@ This text is who you are for this person. As you learn them, their name, their w
1940
2332
  "workflow.goal.resumed",
1941
2333
  "workflow.goal.done",
1942
2334
  "workflow.goal.closed",
2335
+ // LUA-760: an ended goal's cadence Job retired (deleted) — inline on done / closed, by R21 / R28, or by sweep #30
2336
+ "workflow.goal.job_retired",
1943
2337
  // --- org policy (02 §2.10 / 09 R23) ---
1944
2338
  "workflow.policy.retention_changed",
1945
2339
  "workflow.policy.pacing_changed",
@@ -1989,6 +2383,79 @@ listed here; never invent a target.`;
1989
2383
  __name2(reachingIt, "reachingIt");
1990
2384
  __name(renderTargetsBlock, "renderTargetsBlock");
1991
2385
  __name2(renderTargetsBlock, "renderTargetsBlock");
2386
+ WORKFLOW_APPROVAL_OUTPUT_DECISIONS = [
2387
+ "approved",
2388
+ "denied",
2389
+ "timed_out"
2390
+ ];
2391
+ WORKFLOW_APPROVAL_OUTPUT_SCHEMA = {
2392
+ type: "object",
2393
+ properties: {
2394
+ approved: {
2395
+ type: "boolean"
2396
+ },
2397
+ decision: {
2398
+ type: "string",
2399
+ enum: [
2400
+ ...WORKFLOW_APPROVAL_OUTPUT_DECISIONS
2401
+ ]
2402
+ },
2403
+ /** the approver's note when given, else the decision word — what `${stepResults.<id>.text}` reads */
2404
+ text: {
2405
+ type: "string"
2406
+ },
2407
+ note: {
2408
+ type: "string"
2409
+ },
2410
+ editedPayload: {},
2411
+ editRevision: {
2412
+ type: "integer"
2413
+ },
2414
+ decidedBy: {
2415
+ type: "object",
2416
+ properties: {
2417
+ id: {
2418
+ type: "string"
2419
+ },
2420
+ kind: {
2421
+ type: "string"
2422
+ }
2423
+ }
2424
+ },
2425
+ timedOut: {
2426
+ type: "boolean"
2427
+ },
2428
+ escalations: {
2429
+ type: "integer"
2430
+ },
2431
+ evidence: {
2432
+ type: "array",
2433
+ items: {
2434
+ type: "string"
2435
+ }
2436
+ },
2437
+ items: {
2438
+ type: "array",
2439
+ items: {
2440
+ type: "object"
2441
+ }
2442
+ }
2443
+ },
2444
+ required: [
2445
+ "approved",
2446
+ "decision",
2447
+ "text"
2448
+ ]
2449
+ };
2450
+ __name(workflowApprovalDecisionOf, "workflowApprovalDecisionOf");
2451
+ __name2(workflowApprovalDecisionOf, "workflowApprovalDecisionOf");
2452
+ __name(workflowApprovalOutput, "workflowApprovalOutput");
2453
+ __name2(workflowApprovalOutput, "workflowApprovalOutput");
2454
+ __name(isWorkflowApprovalOutput, "isWorkflowApprovalOutput");
2455
+ __name2(isWorkflowApprovalOutput, "isWorkflowApprovalOutput");
2456
+ JSON_FENCE_RE = /```(?:json)?[ \t]*\r?\n([\s\S]*?)\r?\n?```/g;
2457
+ __name(extractSingleJsonValue, "extractSingleJsonValue");
2458
+ __name2(extractSingleJsonValue, "extractSingleJsonValue");
1992
2459
  }
1993
2460
  });
1994
2461
 
@@ -1997,151 +2464,375 @@ import { createHash } from "crypto";
1997
2464
  import { z as z4 } from "zod";
1998
2465
  import { z as z22 } from "zod";
1999
2466
  import { createHash as createHash2 } from "crypto";
2000
- function sleepUntilUnsupportedMessage(id) {
2001
- 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} }`;
2002
- }
2003
- function fillPolicy(node, defaultTimeout) {
2004
- if (node.tier === void 0 && node.workspace !== void 0 && node.workspace !== "inherit") node.tier = "job";
2005
- if (node.timeoutSeconds === void 0) node.timeoutSeconds = node.tier === "job" ? WORKFLOW_JOB_DEFAULT_TIMEOUT_SECONDS : defaultTimeout;
2006
- if (node.retry === void 0) node.retry = {
2007
- maxAttempts: 1
2008
- };
2009
- if (node.onError === void 0) node.onError = "fail";
2010
- if ((node.type === "step" || node.type === "tool") && node.sideEffects === void 0) node.sideEffects = "none";
2467
+ function isMapConfigObject(v) {
2468
+ return typeof v === "object" && v !== null && !Array.isArray(v);
2011
2469
  }
2012
- function fillSingle(node) {
2013
- switch (node.type) {
2014
- case "step": {
2015
- fillPolicy(node, WORKFLOW_STEP_DEFAULT_TIMEOUT_SECONDS);
2016
- const s = node;
2017
- if (s.resumeTimeoutHours === void 0) s.resumeTimeoutHours = WORKFLOW_SUSPEND_DEFAULT_TIMEOUT_HOURS;
2018
- if (s.onSuspendTimeout === void 0) s.onSuspendTimeout = "fail";
2019
- return;
2020
- }
2021
- case "agent":
2022
- fillPolicy(node, WORKFLOW_AGENT_DEFAULT_TIMEOUT_SECONDS);
2023
- return;
2024
- case "tool":
2025
- fillPolicy(node, WORKFLOW_STEP_DEFAULT_TIMEOUT_SECONDS);
2026
- return;
2027
- case "workflow":
2028
- if (node.workflowId === WORKFLOW_ARM_SUBRUN_ID && Array.isArray(node.graph) && node.graph[1]) fillSingle(node.graph[1]);
2029
- return;
2470
+ function parseMapConfig(raw, stepId) {
2471
+ if (isMapConfigObject(raw)) return raw;
2472
+ if (typeof raw !== "string") {
2473
+ throw new Error(`Stored mapping step "${stepId}" has a mapConfig that is neither a JSON string nor an object.`);
2030
2474
  }
2031
- }
2032
- function fillArm(arm) {
2033
- if (arm.type !== "mapping") fillSingle(arm);
2034
- }
2035
- function fillEntry(entry) {
2036
- switch (entry.type) {
2037
- case "step":
2038
- case "agent":
2039
- case "tool":
2040
- case "workflow":
2041
- fillSingle(entry);
2042
- return;
2043
- case "parallel":
2044
- entry.steps.forEach(fillSingle);
2045
- return;
2046
- case "conditional": {
2047
- const c = entry;
2048
- if (c.exclusive === void 0) c.exclusive = false;
2049
- c.steps.forEach(fillArm);
2050
- if (c.otherwise) fillArm(c.otherwise);
2051
- return;
2052
- }
2053
- case "foreach": {
2054
- const f = entry;
2055
- f.opts = f.opts ?? {};
2056
- if (f.opts.concurrency === void 0) f.opts.concurrency = WORKFLOW_FOREACH_DEFAULT_CONCURRENCY;
2057
- if (f.opts.maxItems === void 0) f.opts.maxItems = WORKFLOW_FOREACH_DEFAULT_MAX_ITEMS;
2058
- fillSingle(f.step);
2059
- return;
2060
- }
2061
- case "loop": {
2062
- const l = entry;
2063
- if (l.maxIterations === void 0) l.maxIterations = WORKFLOW_LOOP_DEFAULT_MAX_ITERATIONS;
2064
- fillSingle(l.step);
2065
- return;
2066
- }
2067
- case "approval": {
2068
- const a = entry;
2069
- if (a.approver === void 0) a.approver = "creator";
2070
- if (a.timeoutHours === void 0) a.timeoutHours = WORKFLOW_SUSPEND_DEFAULT_TIMEOUT_HOURS;
2071
- if (a.onTimeout === void 0) a.onTimeout = "deny";
2072
- if (a.onDeny === void 0) a.onDeny = "continue";
2073
- if (a.excludeInitiator === void 0) a.excludeInitiator = false;
2074
- if (a.editable === void 0) a.editable = false;
2075
- return;
2076
- }
2077
- case "waitForSignal": {
2078
- const w = entry;
2079
- if (w.timeoutHours === void 0) w.timeoutHours = WORKFLOW_SUSPEND_DEFAULT_TIMEOUT_HOURS;
2080
- if (w.onTimeout === void 0) w.onTimeout = "fail";
2081
- if (w.acceptedSources === void 0) w.acceptedSources = [
2082
- ...WORKFLOW_SIGNAL_DEFAULT_SOURCES
2083
- ];
2084
- return;
2085
- }
2086
- case "mapping":
2087
- case "sleep":
2088
- case "sleepUntil":
2089
- return;
2475
+ try {
2476
+ return JSON.parse(raw);
2477
+ } catch (e) {
2478
+ throw new Error(`Stored mapping step "${stepId}" has invalid JSON mapConfig: ${e.message}`);
2090
2479
  }
2091
2480
  }
2092
- function withDefaultsFilled(g) {
2093
- const out = clone(g);
2094
- out.definition.graph.forEach(fillEntry);
2095
- return out;
2096
- }
2097
- function isConnectionKeyShaped(value22) {
2098
- return WORKFLOW_CONNECTION_KEY_RE.test(value22) && !CONNECTION_ID_HEX_RE.test(value22);
2481
+ function mapConfigWire(raw) {
2482
+ if (typeof raw === "string") return raw;
2483
+ if (isMapConfigObject(raw)) return canonicalJson(raw);
2484
+ return void 0;
2099
2485
  }
2100
- function connectionKeyUndeclaredMessage(path3, key) {
2101
- 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`;
2486
+ function describeBadPlaceholder(template22, idx, rawExpr) {
2487
+ return `Template placeholder #${idx} (\${${rawExpr}}) in '${template22}'`;
2102
2488
  }
2103
- function classifyModelProvider(model) {
2104
- const m = (model ?? "").trim().toLowerCase();
2105
- if (!m) return null;
2106
- if (/^(anthropic\/|claude)/.test(m)) return "anthropic";
2107
- if (/^(openai\/|gpt-|o[1-9](-|$)|chatgpt)/.test(m)) return "openai";
2108
- if (/^(google\/|gemini)/.test(m)) return "google";
2109
- return null;
2489
+ function parseTemplatePlaceholder(rawExpr) {
2490
+ const dot = rawExpr.indexOf(".");
2491
+ return {
2492
+ scope: dot === -1 ? rawExpr : rawExpr.slice(0, dot),
2493
+ rest: dot === -1 ? "" : rawExpr.slice(dot + 1)
2494
+ };
2110
2495
  }
2111
- function schemaAtPath(schema, path3) {
2112
- let cur = schema;
2113
- if (!cur || typeof cur !== "object") return void 0;
2114
- for (const seg of path3.split(".").filter(Boolean)) {
2115
- const props = cur.properties;
2116
- const next = props?.[seg];
2117
- if (!next || typeof next !== "object") return void 0;
2118
- cur = next;
2496
+ function traverseMappingPath(root, path3, errorLabel) {
2497
+ if (path3 === "" || path3 === ".") return root;
2498
+ const parts = path3.split(".");
2499
+ let value22 = root;
2500
+ for (const part of parts) {
2501
+ if (typeof value22 === "object" && value22 !== null) value22 = value22[part];
2502
+ else throw new WorkflowTemplateError(`Invalid path ${path3} in ${errorLabel}`, path3);
2119
2503
  }
2120
- return cur;
2121
- }
2122
- function templateStepRefs(text) {
2123
- const ids = [];
2124
- for (const m of text.matchAll(TEMPLATE_STEP_REF)) ids.push(m[1]);
2125
- return ids;
2504
+ return value22;
2126
2505
  }
2127
- function mapConfigStepRefs(raw) {
2128
- if (!raw) return [];
2129
- let cfg;
2130
- try {
2131
- cfg = typeof raw === "string" ? JSON.parse(raw) : raw;
2132
- } catch {
2133
- return [];
2134
- }
2135
- const ids = [];
2136
- for (const d of Object.values(cfg)) {
2137
- if (!d || typeof d !== "object") continue;
2138
- const desc = d;
2139
- if (desc.step !== void 0) ids.push(...Array.isArray(desc.step) ? desc.step : [
2140
- desc.step
2141
- ]);
2142
- if (typeof desc.template === "string") ids.push(...templateStepRefs(desc.template));
2506
+ function stringifyTemplateValue(v, template22, idx, rawExpr) {
2507
+ if (v === null || v === void 0) return "";
2508
+ if (typeof v === "object") {
2509
+ try {
2510
+ return JSON.stringify(v);
2511
+ } catch (err) {
2512
+ throw new WorkflowTemplateError(`${describeBadPlaceholder(template22, idx, rawExpr)} resolved to a value that could not be JSON-stringified (${err.message}).`, rawExpr);
2513
+ }
2143
2514
  }
2144
- return ids;
2515
+ return String(v);
2516
+ }
2517
+ function escapeFence(content) {
2518
+ return content.replace(/<\/lua-data/g, "<\\/lua-data");
2519
+ }
2520
+ function fenceBlock(name, source, content) {
2521
+ return `<lua-data name="${name}" source="${source}" untrusted="true">${escapeFence(content)}</lua-data>`;
2522
+ }
2523
+ function renderTemplate(template22, ctx, opts) {
2524
+ let idx = 0;
2525
+ return template22.replace(TEMPLATE_PLACEHOLDER, (_match, rawExpr) => {
2526
+ idx += 1;
2527
+ const { scope, rest } = parseTemplatePlaceholder(rawExpr);
2528
+ const label = describeBadPlaceholder(template22, idx, rawExpr);
2529
+ let rendered;
2530
+ let source;
2531
+ switch (scope) {
2532
+ case "initData":
2533
+ rendered = stringifyTemplateValue(traverseMappingPath(ctx.initData, rest, label), template22, idx, rawExpr);
2534
+ source = "initData";
2535
+ break;
2536
+ case "state":
2537
+ rendered = stringifyTemplateValue(traverseMappingPath(ctx.state, rest, label), template22, idx, rawExpr);
2538
+ source = "state";
2539
+ break;
2540
+ case "requestContext":
2541
+ rendered = stringifyTemplateValue(traverseMappingPath(ctx.requestContext, rest, label), template22, idx, rawExpr);
2542
+ source = "requestContext";
2543
+ break;
2544
+ case "stepResults": {
2545
+ const innerDot = rest.indexOf(".");
2546
+ const stepId = innerDot === -1 ? rest : rest.slice(0, innerDot);
2547
+ const subPath = innerDot === -1 ? "" : rest.slice(innerDot + 1);
2548
+ if (!stepId) throw new WorkflowTemplateError(`${label} must name a step: \${stepResults.<stepId>.<path>}.`, rawExpr);
2549
+ if (!(stepId in ctx.stepResults) || ctx.stepResults[stepId] == null) {
2550
+ 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);
2551
+ }
2552
+ rendered = stringifyTemplateValue(traverseMappingPath(ctx.stepResults[stepId], subPath, label), template22, idx, rawExpr);
2553
+ source = `step:${stepId}`;
2554
+ break;
2555
+ }
2556
+ default:
2557
+ throw new WorkflowTemplateError(`${label} references unknown namespace "${scope}". Use one of: ${TEMPLATE_NAMESPACES.join(", ")}.`, rawExpr);
2558
+ }
2559
+ return opts.fenced ? fenceBlock(rawExpr, source, rendered) : rendered;
2560
+ });
2561
+ }
2562
+ function isMapDescriptor(v) {
2563
+ if (v === null || typeof v !== "object" || Array.isArray(v)) return false;
2564
+ const d = v;
2565
+ const keys = Object.keys(d);
2566
+ const only = /* @__PURE__ */ __name3((...allowed) => keys.every((k) => allowed.includes(k)), "only");
2567
+ if ("value" in d) return keys.length === 1;
2568
+ if ("template" in d) return keys.length === 1 && typeof d.template === "string";
2569
+ if ("requestContextPath" in d) return keys.length === 1 && typeof d.requestContextPath === "string";
2570
+ if ("knowledge" in d) return keys.length === 1 && typeof d.knowledge === "object" && d.knowledge !== null;
2571
+ if ("initData" in d) return d.initData === true && typeof d.path === "string" && only("initData", "path");
2572
+ if ("step" in d) {
2573
+ const stepOk = typeof d.step === "string" || Array.isArray(d.step) && d.step.every((x) => typeof x === "string");
2574
+ return stepOk && typeof d.path === "string" && only("step", "path", "rows");
2575
+ }
2576
+ return false;
2577
+ }
2578
+ function malformedMapMembers(cfg) {
2579
+ if (!cfg || typeof cfg !== "object" || Array.isArray(cfg)) return [];
2580
+ const out = [];
2581
+ for (const [member, v] of Object.entries(cfg)) {
2582
+ if (!v || typeof v !== "object" || Array.isArray(v) || isMapDescriptor(v)) continue;
2583
+ const keys = Object.keys(v).filter((k) => MAP_DESCRIPTOR_KEYS.includes(k));
2584
+ if (keys.length > 0) out.push({
2585
+ member,
2586
+ keys
2587
+ });
2588
+ }
2589
+ return out;
2590
+ }
2591
+ function mapMemberMalformedMessage(id, m) {
2592
+ const keys = m.keys.map((k) => `\`${k}\``).join(", ");
2593
+ 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`;
2594
+ }
2595
+ function resolveDescriptor(key, m, ctx) {
2596
+ if (!isMapDescriptor(m)) return {
2597
+ value: m
2598
+ };
2599
+ try {
2600
+ if ("value" in m) return {
2601
+ value: m.value
2602
+ };
2603
+ if ("template" in m && typeof m.template === "string") {
2604
+ return {
2605
+ value: renderTemplate(m.template, ctx, {
2606
+ fenced: false
2607
+ })
2608
+ };
2609
+ }
2610
+ if ("knowledge" in m || "rows" in m && m.rows !== void 0) {
2611
+ return {
2612
+ error: "binding_unresolved",
2613
+ key
2614
+ };
2615
+ }
2616
+ if ("requestContextPath" in m) {
2617
+ const label = `requestContext path for key "${key}"`;
2618
+ return {
2619
+ value: traverseMappingPath(ctx.requestContext, m.requestContextPath, label)
2620
+ };
2621
+ }
2622
+ if ("path" in m) {
2623
+ const source = "initData" in m && m.initData ? "initData" : "step";
2624
+ if (source === "initData") {
2625
+ return {
2626
+ value: traverseMappingPath(ctx.initData, m.path, `initData for key "${key}"`)
2627
+ };
2628
+ }
2629
+ const stepRef = m.step;
2630
+ const candidates = Array.isArray(stepRef) ? stepRef : [
2631
+ stepRef
2632
+ ];
2633
+ const stepId = candidates.find((s) => ctx.stepResults[s] !== void 0 && ctx.stepResults[s] !== null);
2634
+ if (stepId === void 0) return {
2635
+ error: "binding_unresolved",
2636
+ key
2637
+ };
2638
+ return {
2639
+ value: traverseMappingPath(ctx.stepResults[stepId], m.path, `step ${candidates.join("|")} for key "${key}"`)
2640
+ };
2641
+ }
2642
+ return {
2643
+ error: "binding_unresolved",
2644
+ key
2645
+ };
2646
+ } catch (err) {
2647
+ if (err instanceof WorkflowTemplateError) return {
2648
+ error: "binding_unresolved",
2649
+ key
2650
+ };
2651
+ throw err;
2652
+ }
2653
+ }
2654
+ function resolveMapping(cfg, ctx) {
2655
+ const keys = Object.keys(cfg);
2656
+ if (keys.length === 1 && keys[0] === "") {
2657
+ return resolveDescriptor("", cfg[""], ctx);
2658
+ }
2659
+ const result = {};
2660
+ for (const key of keys) {
2661
+ const resolved = resolveDescriptor(key, cfg[key], ctx);
2662
+ if ("error" in resolved) return resolved;
2663
+ result[key] = resolved.value;
2664
+ }
2665
+ return {
2666
+ value: result
2667
+ };
2668
+ }
2669
+ function workspaceTemplatePath(template22) {
2670
+ const key = template22.trim();
2671
+ const expr = WORKSPACE_TEMPLATE_EXPR_RE.exec(key);
2672
+ if (expr) return expr[1].split(".");
2673
+ if (key.includes("${")) return void 0;
2674
+ return key.replace(/^(?:input|initData)\./, "").split(".");
2675
+ }
2676
+ function retryBackoffs() {
2677
+ if (!Array.isArray(WORKFLOW_RETRY_BACKOFFS)) {
2678
+ 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')");
2679
+ }
2680
+ return WORKFLOW_RETRY_BACKOFFS;
2681
+ }
2682
+ function sleepUntilUnsupportedMessage(id) {
2683
+ 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} }`;
2684
+ }
2685
+ function fillPolicy(node, defaultTimeout) {
2686
+ if (node.tier === void 0 && node.workspace !== void 0 && node.workspace !== "inherit") node.tier = "job";
2687
+ if (node.timeoutSeconds === void 0) node.timeoutSeconds = node.tier === "job" ? WORKFLOW_JOB_DEFAULT_TIMEOUT_SECONDS : defaultTimeout;
2688
+ if (node.retry === void 0) node.retry = {
2689
+ maxAttempts: 1
2690
+ };
2691
+ if (node.onError === void 0) node.onError = "fail";
2692
+ if ((node.type === "step" || node.type === "tool") && node.sideEffects === void 0) node.sideEffects = "none";
2693
+ }
2694
+ function fillSingle(node) {
2695
+ switch (node.type) {
2696
+ case "step": {
2697
+ fillPolicy(node, WORKFLOW_STEP_DEFAULT_TIMEOUT_SECONDS);
2698
+ const s = node;
2699
+ if (s.resumeTimeoutHours === void 0) s.resumeTimeoutHours = WORKFLOW_SUSPEND_DEFAULT_TIMEOUT_HOURS;
2700
+ if (s.onSuspendTimeout === void 0) s.onSuspendTimeout = "fail";
2701
+ return;
2702
+ }
2703
+ case "agent":
2704
+ fillPolicy(node, WORKFLOW_AGENT_DEFAULT_TIMEOUT_SECONDS);
2705
+ return;
2706
+ case "tool":
2707
+ fillPolicy(node, WORKFLOW_STEP_DEFAULT_TIMEOUT_SECONDS);
2708
+ return;
2709
+ case "workflow":
2710
+ if (node.workflowId === WORKFLOW_ARM_SUBRUN_ID && Array.isArray(node.graph) && node.graph[1]) fillSingle(node.graph[1]);
2711
+ return;
2712
+ }
2713
+ }
2714
+ function fillHitl(node) {
2715
+ if (node.type === "approval") {
2716
+ const a = node;
2717
+ if (a.approver === void 0) a.approver = "creator";
2718
+ if (a.timeoutHours === void 0) a.timeoutHours = WORKFLOW_SUSPEND_DEFAULT_TIMEOUT_HOURS;
2719
+ if (a.onTimeout === void 0) a.onTimeout = "deny";
2720
+ if (a.onDeny === void 0) a.onDeny = "continue";
2721
+ if (a.excludeInitiator === void 0) a.excludeInitiator = false;
2722
+ if (a.editable === void 0) a.editable = false;
2723
+ return;
2724
+ }
2725
+ const w = node;
2726
+ if (w.timeoutHours === void 0) w.timeoutHours = WORKFLOW_SUSPEND_DEFAULT_TIMEOUT_HOURS;
2727
+ if (w.onTimeout === void 0) w.onTimeout = "fail";
2728
+ if (w.acceptedSources === void 0) w.acceptedSources = [
2729
+ ...WORKFLOW_SIGNAL_DEFAULT_SOURCES
2730
+ ];
2731
+ }
2732
+ function fillArm(arm) {
2733
+ if (arm.type === "mapping") return;
2734
+ if (isHitlNode(arm)) fillHitl(arm);
2735
+ else fillSingle(arm);
2736
+ }
2737
+ function fillEntry(entry) {
2738
+ switch (entry.type) {
2739
+ case "step":
2740
+ case "agent":
2741
+ case "tool":
2742
+ case "workflow":
2743
+ fillSingle(entry);
2744
+ return;
2745
+ case "parallel":
2746
+ entry.steps.forEach(fillArm);
2747
+ return;
2748
+ case "conditional": {
2749
+ const c = entry;
2750
+ if (c.exclusive === void 0) c.exclusive = false;
2751
+ c.steps.forEach(fillArm);
2752
+ if (c.otherwise) fillArm(c.otherwise);
2753
+ return;
2754
+ }
2755
+ case "foreach": {
2756
+ const f = entry;
2757
+ f.opts = f.opts ?? {};
2758
+ if (f.opts.concurrency === void 0) f.opts.concurrency = WORKFLOW_FOREACH_DEFAULT_CONCURRENCY;
2759
+ if (f.opts.maxItems === void 0) f.opts.maxItems = WORKFLOW_FOREACH_DEFAULT_MAX_ITEMS;
2760
+ fillArm(f.step);
2761
+ return;
2762
+ }
2763
+ case "loop": {
2764
+ const l = entry;
2765
+ if (l.maxIterations === void 0) l.maxIterations = WORKFLOW_LOOP_DEFAULT_MAX_ITERATIONS;
2766
+ fillArm(l.step);
2767
+ return;
2768
+ }
2769
+ case "approval":
2770
+ case "waitForSignal":
2771
+ fillHitl(entry);
2772
+ return;
2773
+ case "mapping":
2774
+ case "sleep":
2775
+ case "sleepUntil":
2776
+ return;
2777
+ }
2778
+ }
2779
+ function withDefaultsFilled(g) {
2780
+ const out = clone(g);
2781
+ out.definition.graph.forEach(fillEntry);
2782
+ return out;
2783
+ }
2784
+ function isConnectionKeyShaped(value22) {
2785
+ return WORKFLOW_CONNECTION_KEY_RE.test(value22) && !CONNECTION_ID_HEX_RE.test(value22);
2786
+ }
2787
+ function connectionKeyUndeclaredMessage(path3, key) {
2788
+ 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`;
2789
+ }
2790
+ function classifyModelProvider(model) {
2791
+ const m = (model ?? "").trim().toLowerCase();
2792
+ if (!m) return null;
2793
+ if (/^(anthropic\/|claude)/.test(m)) return "anthropic";
2794
+ if (/^(openai\/|gpt-|o[1-9](-|$)|chatgpt)/.test(m)) return "openai";
2795
+ if (/^(google\/|gemini)/.test(m)) return "google";
2796
+ return null;
2797
+ }
2798
+ function schemaAtPath(schema, path3) {
2799
+ let cur = schema;
2800
+ if (!cur || typeof cur !== "object") return void 0;
2801
+ for (const seg of path3.split(".").filter(Boolean)) {
2802
+ const props = cur.properties;
2803
+ const next = props?.[seg];
2804
+ if (!next || typeof next !== "object") return void 0;
2805
+ cur = next;
2806
+ }
2807
+ return cur;
2808
+ }
2809
+ function templateStepRefs(text) {
2810
+ const ids = [];
2811
+ for (const m of text.matchAll(TEMPLATE_STEP_REF)) ids.push(m[1]);
2812
+ return ids;
2813
+ }
2814
+ function readMapConfig(raw) {
2815
+ if (!raw) return void 0;
2816
+ if (typeof raw !== "string") return raw;
2817
+ try {
2818
+ const cfg = JSON.parse(raw);
2819
+ return cfg && typeof cfg === "object" && !Array.isArray(cfg) ? cfg : void 0;
2820
+ } catch {
2821
+ return void 0;
2822
+ }
2823
+ }
2824
+ function mapConfigStepRefs(raw) {
2825
+ const cfg = readMapConfig(raw);
2826
+ if (!cfg) return [];
2827
+ const ids = [];
2828
+ for (const d of Object.values(cfg)) {
2829
+ if (!isMapDescriptor(d)) continue;
2830
+ if ("step" in d) ids.push(...Array.isArray(d.step) ? d.step : [
2831
+ d.step
2832
+ ]);
2833
+ if ("template" in d) ids.push(...templateStepRefs(d.template));
2834
+ }
2835
+ return ids;
2145
2836
  }
2146
2837
  function nodeStepRefs(entry) {
2147
2838
  switch (entry.type) {
@@ -2202,6 +2893,16 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
2202
2893
  if (envelopeWorkspace?.backend && envelopeWorkspace.backend !== "ebs" && opts.policy?.workspaceBackends && !opts.policy.workspaceBackends.includes(envelopeWorkspace.backend)) {
2203
2894
  err("workspace-backend-unavailable", `workspace.backend '${envelopeWorkspace.backend}' is not enabled (LUA_WF_WORKSPACE_BACKENDS = ${opts.policy.workspaceBackends.join(",")}) \u2014 'ebs' is the normative fallback`, "workspace.backend");
2204
2895
  }
2896
+ for (const member of [
2897
+ "repo",
2898
+ "ref"
2899
+ ]) {
2900
+ const v = envelopeWorkspace?.[member];
2901
+ const tpl = v && typeof v === "object" && typeof v.template === "string" ? v.template : void 0;
2902
+ if (tpl !== void 0 && workspaceTemplatePath(tpl) === void 0) {
2903
+ warn("workspace-template-composite", `workspace.${member} template ${JSON.stringify(tpl)} is a composite \u2014 the engine binds one whole-string \${initData.<path>} (or an input.<path> key) and every start would fail workspace_provision_failed{binding_unresolved}; put the assembled value in the run input and bind that path`, `workspace.${member}`);
2904
+ }
2905
+ }
2205
2906
  const declaredKeys = new Set(opts.connectionKeys ?? []);
2206
2907
  if (g.connections !== void 0 && !Array.isArray(g.connections)) {
2207
2908
  err("connection-declaration-invalid", "`connections` must be an array of { key, integrationType }", "connections");
@@ -2254,8 +2955,16 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
2254
2955
  const r = node.retry;
2255
2956
  if (!r) return;
2256
2957
  const id = singleId(node);
2257
- if (r.backoff !== void 0 && r.backoff !== "fixed" && r.backoff !== "exponential") {
2258
- err("backoff-invalid", `retry.backoff must be 'fixed' | 'exponential'`, `${path3}.retry.backoff`, id);
2958
+ const unknown = unknownWorkflowRetryMembers(r);
2959
+ if (unknown.length) err("invalid-envelope", workflowRetryUnknownMembersMessage(unknown), `${path3}.retry`, id);
2960
+ if (r.maxAttempts !== void 0 && !isWithinWorkflowRetryAttempts(r.maxAttempts)) {
2961
+ const over = typeof r.maxAttempts === "number" && r.maxAttempts > WORKFLOW_RETRY_MAX_ATTEMPTS;
2962
+ err(over ? "cap-exceeded" : "invalid-envelope", workflowRetryMaxAttemptsMessage(r.maxAttempts), `${path3}.retry.maxAttempts`, id);
2963
+ }
2964
+ const backoffs = retryBackoffs();
2965
+ if (r.backoff !== void 0 && !backoffs.includes(r.backoff)) {
2966
+ const list = backoffs.map((b) => `'${b}'`).join(" | ");
2967
+ err("backoff-invalid", `retry.backoff must be ${list}`, `${path3}.retry.backoff`, id);
2259
2968
  }
2260
2969
  if (r.backoffSeconds !== void 0 && r.backoffSeconds < 0) {
2261
2970
  err("backoff-invalid", "retry.backoffSeconds must be \u2265 0", `${path3}.retry.backoffSeconds`, id);
@@ -2394,6 +3103,21 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
2394
3103
  const schema = node.type === "step" ? node.step.outputSchema : node.type === "agent" ? node.outputSchema : void 0;
2395
3104
  if (schema !== void 0) outputSchemas.set(singleId(node), schema);
2396
3105
  }, "recordOutputSchema");
3106
+ const checkMapMembers = /* @__PURE__ */ __name3((cfg, basePath, id) => {
3107
+ for (const m of malformedMapMembers(cfg)) {
3108
+ warn(MAP_MEMBER_MALFORMED_CODE, mapMemberMalformedMessage(id, m), `${basePath}.${m.member}`, id);
3109
+ }
3110
+ }, "checkMapMembers");
3111
+ const checkInputShape = /* @__PURE__ */ __name3((node, path3) => {
3112
+ if (node.type !== "tool" && node.type !== "workflow") return;
3113
+ const input = node.input;
3114
+ if (input === void 0) return;
3115
+ if (input !== null && typeof input === "object" && !Array.isArray(input)) {
3116
+ checkMapMembers(input, `${path3}.input`, node.id);
3117
+ return;
3118
+ }
3119
+ 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`, node.id);
3120
+ }, "checkInputShape");
2397
3121
  const checkSingle = /* @__PURE__ */ __name3((node, path3, depth) => {
2398
3122
  recordOutputSchema(node);
2399
3123
  if (node.type === "workflow" && node.workflowId === WORKFLOW_ARM_SUBRUN_ID) {
@@ -2407,8 +3131,13 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
2407
3131
  err("container-arm-empty", "a bare mapping arm has nothing to run", `${path3}.graph.1`, node.id);
2408
3132
  return;
2409
3133
  }
3134
+ const inner = body[1];
3135
+ if (isHitlNode(inner)) {
3136
+ err("node-type-unsupported-in-container", workflowHitlArmShapeMessage(inner.type, inner.id, "mapped-arm"), `${path3}.graph.1`, inner.id);
3137
+ return;
3138
+ }
2410
3139
  upstream.add(singleId(body[1]));
2411
- checkArm(body[0], `${path3}.graph.0`, depth);
3140
+ checkArm(body[0], `${path3}.graph.0`, depth, "parallel");
2412
3141
  checkSingle(body[1], `${path3}.graph.1`, depth);
2413
3142
  upstream.add(body[0].id);
2414
3143
  upstream.add(singleId(body[1]));
@@ -2416,6 +3145,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
2416
3145
  }
2417
3146
  checkId(singleId(node), path3);
2418
3147
  checkPolicyEnums(node, path3);
3148
+ checkInputShape(node, path3);
2419
3149
  checkTimeout(node, path3);
2420
3150
  checkTier(node, path3);
2421
3151
  checkRetry(node, path3);
@@ -2440,20 +3170,72 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
2440
3170
  if (node.type === "workflow" && node.kind === "subrun" && depth > caps.maxNestingDepth) {
2441
3171
  err("cap-exceeded", `nesting depth ${depth} exceeds ${caps.maxNestingDepth}`, path3, node.id);
2442
3172
  }
3173
+ if (node.type === "workflow" && node.workflowId !== WORKFLOW_ARM_SUBRUN_ID && typeof g.definition?.id === "string" && node.workflowId === g.definition.id) {
3174
+ err("subrun-cycle", `"${node.id}" starts "${node.workflowId}", which is this workflow itself`, path3, node.id);
3175
+ }
2443
3176
  for (const ref of nodeStepRefs(node)) {
2444
3177
  if (!upstream.has(ref)) {
2445
3178
  err("template-reference-unresolved", `"${singleId(node)}" references stepResults.${ref}, which is not upstream`, path3, singleId(node));
2446
3179
  }
2447
3180
  }
2448
- }, "checkSingle");
2449
- const checkArm = /* @__PURE__ */ __name3((arm, path3, depth) => {
3181
+ }, "checkSingle");
3182
+ const checkHitl = /* @__PURE__ */ __name3((node, path3) => {
3183
+ if (node.type === "waitForSignal") {
3184
+ const w = node;
3185
+ checkId(w.id, path3);
3186
+ if (typeof w.signal !== "string" || w.signal.length === 0) err("invalid-envelope", "waitForSignal.signal is required", `${path3}.signal`, w.id);
3187
+ return;
3188
+ }
3189
+ const a = node;
3190
+ checkId(a.id, path3);
3191
+ if (a.approver === "creator" && a.excludeInitiator === true) {
3192
+ err("approver-excludes-only-candidate", "approver:'creator' with excludeInitiator:true always excludes the only candidate", path3, a.id);
3193
+ }
3194
+ if (a.fourEyes !== void 0 && a.editable !== true) {
3195
+ err("four-eyes-requires-editable", "`fourEyes` requires editable:true", `${path3}.fourEyes`, a.id);
3196
+ }
3197
+ if ((a.editablePaths !== void 0 || a.editedPayloadSchema !== void 0) && a.editable !== true) {
3198
+ err("editable-path-invalid", "`editablePaths` / `editedPayloadSchema` require editable:true", `${path3}.editablePaths`, a.id);
3199
+ }
3200
+ for (const p of a.editablePaths ?? []) {
3201
+ if (!EDITABLE_PATH_RE.test(p)) err("editable-path-invalid", `editablePaths entry "${p}" is outside the seg(.seg)*[*]/[n] grammar`, `${path3}.editablePaths`, a.id);
3202
+ }
3203
+ if (Array.isArray(a.onTimeout)) {
3204
+ const chain = a.onTimeout;
3205
+ const hops = chain.filter((h) => typeof h === "object" && h !== null && "escalateTo" in h);
3206
+ if (hops.length > 3) err("escalation-chain-too-long", "an onTimeout chain carries at most 3 escalation hops", `${path3}.onTimeout`, a.id);
3207
+ const last = chain[chain.length - 1];
3208
+ if (last === void 0 || typeof last === "object" && last !== null && "escalateTo" in last) {
3209
+ err("escalation-chain-not-terminal", "an onTimeout chain must end in a terminal member", `${path3}.onTimeout`, a.id);
3210
+ }
3211
+ }
3212
+ if (typeof a.details === "string") {
3213
+ for (const ref of templateStepRefs(a.details)) {
3214
+ if (!upstream.has(ref)) err("template-reference-unresolved", `"${a.id}" references stepResults.${ref}, which is not upstream`, path3, a.id);
3215
+ }
3216
+ }
3217
+ }, "checkHitl");
3218
+ const checkHitlArm = /* @__PURE__ */ __name3((node, path3, container) => {
3219
+ if (!workflowContainerRunsHitlArm(container)) {
3220
+ checkId(node.id, path3);
3221
+ err("node-type-unsupported-in-container", workflowHitlArmUnsupportedMessage(node.type, node.id, container), path3, node.id);
3222
+ return;
3223
+ }
3224
+ checkHitl(node, path3);
3225
+ }, "checkHitlArm");
3226
+ const checkArm = /* @__PURE__ */ __name3((arm, path3, depth, container) => {
2450
3227
  if (arm.type === "mapping") {
2451
3228
  checkId(arm.id, path3);
3229
+ checkMapMembers(readMapConfig(arm.mapConfig), `${path3}.mapConfig`, arm.id);
2452
3230
  for (const ref of nodeStepRefs(arm)) {
2453
3231
  if (!upstream.has(ref)) err("template-reference-unresolved", `"${arm.id}" references stepResults.${ref}, which is not upstream`, path3, arm.id);
2454
3232
  }
2455
3233
  return;
2456
3234
  }
3235
+ if (isHitlNode(arm)) {
3236
+ checkHitlArm(arm, path3, container);
3237
+ return;
3238
+ }
2457
3239
  checkSingle(arm, path3, depth);
2458
3240
  }, "checkArm");
2459
3241
  graph.forEach((entry, i) => {
@@ -2469,6 +3251,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
2469
3251
  break;
2470
3252
  case "mapping":
2471
3253
  checkId(entry.id, path3);
3254
+ checkMapMembers(readMapConfig(entry.mapConfig), `${path3}.mapConfig`, entry.id);
2472
3255
  for (const ref of nodeStepRefs(entry)) {
2473
3256
  if (!upstream.has(ref)) err("template-reference-unresolved", `"${entry.id}" references stepResults.${ref}, which is not upstream`, path3, entry.id);
2474
3257
  }
@@ -2503,18 +3286,15 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
2503
3286
  err("mapping-placement", "a bare mapping cannot be a parallel arm \u2014 chain it as [map, step]", armPath);
2504
3287
  return;
2505
3288
  }
2506
- if (arm.type === "approval" || arm.type === "waitForSignal") {
2507
- err("approval-inside-container", "approval / waitForSignal are top-level only in v1", armPath);
2508
- return;
2509
- }
2510
- checkSingle(arm, armPath, 1);
3289
+ checkArm(arm, armPath, 1, "parallel");
2511
3290
  declared.push(singleId(arm));
2512
3291
  });
2513
- const worktreeArms = p.steps.filter((a) => a.workspace?.isolation === "worktree");
3292
+ const executableArms = p.steps.filter(isSingleStep);
3293
+ const worktreeArms = executableArms.filter((a) => a.workspace?.isolation === "worktree");
2514
3294
  if (worktreeArms.length > 0 && !p.merge) err("worktree-arms-require-merge", "worktree arms need a `merge` policy", path3);
2515
3295
  if (worktreeArms.length === 0 && p.merge) err("merge-requires-worktree-arms", "`merge` needs at least one worktree arm", path3);
2516
3296
  if (worktreeArms.length > WORKFLOW_JOB_MAX_WORKTREE_ARMS) err("worktree-arms-exceed-cap", `at most ${WORKFLOW_JOB_MAX_WORKTREE_ARMS} worktree arms per parallel`, path3);
2517
- const sharedArms = p.steps.filter((a) => {
3297
+ const sharedArms = executableArms.filter((a) => {
2518
3298
  const w = workspaceOf(a);
2519
3299
  if (!w || w === "inherit" || w.isolation === "worktree") return false;
2520
3300
  return !(envelopeWorkspace?.backend === "efs" && w.mount === "ro");
@@ -2531,11 +3311,11 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
2531
3311
  if (!isWellFormedPredicate(p)) err("closure-predicate", "a conditional predicate must be a well-formed LuaPredicate object ({op, left/right | value | path | args | arg}), not a function or an expression string", `${path3}.predicates.${j}`);
2532
3312
  });
2533
3313
  c.steps.forEach((arm, j) => {
2534
- checkArm(arm, `${path3}.steps.${j}`, 1);
3314
+ checkArm(arm, `${path3}.steps.${j}`, 1, "conditional");
2535
3315
  declared.push(armId(arm));
2536
3316
  });
2537
3317
  if (c.otherwise) {
2538
- checkArm(c.otherwise, `${path3}.otherwise`, 1);
3318
+ checkArm(c.otherwise, `${path3}.otherwise`, 1, "conditional");
2539
3319
  declared.push(armId(c.otherwise));
2540
3320
  }
2541
3321
  break;
@@ -2558,8 +3338,9 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
2558
3338
  }
2559
3339
  if (o.chunk !== void 0) {
2560
3340
  const max = o.maxItems ?? WORKFLOW_FOREACH_DEFAULT_MAX_ITEMS;
2561
- if (!Number.isInteger(o.chunk.size) || o.chunk.size < 1 || o.chunk.size > max) {
2562
- err("chunk-size-invalid", `foreach.chunk.size must be an integer in [1, ${max}]`, `${path3}.opts.chunk.size`);
3341
+ const size = typeof o.chunk === "number" ? o.chunk : o.chunk.size;
3342
+ if (!Number.isInteger(size) || size < 1 || size > max) {
3343
+ err("chunk-size-invalid", `foreach.chunk.size must be an integer in [1, ${max}]`, typeof o.chunk === "number" ? `${path3}.opts.chunk` : `${path3}.opts.chunk.size`);
2563
3344
  }
2564
3345
  }
2565
3346
  if (o.rateLimit !== void 0) {
@@ -2576,12 +3357,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
2576
3357
  const bodyType = f.step.type;
2577
3358
  const prevEntry = i > 0 ? graph[i - 1] : void 0;
2578
3359
  if (bodyType !== "mapping" && prevEntry?.type === "mapping" && prevEntry.id === `${singleId(f.step)}_items`) {
2579
- let cfg;
2580
- try {
2581
- cfg = JSON.parse(prevEntry.mapConfig);
2582
- } catch {
2583
- cfg = void 0;
2584
- }
3360
+ const cfg = readMapConfig(prevEntry.mapConfig);
2585
3361
  const d = cfg?.[""];
2586
3362
  let arrayLike;
2587
3363
  if (d && "value" in d) arrayLike = Array.isArray(d.value);
@@ -2593,8 +3369,13 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
2593
3369
  if (arrayLike === false) err("foreach-items-not-array", `foreach({items}) for "${singleId(f.step)}" binds a value that is not an array`, `${path3}.step`, singleId(f.step));
2594
3370
  }
2595
3371
  if (bodyType === "mapping") err("container-arm-empty", "a foreach body needs a step, not a bare mapping", `${path3}.step`);
2596
- else if (bodyType === "approval" || bodyType === "waitForSignal") err("approval-inside-container", "approval / waitForSignal are top-level only in v1", `${path3}.step`);
2597
- else {
3372
+ else if (isHitlNode(f.step)) {
3373
+ if (o.chunk !== void 0) {
3374
+ checkId(f.step.id, `${path3}.step`);
3375
+ err("node-type-unsupported-in-container", workflowHitlArmShapeMessage(f.step.type, f.step.id, "chunked-foreach"), `${path3}.step`, f.step.id);
3376
+ } else checkHitlArm(f.step, `${path3}.step`, "foreach");
3377
+ declared.push(f.step.id);
3378
+ } else {
2598
3379
  checkSingle(f.step, `${path3}.step`, o.chunk ? 2 : 1);
2599
3380
  declared.push(singleId(f.step));
2600
3381
  }
@@ -2611,52 +3392,20 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
2611
3392
  if (!isWellFormedPredicate(l.predicate)) err("closure-predicate", "a loop predicate must be a well-formed LuaPredicate object ({op, left/right | value | path | args | arg}), not a function or an expression string", `${path3}.predicate`);
2612
3393
  const bodyType = l.step.type;
2613
3394
  if (bodyType === "mapping") err("container-arm-empty", "a loop body needs a step, not a bare mapping", `${path3}.step`);
2614
- else if (bodyType === "approval" || bodyType === "waitForSignal") err("approval-inside-container", "approval / waitForSignal are top-level only in v1", `${path3}.step`);
2615
- else {
3395
+ else if (isHitlNode(l.step)) {
3396
+ checkHitlArm(l.step, `${path3}.step`, "loop");
3397
+ declared.push(l.step.id);
3398
+ } else {
2616
3399
  checkSingle(l.step, `${path3}.step`, 1);
2617
3400
  declared.push(singleId(l.step));
2618
3401
  }
2619
3402
  break;
2620
3403
  }
2621
- case "approval": {
2622
- const a = entry;
2623
- checkId(a.id, path3);
2624
- if (a.approver === "creator" && a.excludeInitiator === true) {
2625
- err("approver-excludes-only-candidate", "approver:'creator' with excludeInitiator:true always excludes the only candidate", path3, a.id);
2626
- }
2627
- if (a.fourEyes !== void 0 && a.editable !== true) {
2628
- err("four-eyes-requires-editable", "`fourEyes` requires editable:true", `${path3}.fourEyes`, a.id);
2629
- }
2630
- if ((a.editablePaths !== void 0 || a.editedPayloadSchema !== void 0) && a.editable !== true) {
2631
- err("editable-path-invalid", "`editablePaths` / `editedPayloadSchema` require editable:true", `${path3}.editablePaths`, a.id);
2632
- }
2633
- for (const p of a.editablePaths ?? []) {
2634
- if (!EDITABLE_PATH_RE.test(p)) err("editable-path-invalid", `editablePaths entry "${p}" is outside the seg(.seg)*[*]/[n] grammar`, `${path3}.editablePaths`, a.id);
2635
- }
2636
- if (Array.isArray(a.onTimeout)) {
2637
- const chain = a.onTimeout;
2638
- const hops = chain.filter((h) => typeof h === "object" && h !== null && "escalateTo" in h);
2639
- if (hops.length > 3) err("escalation-chain-too-long", "an onTimeout chain carries at most 3 escalation hops", `${path3}.onTimeout`, a.id);
2640
- const last = chain[chain.length - 1];
2641
- if (last === void 0 || typeof last === "object" && last !== null && "escalateTo" in last) {
2642
- err("escalation-chain-not-terminal", "an onTimeout chain must end in a terminal member", `${path3}.onTimeout`, a.id);
2643
- }
2644
- }
2645
- if (typeof a.details === "string") {
2646
- for (const ref of templateStepRefs(a.details)) {
2647
- if (!upstream.has(ref)) err("template-reference-unresolved", `"${a.id}" references stepResults.${ref}, which is not upstream`, path3, a.id);
2648
- }
2649
- }
2650
- declared.push(a.id);
2651
- break;
2652
- }
2653
- case "waitForSignal": {
2654
- const w = entry;
2655
- checkId(w.id, path3);
2656
- if (typeof w.signal !== "string" || w.signal.length === 0) err("invalid-envelope", "waitForSignal.signal is required", `${path3}.signal`, w.id);
2657
- declared.push(w.id);
3404
+ case "approval":
3405
+ case "waitForSignal":
3406
+ checkHitl(entry, path3);
3407
+ declared.push(entry.id);
2658
3408
  break;
2659
- }
2660
3409
  default:
2661
3410
  err("invalid-envelope", `unknown entry type "${entry.type}"`, path3);
2662
3411
  }
@@ -2745,10 +3494,10 @@ function compilePlan(g) {
2745
3494
  let prevTails = [];
2746
3495
  const foreachJoinToEntry = /* @__PURE__ */ new Map();
2747
3496
  g.definition.graph.forEach((entry, entryIndex) => {
2748
- if (isSingleStep(entry)) {
2749
- const id = singleStepId(entry);
3497
+ if (isArmStep(entry)) {
3498
+ const id = armStepId(entry);
2750
3499
  addNode(id, {
2751
- kind: SINGLE_STEP_KINDS[entry.type],
3500
+ kind: armStepKind(entry),
2752
3501
  dependsOn: prevTails,
2753
3502
  downstream: [],
2754
3503
  unsatisfiedDeps: prevTails.length,
@@ -2773,9 +3522,8 @@ function compilePlan(g) {
2773
3522
  ];
2774
3523
  return;
2775
3524
  case "sleep":
2776
- case "sleepUntil":
2777
- case "approval": {
2778
- const kind = entry.type === "approval" ? "approval" : entry.type;
3525
+ case "sleepUntil": {
3526
+ const kind = entry.type;
2779
3527
  addNode(entry.id, {
2780
3528
  kind,
2781
3529
  dependsOn: prevTails,
@@ -2788,24 +3536,12 @@ function compilePlan(g) {
2788
3536
  ];
2789
3537
  return;
2790
3538
  }
2791
- case "waitForSignal":
2792
- addNode(entry.id, {
2793
- kind: "signal",
2794
- dependsOn: prevTails,
2795
- downstream: [],
2796
- unsatisfiedDeps: prevTails.length,
2797
- entry
2798
- });
2799
- prevTails = [
2800
- entry.id
2801
- ];
2802
- return;
2803
3539
  case "parallel": {
2804
3540
  const entryId = containerIdOf("parallel", entryIndex);
2805
3541
  const childIds = entry.steps.map((arm) => {
2806
- const childId = singleStepId(arm);
3542
+ const childId = armStepId(arm);
2807
3543
  addNode(childId, {
2808
- kind: SINGLE_STEP_KINDS[arm.type],
3544
+ kind: armStepKind(arm),
2809
3545
  dependsOn: prevTails,
2810
3546
  downstream: [],
2811
3547
  unsatisfiedDeps: prevTails.length,
@@ -2843,9 +3579,9 @@ function compilePlan(g) {
2843
3579
  node.otherwise
2844
3580
  ] : node.steps;
2845
3581
  const childIds = arms.map((arm) => {
2846
- const childId = arm.type === "mapping" ? arm.id : singleStepId(arm);
3582
+ const childId = arm.type === "mapping" ? arm.id : armStepId(arm);
2847
3583
  addNode(childId, {
2848
- kind: arm.type === "mapping" ? "map" : SINGLE_STEP_KINDS[arm.type],
3584
+ kind: arm.type === "mapping" ? "map" : armStepKind(arm),
2849
3585
  dependsOn: [
2850
3586
  entryId
2851
3587
  ],
@@ -3022,250 +3758,96 @@ function compare(op, left, right) {
3022
3758
  return left < right;
3023
3759
  case "lte":
3024
3760
  return left <= right;
3025
- case "gt":
3026
- return left > right;
3027
- case "gte":
3028
- return left >= right;
3029
- }
3030
- }
3031
- return false;
3032
- }
3033
- function derivePredicateLabel(pred, maxLength = 80) {
3034
- const raw = renderPredicate(pred);
3035
- if (raw.length <= maxLength) return raw;
3036
- return raw.slice(0, maxLength - 1) + "\u2026";
3037
- }
3038
- function renderPredicate(pred) {
3039
- switch (pred.op) {
3040
- case "and":
3041
- case "or":
3042
- return pred.args.map((arg) => wrapLabel(arg, renderPredicate(arg))).join(pred.op === "and" ? " AND " : " OR ");
3043
- case "not":
3044
- return `NOT ${wrapLabel(pred.arg, renderPredicate(pred.arg))}`;
3045
- case "exists":
3046
- return `${pred.path} exists`;
3047
- case "notExists":
3048
- return `${pred.path} missing`;
3049
- case "truthy":
3050
- return `${renderRef(pred.value)} is truthy`;
3051
- case "falsy":
3052
- return `${renderRef(pred.value)} is falsy`;
3053
- case "in":
3054
- return `${renderRef(pred.value)} in ${JSON.stringify(pred.set)}`;
3055
- case "notIn":
3056
- return `${renderRef(pred.value)} not in ${JSON.stringify(pred.set)}`;
3057
- case "eq":
3058
- return `${renderRef(pred.left)} == ${renderRef(pred.right)}`;
3059
- case "ne":
3060
- return `${renderRef(pred.left)} != ${renderRef(pred.right)}`;
3061
- case "lt":
3062
- return `${renderRef(pred.left)} < ${renderRef(pred.right)}`;
3063
- case "lte":
3064
- return `${renderRef(pred.left)} <= ${renderRef(pred.right)}`;
3065
- case "gt":
3066
- return `${renderRef(pred.left)} > ${renderRef(pred.right)}`;
3067
- case "gte":
3068
- return `${renderRef(pred.left)} >= ${renderRef(pred.right)}`;
3069
- }
3070
- }
3071
- function wrapLabel(child, rendered) {
3072
- return child.op === "and" || child.op === "or" || child.op === "not" ? `(${rendered})` : rendered;
3073
- }
3074
- function renderRef(ref) {
3075
- if ("literal" in ref) return JSON.stringify(ref.literal);
3076
- return ref.path;
3077
- }
3078
- function step(s) {
3079
- const id = stepIdOf(s);
3080
- return {
3081
- path: /* @__PURE__ */ __name3((p) => ({
3082
- path: p === "" ? `stepResults.${id}` : `stepResults.${id}.${p}`
3083
- }), "path")
3084
- };
3085
- }
3086
- function stepOf(id) {
3087
- return step(id);
3088
- }
3089
- function init(path3) {
3090
- return {
3091
- path: path3 === "" ? "initData" : `initData.${path3}`
3092
- };
3093
- }
3094
- function state(path3) {
3095
- return {
3096
- path: path3 === "" ? "state" : `state.${path3}`
3097
- };
3098
- }
3099
- function lit(v) {
3100
- return {
3101
- literal: v
3102
- };
3103
- }
3104
- function toPathOrLiteral(v) {
3105
- if (typeof v === "object" && v !== null) {
3106
- if ("path" in v) return {
3107
- path: v.path
3108
- };
3109
- if ("literal" in v) return {
3110
- literal: v.literal
3111
- };
3112
- }
3113
- return {
3114
- literal: v
3115
- };
3116
- }
3117
- function parseMapConfig(raw, stepId) {
3118
- try {
3119
- return JSON.parse(raw);
3120
- } catch (e) {
3121
- throw new Error(`Stored mapping step "${stepId}" has invalid JSON mapConfig: ${e.message}`);
3122
- }
3123
- }
3124
- function describeBadPlaceholder(template22, idx, rawExpr) {
3125
- return `Template placeholder #${idx} (\${${rawExpr}}) in '${template22}'`;
3126
- }
3127
- function parseTemplatePlaceholder(rawExpr) {
3128
- const dot = rawExpr.indexOf(".");
3129
- return {
3130
- scope: dot === -1 ? rawExpr : rawExpr.slice(0, dot),
3131
- rest: dot === -1 ? "" : rawExpr.slice(dot + 1)
3132
- };
3133
- }
3134
- function traverseMappingPath(root, path3, errorLabel) {
3135
- if (path3 === "" || path3 === ".") return root;
3136
- const parts = path3.split(".");
3137
- let value22 = root;
3138
- for (const part of parts) {
3139
- if (typeof value22 === "object" && value22 !== null) value22 = value22[part];
3140
- else throw new WorkflowTemplateError(`Invalid path ${path3} in ${errorLabel}`, path3);
3141
- }
3142
- return value22;
3143
- }
3144
- function stringifyTemplateValue(v, template22, idx, rawExpr) {
3145
- if (v === null || v === void 0) return "";
3146
- if (typeof v === "object") {
3147
- try {
3148
- return JSON.stringify(v);
3149
- } catch (err) {
3150
- throw new WorkflowTemplateError(`${describeBadPlaceholder(template22, idx, rawExpr)} resolved to a value that could not be JSON-stringified (${err.message}).`, rawExpr);
3151
- }
3152
- }
3153
- return String(v);
3154
- }
3155
- function escapeFence(content) {
3156
- return content.replace(/<\/lua-data/g, "<\\/lua-data");
3157
- }
3158
- function fenceBlock(name, source, content) {
3159
- return `<lua-data name="${name}" source="${source}" untrusted="true">${escapeFence(content)}</lua-data>`;
3160
- }
3161
- function renderTemplate(template22, ctx, opts) {
3162
- let idx = 0;
3163
- return template22.replace(TEMPLATE_PLACEHOLDER, (_match, rawExpr) => {
3164
- idx += 1;
3165
- const { scope, rest } = parseTemplatePlaceholder(rawExpr);
3166
- const label = describeBadPlaceholder(template22, idx, rawExpr);
3167
- let rendered;
3168
- let source;
3169
- switch (scope) {
3170
- case "initData":
3171
- rendered = stringifyTemplateValue(traverseMappingPath(ctx.initData, rest, label), template22, idx, rawExpr);
3172
- source = "initData";
3173
- break;
3174
- case "state":
3175
- rendered = stringifyTemplateValue(traverseMappingPath(ctx.state, rest, label), template22, idx, rawExpr);
3176
- source = "state";
3177
- break;
3178
- case "requestContext":
3179
- rendered = stringifyTemplateValue(traverseMappingPath(ctx.requestContext, rest, label), template22, idx, rawExpr);
3180
- source = "requestContext";
3181
- break;
3182
- case "stepResults": {
3183
- const innerDot = rest.indexOf(".");
3184
- const stepId = innerDot === -1 ? rest : rest.slice(0, innerDot);
3185
- const subPath = innerDot === -1 ? "" : rest.slice(innerDot + 1);
3186
- if (!stepId) throw new WorkflowTemplateError(`${label} must name a step: \${stepResults.<stepId>.<path>}.`, rawExpr);
3187
- if (!(stepId in ctx.stepResults) || ctx.stepResults[stepId] == null) {
3188
- 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);
3189
- }
3190
- rendered = stringifyTemplateValue(traverseMappingPath(ctx.stepResults[stepId], subPath, label), template22, idx, rawExpr);
3191
- source = `step:${stepId}`;
3192
- break;
3193
- }
3194
- default:
3195
- throw new WorkflowTemplateError(`${label} references unknown namespace "${scope}". Use one of: ${TEMPLATE_NAMESPACES.join(", ")}.`, rawExpr);
3196
- }
3197
- return opts.fenced ? fenceBlock(rawExpr, source, rendered) : rendered;
3198
- });
3199
- }
3200
- function resolveDescriptor(key, m, ctx) {
3201
- try {
3202
- if ("value" in m) return {
3203
- value: m.value
3204
- };
3205
- if ("template" in m && typeof m.template === "string") {
3206
- return {
3207
- value: renderTemplate(m.template, ctx, {
3208
- fenced: false
3209
- })
3210
- };
3211
- }
3212
- if ("knowledge" in m || "rows" in m && m.rows !== void 0) {
3213
- return {
3214
- error: "binding_unresolved",
3215
- key
3216
- };
3217
- }
3218
- if ("requestContextPath" in m) {
3219
- const label = `requestContext path for key "${key}"`;
3220
- return {
3221
- value: traverseMappingPath(ctx.requestContext, m.requestContextPath, label)
3222
- };
3223
- }
3224
- if ("path" in m) {
3225
- const source = "initData" in m && m.initData ? "initData" : "step";
3226
- if (source === "initData") {
3227
- return {
3228
- value: traverseMappingPath(ctx.initData, m.path, `initData for key "${key}"`)
3229
- };
3230
- }
3231
- const stepRef = m.step;
3232
- const candidates = Array.isArray(stepRef) ? stepRef : [
3233
- stepRef
3234
- ];
3235
- const stepId = candidates.find((s) => ctx.stepResults[s] !== void 0 && ctx.stepResults[s] !== null);
3236
- if (stepId === void 0) return {
3237
- error: "binding_unresolved",
3238
- key
3239
- };
3240
- return {
3241
- value: traverseMappingPath(ctx.stepResults[stepId], m.path, `step ${candidates.join("|")} for key "${key}"`)
3242
- };
3761
+ case "gt":
3762
+ return left > right;
3763
+ case "gte":
3764
+ return left >= right;
3243
3765
  }
3244
- return {
3245
- error: "binding_unresolved",
3246
- key
3247
- };
3248
- } catch (err) {
3249
- if (err instanceof WorkflowTemplateError) return {
3250
- error: "binding_unresolved",
3251
- key
3252
- };
3253
- throw err;
3254
3766
  }
3767
+ return false;
3255
3768
  }
3256
- function resolveMapping(cfg, ctx) {
3257
- const keys = Object.keys(cfg);
3258
- if (keys.length === 1 && keys[0] === "") {
3259
- return resolveDescriptor("", cfg[""], ctx);
3769
+ function derivePredicateLabel(pred, maxLength = 80) {
3770
+ const raw = renderPredicate(pred);
3771
+ if (raw.length <= maxLength) return raw;
3772
+ return raw.slice(0, maxLength - 1) + "\u2026";
3773
+ }
3774
+ function renderPredicate(pred) {
3775
+ switch (pred.op) {
3776
+ case "and":
3777
+ case "or":
3778
+ return pred.args.map((arg) => wrapLabel(arg, renderPredicate(arg))).join(pred.op === "and" ? " AND " : " OR ");
3779
+ case "not":
3780
+ return `NOT ${wrapLabel(pred.arg, renderPredicate(pred.arg))}`;
3781
+ case "exists":
3782
+ return `${pred.path} exists`;
3783
+ case "notExists":
3784
+ return `${pred.path} missing`;
3785
+ case "truthy":
3786
+ return `${renderRef(pred.value)} is truthy`;
3787
+ case "falsy":
3788
+ return `${renderRef(pred.value)} is falsy`;
3789
+ case "in":
3790
+ return `${renderRef(pred.value)} in ${JSON.stringify(pred.set)}`;
3791
+ case "notIn":
3792
+ return `${renderRef(pred.value)} not in ${JSON.stringify(pred.set)}`;
3793
+ case "eq":
3794
+ return `${renderRef(pred.left)} == ${renderRef(pred.right)}`;
3795
+ case "ne":
3796
+ return `${renderRef(pred.left)} != ${renderRef(pred.right)}`;
3797
+ case "lt":
3798
+ return `${renderRef(pred.left)} < ${renderRef(pred.right)}`;
3799
+ case "lte":
3800
+ return `${renderRef(pred.left)} <= ${renderRef(pred.right)}`;
3801
+ case "gt":
3802
+ return `${renderRef(pred.left)} > ${renderRef(pred.right)}`;
3803
+ case "gte":
3804
+ return `${renderRef(pred.left)} >= ${renderRef(pred.right)}`;
3260
3805
  }
3261
- const result = {};
3262
- for (const key of keys) {
3263
- const resolved = resolveDescriptor(key, cfg[key], ctx);
3264
- if ("error" in resolved) return resolved;
3265
- result[key] = resolved.value;
3806
+ }
3807
+ function wrapLabel(child, rendered) {
3808
+ return child.op === "and" || child.op === "or" || child.op === "not" ? `(${rendered})` : rendered;
3809
+ }
3810
+ function renderRef(ref) {
3811
+ if ("literal" in ref) return JSON.stringify(ref.literal);
3812
+ return ref.path;
3813
+ }
3814
+ function step(s) {
3815
+ const id = stepIdOf(s);
3816
+ return {
3817
+ path: /* @__PURE__ */ __name3((p) => ({
3818
+ path: p === "" ? `stepResults.${id}` : `stepResults.${id}.${p}`
3819
+ }), "path")
3820
+ };
3821
+ }
3822
+ function stepOf(id) {
3823
+ return step(id);
3824
+ }
3825
+ function init(path3) {
3826
+ return {
3827
+ path: path3 === "" ? "initData" : `initData.${path3}`
3828
+ };
3829
+ }
3830
+ function state(path3) {
3831
+ return {
3832
+ path: path3 === "" ? "state" : `state.${path3}`
3833
+ };
3834
+ }
3835
+ function lit(v) {
3836
+ return {
3837
+ literal: v
3838
+ };
3839
+ }
3840
+ function toPathOrLiteral(v) {
3841
+ if (typeof v === "object" && v !== null) {
3842
+ if ("path" in v) return {
3843
+ path: v.path
3844
+ };
3845
+ if ("literal" in v) return {
3846
+ literal: v.literal
3847
+ };
3266
3848
  }
3267
3849
  return {
3268
- value: result
3850
+ literal: v
3269
3851
  };
3270
3852
  }
3271
3853
  function continuedFailureValue(error, killReason) {
@@ -3367,7 +3949,28 @@ function resolvePlacements(calls) {
3367
3949
  break;
3368
3950
  }
3369
3951
  });
3370
- const resolve = /* @__PURE__ */ __name3((ref, i, allowMapping) => {
3952
+ const hitlPlacementIssue = /* @__PURE__ */ __name3((node, ref, i, container) => {
3953
+ if (!isHitlNode2(node)) return void 0;
3954
+ const id = node.id;
3955
+ if (ref.armMap) {
3956
+ return {
3957
+ code: "node-type-unsupported-in-container",
3958
+ message: workflowHitlArmShapeMessage(node.type, id, "mapped-arm"),
3959
+ callIndex: i,
3960
+ stepId: id
3961
+ };
3962
+ }
3963
+ if (container !== "place" && !workflowContainerRunsHitlArm(container)) {
3964
+ return {
3965
+ code: "node-type-unsupported-in-container",
3966
+ message: workflowHitlArmUnsupportedMessage(node.type, id, container),
3967
+ callIndex: i,
3968
+ stepId: id
3969
+ };
3970
+ }
3971
+ return void 0;
3972
+ }, "hitlPlacementIssue");
3973
+ const resolve3 = /* @__PURE__ */ __name3((ref, i, allowMapping, container) => {
3371
3974
  if ("node" in ref) {
3372
3975
  if (ref.node.type === "mapping" && !allowMapping) {
3373
3976
  issues.push({
@@ -3384,7 +3987,7 @@ function resolvePlacements(calls) {
3384
3987
  if (!d) {
3385
3988
  issues.push({
3386
3989
  code: "unknown-step-ref",
3387
- message: `"${ref.ref}" is not declared anywhere in the chain \u2014 declare it with agentStep/specialistStep/toolStep/map(\u2026, { id })/workflow(\u2026)`,
3990
+ message: `"${ref.ref}" is not declared anywhere in the chain \u2014 declare it with agentStep/specialistStep/toolStep/map(\u2026, { id })/workflow(\u2026)/approval(\u2026)/waitForSignal(\u2026)`,
3388
3991
  callIndex: i,
3389
3992
  stepId: ref.ref
3390
3993
  });
@@ -3399,6 +4002,11 @@ function resolvePlacements(calls) {
3399
4002
  });
3400
4003
  return void 0;
3401
4004
  }
4005
+ const hitl = hitlPlacementIssue(d.node, ref, i, container);
4006
+ if (hitl) {
4007
+ issues.push(hitl);
4008
+ return void 0;
4009
+ }
3402
4010
  const prior = placedBy.get(ref.ref);
3403
4011
  if (prior !== void 0 && prior !== i) {
3404
4012
  issues.push({
@@ -3412,23 +4020,31 @@ function resolvePlacements(calls) {
3412
4020
  placedBy.set(ref.ref, i);
3413
4021
  return d.node;
3414
4022
  }, "resolve");
4023
+ const claim = /* @__PURE__ */ __name3((ref, i, allowMapping, container) => {
4024
+ if ("ref" in ref) {
4025
+ resolve3(ref, i, allowMapping, container);
4026
+ return;
4027
+ }
4028
+ const hitl = hitlPlacementIssue(ref.node, ref, i, container);
4029
+ if (hitl) issues.push(hitl);
4030
+ }, "claim");
3415
4031
  calls.forEach((call, i) => {
3416
4032
  switch (call.kind) {
3417
4033
  case "place":
3418
- resolve({
4034
+ claim({
3419
4035
  ref: call.ref
3420
- }, i, false);
4036
+ }, i, false, "place");
3421
4037
  break;
3422
4038
  case "parallel":
3423
- for (const arm of call.arms) if ("ref" in arm) resolve(arm, i, false);
4039
+ for (const arm of call.arms) claim(arm, i, false, "parallel");
3424
4040
  break;
3425
4041
  case "conditional":
3426
- for (const a of call.arms) if ("ref" in a.target) resolve(a.target, i, true);
3427
- if (call.otherwise && "ref" in call.otherwise) resolve(call.otherwise, i, true);
4042
+ for (const a of call.arms) claim(a.target, i, true, "conditional");
4043
+ if (call.otherwise) claim(call.otherwise, i, true, "conditional");
3428
4044
  break;
3429
4045
  case "foreach":
3430
4046
  case "loop":
3431
- if ("ref" in call.body) resolve(call.body, i, false);
4047
+ claim(call.body, i, false, call.kind);
3432
4048
  break;
3433
4049
  default:
3434
4050
  break;
@@ -3437,7 +4053,7 @@ function resolvePlacements(calls) {
3437
4053
  const graph = [];
3438
4054
  const lookup = /* @__PURE__ */ __name3((ref) => {
3439
4055
  const n2 = "node" in ref ? ref.node : declared.get(ref.ref)?.node;
3440
- if (!n2 || !ref.armMap || n2.type === "mapping") return n2;
4056
+ if (!n2 || !ref.armMap || n2.type === "mapping" || isHitlNode2(n2)) return n2;
3441
4057
  return lowerContainerArm(ref.armMap, n2);
3442
4058
  }, "lookup");
3443
4059
  calls.forEach((call, i) => {
@@ -3517,6 +4133,57 @@ function resolvePlacements(calls) {
3517
4133
  issues
3518
4134
  };
3519
4135
  }
4136
+ function isConditionalJoinId(stepId) {
4137
+ return CONDITIONAL_JOIN_ID.test(stepId);
4138
+ }
4139
+ function isPlainObject(v) {
4140
+ return typeof v === "object" && v !== null && !Array.isArray(v);
4141
+ }
4142
+ function leafValue(row) {
4143
+ if (row.status === "completed") return row.output === void 0 ? null : row.output;
4144
+ return continuedFailureValue(row.error, row.killReason);
4145
+ }
4146
+ function runOutputLeaves(steps) {
4147
+ const dependedOn = /* @__PURE__ */ new Set();
4148
+ for (const s of steps) {
4149
+ if (s.stepId === GOAL_JUDGE_STEP_ID) continue;
4150
+ for (const d of s.dependsOn ?? []) dependedOn.add(d);
4151
+ }
4152
+ return steps.filter((s) => s.stepId !== GOAL_JUDGE_STEP_ID && !NON_LEAF_KINDS.has(s.kind ?? "") && s.foreachIndex === void 0 && s.loopParentId === void 0 && s.status !== "skipped" && !dependedOn.has(s.stepId));
4153
+ }
4154
+ function deriveRunOutput(steps) {
4155
+ const leaves = runOutputLeaves(steps);
4156
+ if (leaves.length === 0) return void 0;
4157
+ if (leaves.length === 1) {
4158
+ const leaf = leaves[0];
4159
+ const value22 = leafValue(leaf);
4160
+ if (isConditionalJoinId(leaf.stepId) && isPlainObject(value22)) {
4161
+ const keys = Object.keys(value22);
4162
+ if (keys.length === 1) return {
4163
+ output: value22[keys[0]],
4164
+ leafIds: [
4165
+ keys[0]
4166
+ ]
4167
+ };
4168
+ }
4169
+ return {
4170
+ output: value22,
4171
+ leafIds: [
4172
+ leaf.stepId
4173
+ ]
4174
+ };
4175
+ }
4176
+ const output = {};
4177
+ for (const leaf of leaves) output[leaf.stepId] = leafValue(leaf);
4178
+ return {
4179
+ output,
4180
+ leafIds: leaves.map((l) => l.stepId)
4181
+ };
4182
+ }
4183
+ function subrunSettledOutput(child) {
4184
+ if (child.output !== void 0) return child.output;
4185
+ return deriveRunOutput(child.steps ?? [])?.output ?? null;
4186
+ }
3520
4187
  function seedLedgerFromRun(run, steps, targetPlan, opts = {}) {
3521
4188
  const byId = /* @__PURE__ */ new Map();
3522
4189
  for (const s of steps) {
@@ -3539,15 +4206,15 @@ function seedLedgerFromRun(run, steps, targetPlan, opts = {}) {
3539
4206
  parent
3540
4207
  ] : [];
3541
4208
  }, "dependsOf");
3542
- const walk2 = [
4209
+ const walk22 = [
3543
4210
  ...targetPlan.order
3544
4211
  ];
3545
4212
  for (const s of steps) {
3546
- if (!known.has(s.stepId) && parentOf(s.stepId) && known.has(parentOf(s.stepId)) && !walk2.includes(s.stepId)) {
3547
- walk2.push(s.stepId);
4213
+ if (!known.has(s.stepId) && parentOf(s.stepId) && known.has(parentOf(s.stepId)) && !walk22.includes(s.stepId)) {
4214
+ walk22.push(s.stepId);
3548
4215
  }
3549
4216
  }
3550
- for (const id of walk2) {
4217
+ for (const id of walk22) {
3551
4218
  const row = byId.get(id);
3552
4219
  if (!row || row.status !== "completed") continue;
3553
4220
  if (!dependsOf(id).every((d) => seededIds.has(d))) {
@@ -3667,6 +4334,15 @@ function replayLedger(g, ledger) {
3667
4334
  local,
3668
4335
  diverged: recordedCount !== local
3669
4336
  });
4337
+ } else if (node.kind === "subrun" && recorded.status === "completed" && recorded.child) {
4338
+ const local = subrunSettledOutput(recorded.child);
4339
+ verdicts.push({
4340
+ stepId: id,
4341
+ kind: "subrun",
4342
+ recorded: recorded.output,
4343
+ local,
4344
+ diverged: canonical(recorded.output) !== canonical(local)
4345
+ });
3670
4346
  }
3671
4347
  }
3672
4348
  return {
@@ -3708,7 +4384,7 @@ function ancestorResults(plan, id, rows22) {
3708
4384
  }
3709
4385
  return hit;
3710
4386
  }, "take");
3711
- const walk2 = /* @__PURE__ */ __name3((ids) => {
4387
+ const walk22 = /* @__PURE__ */ __name3((ids) => {
3712
4388
  for (const dep of ids) {
3713
4389
  if (seen.has(dep)) continue;
3714
4390
  seen.add(dep);
@@ -3733,10 +4409,10 @@ function ancestorResults(plan, id, rows22) {
3733
4409
  }
3734
4410
  }
3735
4411
  }
3736
- walk2(node.dependsOn);
4412
+ walk22(node.dependsOn);
3737
4413
  }
3738
4414
  }, "walk");
3739
- walk2(plan.steps[id]?.dependsOn ?? []);
4415
+ walk22(plan.steps[id]?.dependsOn ?? []);
3740
4416
  return out;
3741
4417
  }
3742
4418
  function inferTaken(entry, rows22) {
@@ -3808,21 +4484,86 @@ function runCounts(counts) {
3808
4484
  pending: n(c.pending) + n(c.ready) + n(c.waiting)
3809
4485
  };
3810
4486
  }
3811
- function runUsage(run) {
4487
+ function isPricedStepReceipt(receipt) {
4488
+ return typeof receipt?.multiplier === "number" && Number.isFinite(receipt.multiplier);
4489
+ }
4490
+ function receiptEngine(engine) {
4491
+ if (engine === "actions") return "seat";
4492
+ if (engine === "credits") return "legacy";
4493
+ return void 0;
4494
+ }
4495
+ function receiptTier(tier) {
4496
+ return tier === "light" || tier === "standard" || tier === "heavy" ? tier : void 0;
4497
+ }
4498
+ function stepBillingView(receipt) {
4499
+ const engine = receiptEngine(receipt?.engine);
4500
+ if (!receipt || engine === void 0) return void 0;
4501
+ return pruneUndefined({
4502
+ engine,
4503
+ attempt: typeof receipt.attempt === "number" ? receipt.attempt : void 0,
4504
+ credits: typeof receipt.credits === "number" ? receipt.credits : void 0,
4505
+ actions: typeof receipt.actionsEstimate === "number" ? receipt.actionsEstimate : void 0,
4506
+ model: typeof receipt.model === "string" ? receipt.model : void 0,
4507
+ tier: receiptTier(receipt.tier),
4508
+ multiplier: typeof receipt.multiplier === "number" ? receipt.multiplier : void 0,
4509
+ byok: typeof receipt.byok === "boolean" ? receipt.byok : void 0,
4510
+ calibrated: typeof receipt.calibrated === "boolean" ? receipt.calibrated : void 0
4511
+ });
4512
+ }
4513
+ function runUsage(run, receipts) {
4514
+ const actions = n(run.budget?.spent?.actionsEstimate);
4515
+ const stamped = run.budget?.engine;
4516
+ const priced = (receipts ?? []).filter(isPricedStepReceipt);
4517
+ 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;
4518
+ const seat = engine === "seat";
4519
+ const metering = engine ? "priced" : "flat";
3812
4520
  return {
3813
4521
  creditsUsed: run.budget?.spent?.credits ?? 0,
3814
4522
  actionsEstimate: run.budget?.spent?.actionsEstimate ?? 0,
4523
+ ...seat ? {
4524
+ actionsUsed: actions
4525
+ } : {},
4526
+ metering,
4527
+ ...engine ? {
4528
+ engine
4529
+ } : {},
3815
4530
  steps: run.budget?.spent?.steps ?? 0,
3816
4531
  inputTokens: run.usage?.inputTokens ?? 0,
3817
4532
  outputTokens: run.usage?.outputTokens ?? 0
3818
4533
  };
3819
4534
  }
4535
+ function runBudgetCap(budget) {
4536
+ const cap = budget?.maxCredits;
4537
+ return typeof cap === "number" && Number.isFinite(cap) && cap > 0 ? cap : void 0;
4538
+ }
4539
+ function runBudgetRemaining(budget) {
4540
+ const cap = runBudgetCap(budget);
4541
+ if (cap === void 0) return void 0;
4542
+ const spent = budget?.spent;
4543
+ return Math.max(0, cap - n(spent?.credits) - n(spent?.actionsEstimate) - n(budget?.reserved));
4544
+ }
3820
4545
  function runCancelView(cancel) {
3821
4546
  if (!cancel) return void 0;
3822
4547
  return {
3823
4548
  requestedAt: cancel.requestedAt,
3824
4549
  requestedBy: cancel.requestedBy ?? "",
3825
- forceAvailableAt: cancel.forceAfter ?? cancel.requestedAt + FORCE_CANCEL_STALE_MS
4550
+ forceAvailableAt: cancel.forceAfter ?? cancel.requestedAt + FORCE_CANCEL_STALE_MS,
4551
+ // LUA-686: the wall's own request (LUA-683 stamps `wall:true` from a system actor only) rides the wire, so a
4552
+ // client can tell a wall-ended `timed_out` run's audit block from a cancel it should chip. Absent, never false.
4553
+ ...cancel.wall === true ? {
4554
+ wall: true
4555
+ } : {},
4556
+ // LUA-704: a forced terminal's audit — who forced it, when, and a human's own reason text (the run's `reason`
4557
+ // is the terminal's). Absent on an unforced terminal, never null.
4558
+ ...typeof cancel.forcedAt === "number" ? {
4559
+ forcedAt: cancel.forcedAt
4560
+ } : {},
4561
+ ...typeof cancel.forcedBy === "string" && cancel.forcedBy ? {
4562
+ forcedBy: cancel.forcedBy
4563
+ } : {},
4564
+ ...typeof cancel.forceReason === "string" && cancel.forceReason ? {
4565
+ forceReason: cancel.forceReason
4566
+ } : {}
3826
4567
  };
3827
4568
  }
3828
4569
  function runWorkspaceView(ws) {
@@ -3841,7 +4582,6 @@ function runWorkspaceView(ws) {
3841
4582
  function toWorkflowRunSummary(run) {
3842
4583
  const status = run.status;
3843
4584
  const principal = run.principal?.principal;
3844
- const gated = status === "gated" || status === "suspended";
3845
4585
  return pruneUndefined({
3846
4586
  runId: run.id,
3847
4587
  workflowId: run.workflowId,
@@ -3853,7 +4593,14 @@ function toWorkflowRunSummary(run) {
3853
4593
  orgId: run.orgId,
3854
4594
  spaceAgentId: run.spaceAgentId,
3855
4595
  status,
3856
- gate: gated ? run.gate : void 0,
4596
+ // LUA-702 (pass-3B D6): the gate rides the wire whenever a NON-terminal doc carries one. In the LUA-681 window
4597
+ // (an approval resolved beside an open exception park) the run is `running` AND still holds `gate{exception}`
4598
+ // until the tick tail parks it back; projecting it only on gated|suspended hid that park from R4 / the CLI /
4599
+ // the desktop (prod 2026-09-05: `status: running, gate: null`). A terminal doc carries no gate by contract
4600
+ // (`terminalizeRun` drops `gate` / `suspendedFor` in every terminal write), but a row terminalized before that
4601
+ // held — pre-LUA-677/694 docs, a script-tier backstop that passed no `unset` — may still carry a stale one:
4602
+ // hidden here, so a `failed` run never reads "Needs a decision" on R3/R4 or the CLI (#2476 review).
4603
+ gate: run.gate && !isTerminalRunStatus(status) ? run.gate : void 0,
3857
4604
  batchId: run.batchId,
3858
4605
  goalId: run.goalId,
3859
4606
  foreachOverflow: run.foreachOverflow,
@@ -3876,9 +4623,11 @@ function toWorkflowRunSummary(run) {
3876
4623
  principalKind: run.principalKind ?? "user",
3877
4624
  cancel: runCancelView(run.cancel),
3878
4625
  usage: runUsage(run),
4626
+ // LUA-697: a row persisted before the write seams (#2406 / #2465 / the script tier) leaves scrubbed here too —
4627
+ // idempotent on a scrubbed message, bounded input; an empty message falls back to the code.
3879
4628
  error: run.error ? {
3880
4629
  code: run.error.code ?? "error",
3881
- message: run.error.message ?? "",
4630
+ message: scrubStepErrorMessage(run.error.message) ?? run.error.code ?? "error",
3882
4631
  stepId: run.error.stepId
3883
4632
  } : void 0,
3884
4633
  kind: "run",
@@ -3897,6 +4646,49 @@ function toWorkflowRunSummary(run) {
3897
4646
  hasOutput: run.hasOutput === true || run.output !== void 0 || run.outputRef !== void 0 ? true : void 0
3898
4647
  });
3899
4648
  }
4649
+ function scrubDetailValue(value22, depth) {
4650
+ if (typeof value22 === "string") return scrubSecretText(value22);
4651
+ if (typeof value22 === "number" || typeof value22 === "boolean" || value22 === null) return value22;
4652
+ if (depth >= DETAIL_MAX_DEPTH) return void 0;
4653
+ if (Array.isArray(value22)) {
4654
+ return value22.slice(0, DETAIL_MAX_ITEMS).map((v) => scrubDetailValue(v, depth + 1)).filter((v) => v !== void 0);
4655
+ }
4656
+ if (typeof value22 === "object") {
4657
+ const out = {};
4658
+ for (const [k, v] of Object.entries(value22)) {
4659
+ const s = scrubDetailValue(v, depth + 1);
4660
+ if (s !== void 0) out[k] = s;
4661
+ }
4662
+ return out;
4663
+ }
4664
+ return void 0;
4665
+ }
4666
+ function stepErrorDetail(error) {
4667
+ if (!error || typeof error !== "object") return void 0;
4668
+ const d = error.detail;
4669
+ if (!d || typeof d !== "object" || Array.isArray(d)) return void 0;
4670
+ const bag = d;
4671
+ const out = {};
4672
+ for (const k of STEP_ERROR_DETAIL_KEYS) {
4673
+ if (!(k in bag)) continue;
4674
+ const v = scrubDetailValue(bag[k], 1);
4675
+ if (v !== void 0) out[k] = v;
4676
+ }
4677
+ if (!Object.keys(out).length) return void 0;
4678
+ let bytes;
4679
+ try {
4680
+ bytes = new TextEncoder().encode(JSON.stringify(out)).length;
4681
+ } catch {
4682
+ return void 0;
4683
+ }
4684
+ if (bytes <= STEP_ERROR_DETAIL_MAX_BYTES) return out;
4685
+ const scalars = Object.fromEntries(Object.entries(out).filter(([, v]) => v === null || typeof v !== "object"));
4686
+ return {
4687
+ ...scalars,
4688
+ __truncated: true,
4689
+ bytes
4690
+ };
4691
+ }
3900
4692
  function timeZoneSupported(tz) {
3901
4693
  if (typeof tz !== "string" || !tz) return false;
3902
4694
  const intl = Intl;
@@ -4360,6 +5152,22 @@ function rebaseItemPointer(pointer, itemsPath, index) {
4360
5152
  const base = `/${itemsPath.split(".").map(escapePointer).join("/")}/${index}`;
4361
5153
  return pointer === "/" || pointer === "" ? base : `${base}${pointer}`;
4362
5154
  }
5155
+ function describeApproverSpecRefusal(spec) {
5156
+ const raw = spec === void 0 ? "undefined" : JSON.stringify(spec) ?? String(spec);
5157
+ const written = raw.length > APPROVER_WRITTEN_MAX ? `${raw.slice(0, APPROVER_WRITTEN_MAX - 1)}\u2026` : raw;
5158
+ const users = typeof spec === "object" && spec !== null ? spec.users : void 0;
5159
+ const approver = typeof users === "string" && USER_ID_SHAPED_RE.test(users) ? {
5160
+ users: [
5161
+ users
5162
+ ]
5163
+ } : "creator";
5164
+ 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)}`);
5165
+ return {
5166
+ approver,
5167
+ written,
5168
+ message
5169
+ };
5170
+ }
4363
5171
  function bindingRootsOk(template22) {
4364
5172
  const refs = [
4365
5173
  ...template22.matchAll(/\$\{\s*([A-Za-z_$][\w$]*)/g)
@@ -4384,7 +5192,19 @@ function validateApproverBlock(node, opts = {
4384
5192
  if (!r.success) {
4385
5193
  const users = spec?.users;
4386
5194
  if (Array.isArray(users) && users.length > APPROVER_SPEC_MAX_USERS) push("cap-exceeded", path3, `at most ${APPROVER_SPEC_MAX_USERS} users`);
4387
- else push("approver-invalid", path3, r.error.issues[0]?.message ?? "invalid approver");
5195
+ else {
5196
+ const refusal = describeApproverSpecRefusal(spec);
5197
+ issues.push({
5198
+ code: "approver-invalid",
5199
+ path: path3,
5200
+ severity: "error",
5201
+ message: refusal.message,
5202
+ repair: {
5203
+ approver: refusal.approver,
5204
+ written: refusal.written
5205
+ }
5206
+ });
5207
+ }
4388
5208
  return;
4389
5209
  }
4390
5210
  const s = r.data;
@@ -4452,7 +5272,7 @@ function liftRenderedApprover(row, rendered) {
4452
5272
  }
4453
5273
  function collectEnvTemplateKeys(value22) {
4454
5274
  const keys = /* @__PURE__ */ new Set();
4455
- const walk2 = /* @__PURE__ */ __name3((v) => {
5275
+ const walk22 = /* @__PURE__ */ __name3((v) => {
4456
5276
  if (isEnvRef(v)) {
4457
5277
  keys.add(v.__envRef);
4458
5278
  return;
@@ -4460,26 +5280,26 @@ function collectEnvTemplateKeys(value22) {
4460
5280
  if (typeof v === "string") {
4461
5281
  if (looksLikeEmbeddedJson(v)) {
4462
5282
  try {
4463
- walk2(JSON.parse(v));
5283
+ walk22(JSON.parse(v));
4464
5284
  } catch {
4465
5285
  }
4466
5286
  }
4467
5287
  return;
4468
5288
  }
4469
5289
  if (Array.isArray(v)) {
4470
- for (const e of v) walk2(e);
5290
+ for (const e of v) walk22(e);
4471
5291
  return;
4472
5292
  }
4473
- if (v && typeof v === "object") for (const e of Object.values(v)) walk2(e);
5293
+ if (v && typeof v === "object") for (const e of Object.values(v)) walk22(e);
4474
5294
  }, "walk");
4475
- walk2(value22);
5295
+ walk22(value22);
4476
5296
  return [
4477
5297
  ...keys
4478
5298
  ].sort();
4479
5299
  }
4480
5300
  function substituteEnvRefs(value22, overlay) {
4481
5301
  const missing = /* @__PURE__ */ new Set();
4482
- const walk2 = /* @__PURE__ */ __name3((v, slot = false) => {
5302
+ const walk22 = /* @__PURE__ */ __name3((v, slot = false) => {
4483
5303
  if (isEnvRef(v)) {
4484
5304
  if (Object.prototype.hasOwnProperty.call(overlay, v.__envRef)) {
4485
5305
  const s = overlay[v.__envRef];
@@ -4496,22 +5316,22 @@ function substituteEnvRefs(value22, overlay) {
4496
5316
  const cfg = JSON.parse(v);
4497
5317
  if (!cfg || typeof cfg !== "object" || Array.isArray(cfg)) return v;
4498
5318
  const out = {};
4499
- for (const [k, e] of Object.entries(cfg)) out[k] = walk2(e, true);
5319
+ for (const [k, e] of Object.entries(cfg)) out[k] = walk22(e, true);
4500
5320
  return canonicalJson(out);
4501
5321
  } catch {
4502
5322
  return v;
4503
5323
  }
4504
5324
  }
4505
- if (Array.isArray(v)) return v.map((e) => walk2(e));
5325
+ if (Array.isArray(v)) return v.map((e) => walk22(e));
4506
5326
  if (v && typeof v === "object") {
4507
5327
  const out = {};
4508
- for (const [k, e] of Object.entries(v)) out[k] = walk2(e);
5328
+ for (const [k, e] of Object.entries(v)) out[k] = walk22(e);
4509
5329
  return out;
4510
5330
  }
4511
5331
  return v;
4512
5332
  }, "walk");
4513
5333
  return {
4514
- value: walk2(value22),
5334
+ value: walk22(value22),
4515
5335
  missing: [
4516
5336
  ...missing
4517
5337
  ].sort()
@@ -4740,26 +5560,115 @@ function needsInheritedWorkspace(graph) {
4740
5560
  }
4741
5561
  return false;
4742
5562
  }
4743
- var __defProp3, __name3, WORKFLOW_SIDE_EFFECTS, WORKFLOW_JOB_RESOURCES, SideEffectsSchema, JobResourcesSchema, WORKFLOW_ARM_SUBRUN_ID, 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, singleId, armId, TEMPLATE_STEP_REF, EDITABLE_PATH_RE, PREDICATE_OPS, isPredicateScalar, GRAPH_HASH_PREFIX, WorkflowPlanError, SINGLE_STEP_KINDS, isSingleStep, singleStepId, 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, nodeIdOf, branchArmId, canonical, sortKeys, JOIN, entryOfJoin, FORCE_CANCEL_STALE_MS, TERMINAL, IN_FLIGHT, n, 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, BINDING_ROOTS, WORKFLOW_ENV_OVERLAY_MAX_KEYS, WORKFLOW_ENV_OVERLAY_MAX_VALUE_BYTES, WORKFLOW_ENV_TEMPLATE_SECRET_KEY_RE, isEnvRef, looksLikeEmbeddedJson, ZERO, isRecord2;
5563
+ var __defProp3, __name3, WorkflowTemplateError, TEMPLATE_PLACEHOLDER, TEMPLATE_NAMESPACES, MAP_DESCRIPTOR_KEYS, MAP_MEMBER_MALFORMED_CODE, fromInit, fromStep, value, template, fromRequest, rows, fromKnowledge, 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, 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;
4744
5564
  var init_dist2 = __esm({
4745
5565
  "../workflow-graph/dist/index.mjs"() {
4746
5566
  "use strict";
4747
5567
  init_dist();
4748
5568
  init_dist();
5569
+ init_dist();
5570
+ init_dist();
5571
+ init_dist();
5572
+ init_dist();
5573
+ init_dist();
4749
5574
  __defProp3 = Object.defineProperty;
4750
5575
  __name3 = /* @__PURE__ */ __name((target, value22) => __defProp3(target, "name", { value: value22, configurable: true }), "__name");
4751
- WORKFLOW_SIDE_EFFECTS = [
4752
- "none",
4753
- "external"
5576
+ WorkflowTemplateError = class extends Error {
5577
+ static {
5578
+ __name(this, "WorkflowTemplateError");
5579
+ }
5580
+ static {
5581
+ __name3(this, "WorkflowTemplateError");
5582
+ }
5583
+ placeholder;
5584
+ constructor(message, placeholder) {
5585
+ super(message), this.placeholder = placeholder;
5586
+ this.name = "WorkflowTemplateError";
5587
+ }
5588
+ };
5589
+ __name(isMapConfigObject, "isMapConfigObject");
5590
+ __name3(isMapConfigObject, "isMapConfigObject");
5591
+ __name(parseMapConfig, "parseMapConfig");
5592
+ __name3(parseMapConfig, "parseMapConfig");
5593
+ __name(mapConfigWire, "mapConfigWire");
5594
+ __name3(mapConfigWire, "mapConfigWire");
5595
+ TEMPLATE_PLACEHOLDER = /\$\{([^}]*)\}/g;
5596
+ TEMPLATE_NAMESPACES = [
5597
+ "initData",
5598
+ "state",
5599
+ "requestContext",
5600
+ "stepResults"
4754
5601
  ];
4755
- WORKFLOW_JOB_RESOURCES = [
4756
- "small",
4757
- "medium",
4758
- "large"
5602
+ __name(describeBadPlaceholder, "describeBadPlaceholder");
5603
+ __name3(describeBadPlaceholder, "describeBadPlaceholder");
5604
+ __name(parseTemplatePlaceholder, "parseTemplatePlaceholder");
5605
+ __name3(parseTemplatePlaceholder, "parseTemplatePlaceholder");
5606
+ __name(traverseMappingPath, "traverseMappingPath");
5607
+ __name3(traverseMappingPath, "traverseMappingPath");
5608
+ __name(stringifyTemplateValue, "stringifyTemplateValue");
5609
+ __name3(stringifyTemplateValue, "stringifyTemplateValue");
5610
+ __name(escapeFence, "escapeFence");
5611
+ __name3(escapeFence, "escapeFence");
5612
+ __name(fenceBlock, "fenceBlock");
5613
+ __name3(fenceBlock, "fenceBlock");
5614
+ __name(renderTemplate, "renderTemplate");
5615
+ __name3(renderTemplate, "renderTemplate");
5616
+ __name(isMapDescriptor, "isMapDescriptor");
5617
+ __name3(isMapDescriptor, "isMapDescriptor");
5618
+ MAP_DESCRIPTOR_KEYS = [
5619
+ "step",
5620
+ "path",
5621
+ "initData",
5622
+ "value",
5623
+ "template",
5624
+ "requestContextPath",
5625
+ "knowledge"
4759
5626
  ];
5627
+ MAP_MEMBER_MALFORMED_CODE = "map-member-malformed";
5628
+ __name(malformedMapMembers, "malformedMapMembers");
5629
+ __name3(malformedMapMembers, "malformedMapMembers");
5630
+ __name(mapMemberMalformedMessage, "mapMemberMalformedMessage");
5631
+ __name3(mapMemberMalformedMessage, "mapMemberMalformedMessage");
5632
+ __name(resolveDescriptor, "resolveDescriptor");
5633
+ __name3(resolveDescriptor, "resolveDescriptor");
5634
+ __name(resolveMapping, "resolveMapping");
5635
+ __name3(resolveMapping, "resolveMapping");
5636
+ fromInit = /* @__PURE__ */ __name3((path3) => ({
5637
+ initData: true,
5638
+ path: path3
5639
+ }), "fromInit");
5640
+ fromStep = /* @__PURE__ */ __name3((s, path3 = "") => {
5641
+ const idOf = /* @__PURE__ */ __name3((x) => typeof x === "string" ? x : x.id, "idOf");
5642
+ return {
5643
+ step: Array.isArray(s) ? s.map(idOf) : idOf(s),
5644
+ path: path3
5645
+ };
5646
+ }, "fromStep");
5647
+ value = /* @__PURE__ */ __name3((v) => ({
5648
+ value: v
5649
+ }), "value");
5650
+ template = /* @__PURE__ */ __name3((s) => ({
5651
+ template: s
5652
+ }), "template");
5653
+ fromRequest = /* @__PURE__ */ __name3((path3) => ({
5654
+ requestContextPath: path3
5655
+ }), "fromRequest");
5656
+ rows = /* @__PURE__ */ __name3((s, path3, page) => ({
5657
+ step: typeof s === "string" ? s : s.id,
5658
+ path: path3,
5659
+ rows: page
5660
+ }), "rows");
5661
+ fromKnowledge = /* @__PURE__ */ __name3((k) => ({
5662
+ knowledge: k
5663
+ }), "fromKnowledge");
4760
5664
  SideEffectsSchema = z4.enum(WORKFLOW_SIDE_EFFECTS);
4761
5665
  JobResourcesSchema = z4.enum(WORKFLOW_JOB_RESOURCES);
4762
5666
  WORKFLOW_ARM_SUBRUN_ID = "$arm";
5667
+ WORKSPACE_TEMPLATE_EXPR_RE = /^\$\{\s*(?:initData|input)\.([^}]+?)\s*\}$/;
5668
+ __name(workspaceTemplatePath, "workspaceTemplatePath");
5669
+ __name3(workspaceTemplatePath, "workspaceTemplatePath");
5670
+ __name(retryBackoffs, "retryBackoffs");
5671
+ __name3(retryBackoffs, "retryBackoffs");
4763
5672
  SLEEP_UNTIL_REPLACEMENT = Object.freeze({
4764
5673
  type: "sleep",
4765
5674
  duration: 6e4
@@ -4795,6 +5704,8 @@ var init_dist2 = __esm({
4795
5704
  __name3(fillPolicy, "fillPolicy");
4796
5705
  __name(fillSingle, "fillSingle");
4797
5706
  __name3(fillSingle, "fillSingle");
5707
+ __name(fillHitl, "fillHitl");
5708
+ __name3(fillHitl, "fillHitl");
4798
5709
  __name(fillArm, "fillArm");
4799
5710
  __name3(fillArm, "fillArm");
4800
5711
  __name(fillEntry, "fillEntry");
@@ -4838,11 +5749,15 @@ var init_dist2 = __esm({
4838
5749
  if (t === void 0) return void 0;
4839
5750
  return Array.isArray(t) ? t.includes("array") : t === "array";
4840
5751
  }, "schemaIsArray");
5752
+ isHitlNode = /* @__PURE__ */ __name3((n2) => isWorkflowHitlEntryType(n2.type), "isHitlNode");
5753
+ isSingleStep = /* @__PURE__ */ __name3((n2) => !isHitlNode(n2), "isSingleStep");
4841
5754
  singleId = /* @__PURE__ */ __name3((s) => s.type === "step" ? s.step.id : s.id, "singleId");
4842
5755
  armId = /* @__PURE__ */ __name3((a) => a.type === "mapping" ? a.id : singleId(a), "armId");
4843
5756
  TEMPLATE_STEP_REF = /\$\{\s*stepResults\.([A-Za-z0-9_\-]+)/g;
4844
5757
  __name(templateStepRefs, "templateStepRefs");
4845
5758
  __name3(templateStepRefs, "templateStepRefs");
5759
+ __name(readMapConfig, "readMapConfig");
5760
+ __name3(readMapConfig, "readMapConfig");
4846
5761
  __name(mapConfigStepRefs, "mapConfigStepRefs");
4847
5762
  __name3(mapConfigStepRefs, "mapConfigStepRefs");
4848
5763
  __name(nodeStepRefs, "nodeStepRefs");
@@ -4892,14 +5807,9 @@ var init_dist2 = __esm({
4892
5807
  this.name = "WorkflowPlanError";
4893
5808
  }
4894
5809
  };
4895
- SINGLE_STEP_KINDS = {
4896
- step: "code",
4897
- agent: "agent",
4898
- tool: "tool",
4899
- workflow: "subrun"
4900
- };
4901
- isSingleStep = /* @__PURE__ */ __name3((e) => e.type === "step" || e.type === "agent" || e.type === "tool" || e.type === "workflow", "isSingleStep");
4902
- singleStepId = /* @__PURE__ */ __name3((e) => e.type === "step" ? e.step.id : e.id, "singleStepId");
5810
+ isArmStep = /* @__PURE__ */ __name3((e) => isWorkflowArmEntryType(e.type), "isArmStep");
5811
+ armStepId = /* @__PURE__ */ __name3((e) => e.type === "step" ? e.step.id : e.id, "armStepId");
5812
+ armStepKind = /* @__PURE__ */ __name3((e) => WORKFLOW_ARM_ENTRY_STEP_KINDS[e.type], "armStepKind");
4903
5813
  joinIdOf = /* @__PURE__ */ __name3((entryId) => `${entryId}.join`, "joinIdOf");
4904
5814
  containerIdOf = /* @__PURE__ */ __name3((type, entryIndex) => `${type}@${entryIndex}`, "containerIdOf");
4905
5815
  __name(compilePlan, "compilePlan");
@@ -4994,74 +5904,6 @@ var init_dist2 = __esm({
4994
5904
  op: "not",
4995
5905
  arg
4996
5906
  }), "not");
4997
- WorkflowTemplateError = class extends Error {
4998
- static {
4999
- __name(this, "WorkflowTemplateError");
5000
- }
5001
- static {
5002
- __name3(this, "WorkflowTemplateError");
5003
- }
5004
- placeholder;
5005
- constructor(message, placeholder) {
5006
- super(message), this.placeholder = placeholder;
5007
- this.name = "WorkflowTemplateError";
5008
- }
5009
- };
5010
- __name(parseMapConfig, "parseMapConfig");
5011
- __name3(parseMapConfig, "parseMapConfig");
5012
- TEMPLATE_PLACEHOLDER = /\$\{([^}]*)\}/g;
5013
- TEMPLATE_NAMESPACES = [
5014
- "initData",
5015
- "state",
5016
- "requestContext",
5017
- "stepResults"
5018
- ];
5019
- __name(describeBadPlaceholder, "describeBadPlaceholder");
5020
- __name3(describeBadPlaceholder, "describeBadPlaceholder");
5021
- __name(parseTemplatePlaceholder, "parseTemplatePlaceholder");
5022
- __name3(parseTemplatePlaceholder, "parseTemplatePlaceholder");
5023
- __name(traverseMappingPath, "traverseMappingPath");
5024
- __name3(traverseMappingPath, "traverseMappingPath");
5025
- __name(stringifyTemplateValue, "stringifyTemplateValue");
5026
- __name3(stringifyTemplateValue, "stringifyTemplateValue");
5027
- __name(escapeFence, "escapeFence");
5028
- __name3(escapeFence, "escapeFence");
5029
- __name(fenceBlock, "fenceBlock");
5030
- __name3(fenceBlock, "fenceBlock");
5031
- __name(renderTemplate, "renderTemplate");
5032
- __name3(renderTemplate, "renderTemplate");
5033
- __name(resolveDescriptor, "resolveDescriptor");
5034
- __name3(resolveDescriptor, "resolveDescriptor");
5035
- __name(resolveMapping, "resolveMapping");
5036
- __name3(resolveMapping, "resolveMapping");
5037
- fromInit = /* @__PURE__ */ __name3((path3) => ({
5038
- initData: true,
5039
- path: path3
5040
- }), "fromInit");
5041
- fromStep = /* @__PURE__ */ __name3((s, path3 = "") => {
5042
- const idOf = /* @__PURE__ */ __name3((x) => typeof x === "string" ? x : x.id, "idOf");
5043
- return {
5044
- step: Array.isArray(s) ? s.map(idOf) : idOf(s),
5045
- path: path3
5046
- };
5047
- }, "fromStep");
5048
- value = /* @__PURE__ */ __name3((v) => ({
5049
- value: v
5050
- }), "value");
5051
- template = /* @__PURE__ */ __name3((s) => ({
5052
- template: s
5053
- }), "template");
5054
- fromRequest = /* @__PURE__ */ __name3((path3) => ({
5055
- requestContextPath: path3
5056
- }), "fromRequest");
5057
- rows = /* @__PURE__ */ __name3((s, path3, page) => ({
5058
- step: typeof s === "string" ? s : s.id,
5059
- path: path3,
5060
- rows: page
5061
- }), "rows");
5062
- fromKnowledge = /* @__PURE__ */ __name3((k) => ({
5063
- knowledge: k
5064
- }), "fromKnowledge");
5065
5907
  CONTINUED_FAILURE_TAG = "continued_failure";
5066
5908
  CONTINUED_FAILURE_DEFAULT_CODE = "step_failed";
5067
5909
  CONTINUED_FAILURE_OUTPUT_SCHEMA = Object.freeze({
@@ -5113,6 +5955,7 @@ var init_dist2 = __esm({
5113
5955
  __name3(continuedFailureValue, "continuedFailureValue");
5114
5956
  __name(isContinuedFailureValue, "isContinuedFailureValue");
5115
5957
  __name3(isContinuedFailureValue, "isContinuedFailureValue");
5958
+ isHitlNode2 = /* @__PURE__ */ __name3((n2) => isWorkflowHitlEntryType(n2.type), "isHitlNode");
5116
5959
  __name(lowerContainerArm, "lowerContainerArm");
5117
5960
  __name3(lowerContainerArm, "lowerContainerArm");
5118
5961
  nodeIdOf = /* @__PURE__ */ __name3((n2) => n2.type === "step" ? n2.step.id : n2.id, "nodeIdOf");
@@ -5120,6 +5963,24 @@ var init_dist2 = __esm({
5120
5963
  __name3(entryIds, "entryIds");
5121
5964
  __name(resolvePlacements, "resolvePlacements");
5122
5965
  __name3(resolvePlacements, "resolvePlacements");
5966
+ GOAL_JUDGE_STEP_ID = "__goal_judge";
5967
+ NON_LEAF_KINDS = /* @__PURE__ */ new Set([
5968
+ "foreach",
5969
+ "branch"
5970
+ ]);
5971
+ CONDITIONAL_JOIN_ID = /^conditional@\d+\.join$/;
5972
+ __name(isConditionalJoinId, "isConditionalJoinId");
5973
+ __name3(isConditionalJoinId, "isConditionalJoinId");
5974
+ __name(isPlainObject, "isPlainObject");
5975
+ __name3(isPlainObject, "isPlainObject");
5976
+ __name(leafValue, "leafValue");
5977
+ __name3(leafValue, "leafValue");
5978
+ __name(runOutputLeaves, "runOutputLeaves");
5979
+ __name3(runOutputLeaves, "runOutputLeaves");
5980
+ __name(deriveRunOutput, "deriveRunOutput");
5981
+ __name3(deriveRunOutput, "deriveRunOutput");
5982
+ __name(subrunSettledOutput, "subrunSettledOutput");
5983
+ __name3(subrunSettledOutput, "subrunSettledOutput");
5123
5984
  __name(seedLedgerFromRun, "seedLedgerFromRun");
5124
5985
  __name3(seedLedgerFromRun, "seedLedgerFromRun");
5125
5986
  branchArmId = /* @__PURE__ */ __name3((arm) => arm.type === "step" ? arm.step.id : arm.id, "branchArmId");
@@ -5168,14 +6029,63 @@ var init_dist2 = __esm({
5168
6029
  n = /* @__PURE__ */ __name3((v) => typeof v === "number" && Number.isFinite(v) ? v : 0, "n");
5169
6030
  __name(runCounts, "runCounts");
5170
6031
  __name3(runCounts, "runCounts");
6032
+ __name(isPricedStepReceipt, "isPricedStepReceipt");
6033
+ __name3(isPricedStepReceipt, "isPricedStepReceipt");
6034
+ __name(receiptEngine, "receiptEngine");
6035
+ __name3(receiptEngine, "receiptEngine");
6036
+ __name(receiptTier, "receiptTier");
6037
+ __name3(receiptTier, "receiptTier");
6038
+ __name(stepBillingView, "stepBillingView");
6039
+ __name3(stepBillingView, "stepBillingView");
5171
6040
  __name(runUsage, "runUsage");
5172
6041
  __name3(runUsage, "runUsage");
6042
+ __name(runBudgetCap, "runBudgetCap");
6043
+ __name3(runBudgetCap, "runBudgetCap");
6044
+ __name(runBudgetRemaining, "runBudgetRemaining");
6045
+ __name3(runBudgetRemaining, "runBudgetRemaining");
5173
6046
  __name(runCancelView, "runCancelView");
5174
6047
  __name3(runCancelView, "runCancelView");
5175
6048
  __name(runWorkspaceView, "runWorkspaceView");
5176
6049
  __name3(runWorkspaceView, "runWorkspaceView");
5177
6050
  __name(toWorkflowRunSummary, "toWorkflowRunSummary");
5178
6051
  __name3(toWorkflowRunSummary, "toWorkflowRunSummary");
6052
+ STEP_ERROR_DETAIL_KEYS = [
6053
+ "reason",
6054
+ "key",
6055
+ "integrationType",
6056
+ "candidates",
6057
+ "connectionId",
6058
+ "status",
6059
+ "code",
6060
+ "providerStatus",
6061
+ "model",
6062
+ "turnIndex",
6063
+ "message",
6064
+ // LUA-655: `output_schema_invalid` — the Ajv issues ({path, message}, ≤ 20, scrubbed at the source too) and the
6065
+ // in-session repair rounds the attempt spent.
6066
+ "issues",
6067
+ "repairRounds",
6068
+ // LUA-669 (#2446 review 4): the subrun failures — `subrun_<status>` names the child run and how it ended
6069
+ // (`childStatus`, `childReason: 'max_duration'` under `subrun_timed_out`), `subrun_depth_exceeded` its `depth` /
6070
+ // `max`, and every refusal the target `workflowId`. Short scalars only: the child's whole `childError` and the
6071
+ // cycle walk's `ancestors` stay off the wire — the child run's own R4 carries its error.
6072
+ "childRunId",
6073
+ "childStatus",
6074
+ "childReason",
6075
+ "max",
6076
+ "depth",
6077
+ "workflowId",
6078
+ // LUA-696 (review 2): the `ctx.once` key of an `effect_in_doubt` park — the step site stamps it here (scrubbed)
6079
+ // beside `park.effectKey`; a key is user text and leaves scrubbed like every other string leaf.
6080
+ "effectKey"
6081
+ ];
6082
+ STEP_ERROR_DETAIL_MAX_BYTES = 8 * 1024;
6083
+ DETAIL_MAX_DEPTH = 4;
6084
+ DETAIL_MAX_ITEMS = 100;
6085
+ __name(scrubDetailValue, "scrubDetailValue");
6086
+ __name3(scrubDetailValue, "scrubDetailValue");
6087
+ __name(stepErrorDetail, "stepErrorDetail");
6088
+ __name3(stepErrorDetail, "stepErrorDetail");
5179
6089
  MAX_HOLIDAYS = 366;
5180
6090
  MAX_WALK_DAYS = 400;
5181
6091
  HHMM = /^([01]\d|2[0-3]):([0-5]\d)$/;
@@ -5321,6 +6231,18 @@ var init_dist2 = __esm({
5321
6231
  EscalationHopSchema
5322
6232
  ])).min(1).max(ESCALATION_MAX_HOPS + 1)
5323
6233
  ]);
6234
+ APPROVER_SPEC_SHAPES = [
6235
+ "'creator'",
6236
+ "'org-admins'",
6237
+ "{users:[userId, \u2026]}",
6238
+ "{role:roleName}",
6239
+ "{group:groupName}",
6240
+ "{governance:{policyId}}"
6241
+ ];
6242
+ APPROVER_WRITTEN_MAX = 120;
6243
+ USER_ID_SHAPED_RE = /^[^\s@]{1,128}$/;
6244
+ __name(describeApproverSpecRefusal, "describeApproverSpecRefusal");
6245
+ __name3(describeApproverSpecRefusal, "describeApproverSpecRefusal");
5324
6246
  BINDING_ROOTS = [
5325
6247
  "initData",
5326
6248
  "stepResults",
@@ -5476,7 +6398,12 @@ function materializeEntry(entry, steps) {
5476
6398
  }
5477
6399
  }
5478
6400
  function graphHasHitl(graph, steps) {
5479
- if (graph.some((e) => e.type === "approval" || e.type === "waitForSignal")) return true;
6401
+ for (const e of graph) {
6402
+ if (isHitlEntry(e)) return true;
6403
+ if (e.type === "parallel" && e.steps.some(isHitlEntry)) return true;
6404
+ if (e.type === "conditional" && (e.steps.some(isHitlEntry) || isHitlEntry(e.otherwise))) return true;
6405
+ if ((e.type === "foreach" || e.type === "loop") && isHitlEntry(e.step)) return true;
6406
+ }
5480
6407
  return Object.values(steps).some((s) => s.suspendSchema !== void 0);
5481
6408
  }
5482
6409
  function createWorkflow(cfg) {
@@ -5510,10 +6437,11 @@ function defineWorkflow(cfg, build) {
5510
6437
  if (!(wf instanceof LuaWorkflow)) throw new LuaWorkflowBuildError("invalid-envelope", "defineWorkflow: the build callback must return `wf\u2026.commit()`");
5511
6438
  return wf;
5512
6439
  }
5513
- var init2, state2, lit2, eq2, ne2, gt2, gte2, lt2, lte2, inSet2, notIn2, exists2, notExists2, truthy2, falsy2, and2, or2, not2, fromInit2, fromStep2, value2, template2, fromRequest2, rows2, fromKnowledge2, LuaWorkflowBuildError, STEP_ID_RE, WORKFLOW_NAME_RE, WORKFLOW_MAX_PARALLEL_ARMS, WORKFLOW_MAX_FOREACH_CONCURRENCY, WORKFLOW_MAX_FOREACH_ITEMS, WORKFLOW_WORKER_MAX_TIMEOUT_SECONDS, WORKFLOW_JOB_SEGMENT_MAX_SECONDS, WORKFLOW_JOB_MAX_TIMEOUT_SECONDS, WORKFLOW_LOOP_INTERVAL_MAX_SECONDS, WORKFLOW_FOREACH_RATE_MAX_PER_SECOND, WORKFLOW_SPECIALIST_ROLE_MAX_INSTRUCTIONS, WORKFLOW_DEFAULT_MAX_DURATION_SECONDS, WORKFLOW_HITL_MAX_DURATION_SECONDS, SECRET_KEY_RE, isZod, defined, templateText, assertNoClosure, assertPredicate, assertRetry, assertTimeout, envRefKeys, refToDescriptor, __workflowCommitHook, LuaWorkflow, WorkflowBuilderImpl, EDITABLE_PATH_RE2;
6440
+ var init2, state2, lit2, eq2, ne2, gt2, gte2, lt2, lte2, inSet2, notIn2, exists2, notExists2, truthy2, falsy2, and2, or2, not2, fromInit2, fromStep2, value2, template2, fromRequest2, rows2, fromKnowledge2, LuaWorkflowBuildError, STEP_ID_RE, WORKFLOW_NAME_RE, WORKFLOW_MAX_PARALLEL_ARMS, WORKFLOW_MAX_FOREACH_CONCURRENCY, WORKFLOW_MAX_FOREACH_ITEMS, WORKFLOW_WORKER_MAX_TIMEOUT_SECONDS, WORKFLOW_JOB_SEGMENT_MAX_SECONDS, WORKFLOW_JOB_MAX_TIMEOUT_SECONDS, WORKFLOW_LOOP_INTERVAL_MAX_SECONDS, WORKFLOW_FOREACH_RATE_MAX_PER_SECOND, WORKFLOW_SPECIALIST_ROLE_MAX_INSTRUCTIONS, WORKFLOW_DEFAULT_MAX_DURATION_SECONDS, WORKFLOW_HITL_MAX_DURATION_SECONDS, SECRET_KEY_RE, isZod, defined, templateText, assertNoClosure, assertPredicate, assertRetry, assertTimeout, envRefKeys, refToDescriptor, __workflowCommitHook, LuaWorkflow, isHitlEntry, WorkflowBuilderImpl, EDITABLE_PATH_RE2;
5514
6441
  var init_workflow = __esm({
5515
6442
  "src/types/workflow.ts"() {
5516
6443
  "use strict";
6444
+ init_dist();
5517
6445
  init_dist2();
5518
6446
  __name(createStep, "createStep");
5519
6447
  __name(step2, "step");
@@ -5596,7 +6524,8 @@ var init_workflow = __esm({
5596
6524
  }, "assertPredicate");
5597
6525
  assertRetry = /* @__PURE__ */ __name((r, id) => {
5598
6526
  if (!r) return;
5599
- if (r.backoff !== void 0 && r.backoff !== "fixed" && r.backoff !== "exponential") throw new LuaWorkflowBuildError("backoff-invalid", `"${id}": retry.backoff must be 'fixed' | 'exponential'`);
6527
+ if (typeof r.maxAttempts === "number" && r.maxAttempts > WORKFLOW_RETRY_MAX_ATTEMPTS) throw new LuaWorkflowBuildError("cap-exceeded", `"${id}": ${workflowRetryMaxAttemptsMessage(r.maxAttempts)}`);
6528
+ 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(" | ")}`);
5600
6529
  if (r.maxBackoffSeconds !== void 0) {
5601
6530
  if (r.backoff !== "exponential") throw new LuaWorkflowBuildError("backoff-invalid", `"${id}": retry.maxBackoffSeconds is only meaningful with backoff:'exponential'`);
5602
6531
  if (r.maxBackoffSeconds <= 0 || r.backoffSeconds !== void 0 && r.maxBackoffSeconds < r.backoffSeconds) throw new LuaWorkflowBuildError("backoff-invalid", `"${id}": retry.maxBackoffSeconds must be > 0 and \u2265 backoffSeconds`);
@@ -5718,6 +6647,7 @@ var init_workflow = __esm({
5718
6647
  };
5719
6648
  __name(stepNodeOf, "stepNodeOf");
5720
6649
  __name(materializeEntry, "materializeEntry");
6650
+ isHitlEntry = /* @__PURE__ */ __name((n2) => isWorkflowHitlEntryType(n2?.type), "isHitlEntry");
5721
6651
  __name(graphHasHitl, "graphHasHitl");
5722
6652
  WorkflowBuilderImpl = class WorkflowBuilderImpl2 {
5723
6653
  static {
@@ -5770,7 +6700,6 @@ var init_workflow = __esm({
5770
6700
  const [mapping, target] = arm;
5771
6701
  if (target === void 0 || arm.length < 2) throw new LuaWorkflowBuildError("container-arm-empty", `${where}: a bare mapping arm has nothing to run`);
5772
6702
  if (!mapping || typeof mapping !== "object" || Array.isArray(mapping)) throw new LuaWorkflowBuildError("mapping-placement", `${where}: the arm head must be a map config object`);
5773
- if (mapping.type === "approval" || target.type === "approval") throw new LuaWorkflowBuildError("approval-inside-container", `${where}: approval / waitForSignal are top-level only in v1`);
5774
6703
  assertNoClosure(mapping, `${where} arm map`);
5775
6704
  this.recordEnvRefs(mapping);
5776
6705
  const inner = this.armRef(target, where);
@@ -5788,8 +6717,9 @@ var init_workflow = __esm({
5788
6717
  if (typeof arm === "string") return {
5789
6718
  ref: arm
5790
6719
  };
5791
- if (arm && typeof arm === "object" && arm.type === "approval") {
5792
- throw new LuaWorkflowBuildError("approval-inside-container", `${where}: approval / waitForSignal are top-level only in v1`);
6720
+ if (arm && typeof arm === "object" && isHitlEntry(arm)) {
6721
+ const id = arm.id;
6722
+ throw new LuaWorkflowBuildError("invalid-step", `${where}: an approval / waitForSignal arm is placed by id \u2014 declare it with .approval(${JSON.stringify(id ?? "id")}, \u2026) / .waitForSignal(\u2026) and pass the id string`);
5793
6723
  }
5794
6724
  return {
5795
6725
  node: this.registerStep(arm, where)
@@ -5990,17 +6920,13 @@ var init_workflow = __esm({
5990
6920
  if (opts.workspace && opts.tier !== void 0 && opts.tier !== "job") throw new LuaWorkflowBuildError("workspace-requires-job-tier", `"${id}": a step mounting a workspace must be tier:'job'`);
5991
6921
  if (opts.harness !== void 0 && tier !== "job") throw new LuaWorkflowBuildError("harness-requires-job-tier", `"${id}": harness is only legal on a tier:'job' agent step`);
5992
6922
  if (opts.toolScope?.jobTools && tier !== "job") throw new LuaWorkflowBuildError("cap-exceeded", `"${id}": toolScope.jobTools require tier:'job' (job-tools-require-job-tier)`);
5993
- if (opts.maxTurns !== void 0) {
5994
- if (tier !== "job") throw new LuaWorkflowBuildError("max-turns-requires-job-tier", `"${id}": maxTurns is only legal on a tier:'job' agent step`);
5995
- if (!Number.isInteger(opts.maxTurns) || opts.maxTurns < 1 || opts.maxTurns > 500) throw new LuaWorkflowBuildError("max-turns-invalid", `"${id}": maxTurns must be an integer 1..500`);
5996
- }
5997
- if (opts.maxMessages !== void 0) {
5998
- if (tier !== "job") throw new LuaWorkflowBuildError("max-turns-requires-job-tier", `"${id}": maxMessages is only legal on a tier:'job' agent step`);
5999
- if (!Number.isInteger(opts.maxMessages) || opts.maxMessages < 1 || opts.maxMessages > 5e3) throw new LuaWorkflowBuildError("max-turns-invalid", `"${id}": maxMessages must be an integer 1..5000`);
6000
- }
6001
- if (opts.maxInputTokens !== void 0) {
6002
- if (tier !== "job") throw new LuaWorkflowBuildError("max-turns-requires-job-tier", `"${id}": maxInputTokens is only legal on a tier:'job' agent step`);
6003
- if (!Number.isInteger(opts.maxInputTokens) || opts.maxInputTokens < 1e6 || opts.maxInputTokens > 5e8) throw new LuaWorkflowBuildError("max-turns-invalid", `"${id}": maxInputTokens must be an integer 1000000..500000000`);
6923
+ for (const m of WORKFLOW_JOB_RANGE_MEMBERS) {
6924
+ if (opts[m] === void 0) continue;
6925
+ if (tier !== "job") throw new LuaWorkflowBuildError("max-turns-requires-job-tier", `"${id}": ${m} is only legal on a tier:'job' agent step`);
6926
+ if (!isWithinWorkflowJobRange(m, opts[m])) {
6927
+ const { min, max } = WORKFLOW_JOB_RANGES[m];
6928
+ throw new LuaWorkflowBuildError("max-turns-invalid", `"${id}": ${m} must be an integer ${min}..${max}`);
6929
+ }
6004
6930
  }
6005
6931
  assertTimeout({
6006
6932
  id,
@@ -6152,8 +7078,8 @@ var init_workflow = __esm({
6152
7078
  itemTimeout: opts.itemTimeout
6153
7079
  });
6154
7080
  return this.push({
6155
- kind: "entry",
6156
- entry: node
7081
+ kind: "declare",
7082
+ node
6157
7083
  });
6158
7084
  }
6159
7085
  waitForSignal(id, opts) {
@@ -6171,12 +7097,13 @@ var init_workflow = __esm({
6171
7097
  acceptedSources: opts.acceptedSources
6172
7098
  });
6173
7099
  return this.push({
6174
- kind: "entry",
6175
- entry: node
7100
+ kind: "declare",
7101
+ node
6176
7102
  });
6177
7103
  }
6178
7104
  workflow(id, ref, input, opts) {
6179
7105
  this.assertId(id, "workflow()");
7106
+ assertRetry(opts?.retry, id);
6180
7107
  const name = typeof ref === "string" ? ref : ref instanceof LuaWorkflow ? ref.getName() : void 0;
6181
7108
  if (!name) throw new LuaWorkflowBuildError("invalid-envelope", `workflow("${id}") needs a LuaWorkflow or a workflow name`);
6182
7109
  if (opts?.workspace === "inherit") {
@@ -6198,7 +7125,8 @@ var init_workflow = __esm({
6198
7125
  id,
6199
7126
  workflowId: name,
6200
7127
  input,
6201
- workspace: opts?.workspace
7128
+ workspace: opts?.workspace,
7129
+ retry: opts?.retry
6202
7130
  });
6203
7131
  return this.push({
6204
7132
  kind: "declare",
@@ -6212,7 +7140,7 @@ var init_workflow = __esm({
6212
7140
  const { graph, issues } = resolvePlacements(this.calls);
6213
7141
  const fatal = issues[0];
6214
7142
  if (fatal) {
6215
- const hint = fatal.code === "unknown-step-ref" ? "a string StepRef must name an entry declared by agentStep/specialistStep/toolStep/map(\u2026, { id })/workflow(\u2026) somewhere in the chain \u2014 before OR after the reference" : void 0;
7143
+ const hint = fatal.code === "unknown-step-ref" ? "a string StepRef must name an entry declared by agentStep/specialistStep/toolStep/map(\u2026, { id })/workflow(\u2026)/approval(\u2026)/waitForSignal(\u2026) somewhere in the chain \u2014 before OR after the reference" : void 0;
6216
7144
  throw new LuaWorkflowBuildError(fatal.code, fatal.message, hint);
6217
7145
  }
6218
7146
  if (graph.length === 0) throw new LuaWorkflowBuildError("empty-graph", `workflow "${this.config.name}" has no entries`);
@@ -6366,6 +7294,74 @@ var init_auth_error = __esm({
6366
7294
  }
6367
7295
  });
6368
7296
 
7297
+ // src/errors/cli.error.ts
7298
+ function isAccessDeniedError(error) {
7299
+ if (CliError.isCliError(error)) return error.statusCode === 403;
7300
+ return error instanceof Error && error.message.startsWith("Access denied (403)");
7301
+ }
7302
+ var CLI_EXIT, CliError;
7303
+ var init_cli_error = __esm({
7304
+ "src/errors/cli.error.ts"() {
7305
+ "use strict";
7306
+ init_auth_error();
7307
+ CLI_EXIT = {
7308
+ OK: 0,
7309
+ ERROR: 1,
7310
+ USAGE: 2,
7311
+ NOT_FOUND: 3,
7312
+ AUTH: 9,
7313
+ FORBIDDEN: 10,
7314
+ UNAVAILABLE: 11
7315
+ };
7316
+ CliError = class _CliError extends Error {
7317
+ static {
7318
+ __name(this, "CliError");
7319
+ }
7320
+ isCliError = true;
7321
+ code;
7322
+ exitCode;
7323
+ hint;
7324
+ statusCode;
7325
+ constructor(code, message, options = {}) {
7326
+ super(message);
7327
+ this.name = "CliError";
7328
+ this.code = code;
7329
+ this.exitCode = options.exitCode ?? CLI_EXIT.ERROR;
7330
+ this.hint = options.hint;
7331
+ this.statusCode = options.statusCode;
7332
+ if (Error.captureStackTrace) Error.captureStackTrace(this, _CliError);
7333
+ }
7334
+ /** Bad arguments, an unknown action, no project — exit 2. */
7335
+ static usage(message, hint) {
7336
+ return new _CliError("usage", message, {
7337
+ exitCode: CLI_EXIT.USAGE,
7338
+ hint
7339
+ });
7340
+ }
7341
+ /** The named thing does not exist — exit 3. */
7342
+ static notFound(message, hint) {
7343
+ return new _CliError("not_found", message, {
7344
+ exitCode: CLI_EXIT.NOT_FOUND,
7345
+ hint,
7346
+ statusCode: 404
7347
+ });
7348
+ }
7349
+ /** The credential may not do this — exit 10. */
7350
+ static forbidden(message, hint) {
7351
+ return new _CliError("forbidden", message, {
7352
+ exitCode: CLI_EXIT.FORBIDDEN,
7353
+ hint,
7354
+ statusCode: 403
7355
+ });
7356
+ }
7357
+ static isCliError(error) {
7358
+ return error instanceof _CliError || typeof error === "object" && error !== null && error.isCliError === true;
7359
+ }
7360
+ };
7361
+ __name(isAccessDeniedError, "isAccessDeniedError");
7362
+ }
7363
+ });
7364
+
6369
7365
  // src/utils/package-root.ts
6370
7366
  import { readFileSync, existsSync } from "fs";
6371
7367
  import { fileURLToPath, pathToFileURL } from "url";
@@ -6610,7 +7606,7 @@ var init_firebase_session_store = __esm({
6610
7606
  }), "currentFirebaseSessionEnvironment");
6611
7607
  __name(environmentKey, "environmentKey");
6612
7608
  __name(isMissing, "isMissing");
6613
- wait = /* @__PURE__ */ __name((milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)), "wait");
7609
+ wait = /* @__PURE__ */ __name((milliseconds) => new Promise((resolve3) => setTimeout(resolve3, milliseconds)), "wait");
6614
7610
  FirebaseSessionStore = class {
6615
7611
  static {
6616
7612
  __name(this, "FirebaseSessionStore");
@@ -6878,13 +7874,13 @@ async function* parseSseStream(body, signal) {
6878
7874
  buffer += decoder.decode(value3, {
6879
7875
  stream: true
6880
7876
  });
6881
- let sep = buffer.search(/\r?\n\r?\n/);
6882
- while (sep !== -1) {
6883
- const block = buffer.slice(0, sep);
6884
- buffer = buffer.slice(sep).replace(/^\r?\n\r?\n/, "");
7877
+ let sep4 = buffer.search(/\r?\n\r?\n/);
7878
+ while (sep4 !== -1) {
7879
+ const block = buffer.slice(0, sep4);
7880
+ buffer = buffer.slice(sep4).replace(/^\r?\n\r?\n/, "");
6885
7881
  const frame = flush(block);
6886
7882
  if (frame) yield frame;
6887
- sep = buffer.search(/\r?\n\r?\n/);
7883
+ sep4 = buffer.search(/\r?\n\r?\n/);
6888
7884
  }
6889
7885
  }
6890
7886
  if (buffer.trim()) {
@@ -6892,6 +7888,10 @@ async function* parseSseStream(body, signal) {
6892
7888
  if (frame) yield frame;
6893
7889
  }
6894
7890
  } finally {
7891
+ try {
7892
+ await reader.cancel();
7893
+ } catch {
7894
+ }
6895
7895
  try {
6896
7896
  reader.releaseLock();
6897
7897
  } catch {
@@ -6921,6 +7921,7 @@ var init_http_client = __esm({
6921
7921
  "use strict";
6922
7922
  init_dist();
6923
7923
  init_auth_error();
7924
+ init_cli_error();
6924
7925
  init_lua_fetch();
6925
7926
  init_request_credential();
6926
7927
  DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
@@ -6984,7 +7985,7 @@ var init_http_client = __esm({
6984
7985
  if (AuthenticationError.isAuthenticationError(error)) {
6985
7986
  throw error;
6986
7987
  }
6987
- if (error instanceof Error && error.message.startsWith("Access denied (403)")) {
7988
+ if (isAccessDeniedError(error)) {
6988
7989
  throw error;
6989
7990
  }
6990
7991
  if (error instanceof DOMException && error.name === "AbortError") {
@@ -7032,8 +8033,11 @@ var init_http_client = __esm({
7032
8033
  }
7033
8034
  if (response.status === 403) {
7034
8035
  const detail = errorData.message || "You do not have permission to access this resource.";
7035
- throw new Error(`Access denied (403): ${detail}
7036
- Check that your Lua login has access to this agent or organization.`);
8036
+ throw new CliError("forbidden", `Access denied (403): ${detail}`, {
8037
+ exitCode: CLI_EXIT.FORBIDDEN,
8038
+ statusCode: 403,
8039
+ hint: "Check that your Lua login has access to this agent or organization."
8040
+ });
7037
8041
  }
7038
8042
  return {
7039
8043
  success: false,
@@ -7103,7 +8107,7 @@ Check that your Lua login has access to this agent or organization.`);
7103
8107
  if (attempt < maxRetries) {
7104
8108
  const serverDelay = Number(lastResult?.error?.retryAfterSeconds ?? 0) * 1e3;
7105
8109
  const backoff = Math.max(this.calculateBackoff(attempt), serverDelay);
7106
- await new Promise((resolve) => setTimeout(resolve, backoff));
8110
+ await new Promise((resolve3) => setTimeout(resolve3, backoff));
7107
8111
  }
7108
8112
  }
7109
8113
  return lastResult;
@@ -7152,7 +8156,7 @@ Check that your Lua login has access to this agent or organization.`);
7152
8156
  async httpPostCoreDrainRetry(url, data, headers) {
7153
8157
  const first = await this.httpPostOnce(url, data, headers);
7154
8158
  if (!isCoreDrainApiError(first.error)) return first;
7155
- await new Promise((resolve) => setTimeout(resolve, coreDrainApiRetryDelayMs(first.error)));
8159
+ await new Promise((resolve3) => setTimeout(resolve3, coreDrainApiRetryDelayMs(first.error)));
7156
8160
  return this.httpPostOnce(url, data, headers);
7157
8161
  }
7158
8162
  /**
@@ -7275,6 +8279,7 @@ var init_auth = __esm({
7275
8279
  init_auth_api_service();
7276
8280
  init_constants();
7277
8281
  init_auth_error();
8282
+ init_cli_error();
7278
8283
  }
7279
8284
  });
7280
8285
 
@@ -7502,6 +8507,9 @@ function buildSourceArchive(files) {
7502
8507
  const gz = zlib.gzipSync(Buffer.from(json, "utf-8"));
7503
8508
  return gz.toString("base64");
7504
8509
  }
8510
+ function getPrimitivesByKind(manifest, kind) {
8511
+ return manifest.primitives.filter((p) => p.kind === kind);
8512
+ }
7505
8513
  function findPrimitive(manifest, name, kind) {
7506
8514
  return manifest.primitives.find((p) => {
7507
8515
  if (kind && p.kind !== kind) return false;
@@ -7522,15 +8530,565 @@ var init_artifact_loader = __esm({
7522
8530
  __name(loadOriginalSource, "loadOriginalSource");
7523
8531
  __name(normalizeEntryFile, "normalizeEntryFile");
7524
8532
  __name(buildSourceArchive, "buildSourceArchive");
8533
+ __name(getPrimitivesByKind, "getPrimitivesByKind");
7525
8534
  __name(findPrimitive, "findPrimitive");
7526
8535
  }
7527
8536
  });
7528
8537
 
8538
+ // ../shared-source-sync/dist/index.mjs
8539
+ import { createHash as createHash4 } from "crypto";
8540
+ import { extname } from "path";
8541
+ import { readdirSync, readFileSync as readFileSync3, statSync } from "fs";
8542
+ import { join as join4, sep } from "path";
8543
+ import { gunzipSync, gzipSync } from "zlib";
8544
+ import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync22, statSync as statSync2, writeFileSync } from "fs";
8545
+ import { dirname as dirname2, join as join22, resolve, sep as sep2 } from "path";
8546
+ import { posix } from "path";
8547
+ import { mkdirSync as mkdirSync2, readdirSync as readdirSync2, readFileSync as readFileSync32, statSync as statSync3, writeFileSync as writeFileSync2 } from "fs";
8548
+ import { dirname as dirname22, join as join32, resolve as resolve2, sep as sep3 } from "path";
8549
+ import { gunzipSync as gunzipSync2, gzipSync as gzipSync2 } from "zlib";
8550
+ function hashContentTruncated(content) {
8551
+ return createHash4("sha256").update(content).digest("hex").slice(0, FILE_HASH_LENGTH);
8552
+ }
8553
+ function sha256Hex(content) {
8554
+ return createHash4("sha256").update(content).digest("hex");
8555
+ }
8556
+ function matchesFileHash(hash, plaintext) {
8557
+ if (!/^(?:[a-f0-9]{16}|[a-f0-9]{64})$/.test(hash)) return false;
8558
+ if (sha256Hex(plaintext).startsWith(hash)) return true;
8559
+ return sha256Hex(plaintext.toString("utf-8")).startsWith(hash);
8560
+ }
8561
+ function combineFileHashes(files) {
8562
+ const sorted = [
8563
+ ...files
8564
+ ].sort((a, b) => a.relativePath.localeCompare(b.relativePath));
8565
+ return sha256Hex(sorted.map((f) => f.hash).join("|"));
8566
+ }
8567
+ function shouldSkipDirectory(name) {
8568
+ return SKIP_DIRECTORIES.has(name);
8569
+ }
8570
+ function shouldSkipFile(filePathOrName) {
8571
+ return filePathOrName.includes("node_modules") || filePathOrName.includes(".test.") || filePathOrName.includes(".spec.") || filePathOrName.includes("__tests__") || filePathOrName.endsWith(".d.ts") || filePathOrName.endsWith(".map") || filePathOrName.startsWith(".") || filePathOrName === "package-lock.json" || filePathOrName === "yarn.lock" || filePathOrName === "pnpm-lock.yaml";
8572
+ }
8573
+ function classifyFile(relPath) {
8574
+ return KIND_BY_EXT[extname(relPath)] ?? "other";
8575
+ }
8576
+ function walkWorkspace(rootDir, opts = {}) {
8577
+ const maxBytes = opts.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES;
8578
+ const refs = [];
8579
+ const contentByHash = /* @__PURE__ */ new Map();
8580
+ let totalSize = 0;
8581
+ const visit = /* @__PURE__ */ __name4((relPrefix) => {
8582
+ const absDir = relPrefix ? join4(rootDir, relPrefix) : rootDir;
8583
+ let entries;
8584
+ try {
8585
+ entries = readdirSync(absDir, {
8586
+ withFileTypes: true
8587
+ });
8588
+ } catch {
8589
+ return;
8590
+ }
8591
+ for (const entry of entries) {
8592
+ if (entry.isDirectory()) {
8593
+ if (shouldSkipDirectory(entry.name)) continue;
8594
+ if (shouldSkipFile(entry.name)) continue;
8595
+ visit(relPrefix ? `${relPrefix}${sep}${entry.name}` : entry.name);
8596
+ continue;
8597
+ }
8598
+ if (!entry.isFile()) continue;
8599
+ if (shouldSkipFile(entry.name)) continue;
8600
+ const rel = (relPrefix ? `${relPrefix}${sep}${entry.name}` : entry.name).split(sep).join("/");
8601
+ const abs = join4(rootDir, rel);
8602
+ let stats;
8603
+ try {
8604
+ stats = statSync(abs);
8605
+ } catch {
8606
+ continue;
8607
+ }
8608
+ if (stats.size > maxBytes) continue;
8609
+ let content;
8610
+ try {
8611
+ content = readFileSync3(abs);
8612
+ } catch {
8613
+ continue;
8614
+ }
8615
+ const hash = hashContentTruncated(content.toString("utf-8"));
8616
+ refs.push({
8617
+ relativePath: rel,
8618
+ hash,
8619
+ size: stats.size,
8620
+ type: classifyFile(rel)
8621
+ });
8622
+ if (!contentByHash.has(hash)) contentByHash.set(hash, content);
8623
+ totalSize += stats.size;
8624
+ }
8625
+ }, "visit");
8626
+ visit("");
8627
+ refs.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
8628
+ const projectHash = combineFileHashes(refs);
8629
+ return {
8630
+ files: refs,
8631
+ projectHash,
8632
+ totalSize,
8633
+ contentByHash
8634
+ };
8635
+ }
8636
+ async function uploadBlobs(opts) {
8637
+ const fetchFn = opts.fetch ?? fetch;
8638
+ const concurrency = opts.concurrency ?? DEFAULT_CONCURRENCY;
8639
+ const hashes = Object.keys(opts.uploadUrls);
8640
+ for (let i = 0; i < hashes.length; i += concurrency) {
8641
+ const batch = hashes.slice(i, i + concurrency);
8642
+ await Promise.all(batch.map(async (hash) => {
8643
+ const buf = opts.contentByHash.get(hash);
8644
+ if (!buf) {
8645
+ throw new Error(`uploadBlobs: no content for hash ${hash.slice(0, 12)}\u2026`);
8646
+ }
8647
+ const compressed = gzipSync(buf);
8648
+ const res = await fetchFn(opts.uploadUrls[hash], {
8649
+ method: "PUT",
8650
+ body: compressed,
8651
+ headers: {
8652
+ "Content-Type": "application/octet-stream"
8653
+ }
8654
+ });
8655
+ if (!res.ok) {
8656
+ throw new Error(`S3 upload failed for ${hash.slice(0, 12)}\u2026: ${res.status} ${res.statusText}`);
8657
+ }
8658
+ }));
8659
+ }
8660
+ return hashes.length;
8661
+ }
8662
+ function decodeBlob(buf) {
8663
+ if (buf.length >= 2 && buf[0] === 31 && buf[1] === 139) {
8664
+ try {
8665
+ return gunzipSync(buf);
8666
+ } catch {
8667
+ return buf;
8668
+ }
8669
+ }
8670
+ return buf;
8671
+ }
8672
+ function verifyDownloaded(hash, plaintext) {
8673
+ if (!matchesFileHash(hash, plaintext)) {
8674
+ throw new Error(`Blob ${hash.slice(0, 12)}\u2026 failed integrity verification: its content does not hash to its key (refusing to restore it)`);
8675
+ }
8676
+ return plaintext;
8677
+ }
8678
+ async function downloadBlobs(opts) {
8679
+ const fetchFn = opts.fetch ?? fetch;
8680
+ const concurrency = opts.concurrency ?? DEFAULT_CONCURRENCY;
8681
+ const out = /* @__PURE__ */ new Map();
8682
+ const entries = Object.entries(opts.urls);
8683
+ for (let i = 0; i < entries.length; i += concurrency) {
8684
+ const batch = entries.slice(i, i + concurrency);
8685
+ const downloaded = await Promise.all(batch.map(async ([hash, url]) => {
8686
+ const res = await fetchFn(url);
8687
+ if (!res.ok) {
8688
+ throw new Error(`S3 download failed for ${hash.slice(0, 12)}\u2026: ${res.status} ${res.statusText}`);
8689
+ }
8690
+ const ab = await res.arrayBuffer();
8691
+ const buf = Buffer.from(ab);
8692
+ return [
8693
+ hash,
8694
+ verifyDownloaded(hash, decodeBlob(buf))
8695
+ ];
8696
+ }));
8697
+ for (const [hash, buf] of downloaded) out.set(hash, buf);
8698
+ }
8699
+ return out;
8700
+ }
8701
+ function resolveBackupFileTarget(file, targetDir) {
8702
+ const base = resolve(targetDir);
8703
+ const sandboxed = file.external ? join22(base, ".lua", "external", file.relativePath) : join22(base, file.relativePath);
8704
+ const resolved = resolve(sandboxed);
8705
+ if (resolved !== base && !resolved.startsWith(base + sep2)) {
8706
+ throw new Error(`Backup entry escapes target directory: ${file.relativePath}`);
8707
+ }
8708
+ return resolved;
8709
+ }
8710
+ function restoreFromBlobs(manifest, blobs, targetDir, opts = {}) {
8711
+ let filesWritten = 0;
8712
+ let filesUnchanged = 0;
8713
+ let filesSkipped = 0;
8714
+ for (const file of manifest.files) {
8715
+ const content = blobs.get(file.hash);
8716
+ if (!content) {
8717
+ throw new Error(`Blob not found for hash: ${file.hash} (${file.relativePath})`);
8718
+ }
8719
+ const targetPath = resolveBackupFileTarget(file, targetDir);
8720
+ if (existsSync2(targetPath)) {
8721
+ const sameSize = statSync2(targetPath).size === content.length;
8722
+ if (sameSize && readFileSync22(targetPath).equals(content)) {
8723
+ filesUnchanged++;
8724
+ continue;
8725
+ }
8726
+ if (!opts.overwrite) {
8727
+ filesSkipped++;
8728
+ continue;
8729
+ }
8730
+ }
8731
+ mkdirSync(dirname2(targetPath), {
8732
+ recursive: true
8733
+ });
8734
+ writeFileSync(targetPath, content);
8735
+ filesWritten++;
8736
+ }
8737
+ return {
8738
+ filesWritten,
8739
+ filesUnchanged,
8740
+ filesSkipped
8741
+ };
8742
+ }
8743
+ function normalizeWorkspaceRelativePath(value3) {
8744
+ if (!value3 || value3.includes("\0")) return void 0;
8745
+ const withPortableSeparators = value3.replace(/\\/g, "/");
8746
+ if (withPortableSeparators.startsWith("/") || WINDOWS_ABSOLUTE_PATH.test(withPortableSeparators)) return void 0;
8747
+ const normalized = posix.normalize(withPortableSeparators);
8748
+ if (!normalized || normalized === "." || normalized === ".." || normalized.startsWith("../")) return void 0;
8749
+ return normalized;
8750
+ }
8751
+ function isCredentialPersistencePath(value3) {
8752
+ const normalized = normalizeWorkspaceRelativePath(value3);
8753
+ return normalized !== void 0 && CREDENTIAL_PATHS.has(normalized.toLowerCase());
8754
+ }
8755
+ async function pushAgentBackup(opts) {
8756
+ const snapshot = walkWorkspace(opts.workspaceDir, opts.walkOptions);
8757
+ const client = new BackupHttpClient(opts.http);
8758
+ const uniqueHashes = [
8759
+ ...new Set(snapshot.files.map((f) => f.hash))
8760
+ ];
8761
+ let filesUploaded = 0;
8762
+ if (uniqueHashes.length > 0) {
8763
+ const checked = await client.checkBlobsExist(uniqueHashes);
8764
+ if (checked.missing.length > 0) {
8765
+ const urls = await client.getBlobUploadUrls(checked.missing);
8766
+ filesUploaded = await uploadBlobs({
8767
+ uploadUrls: urls.urls,
8768
+ contentByHash: snapshot.contentByHash,
8769
+ fetch: opts.http.fetch,
8770
+ concurrency: opts.concurrency
8771
+ });
8772
+ }
8773
+ }
8774
+ const metadata = await client.saveManifest({
8775
+ projectHash: snapshot.projectHash,
8776
+ files: snapshot.files,
8777
+ version: opts.version,
8778
+ orgId: opts.orgId,
8779
+ createdBy: opts.createdBy ?? "cli",
8780
+ triggeredBy: opts.triggeredBy
8781
+ });
8782
+ return {
8783
+ projectHash: snapshot.projectHash,
8784
+ fileCount: snapshot.files.length,
8785
+ filesUploaded,
8786
+ activeVersion: metadata.activeVersion,
8787
+ metadata,
8788
+ files: snapshot.files
8789
+ };
8790
+ }
8791
+ async function pullAgentBackup(opts) {
8792
+ const client = new BackupHttpClient(opts.http);
8793
+ const manifest = await client.getManifest();
8794
+ const uniqueHashes = [
8795
+ ...new Set(manifest.files.map((f) => f.hash))
8796
+ ];
8797
+ const blobs = uniqueHashes.length ? await (async () => {
8798
+ const urls = await client.getBlobUrls(uniqueHashes);
8799
+ return downloadBlobs({
8800
+ urls: urls.urls,
8801
+ fetch: opts.http.fetch,
8802
+ concurrency: opts.concurrency
8803
+ });
8804
+ })() : /* @__PURE__ */ new Map();
8805
+ const result = restoreFromBlobs(manifest, blobs, opts.targetDir, opts.restore);
8806
+ return {
8807
+ ...result,
8808
+ manifest
8809
+ };
8810
+ }
8811
+ function encodeWorkspaceArchive(workspaceDir) {
8812
+ const files = {};
8813
+ try {
8814
+ walk2(workspaceDir, "", files);
8815
+ } catch {
8816
+ return null;
8817
+ }
8818
+ if (Object.keys(files).length === 0) return null;
8819
+ const gz = gzipSync2(Buffer.from(JSON.stringify(files), "utf8"));
8820
+ return gz.toString("base64");
8821
+ }
8822
+ function decodeWorkspaceArchive(archive, schemaVersion) {
8823
+ if (schemaVersion !== void 0 && schemaVersion !== ARCHIVE_SCHEMA_VERSION) {
8824
+ throw new Error(`Unsupported archive schema version: ${schemaVersion} (expected ${ARCHIVE_SCHEMA_VERSION})`);
8825
+ }
8826
+ const raw = gunzipSync2(Buffer.from(archive, "base64")).toString("utf8");
8827
+ const parsed = JSON.parse(raw);
8828
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
8829
+ throw new Error("Decoded archive is not a JSON object map");
8830
+ }
8831
+ return parsed;
8832
+ }
8833
+ function writeArchiveToWorkspace(workspaceDir, files) {
8834
+ const writes = [];
8835
+ for (const [relPath, content] of Object.entries(files)) {
8836
+ const normalized = normalizeWorkspaceRelativePath(relPath);
8837
+ if (!normalized) throw new Error(`Refusing to write invalid archived path: ${relPath}`);
8838
+ if (isCredentialPersistencePath(normalized)) {
8839
+ throw new Error(`Refusing to restore credential file: ${normalized}`);
8840
+ }
8841
+ writes.push({
8842
+ path: resolve2(workspaceDir, normalized),
8843
+ content
8844
+ });
8845
+ }
8846
+ for (const write of writes) {
8847
+ mkdirSync2(dirname22(write.path), {
8848
+ recursive: true
8849
+ });
8850
+ writeFileSync2(write.path, write.content, "utf8");
8851
+ }
8852
+ }
8853
+ function walk2(root, prefix, out) {
8854
+ const dir = prefix ? join32(root, prefix) : root;
8855
+ const entries = readdirSync2(dir, {
8856
+ withFileTypes: true
8857
+ });
8858
+ for (const entry of entries) {
8859
+ if (entry.isDirectory()) {
8860
+ if (shouldSkipDirectory(entry.name) || ARCHIVE_ONLY_SKIP_DIRECTORIES.has(entry.name) || shouldSkipFile(entry.name)) continue;
8861
+ walk2(root, prefix ? `${prefix}${sep3}${entry.name}` : entry.name, out);
8862
+ continue;
8863
+ }
8864
+ if (!entry.isFile()) continue;
8865
+ if (shouldSkipFile(entry.name)) continue;
8866
+ const relPath = (prefix ? `${prefix}${sep3}${entry.name}` : entry.name).split(sep3).join("/");
8867
+ const abs = join32(dir, entry.name);
8868
+ if (statSync3(abs).size > DEFAULT_MAX_FILE_BYTES) continue;
8869
+ try {
8870
+ out[relPath] = readFileSync32(abs, "utf8");
8871
+ } catch {
8872
+ }
8873
+ }
8874
+ }
8875
+ var __defProp4, __name4, FILE_HASH_LENGTH, SKIP_DIRECTORIES, ARCHIVE_ONLY_SKIP_DIRECTORIES, DEFAULT_MAX_FILE_BYTES, KIND_BY_EXT, CHECK_BLOBS_MAX_HASHES, BackupHttpError, BackupHttpClient, DEFAULT_CONCURRENCY, CREDENTIAL_PATHS, WINDOWS_ABSOLUTE_PATH, ARCHIVE_SCHEMA_VERSION;
8876
+ var init_dist3 = __esm({
8877
+ "../shared-source-sync/dist/index.mjs"() {
8878
+ "use strict";
8879
+ __defProp4 = Object.defineProperty;
8880
+ __name4 = /* @__PURE__ */ __name((target, value3) => __defProp4(target, "name", { value: value3, configurable: true }), "__name");
8881
+ FILE_HASH_LENGTH = 16;
8882
+ __name(hashContentTruncated, "hashContentTruncated");
8883
+ __name4(hashContentTruncated, "hashContentTruncated");
8884
+ __name(sha256Hex, "sha256Hex");
8885
+ __name4(sha256Hex, "sha256Hex");
8886
+ __name(matchesFileHash, "matchesFileHash");
8887
+ __name4(matchesFileHash, "matchesFileHash");
8888
+ __name(combineFileHashes, "combineFileHashes");
8889
+ __name4(combineFileHashes, "combineFileHashes");
8890
+ SKIP_DIRECTORIES = /* @__PURE__ */ new Set([
8891
+ "node_modules",
8892
+ "dist",
8893
+ "dist-v2",
8894
+ ".git",
8895
+ ".lua",
8896
+ ".temp",
8897
+ "coverage",
8898
+ ".next",
8899
+ ".turbo"
8900
+ ]);
8901
+ ARCHIVE_ONLY_SKIP_DIRECTORIES = /* @__PURE__ */ new Set([
8902
+ "build"
8903
+ ]);
8904
+ DEFAULT_MAX_FILE_BYTES = 256 * 1024;
8905
+ __name(shouldSkipDirectory, "shouldSkipDirectory");
8906
+ __name4(shouldSkipDirectory, "shouldSkipDirectory");
8907
+ __name(shouldSkipFile, "shouldSkipFile");
8908
+ __name4(shouldSkipFile, "shouldSkipFile");
8909
+ KIND_BY_EXT = {
8910
+ ".ts": "source",
8911
+ ".tsx": "source",
8912
+ ".js": "source",
8913
+ ".jsx": "source",
8914
+ ".yaml": "config",
8915
+ ".yml": "config",
8916
+ ".json": "config",
8917
+ ".toml": "config"
8918
+ };
8919
+ __name(classifyFile, "classifyFile");
8920
+ __name4(classifyFile, "classifyFile");
8921
+ __name(walkWorkspace, "walkWorkspace");
8922
+ __name4(walkWorkspace, "walkWorkspace");
8923
+ CHECK_BLOBS_MAX_HASHES = 500;
8924
+ BackupHttpError = class extends Error {
8925
+ static {
8926
+ __name(this, "BackupHttpError");
8927
+ }
8928
+ static {
8929
+ __name4(this, "BackupHttpError");
8930
+ }
8931
+ status;
8932
+ endpoint;
8933
+ body;
8934
+ constructor(message, status, endpoint, body) {
8935
+ super(message), this.status = status, this.endpoint = endpoint, this.body = body;
8936
+ this.name = "BackupHttpError";
8937
+ }
8938
+ };
8939
+ BackupHttpClient = class {
8940
+ static {
8941
+ __name(this, "BackupHttpClient");
8942
+ }
8943
+ static {
8944
+ __name4(this, "BackupHttpClient");
8945
+ }
8946
+ options;
8947
+ fetchFn;
8948
+ constructor(options) {
8949
+ this.options = options;
8950
+ this.fetchFn = options.fetch ?? fetch;
8951
+ }
8952
+ url(path3) {
8953
+ const base = this.options.baseUrl.replace(/\/$/, "");
8954
+ return `${base}/developer/agents/${encodeURIComponent(this.options.agentId)}${path3}`;
8955
+ }
8956
+ async json(method, path3, body) {
8957
+ const endpoint = this.url(path3);
8958
+ const res = await this.fetchFn(endpoint, {
8959
+ method,
8960
+ headers: {
8961
+ Authorization: this.options.authHeader,
8962
+ ...body !== void 0 ? {
8963
+ "Content-Type": "application/json"
8964
+ } : {},
8965
+ Accept: "application/json"
8966
+ },
8967
+ body: body !== void 0 ? JSON.stringify(body) : void 0
8968
+ });
8969
+ if (!res.ok) {
8970
+ let text2;
8971
+ try {
8972
+ text2 = await res.text();
8973
+ } catch {
8974
+ text2 = void 0;
8975
+ }
8976
+ throw new BackupHttpError(`Backup request failed: ${method} ${path3} \u2192 ${res.status} ${res.statusText}`, res.status, endpoint, text2);
8977
+ }
8978
+ const text = await res.text();
8979
+ if (!text) return void 0;
8980
+ return JSON.parse(text);
8981
+ }
8982
+ /**
8983
+ * `POST /backup/check-blobs` — returns existing vs missing partition.
8984
+ *
8985
+ * LUA-675: the server caps one request at `CHECK_BLOBS_MAX_HASHES`; a
8986
+ * larger set is sent in sequential chunks and the partitions merged, so a
8987
+ * big workspace still pushes and the client never fans out on its own.
8988
+ */
8989
+ async checkBlobsExist(hashes) {
8990
+ if (hashes.length <= CHECK_BLOBS_MAX_HASHES) {
8991
+ return this.json("POST", "/backup/check-blobs", {
8992
+ hashes
8993
+ });
8994
+ }
8995
+ const merged = {
8996
+ missing: [],
8997
+ existing: [],
8998
+ results: {}
8999
+ };
9000
+ let unverified;
9001
+ for (let i = 0; i < hashes.length; i += CHECK_BLOBS_MAX_HASHES) {
9002
+ const part = await this.json("POST", "/backup/check-blobs", {
9003
+ hashes: hashes.slice(i, i + CHECK_BLOBS_MAX_HASHES)
9004
+ });
9005
+ merged.missing.push(...part.missing);
9006
+ merged.existing.push(...part.existing);
9007
+ Object.assign(merged.results, part.results);
9008
+ if (part.unverified) unverified = [
9009
+ ...unverified ?? [],
9010
+ ...part.unverified
9011
+ ];
9012
+ }
9013
+ return unverified ? {
9014
+ ...merged,
9015
+ unverified
9016
+ } : merged;
9017
+ }
9018
+ /** `POST /backup/blob-upload-urls` — presigned S3 PUT URLs. */
9019
+ getBlobUploadUrls(hashes) {
9020
+ return this.json("POST", "/backup/blob-upload-urls", {
9021
+ hashes
9022
+ });
9023
+ }
9024
+ /** `POST /backup/blob-urls` — presigned S3 GET URLs (for restore). */
9025
+ getBlobUrls(hashes) {
9026
+ return this.json("POST", "/backup/blob-urls", {
9027
+ hashes
9028
+ });
9029
+ }
9030
+ /** `POST /backup/manifest` — final step in a push; returns server metadata. */
9031
+ saveManifest(data) {
9032
+ return this.json("POST", "/backup/manifest", data);
9033
+ }
9034
+ /** `GET /backup` — metadata only, no file list. */
9035
+ getMetadata() {
9036
+ return this.json("GET", "/backup");
9037
+ }
9038
+ /** `GET /backup/manifest` — metadata + file list. */
9039
+ getManifest() {
9040
+ return this.json("GET", "/backup/manifest");
9041
+ }
9042
+ /** `GET /backup/check/:hash` — fast freshness probe. */
9043
+ checkBackupExists(hash) {
9044
+ return this.json("GET", `/backup/check/${encodeURIComponent(hash)}`);
9045
+ }
9046
+ };
9047
+ DEFAULT_CONCURRENCY = 10;
9048
+ __name(uploadBlobs, "uploadBlobs");
9049
+ __name4(uploadBlobs, "uploadBlobs");
9050
+ __name(decodeBlob, "decodeBlob");
9051
+ __name4(decodeBlob, "decodeBlob");
9052
+ __name(verifyDownloaded, "verifyDownloaded");
9053
+ __name4(verifyDownloaded, "verifyDownloaded");
9054
+ __name(downloadBlobs, "downloadBlobs");
9055
+ __name4(downloadBlobs, "downloadBlobs");
9056
+ __name(resolveBackupFileTarget, "resolveBackupFileTarget");
9057
+ __name4(resolveBackupFileTarget, "resolveBackupFileTarget");
9058
+ __name(restoreFromBlobs, "restoreFromBlobs");
9059
+ __name4(restoreFromBlobs, "restoreFromBlobs");
9060
+ CREDENTIAL_PATHS = /* @__PURE__ */ new Set([
9061
+ ".env",
9062
+ ".lua/config.json",
9063
+ ".claude/settings.local.json"
9064
+ ]);
9065
+ WINDOWS_ABSOLUTE_PATH = /^[a-z]:\//i;
9066
+ __name(normalizeWorkspaceRelativePath, "normalizeWorkspaceRelativePath");
9067
+ __name4(normalizeWorkspaceRelativePath, "normalizeWorkspaceRelativePath");
9068
+ __name(isCredentialPersistencePath, "isCredentialPersistencePath");
9069
+ __name4(isCredentialPersistencePath, "isCredentialPersistencePath");
9070
+ __name(pushAgentBackup, "pushAgentBackup");
9071
+ __name4(pushAgentBackup, "pushAgentBackup");
9072
+ __name(pullAgentBackup, "pullAgentBackup");
9073
+ __name4(pullAgentBackup, "pullAgentBackup");
9074
+ ARCHIVE_SCHEMA_VERSION = 1;
9075
+ __name(encodeWorkspaceArchive, "encodeWorkspaceArchive");
9076
+ __name4(encodeWorkspaceArchive, "encodeWorkspaceArchive");
9077
+ __name(decodeWorkspaceArchive, "decodeWorkspaceArchive");
9078
+ __name4(decodeWorkspaceArchive, "decodeWorkspaceArchive");
9079
+ __name(writeArchiveToWorkspace, "writeArchiveToWorkspace");
9080
+ __name4(writeArchiveToWorkspace, "writeArchiveToWorkspace");
9081
+ __name(walk2, "walk");
9082
+ __name4(walk2, "walk");
9083
+ }
9084
+ });
9085
+
7529
9086
  // src/api/backup.api.service.ts
7530
9087
  var init_backup_api_service = __esm({
7531
9088
  "src/api/backup.api.service.ts"() {
7532
9089
  "use strict";
7533
9090
  init_http_client();
9091
+ init_dist3();
7534
9092
  }
7535
9093
  });
7536
9094
 
@@ -7619,6 +9177,7 @@ var init_base_handler = __esm({
7619
9177
  init_bundle_upload();
7620
9178
  init_semver();
7621
9179
  init_auth_error();
9180
+ init_cli_error();
7622
9181
  DEFAULT_VERSION = SKILL_DEFAULTS.VERSION;
7623
9182
  BaseVersionedHandler = class {
7624
9183
  static {
@@ -7700,7 +9259,7 @@ var init_base_handler = __esm({
7700
9259
  serverItems
7701
9260
  };
7702
9261
  } catch (error) {
7703
- if (AuthenticationError.isAuthenticationError(error)) throw error;
9262
+ if (AuthenticationError.isAuthenticationError(error) || isAccessDeniedError(error)) throw error;
7704
9263
  return {
7705
9264
  serverItems: null,
7706
9265
  fetchError: error instanceof Error ? error.message : String(error)
@@ -7730,13 +9289,24 @@ var init_base_handler = __esm({
7730
9289
  }
7731
9290
  const yamlItems = this.getFromYaml(config);
7732
9291
  const { yamlById, yamlByName, serverByName } = this.buildMaps(serverData.serverItems, yamlItems);
9292
+ const idField = this.yamlConfig.idField;
9293
+ const manifestNames = manifest ? new Set(getPrimitivesByKind(manifest, this.kind).map((p) => p.name)) : /* @__PURE__ */ new Set();
9294
+ for (const stale of this.staleYamlRows(yamlItems, serverData.serverItems, manifestNames)) {
9295
+ const idx = yamlItems.indexOf(stale);
9296
+ if (idx < 0) continue;
9297
+ const { [idField]: goneId, ...rest } = stale;
9298
+ yamlItems[idx] = rest;
9299
+ yamlUpdated = true;
9300
+ const msg = `\u2139\uFE0F ${this.displayName} "${stale.name}" (${goneId}) no longer exists on the server \u2014 re-registering it`;
9301
+ messages.push(msg);
9302
+ console.log(msg);
9303
+ }
7733
9304
  const orphans = serverData.serverItems.filter((item) => {
7734
9305
  const id = item.id;
7735
9306
  const name = item.name;
7736
9307
  return !yamlById.has(id) && !yamlByName.has(name) && this.isActive(item) && this.shouldConsiderForOrphan(item);
7737
9308
  });
7738
9309
  if (orphans.length > 0) {
7739
- const idField = this.yamlConfig.idField;
7740
9310
  const stubs = orphans.map((item) => this.cleanItem({
7741
9311
  name: item.name,
7742
9312
  version: this.getActiveVersion(item) || DEFAULT_VERSION,
@@ -7779,6 +9349,7 @@ var init_base_handler = __esm({
7779
9349
  console.log(`\u2705 Server ${this.displayNamePlural} and YAML are fully in sync`);
7780
9350
  }
7781
9351
  } catch (error) {
9352
+ if (AuthenticationError.isAuthenticationError(error) || isAccessDeniedError(error)) throw error;
7782
9353
  console.error(`\u274C Error syncing server ${this.displayNamePlural}:`, error);
7783
9354
  }
7784
9355
  return {
@@ -7835,6 +9406,7 @@ var init_base_handler = __esm({
7835
9406
  console.error(` \u274C Failed to create "${item.name}" - no ID returned`);
7836
9407
  }
7837
9408
  } catch (error) {
9409
+ if (AuthenticationError.isAuthenticationError(error) || isAccessDeniedError(error)) throw error;
7838
9410
  console.error(` \u274C Failed to create "${item.name}": ${error instanceof Error ? error.message : error}`);
7839
9411
  }
7840
9412
  }
@@ -7969,6 +9541,15 @@ var init_base_handler = __esm({
7969
9541
  getItemId(item) {
7970
9542
  return item[this.yamlConfig.idField] || "";
7971
9543
  }
9544
+ /**
9545
+ * LUA-750: the yaml rows whose server id is gone (deleted server-side) and which `applySyncToYaml` should
9546
+ * re-register. Default none — a handler whose `fetchFromServer` lists EVERY live row of its kind for the
9547
+ * agent overrides this (a kind whose list omits inactive rows must not, or it would duplicate them).
9548
+ * `manifestNames` is the set of primitives in local code: only those are worth re-creating.
9549
+ */
9550
+ staleYamlRows(_yamlItems, _serverItems, _manifestNames) {
9551
+ return [];
9552
+ }
7972
9553
  buildMaps(serverItems, yamlItems) {
7973
9554
  const yamlById = /* @__PURE__ */ new Map();
7974
9555
  const yamlByName = /* @__PURE__ */ new Map();
@@ -8367,6 +9948,7 @@ var init_cli = __esm({
8367
9948
  "src/utils/cli.ts"() {
8368
9949
  "use strict";
8369
9950
  init_auth_error();
9951
+ init_cli_error();
8370
9952
  init_version_check();
8371
9953
  init_package_root();
8372
9954
  init_analytics();
@@ -8386,6 +9968,7 @@ var init_command_utils = __esm({
8386
9968
  init_request_credential();
8387
9969
  init_files();
8388
9970
  init_cli();
9971
+ init_cli_error();
8389
9972
  __name(requireAuth, "requireAuth");
8390
9973
  }
8391
9974
  });
@@ -11934,7 +13517,10 @@ var init_workflow_api_service = __esm({
11934
13517
  async getRunReplayBundle(runId) {
11935
13518
  return this.httpGet(`${this.runs}/${runId}/journal?format=replay`, await this.auth());
11936
13519
  }
11937
- /** R11 — cancel (`mode:'request'` default; `'force'` after `forceAvailableAt`, 409 `FORCE_NOT_YET_AVAILABLE` before). */
13520
+ /**
13521
+ * R11 — cancel (`mode:'request'` default). A `'force'` before `forceAvailableAt` is the 200 verdict
13522
+ * `{ transitioned:false, nextAction:'cancel_again', forceAvailableAt }` (PRO-979) — never a 409 (LUA-748).
13523
+ */
11938
13524
  async cancelRun(runId, data = {}) {
11939
13525
  return this.httpPost(`${this.runs}/${runId}/cancel`, data, await this.auth());
11940
13526
  }
@@ -12013,6 +13599,14 @@ var init_workflow_api_service = __esm({
12013
13599
  async closeGoal(goalId, data = {}) {
12014
13600
  return this.httpPost(`${this.goals}/${encodeURIComponent(goalId)}/close`, data, await this.auth());
12015
13601
  }
13602
+ /** LUA-749 R63 — edit (objective / judge / cadence / caps / note); a `budget` / `max_runs` park re-arms when the cap clears (`rearmed:true`). */
13603
+ async updateGoal(goalId, data) {
13604
+ return this.httpPatch(`${this.goals}/${encodeURIComponent(goalId)}`, data, await this.auth());
13605
+ }
13606
+ /** LUA-749 R64 — raise `maxTotalCredits` / `maxRuns` (increases only; 400 `GOAL_RAISE_BELOW_SPENT{field, value, spent}`). */
13607
+ async raiseGoal(goalId, data) {
13608
+ return this.httpPost(`${this.goals}/${encodeURIComponent(goalId)}/raise`, data, await this.auth());
13609
+ }
12016
13610
  // ─── Schedules (R4-MF-2 list/get + R28 delete — `/workflows/:agentId/schedules`; LUA-627 stanza) ───
12017
13611
  /** Schedule tree (09 §9.5 — the write-only R27/R56/R28 family plus the R4-MF-2 read rows). */
12018
13612
  get schedules() {
@@ -12026,7 +13620,7 @@ var init_workflow_api_service = __esm({
12026
13620
  async getSchedule(jobId) {
12027
13621
  return this.httpGet(`${this.schedules}/${encodeURIComponent(jobId)}`, await this.auth());
12028
13622
  }
12029
- /** R28 — delete a schedule Job (404 `SCHEDULE_NOT_FOUND`). The CLI refuses a goal-owned job BEFORE this call (`goal_schedule`). */
13623
+ /** 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). */
12030
13624
  async deleteSchedule(jobId) {
12031
13625
  return this.httpDelete(`${this.schedules}/${encodeURIComponent(jobId)}`, await this.auth());
12032
13626
  }