lua-cli 3.31.0 → 3.32.1

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.
@@ -514,7 +514,6 @@ function luaClientMetricLabels(client) {
514
514
  const canonical2 = parseLuaClientHeader(serializeLuaClientHeader(client));
515
515
  return {
516
516
  client_family: canonical2?.app ?? "unknown",
517
- client_version: canonical2?.version ?? "unknown",
518
517
  client_attribution: canonical2?.attribution ?? "unknown"
519
518
  };
520
519
  }
@@ -582,6 +581,59 @@ function scheduledTimeKey(scheduledTime) {
582
581
  function scheduledWorkflowRunIdForTime(jobId, scheduledTime) {
583
582
  return scheduledWorkflowRunId(jobId, scheduledTime);
584
583
  }
584
+ function groupCount(re) {
585
+ let n2 = GROUP_COUNT.get(re);
586
+ if (n2 === void 0) {
587
+ n2 = new RegExp(`${re.source}|`, re.flags.replace("g", "")).exec("").length - 1;
588
+ GROUP_COUNT.set(re, n2);
589
+ }
590
+ return n2;
591
+ }
592
+ function applyPatterns(text, patterns) {
593
+ let out = text;
594
+ for (const { re, suffix } of patterns) {
595
+ re.lastIndex = 0;
596
+ if (!re.test(out)) continue;
597
+ re.lastIndex = 0;
598
+ const groups = groupCount(re);
599
+ out = out.replace(re, (...args) => {
600
+ const kept = args.slice(1, 1 + groups).map((g) => typeof g === "string" ? g : "");
601
+ if (suffix && kept.length > 0) {
602
+ const tail = kept[kept.length - 1];
603
+ return `${kept.slice(0, -1).join("")}${REDACTED_PLACEHOLDER}${tail}`;
604
+ }
605
+ return `${kept.join("")}${REDACTED_PLACEHOLDER}`;
606
+ });
607
+ }
608
+ return out;
609
+ }
610
+ function scrubSecretText(text) {
611
+ if (typeof text !== "string" || text.length < 4) return text;
612
+ return applyPatterns(applyPatterns(text, SECRET_LITERAL_PATTERNS), SECRET_PAIR_PATTERNS);
613
+ }
614
+ function scrubSecretLines(lines) {
615
+ return lines.map((l) => typeof l === "string" ? scrubSecretText(l) : l);
616
+ }
617
+ function messageText(value3) {
618
+ if (typeof value3 === "string") return value3;
619
+ if (value3 instanceof Error) return value3.message;
620
+ if (value3 && typeof value3 === "object") {
621
+ const m = value3.message;
622
+ if (typeof m === "string") return m;
623
+ try {
624
+ return JSON.stringify(value3);
625
+ } catch {
626
+ return "";
627
+ }
628
+ }
629
+ return value3 === void 0 || value3 === null ? "" : String(value3);
630
+ }
631
+ function scrubProviderMessage(raw, max = PROVIDER_MESSAGE_MAX_CHARS) {
632
+ const text = messageText(raw).replace(/\s+/g, " ").trim();
633
+ if (!text) return void 0;
634
+ const out = scrubSecretText(text);
635
+ return out.length > max ? `${out.slice(0, max - 1)}\u2026` : out;
636
+ }
585
637
  function isWorkflowAuditEvent(action) {
586
638
  return typeof action === "string" && WORKFLOW_AUDIT_EVENTS.includes(action);
587
639
  }
@@ -676,7 +728,7 @@ ${PREAMBLE}
676
728
 
677
729
  ${items.join("\n\n")}`;
678
730
  }
679
- 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, 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_SIGNAL_PAYLOAD_MAX_BYTES, WORKFLOW_AUDIT_EVENTS, WORKFLOW_AUDIT_METADATA_MAX_BYTES, INDENT, WRAP_WIDTH, NOUNS, GET_TOOL_NAMES, PREAMBLE;
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;
680
732
  var init_dist = __esm({
681
733
  "../shared-types/dist/index.mjs"() {
682
734
  "use strict";
@@ -1688,6 +1740,27 @@ This text is who you are for this person. As you learn them, their name, their w
1688
1740
  ...WORKFLOW_RUN_IDLE,
1689
1741
  ...WORKFLOW_RUN_TERMINAL
1690
1742
  ];
1743
+ WORKFLOW_STEP_STATUSES = [
1744
+ "pending",
1745
+ "ready",
1746
+ "waiting",
1747
+ "dispatched",
1748
+ "claimed",
1749
+ "running",
1750
+ "suspended",
1751
+ "cancellation_requested",
1752
+ "completed",
1753
+ "failed",
1754
+ "skipped",
1755
+ "cancelled",
1756
+ "timeout",
1757
+ "reaped"
1758
+ ];
1759
+ WORKFLOW_STEP_IN_FLIGHT = [
1760
+ "claimed",
1761
+ "running",
1762
+ "cancellation_requested"
1763
+ ];
1691
1764
  ARCHIVE_WINDOW_MARGIN_DAYS = 7;
1692
1765
  __name(shouldSkipArchive, "shouldSkipArchive");
1693
1766
  __name2(shouldSkipArchive, "shouldSkipArchive");
@@ -1717,11 +1790,90 @@ This text is who you are for this person. As you learn them, their name, their w
1717
1790
  __name2(workflowOccurrenceId, "workflowOccurrenceId");
1718
1791
  __name(workflowOrgSlotKey, "workflowOrgSlotKey");
1719
1792
  __name2(workflowOrgSlotKey, "workflowOrgSlotKey");
1793
+ WORKFLOW_CONNECTION_KEY_RE = /^[a-z][a-z0-9_-]{0,63}$/;
1720
1794
  __name(scheduledTimeKey, "scheduledTimeKey");
1721
1795
  __name2(scheduledTimeKey, "scheduledTimeKey");
1722
1796
  __name(scheduledWorkflowRunIdForTime, "scheduledWorkflowRunIdForTime");
1723
1797
  __name2(scheduledWorkflowRunIdForTime, "scheduledWorkflowRunIdForTime");
1724
1798
  WORKFLOW_SIGNAL_PAYLOAD_MAX_BYTES = 64 * 1024;
1799
+ REDACTED_PLACEHOLDER = "[REDACTED]";
1800
+ PROVIDER_MESSAGE_MAX_CHARS = 300;
1801
+ SECRET_LITERAL_PATTERNS = [
1802
+ {
1803
+ re: /\b(github_pat_)[A-Za-z0-9_]{16,}/g
1804
+ },
1805
+ {
1806
+ re: /\b(gh[pousr]_)[A-Za-z0-9]{16,}/g
1807
+ },
1808
+ {
1809
+ re: /\b(glpat-)[A-Za-z0-9_-]{16,}/g
1810
+ },
1811
+ {
1812
+ re: /\b(sk-ant-)[A-Za-z0-9_-]{16,}/g
1813
+ },
1814
+ {
1815
+ re: /\b(sk-)(?!ant-)[A-Za-z0-9_-]{20,}/g
1816
+ },
1817
+ {
1818
+ re: /\b(AKIA)[A-Z0-9]{16}\b/g
1819
+ },
1820
+ {
1821
+ re: /\b(xox[abprs]-)[A-Za-z0-9-]{10,}/g
1822
+ },
1823
+ {
1824
+ re: /\b(AIza)[0-9A-Za-z_-]{35}/g
1825
+ },
1826
+ {
1827
+ re: /\b(ya29\.)[A-Za-z0-9_-]{20,}/g
1828
+ },
1829
+ {
1830
+ re: /\b(eyJ)[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}/g
1831
+ },
1832
+ {
1833
+ re: /(-----BEGIN [A-Z ]*PRIVATE KEY-----)[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g
1834
+ },
1835
+ // `https://x-access-token:<token>@github.com/…` (the git credential shape the pod scrubbed already)
1836
+ {
1837
+ re: /\b(x-access-token:)[^@\s]+(@)/g,
1838
+ suffix: true
1839
+ }
1840
+ ];
1841
+ SECRET_NAME = "(?:authorization|proxy-authorization|x-api-key|x-wf-[a-z-]*key|x-internal-auth|api[_-]?key|apikey|access[_-]?key|secret[_-]?key|private[_-]?key|client[_-]?secret|secret|password|passwd|pwd|passphrase|token|credentials?)";
1842
+ SECRET_PAIR_PATTERNS = [
1843
+ // `Authorization: Bearer x`, `x-api-key: x`, `AWS_SECRET_ACCESS_KEY=x`, `SVC_JOB_KEY=x`, `"apiKey": "x"`,
1844
+ // `FOO_TOKEN=x`, `secretKey=x`, `password=x`. Groups: the char before the name, the name, the separator
1845
+ // (with its quotes), the scheme word — all kept; the value goes. A value a literal rule already replaced
1846
+ // (`x-access-token:[REDACTED]@host`) is left alone so the host after it survives.
1847
+ {
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")
1849
+ },
1850
+ // `?token=x`, `&key=x`, `&X-Amz-Signature=x`, `&sig=x`
1851
+ {
1852
+ re: /([?&](?:token|key|api[_-]?key|apikey|access[_-]?token|id[_-]?token|auth|sig|signature|secret|password|pwd|x-amz-signature|x-amz-credential|x-amz-security-token)=)[^&\s"'#]+/gi
1853
+ },
1854
+ // `mongodb+srv://user:pass@host`, `postgres://user:pass@host`, `https://user:pass@host`
1855
+ {
1856
+ re: /(\/\/[^\s/:@]+:)[^\s/@]+(@)/g,
1857
+ suffix: true
1858
+ },
1859
+ // `Basic <base64>` / `bearer <short token>` (the literal list needs ≥ 16 chars; any case)
1860
+ {
1861
+ re: /\b((?:Basic|Bearer)\s+)(?!\[REDACTED\])[A-Za-z0-9+/=_.-]{8,}/gi
1862
+ }
1863
+ ];
1864
+ GROUP_COUNT = /* @__PURE__ */ new WeakMap();
1865
+ __name(groupCount, "groupCount");
1866
+ __name2(groupCount, "groupCount");
1867
+ __name(applyPatterns, "applyPatterns");
1868
+ __name2(applyPatterns, "applyPatterns");
1869
+ __name(scrubSecretText, "scrubSecretText");
1870
+ __name2(scrubSecretText, "scrubSecretText");
1871
+ __name(scrubSecretLines, "scrubSecretLines");
1872
+ __name2(scrubSecretLines, "scrubSecretLines");
1873
+ __name(messageText, "messageText");
1874
+ __name2(messageText, "messageText");
1875
+ __name(scrubProviderMessage, "scrubProviderMessage");
1876
+ __name2(scrubProviderMessage, "scrubProviderMessage");
1725
1877
  WORKFLOW_AUDIT_EVENTS = [
1726
1878
  // --- definitions / versions / templates (13, 11 §11.11.5) ---
1727
1879
  "workflow.published",
@@ -1843,7 +1995,11 @@ listed here; never invent a target.`;
1843
1995
  // ../workflow-graph/dist/index.mjs
1844
1996
  import { createHash } from "crypto";
1845
1997
  import { z as z4 } from "zod";
1998
+ import { z as z22 } from "zod";
1846
1999
  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
+ }
1847
2003
  function fillPolicy(node, defaultTimeout) {
1848
2004
  if (node.tier === void 0 && node.workspace !== void 0 && node.workspace !== "inherit") node.tier = "job";
1849
2005
  if (node.timeoutSeconds === void 0) node.timeoutSeconds = node.tier === "job" ? WORKFLOW_JOB_DEFAULT_TIMEOUT_SECONDS : defaultTimeout;
@@ -1938,6 +2094,12 @@ function withDefaultsFilled(g) {
1938
2094
  out.definition.graph.forEach(fillEntry);
1939
2095
  return out;
1940
2096
  }
2097
+ function isConnectionKeyShaped(value22) {
2098
+ return WORKFLOW_CONNECTION_KEY_RE.test(value22) && !CONNECTION_ID_HEX_RE.test(value22);
2099
+ }
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`;
2102
+ }
1941
2103
  function classifyModelProvider(model) {
1942
2104
  const m = (model ?? "").trim().toLowerCase();
1943
2105
  if (!m) return null;
@@ -2040,6 +2202,33 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
2040
2202
  if (envelopeWorkspace?.backend && envelopeWorkspace.backend !== "ebs" && opts.policy?.workspaceBackends && !opts.policy.workspaceBackends.includes(envelopeWorkspace.backend)) {
2041
2203
  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");
2042
2204
  }
2205
+ const declaredKeys = new Set(opts.connectionKeys ?? []);
2206
+ if (g.connections !== void 0 && !Array.isArray(g.connections)) {
2207
+ err("connection-declaration-invalid", "`connections` must be an array of { key, integrationType }", "connections");
2208
+ }
2209
+ (Array.isArray(g.connections) ? g.connections : []).forEach((c, i) => {
2210
+ const path3 = `connections.${i}`;
2211
+ const key = c?.key;
2212
+ const integrationType = c?.integrationType;
2213
+ if (typeof key !== "string" || !WORKFLOW_CONNECTION_KEY_RE.test(key)) {
2214
+ err("connection-declaration-invalid", `connections[${i}].key must match ${WORKFLOW_CONNECTION_KEY_RE}`, `${path3}.key`);
2215
+ return;
2216
+ }
2217
+ if (declaredKeys.has(key)) {
2218
+ err("connection-declaration-invalid", `connections[${i}].key "${key}" is declared twice`, `${path3}.key`);
2219
+ return;
2220
+ }
2221
+ if (typeof integrationType !== "string" || !integrationType.trim()) {
2222
+ err("connection-declaration-invalid", `connections[${i}] ("${key}") needs an integrationType (the catalog slug, e.g. 'github')`, `${path3}.integrationType`);
2223
+ return;
2224
+ }
2225
+ declaredKeys.add(key);
2226
+ });
2227
+ const undeclaredKey = /* @__PURE__ */ __name3((ref) => typeof ref === "string" && !declaredKeys.has(ref) && isConnectionKeyShaped(ref) && opts.connectionIds?.has(ref) !== true, "undeclaredKey");
2228
+ const credentialsRef = envelopeWorkspace?.credentialsRef;
2229
+ if (undeclaredKey(credentialsRef)) {
2230
+ err("connection-key-undeclared", connectionKeyUndeclaredMessage("workspace.credentialsRef", credentialsRef), "workspace.credentialsRef");
2231
+ }
2043
2232
  const seen = /* @__PURE__ */ new Map();
2044
2233
  let nodeCount = 0;
2045
2234
  const upstream = /* @__PURE__ */ new Set();
@@ -2051,6 +2240,16 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
2051
2240
  seen.set(id, path3);
2052
2241
  }
2053
2242
  }, "checkId");
2243
+ const checkPolicyEnums = /* @__PURE__ */ __name3((node, path3) => {
2244
+ const id = singleId(node);
2245
+ const check = /* @__PURE__ */ __name3((member, allowed) => {
2246
+ const value22 = node[member];
2247
+ if (value22 === void 0 || typeof value22 === "string" && allowed.includes(value22)) return;
2248
+ err("invalid-envelope", `\`${member}\` must be ${allowed.map((a) => `'${a}'`).join(" | ")} (got ${JSON.stringify(value22)})`, `${path3}.${member}`, id);
2249
+ }, "check");
2250
+ check("sideEffects", WORKFLOW_SIDE_EFFECTS);
2251
+ check("jobResources", WORKFLOW_JOB_RESOURCES);
2252
+ }, "checkPolicyEnums");
2054
2253
  const checkRetry = /* @__PURE__ */ __name3((node, path3) => {
2055
2254
  const r = node.retry;
2056
2255
  if (!r) return;
@@ -2058,6 +2257,9 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
2058
2257
  if (r.backoff !== void 0 && r.backoff !== "fixed" && r.backoff !== "exponential") {
2059
2258
  err("backoff-invalid", `retry.backoff must be 'fixed' | 'exponential'`, `${path3}.retry.backoff`, id);
2060
2259
  }
2260
+ if (r.backoffSeconds !== void 0 && r.backoffSeconds < 0) {
2261
+ err("backoff-invalid", "retry.backoffSeconds must be \u2265 0", `${path3}.retry.backoffSeconds`, id);
2262
+ }
2061
2263
  if (r.maxBackoffSeconds !== void 0) {
2062
2264
  if (r.backoff !== "exponential") {
2063
2265
  err("backoff-invalid", "retry.maxBackoffSeconds is only meaningful with backoff:'exponential'", `${path3}.retry`, id);
@@ -2119,10 +2321,15 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
2119
2321
  }, "checkSpecialistRole");
2120
2322
  const checkRequiredConnections = /* @__PURE__ */ __name3((node, path3) => {
2121
2323
  const required = node.requiredConnections;
2122
- if (!Array.isArray(required) || !opts.connectionIds) return;
2123
- const unknown = required.filter((c) => typeof c !== "string" || !opts.connectionIds.has(c));
2324
+ if (!Array.isArray(required)) return;
2325
+ const undeclared = required.filter(undeclaredKey);
2326
+ if (undeclared.length) {
2327
+ err("connection-key-undeclared", connectionKeyUndeclaredMessage(`${path3}.requiredConnections`, undeclared[0]) + (undeclared.length > 1 ? ` (also undeclared: ${JSON.stringify(undeclared.slice(1))})` : ""), `${path3}.requiredConnections`, singleId(node));
2328
+ }
2329
+ if (!opts.connectionIds) return;
2330
+ const unknown = required.filter((c) => typeof c !== "string" || !declaredKeys.has(c) && !opts.connectionIds.has(c));
2124
2331
  if (unknown.length) {
2125
- err("required-connection-unknown", `requiredConnections ${JSON.stringify(unknown)} are not connections the owner can mount`, `${path3}.requiredConnections`, singleId(node));
2332
+ err("required-connection-unknown", `requiredConnections ${JSON.stringify(unknown)} are neither declared connections[].key values nor connections the owner can mount`, `${path3}.requiredConnections`, singleId(node));
2126
2333
  }
2127
2334
  }, "checkRequiredConnections");
2128
2335
  const checkTier = /* @__PURE__ */ __name3((node, path3) => {
@@ -2208,6 +2415,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
2208
2415
  return;
2209
2416
  }
2210
2417
  checkId(singleId(node), path3);
2418
+ checkPolicyEnums(node, path3);
2211
2419
  checkTimeout(node, path3);
2212
2420
  checkTier(node, path3);
2213
2421
  checkRetry(node, path3);
@@ -2274,6 +2482,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
2274
2482
  case "sleepUntil": {
2275
2483
  const s = entry;
2276
2484
  checkId(s.id, path3);
2485
+ err("node-type-unsupported-by-engine", sleepUntilUnsupportedMessage(s.id), path3, s.id);
2277
2486
  if (s.date === void 0 === (s.dateFrom === void 0)) {
2278
2487
  err("invalid-envelope", "sleepUntil needs exactly one of `date` | `dateFrom`", path3, s.id);
2279
2488
  }
@@ -2319,7 +2528,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
2319
2528
  err("invalid-envelope", "conditional.predicates must match conditional.steps one-to-one", path3);
2320
2529
  }
2321
2530
  (c.predicates ?? []).forEach((p, j) => {
2322
- if (!isPredicate(p)) err("closure-predicate", "a conditional predicate must be a LuaPredicate object, not a function", `${path3}.predicates.${j}`);
2531
+ 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}`);
2323
2532
  });
2324
2533
  c.steps.forEach((arm, j) => {
2325
2534
  checkArm(arm, `${path3}.steps.${j}`, 1);
@@ -2399,7 +2608,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
2399
2608
  if (l.intervalSeconds !== void 0 && (!Number.isInteger(l.intervalSeconds) || l.intervalSeconds < 1 || l.intervalSeconds > caps.maxLoopIntervalSeconds)) {
2400
2609
  err("loop-interval-out-of-range", `loop.intervalSeconds must be an integer in 1..${caps.maxLoopIntervalSeconds}`, `${path3}.intervalSeconds`);
2401
2610
  }
2402
- if (!isPredicate(l.predicate)) err("closure-predicate", "a loop predicate must be a LuaPredicate object, not a function", `${path3}.predicate`);
2611
+ 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`);
2403
2612
  const bodyType = l.step.type;
2404
2613
  if (bodyType === "mapping") err("container-arm-empty", "a loop body needs a step, not a bare mapping", `${path3}.step`);
2405
2614
  else if (bodyType === "approval" || bodyType === "waitForSignal") err("approval-inside-container", "approval / waitForSignal are top-level only in v1", `${path3}.step`);
@@ -2462,6 +2671,41 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
2462
2671
  function isPredicate(p) {
2463
2672
  return typeof p === "object" && p !== null && typeof p.op === "string" && PREDICATE_OPS.has(p.op);
2464
2673
  }
2674
+ function isPathOrLiteral(v) {
2675
+ if (typeof v !== "object" || v === null) return false;
2676
+ const r = v;
2677
+ if ("path" in r) return typeof r.path === "string" && r.path.length > 0;
2678
+ return "literal" in r && isPredicateScalar(r.literal);
2679
+ }
2680
+ function isWellFormedPredicate(p) {
2681
+ if (!isPredicate(p)) return false;
2682
+ const r = p;
2683
+ switch (r.op) {
2684
+ case "eq":
2685
+ case "ne":
2686
+ case "lt":
2687
+ case "lte":
2688
+ case "gt":
2689
+ case "gte":
2690
+ return isPathOrLiteral(r.left) && isPathOrLiteral(r.right);
2691
+ case "in":
2692
+ case "notIn":
2693
+ return isPathOrLiteral(r.value) && Array.isArray(r.set) && r.set.every(isPredicateScalar);
2694
+ case "exists":
2695
+ case "notExists":
2696
+ return typeof r.path === "string" && r.path.length > 0;
2697
+ case "truthy":
2698
+ case "falsy":
2699
+ return isPathOrLiteral(r.value);
2700
+ case "and":
2701
+ case "or":
2702
+ return Array.isArray(r.args) && r.args.every(isWellFormedPredicate);
2703
+ case "not":
2704
+ return isWellFormedPredicate(r.arg);
2705
+ default:
2706
+ return false;
2707
+ }
2708
+ }
2465
2709
  function canonicalJson(value22) {
2466
2710
  const seen = /* @__PURE__ */ new WeakSet();
2467
2711
  const encode = /* @__PURE__ */ __name3((v) => {
@@ -3024,6 +3268,25 @@ function resolveMapping(cfg, ctx) {
3024
3268
  value: result
3025
3269
  };
3026
3270
  }
3271
+ function continuedFailureValue(error, killReason) {
3272
+ const code = typeof error?.code === "string" && error.code || typeof killReason === "string" && killReason || CONTINUED_FAILURE_DEFAULT_CODE;
3273
+ const message = typeof error?.message === "string" && error.message ? error.message : code;
3274
+ return {
3275
+ __lua_workflow: CONTINUED_FAILURE_TAG,
3276
+ failed: true,
3277
+ error: {
3278
+ code,
3279
+ message
3280
+ },
3281
+ text: ""
3282
+ };
3283
+ }
3284
+ function isContinuedFailureValue(v) {
3285
+ if (v === null || typeof v !== "object" || Array.isArray(v)) return false;
3286
+ const o = v;
3287
+ const err = o.error;
3288
+ return o.__lua_workflow === CONTINUED_FAILURE_TAG && o.failed === true && o.text === "" && err !== null && typeof err === "object" && typeof err.code === "string" && typeof err.message === "string";
3289
+ }
3027
3290
  function lowerContainerArm(mapping, step22) {
3028
3291
  const stepId = nodeIdOf(step22);
3029
3292
  return {
@@ -3121,7 +3384,7 @@ function resolvePlacements(calls) {
3121
3384
  if (!d) {
3122
3385
  issues.push({
3123
3386
  code: "unknown-step-ref",
3124
- message: `"${ref.ref}" is not declared anywhere in the chain \u2014 declare it with agentStep/specialistStep/toolStep/map(\u2026, { id })`,
3387
+ message: `"${ref.ref}" is not declared anywhere in the chain \u2014 declare it with agentStep/specialistStep/toolStep/map(\u2026, { id })/workflow(\u2026)`,
3125
3388
  callIndex: i,
3126
3389
  stepId: ref.ref
3127
3390
  });
@@ -3173,9 +3436,9 @@ function resolvePlacements(calls) {
3173
3436
  });
3174
3437
  const graph = [];
3175
3438
  const lookup = /* @__PURE__ */ __name3((ref) => {
3176
- const n = "node" in ref ? ref.node : declared.get(ref.ref)?.node;
3177
- if (!n || !ref.armMap || n.type === "mapping") return n;
3178
- return lowerContainerArm(ref.armMap, n);
3439
+ const n2 = "node" in ref ? ref.node : declared.get(ref.ref)?.node;
3440
+ if (!n2 || !ref.armMap || n2.type === "mapping") return n2;
3441
+ return lowerContainerArm(ref.armMap, n2);
3179
3442
  }, "lookup");
3180
3443
  calls.forEach((call, i) => {
3181
3444
  switch (call.kind) {
@@ -3194,7 +3457,7 @@ function resolvePlacements(calls) {
3194
3457
  return;
3195
3458
  }
3196
3459
  case "parallel": {
3197
- const steps = call.arms.map(lookup).filter((n) => !!n && n.type !== "mapping");
3460
+ const steps = call.arms.map(lookup).filter((n2) => !!n2 && n2.type !== "mapping");
3198
3461
  const node = {
3199
3462
  type: "parallel",
3200
3463
  steps
@@ -3207,9 +3470,9 @@ function resolvePlacements(calls) {
3207
3470
  const steps = [];
3208
3471
  const predicates = [];
3209
3472
  for (const a of call.arms) {
3210
- const n = lookup(a.target);
3211
- if (!n) continue;
3212
- steps.push(n);
3473
+ const n2 = lookup(a.target);
3474
+ if (!n2) continue;
3475
+ steps.push(n2);
3213
3476
  predicates.push(a.predicate);
3214
3477
  }
3215
3478
  const node = {
@@ -3313,31 +3576,54 @@ function seedLedgerFromRun(run, steps, targetPlan, opts = {}) {
3313
3576
  graphChanged
3314
3577
  };
3315
3578
  }
3579
+ function branchSpecFromConditional(entry) {
3580
+ return {
3581
+ arms: entry.steps.map((arm, i) => ({
3582
+ stepId: branchArmId(arm),
3583
+ predicate: entry.predicates[i]
3584
+ })),
3585
+ ...entry.exclusive === true ? {
3586
+ exclusive: true
3587
+ } : {},
3588
+ ...entry.otherwise ? {
3589
+ otherwise: branchArmId(entry.otherwise)
3590
+ } : {}
3591
+ };
3592
+ }
3593
+ function selectBranchArms(spec, ctx) {
3594
+ const taken = [];
3595
+ for (const arm of spec.arms) {
3596
+ if (spec.exclusive && taken.length > 0) break;
3597
+ const hit = arm.predicate ? evaluatePredicate(arm.predicate, ctx) : false;
3598
+ if (hit) taken.push(arm.stepId);
3599
+ }
3600
+ if (taken.length === 0 && spec.otherwise) taken.push(spec.otherwise);
3601
+ return taken;
3602
+ }
3316
3603
  function replayLedger(g, ledger) {
3317
3604
  const plan = compilePlan(g);
3318
3605
  const rows22 = new Map(ledger.steps.map((r) => [
3319
3606
  r.stepId,
3320
3607
  r
3321
3608
  ]));
3322
- const stepResults = {};
3323
- for (const r of ledger.steps) if (r.status === "completed") stepResults[r.stepId] = r.output;
3324
- const ctx = {
3609
+ const requestContext = {
3610
+ runId: "replay",
3611
+ workflowId: g.definition.id,
3612
+ orgId: "",
3613
+ agentId: "",
3614
+ userId: "",
3615
+ trigger: "sdk",
3616
+ threadId: "replay",
3617
+ depth: 0,
3618
+ startedAt: 0,
3619
+ ...ledger.requestContext
3620
+ };
3621
+ const ctxFor = /* @__PURE__ */ __name3((id) => ({
3325
3622
  initData: ledger.initData,
3326
- stepResults,
3623
+ stepResults: ancestorResults(plan, id, rows22),
3327
3624
  state: ledger.state ?? {},
3328
- requestContext: {
3329
- runId: "replay",
3330
- workflowId: g.definition.id,
3331
- orgId: "",
3332
- agentId: "",
3333
- userId: "",
3334
- trigger: "sdk",
3335
- threadId: "replay",
3336
- depth: 0,
3337
- startedAt: 0,
3338
- ...ledger.requestContext
3339
- }
3340
- };
3625
+ requestContext
3626
+ }), "ctxFor");
3341
3627
  const verdicts = [];
3342
3628
  for (const id of plan.order) {
3343
3629
  const node = plan.steps[id];
@@ -3345,17 +3631,7 @@ function replayLedger(g, ledger) {
3345
3631
  if (!recorded || recorded.status === "pending" || recorded.status === "skipped") continue;
3346
3632
  if (node.kind === "branch") {
3347
3633
  const entry = node.entry;
3348
- const taken = [];
3349
- entry.steps.forEach((arm, i) => {
3350
- const pred = entry.predicates[i];
3351
- const hit = pred ? evaluatePredicate(pred, {
3352
- initData: ledger.initData,
3353
- stepResults,
3354
- state: ledger.state
3355
- }) : false;
3356
- if (hit && (!entry.exclusive || taken.length === 0)) taken.push(armId2(arm));
3357
- });
3358
- if (taken.length === 0 && entry.otherwise) taken.push(armId2(entry.otherwise));
3634
+ const taken = selectBranchArms(branchSpecFromConditional(entry), ctxFor(id));
3359
3635
  const recordedTaken = recorded.taken ?? inferTaken(entry, rows22);
3360
3636
  verdicts.push({
3361
3637
  stepId: id,
@@ -3366,7 +3642,7 @@ function replayLedger(g, ledger) {
3366
3642
  });
3367
3643
  } else if (node.kind === "map" && !id.endsWith(".join") && recorded.status === "completed") {
3368
3644
  const entry = node.entry;
3369
- const resolved = resolveMapping(parseMapConfig(entry.mapConfig, id), ctx);
3645
+ const resolved = resolveMapping(parseMapConfig(entry.mapConfig, id), ctxFor(id));
3370
3646
  const local = "error" in resolved ? {
3371
3647
  error: resolved.error,
3372
3648
  key: resolved.key
@@ -3381,7 +3657,7 @@ function replayLedger(g, ledger) {
3381
3657
  } else if (node.kind === "foreach") {
3382
3658
  const entry = node.entry;
3383
3659
  const source = node.dependsOn[0];
3384
- const items = source ? stepResults[source] : void 0;
3660
+ const items = source ? ctxFor(id).stepResults[source] : void 0;
3385
3661
  const local = Array.isArray(items) ? items.length : void 0;
3386
3662
  const recordedCount = recorded.itemCount ?? countChildren(entry, rows22);
3387
3663
  verdicts.push({
@@ -3398,6 +3674,71 @@ function replayLedger(g, ledger) {
3398
3674
  diverged: verdicts.some((v) => v.diverged)
3399
3675
  };
3400
3676
  }
3677
+ function replayResultOf(row, node) {
3678
+ if (!row) return void 0;
3679
+ if (row.status === "completed") return {
3680
+ value: row.output
3681
+ };
3682
+ if (row.status === "failed") {
3683
+ const onError = row.onError ?? node?.entry?.onError;
3684
+ if (onError === "continue") return {
3685
+ value: continuedFailureValue(row.error, row.killReason)
3686
+ };
3687
+ }
3688
+ return void 0;
3689
+ }
3690
+ function ancestorResults(plan, id, rows22) {
3691
+ const out = {};
3692
+ const joinAliased = /* @__PURE__ */ new Set();
3693
+ const seen = /* @__PURE__ */ new Set();
3694
+ const take = /* @__PURE__ */ __name3((rowId) => {
3695
+ const hit = replayResultOf(rows22.get(rowId), plan.steps[rowId]);
3696
+ if (!hit) return void 0;
3697
+ if (!joinAliased.has(rowId)) out[rowId] = hit.value;
3698
+ const entryId = entryOfJoin(rowId);
3699
+ if (entryId) {
3700
+ out[entryId] = hit.value;
3701
+ joinAliased.add(entryId);
3702
+ const entryNode = plan.steps[entryId];
3703
+ if (entryNode?.kind === "foreach") {
3704
+ const body = branchArmId(entryNode.entry.step);
3705
+ out[body] = hit.value;
3706
+ joinAliased.add(body);
3707
+ }
3708
+ }
3709
+ return hit;
3710
+ }, "take");
3711
+ const walk2 = /* @__PURE__ */ __name3((ids) => {
3712
+ for (const dep of ids) {
3713
+ if (seen.has(dep)) continue;
3714
+ seen.add(dep);
3715
+ take(dep);
3716
+ const node = plan.steps[dep] ?? plan.steps[entryOfJoin(dep) ?? ""];
3717
+ if (!node) continue;
3718
+ if (node.kind === "foreach") {
3719
+ const body = branchArmId(node.entry.step);
3720
+ for (const rowId of rows22.keys()) if (rowId.startsWith(`${body}[`)) take(rowId);
3721
+ } else if (node.kind === "loop") {
3722
+ const body = branchArmId(node.entry.step);
3723
+ let best = -1;
3724
+ for (const rowId of rows22.keys()) {
3725
+ if (!rowId.startsWith(`${body}#`)) continue;
3726
+ const n2 = Number(rowId.slice(body.length + 1));
3727
+ if (!Number.isInteger(n2)) continue;
3728
+ const hit = take(rowId);
3729
+ if (!hit) continue;
3730
+ if (n2 > best) {
3731
+ best = n2;
3732
+ out[body] = hit.value;
3733
+ }
3734
+ }
3735
+ }
3736
+ walk2(node.dependsOn);
3737
+ }
3738
+ }, "walk");
3739
+ walk2(plan.steps[id]?.dependsOn ?? []);
3740
+ return out;
3741
+ }
3401
3742
  function inferTaken(entry, rows22) {
3402
3743
  const arms = [
3403
3744
  ...entry.steps,
@@ -3405,16 +3746,16 @@ function inferTaken(entry, rows22) {
3405
3746
  entry.otherwise
3406
3747
  ] : []
3407
3748
  ];
3408
- return arms.map(armId2).filter((id) => {
3749
+ return arms.map(branchArmId).filter((id) => {
3409
3750
  const r = rows22.get(id);
3410
3751
  return r !== void 0 && r.status !== "skipped" && r.status !== "pending";
3411
3752
  });
3412
3753
  }
3413
3754
  function countChildren(entry, rows22) {
3414
- const body = armId2(entry.step);
3415
- let n = 0;
3416
- for (const id of rows22.keys()) if (id.startsWith(`${body}[`)) n += 1;
3417
- return n;
3755
+ const body = branchArmId(entry.step);
3756
+ let n2 = 0;
3757
+ for (const id of rows22.keys()) if (id.startsWith(`${body}[`)) n2 += 1;
3758
+ return n2;
3418
3759
  }
3419
3760
  function isTerminalRunStatus(status) {
3420
3761
  return TERMINAL.has(status);
@@ -3423,19 +3764,48 @@ function pruneUndefined(o) {
3423
3764
  return Object.fromEntries(Object.entries(o).filter(([, v]) => v !== void 0));
3424
3765
  }
3425
3766
  function runNextAction(run) {
3426
- if (isTerminalRunStatus(run.status) || !run.cancel?.requestedAt) return "none";
3767
+ if (isTerminalRunStatus(run.status)) return "none";
3768
+ if (run.status === "suspended" && run.gate?.kind === "budget") return "raise_budget";
3769
+ if (!run.cancel?.requestedAt) return "none";
3427
3770
  const forceAt = run.cancel.forceAfter ?? run.cancel.requestedAt + FORCE_CANCEL_STALE_MS;
3428
3771
  return Date.now() >= forceAt ? "force" : "cancel_again";
3429
3772
  }
3773
+ function emptyRunCounts() {
3774
+ const out = {
3775
+ total: 0,
3776
+ inFlight: 0
3777
+ };
3778
+ for (const status of WORKFLOW_STEP_STATUSES) out[status] = 0;
3779
+ return out;
3780
+ }
3781
+ function runCountsFromStatusTally(tally) {
3782
+ const out = emptyRunCounts();
3783
+ for (const [status, n2] of Object.entries(tally)) {
3784
+ if (!Number.isFinite(n2) || n2 <= 0) continue;
3785
+ out.total += n2;
3786
+ if (WORKFLOW_STEP_STATUSES.includes(status)) out[status] += n2;
3787
+ if (IN_FLIGHT.has(status)) out.inFlight += n2;
3788
+ }
3789
+ return out;
3790
+ }
3791
+ function runCountsFromStepStatuses(statuses) {
3792
+ const tally = {};
3793
+ for (const s of statuses) tally[s] = (tally[s] ?? 0) + 1;
3794
+ return runCountsFromStatusTally(tally);
3795
+ }
3430
3796
  function runCounts(counts) {
3797
+ const c = counts ?? {};
3798
+ const rawInFlight = c.dispatched !== void 0 || c.claimed !== void 0 || c.running !== void 0 || c.cancellation_requested !== void 0;
3431
3799
  return {
3432
- total: counts?.total ?? 0,
3433
- completed: counts?.completed ?? 0,
3434
- failed: counts?.failed ?? 0,
3435
- skipped: counts?.skipped ?? 0,
3436
- running: counts?.inFlight ?? 0,
3437
- suspended: counts?.suspended ?? 0,
3438
- pending: (counts?.pending ?? 0) + (counts?.ready ?? 0) + (counts?.waiting ?? 0)
3800
+ total: n(c.total),
3801
+ completed: n(c.completed),
3802
+ failed: n(c.failed) + n(c.timeout) + n(c.reaped),
3803
+ skipped: n(c.skipped),
3804
+ // LUA-664: cancelled rows never ran — their own wire bucket, never a failure (the detail's map, verbatim).
3805
+ cancelled: n(c.cancelled),
3806
+ running: rawInFlight ? n(c.dispatched) + n(c.claimed) + n(c.running) + n(c.cancellation_requested) : n(c.inFlight),
3807
+ suspended: n(c.suspended),
3808
+ pending: n(c.pending) + n(c.ready) + n(c.waiting)
3439
3809
  };
3440
3810
  }
3441
3811
  function runUsage(run) {
@@ -3459,7 +3829,7 @@ function runWorkspaceView(ws) {
3459
3829
  if (!ws) return void 0;
3460
3830
  const w = ws;
3461
3831
  return pruneUndefined({
3462
- kind: w.kind ?? "empty",
3832
+ kind: w.spec?.kind ?? w.kind ?? "empty",
3463
3833
  backend: w.backend,
3464
3834
  status: String(w.status ?? ""),
3465
3835
  branch: w.branch,
@@ -3521,7 +3891,10 @@ function toWorkflowRunSummary(run) {
3521
3891
  eventSeq: run.eventSeq ?? 0,
3522
3892
  cancellable: !isTerminalRunStatus(status),
3523
3893
  nextAction: runNextAction(run),
3524
- workspace: runWorkspaceView(run.workspace)
3894
+ workspace: runWorkspaceView(run.workspace),
3895
+ // LUA-643: the runs list says a result exists; R4 `fields:'full'` (output-ACL gated, audited) serves it. The
3896
+ // stamped flag is what a list page carries (it projects `output` out); the payload members cover R4's full row.
3897
+ hasOutput: run.hasOutput === true || run.output !== void 0 || run.outputRef !== void 0 ? true : void 0
3525
3898
  });
3526
3899
  }
3527
3900
  function timeZoneSupported(tz) {
@@ -4216,8 +4589,8 @@ function armEntry(arm) {
4216
4589
  }
4217
4590
  function ofEntry(e) {
4218
4591
  if (!e || typeof e !== "object") return ZERO;
4219
- const n = e;
4220
- switch (n.type) {
4592
+ const n2 = e;
4593
+ switch (n2.type) {
4221
4594
  case "agent":
4222
4595
  return {
4223
4596
  steps: {
@@ -4250,17 +4623,17 @@ function ofEntry(e) {
4250
4623
  agentCalls: 0
4251
4624
  };
4252
4625
  case "parallel": {
4253
- const arms = Array.isArray(n.steps) ? n.steps : [];
4626
+ const arms = Array.isArray(n2.steps) ? n2.steps : [];
4254
4627
  return arms.map(armEntry).map(ofEntry).reduce(add, ZERO);
4255
4628
  }
4256
4629
  case "conditional": {
4257
- const arms = (Array.isArray(n.steps) ? n.steps : []).map(armEntry).map(ofEntry);
4630
+ const arms = (Array.isArray(n2.steps) ? n2.steps : []).map(armEntry).map(ofEntry);
4258
4631
  if (arms.length === 0) return ZERO;
4259
- const hasOtherwise = n.otherwise !== void 0;
4632
+ const hasOtherwise = n2.otherwise !== void 0;
4260
4633
  const minArm = arms.reduce((a, b) => b.credits.min < a.credits.min ? b : a);
4261
4634
  const maxArm = arms.reduce((a, b) => b.credits.max > a.credits.max ? b : a);
4262
4635
  const summed = arms.reduce(add, ZERO);
4263
- const max = n.exclusive === true ? maxArm : summed;
4636
+ const max = n2.exclusive === true ? maxArm : summed;
4264
4637
  const min = hasOtherwise ? minArm : {
4265
4638
  ...ZERO
4266
4639
  };
@@ -4277,17 +4650,14 @@ function ofEntry(e) {
4277
4650
  };
4278
4651
  }
4279
4652
  case "foreach": {
4280
- const body = ofEntry(armEntry(n.step));
4281
- const opts = n.options ?? {};
4282
- const cap = typeof opts.maxItems === "number" && opts.maxItems > 0 ? opts.maxItems : 1;
4653
+ const body = ofEntry(armEntry(n2.step));
4654
+ const opts = n2.opts ?? {};
4655
+ const cap = typeof opts.maxItems === "number" && opts.maxItems > 0 ? opts.maxItems : WORKFLOW_FOREACH_DEFAULT_MAX_ITEMS;
4283
4656
  return scale(body, 0, cap);
4284
4657
  }
4285
- case "loop":
4286
- case "dowhile":
4287
- case "dountil": {
4288
- const body = ofEntry(armEntry(n.step));
4289
- const opts = n.options ?? {};
4290
- const cap = typeof opts.maxIterations === "number" && opts.maxIterations > 0 ? opts.maxIterations : 1;
4658
+ case "loop": {
4659
+ const body = ofEntry(armEntry(n2.step));
4660
+ const cap = typeof n2.maxIterations === "number" && n2.maxIterations > 0 ? n2.maxIterations : WORKFLOW_LOOP_DEFAULT_MAX_ITERATIONS;
4291
4661
  return scale(body, 1, cap);
4292
4662
  }
4293
4663
  default:
@@ -4318,14 +4688,84 @@ function estimateGraph(envelopeOrGraph) {
4318
4688
  consentCredits: r.credits.max
4319
4689
  };
4320
4690
  }
4321
- var __defProp3, __name3, WORKFLOW_ARM_SUBRUN_ID, 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, WORKFLOW_JOB_TOOLS, WORKFLOW_JOB_MAX_WORKTREE_ARMS, workspaceOf, mountsWorkspace, isJobTier, jobToolsOf, schemaIsArray, singleId, armId, TEMPLATE_STEP_REF, EDITABLE_PATH_RE, PREDICATE_OPS, 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, nodeIdOf, armId2, canonical, sortKeys, FORCE_CANCEL_STALE_MS, TERMINAL, 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;
4691
+ function* singleStepsOf(entry) {
4692
+ if (!isRecord2(entry)) return;
4693
+ switch (entry.type) {
4694
+ case "step":
4695
+ case "agent":
4696
+ case "tool":
4697
+ yield entry;
4698
+ return;
4699
+ case "workflow":
4700
+ yield entry;
4701
+ if (Array.isArray(entry.graph)) yield* singleStepsOf(entry.graph[1]);
4702
+ return;
4703
+ case "parallel":
4704
+ case "conditional":
4705
+ if (Array.isArray(entry.steps)) for (const arm of entry.steps) yield* singleStepsOf(arm);
4706
+ yield* singleStepsOf(entry.otherwise);
4707
+ return;
4708
+ case "foreach":
4709
+ case "loop":
4710
+ yield* singleStepsOf(entry.step);
4711
+ return;
4712
+ default:
4713
+ return;
4714
+ }
4715
+ }
4716
+ function entriesOf(graph) {
4717
+ const definition = isRecord2(graph) ? graph.definition : void 0;
4718
+ const entries = isRecord2(definition) ? definition.graph : void 0;
4719
+ return Array.isArray(entries) ? entries : [];
4720
+ }
4721
+ function inheritTargets(graphs) {
4722
+ const targets = /* @__PURE__ */ new Set();
4723
+ for (const graph of graphs) {
4724
+ for (const entry of entriesOf(graph)) {
4725
+ for (const node of singleStepsOf(entry)) {
4726
+ if (node.type === "workflow" && node.workspace === "inherit" && typeof node.workflowId === "string" && node.workflowId.length > 0) {
4727
+ targets.add(node.workflowId);
4728
+ }
4729
+ }
4730
+ }
4731
+ }
4732
+ return targets;
4733
+ }
4734
+ function needsInheritedWorkspace(graph) {
4735
+ if (!isRecord2(graph) || graph.workspace !== void 0) return false;
4736
+ for (const entry of entriesOf(graph)) {
4737
+ for (const node of singleStepsOf(entry)) {
4738
+ if (node.workspace !== void 0 && node.workspace !== "inherit") return true;
4739
+ }
4740
+ }
4741
+ return false;
4742
+ }
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;
4322
4744
  var init_dist2 = __esm({
4323
4745
  "../workflow-graph/dist/index.mjs"() {
4324
4746
  "use strict";
4325
4747
  init_dist();
4748
+ init_dist();
4326
4749
  __defProp3 = Object.defineProperty;
4327
4750
  __name3 = /* @__PURE__ */ __name((target, value22) => __defProp3(target, "name", { value: value22, configurable: true }), "__name");
4751
+ WORKFLOW_SIDE_EFFECTS = [
4752
+ "none",
4753
+ "external"
4754
+ ];
4755
+ WORKFLOW_JOB_RESOURCES = [
4756
+ "small",
4757
+ "medium",
4758
+ "large"
4759
+ ];
4760
+ SideEffectsSchema = z4.enum(WORKFLOW_SIDE_EFFECTS);
4761
+ JobResourcesSchema = z4.enum(WORKFLOW_JOB_RESOURCES);
4328
4762
  WORKFLOW_ARM_SUBRUN_ID = "$arm";
4763
+ SLEEP_UNTIL_REPLACEMENT = Object.freeze({
4764
+ type: "sleep",
4765
+ duration: 6e4
4766
+ });
4767
+ __name(sleepUntilUnsupportedMessage, "sleepUntilUnsupportedMessage");
4768
+ __name3(sleepUntilUnsupportedMessage, "sleepUntilUnsupportedMessage");
4329
4769
  WORKFLOW_CAPS_DEFAULT = Object.freeze({
4330
4770
  maxParallelArms: 16,
4331
4771
  maxForeachConcurrency: 16,
@@ -4361,6 +4801,11 @@ var init_dist2 = __esm({
4361
4801
  __name3(fillEntry, "fillEntry");
4362
4802
  __name(withDefaultsFilled, "withDefaultsFilled");
4363
4803
  __name3(withDefaultsFilled, "withDefaultsFilled");
4804
+ CONNECTION_ID_HEX_RE = /^[0-9a-f]{24}$/;
4805
+ __name(isConnectionKeyShaped, "isConnectionKeyShaped");
4806
+ __name3(isConnectionKeyShaped, "isConnectionKeyShaped");
4807
+ __name(connectionKeyUndeclaredMessage, "connectionKeyUndeclaredMessage");
4808
+ __name3(connectionKeyUndeclaredMessage, "connectionKeyUndeclaredMessage");
4364
4809
  WORKFLOW_JOB_TOOLS = [
4365
4810
  "shell",
4366
4811
  "read",
@@ -4424,6 +4869,11 @@ var init_dist2 = __esm({
4424
4869
  ]);
4425
4870
  __name(isPredicate, "isPredicate");
4426
4871
  __name3(isPredicate, "isPredicate");
4872
+ isPredicateScalar = /* @__PURE__ */ __name3((v) => v === null || typeof v === "string" || typeof v === "number" || typeof v === "boolean", "isPredicateScalar");
4873
+ __name(isPathOrLiteral, "isPathOrLiteral");
4874
+ __name3(isPathOrLiteral, "isPathOrLiteral");
4875
+ __name(isWellFormedPredicate, "isWellFormedPredicate");
4876
+ __name3(isWellFormedPredicate, "isWellFormedPredicate");
4427
4877
  GRAPH_HASH_PREFIX = "sha256-cj1:";
4428
4878
  __name(canonicalJson, "canonicalJson");
4429
4879
  __name3(canonicalJson, "canonicalJson");
@@ -4612,16 +5062,71 @@ var init_dist2 = __esm({
4612
5062
  fromKnowledge = /* @__PURE__ */ __name3((k) => ({
4613
5063
  knowledge: k
4614
5064
  }), "fromKnowledge");
5065
+ CONTINUED_FAILURE_TAG = "continued_failure";
5066
+ CONTINUED_FAILURE_DEFAULT_CODE = "step_failed";
5067
+ CONTINUED_FAILURE_OUTPUT_SCHEMA = Object.freeze({
5068
+ type: "object",
5069
+ properties: {
5070
+ __lua_workflow: {
5071
+ type: "string",
5072
+ const: CONTINUED_FAILURE_TAG
5073
+ },
5074
+ failed: {
5075
+ type: "boolean",
5076
+ const: true
5077
+ },
5078
+ error: {
5079
+ type: "object",
5080
+ properties: {
5081
+ code: {
5082
+ type: "string"
5083
+ },
5084
+ message: {
5085
+ type: "string"
5086
+ }
5087
+ },
5088
+ required: [
5089
+ "code",
5090
+ "message"
5091
+ ]
5092
+ },
5093
+ text: {
5094
+ type: "string",
5095
+ const: ""
5096
+ }
5097
+ },
5098
+ required: [
5099
+ "__lua_workflow",
5100
+ "failed",
5101
+ "error",
5102
+ "text"
5103
+ ]
5104
+ });
5105
+ CONTINUED_FAILURE_LEAF_PATHS = Object.freeze([
5106
+ "failed",
5107
+ "error",
5108
+ "error.code",
5109
+ "error.message",
5110
+ "text"
5111
+ ]);
5112
+ __name(continuedFailureValue, "continuedFailureValue");
5113
+ __name3(continuedFailureValue, "continuedFailureValue");
5114
+ __name(isContinuedFailureValue, "isContinuedFailureValue");
5115
+ __name3(isContinuedFailureValue, "isContinuedFailureValue");
4615
5116
  __name(lowerContainerArm, "lowerContainerArm");
4616
5117
  __name3(lowerContainerArm, "lowerContainerArm");
4617
- nodeIdOf = /* @__PURE__ */ __name3((n) => n.type === "step" ? n.step.id : n.id, "nodeIdOf");
5118
+ nodeIdOf = /* @__PURE__ */ __name3((n2) => n2.type === "step" ? n2.step.id : n2.id, "nodeIdOf");
4618
5119
  __name(entryIds, "entryIds");
4619
5120
  __name3(entryIds, "entryIds");
4620
5121
  __name(resolvePlacements, "resolvePlacements");
4621
5122
  __name3(resolvePlacements, "resolvePlacements");
4622
5123
  __name(seedLedgerFromRun, "seedLedgerFromRun");
4623
5124
  __name3(seedLedgerFromRun, "seedLedgerFromRun");
4624
- armId2 = /* @__PURE__ */ __name3((arm) => arm.type === "step" ? arm.step.id : arm.id, "armId");
5125
+ branchArmId = /* @__PURE__ */ __name3((arm) => arm.type === "step" ? arm.step.id : arm.id, "branchArmId");
5126
+ __name(branchSpecFromConditional, "branchSpecFromConditional");
5127
+ __name3(branchSpecFromConditional, "branchSpecFromConditional");
5128
+ __name(selectBranchArms, "selectBranchArms");
5129
+ __name3(selectBranchArms, "selectBranchArms");
4625
5130
  canonical = /* @__PURE__ */ __name3((v) => JSON.stringify(sortKeys(v)), "canonical");
4626
5131
  sortKeys = /* @__PURE__ */ __name3((v) => {
4627
5132
  if (Array.isArray(v)) return v.map(sortKeys);
@@ -4635,6 +5140,12 @@ var init_dist2 = __esm({
4635
5140
  }, "sortKeys");
4636
5141
  __name(replayLedger, "replayLedger");
4637
5142
  __name3(replayLedger, "replayLedger");
5143
+ JOIN = ".join";
5144
+ entryOfJoin = /* @__PURE__ */ __name3((id) => id.endsWith(JOIN) ? id.slice(0, -JOIN.length) : void 0, "entryOfJoin");
5145
+ __name(replayResultOf, "replayResultOf");
5146
+ __name3(replayResultOf, "replayResultOf");
5147
+ __name(ancestorResults, "ancestorResults");
5148
+ __name3(ancestorResults, "ancestorResults");
4638
5149
  __name(inferTaken, "inferTaken");
4639
5150
  __name3(inferTaken, "inferTaken");
4640
5151
  __name(countChildren, "countChildren");
@@ -4647,6 +5158,14 @@ var init_dist2 = __esm({
4647
5158
  __name3(pruneUndefined, "pruneUndefined");
4648
5159
  __name(runNextAction, "runNextAction");
4649
5160
  __name3(runNextAction, "runNextAction");
5161
+ IN_FLIGHT = new Set(WORKFLOW_STEP_IN_FLIGHT);
5162
+ __name(emptyRunCounts, "emptyRunCounts");
5163
+ __name3(emptyRunCounts, "emptyRunCounts");
5164
+ __name(runCountsFromStatusTally, "runCountsFromStatusTally");
5165
+ __name3(runCountsFromStatusTally, "runCountsFromStatusTally");
5166
+ __name(runCountsFromStepStatuses, "runCountsFromStepStatuses");
5167
+ __name3(runCountsFromStepStatuses, "runCountsFromStepStatuses");
5168
+ n = /* @__PURE__ */ __name3((v) => typeof v === "number" && Number.isFinite(v) ? v : 0, "n");
4650
5169
  __name(runCounts, "runCounts");
4651
5170
  __name3(runCounts, "runCounts");
4652
5171
  __name(runUsage, "runUsage");
@@ -4750,54 +5269,54 @@ var init_dist2 = __esm({
4750
5269
  __name3(rebaseItemPointer, "rebaseItemPointer");
4751
5270
  APPROVER_SPEC_MAX_USERS = 20;
4752
5271
  ESCALATION_MAX_HOPS = 3;
4753
- TemplateBindingSchema = z4.object({
4754
- template: z4.string().min(1).max(2048)
5272
+ TemplateBindingSchema = z22.object({
5273
+ template: z22.string().min(1).max(2048)
4755
5274
  }).strict();
4756
- ApproverSpecSchema = z4.union([
4757
- z4.literal("creator"),
4758
- z4.literal("org-admins"),
4759
- z4.object({
4760
- users: z4.union([
4761
- z4.array(z4.string().min(1).max(128)).min(1).max(APPROVER_SPEC_MAX_USERS),
5275
+ ApproverSpecSchema = z22.union([
5276
+ z22.literal("creator"),
5277
+ z22.literal("org-admins"),
5278
+ z22.object({
5279
+ users: z22.union([
5280
+ z22.array(z22.string().min(1).max(128)).min(1).max(APPROVER_SPEC_MAX_USERS),
4762
5281
  TemplateBindingSchema
4763
5282
  ])
4764
5283
  }).strict(),
4765
- z4.object({
4766
- role: z4.union([
4767
- z4.string().min(1).max(128),
5284
+ z22.object({
5285
+ role: z22.union([
5286
+ z22.string().min(1).max(128),
4768
5287
  TemplateBindingSchema
4769
5288
  ])
4770
5289
  }).strict(),
4771
- z4.object({
4772
- group: z4.union([
4773
- z4.string().min(1).max(128),
5290
+ z22.object({
5291
+ group: z22.union([
5292
+ z22.string().min(1).max(128),
4774
5293
  TemplateBindingSchema
4775
5294
  ])
4776
5295
  }).strict(),
4777
- z4.object({
4778
- governance: z4.object({
4779
- policyId: z4.string().min(1).max(128)
5296
+ z22.object({
5297
+ governance: z22.object({
5298
+ policyId: z22.string().min(1).max(128)
4780
5299
  }).strict()
4781
5300
  }).strict()
4782
5301
  ]);
4783
- FourEyesSchema = z4.object({
5302
+ FourEyesSchema = z22.object({
4784
5303
  edit: ApproverSpecSchema,
4785
5304
  approve: ApproverSpecSchema
4786
5305
  }).strict();
4787
- EscalationHopSchema = z4.object({
5306
+ EscalationHopSchema = z22.object({
4788
5307
  escalateTo: ApproverSpecSchema,
4789
- timeoutHours: z4.number().finite().min(1).max(720)
5308
+ timeoutHours: z22.number().finite().min(1).max(720)
4790
5309
  }).strict();
4791
- TerminalOutcomeSchema = z4.enum([
5310
+ TerminalOutcomeSchema = z22.enum([
4792
5311
  "deny",
4793
5312
  "cancel-run",
4794
5313
  "fail",
4795
5314
  "continue"
4796
5315
  ]);
4797
- ApprovalOnTimeoutSchema = z4.union([
5316
+ ApprovalOnTimeoutSchema = z22.union([
4798
5317
  TerminalOutcomeSchema,
4799
5318
  EscalationHopSchema,
4800
- z4.array(z4.union([
5319
+ z22.array(z22.union([
4801
5320
  TerminalOutcomeSchema,
4802
5321
  EscalationHopSchema
4803
5322
  ])).min(1).max(ESCALATION_MAX_HOPS + 1)
@@ -4850,6 +5369,15 @@ var init_dist2 = __esm({
4850
5369
  __name3(ofEntry, "ofEntry");
4851
5370
  __name(estimateGraph, "estimateGraph");
4852
5371
  __name3(estimateGraph, "estimateGraph");
5372
+ isRecord2 = /* @__PURE__ */ __name3((v) => !!v && typeof v === "object" && !Array.isArray(v), "isRecord");
5373
+ __name(singleStepsOf, "singleStepsOf");
5374
+ __name3(singleStepsOf, "singleStepsOf");
5375
+ __name(entriesOf, "entriesOf");
5376
+ __name3(entriesOf, "entriesOf");
5377
+ __name(inheritTargets, "inheritTargets");
5378
+ __name3(inheritTargets, "inheritTargets");
5379
+ __name(needsInheritedWorkspace, "needsInheritedWorkspace");
5380
+ __name3(needsInheritedWorkspace, "needsInheritedWorkspace");
4853
5381
  }
4854
5382
  });
4855
5383
 
@@ -4908,16 +5436,16 @@ function stepNodeOf(s) {
4908
5436
  });
4909
5437
  }
4910
5438
  function materializeEntry(entry, steps) {
4911
- const single = /* @__PURE__ */ __name((n) => {
4912
- if (n.type === "step" && steps[n.step.id]) return stepNodeOf(steps[n.step.id]);
4913
- if (n.type === "workflow" && n.workflowId === WORKFLOW_ARM_SUBRUN_ID && n.graph) return {
4914
- ...n,
5439
+ const single = /* @__PURE__ */ __name((n2) => {
5440
+ if (n2.type === "step" && steps[n2.step.id]) return stepNodeOf(steps[n2.step.id]);
5441
+ if (n2.type === "workflow" && n2.workflowId === WORKFLOW_ARM_SUBRUN_ID && n2.graph) return {
5442
+ ...n2,
4915
5443
  graph: [
4916
- n.graph[0],
4917
- single(n.graph[1])
5444
+ n2.graph[0],
5445
+ single(n2.graph[1])
4918
5446
  ]
4919
5447
  };
4920
- return n;
5448
+ return n2;
4921
5449
  }, "single");
4922
5450
  switch (entry.type) {
4923
5451
  case "step":
@@ -4962,8 +5490,8 @@ function createWorkflow(cfg) {
4962
5490
  if (v.roles.length > 20 || (v.users?.length ?? 0) > 50) throw new LuaWorkflowBuildError("cap-exceeded", "outputVisibility allows \u2264 20 roles and \u2264 50 users");
4963
5491
  }
4964
5492
  if (cfg.backfillOnEnable?.maxOccurrences !== void 0) {
4965
- const n = cfg.backfillOnEnable.maxOccurrences;
4966
- if (!Number.isInteger(n) || n < 1 || n > 200) throw new LuaWorkflowBuildError("invalid-envelope", "backfillOnEnable.maxOccurrences must be an integer in 1..200 (backfill-max-occurrences-out-of-range; the org maxBatchItems twin is checked at publish/R56)");
5493
+ const n2 = cfg.backfillOnEnable.maxOccurrences;
5494
+ if (!Number.isInteger(n2) || n2 < 1 || n2 > 200) throw new LuaWorkflowBuildError("invalid-envelope", "backfillOnEnable.maxOccurrences must be an integer in 1..200 (backfill-max-occurrences-out-of-range; the org maxBatchItems twin is checked at publish/R56)");
4967
5495
  }
4968
5496
  const keys = /* @__PURE__ */ new Set();
4969
5497
  envRefKeys(cfg.schedule, keys);
@@ -5154,7 +5682,7 @@ var init_workflow = __esm({
5154
5682
  getEnvTemplateKeys() {
5155
5683
  return this.built.envTemplateKeys;
5156
5684
  }
5157
- /** `.workflow(id, ref)` targets by name — `ManifestWorkflow.workflowRefs`. */
5685
+ /** `.workflow(id, ref)` targets by name — `ManifestWorkflow.workflowRefs`; `workspace:'inherit'` marks the inherit children the compiler defers `workspace-not-declared` for. */
5158
5686
  getNestedWorkflowRefs() {
5159
5687
  return this.built.nestedRefs;
5160
5688
  }
@@ -5184,6 +5712,7 @@ var init_workflow = __esm({
5184
5712
  };
5185
5713
  if (cfg.concurrencyPolicy !== void 0) envelope.concurrencyPolicy = cfg.concurrencyPolicy;
5186
5714
  if (cfg.workspace !== void 0) envelope.workspace = cfg.workspace;
5715
+ if (cfg.connections !== void 0) envelope.connections = cfg.connections;
5187
5716
  return withDefaultsFilled(envelope);
5188
5717
  }
5189
5718
  };
@@ -5219,7 +5748,6 @@ var init_workflow = __esm({
5219
5748
  if (this.steps[s.id] && this.steps[s.id] !== s) throw new LuaWorkflowBuildError("duplicate-step-id", `step id "${s.id}" is declared twice`);
5220
5749
  const tier = s.tier ?? (s.workspace ? "job" : void 0);
5221
5750
  if (s.workspace && s.tier !== void 0 && s.tier !== "job") throw new LuaWorkflowBuildError("workspace-requires-job-tier", `"${s.id}": a step mounting a workspace must be tier:'job'`);
5222
- if (s.workspace && !this.config.workspace) throw new LuaWorkflowBuildError("workspace-not-declared", `"${s.id}" mounts a workspace but createWorkflow declares none`);
5223
5751
  if (s.jobTools && tier !== "job") throw new LuaWorkflowBuildError("cap-exceeded", `"${s.id}": jobTools require tier:'job' (job-tools-require-job-tier)`);
5224
5752
  assertTimeout({
5225
5753
  id: s.id,
@@ -5460,13 +5988,20 @@ var init_workflow = __esm({
5460
5988
  }
5461
5989
  const tier = opts.tier ?? (opts.workspace ? "job" : void 0);
5462
5990
  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'`);
5463
- if (opts.workspace && !this.config.workspace) throw new LuaWorkflowBuildError("workspace-not-declared", `"${id}" mounts a workspace but createWorkflow declares none`);
5464
5991
  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`);
5465
5992
  if (opts.toolScope?.jobTools && tier !== "job") throw new LuaWorkflowBuildError("cap-exceeded", `"${id}": toolScope.jobTools require tier:'job' (job-tools-require-job-tier)`);
5466
5993
  if (opts.maxTurns !== void 0) {
5467
5994
  if (tier !== "job") throw new LuaWorkflowBuildError("max-turns-requires-job-tier", `"${id}": maxTurns is only legal on a tier:'job' agent step`);
5468
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`);
5469
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`);
6004
+ }
5470
6005
  assertTimeout({
5471
6006
  id,
5472
6007
  timeoutSeconds: opts.timeoutSeconds,
@@ -5493,7 +6028,9 @@ var init_workflow = __esm({
5493
6028
  workspace: opts.workspace,
5494
6029
  jobResources: opts.jobResources,
5495
6030
  harness: opts.harness,
5496
- maxTurns: opts.maxTurns
6031
+ maxTurns: opts.maxTurns,
6032
+ maxMessages: opts.maxMessages,
6033
+ maxInputTokens: opts.maxInputTokens
5497
6034
  });
5498
6035
  return this.push({
5499
6036
  kind: "declare",
@@ -5648,7 +6185,11 @@ var init_workflow = __esm({
5648
6185
  }
5649
6186
  assertNoClosure(input, `workflow("${id}").input`);
5650
6187
  this.recordEnvRefs(input);
5651
- this.nestedRefs.push({
6188
+ this.nestedRefs.push(opts?.workspace === "inherit" ? {
6189
+ id,
6190
+ name,
6191
+ workspace: "inherit"
6192
+ } : {
5652
6193
  id,
5653
6194
  name
5654
6195
  });
@@ -5660,8 +6201,8 @@ var init_workflow = __esm({
5660
6201
  workspace: opts?.workspace
5661
6202
  });
5662
6203
  return this.push({
5663
- kind: "entry",
5664
- entry: node
6204
+ kind: "declare",
6205
+ node
5665
6206
  });
5666
6207
  }
5667
6208
  commit() {
@@ -5671,7 +6212,7 @@ var init_workflow = __esm({
5671
6212
  const { graph, issues } = resolvePlacements(this.calls);
5672
6213
  const fatal = issues[0];
5673
6214
  if (fatal) {
5674
- const hint = fatal.code === "unknown-step-ref" ? "a string StepRef must name an entry declared by agentStep/specialistStep/toolStep/map(\u2026, { id }) somewhere in the chain \u2014 before OR after the reference" : void 0;
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;
5675
6216
  throw new LuaWorkflowBuildError(fatal.code, fatal.message, hint);
5676
6217
  }
5677
6218
  if (graph.length === 0) throw new LuaWorkflowBuildError("empty-graph", `workflow "${this.config.name}" has no entries`);
@@ -5684,7 +6225,8 @@ var init_workflow = __esm({
5684
6225
  if (graphHasHitl(graph, this.steps) && this.config.budget?.maxDurationSeconds === void 0) {
5685
6226
  this.warnings.push({
5686
6227
  code: "hitl-duration-defaulted",
5687
- message: `budget.maxDurationSeconds defaulted to ${WORKFLOW_HITL_MAX_DURATION_SECONDS} s (30 d) because the graph contains an approval / waitForSignal / suspend-capable step \u2014 set it explicitly to silence`
6228
+ // LUA-668: names the workflow two HITL workflows in one project printed two identical lines that read as a duplicate.
6229
+ message: `workflow "${this.config.name}": budget.maxDurationSeconds defaulted to ${WORKFLOW_HITL_MAX_DURATION_SECONDS} s (30 d) because the graph contains an approval / waitForSignal / suspend-capable step \u2014 set it explicitly to silence`
5688
6230
  });
5689
6231
  }
5690
6232
  this.checkScheduleInput();
@@ -11215,7 +11757,7 @@ function createWorkflowsRuntime(getApi) {
11215
11757
  }
11216
11758
  };
11217
11759
  }
11218
- var WORKFLOW_START_MAX_WAIT_SECONDS, WORKFLOW_START_CLIENT_DEADLINE_SLACK_MS, WorkflowApiError, unwrap, assertBoundAgent, unavailable, WorkflowApi;
11760
+ var WORKFLOW_START_MAX_WAIT_SECONDS, WORKFLOW_START_CLIENT_DEADLINE_SLACK_MS, WorkflowApiError, unwrap, assertBoundAgent, pathId, unavailable, WorkflowApi;
11219
11761
  var init_workflow_api_service = __esm({
11220
11762
  "src/api/workflow.api.service.ts"() {
11221
11763
  "use strict";
@@ -11252,6 +11794,7 @@ var init_workflow_api_service = __esm({
11252
11794
  throw new WorkflowApiError("FORBIDDEN", `Workflows.${member}: goals are scoped to the bound agent ${api.agentId}`, 403);
11253
11795
  }
11254
11796
  }, "assertBoundAgent");
11797
+ pathId = /* @__PURE__ */ __name((id) => encodeURIComponent(id), "pathId");
11255
11798
  unavailable = /* @__PURE__ */ __name((member, route) => async () => {
11256
11799
  throw new WorkflowApiError("WORKFLOWS_API_UNAVAILABLE", `Workflows.${member} is not available in this runtime yet (${route} lands with a later wave)`, 501);
11257
11800
  }, "unavailable");
@@ -11377,7 +11920,7 @@ var init_workflow_api_service = __esm({
11377
11920
  /** R5 — one step of a run (`?attempt=n` selects from the attempt history). */
11378
11921
  async getRunStep(runId, stepId, options = {}) {
11379
11922
  const qs = options.attempt !== void 0 ? `?attempt=${options.attempt}` : "";
11380
- return this.httpGet(`${this.runs}/${runId}/steps/${stepId}${qs}`, await this.auth());
11923
+ return this.httpGet(`${this.runs}/${runId}/steps/${pathId(stepId)}${qs}`, await this.auth());
11381
11924
  }
11382
11925
  /** R6 — script-form journal page (404 `NOT_SCRIPT_RUN` for graph runs — IF-16). */
11383
11926
  async getRunJournal(runId, options = {}) {
@@ -11397,7 +11940,7 @@ var init_workflow_api_service = __esm({
11397
11940
  }
11398
11941
  /** R12 — resume a suspended step; the loser of a race gets `{ resumed:false, reason:'already_resumed' }`, never a 4xx. */
11399
11942
  async resumeRun(runId, stepId, data) {
11400
- return this.httpPost(`${this.runs}/${runId}/steps/${stepId}/resume`, data, await this.auth());
11943
+ return this.httpPost(`${this.runs}/${runId}/steps/${pathId(stepId)}/resume`, data, await this.auth());
11401
11944
  }
11402
11945
  /**
11403
11946
  * R36 — re-arm a parked step: a failed row past its retries, a gate-2 park, or a billing hold (the only exit
@@ -11405,15 +11948,15 @@ var init_workflow_api_service = __esm({
11405
11948
  * STEP_NOT_PARKED.
11406
11949
  */
11407
11950
  async retryStep(runId, stepId, data = {}) {
11408
- return this.httpPost(`${this.runs}/${runId}/steps/${stepId}/retry`, data, await this.auth());
11951
+ return this.httpPost(`${this.runs}/${runId}/steps/${pathId(stepId)}/retry`, data, await this.auth());
11409
11952
  }
11410
11953
  /** R13 — resolve an approval (human; `expectedFingerprint` guards against an edited payload — 409 `PAYLOAD_MISMATCH`). */
11411
11954
  async resolveApproval(runId, approvalId, data) {
11412
- return this.httpPost(`${this.runs}/${runId}/approvals/${approvalId}/resolve`, data, await this.auth());
11955
+ return this.httpPost(`${this.runs}/${runId}/approvals/${pathId(approvalId)}/resolve`, data, await this.auth());
11413
11956
  }
11414
11957
  /** R14 — deliver a named signal (`dedupeKey` ⇒ 200 `duplicate:true` on replay). */
11415
11958
  async signalRun(runId, name, data = {}) {
11416
- return this.httpPost(`${this.runs}/${runId}/signals/${encodeURIComponent(name)}`, data, await this.auth());
11959
+ return this.httpPost(`${this.runs}/${runId}/signals/${pathId(name)}`, data, await this.auth());
11417
11960
  }
11418
11961
  /** R31 — `DELETE …/runs/:runId` (`eraseRun`; human principal) → 202 `{ accepted, purgeId }`; 409 `RUN_NOT_TERMINAL { nextAction:'cancel' }`. */
11419
11962
  /** R50 — request an evidence-bundle export (202 `{exportId, status:'pending'}`; 409 `RUN_NOT_TERMINAL` / `EXPORT_IN_PROGRESS`). */
@@ -11470,6 +12013,23 @@ var init_workflow_api_service = __esm({
11470
12013
  async closeGoal(goalId, data = {}) {
11471
12014
  return this.httpPost(`${this.goals}/${encodeURIComponent(goalId)}/close`, data, await this.auth());
11472
12015
  }
12016
+ // ─── Schedules (R4-MF-2 list/get + R28 delete — `/workflows/:agentId/schedules`; LUA-627 stanza) ───
12017
+ /** Schedule tree (09 §9.5 — the write-only R27/R56/R28 family plus the R4-MF-2 read rows). */
12018
+ get schedules() {
12019
+ return `/workflows/${this.agentId}/schedules`;
12020
+ }
12021
+ /** R4-MF-2 — every `Job{kind:'workflow'}` of the agent with the PRO-726 strike fields (`goalId` marks a goal's cadence). */
12022
+ async listSchedules() {
12023
+ return this.httpGet(this.schedules, await this.auth());
12024
+ }
12025
+ /** R4-MF-2 — one schedule row; unknown, cross-agent and non-workflow-kind jobs all 404 `SCHEDULE_NOT_FOUND`. */
12026
+ async getSchedule(jobId) {
12027
+ return this.httpGet(`${this.schedules}/${encodeURIComponent(jobId)}`, await this.auth());
12028
+ }
12029
+ /** R28 — delete a schedule Job (404 `SCHEDULE_NOT_FOUND`). The CLI refuses a goal-owned job BEFORE this call (`goal_schedule`). */
12030
+ async deleteSchedule(jobId) {
12031
+ return this.httpDelete(`${this.schedules}/${encodeURIComponent(jobId)}`, await this.auth());
12032
+ }
11473
12033
  // ─── Events (R7 — SSE `watch`, WF-204) ───
11474
12034
  /**
11475
12035
  * R7 SSE — `GET …/runs/:runId/events` as a frame stream (`id:<seq>` / `event:<type>` /
@@ -11513,7 +12073,7 @@ var init_workflow_api_service = __esm({
11513
12073
  if (options.attempt !== void 0) query.append("attempt", String(options.attempt));
11514
12074
  if (options.tail !== void 0) query.append("tail", String(options.tail));
11515
12075
  const qs = query.toString();
11516
- return this.httpGet(`${this.runs}/${runId}/steps/${stepId}/job${qs ? `?${qs}` : ""}`, await this.auth());
12076
+ return this.httpGet(`${this.runs}/${runId}/steps/${pathId(stepId)}/job${qs ? `?${qs}` : ""}`, await this.auth());
11517
12077
  }
11518
12078
  };
11519
12079
  }