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.
package/dist/index.js CHANGED
@@ -859,7 +859,6 @@ function luaClientMetricLabels(client2) {
859
859
  const canonical2 = parseLuaClientHeader(serializeLuaClientHeader(client2));
860
860
  return {
861
861
  client_family: canonical2?.app ?? "unknown",
862
- client_version: canonical2?.version ?? "unknown",
863
862
  client_attribution: canonical2?.attribution ?? "unknown"
864
863
  };
865
864
  }
@@ -927,6 +926,59 @@ function scheduledTimeKey(scheduledTime) {
927
926
  function scheduledWorkflowRunIdForTime(jobId, scheduledTime) {
928
927
  return scheduledWorkflowRunId(jobId, scheduledTime);
929
928
  }
929
+ function groupCount(re) {
930
+ let n2 = GROUP_COUNT.get(re);
931
+ if (n2 === void 0) {
932
+ n2 = new RegExp(`${re.source}|`, re.flags.replace("g", "")).exec("").length - 1;
933
+ GROUP_COUNT.set(re, n2);
934
+ }
935
+ return n2;
936
+ }
937
+ function applyPatterns(text, patterns) {
938
+ let out = text;
939
+ for (const { re, suffix } of patterns) {
940
+ re.lastIndex = 0;
941
+ if (!re.test(out)) continue;
942
+ re.lastIndex = 0;
943
+ const groups = groupCount(re);
944
+ out = out.replace(re, (...args2) => {
945
+ const kept = args2.slice(1, 1 + groups).map((g) => typeof g === "string" ? g : "");
946
+ if (suffix && kept.length > 0) {
947
+ const tail = kept[kept.length - 1];
948
+ return `${kept.slice(0, -1).join("")}${REDACTED_PLACEHOLDER}${tail}`;
949
+ }
950
+ return `${kept.join("")}${REDACTED_PLACEHOLDER}`;
951
+ });
952
+ }
953
+ return out;
954
+ }
955
+ function scrubSecretText(text) {
956
+ if (typeof text !== "string" || text.length < 4) return text;
957
+ return applyPatterns(applyPatterns(text, SECRET_LITERAL_PATTERNS), SECRET_PAIR_PATTERNS);
958
+ }
959
+ function scrubSecretLines(lines) {
960
+ return lines.map((l) => typeof l === "string" ? scrubSecretText(l) : l);
961
+ }
962
+ function messageText(value3) {
963
+ if (typeof value3 === "string") return value3;
964
+ if (value3 instanceof Error) return value3.message;
965
+ if (value3 && typeof value3 === "object") {
966
+ const m = value3.message;
967
+ if (typeof m === "string") return m;
968
+ try {
969
+ return JSON.stringify(value3);
970
+ } catch {
971
+ return "";
972
+ }
973
+ }
974
+ return value3 === void 0 || value3 === null ? "" : String(value3);
975
+ }
976
+ function scrubProviderMessage(raw, max = PROVIDER_MESSAGE_MAX_CHARS) {
977
+ const text = messageText(raw).replace(/\s+/g, " ").trim();
978
+ if (!text) return void 0;
979
+ const out = scrubSecretText(text);
980
+ return out.length > max ? `${out.slice(0, max - 1)}\u2026` : out;
981
+ }
930
982
  function isWorkflowAuditEvent(action) {
931
983
  return typeof action === "string" && WORKFLOW_AUDIT_EVENTS.includes(action);
932
984
  }
@@ -1021,7 +1073,7 @@ ${PREAMBLE}
1021
1073
 
1022
1074
  ${items.join("\n\n")}`;
1023
1075
  }
1024
- var __defProp2, __name2, 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, AGENT_LOG_SOURCES, 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, EventType, 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, TYPED_API_KEY, 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_JOURNAL_PROTOCOL_VERSION, WORKFLOW_SIGNAL_PAYLOAD_MAX_BYTES, WORKFLOW_AUDIT_EVENTS, WORKFLOW_AUDIT_METADATA_MAX_BYTES, INDENT, WRAP_WIDTH, NOUNS, GET_TOOL_NAMES, PREAMBLE;
1076
+ var __defProp2, __name2, 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, AGENT_LOG_SOURCES, 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, EventType, 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, TYPED_API_KEY, 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_JOURNAL_PROTOCOL_VERSION, 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;
1025
1077
  var init_dist = __esm({
1026
1078
  "../shared-types/dist/index.mjs"() {
1027
1079
  "use strict";
@@ -2065,6 +2117,27 @@ This text is who you are for this person. As you learn them, their name, their w
2065
2117
  ...WORKFLOW_RUN_IDLE,
2066
2118
  ...WORKFLOW_RUN_TERMINAL
2067
2119
  ];
2120
+ WORKFLOW_STEP_STATUSES = [
2121
+ "pending",
2122
+ "ready",
2123
+ "waiting",
2124
+ "dispatched",
2125
+ "claimed",
2126
+ "running",
2127
+ "suspended",
2128
+ "cancellation_requested",
2129
+ "completed",
2130
+ "failed",
2131
+ "skipped",
2132
+ "cancelled",
2133
+ "timeout",
2134
+ "reaped"
2135
+ ];
2136
+ WORKFLOW_STEP_IN_FLIGHT = [
2137
+ "claimed",
2138
+ "running",
2139
+ "cancellation_requested"
2140
+ ];
2068
2141
  ARCHIVE_WINDOW_MARGIN_DAYS = 7;
2069
2142
  __name(shouldSkipArchive, "shouldSkipArchive");
2070
2143
  __name2(shouldSkipArchive, "shouldSkipArchive");
@@ -2095,11 +2168,90 @@ This text is who you are for this person. As you learn them, their name, their w
2095
2168
  __name(workflowOrgSlotKey, "workflowOrgSlotKey");
2096
2169
  __name2(workflowOrgSlotKey, "workflowOrgSlotKey");
2097
2170
  WORKFLOW_JOURNAL_PROTOCOL_VERSION = 1;
2171
+ WORKFLOW_CONNECTION_KEY_RE = /^[a-z][a-z0-9_-]{0,63}$/;
2098
2172
  __name(scheduledTimeKey, "scheduledTimeKey");
2099
2173
  __name2(scheduledTimeKey, "scheduledTimeKey");
2100
2174
  __name(scheduledWorkflowRunIdForTime, "scheduledWorkflowRunIdForTime");
2101
2175
  __name2(scheduledWorkflowRunIdForTime, "scheduledWorkflowRunIdForTime");
2102
2176
  WORKFLOW_SIGNAL_PAYLOAD_MAX_BYTES = 64 * 1024;
2177
+ REDACTED_PLACEHOLDER = "[REDACTED]";
2178
+ PROVIDER_MESSAGE_MAX_CHARS = 300;
2179
+ SECRET_LITERAL_PATTERNS = [
2180
+ {
2181
+ re: /\b(github_pat_)[A-Za-z0-9_]{16,}/g
2182
+ },
2183
+ {
2184
+ re: /\b(gh[pousr]_)[A-Za-z0-9]{16,}/g
2185
+ },
2186
+ {
2187
+ re: /\b(glpat-)[A-Za-z0-9_-]{16,}/g
2188
+ },
2189
+ {
2190
+ re: /\b(sk-ant-)[A-Za-z0-9_-]{16,}/g
2191
+ },
2192
+ {
2193
+ re: /\b(sk-)(?!ant-)[A-Za-z0-9_-]{20,}/g
2194
+ },
2195
+ {
2196
+ re: /\b(AKIA)[A-Z0-9]{16}\b/g
2197
+ },
2198
+ {
2199
+ re: /\b(xox[abprs]-)[A-Za-z0-9-]{10,}/g
2200
+ },
2201
+ {
2202
+ re: /\b(AIza)[0-9A-Za-z_-]{35}/g
2203
+ },
2204
+ {
2205
+ re: /\b(ya29\.)[A-Za-z0-9_-]{20,}/g
2206
+ },
2207
+ {
2208
+ re: /\b(eyJ)[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}/g
2209
+ },
2210
+ {
2211
+ re: /(-----BEGIN [A-Z ]*PRIVATE KEY-----)[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g
2212
+ },
2213
+ // `https://x-access-token:<token>@github.com/…` (the git credential shape the pod scrubbed already)
2214
+ {
2215
+ re: /\b(x-access-token:)[^@\s]+(@)/g,
2216
+ suffix: true
2217
+ }
2218
+ ];
2219
+ 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?)";
2220
+ SECRET_PAIR_PATTERNS = [
2221
+ // `Authorization: Bearer x`, `x-api-key: x`, `AWS_SECRET_ACCESS_KEY=x`, `SVC_JOB_KEY=x`, `"apiKey": "x"`,
2222
+ // `FOO_TOKEN=x`, `secretKey=x`, `password=x`. Groups: the char before the name, the name, the separator
2223
+ // (with its quotes), the scheme word — all kept; the value goes. A value a literal rule already replaced
2224
+ // (`x-access-token:[REDACTED]@host`) is left alone so the host after it survives.
2225
+ {
2226
+ 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")
2227
+ },
2228
+ // `?token=x`, `&key=x`, `&X-Amz-Signature=x`, `&sig=x`
2229
+ {
2230
+ 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
2231
+ },
2232
+ // `mongodb+srv://user:pass@host`, `postgres://user:pass@host`, `https://user:pass@host`
2233
+ {
2234
+ re: /(\/\/[^\s/:@]+:)[^\s/@]+(@)/g,
2235
+ suffix: true
2236
+ },
2237
+ // `Basic <base64>` / `bearer <short token>` (the literal list needs ≥ 16 chars; any case)
2238
+ {
2239
+ re: /\b((?:Basic|Bearer)\s+)(?!\[REDACTED\])[A-Za-z0-9+/=_.-]{8,}/gi
2240
+ }
2241
+ ];
2242
+ GROUP_COUNT = /* @__PURE__ */ new WeakMap();
2243
+ __name(groupCount, "groupCount");
2244
+ __name2(groupCount, "groupCount");
2245
+ __name(applyPatterns, "applyPatterns");
2246
+ __name2(applyPatterns, "applyPatterns");
2247
+ __name(scrubSecretText, "scrubSecretText");
2248
+ __name2(scrubSecretText, "scrubSecretText");
2249
+ __name(scrubSecretLines, "scrubSecretLines");
2250
+ __name2(scrubSecretLines, "scrubSecretLines");
2251
+ __name(messageText, "messageText");
2252
+ __name2(messageText, "messageText");
2253
+ __name(scrubProviderMessage, "scrubProviderMessage");
2254
+ __name2(scrubProviderMessage, "scrubProviderMessage");
2103
2255
  WORKFLOW_AUDIT_EVENTS = [
2104
2256
  // --- definitions / versions / templates (13, 11 §11.11.5) ---
2105
2257
  "workflow.published",
@@ -2719,7 +2871,11 @@ var init_request_credential = __esm({
2719
2871
  // ../workflow-graph/dist/index.mjs
2720
2872
  import { createHash as createHash2 } from "crypto";
2721
2873
  import { z as z6 } from "zod";
2874
+ import { z as z22 } from "zod";
2722
2875
  import { createHash as createHash22 } from "crypto";
2876
+ function sleepUntilUnsupportedMessage(id) {
2877
+ 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} }`;
2878
+ }
2723
2879
  function fillPolicy(node, defaultTimeout) {
2724
2880
  if (node.tier === void 0 && node.workspace !== void 0 && node.workspace !== "inherit") node.tier = "job";
2725
2881
  if (node.timeoutSeconds === void 0) node.timeoutSeconds = node.tier === "job" ? WORKFLOW_JOB_DEFAULT_TIMEOUT_SECONDS : defaultTimeout;
@@ -2814,6 +2970,12 @@ function withDefaultsFilled(g) {
2814
2970
  out.definition.graph.forEach(fillEntry);
2815
2971
  return out;
2816
2972
  }
2973
+ function isConnectionKeyShaped(value22) {
2974
+ return WORKFLOW_CONNECTION_KEY_RE.test(value22) && !CONNECTION_ID_HEX_RE.test(value22);
2975
+ }
2976
+ function connectionKeyUndeclaredMessage(path23, key) {
2977
+ return `${path23} '${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`;
2978
+ }
2817
2979
  function classifyModelProvider(model) {
2818
2980
  const m = (model ?? "").trim().toLowerCase();
2819
2981
  if (!m) return null;
@@ -2916,6 +3078,33 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
2916
3078
  if (envelopeWorkspace?.backend && envelopeWorkspace.backend !== "ebs" && opts.policy?.workspaceBackends && !opts.policy.workspaceBackends.includes(envelopeWorkspace.backend)) {
2917
3079
  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");
2918
3080
  }
3081
+ const declaredKeys = new Set(opts.connectionKeys ?? []);
3082
+ if (g.connections !== void 0 && !Array.isArray(g.connections)) {
3083
+ err("connection-declaration-invalid", "`connections` must be an array of { key, integrationType }", "connections");
3084
+ }
3085
+ (Array.isArray(g.connections) ? g.connections : []).forEach((c, i) => {
3086
+ const path23 = `connections.${i}`;
3087
+ const key = c?.key;
3088
+ const integrationType = c?.integrationType;
3089
+ if (typeof key !== "string" || !WORKFLOW_CONNECTION_KEY_RE.test(key)) {
3090
+ err("connection-declaration-invalid", `connections[${i}].key must match ${WORKFLOW_CONNECTION_KEY_RE}`, `${path23}.key`);
3091
+ return;
3092
+ }
3093
+ if (declaredKeys.has(key)) {
3094
+ err("connection-declaration-invalid", `connections[${i}].key "${key}" is declared twice`, `${path23}.key`);
3095
+ return;
3096
+ }
3097
+ if (typeof integrationType !== "string" || !integrationType.trim()) {
3098
+ err("connection-declaration-invalid", `connections[${i}] ("${key}") needs an integrationType (the catalog slug, e.g. 'github')`, `${path23}.integrationType`);
3099
+ return;
3100
+ }
3101
+ declaredKeys.add(key);
3102
+ });
3103
+ const undeclaredKey = /* @__PURE__ */ __name3((ref) => typeof ref === "string" && !declaredKeys.has(ref) && isConnectionKeyShaped(ref) && opts.connectionIds?.has(ref) !== true, "undeclaredKey");
3104
+ const credentialsRef = envelopeWorkspace?.credentialsRef;
3105
+ if (undeclaredKey(credentialsRef)) {
3106
+ err("connection-key-undeclared", connectionKeyUndeclaredMessage("workspace.credentialsRef", credentialsRef), "workspace.credentialsRef");
3107
+ }
2919
3108
  const seen = /* @__PURE__ */ new Map();
2920
3109
  let nodeCount = 0;
2921
3110
  const upstream = /* @__PURE__ */ new Set();
@@ -2927,6 +3116,16 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
2927
3116
  seen.set(id, path23);
2928
3117
  }
2929
3118
  }, "checkId");
3119
+ const checkPolicyEnums = /* @__PURE__ */ __name3((node, path23) => {
3120
+ const id = singleId(node);
3121
+ const check = /* @__PURE__ */ __name3((member, allowed) => {
3122
+ const value22 = node[member];
3123
+ if (value22 === void 0 || typeof value22 === "string" && allowed.includes(value22)) return;
3124
+ err("invalid-envelope", `\`${member}\` must be ${allowed.map((a) => `'${a}'`).join(" | ")} (got ${JSON.stringify(value22)})`, `${path23}.${member}`, id);
3125
+ }, "check");
3126
+ check("sideEffects", WORKFLOW_SIDE_EFFECTS);
3127
+ check("jobResources", WORKFLOW_JOB_RESOURCES);
3128
+ }, "checkPolicyEnums");
2930
3129
  const checkRetry = /* @__PURE__ */ __name3((node, path23) => {
2931
3130
  const r = node.retry;
2932
3131
  if (!r) return;
@@ -2934,6 +3133,9 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
2934
3133
  if (r.backoff !== void 0 && r.backoff !== "fixed" && r.backoff !== "exponential") {
2935
3134
  err("backoff-invalid", `retry.backoff must be 'fixed' | 'exponential'`, `${path23}.retry.backoff`, id);
2936
3135
  }
3136
+ if (r.backoffSeconds !== void 0 && r.backoffSeconds < 0) {
3137
+ err("backoff-invalid", "retry.backoffSeconds must be \u2265 0", `${path23}.retry.backoffSeconds`, id);
3138
+ }
2937
3139
  if (r.maxBackoffSeconds !== void 0) {
2938
3140
  if (r.backoff !== "exponential") {
2939
3141
  err("backoff-invalid", "retry.maxBackoffSeconds is only meaningful with backoff:'exponential'", `${path23}.retry`, id);
@@ -2995,10 +3197,15 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
2995
3197
  }, "checkSpecialistRole");
2996
3198
  const checkRequiredConnections = /* @__PURE__ */ __name3((node, path23) => {
2997
3199
  const required = node.requiredConnections;
2998
- if (!Array.isArray(required) || !opts.connectionIds) return;
2999
- const unknown = required.filter((c) => typeof c !== "string" || !opts.connectionIds.has(c));
3200
+ if (!Array.isArray(required)) return;
3201
+ const undeclared = required.filter(undeclaredKey);
3202
+ if (undeclared.length) {
3203
+ err("connection-key-undeclared", connectionKeyUndeclaredMessage(`${path23}.requiredConnections`, undeclared[0]) + (undeclared.length > 1 ? ` (also undeclared: ${JSON.stringify(undeclared.slice(1))})` : ""), `${path23}.requiredConnections`, singleId(node));
3204
+ }
3205
+ if (!opts.connectionIds) return;
3206
+ const unknown = required.filter((c) => typeof c !== "string" || !declaredKeys.has(c) && !opts.connectionIds.has(c));
3000
3207
  if (unknown.length) {
3001
- err("required-connection-unknown", `requiredConnections ${JSON.stringify(unknown)} are not connections the owner can mount`, `${path23}.requiredConnections`, singleId(node));
3208
+ err("required-connection-unknown", `requiredConnections ${JSON.stringify(unknown)} are neither declared connections[].key values nor connections the owner can mount`, `${path23}.requiredConnections`, singleId(node));
3002
3209
  }
3003
3210
  }, "checkRequiredConnections");
3004
3211
  const checkTier = /* @__PURE__ */ __name3((node, path23) => {
@@ -3084,6 +3291,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
3084
3291
  return;
3085
3292
  }
3086
3293
  checkId(singleId(node), path23);
3294
+ checkPolicyEnums(node, path23);
3087
3295
  checkTimeout(node, path23);
3088
3296
  checkTier(node, path23);
3089
3297
  checkRetry(node, path23);
@@ -3150,6 +3358,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
3150
3358
  case "sleepUntil": {
3151
3359
  const s = entry;
3152
3360
  checkId(s.id, path23);
3361
+ err("node-type-unsupported-by-engine", sleepUntilUnsupportedMessage(s.id), path23, s.id);
3153
3362
  if (s.date === void 0 === (s.dateFrom === void 0)) {
3154
3363
  err("invalid-envelope", "sleepUntil needs exactly one of `date` | `dateFrom`", path23, s.id);
3155
3364
  }
@@ -3195,7 +3404,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
3195
3404
  err("invalid-envelope", "conditional.predicates must match conditional.steps one-to-one", path23);
3196
3405
  }
3197
3406
  (c.predicates ?? []).forEach((p, j) => {
3198
- if (!isPredicate(p)) err("closure-predicate", "a conditional predicate must be a LuaPredicate object, not a function", `${path23}.predicates.${j}`);
3407
+ 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", `${path23}.predicates.${j}`);
3199
3408
  });
3200
3409
  c.steps.forEach((arm, j) => {
3201
3410
  checkArm(arm, `${path23}.steps.${j}`, 1);
@@ -3275,7 +3484,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
3275
3484
  if (l.intervalSeconds !== void 0 && (!Number.isInteger(l.intervalSeconds) || l.intervalSeconds < 1 || l.intervalSeconds > caps.maxLoopIntervalSeconds)) {
3276
3485
  err("loop-interval-out-of-range", `loop.intervalSeconds must be an integer in 1..${caps.maxLoopIntervalSeconds}`, `${path23}.intervalSeconds`);
3277
3486
  }
3278
- if (!isPredicate(l.predicate)) err("closure-predicate", "a loop predicate must be a LuaPredicate object, not a function", `${path23}.predicate`);
3487
+ 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", `${path23}.predicate`);
3279
3488
  const bodyType = l.step.type;
3280
3489
  if (bodyType === "mapping") err("container-arm-empty", "a loop body needs a step, not a bare mapping", `${path23}.step`);
3281
3490
  else if (bodyType === "approval" || bodyType === "waitForSignal") err("approval-inside-container", "approval / waitForSignal are top-level only in v1", `${path23}.step`);
@@ -3338,6 +3547,41 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
3338
3547
  function isPredicate(p) {
3339
3548
  return typeof p === "object" && p !== null && typeof p.op === "string" && PREDICATE_OPS.has(p.op);
3340
3549
  }
3550
+ function isPathOrLiteral(v) {
3551
+ if (typeof v !== "object" || v === null) return false;
3552
+ const r = v;
3553
+ if ("path" in r) return typeof r.path === "string" && r.path.length > 0;
3554
+ return "literal" in r && isPredicateScalar(r.literal);
3555
+ }
3556
+ function isWellFormedPredicate(p) {
3557
+ if (!isPredicate(p)) return false;
3558
+ const r = p;
3559
+ switch (r.op) {
3560
+ case "eq":
3561
+ case "ne":
3562
+ case "lt":
3563
+ case "lte":
3564
+ case "gt":
3565
+ case "gte":
3566
+ return isPathOrLiteral(r.left) && isPathOrLiteral(r.right);
3567
+ case "in":
3568
+ case "notIn":
3569
+ return isPathOrLiteral(r.value) && Array.isArray(r.set) && r.set.every(isPredicateScalar);
3570
+ case "exists":
3571
+ case "notExists":
3572
+ return typeof r.path === "string" && r.path.length > 0;
3573
+ case "truthy":
3574
+ case "falsy":
3575
+ return isPathOrLiteral(r.value);
3576
+ case "and":
3577
+ case "or":
3578
+ return Array.isArray(r.args) && r.args.every(isWellFormedPredicate);
3579
+ case "not":
3580
+ return isWellFormedPredicate(r.arg);
3581
+ default:
3582
+ return false;
3583
+ }
3584
+ }
3341
3585
  function canonicalJson(value22) {
3342
3586
  const seen = /* @__PURE__ */ new WeakSet();
3343
3587
  const encode = /* @__PURE__ */ __name3((v) => {
@@ -3763,13 +4007,13 @@ function parseTemplatePlaceholder(rawExpr) {
3763
4007
  rest: dot === -1 ? "" : rawExpr.slice(dot + 1)
3764
4008
  };
3765
4009
  }
3766
- function traverseMappingPath(root, path23, errorLabel) {
4010
+ function traverseMappingPath(root, path23, errorLabel2) {
3767
4011
  if (path23 === "" || path23 === ".") return root;
3768
4012
  const parts = path23.split(".");
3769
4013
  let value22 = root;
3770
4014
  for (const part of parts) {
3771
4015
  if (typeof value22 === "object" && value22 !== null) value22 = value22[part];
3772
- else throw new WorkflowTemplateError(`Invalid path ${path23} in ${errorLabel}`, path23);
4016
+ else throw new WorkflowTemplateError(`Invalid path ${path23} in ${errorLabel2}`, path23);
3773
4017
  }
3774
4018
  return value22;
3775
4019
  }
@@ -3900,6 +4144,25 @@ function resolveMapping(cfg, ctx) {
3900
4144
  value: result
3901
4145
  };
3902
4146
  }
4147
+ function continuedFailureValue(error, killReason) {
4148
+ const code = typeof error?.code === "string" && error.code || typeof killReason === "string" && killReason || CONTINUED_FAILURE_DEFAULT_CODE;
4149
+ const message = typeof error?.message === "string" && error.message ? error.message : code;
4150
+ return {
4151
+ __lua_workflow: CONTINUED_FAILURE_TAG,
4152
+ failed: true,
4153
+ error: {
4154
+ code,
4155
+ message
4156
+ },
4157
+ text: ""
4158
+ };
4159
+ }
4160
+ function isContinuedFailureValue(v) {
4161
+ if (v === null || typeof v !== "object" || Array.isArray(v)) return false;
4162
+ const o = v;
4163
+ const err = o.error;
4164
+ 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";
4165
+ }
3903
4166
  function lowerContainerArm(mapping, step22) {
3904
4167
  const stepId = nodeIdOf(step22);
3905
4168
  return {
@@ -3997,7 +4260,7 @@ function resolvePlacements(calls) {
3997
4260
  if (!d) {
3998
4261
  issues.push({
3999
4262
  code: "unknown-step-ref",
4000
- message: `"${ref.ref}" is not declared anywhere in the chain \u2014 declare it with agentStep/specialistStep/toolStep/map(\u2026, { id })`,
4263
+ message: `"${ref.ref}" is not declared anywhere in the chain \u2014 declare it with agentStep/specialistStep/toolStep/map(\u2026, { id })/workflow(\u2026)`,
4001
4264
  callIndex: i,
4002
4265
  stepId: ref.ref
4003
4266
  });
@@ -4049,9 +4312,9 @@ function resolvePlacements(calls) {
4049
4312
  });
4050
4313
  const graph = [];
4051
4314
  const lookup = /* @__PURE__ */ __name3((ref) => {
4052
- const n = "node" in ref ? ref.node : declared.get(ref.ref)?.node;
4053
- if (!n || !ref.armMap || n.type === "mapping") return n;
4054
- return lowerContainerArm(ref.armMap, n);
4315
+ const n2 = "node" in ref ? ref.node : declared.get(ref.ref)?.node;
4316
+ if (!n2 || !ref.armMap || n2.type === "mapping") return n2;
4317
+ return lowerContainerArm(ref.armMap, n2);
4055
4318
  }, "lookup");
4056
4319
  calls.forEach((call, i) => {
4057
4320
  switch (call.kind) {
@@ -4070,7 +4333,7 @@ function resolvePlacements(calls) {
4070
4333
  return;
4071
4334
  }
4072
4335
  case "parallel": {
4073
- const steps = call.arms.map(lookup).filter((n) => !!n && n.type !== "mapping");
4336
+ const steps = call.arms.map(lookup).filter((n2) => !!n2 && n2.type !== "mapping");
4074
4337
  const node = {
4075
4338
  type: "parallel",
4076
4339
  steps
@@ -4083,9 +4346,9 @@ function resolvePlacements(calls) {
4083
4346
  const steps = [];
4084
4347
  const predicates = [];
4085
4348
  for (const a of call.arms) {
4086
- const n = lookup(a.target);
4087
- if (!n) continue;
4088
- steps.push(n);
4349
+ const n2 = lookup(a.target);
4350
+ if (!n2) continue;
4351
+ steps.push(n2);
4089
4352
  predicates.push(a.predicate);
4090
4353
  }
4091
4354
  const node = {
@@ -4189,31 +4452,54 @@ function seedLedgerFromRun(run, steps, targetPlan, opts = {}) {
4189
4452
  graphChanged
4190
4453
  };
4191
4454
  }
4455
+ function branchSpecFromConditional(entry) {
4456
+ return {
4457
+ arms: entry.steps.map((arm, i) => ({
4458
+ stepId: branchArmId(arm),
4459
+ predicate: entry.predicates[i]
4460
+ })),
4461
+ ...entry.exclusive === true ? {
4462
+ exclusive: true
4463
+ } : {},
4464
+ ...entry.otherwise ? {
4465
+ otherwise: branchArmId(entry.otherwise)
4466
+ } : {}
4467
+ };
4468
+ }
4469
+ function selectBranchArms(spec, ctx) {
4470
+ const taken = [];
4471
+ for (const arm of spec.arms) {
4472
+ if (spec.exclusive && taken.length > 0) break;
4473
+ const hit = arm.predicate ? evaluatePredicate(arm.predicate, ctx) : false;
4474
+ if (hit) taken.push(arm.stepId);
4475
+ }
4476
+ if (taken.length === 0 && spec.otherwise) taken.push(spec.otherwise);
4477
+ return taken;
4478
+ }
4192
4479
  function replayLedger(g, ledger) {
4193
4480
  const plan = compilePlan(g);
4194
4481
  const rows22 = new Map(ledger.steps.map((r) => [
4195
4482
  r.stepId,
4196
4483
  r
4197
4484
  ]));
4198
- const stepResults = {};
4199
- for (const r of ledger.steps) if (r.status === "completed") stepResults[r.stepId] = r.output;
4200
- const ctx = {
4485
+ const requestContext = {
4486
+ runId: "replay",
4487
+ workflowId: g.definition.id,
4488
+ orgId: "",
4489
+ agentId: "",
4490
+ userId: "",
4491
+ trigger: "sdk",
4492
+ threadId: "replay",
4493
+ depth: 0,
4494
+ startedAt: 0,
4495
+ ...ledger.requestContext
4496
+ };
4497
+ const ctxFor = /* @__PURE__ */ __name3((id) => ({
4201
4498
  initData: ledger.initData,
4202
- stepResults,
4499
+ stepResults: ancestorResults(plan, id, rows22),
4203
4500
  state: ledger.state ?? {},
4204
- requestContext: {
4205
- runId: "replay",
4206
- workflowId: g.definition.id,
4207
- orgId: "",
4208
- agentId: "",
4209
- userId: "",
4210
- trigger: "sdk",
4211
- threadId: "replay",
4212
- depth: 0,
4213
- startedAt: 0,
4214
- ...ledger.requestContext
4215
- }
4216
- };
4501
+ requestContext
4502
+ }), "ctxFor");
4217
4503
  const verdicts = [];
4218
4504
  for (const id of plan.order) {
4219
4505
  const node = plan.steps[id];
@@ -4221,17 +4507,7 @@ function replayLedger(g, ledger) {
4221
4507
  if (!recorded || recorded.status === "pending" || recorded.status === "skipped") continue;
4222
4508
  if (node.kind === "branch") {
4223
4509
  const entry = node.entry;
4224
- const taken = [];
4225
- entry.steps.forEach((arm, i) => {
4226
- const pred = entry.predicates[i];
4227
- const hit = pred ? evaluatePredicate(pred, {
4228
- initData: ledger.initData,
4229
- stepResults,
4230
- state: ledger.state
4231
- }) : false;
4232
- if (hit && (!entry.exclusive || taken.length === 0)) taken.push(armId2(arm));
4233
- });
4234
- if (taken.length === 0 && entry.otherwise) taken.push(armId2(entry.otherwise));
4510
+ const taken = selectBranchArms(branchSpecFromConditional(entry), ctxFor(id));
4235
4511
  const recordedTaken = recorded.taken ?? inferTaken(entry, rows22);
4236
4512
  verdicts.push({
4237
4513
  stepId: id,
@@ -4242,7 +4518,7 @@ function replayLedger(g, ledger) {
4242
4518
  });
4243
4519
  } else if (node.kind === "map" && !id.endsWith(".join") && recorded.status === "completed") {
4244
4520
  const entry = node.entry;
4245
- const resolved = resolveMapping(parseMapConfig(entry.mapConfig, id), ctx);
4521
+ const resolved = resolveMapping(parseMapConfig(entry.mapConfig, id), ctxFor(id));
4246
4522
  const local = "error" in resolved ? {
4247
4523
  error: resolved.error,
4248
4524
  key: resolved.key
@@ -4257,7 +4533,7 @@ function replayLedger(g, ledger) {
4257
4533
  } else if (node.kind === "foreach") {
4258
4534
  const entry = node.entry;
4259
4535
  const source = node.dependsOn[0];
4260
- const items = source ? stepResults[source] : void 0;
4536
+ const items = source ? ctxFor(id).stepResults[source] : void 0;
4261
4537
  const local = Array.isArray(items) ? items.length : void 0;
4262
4538
  const recordedCount = recorded.itemCount ?? countChildren(entry, rows22);
4263
4539
  verdicts.push({
@@ -4274,6 +4550,71 @@ function replayLedger(g, ledger) {
4274
4550
  diverged: verdicts.some((v) => v.diverged)
4275
4551
  };
4276
4552
  }
4553
+ function replayResultOf(row2, node) {
4554
+ if (!row2) return void 0;
4555
+ if (row2.status === "completed") return {
4556
+ value: row2.output
4557
+ };
4558
+ if (row2.status === "failed") {
4559
+ const onError = row2.onError ?? node?.entry?.onError;
4560
+ if (onError === "continue") return {
4561
+ value: continuedFailureValue(row2.error, row2.killReason)
4562
+ };
4563
+ }
4564
+ return void 0;
4565
+ }
4566
+ function ancestorResults(plan, id, rows22) {
4567
+ const out = {};
4568
+ const joinAliased = /* @__PURE__ */ new Set();
4569
+ const seen = /* @__PURE__ */ new Set();
4570
+ const take = /* @__PURE__ */ __name3((rowId) => {
4571
+ const hit = replayResultOf(rows22.get(rowId), plan.steps[rowId]);
4572
+ if (!hit) return void 0;
4573
+ if (!joinAliased.has(rowId)) out[rowId] = hit.value;
4574
+ const entryId = entryOfJoin(rowId);
4575
+ if (entryId) {
4576
+ out[entryId] = hit.value;
4577
+ joinAliased.add(entryId);
4578
+ const entryNode = plan.steps[entryId];
4579
+ if (entryNode?.kind === "foreach") {
4580
+ const body = branchArmId(entryNode.entry.step);
4581
+ out[body] = hit.value;
4582
+ joinAliased.add(body);
4583
+ }
4584
+ }
4585
+ return hit;
4586
+ }, "take");
4587
+ const walk22 = /* @__PURE__ */ __name3((ids) => {
4588
+ for (const dep of ids) {
4589
+ if (seen.has(dep)) continue;
4590
+ seen.add(dep);
4591
+ take(dep);
4592
+ const node = plan.steps[dep] ?? plan.steps[entryOfJoin(dep) ?? ""];
4593
+ if (!node) continue;
4594
+ if (node.kind === "foreach") {
4595
+ const body = branchArmId(node.entry.step);
4596
+ for (const rowId of rows22.keys()) if (rowId.startsWith(`${body}[`)) take(rowId);
4597
+ } else if (node.kind === "loop") {
4598
+ const body = branchArmId(node.entry.step);
4599
+ let best = -1;
4600
+ for (const rowId of rows22.keys()) {
4601
+ if (!rowId.startsWith(`${body}#`)) continue;
4602
+ const n2 = Number(rowId.slice(body.length + 1));
4603
+ if (!Number.isInteger(n2)) continue;
4604
+ const hit = take(rowId);
4605
+ if (!hit) continue;
4606
+ if (n2 > best) {
4607
+ best = n2;
4608
+ out[body] = hit.value;
4609
+ }
4610
+ }
4611
+ }
4612
+ walk22(node.dependsOn);
4613
+ }
4614
+ }, "walk");
4615
+ walk22(plan.steps[id]?.dependsOn ?? []);
4616
+ return out;
4617
+ }
4277
4618
  function inferTaken(entry, rows22) {
4278
4619
  const arms = [
4279
4620
  ...entry.steps,
@@ -4281,16 +4622,16 @@ function inferTaken(entry, rows22) {
4281
4622
  entry.otherwise
4282
4623
  ] : []
4283
4624
  ];
4284
- return arms.map(armId2).filter((id) => {
4625
+ return arms.map(branchArmId).filter((id) => {
4285
4626
  const r = rows22.get(id);
4286
4627
  return r !== void 0 && r.status !== "skipped" && r.status !== "pending";
4287
4628
  });
4288
4629
  }
4289
4630
  function countChildren(entry, rows22) {
4290
- const body = armId2(entry.step);
4291
- let n = 0;
4292
- for (const id of rows22.keys()) if (id.startsWith(`${body}[`)) n += 1;
4293
- return n;
4631
+ const body = branchArmId(entry.step);
4632
+ let n2 = 0;
4633
+ for (const id of rows22.keys()) if (id.startsWith(`${body}[`)) n2 += 1;
4634
+ return n2;
4294
4635
  }
4295
4636
  function isTerminalRunStatus(status) {
4296
4637
  return TERMINAL.has(status);
@@ -4299,19 +4640,48 @@ function pruneUndefined(o) {
4299
4640
  return Object.fromEntries(Object.entries(o).filter(([, v]) => v !== void 0));
4300
4641
  }
4301
4642
  function runNextAction(run) {
4302
- if (isTerminalRunStatus(run.status) || !run.cancel?.requestedAt) return "none";
4643
+ if (isTerminalRunStatus(run.status)) return "none";
4644
+ if (run.status === "suspended" && run.gate?.kind === "budget") return "raise_budget";
4645
+ if (!run.cancel?.requestedAt) return "none";
4303
4646
  const forceAt = run.cancel.forceAfter ?? run.cancel.requestedAt + FORCE_CANCEL_STALE_MS;
4304
4647
  return Date.now() >= forceAt ? "force" : "cancel_again";
4305
4648
  }
4649
+ function emptyRunCounts() {
4650
+ const out = {
4651
+ total: 0,
4652
+ inFlight: 0
4653
+ };
4654
+ for (const status of WORKFLOW_STEP_STATUSES) out[status] = 0;
4655
+ return out;
4656
+ }
4657
+ function runCountsFromStatusTally(tally) {
4658
+ const out = emptyRunCounts();
4659
+ for (const [status, n2] of Object.entries(tally)) {
4660
+ if (!Number.isFinite(n2) || n2 <= 0) continue;
4661
+ out.total += n2;
4662
+ if (WORKFLOW_STEP_STATUSES.includes(status)) out[status] += n2;
4663
+ if (IN_FLIGHT.has(status)) out.inFlight += n2;
4664
+ }
4665
+ return out;
4666
+ }
4667
+ function runCountsFromStepStatuses(statuses) {
4668
+ const tally = {};
4669
+ for (const s of statuses) tally[s] = (tally[s] ?? 0) + 1;
4670
+ return runCountsFromStatusTally(tally);
4671
+ }
4306
4672
  function runCounts(counts) {
4673
+ const c = counts ?? {};
4674
+ const rawInFlight = c.dispatched !== void 0 || c.claimed !== void 0 || c.running !== void 0 || c.cancellation_requested !== void 0;
4307
4675
  return {
4308
- total: counts?.total ?? 0,
4309
- completed: counts?.completed ?? 0,
4310
- failed: counts?.failed ?? 0,
4311
- skipped: counts?.skipped ?? 0,
4312
- running: counts?.inFlight ?? 0,
4313
- suspended: counts?.suspended ?? 0,
4314
- pending: (counts?.pending ?? 0) + (counts?.ready ?? 0) + (counts?.waiting ?? 0)
4676
+ total: n(c.total),
4677
+ completed: n(c.completed),
4678
+ failed: n(c.failed) + n(c.timeout) + n(c.reaped),
4679
+ skipped: n(c.skipped),
4680
+ // LUA-664: cancelled rows never ran — their own wire bucket, never a failure (the detail's map, verbatim).
4681
+ cancelled: n(c.cancelled),
4682
+ running: rawInFlight ? n(c.dispatched) + n(c.claimed) + n(c.running) + n(c.cancellation_requested) : n(c.inFlight),
4683
+ suspended: n(c.suspended),
4684
+ pending: n(c.pending) + n(c.ready) + n(c.waiting)
4315
4685
  };
4316
4686
  }
4317
4687
  function runUsage(run) {
@@ -4335,7 +4705,7 @@ function runWorkspaceView(ws) {
4335
4705
  if (!ws) return void 0;
4336
4706
  const w = ws;
4337
4707
  return pruneUndefined({
4338
- kind: w.kind ?? "empty",
4708
+ kind: w.spec?.kind ?? w.kind ?? "empty",
4339
4709
  backend: w.backend,
4340
4710
  status: String(w.status ?? ""),
4341
4711
  branch: w.branch,
@@ -4397,7 +4767,10 @@ function toWorkflowRunSummary(run) {
4397
4767
  eventSeq: run.eventSeq ?? 0,
4398
4768
  cancellable: !isTerminalRunStatus(status),
4399
4769
  nextAction: runNextAction(run),
4400
- workspace: runWorkspaceView(run.workspace)
4770
+ workspace: runWorkspaceView(run.workspace),
4771
+ // LUA-643: the runs list says a result exists; R4 `fields:'full'` (output-ACL gated, audited) serves it. The
4772
+ // stamped flag is what a list page carries (it projects `output` out); the payload members cover R4's full row.
4773
+ hasOutput: run.hasOutput === true || run.output !== void 0 || run.outputRef !== void 0 ? true : void 0
4401
4774
  });
4402
4775
  }
4403
4776
  function timeZoneSupported(tz) {
@@ -5092,8 +5465,8 @@ function armEntry(arm) {
5092
5465
  }
5093
5466
  function ofEntry(e) {
5094
5467
  if (!e || typeof e !== "object") return ZERO;
5095
- const n = e;
5096
- switch (n.type) {
5468
+ const n2 = e;
5469
+ switch (n2.type) {
5097
5470
  case "agent":
5098
5471
  return {
5099
5472
  steps: {
@@ -5126,17 +5499,17 @@ function ofEntry(e) {
5126
5499
  agentCalls: 0
5127
5500
  };
5128
5501
  case "parallel": {
5129
- const arms = Array.isArray(n.steps) ? n.steps : [];
5502
+ const arms = Array.isArray(n2.steps) ? n2.steps : [];
5130
5503
  return arms.map(armEntry).map(ofEntry).reduce(add, ZERO);
5131
5504
  }
5132
5505
  case "conditional": {
5133
- const arms = (Array.isArray(n.steps) ? n.steps : []).map(armEntry).map(ofEntry);
5506
+ const arms = (Array.isArray(n2.steps) ? n2.steps : []).map(armEntry).map(ofEntry);
5134
5507
  if (arms.length === 0) return ZERO;
5135
- const hasOtherwise = n.otherwise !== void 0;
5508
+ const hasOtherwise = n2.otherwise !== void 0;
5136
5509
  const minArm = arms.reduce((a, b) => b.credits.min < a.credits.min ? b : a);
5137
5510
  const maxArm = arms.reduce((a, b) => b.credits.max > a.credits.max ? b : a);
5138
5511
  const summed = arms.reduce(add, ZERO);
5139
- const max = n.exclusive === true ? maxArm : summed;
5512
+ const max = n2.exclusive === true ? maxArm : summed;
5140
5513
  const min = hasOtherwise ? minArm : {
5141
5514
  ...ZERO
5142
5515
  };
@@ -5153,17 +5526,14 @@ function ofEntry(e) {
5153
5526
  };
5154
5527
  }
5155
5528
  case "foreach": {
5156
- const body = ofEntry(armEntry(n.step));
5157
- const opts = n.options ?? {};
5158
- const cap = typeof opts.maxItems === "number" && opts.maxItems > 0 ? opts.maxItems : 1;
5529
+ const body = ofEntry(armEntry(n2.step));
5530
+ const opts = n2.opts ?? {};
5531
+ const cap = typeof opts.maxItems === "number" && opts.maxItems > 0 ? opts.maxItems : WORKFLOW_FOREACH_DEFAULT_MAX_ITEMS;
5159
5532
  return scale(body, 0, cap);
5160
5533
  }
5161
- case "loop":
5162
- case "dowhile":
5163
- case "dountil": {
5164
- const body = ofEntry(armEntry(n.step));
5165
- const opts = n.options ?? {};
5166
- const cap = typeof opts.maxIterations === "number" && opts.maxIterations > 0 ? opts.maxIterations : 1;
5534
+ case "loop": {
5535
+ const body = ofEntry(armEntry(n2.step));
5536
+ const cap = typeof n2.maxIterations === "number" && n2.maxIterations > 0 ? n2.maxIterations : WORKFLOW_LOOP_DEFAULT_MAX_ITERATIONS;
5167
5537
  return scale(body, 1, cap);
5168
5538
  }
5169
5539
  default:
@@ -5194,14 +5564,84 @@ function estimateGraph(envelopeOrGraph) {
5194
5564
  consentCredits: r.credits.max
5195
5565
  };
5196
5566
  }
5197
- 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;
5567
+ function* singleStepsOf(entry) {
5568
+ if (!isRecord2(entry)) return;
5569
+ switch (entry.type) {
5570
+ case "step":
5571
+ case "agent":
5572
+ case "tool":
5573
+ yield entry;
5574
+ return;
5575
+ case "workflow":
5576
+ yield entry;
5577
+ if (Array.isArray(entry.graph)) yield* singleStepsOf(entry.graph[1]);
5578
+ return;
5579
+ case "parallel":
5580
+ case "conditional":
5581
+ if (Array.isArray(entry.steps)) for (const arm of entry.steps) yield* singleStepsOf(arm);
5582
+ yield* singleStepsOf(entry.otherwise);
5583
+ return;
5584
+ case "foreach":
5585
+ case "loop":
5586
+ yield* singleStepsOf(entry.step);
5587
+ return;
5588
+ default:
5589
+ return;
5590
+ }
5591
+ }
5592
+ function entriesOf(graph) {
5593
+ const definition = isRecord2(graph) ? graph.definition : void 0;
5594
+ const entries = isRecord2(definition) ? definition.graph : void 0;
5595
+ return Array.isArray(entries) ? entries : [];
5596
+ }
5597
+ function inheritTargets(graphs) {
5598
+ const targets = /* @__PURE__ */ new Set();
5599
+ for (const graph of graphs) {
5600
+ for (const entry of entriesOf(graph)) {
5601
+ for (const node of singleStepsOf(entry)) {
5602
+ if (node.type === "workflow" && node.workspace === "inherit" && typeof node.workflowId === "string" && node.workflowId.length > 0) {
5603
+ targets.add(node.workflowId);
5604
+ }
5605
+ }
5606
+ }
5607
+ }
5608
+ return targets;
5609
+ }
5610
+ function needsInheritedWorkspace(graph) {
5611
+ if (!isRecord2(graph) || graph.workspace !== void 0) return false;
5612
+ for (const entry of entriesOf(graph)) {
5613
+ for (const node of singleStepsOf(entry)) {
5614
+ if (node.workspace !== void 0 && node.workspace !== "inherit") return true;
5615
+ }
5616
+ }
5617
+ return false;
5618
+ }
5619
+ 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;
5198
5620
  var init_dist2 = __esm({
5199
5621
  "../workflow-graph/dist/index.mjs"() {
5200
5622
  "use strict";
5201
5623
  init_dist();
5624
+ init_dist();
5202
5625
  __defProp3 = Object.defineProperty;
5203
5626
  __name3 = /* @__PURE__ */ __name((target, value22) => __defProp3(target, "name", { value: value22, configurable: true }), "__name");
5627
+ WORKFLOW_SIDE_EFFECTS = [
5628
+ "none",
5629
+ "external"
5630
+ ];
5631
+ WORKFLOW_JOB_RESOURCES = [
5632
+ "small",
5633
+ "medium",
5634
+ "large"
5635
+ ];
5636
+ SideEffectsSchema = z6.enum(WORKFLOW_SIDE_EFFECTS);
5637
+ JobResourcesSchema = z6.enum(WORKFLOW_JOB_RESOURCES);
5204
5638
  WORKFLOW_ARM_SUBRUN_ID = "$arm";
5639
+ SLEEP_UNTIL_REPLACEMENT = Object.freeze({
5640
+ type: "sleep",
5641
+ duration: 6e4
5642
+ });
5643
+ __name(sleepUntilUnsupportedMessage, "sleepUntilUnsupportedMessage");
5644
+ __name3(sleepUntilUnsupportedMessage, "sleepUntilUnsupportedMessage");
5205
5645
  WORKFLOW_CAPS_DEFAULT = Object.freeze({
5206
5646
  maxParallelArms: 16,
5207
5647
  maxForeachConcurrency: 16,
@@ -5237,6 +5677,11 @@ var init_dist2 = __esm({
5237
5677
  __name3(fillEntry, "fillEntry");
5238
5678
  __name(withDefaultsFilled, "withDefaultsFilled");
5239
5679
  __name3(withDefaultsFilled, "withDefaultsFilled");
5680
+ CONNECTION_ID_HEX_RE = /^[0-9a-f]{24}$/;
5681
+ __name(isConnectionKeyShaped, "isConnectionKeyShaped");
5682
+ __name3(isConnectionKeyShaped, "isConnectionKeyShaped");
5683
+ __name(connectionKeyUndeclaredMessage, "connectionKeyUndeclaredMessage");
5684
+ __name3(connectionKeyUndeclaredMessage, "connectionKeyUndeclaredMessage");
5240
5685
  WORKFLOW_JOB_TOOLS = [
5241
5686
  "shell",
5242
5687
  "read",
@@ -5300,6 +5745,11 @@ var init_dist2 = __esm({
5300
5745
  ]);
5301
5746
  __name(isPredicate, "isPredicate");
5302
5747
  __name3(isPredicate, "isPredicate");
5748
+ isPredicateScalar = /* @__PURE__ */ __name3((v) => v === null || typeof v === "string" || typeof v === "number" || typeof v === "boolean", "isPredicateScalar");
5749
+ __name(isPathOrLiteral, "isPathOrLiteral");
5750
+ __name3(isPathOrLiteral, "isPathOrLiteral");
5751
+ __name(isWellFormedPredicate, "isWellFormedPredicate");
5752
+ __name3(isWellFormedPredicate, "isWellFormedPredicate");
5303
5753
  GRAPH_HASH_PREFIX = "sha256-cj1:";
5304
5754
  __name(canonicalJson, "canonicalJson");
5305
5755
  __name3(canonicalJson, "canonicalJson");
@@ -5488,16 +5938,71 @@ var init_dist2 = __esm({
5488
5938
  fromKnowledge = /* @__PURE__ */ __name3((k) => ({
5489
5939
  knowledge: k
5490
5940
  }), "fromKnowledge");
5941
+ CONTINUED_FAILURE_TAG = "continued_failure";
5942
+ CONTINUED_FAILURE_DEFAULT_CODE = "step_failed";
5943
+ CONTINUED_FAILURE_OUTPUT_SCHEMA = Object.freeze({
5944
+ type: "object",
5945
+ properties: {
5946
+ __lua_workflow: {
5947
+ type: "string",
5948
+ const: CONTINUED_FAILURE_TAG
5949
+ },
5950
+ failed: {
5951
+ type: "boolean",
5952
+ const: true
5953
+ },
5954
+ error: {
5955
+ type: "object",
5956
+ properties: {
5957
+ code: {
5958
+ type: "string"
5959
+ },
5960
+ message: {
5961
+ type: "string"
5962
+ }
5963
+ },
5964
+ required: [
5965
+ "code",
5966
+ "message"
5967
+ ]
5968
+ },
5969
+ text: {
5970
+ type: "string",
5971
+ const: ""
5972
+ }
5973
+ },
5974
+ required: [
5975
+ "__lua_workflow",
5976
+ "failed",
5977
+ "error",
5978
+ "text"
5979
+ ]
5980
+ });
5981
+ CONTINUED_FAILURE_LEAF_PATHS = Object.freeze([
5982
+ "failed",
5983
+ "error",
5984
+ "error.code",
5985
+ "error.message",
5986
+ "text"
5987
+ ]);
5988
+ __name(continuedFailureValue, "continuedFailureValue");
5989
+ __name3(continuedFailureValue, "continuedFailureValue");
5990
+ __name(isContinuedFailureValue, "isContinuedFailureValue");
5991
+ __name3(isContinuedFailureValue, "isContinuedFailureValue");
5491
5992
  __name(lowerContainerArm, "lowerContainerArm");
5492
5993
  __name3(lowerContainerArm, "lowerContainerArm");
5493
- nodeIdOf = /* @__PURE__ */ __name3((n) => n.type === "step" ? n.step.id : n.id, "nodeIdOf");
5994
+ nodeIdOf = /* @__PURE__ */ __name3((n2) => n2.type === "step" ? n2.step.id : n2.id, "nodeIdOf");
5494
5995
  __name(entryIds, "entryIds");
5495
5996
  __name3(entryIds, "entryIds");
5496
5997
  __name(resolvePlacements, "resolvePlacements");
5497
5998
  __name3(resolvePlacements, "resolvePlacements");
5498
5999
  __name(seedLedgerFromRun, "seedLedgerFromRun");
5499
6000
  __name3(seedLedgerFromRun, "seedLedgerFromRun");
5500
- armId2 = /* @__PURE__ */ __name3((arm) => arm.type === "step" ? arm.step.id : arm.id, "armId");
6001
+ branchArmId = /* @__PURE__ */ __name3((arm) => arm.type === "step" ? arm.step.id : arm.id, "branchArmId");
6002
+ __name(branchSpecFromConditional, "branchSpecFromConditional");
6003
+ __name3(branchSpecFromConditional, "branchSpecFromConditional");
6004
+ __name(selectBranchArms, "selectBranchArms");
6005
+ __name3(selectBranchArms, "selectBranchArms");
5501
6006
  canonical = /* @__PURE__ */ __name3((v) => JSON.stringify(sortKeys(v)), "canonical");
5502
6007
  sortKeys = /* @__PURE__ */ __name3((v) => {
5503
6008
  if (Array.isArray(v)) return v.map(sortKeys);
@@ -5511,6 +6016,12 @@ var init_dist2 = __esm({
5511
6016
  }, "sortKeys");
5512
6017
  __name(replayLedger, "replayLedger");
5513
6018
  __name3(replayLedger, "replayLedger");
6019
+ JOIN = ".join";
6020
+ entryOfJoin = /* @__PURE__ */ __name3((id) => id.endsWith(JOIN) ? id.slice(0, -JOIN.length) : void 0, "entryOfJoin");
6021
+ __name(replayResultOf, "replayResultOf");
6022
+ __name3(replayResultOf, "replayResultOf");
6023
+ __name(ancestorResults, "ancestorResults");
6024
+ __name3(ancestorResults, "ancestorResults");
5514
6025
  __name(inferTaken, "inferTaken");
5515
6026
  __name3(inferTaken, "inferTaken");
5516
6027
  __name(countChildren, "countChildren");
@@ -5523,6 +6034,14 @@ var init_dist2 = __esm({
5523
6034
  __name3(pruneUndefined, "pruneUndefined");
5524
6035
  __name(runNextAction, "runNextAction");
5525
6036
  __name3(runNextAction, "runNextAction");
6037
+ IN_FLIGHT = new Set(WORKFLOW_STEP_IN_FLIGHT);
6038
+ __name(emptyRunCounts, "emptyRunCounts");
6039
+ __name3(emptyRunCounts, "emptyRunCounts");
6040
+ __name(runCountsFromStatusTally, "runCountsFromStatusTally");
6041
+ __name3(runCountsFromStatusTally, "runCountsFromStatusTally");
6042
+ __name(runCountsFromStepStatuses, "runCountsFromStepStatuses");
6043
+ __name3(runCountsFromStepStatuses, "runCountsFromStepStatuses");
6044
+ n = /* @__PURE__ */ __name3((v) => typeof v === "number" && Number.isFinite(v) ? v : 0, "n");
5526
6045
  __name(runCounts, "runCounts");
5527
6046
  __name3(runCounts, "runCounts");
5528
6047
  __name(runUsage, "runUsage");
@@ -5626,54 +6145,54 @@ var init_dist2 = __esm({
5626
6145
  __name3(rebaseItemPointer, "rebaseItemPointer");
5627
6146
  APPROVER_SPEC_MAX_USERS = 20;
5628
6147
  ESCALATION_MAX_HOPS = 3;
5629
- TemplateBindingSchema = z6.object({
5630
- template: z6.string().min(1).max(2048)
6148
+ TemplateBindingSchema = z22.object({
6149
+ template: z22.string().min(1).max(2048)
5631
6150
  }).strict();
5632
- ApproverSpecSchema = z6.union([
5633
- z6.literal("creator"),
5634
- z6.literal("org-admins"),
5635
- z6.object({
5636
- users: z6.union([
5637
- z6.array(z6.string().min(1).max(128)).min(1).max(APPROVER_SPEC_MAX_USERS),
6151
+ ApproverSpecSchema = z22.union([
6152
+ z22.literal("creator"),
6153
+ z22.literal("org-admins"),
6154
+ z22.object({
6155
+ users: z22.union([
6156
+ z22.array(z22.string().min(1).max(128)).min(1).max(APPROVER_SPEC_MAX_USERS),
5638
6157
  TemplateBindingSchema
5639
6158
  ])
5640
6159
  }).strict(),
5641
- z6.object({
5642
- role: z6.union([
5643
- z6.string().min(1).max(128),
6160
+ z22.object({
6161
+ role: z22.union([
6162
+ z22.string().min(1).max(128),
5644
6163
  TemplateBindingSchema
5645
6164
  ])
5646
6165
  }).strict(),
5647
- z6.object({
5648
- group: z6.union([
5649
- z6.string().min(1).max(128),
6166
+ z22.object({
6167
+ group: z22.union([
6168
+ z22.string().min(1).max(128),
5650
6169
  TemplateBindingSchema
5651
6170
  ])
5652
6171
  }).strict(),
5653
- z6.object({
5654
- governance: z6.object({
5655
- policyId: z6.string().min(1).max(128)
6172
+ z22.object({
6173
+ governance: z22.object({
6174
+ policyId: z22.string().min(1).max(128)
5656
6175
  }).strict()
5657
6176
  }).strict()
5658
6177
  ]);
5659
- FourEyesSchema = z6.object({
6178
+ FourEyesSchema = z22.object({
5660
6179
  edit: ApproverSpecSchema,
5661
6180
  approve: ApproverSpecSchema
5662
6181
  }).strict();
5663
- EscalationHopSchema = z6.object({
6182
+ EscalationHopSchema = z22.object({
5664
6183
  escalateTo: ApproverSpecSchema,
5665
- timeoutHours: z6.number().finite().min(1).max(720)
6184
+ timeoutHours: z22.number().finite().min(1).max(720)
5666
6185
  }).strict();
5667
- TerminalOutcomeSchema = z6.enum([
6186
+ TerminalOutcomeSchema = z22.enum([
5668
6187
  "deny",
5669
6188
  "cancel-run",
5670
6189
  "fail",
5671
6190
  "continue"
5672
6191
  ]);
5673
- ApprovalOnTimeoutSchema = z6.union([
6192
+ ApprovalOnTimeoutSchema = z22.union([
5674
6193
  TerminalOutcomeSchema,
5675
6194
  EscalationHopSchema,
5676
- z6.array(z6.union([
6195
+ z22.array(z22.union([
5677
6196
  TerminalOutcomeSchema,
5678
6197
  EscalationHopSchema
5679
6198
  ])).min(1).max(ESCALATION_MAX_HOPS + 1)
@@ -5726,6 +6245,15 @@ var init_dist2 = __esm({
5726
6245
  __name3(ofEntry, "ofEntry");
5727
6246
  __name(estimateGraph, "estimateGraph");
5728
6247
  __name3(estimateGraph, "estimateGraph");
6248
+ isRecord2 = /* @__PURE__ */ __name3((v) => !!v && typeof v === "object" && !Array.isArray(v), "isRecord");
6249
+ __name(singleStepsOf, "singleStepsOf");
6250
+ __name3(singleStepsOf, "singleStepsOf");
6251
+ __name(entriesOf, "entriesOf");
6252
+ __name3(entriesOf, "entriesOf");
6253
+ __name(inheritTargets, "inheritTargets");
6254
+ __name3(inheritTargets, "inheritTargets");
6255
+ __name(needsInheritedWorkspace, "needsInheritedWorkspace");
6256
+ __name3(needsInheritedWorkspace, "needsInheritedWorkspace");
5729
6257
  }
5730
6258
  });
5731
6259
 
@@ -5784,16 +6312,16 @@ function stepNodeOf(s) {
5784
6312
  });
5785
6313
  }
5786
6314
  function materializeEntry(entry, steps) {
5787
- const single = /* @__PURE__ */ __name((n) => {
5788
- if (n.type === "step" && steps[n.step.id]) return stepNodeOf(steps[n.step.id]);
5789
- if (n.type === "workflow" && n.workflowId === WORKFLOW_ARM_SUBRUN_ID && n.graph) return {
5790
- ...n,
6315
+ const single = /* @__PURE__ */ __name((n2) => {
6316
+ if (n2.type === "step" && steps[n2.step.id]) return stepNodeOf(steps[n2.step.id]);
6317
+ if (n2.type === "workflow" && n2.workflowId === WORKFLOW_ARM_SUBRUN_ID && n2.graph) return {
6318
+ ...n2,
5791
6319
  graph: [
5792
- n.graph[0],
5793
- single(n.graph[1])
6320
+ n2.graph[0],
6321
+ single(n2.graph[1])
5794
6322
  ]
5795
6323
  };
5796
- return n;
6324
+ return n2;
5797
6325
  }, "single");
5798
6326
  switch (entry.type) {
5799
6327
  case "step":
@@ -5838,8 +6366,8 @@ function createWorkflow(cfg) {
5838
6366
  if (v.roles.length > 20 || (v.users?.length ?? 0) > 50) throw new LuaWorkflowBuildError("cap-exceeded", "outputVisibility allows \u2264 20 roles and \u2264 50 users");
5839
6367
  }
5840
6368
  if (cfg.backfillOnEnable?.maxOccurrences !== void 0) {
5841
- const n = cfg.backfillOnEnable.maxOccurrences;
5842
- 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)");
6369
+ const n2 = cfg.backfillOnEnable.maxOccurrences;
6370
+ 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)");
5843
6371
  }
5844
6372
  const keys = /* @__PURE__ */ new Set();
5845
6373
  envRefKeys(cfg.schedule, keys);
@@ -6033,7 +6561,7 @@ var init_workflow = __esm({
6033
6561
  getEnvTemplateKeys() {
6034
6562
  return this.built.envTemplateKeys;
6035
6563
  }
6036
- /** `.workflow(id, ref)` targets by name — `ManifestWorkflow.workflowRefs`. */
6564
+ /** `.workflow(id, ref)` targets by name — `ManifestWorkflow.workflowRefs`; `workspace:'inherit'` marks the inherit children the compiler defers `workspace-not-declared` for. */
6037
6565
  getNestedWorkflowRefs() {
6038
6566
  return this.built.nestedRefs;
6039
6567
  }
@@ -6063,6 +6591,7 @@ var init_workflow = __esm({
6063
6591
  };
6064
6592
  if (cfg.concurrencyPolicy !== void 0) envelope.concurrencyPolicy = cfg.concurrencyPolicy;
6065
6593
  if (cfg.workspace !== void 0) envelope.workspace = cfg.workspace;
6594
+ if (cfg.connections !== void 0) envelope.connections = cfg.connections;
6066
6595
  return withDefaultsFilled(envelope);
6067
6596
  }
6068
6597
  };
@@ -6098,7 +6627,6 @@ var init_workflow = __esm({
6098
6627
  if (this.steps[s.id] && this.steps[s.id] !== s) throw new LuaWorkflowBuildError("duplicate-step-id", `step id "${s.id}" is declared twice`);
6099
6628
  const tier = s.tier ?? (s.workspace ? "job" : void 0);
6100
6629
  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'`);
6101
- if (s.workspace && !this.config.workspace) throw new LuaWorkflowBuildError("workspace-not-declared", `"${s.id}" mounts a workspace but createWorkflow declares none`);
6102
6630
  if (s.jobTools && tier !== "job") throw new LuaWorkflowBuildError("cap-exceeded", `"${s.id}": jobTools require tier:'job' (job-tools-require-job-tier)`);
6103
6631
  assertTimeout({
6104
6632
  id: s.id,
@@ -6339,13 +6867,20 @@ var init_workflow = __esm({
6339
6867
  }
6340
6868
  const tier = opts.tier ?? (opts.workspace ? "job" : void 0);
6341
6869
  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'`);
6342
- if (opts.workspace && !this.config.workspace) throw new LuaWorkflowBuildError("workspace-not-declared", `"${id}" mounts a workspace but createWorkflow declares none`);
6343
6870
  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`);
6344
6871
  if (opts.toolScope?.jobTools && tier !== "job") throw new LuaWorkflowBuildError("cap-exceeded", `"${id}": toolScope.jobTools require tier:'job' (job-tools-require-job-tier)`);
6345
6872
  if (opts.maxTurns !== void 0) {
6346
6873
  if (tier !== "job") throw new LuaWorkflowBuildError("max-turns-requires-job-tier", `"${id}": maxTurns is only legal on a tier:'job' agent step`);
6347
6874
  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`);
6348
6875
  }
6876
+ if (opts.maxMessages !== void 0) {
6877
+ if (tier !== "job") throw new LuaWorkflowBuildError("max-turns-requires-job-tier", `"${id}": maxMessages is only legal on a tier:'job' agent step`);
6878
+ 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`);
6879
+ }
6880
+ if (opts.maxInputTokens !== void 0) {
6881
+ if (tier !== "job") throw new LuaWorkflowBuildError("max-turns-requires-job-tier", `"${id}": maxInputTokens is only legal on a tier:'job' agent step`);
6882
+ 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`);
6883
+ }
6349
6884
  assertTimeout({
6350
6885
  id,
6351
6886
  timeoutSeconds: opts.timeoutSeconds,
@@ -6372,7 +6907,9 @@ var init_workflow = __esm({
6372
6907
  workspace: opts.workspace,
6373
6908
  jobResources: opts.jobResources,
6374
6909
  harness: opts.harness,
6375
- maxTurns: opts.maxTurns
6910
+ maxTurns: opts.maxTurns,
6911
+ maxMessages: opts.maxMessages,
6912
+ maxInputTokens: opts.maxInputTokens
6376
6913
  });
6377
6914
  return this.push({
6378
6915
  kind: "declare",
@@ -6527,7 +7064,11 @@ var init_workflow = __esm({
6527
7064
  }
6528
7065
  assertNoClosure(input, `workflow("${id}").input`);
6529
7066
  this.recordEnvRefs(input);
6530
- this.nestedRefs.push({
7067
+ this.nestedRefs.push(opts?.workspace === "inherit" ? {
7068
+ id,
7069
+ name,
7070
+ workspace: "inherit"
7071
+ } : {
6531
7072
  id,
6532
7073
  name
6533
7074
  });
@@ -6539,8 +7080,8 @@ var init_workflow = __esm({
6539
7080
  workspace: opts?.workspace
6540
7081
  });
6541
7082
  return this.push({
6542
- kind: "entry",
6543
- entry: node
7083
+ kind: "declare",
7084
+ node
6544
7085
  });
6545
7086
  }
6546
7087
  commit() {
@@ -6550,7 +7091,7 @@ var init_workflow = __esm({
6550
7091
  const { graph, issues } = resolvePlacements(this.calls);
6551
7092
  const fatal = issues[0];
6552
7093
  if (fatal) {
6553
- 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;
7094
+ 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;
6554
7095
  throw new LuaWorkflowBuildError(fatal.code, fatal.message, hint);
6555
7096
  }
6556
7097
  if (graph.length === 0) throw new LuaWorkflowBuildError("empty-graph", `workflow "${this.config.name}" has no entries`);
@@ -6563,7 +7104,8 @@ var init_workflow = __esm({
6563
7104
  if (graphHasHitl(graph, this.steps) && this.config.budget?.maxDurationSeconds === void 0) {
6564
7105
  this.warnings.push({
6565
7106
  code: "hitl-duration-defaulted",
6566
- 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`
7107
+ // LUA-668: names the workflow two HITL workflows in one project printed two identical lines that read as a duplicate.
7108
+ 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`
6567
7109
  });
6568
7110
  }
6569
7111
  this.checkScheduleInput();
@@ -12276,7 +12818,7 @@ function findFileLevelImport(node, state3) {
12276
12818
  for (const importDecl of sourceFile.getImportDeclarations()) {
12277
12819
  const defaultImport = importDecl.getDefaultImport();
12278
12820
  const matchesDefault = defaultImport?.getText() === name;
12279
- const matchesNamed = importDecl.getNamedImports().some((n) => (n.getAliasNode()?.getText() ?? n.getName()) === name);
12821
+ const matchesNamed = importDecl.getNamedImports().some((n2) => (n2.getAliasNode()?.getText() ?? n2.getName()) === name);
12280
12822
  if (!matchesDefault && !matchesNamed) continue;
12281
12823
  const target = loadImportTarget(importDecl, state3, sourceFile.getFilePath());
12282
12824
  if (!target) return void 0;
@@ -13783,13 +14325,13 @@ function rewritePrimitiveSource(metadata, opts) {
13783
14325
  }
13784
14326
  });
13785
14327
  const sf = localProject.createSourceFile(`__rewrite_${metadata.kind}_${metadata.name}.ts`, sourceCode);
13786
- const isPrimitiveCall = /* @__PURE__ */ __name((n) => {
13787
- if (Node15.isNewExpression(n)) {
13788
- const callee = n.getExpression();
14328
+ const isPrimitiveCall = /* @__PURE__ */ __name((n2) => {
14329
+ if (Node15.isNewExpression(n2)) {
14330
+ const callee = n2.getExpression();
13789
14331
  return Node15.isIdentifier(callee) && opts.constructorNames.includes(callee.getText());
13790
14332
  }
13791
- if (Node15.isCallExpression(n)) {
13792
- const callee = n.getExpression();
14333
+ if (Node15.isCallExpression(n2)) {
14334
+ const callee = n2.getExpression();
13793
14335
  return Node15.isIdentifier(callee) && opts.defineFunctionName !== void 0 && callee.getText() === opts.defineFunctionName;
13794
14336
  }
13795
14337
  return false;
@@ -13807,9 +14349,9 @@ function rewritePrimitiveSource(metadata, opts) {
13807
14349
  compilerVersion: COMPILER_VERSION
13808
14350
  });
13809
14351
  callNode.replaceWithText(synthesized);
13810
- sf.forEachDescendant((n) => {
13811
- if (n === callNode) return;
13812
- if (isPrimitiveCall(n)) n.replaceWithText("undefined");
14352
+ sf.forEachDescendant((n2) => {
14353
+ if (n2 === callNode) return;
14354
+ if (isPrimitiveCall(n2)) n2.replaceWithText("undefined");
13813
14355
  });
13814
14356
  rewriteCrossFileCallsInSourceFile(sf, opts.crossFileSpecs, opts.sdkBaseClassNames);
13815
14357
  stripLuaCliImports(sf);
@@ -13879,14 +14421,14 @@ function stripSdkExtendsClauses(sf, sdkBaseClassNames, aliasMap, specs) {
13879
14421
  const canonical2 = matchSdkClass(expr.getText());
13880
14422
  if (canonical2) strip(classDecl, canonical2);
13881
14423
  }
13882
- sf.forEachDescendant((n) => {
13883
- if (!Node15.isClassExpression(n)) return;
13884
- const ext = n.getExtends();
14424
+ sf.forEachDescendant((n2) => {
14425
+ if (!Node15.isClassExpression(n2)) return;
14426
+ const ext = n2.getExtends();
13885
14427
  if (!ext) return;
13886
14428
  const expr = ext.getExpression();
13887
14429
  if (!Node15.isIdentifier(expr)) return;
13888
14430
  const canonical2 = matchSdkClass(expr.getText());
13889
- if (canonical2) strip(n, canonical2);
14431
+ if (canonical2) strip(n2, canonical2);
13890
14432
  });
13891
14433
  }
13892
14434
  function stripDroppedClassMembers(classNode, dropNames) {
@@ -13894,9 +14436,9 @@ function stripDroppedClassMembers(classNode, dropNames) {
13894
14436
  if (dropNames.has(prop.getName())) prop.remove();
13895
14437
  }
13896
14438
  const statements = [];
13897
- classNode.forEachDescendant((n) => {
13898
- if (!Node15.isExpressionStatement(n)) return;
13899
- if (isThisRootedCallTo(n.getExpression(), dropNames)) statements.push(n);
14439
+ classNode.forEachDescendant((n2) => {
14440
+ if (!Node15.isExpressionStatement(n2)) return;
14441
+ if (isThisRootedCallTo(n2.getExpression(), dropNames)) statements.push(n2);
13900
14442
  });
13901
14443
  for (const stmt of statements) stmt.remove();
13902
14444
  }
@@ -13929,13 +14471,13 @@ function stripSuperCallsInConstructors(classNode) {
13929
14471
  }
13930
14472
  function warnRemainingSuperReferences(classNode) {
13931
14473
  const sf = classNode.getSourceFile();
13932
- classNode.forEachDescendant((n) => {
13933
- if (n.getKind() !== ts3.SyntaxKind.SuperKeyword) return;
13934
- const parent = n.getParent();
13935
- if (parent && Node15.isCallExpression(parent) && parent.getExpression() === n && Node15.isExpressionStatement(parent.getParent())) {
14474
+ classNode.forEachDescendant((n2) => {
14475
+ if (n2.getKind() !== ts3.SyntaxKind.SuperKeyword) return;
14476
+ const parent = n2.getParent();
14477
+ if (parent && Node15.isCallExpression(parent) && parent.getExpression() === n2 && Node15.isExpressionStatement(parent.getParent())) {
13936
14478
  return;
13937
14479
  }
13938
- const pos = sf.getLineAndColumnAtPos(n.getStart());
14480
+ const pos = sf.getLineAndColumnAtPos(n2.getStart());
13939
14481
  console.warn(formatSarifWarning({
13940
14482
  ruleId: "lua/orphan-super-reference",
13941
14483
  filePath: sf.getFilePath(),
@@ -13979,24 +14521,24 @@ function buildSdkAliasMap(sf, sdkIdentifiers) {
13979
14521
  }
13980
14522
  function findFirstCrossFileSdkCall(sf, specs, aliasMap) {
13981
14523
  let found;
13982
- sf.forEachDescendant((n) => {
14524
+ sf.forEachDescendant((n2) => {
13983
14525
  if (found) return;
13984
- if (Node15.isNewExpression(n)) {
13985
- const callee = n.getExpression();
14526
+ if (Node15.isNewExpression(n2)) {
14527
+ const callee = n2.getExpression();
13986
14528
  if (!Node15.isIdentifier(callee)) return;
13987
14529
  const canonical2 = aliasMap.get(callee.getText()) ?? callee.getText();
13988
14530
  const spec = specs.find((s) => s.classNames.includes(canonical2));
13989
14531
  if (spec) found = {
13990
- node: n,
14532
+ node: n2,
13991
14533
  spec
13992
14534
  };
13993
- } else if (Node15.isCallExpression(n)) {
13994
- const callee = n.getExpression();
14535
+ } else if (Node15.isCallExpression(n2)) {
14536
+ const callee = n2.getExpression();
13995
14537
  if (!Node15.isIdentifier(callee)) return;
13996
14538
  const canonical2 = aliasMap.get(callee.getText()) ?? callee.getText();
13997
14539
  const spec = specs.find((s) => s.defineFunction !== void 0 && s.defineFunction === canonical2);
13998
14540
  if (spec) found = {
13999
- node: n,
14541
+ node: n2,
14000
14542
  spec
14001
14543
  };
14002
14544
  }
@@ -15020,14 +15562,14 @@ function buildSandboxRequire(opts) {
15020
15562
  function v4ToInt(ip) {
15021
15563
  const parts = ip.split(".");
15022
15564
  if (parts.length !== 4) return null;
15023
- let n = 0;
15565
+ let n2 = 0;
15024
15566
  for (const p of parts) {
15025
15567
  if (!/^\d{1,3}$/.test(p)) return null;
15026
15568
  const v = Number(p);
15027
15569
  if (v > 255) return null;
15028
- n = n * 256 + v;
15570
+ n2 = n2 * 256 + v;
15029
15571
  }
15030
- return n;
15572
+ return n2;
15031
15573
  }
15032
15574
  function range(cidr, klass) {
15033
15575
  const [ip, prefix] = cidr.split("/");
@@ -15037,10 +15579,10 @@ function range(cidr, klass) {
15037
15579
  klass
15038
15580
  };
15039
15581
  }
15040
- function inRange(n, r) {
15582
+ function inRange(n2, r) {
15041
15583
  if (r.prefix === 0) return true;
15042
15584
  const shift = 32 - r.prefix;
15043
- return Math.floor(n / 2 ** shift) === Math.floor(r.base / 2 ** shift);
15585
+ return Math.floor(n2 / 2 ** shift) === Math.floor(r.base / 2 ** shift);
15044
15586
  }
15045
15587
  function mappedV4(address) {
15046
15588
  const m = /^::ffff:(\d{1,3}(?:\.\d{1,3}){3})$/i.exec(address.trim());
@@ -15049,9 +15591,9 @@ function mappedV4(address) {
15049
15591
  function classifyPrivateAddress(address) {
15050
15592
  const v4 = net.isIPv4(address) ? address : mappedV4(address);
15051
15593
  if (!v4) return null;
15052
- const n = v4ToInt(v4);
15053
- if (n === null) return null;
15054
- for (const r of PRIVATE_RANGES) if (inRange(n, r)) return r.klass;
15594
+ const n2 = v4ToInt(v4);
15595
+ if (n2 === null) return null;
15596
+ for (const r of PRIVATE_RANGES) if (inRange(n2, r)) return r.klass;
15055
15597
  return null;
15056
15598
  }
15057
15599
  function egressDeniedMessage(host) {
@@ -15072,12 +15614,12 @@ function isEgressDeniedError(err) {
15072
15614
  }
15073
15615
  function resolveEgressProbeMs(env = process.env) {
15074
15616
  const raw = env.LUA_SANDBOX_EGRESS_PROBE_MS;
15075
- const n = raw !== void 0 && raw !== "" ? Number.parseInt(raw, 10) : NaN;
15076
- return Number.isInteger(n) && n > 0 ? n : EGRESS_PROBE_DEFAULT_MS;
15617
+ const n2 = raw !== void 0 && raw !== "" ? Number.parseInt(raw, 10) : NaN;
15618
+ return Number.isInteger(n2) && n2 > 0 ? n2 : EGRESS_PROBE_DEFAULT_MS;
15077
15619
  }
15078
15620
  function ipv4ToInt(ip) {
15079
15621
  const parts = ip.split(".").map(Number);
15080
- if (parts.length !== 4 || parts.some((n) => Number.isNaN(n) || n < 0 || n > 255)) {
15622
+ if (parts.length !== 4 || parts.some((n2) => Number.isNaN(n2) || n2 < 0 || n2 > 255)) {
15081
15623
  throw new Error(`Invalid IPv4: ${ip}`);
15082
15624
  }
15083
15625
  return (parts[0] << 24 >>> 0) + (parts[1] << 16) + (parts[2] << 8) + parts[3];
@@ -16742,6 +17284,7 @@ function serializeViaVm(bundleCode, opts = {}) {
16742
17284
  ...templateKeys
16743
17285
  ].sort(),
16744
17286
  workflowRefs: (wf?.getNestedWorkflowRefs() ?? []).map((r) => r.name),
17287
+ inheritRefs: inheritRefsOf(wf),
16745
17288
  buildWarnings: wf?.getBuildWarnings() ?? []
16746
17289
  }
16747
17290
  };
@@ -16825,6 +17368,31 @@ function stepRefId(arg) {
16825
17368
  }
16826
17369
  return void 0;
16827
17370
  }
17371
+ function workflowRefName(arg) {
17372
+ if (!arg) return void 0;
17373
+ if (Node17.isStringLiteral(arg) || Node17.isNoSubstitutionTemplateLiteral(arg)) return arg.getLiteralText();
17374
+ if (!Node17.isIdentifier(arg)) return void 0;
17375
+ const decl = resolveIdentifier(arg);
17376
+ const init3 = decl && Node17.isVariableDeclaration(decl) ? decl.getInitializer() : decl;
17377
+ if (!init3 || !Node17.isExpression(init3)) return void 0;
17378
+ const root = chainCalls(init3)[0];
17379
+ const cfg = root && [
17380
+ "createWorkflow",
17381
+ "defineWorkflow"
17382
+ ].includes(calleeName(root)) ? root.getArguments()[0] : void 0;
17383
+ return cfg && Node17.isObjectLiteralExpression(cfg) ? extractStringProperty(cfg, "name") : void 0;
17384
+ }
17385
+ function sketchInheritRefs(calls) {
17386
+ const out = [];
17387
+ for (const call of calls) {
17388
+ if (calleeName(call) !== "workflow") continue;
17389
+ const [, ref, , opts] = call.getArguments();
17390
+ if (!opts || !Node17.isObjectLiteralExpression(opts) || extractStringProperty(opts, "workspace") !== "inherit") continue;
17391
+ const name = workflowRefName(ref);
17392
+ if (name && !out.includes(name)) out.push(name);
17393
+ }
17394
+ return out;
17395
+ }
16828
17396
  function callDeclaredIds(call, mapCounter) {
16829
17397
  const name = calleeName(call);
16830
17398
  const args2 = call.getArguments();
@@ -16976,6 +17544,7 @@ function serializeViaAst(sourceFile, exportName) {
16976
17544
  envKeys: [],
16977
17545
  envTemplateKeys: inst?.getEnvTemplateKeys() ?? [],
16978
17546
  workflowRefs: (inst?.getNestedWorkflowRefs() ?? []).map((r) => r.name),
17547
+ inheritRefs: inheritRefsOf(inst),
16979
17548
  buildWarnings: inst?.getBuildWarnings() ?? []
16980
17549
  };
16981
17550
  } catch {
@@ -17021,6 +17590,7 @@ function sketchTopology(sourceFile, exportName, name, description) {
17021
17590
  envKeys: [],
17022
17591
  envTemplateKeys: [],
17023
17592
  workflowRefs: [],
17593
+ inheritRefs: sketchInheritRefs(calls),
17024
17594
  buildWarnings: [],
17025
17595
  partialNodes: [
17026
17596
  ...seen
@@ -17028,47 +17598,49 @@ function sketchTopology(sourceFile, exportName, name, description) {
17028
17598
  };
17029
17599
  }
17030
17600
  function metaOf(node, sources) {
17031
- const n = node;
17032
- const id = n.type === "step" ? n.step.id : n.id;
17601
+ const n2 = node;
17602
+ const id = n2.type === "step" ? n2.step.id : n2.id;
17033
17603
  const meta = {
17034
17604
  id,
17035
- kind: KIND_OF[n.type] ?? n.type
17605
+ kind: KIND_OF[n2.type] ?? n2.type
17036
17606
  };
17037
17607
  const pick = /* @__PURE__ */ __name((k, v) => {
17038
17608
  if (v !== void 0) meta[k] = v;
17039
17609
  }, "pick");
17040
- pick("timeoutSeconds", n.timeoutSeconds);
17041
- pick("retry", n.retry);
17042
- pick("sideEffects", n.sideEffects);
17043
- pick("onError", n.onError);
17044
- pick("requiredConnections", n.requiredConnections);
17045
- pick("resumeTimeoutHours", n.resumeTimeoutHours);
17046
- pick("onSuspendTimeout", n.onSuspendTimeout);
17047
- pick("businessHours", n.businessHours);
17048
- pick("tier", n.tier);
17049
- pick("harness", n.harness);
17050
- pick("workspace", n.workspace);
17051
- pick("jobResources", n.jobResources);
17052
- pick("maxTurns", n.maxTurns);
17053
- if (n.type === "step") {
17054
- const s = n.step;
17610
+ pick("timeoutSeconds", n2.timeoutSeconds);
17611
+ pick("retry", n2.retry);
17612
+ pick("sideEffects", n2.sideEffects);
17613
+ pick("onError", n2.onError);
17614
+ pick("requiredConnections", n2.requiredConnections);
17615
+ pick("resumeTimeoutHours", n2.resumeTimeoutHours);
17616
+ pick("onSuspendTimeout", n2.onSuspendTimeout);
17617
+ pick("businessHours", n2.businessHours);
17618
+ pick("tier", n2.tier);
17619
+ pick("harness", n2.harness);
17620
+ pick("workspace", n2.workspace);
17621
+ pick("jobResources", n2.jobResources);
17622
+ pick("maxTurns", n2.maxTurns);
17623
+ pick("maxMessages", n2.maxMessages);
17624
+ pick("maxInputTokens", n2.maxInputTokens);
17625
+ if (n2.type === "step") {
17626
+ const s = n2.step;
17055
17627
  pick("inputSchema", s.inputSchema);
17056
17628
  pick("outputSchema", s.outputSchema);
17057
17629
  pick("suspendSchema", s.suspendSchema);
17058
17630
  pick("resumeSchema", s.resumeSchema);
17059
17631
  }
17060
- if (n.type === "agent") {
17061
- pick("outputSchema", n.outputSchema);
17062
- const jobTools = n.toolScope?.jobTools;
17632
+ if (n2.type === "agent") {
17633
+ pick("outputSchema", n2.outputSchema);
17634
+ const jobTools = n2.toolScope?.jobTools;
17063
17635
  pick("jobTools", jobTools);
17064
17636
  }
17065
- if (n.type === "approval") {
17066
- pick("editedPayloadSchema", n.editedPayloadSchema);
17067
- pick("itemsPath", n.itemsPath);
17068
- pick("itemApprover", n.itemApprover);
17069
- pick("itemTimeout", n.itemTimeout);
17637
+ if (n2.type === "approval") {
17638
+ pick("editedPayloadSchema", n2.editedPayloadSchema);
17639
+ pick("itemsPath", n2.itemsPath);
17640
+ pick("itemApprover", n2.itemApprover);
17641
+ pick("itemTimeout", n2.itemTimeout);
17070
17642
  }
17071
- if (n.type === "waitForSignal") pick("outputSchema", n.schema);
17643
+ if (n2.type === "waitForSignal") pick("outputSchema", n2.schema);
17072
17644
  const src = sources.get(id);
17073
17645
  if (src) meta.source = src;
17074
17646
  return meta;
@@ -17108,21 +17680,25 @@ function deriveStepMeta(graph, sources) {
17108
17680
  }
17109
17681
  return out;
17110
17682
  }
17111
- function computeDeferred(graph) {
17683
+ function computeDeferred(graph, opts = {}) {
17112
17684
  const deferred = /* @__PURE__ */ new Set();
17113
- const visit = /* @__PURE__ */ __name((n) => {
17114
- if (n.type === "agent" && n.agentId === "$self") {
17685
+ let mounts = false;
17686
+ const declaredKeys = new Set((graph.connections ?? []).map((c) => c.key));
17687
+ const serverDecides = /* @__PURE__ */ __name((ref) => typeof ref === "string" && !declaredKeys.has(ref) && !isConnectionKeyShaped(ref), "serverDecides");
17688
+ const visit = /* @__PURE__ */ __name((n2) => {
17689
+ if (n2.type === "agent" && n2.agentId === "$self") {
17115
17690
  deferred.add("ephemeral-tools-not-subset");
17116
17691
  deferred.add("ephemeral-specialists-disabled");
17117
- if (n.role?.ref) deferred.add("role-ref-unknown");
17692
+ if (n2.role?.ref) deferred.add("role-ref-unknown");
17118
17693
  }
17119
- if (n.tier === "job") {
17694
+ if (n2.workspace !== void 0 && n2.workspace !== "inherit") mounts = true;
17695
+ if (n2.tier === "job") {
17120
17696
  deferred.add("job-tier-disabled");
17121
17697
  deferred.add("job-tier-provider-unsupported");
17122
17698
  }
17123
- if (Array.isArray(n.requiredConnections) && n.requiredConnections.length > 0) deferred.add("required-connection-unknown");
17124
- if (n.type === "approval") {
17125
- const a = n.approver;
17699
+ if (Array.isArray(n2.requiredConnections) && n2.requiredConnections.some(serverDecides)) deferred.add("required-connection-unknown");
17700
+ if (n2.type === "approval") {
17701
+ const a = n2.approver;
17126
17702
  if (a && typeof a === "object") {
17127
17703
  if ("role" in a) deferred.add("approver-role-unknown");
17128
17704
  if ("users" in a) deferred.add("approver-not-member");
@@ -17130,7 +17706,7 @@ function computeDeferred(graph) {
17130
17706
  if ("governance" in a) deferred.add("approver-governance-unavailable");
17131
17707
  }
17132
17708
  }
17133
- if (n.type === "foreach") deferred.add("foreach-items-exceed-cap");
17709
+ if (n2.type === "foreach") deferred.add("foreach-items-exceed-cap");
17134
17710
  }, "visit");
17135
17711
  for (const entry of graph.definition.graph) {
17136
17712
  visit(entry);
@@ -17142,16 +17718,20 @@ function computeDeferred(graph) {
17142
17718
  ]) if (child) visit(child);
17143
17719
  }
17144
17720
  const ws = graph.workspace;
17145
- if (ws?.credentialsRef) deferred.add("credentials-ref-unknown");
17721
+ if (serverDecides(ws?.credentialsRef)) deferred.add("credentials-ref-unknown");
17146
17722
  if (ws?.backend && ws.backend !== "ebs") deferred.add("workspace-backend-unavailable");
17723
+ if (opts.mayInherit && !ws && mounts) deferred.add("workspace-not-declared");
17147
17724
  if (graph.budget && graph.budget.maxCredits !== void 0) deferred.add("budget-exceeds-cap");
17148
17725
  return [
17149
17726
  ...deferred
17150
17727
  ].sort();
17151
17728
  }
17152
- function validateGraph(graph) {
17729
+ function validateGraph(graph, opts = {}) {
17153
17730
  return validateLuaExtensions(graph, WORKFLOW_CAPS_DEFAULT, {
17154
- static: true
17731
+ static: true,
17732
+ ...opts.mayInherit ? {
17733
+ mayInherit: true
17734
+ } : {}
17155
17735
  });
17156
17736
  }
17157
17737
  function stampGraphHash(code, graph) {
@@ -17171,7 +17751,32 @@ function toIssues(issues, sources) {
17171
17751
  source: i.stepId ? sources.get(i.stepId) : void 0
17172
17752
  }));
17173
17753
  }
17174
- var NOT_STATIC, isNotStatic, isBuildError, COMMITTED_WORKFLOW_MEMBERS, isCommittedWorkflow, PLATFORM_GLOBALS, calleeName, KIND_OF, GRAPH_HASH_PLACEHOLDER;
17754
+ function applyInheritCandidate(d) {
17755
+ if (d.topology === "sketch") return d;
17756
+ const sources = new Map(d.stepMeta.flatMap((m) => m.source ? [
17757
+ [
17758
+ m.id,
17759
+ m.source
17760
+ ]
17761
+ ] : []));
17762
+ const before = new Set(toIssues(d.validation, sources).map(issueKey));
17763
+ const validation = validateGraph(d.graph, {
17764
+ mayInherit: true
17765
+ });
17766
+ return {
17767
+ ...d,
17768
+ mayInherit: true,
17769
+ validation,
17770
+ issues: [
17771
+ ...d.issues.filter((i) => !before.has(issueKey(i))),
17772
+ ...toIssues(validation, sources)
17773
+ ],
17774
+ deferred: computeDeferred(d.graph, {
17775
+ mayInherit: true
17776
+ })
17777
+ };
17778
+ }
17779
+ var NOT_STATIC, isNotStatic, isBuildError, inheritRefsOf, COMMITTED_WORKFLOW_MEMBERS, isCommittedWorkflow, PLATFORM_GLOBALS, calleeName, KIND_OF, GRAPH_HASH_PLACEHOLDER, issueKey;
17175
17780
  var init_graph_serializer = __esm({
17176
17781
  "src/compiler/utils/graph-serializer.ts"() {
17177
17782
  "use strict";
@@ -17183,6 +17788,7 @@ var init_graph_serializer = __esm({
17183
17788
  NOT_STATIC = "WORKFLOW_GRAPH_NOT_STATIC";
17184
17789
  isNotStatic = /* @__PURE__ */ __name((e) => typeof e === "object" && e !== null && e.code === NOT_STATIC, "isNotStatic");
17185
17790
  isBuildError = /* @__PURE__ */ __name((e) => e instanceof LuaWorkflowBuildError || typeof e === "object" && e !== null && e.name === "LuaWorkflowBuildError" && typeof e.code === "string", "isBuildError");
17791
+ inheritRefsOf = /* @__PURE__ */ __name((wf) => (wf?.getNestedWorkflowRefs() ?? []).filter((r) => r.workspace === "inherit").map((r) => r.name), "inheritRefsOf");
17186
17792
  COMMITTED_WORKFLOW_MEMBERS = [
17187
17793
  "getName",
17188
17794
  "getBuildWarnings",
@@ -17222,6 +17828,8 @@ var init_graph_serializer = __esm({
17222
17828
  return Node17.isPropertyAccessExpression(callee) ? callee.getName() : callee.getText();
17223
17829
  }, "calleeName");
17224
17830
  __name(stepRefId, "stepRefId");
17831
+ __name(workflowRefName, "workflowRefName");
17832
+ __name(sketchInheritRefs, "sketchInheritRefs");
17225
17833
  __name(callDeclaredIds, "callDeclaredIds");
17226
17834
  __name(stampSources, "stampSources");
17227
17835
  __name(collectBuilderCalls, "collectBuilderCalls");
@@ -17245,6 +17853,13 @@ var init_graph_serializer = __esm({
17245
17853
  GRAPH_HASH_PLACEHOLDER = "__LUA_GRAPH_HASH__";
17246
17854
  __name(stampGraphHash, "stampGraphHash");
17247
17855
  __name(toIssues, "toIssues");
17856
+ issueKey = /* @__PURE__ */ __name((i) => [
17857
+ i.code,
17858
+ i.path ?? "",
17859
+ i.stepId ?? "",
17860
+ i.message
17861
+ ].join("\0"), "issueKey");
17862
+ __name(applyInheritCandidate, "applyInheritCandidate");
17248
17863
  }
17249
17864
  });
17250
17865
 
@@ -17352,7 +17967,7 @@ function collectSingleSteps(graph) {
17352
17967
  }
17353
17968
  function collectGraphIds(graph) {
17354
17969
  const ids = [];
17355
- const idOf = /* @__PURE__ */ __name((n) => n.type === "step" ? n.step.id : n.id, "idOf");
17970
+ const idOf = /* @__PURE__ */ __name((n2) => n2.type === "step" ? n2.step.id : n2.id, "idOf");
17356
17971
  for (const entry of graph.definition.graph) {
17357
17972
  const e = entry;
17358
17973
  const own = idOf(e);
@@ -17698,6 +18313,7 @@ ${firstBundleFrame(vmResult.error.stack)}` : ""}`,
17698
18313
  envKeys: [],
17699
18314
  envTemplateKeys: [],
17700
18315
  workflowRefs: [],
18316
+ inheritRefs: [],
17701
18317
  buildWarnings: []
17702
18318
  };
17703
18319
  issues.push({
@@ -17719,6 +18335,7 @@ ${firstBundleFrame(vmResult.error.stack)}` : ""}`,
17719
18335
  envKeys: [],
17720
18336
  envTemplateKeys: [],
17721
18337
  workflowRefs: [],
18338
+ inheritRefs: [],
17722
18339
  buildWarnings: []
17723
18340
  };
17724
18341
  }
@@ -17761,7 +18378,9 @@ ${firstBundleFrame(vmResult.error.stack)}` : ""}`,
17761
18378
  }
17762
18379
  }
17763
18380
  }
17764
- const validation = tier.topology === "sketch" ? [] : validateGraph(graph);
18381
+ const validation = tier.topology === "sketch" ? [] : validateGraph(graph, {
18382
+ mayInherit: opts.inheritCandidate
18383
+ });
17765
18384
  issues.push(...toIssues(validation, sources));
17766
18385
  const stamped = stampGraphHash(artifact.code, graph);
17767
18386
  const stampedArtifact = {
@@ -17778,12 +18397,38 @@ ${firstBundleFrame(vmResult.error.stack)}` : ""}`,
17778
18397
  envTemplateKeys: tier.envTemplateKeys,
17779
18398
  partialNodes: tier.partialNodes,
17780
18399
  workflowRefs: tier.workflowRefs,
18400
+ inheritRefs: tier.inheritRefs,
18401
+ ...opts.inheritCandidate ? {
18402
+ mayInherit: true
18403
+ } : {},
17781
18404
  issues,
17782
- deferred: computeDeferred(graph),
18405
+ deferred: computeDeferred(graph, {
18406
+ mayInherit: opts.inheritCandidate
18407
+ }),
17783
18408
  artifact: stampedArtifact,
17784
18409
  validation
17785
18410
  };
17786
18411
  }
18412
+ /**
18413
+ * LUA-635 (03 §3.1 table): once every primitive is derived, a workflow that another workflow of the project starts
18414
+ * with `.workflow(…, { workspace: 'inherit' })` is an inherit candidate — its `workspace-not-declared` defers to
18415
+ * createRun (it mounts the parent's volume). The child's own `.commit()` could not know this: it ran at module
18416
+ * evaluation, before any parent existed.
18417
+ */
18418
+ linkGraphs(compiled) {
18419
+ const workflows = compiled.filter((p) => p.kind === PrimitiveKind.WORKFLOW && p.graph);
18420
+ const inheritTargets2 = new Set(workflows.flatMap((p) => p.graph?.inheritRefs ?? []));
18421
+ for (const p of workflows) if (p.graph && inheritTargets2.has(p.name)) p.graph = applyInheritCandidate(p.graph);
18422
+ const sketched = workflows.filter((p) => p.graph?.topology === "sketch").map((p) => p.name);
18423
+ if (sketched.length === 0) return;
18424
+ for (const p of workflows) {
18425
+ if (!p.graph || inheritTargets2.has(p.name)) continue;
18426
+ for (const issue of p.graph.issues) {
18427
+ if (issue.code !== "workspace-not-declared") continue;
18428
+ issue.message += ` \u2014 ${sketched.join(", ")} ${sketched.length === 1 ? "is" : "are"} at sketch topology: a \`.workflow(\u2026, { workspace: 'inherit' })\` there that names this workflow through anything but a string or a createWorkflow({ name }) binding is invisible to the compiler; make that file evaluate statically first, or declare the workspace here`;
18429
+ }
18430
+ }
18431
+ }
17787
18432
  // ===========================================================================
17788
18433
  // MANIFEST — 03 §3.3.3
17789
18434
  // ===========================================================================
@@ -17816,7 +18461,7 @@ ${firstBundleFrame(vmResult.error.stack)}` : ""}`,
17816
18461
  if (!d) throw new Error(`workflow "${compiled.name}" was compiled without a graph derivation`);
17817
18462
  const cfg = d.graph ?? {};
17818
18463
  const config = compiled.metadata.config ?? {};
17819
- const toolIds = collectSingleSteps(d.graph).filter((n) => n.type === "tool").map((n) => n.toolId);
18464
+ const toolIds = collectSingleSteps(d.graph).filter((n2) => n2.type === "tool").map((n2) => n2.toolId);
17820
18465
  const toolRefs = [
17821
18466
  ...new Set(toolIds)
17822
18467
  ].map((toolId) => allPrimitives?.find((p) => p.kind === "tool" && p.name === toolId)?.name ?? toolId);
@@ -17839,6 +18484,7 @@ ${firstBundleFrame(vmResult.error.stack)}` : ""}`,
17839
18484
  partialNodes: d.partialNodes,
17840
18485
  toolRefs,
17841
18486
  workflowRefs: d.workflowRefs,
18487
+ mayInherit: d.mayInherit,
17842
18488
  budget: cfg.budget,
17843
18489
  concurrencyPolicy: cfg.concurrencyPolicy,
17844
18490
  schedule: config.schedule,
@@ -18838,6 +19484,10 @@ var init_compiler = __esm({
18838
19484
  });
18839
19485
  const CONCURRENCY = 4;
18840
19486
  const compileResults = await this.compileInParallel(allMetadata, CONCURRENCY);
19487
+ const compiled = compileResults.flatMap((r) => "compiled" in r && r.compiled ? [
19488
+ r.compiled
19489
+ ] : []);
19490
+ for (const plugin of pluginRegistry.getAll()) plugin.linkGraphs?.(compiled);
18841
19491
  let graphErrors = 0;
18842
19492
  for (const result of compileResults) {
18843
19493
  if ("error" in result) {
@@ -22511,7 +23161,7 @@ function createWorkflowsRuntime(getApi) {
22511
23161
  }
22512
23162
  };
22513
23163
  }
22514
- var WORKFLOW_START_MAX_WAIT_SECONDS, WORKFLOW_START_CLIENT_DEADLINE_SLACK_MS, CONTROL_UNAVAILABLE_HINT, WorkflowApiError, WORKFLOWS_RUNTIME_MEMBERS, unwrap, assertBoundAgent, unavailable, WorkflowApi;
23164
+ var WORKFLOW_START_MAX_WAIT_SECONDS, WORKFLOW_START_CLIENT_DEADLINE_SLACK_MS, CONTROL_UNAVAILABLE_HINT, WorkflowApiError, WORKFLOWS_RUNTIME_MEMBERS, unwrap, assertBoundAgent, pathId, unavailable, WorkflowApi;
22515
23165
  var init_workflow_api_service = __esm({
22516
23166
  "src/api/workflow.api.service.ts"() {
22517
23167
  "use strict";
@@ -22562,6 +23212,7 @@ var init_workflow_api_service = __esm({
22562
23212
  throw new WorkflowApiError("FORBIDDEN", `Workflows.${member}: goals are scoped to the bound agent ${api.agentId}`, 403);
22563
23213
  }
22564
23214
  }, "assertBoundAgent");
23215
+ pathId = /* @__PURE__ */ __name((id) => encodeURIComponent(id), "pathId");
22565
23216
  unavailable = /* @__PURE__ */ __name((member, route) => async () => {
22566
23217
  throw new WorkflowApiError("WORKFLOWS_API_UNAVAILABLE", `Workflows.${member} is not available in this runtime yet (${route} lands with a later wave)`, 501);
22567
23218
  }, "unavailable");
@@ -22687,7 +23338,7 @@ var init_workflow_api_service = __esm({
22687
23338
  /** R5 — one step of a run (`?attempt=n` selects from the attempt history). */
22688
23339
  async getRunStep(runId, stepId, options = {}) {
22689
23340
  const qs = options.attempt !== void 0 ? `?attempt=${options.attempt}` : "";
22690
- return this.httpGet(`${this.runs}/${runId}/steps/${stepId}${qs}`, await this.auth());
23341
+ return this.httpGet(`${this.runs}/${runId}/steps/${pathId(stepId)}${qs}`, await this.auth());
22691
23342
  }
22692
23343
  /** R6 — script-form journal page (404 `NOT_SCRIPT_RUN` for graph runs — IF-16). */
22693
23344
  async getRunJournal(runId, options = {}) {
@@ -22707,7 +23358,7 @@ var init_workflow_api_service = __esm({
22707
23358
  }
22708
23359
  /** R12 — resume a suspended step; the loser of a race gets `{ resumed:false, reason:'already_resumed' }`, never a 4xx. */
22709
23360
  async resumeRun(runId, stepId, data) {
22710
- return this.httpPost(`${this.runs}/${runId}/steps/${stepId}/resume`, data, await this.auth());
23361
+ return this.httpPost(`${this.runs}/${runId}/steps/${pathId(stepId)}/resume`, data, await this.auth());
22711
23362
  }
22712
23363
  /**
22713
23364
  * R36 — re-arm a parked step: a failed row past its retries, a gate-2 park, or a billing hold (the only exit
@@ -22715,15 +23366,15 @@ var init_workflow_api_service = __esm({
22715
23366
  * STEP_NOT_PARKED.
22716
23367
  */
22717
23368
  async retryStep(runId, stepId, data = {}) {
22718
- return this.httpPost(`${this.runs}/${runId}/steps/${stepId}/retry`, data, await this.auth());
23369
+ return this.httpPost(`${this.runs}/${runId}/steps/${pathId(stepId)}/retry`, data, await this.auth());
22719
23370
  }
22720
23371
  /** R13 — resolve an approval (human; `expectedFingerprint` guards against an edited payload — 409 `PAYLOAD_MISMATCH`). */
22721
23372
  async resolveApproval(runId, approvalId, data) {
22722
- return this.httpPost(`${this.runs}/${runId}/approvals/${approvalId}/resolve`, data, await this.auth());
23373
+ return this.httpPost(`${this.runs}/${runId}/approvals/${pathId(approvalId)}/resolve`, data, await this.auth());
22723
23374
  }
22724
23375
  /** R14 — deliver a named signal (`dedupeKey` ⇒ 200 `duplicate:true` on replay). */
22725
23376
  async signalRun(runId, name, data = {}) {
22726
- return this.httpPost(`${this.runs}/${runId}/signals/${encodeURIComponent(name)}`, data, await this.auth());
23377
+ return this.httpPost(`${this.runs}/${runId}/signals/${pathId(name)}`, data, await this.auth());
22727
23378
  }
22728
23379
  /** R31 — `DELETE …/runs/:runId` (`eraseRun`; human principal) → 202 `{ accepted, purgeId }`; 409 `RUN_NOT_TERMINAL { nextAction:'cancel' }`. */
22729
23380
  /** R50 — request an evidence-bundle export (202 `{exportId, status:'pending'}`; 409 `RUN_NOT_TERMINAL` / `EXPORT_IN_PROGRESS`). */
@@ -22780,6 +23431,23 @@ var init_workflow_api_service = __esm({
22780
23431
  async closeGoal(goalId, data = {}) {
22781
23432
  return this.httpPost(`${this.goals}/${encodeURIComponent(goalId)}/close`, data, await this.auth());
22782
23433
  }
23434
+ // ─── Schedules (R4-MF-2 list/get + R28 delete — `/workflows/:agentId/schedules`; LUA-627 stanza) ───
23435
+ /** Schedule tree (09 §9.5 — the write-only R27/R56/R28 family plus the R4-MF-2 read rows). */
23436
+ get schedules() {
23437
+ return `/workflows/${this.agentId}/schedules`;
23438
+ }
23439
+ /** R4-MF-2 — every `Job{kind:'workflow'}` of the agent with the PRO-726 strike fields (`goalId` marks a goal's cadence). */
23440
+ async listSchedules() {
23441
+ return this.httpGet(this.schedules, await this.auth());
23442
+ }
23443
+ /** R4-MF-2 — one schedule row; unknown, cross-agent and non-workflow-kind jobs all 404 `SCHEDULE_NOT_FOUND`. */
23444
+ async getSchedule(jobId) {
23445
+ return this.httpGet(`${this.schedules}/${encodeURIComponent(jobId)}`, await this.auth());
23446
+ }
23447
+ /** R28 — delete a schedule Job (404 `SCHEDULE_NOT_FOUND`). The CLI refuses a goal-owned job BEFORE this call (`goal_schedule`). */
23448
+ async deleteSchedule(jobId) {
23449
+ return this.httpDelete(`${this.schedules}/${encodeURIComponent(jobId)}`, await this.auth());
23450
+ }
22783
23451
  // ─── Events (R7 — SSE `watch`, WF-204) ───
22784
23452
  /**
22785
23453
  * R7 SSE — `GET …/runs/:runId/events` as a frame stream (`id:<seq>` / `event:<type>` /
@@ -22823,7 +23491,7 @@ var init_workflow_api_service = __esm({
22823
23491
  if (options.attempt !== void 0) query.append("attempt", String(options.attempt));
22824
23492
  if (options.tail !== void 0) query.append("tail", String(options.tail));
22825
23493
  const qs = query.toString();
22826
- return this.httpGet(`${this.runs}/${runId}/steps/${stepId}/job${qs ? `?${qs}` : ""}`, await this.auth());
23494
+ return this.httpGet(`${this.runs}/${runId}/steps/${pathId(stepId)}/job${qs ? `?${qs}` : ""}`, await this.auth());
22827
23495
  }
22828
23496
  };
22829
23497
  }
@@ -29501,6 +30169,7 @@ init_types();
29501
30169
  init_constants();
29502
30170
  init_workflow_api_service();
29503
30171
  init_artifact_loader();
30172
+ init_bundle_upload();
29504
30173
 
29505
30174
  // src/utils/sandbox.ts
29506
30175
  init_dist4();
@@ -29740,7 +30409,7 @@ function matchesEditablePath2(leafPath, editablePaths) {
29740
30409
  });
29741
30410
  }
29742
30411
  __name(matchesEditablePath2, "matchesEditablePath");
29743
- var armId3 = /* @__PURE__ */ __name((arm) => arm.type === "step" ? arm.step.id : arm.id, "armId");
30412
+ var armId2 = /* @__PURE__ */ __name((arm) => arm.type === "step" ? arm.step.id : arm.id, "armId");
29744
30413
  var outputSchemaOf = /* @__PURE__ */ __name((entry) => {
29745
30414
  if (!entry) return void 0;
29746
30415
  if (entry.type === "step") return entry.step.outputSchema;
@@ -30254,7 +30923,7 @@ async function runWorkflowLocallyInner(rawOpts) {
30254
30923
  case "workflow": {
30255
30924
  if (single.workflowId === WORKFLOW_ARM_SUBRUN_ID && single.graph) {
30256
30925
  const [armMap, inner] = single.graph;
30257
- const innerId = armId3(inner);
30926
+ const innerId = armId2(inner);
30258
30927
  const ctx = {
30259
30928
  ...mappingCtx(),
30260
30929
  stepResults: {
@@ -30430,6 +31099,7 @@ async function runWorkflowLocallyInner(rawOpts) {
30430
31099
  const onError = single.onError ?? "fail";
30431
31100
  if (onError === "continue") {
30432
31101
  terminalize(row2, "failed");
31102
+ stepResults[row2.stepId] = continuedFailureValue(row2.error);
30433
31103
  return;
30434
31104
  }
30435
31105
  if (onError === "park") {
@@ -30472,15 +31142,15 @@ async function runWorkflowLocallyInner(rawOpts) {
30472
31142
  entry.steps.forEach((arm, i) => {
30473
31143
  const pred = entry.predicates[i];
30474
31144
  const hit = pred ? evaluatePredicate(pred, ctx) : false;
30475
- if (hit && (!entry.exclusive || taken.length === 0)) taken.push(armId3(arm));
31145
+ if (hit && (!entry.exclusive || taken.length === 0)) taken.push(armId2(arm));
30476
31146
  });
30477
- if (taken.length === 0 && entry.otherwise) taken.push(armId3(entry.otherwise));
31147
+ if (taken.length === 0 && entry.otherwise) taken.push(armId2(entry.otherwise));
30478
31148
  const arms = [
30479
31149
  ...entry.steps,
30480
31150
  ...entry.otherwise ? [
30481
31151
  entry.otherwise
30482
31152
  ] : []
30483
- ].map(armId3);
31153
+ ].map(armId2);
30484
31154
  terminalize(row2, "completed", {
30485
31155
  output: inputFor(row2),
30486
31156
  taken
@@ -30545,7 +31215,7 @@ async function runWorkflowLocallyInner(rawOpts) {
30545
31215
  };
30546
31216
  return;
30547
31217
  }
30548
- const body = armId3(entry.step);
31218
+ const body = armId2(entry.step);
30549
31219
  const joinId = joinIdOf(row2.stepId);
30550
31220
  const childIds = items.map((_, i) => `${body}[${i}]`);
30551
31221
  const childNode = {
@@ -30590,12 +31260,12 @@ async function runWorkflowLocallyInner(rawOpts) {
30590
31260
  });
30591
31261
  }, "runForeach");
30592
31262
  const runLoop = /* @__PURE__ */ __name(async (row2, entry) => {
30593
- const body = armId3(entry.step);
31263
+ const body = armId2(entry.step);
30594
31264
  const max = entry.maxIterations ?? 100;
30595
31265
  let last = inputFor(row2);
30596
31266
  row2.status = "running";
30597
- for (let n = 1; n <= max; n++) {
30598
- const iterId = `${body}#${n}`;
31267
+ for (let n2 = 1; n2 <= max; n2++) {
31268
+ const iterId = `${body}#${n2}`;
30599
31269
  const iterRow = addRow(iterId, {
30600
31270
  kind: kindOfSingle(entry.step),
30601
31271
  dependsOn: [],
@@ -30627,7 +31297,7 @@ async function runWorkflowLocallyInner(rawOpts) {
30627
31297
  clock += entry.intervalSeconds * 1e3;
30628
31298
  say(`[throttle:${row2.stepId}] kind=loop_interval resumeAt=${new Date(clock).toISOString()}`);
30629
31299
  }
30630
- if (n === max) {
31300
+ if (n2 === max) {
30631
31301
  row2.error = {
30632
31302
  code: "loop_max_iterations",
30633
31303
  message: `loop ${row2.stepId} hit maxIterations ${max}`
@@ -30740,14 +31410,16 @@ async function runWorkflowLocallyInner(rawOpts) {
30740
31410
  case "loop":
30741
31411
  await runLoop(row2, entry);
30742
31412
  return;
30743
- case "sleep":
30744
- clock += entry.duration * 1e3;
30745
- say(`[${stamp()}] ${row2.stepId} \xB7 sleep ${entry.duration}s (${opts.realTime ? "real time" : "virtual clock"})`);
30746
- if (opts.realTime) await new Promise((r) => setTimeout(r, entry.duration * 1e3));
31413
+ case "sleep": {
31414
+ const ms = Math.max(0, Number(entry.duration) || 0);
31415
+ clock += ms;
31416
+ say(`[${stamp()}] ${row2.stepId} \xB7 sleep ${formatSleep(ms)} (${opts.realTime ? "real time" : "virtual clock"})`);
31417
+ if (opts.realTime) await new Promise((r) => setTimeout(r, ms));
30747
31418
  terminalize(row2, "completed", {
30748
31419
  output: inputFor(row2)
30749
31420
  });
30750
31421
  return;
31422
+ }
30751
31423
  case "sleepUntil": {
30752
31424
  let until;
30753
31425
  if (entry.date) until = Date.parse(entry.date);
@@ -30984,6 +31656,13 @@ function writeLedger(file, ledger) {
30984
31656
  `);
30985
31657
  }
30986
31658
  __name(writeLedger, "writeLedger");
31659
+ function formatSleep(ms) {
31660
+ if (ms < 1e3) return `${ms} ms`;
31661
+ const s = ms / 1e3;
31662
+ const human = s >= 3600 ? `${+(s / 3600).toFixed(2)} h` : s >= 60 ? `${+(s / 60).toFixed(2)} min` : `${+s.toFixed(3)} s`;
31663
+ return `${ms} ms (${human})`;
31664
+ }
31665
+ __name(formatSleep, "formatSleep");
30987
31666
  function parseKeyedJsonFlag(raw, flag) {
30988
31667
  const eq3 = raw.indexOf("=");
30989
31668
  if (eq3 === -1) throw new WorkflowLocalUsageError("usage", `${flag} expects <id>=@file|<json> (got "${raw}")`);
@@ -31652,7 +32331,11 @@ var WorkflowHandler = class extends BaseVersionedHandler {
31652
32331
  name: wf.name,
31653
32332
  description: wf.description
31654
32333
  });
31655
- if (!response.success || !response.data?.id) return null;
32334
+ if (!response.success || !response.data?.id) {
32335
+ const hint = workflowNameTakenHint(wf.name, response.error);
32336
+ if (hint) throw new Error(hint);
32337
+ return null;
32338
+ }
31656
32339
  return response.data.id;
31657
32340
  }
31658
32341
  async pushToServer(apiKey, agentId, entityId, pushData) {
@@ -31680,7 +32363,48 @@ var WorkflowHandler = class extends BaseVersionedHandler {
31680
32363
  const primitive = findPrimitive(manifest, name, this.kind);
31681
32364
  if (!primitive) return null;
31682
32365
  if (primitive.form === "script") return this.buildPushData(primitive, void 0);
31683
- return super.prepareForPush(manifest, name, projectPath, bundleAccumulator);
32366
+ const data = super.prepareForPush(manifest, name, projectPath, bundleAccumulator);
32367
+ if (!data) return null;
32368
+ const tools = this.toolBundlesFor(primitive, manifest, projectPath, bundleAccumulator);
32369
+ return tools.length > 0 ? {
32370
+ ...data,
32371
+ tools
32372
+ } : data;
32373
+ }
32374
+ /**
32375
+ * LUA-656 (03 §3.5.2 / S3-6): every `.toolStep(id, tool)` reference compiles the tool as its own artifact
32376
+ * (`toolRefs[]`), but nothing ever pushed those artifacts — the version landed with `tools: null`, so the
32377
+ * engine's claim could not resolve `toolId` and every static tool step failed `TOOL_REF_UNRESOLVED`
32378
+ * (prod 2026-09-05, kitchen-sink `armB`, 39/39 runs). Each referenced tool rides the version as
32379
+ * `tools[] = {name, code | codeS3Hash}`, through the same accumulator the workflow bundle uses.
32380
+ */
32381
+ toolBundlesFor(wf, manifest, projectPath, bundleAccumulator) {
32382
+ const out = [];
32383
+ for (const toolName of [
32384
+ ...new Set(wf.toolRefs ?? [])
32385
+ ]) {
32386
+ const tool = findPrimitive(manifest, toolName, PrimitiveKind.TOOL);
32387
+ if (!tool) {
32388
+ console.warn(`\u26A0\uFE0F workflow "${wf.name}" references tool "${toolName}" but the manifest carries no compiled artifact for it \u2014 the engine can only resolve it through the agent's skills (TOOL_REF_UNRESOLVED otherwise)`);
32389
+ continue;
32390
+ }
32391
+ const code = loadArtifact(tool, projectPath);
32392
+ if (bundleAccumulator) {
32393
+ const rawGzip = compressForPushRaw(code);
32394
+ const codeS3Hash = hashBundle(rawGzip);
32395
+ bundleAccumulator.set(codeS3Hash, rawGzip);
32396
+ out.push({
32397
+ name: toolName,
32398
+ codeS3Hash
32399
+ });
32400
+ } else {
32401
+ out.push({
32402
+ name: toolName,
32403
+ code: compressForPush(code)
32404
+ });
32405
+ }
32406
+ }
32407
+ return out;
31684
32408
  }
31685
32409
  buildPushData(primitive, compressedCode, codeS3Hash) {
31686
32410
  const wf = primitive;
@@ -31746,6 +32470,10 @@ var WorkflowHandler = class extends BaseVersionedHandler {
31746
32470
  graph: wf.graph,
31747
32471
  graphHash: wf.graphHash,
31748
32472
  topology: wf.topology,
32473
+ // LUA-650: the compiler's linkGraphs verdict rides the push so the server defers workspace-not-declared too.
32474
+ ...wf.mayInherit ? {
32475
+ mayInherit: true
32476
+ } : {},
31749
32477
  ...wf.schemas ? {
31750
32478
  schemas: wf.schemas
31751
32479
  } : {},
@@ -31757,6 +32485,14 @@ var WorkflowHandler = class extends BaseVersionedHandler {
31757
32485
  }
31758
32486
  };
31759
32487
  var workflowHandler = new WorkflowHandler();
32488
+ function workflowNameTakenHint(name, error) {
32489
+ if (error?.code !== "WORKFLOW_NAME_TAKEN") return null;
32490
+ const holder = error.dynamic ? "a chat-composed (dynamic) workflow" : "an existing workflow";
32491
+ const id = error.workflowId ? ` (${error.workflowId})` : "";
32492
+ const see = error.dynamic ? "run `lua workflows list --all` to see it" : "run `lua workflows list` to see it";
32493
+ return `workflow name "${name}" is held by ${holder}${id} \u2014 ${see}. Push does not replace it: rename this workflow, or delete the other one first.`;
32494
+ }
32495
+ __name(workflowNameTakenHint, "workflowNameTakenHint");
31760
32496
 
31761
32497
  // src/primitives/index.ts
31762
32498
  init_skill_handler();
@@ -31787,6 +32523,7 @@ async function compileCommand(options) {
31787
32523
  const debugMode = options?.debug || process.env.LUA_DEBUG === "true";
31788
32524
  const verboseMode = options?.verbose || debugMode;
31789
32525
  const doSync = options?.sync === true;
32526
+ const doServerSync = options?.serverSync !== false;
31790
32527
  if (debugMode) {
31791
32528
  console.log("\u{1F41B} Debug mode enabled");
31792
32529
  }
@@ -31858,8 +32595,13 @@ async function compileCommand(options) {
31858
32595
  }
31859
32596
  ensureGitignored(rootDir, "dist-v2/", "dist/");
31860
32597
  if (result.warnings.length > 0) {
32598
+ const seen = /* @__PURE__ */ new Set();
31861
32599
  for (const warning of result.warnings) {
31862
- console.warn(`\u26A0\uFE0F ${warning.message}`);
32600
+ const key = `${warning.primitive ?? ""}\0${warning.message}`;
32601
+ if (seen.has(key)) continue;
32602
+ seen.add(key);
32603
+ const who = warning.primitive ? `[${warning.kind ? `${warning.kind} ` : ""}${warning.primitive}] ` : "";
32604
+ console.warn(`\u26A0\uFE0F ${who}${warning.message}`);
31863
32605
  }
31864
32606
  }
31865
32607
  writeProgress("\u{1F504} Syncing YAML with manifest...");
@@ -31869,13 +32611,17 @@ async function compileCommand(options) {
31869
32611
  config = readYamlConfig();
31870
32612
  }
31871
32613
  let apiKey = null;
31872
- try {
31873
- apiKey = await resolveRequestCredential();
31874
- } catch {
32614
+ if (doServerSync) {
32615
+ try {
32616
+ apiKey = await resolveRequestCredential();
32617
+ } catch {
32618
+ }
31875
32619
  }
31876
32620
  let syncConfig = readYamlConfig();
31877
32621
  const agentId = syncConfig?.agent?.agentId;
31878
- if (apiKey && agentId) {
32622
+ if (!doServerSync) {
32623
+ writeInfo("\u2139\uFE0F Server sync skipped (offline verb) \u2014 `lua push` when you are ready to publish.");
32624
+ } else if (apiKey && agentId) {
31879
32625
  writeProgress("\u{1F504} Syncing with server...");
31880
32626
  const fetchResults = await Promise.all(syncableHandlers.map((h) => h.fetchServerState(apiKey, agentId)));
31881
32627
  for (let i = 0; i < syncableHandlers.length; i++) {
@@ -31922,7 +32668,7 @@ async function compileCommand(options) {
31922
32668
  verbose_mode: verboseMode,
31923
32669
  sync_enabled: doSync,
31924
32670
  warnings_count: result.warnings.length,
31925
- server_sync_performed: !!(apiKey && agentId),
32671
+ server_sync_performed: doServerSync && !!(apiKey && agentId),
31926
32672
  backup_out_of_sync: backupOutOfSync
31927
32673
  });
31928
32674
  }, "compilation");
@@ -31981,18 +32727,18 @@ init_analytics();
31981
32727
 
31982
32728
  // src/utils/suggest.ts
31983
32729
  function levenshtein(a, b) {
31984
- const m = a.length, n = b.length;
32730
+ const m = a.length, n2 = b.length;
31985
32731
  const dp = Array.from({
31986
32732
  length: m + 1
31987
- }, () => Array(n + 1).fill(0));
32733
+ }, () => Array(n2 + 1).fill(0));
31988
32734
  for (let i = 0; i <= m; i++) dp[i][0] = i;
31989
- for (let j = 0; j <= n; j++) dp[0][j] = j;
32735
+ for (let j = 0; j <= n2; j++) dp[0][j] = j;
31990
32736
  for (let i = 1; i <= m; i++) {
31991
- for (let j = 1; j <= n; j++) {
32737
+ for (let j = 1; j <= n2; j++) {
31992
32738
  dp[i][j] = a[i - 1] === b[j - 1] ? dp[i - 1][j - 1] : 1 + Math.min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]);
31993
32739
  }
31994
32740
  }
31995
- return dp[m][n];
32741
+ return dp[m][n2];
31996
32742
  }
31997
32743
  __name(levenshtein, "levenshtein");
31998
32744
  function suggestClosest(input, candidates) {
@@ -32394,9 +33140,14 @@ var ALIAS_MAP = {
32394
33140
  "archive-runs",
32395
33141
  "workspace",
32396
33142
  "jobs",
32397
- "job-logs"
33143
+ "job-logs",
33144
+ "goals",
33145
+ "schedules"
32398
33146
  ],
32399
33147
  aliases: lowerKeys({
33148
+ goal: "goals",
33149
+ schedule: "schedules",
33150
+ sched: "schedules",
32400
33151
  ws: "workspace",
32401
33152
  job: "jobs",
32402
33153
  joblogs: "job-logs",
@@ -32426,6 +33177,45 @@ var ALIAS_MAP = {
32426
33177
  follow: "watch"
32427
33178
  })
32428
33179
  },
33180
+ // LUA-627 — the sub-verbs of `lua workflows goals <verb>` / `lua workflows schedules <verb>`.
33181
+ "workflows.goals.action": {
33182
+ canonical: [
33183
+ "list",
33184
+ "get",
33185
+ "create",
33186
+ "pause",
33187
+ "resume",
33188
+ "close"
33189
+ ],
33190
+ aliases: lowerKeys({
33191
+ ls: "list",
33192
+ view: "get",
33193
+ show: "get",
33194
+ info: "get",
33195
+ new: "create",
33196
+ add: "create",
33197
+ set: "create",
33198
+ hold: "pause",
33199
+ stop: "pause",
33200
+ unpause: "resume",
33201
+ continue: "resume",
33202
+ end: "close",
33203
+ finish: "close"
33204
+ })
33205
+ },
33206
+ "workflows.schedules.action": {
33207
+ canonical: [
33208
+ "list",
33209
+ "delete"
33210
+ ],
33211
+ aliases: lowerKeys({
33212
+ ls: "list",
33213
+ rm: "delete",
33214
+ remove: "delete",
33215
+ del: "delete",
33216
+ unschedule: "delete"
33217
+ })
33218
+ },
32429
33219
  "preprocessors.action": preprocessorsAction,
32430
33220
  "postprocessors.action": preprocessorsAction,
32431
33221
  "devices.action": {
@@ -32789,6 +33579,14 @@ init_cli();
32789
33579
  init_command_utils();
32790
33580
  init_artifact_loader();
32791
33581
  init_types();
33582
+
33583
+ // src/interfaces/workflows.ts
33584
+ function runIdOf(run) {
33585
+ return run.runId ?? run.id ?? "";
33586
+ }
33587
+ __name(runIdOf, "runIdOf");
33588
+
33589
+ // src/commands/workflow-local-run.ts
32792
33590
  init_dist2();
32793
33591
 
32794
33592
  // src/utils/workflow-script-local.ts
@@ -33227,7 +34025,9 @@ async function runWorkflowLocalFromProject(name, flags) {
33227
34025
  }, "say");
33228
34026
  if (!flags.noCompile) {
33229
34027
  say("\u{1F4E6} Compiling code first...");
33230
- await compileCommand();
34028
+ await compileCommand({
34029
+ serverSync: false
34030
+ });
33231
34031
  }
33232
34032
  const manifest = loadManifest();
33233
34033
  const workflows = getPrimitivesByKind(manifest, PrimitiveKind.WORKFLOW);
@@ -33410,7 +34210,7 @@ async function loadSeedFromRun(runId, wf, apiKey, agentId) {
33410
34210
  }
33411
34211
  }
33412
34212
  return seedLedgerFromRun({
33413
- id: run.data.id,
34213
+ id: runIdOf(run.data),
33414
34214
  graphHash: run.data.graphHash
33415
34215
  }, rows3, plan, {
33416
34216
  targetGraphHash: wf.graphHash
@@ -36934,6 +37734,19 @@ init_http_client();
36934
37734
  init_dist();
36935
37735
  init_lua_fetch();
36936
37736
  init_request_credential();
37737
+ var PLAIN_MARKDOWN_CLIENT_CAPABILITY = "plain-markdown";
37738
+ function withPlainMarkdown(chatData) {
37739
+ const declared = Array.isArray(chatData.clientCapabilities) ? chatData.clientCapabilities : [];
37740
+ if (declared.includes(PLAIN_MARKDOWN_CLIENT_CAPABILITY)) return chatData;
37741
+ return {
37742
+ ...chatData,
37743
+ clientCapabilities: [
37744
+ ...declared,
37745
+ PLAIN_MARKDOWN_CLIENT_CAPABILITY
37746
+ ]
37747
+ };
37748
+ }
37749
+ __name(withPlainMarkdown, "withPlainMarkdown");
36937
37750
  var ChatApi = class extends HttpClient {
36938
37751
  static {
36939
37752
  __name(this, "ChatApi");
@@ -36956,7 +37769,7 @@ var ChatApi = class extends HttpClient {
36956
37769
  * @throws Error if the agent is not found or the chat request fails
36957
37770
  */
36958
37771
  async sendMessage(agentId, chatData) {
36959
- return this.httpPostCoreDrainRetry(`/chat/generate/${agentId}?channel=dev`, chatData, {});
37772
+ return this.httpPostCoreDrainRetry(`/chat/generate/${agentId}?channel=dev`, withPlainMarkdown(chatData), {});
36960
37773
  }
36961
37774
  /**
36962
37775
  * Streams a message to an agent and receives chunked responses
@@ -36978,7 +37791,7 @@ var ChatApi = class extends HttpClient {
36978
37791
  Authorization: `Bearer ${await bearerFor(this.credential)}`,
36979
37792
  "Content-Type": "application/json"
36980
37793
  },
36981
- body: JSON.stringify(chatData)
37794
+ body: JSON.stringify(withPlainMarkdown(chatData))
36982
37795
  }), {
36983
37796
  signal: controller.signal
36984
37797
  });
@@ -40968,8 +41781,8 @@ async function doSearch(api) {
40968
41781
  return;
40969
41782
  }
40970
41783
  writeSuccess(`Found ${numbers.length} number(s):`);
40971
- for (const n of numbers) {
40972
- console.log(` ${n.msisdn} ${n.country ?? ""} ${n.type ?? ""} ${smsLabel(n.transport)} ${n.monthlyCost ?? ""} ${n.currency ?? ""}`);
41784
+ for (const n2 of numbers) {
41785
+ console.log(` ${n2.msisdn} ${n2.country ?? ""} ${n2.type ?? ""} ${smsLabel(n2.transport)} ${n2.monthlyCost ?? ""} ${n2.currency ?? ""}`);
40973
41786
  }
40974
41787
  }
40975
41788
  __name(doSearch, "doSearch");
@@ -40995,9 +41808,9 @@ async function doPurchase(api, agentId, orgId, apiKey) {
40995
41808
  // height and silently hides later entries.
40996
41809
  pageSize: 15,
40997
41810
  message: `Pick a number (${numbers.length} available):`,
40998
- choices: numbers.map((n) => ({
40999
- name: `${n.msisdn} (${n.country}, ${n.type ?? "\u2014"}) \u2014 ${smsLabel(n.transport)}`,
41000
- value: n.msisdn
41811
+ choices: numbers.map((n2) => ({
41812
+ name: `${n2.msisdn} (${n2.country}, ${n2.type ?? "\u2014"}) \u2014 ${smsLabel(n2.transport)}`,
41813
+ value: n2.msisdn
41001
41814
  }))
41002
41815
  }
41003
41816
  ]);
@@ -41102,13 +41915,13 @@ async function doList(api, orgId) {
41102
41915
  "Modality",
41103
41916
  "Agent",
41104
41917
  "Voice"
41105
- ], numbers.map((n) => [
41106
- n.msisdn,
41107
- n.country ?? "\u2014",
41108
- n.status,
41109
- smsLabel(n.transport),
41110
- n.agentId ?? "\u2014",
41111
- n.voiceId ?? "\u2014"
41918
+ ], numbers.map((n2) => [
41919
+ n2.msisdn,
41920
+ n2.country ?? "\u2014",
41921
+ n2.status,
41922
+ smsLabel(n2.transport),
41923
+ n2.agentId ?? "\u2014",
41924
+ n2.voiceId ?? "\u2014"
41112
41925
  ])));
41113
41926
  }
41114
41927
  __name(doList, "doList");
@@ -43872,12 +44685,12 @@ function deriveHints(report) {
43872
44685
  return hints;
43873
44686
  }
43874
44687
  __name(deriveHints, "deriveHints");
43875
- function pad(s, n) {
43876
- return s.length >= n ? s : s + " ".repeat(n - s.length);
44688
+ function pad(s, n2) {
44689
+ return s.length >= n2 ? s : s + " ".repeat(n2 - s.length);
43877
44690
  }
43878
44691
  __name(pad, "pad");
43879
- function truncate2(s, n) {
43880
- return s.length <= n ? s : s.slice(0, Math.max(0, n - 1)) + "\u2026";
44692
+ function truncate2(s, n2) {
44693
+ return s.length <= n2 ? s : s.slice(0, Math.max(0, n2 - 1)) + "\u2026";
43881
44694
  }
43882
44695
  __name(truncate2, "truncate");
43883
44696
  function printJson(report) {
@@ -44947,8 +45760,8 @@ var ACCEPTED_TIMEOUT_MS = 12e4;
44947
45760
  var DEFAULT_LOGS_LIMIT = 20;
44948
45761
  function parseLimitOption(raw) {
44949
45762
  if (raw === void 0 || raw === null || raw === "") return void 0;
44950
- const n = Number.parseInt(String(raw), 10);
44951
- return Number.isFinite(n) && n > 0 ? n : void 0;
45763
+ const n2 = Number.parseInt(String(raw), 10);
45764
+ return Number.isFinite(n2) && n2 > 0 ? n2 : void 0;
44952
45765
  }
44953
45766
  __name(parseLimitOption, "parseLimitOption");
44954
45767
  function isLegacyIntegrationAction(action) {
@@ -46895,6 +47708,12 @@ async function executeAction(ctx, action, target, extra, o) {
46895
47708
  return requireRun(runId, "jobs", () => jobsCore(ctx, runId, o));
46896
47709
  case "job-logs":
46897
47710
  return requireRun(runId, "job-logs", () => jobLogsCore(ctx, runId, extra ?? o.step, o));
47711
+ // ── goals <list|get|create|pause|resume|close> (LUA-627 — R57–R62) ──
47712
+ case "goals":
47713
+ return goalsCore(ctx, target, extra, o);
47714
+ // ── schedules <list|delete> (LUA-627 — R4-MF-2 reads, R28 delete; goal-owned rows refuse) ──
47715
+ case "schedules":
47716
+ return schedulesCore(ctx, target, extra, o);
46898
47717
  case "run":
46899
47718
  return WORKFLOW_EXIT.OK;
46900
47719
  }
@@ -46934,6 +47753,11 @@ function apiFailure(ctx, res, verb) {
46934
47753
  }
46935
47754
  __name(apiFailure, "apiFailure");
46936
47755
  async function resolveWorkflow(ctx, nameOrId) {
47756
+ const list = await loadWorkflowList(ctx);
47757
+ return list && pickWorkflow(list, nameOrId);
47758
+ }
47759
+ __name(resolveWorkflow, "resolveWorkflow");
47760
+ async function loadWorkflowList(ctx) {
46937
47761
  const res = await ctx.api.getWorkflows({
46938
47762
  includeDynamic: true
46939
47763
  });
@@ -46941,15 +47765,19 @@ async function resolveWorkflow(ctx, nameOrId) {
46941
47765
  console.error(`\u274C Failed to list workflows: ${res.error?.message ?? "Unknown error"}`);
46942
47766
  return void 0;
46943
47767
  }
46944
- const wf = res.data.workflows.find((w) => w.name === nameOrId) ?? res.data.workflows.find((w) => w.id === nameOrId);
47768
+ return res.data.workflows;
47769
+ }
47770
+ __name(loadWorkflowList, "loadWorkflowList");
47771
+ function pickWorkflow(list, nameOrId) {
47772
+ const wf = list.find((w) => w.name === nameOrId) ?? list.find((w) => w.id === nameOrId);
46945
47773
  if (!wf) {
46946
47774
  console.error(`\u274C Workflow "${nameOrId}" not found`);
46947
- const names = res.data.workflows.map((w) => w.name);
47775
+ const names = list.map((w) => w.name);
46948
47776
  if (names.length) console.log(` Available: ${names.join(", ")}`);
46949
47777
  }
46950
47778
  return wf;
46951
47779
  }
46952
- __name(resolveWorkflow, "resolveWorkflow");
47780
+ __name(pickWorkflow, "pickWorkflow");
46953
47781
  var shortHash = /* @__PURE__ */ __name((h) => h ? h.replace(/^sha256-cj1:/, "").slice(0, 12) : "\u2014", "shortHash");
46954
47782
  var when = /* @__PURE__ */ __name((iso) => iso ? new Date(iso).toLocaleString() : "\u2014", "when");
46955
47783
  var clockOf = /* @__PURE__ */ __name((at) => {
@@ -46958,9 +47786,9 @@ var clockOf = /* @__PURE__ */ __name((at) => {
46958
47786
  }, "clockOf");
46959
47787
  var parseIntFlag = /* @__PURE__ */ __name((v, flag) => {
46960
47788
  if (v === void 0) return void 0;
46961
- const n = Number(v);
46962
- if (!Number.isFinite(n)) throw new WorkflowLocalUsageError("usage", `${flag}: expected a number (got "${v}")`);
46963
- return n;
47789
+ const n2 = Number(v);
47790
+ if (!Number.isFinite(n2)) throw new WorkflowLocalUsageError("usage", `${flag}: expected a number (got "${v}")`);
47791
+ return n2;
46964
47792
  }, "parseIntFlag");
46965
47793
  async function listCore(ctx, opts) {
46966
47794
  const res = await ctx.api.getWorkflows({
@@ -46995,9 +47823,27 @@ async function viewCore(ctx, name) {
46995
47823
  if (!wf) return WORKFLOW_EXIT.NOT_FOUND;
46996
47824
  const res = await ctx.api.getWorkflow(wf.id);
46997
47825
  if (!res.success || !res.data) return apiFailure(ctx, res, "view");
46998
- emitJson(ctx, res);
46999
- if (ctx.json) return WORKFLOW_EXIT.OK;
47000
47826
  const w = res.data;
47827
+ const goals = await loadGoals(ctx, w.id);
47828
+ const sched = await loadSchedules(ctx, w.id, goals.items);
47829
+ if (ctx.json) {
47830
+ const data = {
47831
+ ...w,
47832
+ schedules: sched.items ?? null,
47833
+ ...sched.error ? {
47834
+ schedulesError: sched.error
47835
+ } : {},
47836
+ goals: goals.items ?? null,
47837
+ ...goals.error ? {
47838
+ goalsError: goals.error
47839
+ } : {}
47840
+ };
47841
+ console.log(JSON.stringify({
47842
+ ...res,
47843
+ data
47844
+ }, null, 2));
47845
+ return WORKFLOW_EXIT.OK;
47846
+ }
47001
47847
  console.log(`
47002
47848
  \u{1F9ED} ${w.name}${w.description ? ` \u2014 ${w.description}` : ""}`);
47003
47849
  console.log(` Id: ${w.id}`);
@@ -47011,6 +47857,8 @@ async function viewCore(ctx, name) {
47011
47857
  if (active?.envTemplateKeys?.length) {
47012
47858
  console.log(` Env overlay: ${active.envTemplateKeys.join(", ")}${active.envOverlayHash ? ` \xB7 ${active.envOverlayHash}` : ""}`);
47013
47859
  }
47860
+ printViewSchedules(sched);
47861
+ printViewGoals(goals);
47014
47862
  printVersions(w.versions ?? [], w.activeVersionId);
47015
47863
  return WORKFLOW_EXIT.OK;
47016
47864
  }
@@ -47047,24 +47895,41 @@ async function versionsCore(ctx, name) {
47047
47895
  return WORKFLOW_EXIT.OK;
47048
47896
  }
47049
47897
  __name(versionsCore, "versionsCore");
47050
- async function deployCore(ctx, name, version) {
47051
- const wf = await resolveWorkflow(ctx, name);
47052
- if (!wf) return WORKFLOW_EXIT.NOT_FOUND;
47898
+ async function resolveVersionRef(ctx, wf, raw, verb) {
47053
47899
  const versionsRes = await ctx.api.getWorkflowVersions(wf.id);
47054
- if (!versionsRes.success || !versionsRes.data) return apiFailure(ctx, versionsRes, "deploy");
47900
+ if (!versionsRes.success || !versionsRes.data) return {
47901
+ exit: apiFailure(ctx, versionsRes, verb)
47902
+ };
47055
47903
  const sorted = [
47056
47904
  ...versionsRes.data
47057
47905
  ].sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
47058
47906
  if (sorted.length === 0) {
47059
47907
  console.error(`\u274C "${wf.name}" has no versions \u2014 push one first: lua push workflow`);
47060
- return WORKFLOW_EXIT.NOT_FOUND;
47908
+ return {
47909
+ exit: WORKFLOW_EXIT.NOT_FOUND
47910
+ };
47061
47911
  }
47062
- let target = version;
47063
- if (!target || target.toLowerCase() === "latest") target = sorted[0].version;
47064
- else if (!sorted.some((v) => v.version === target)) {
47065
- console.error(`\u274C Version "${target}" not found. Available: ${sorted.slice(0, 5).map((v) => v.version).join(", ")}`);
47066
- return WORKFLOW_EXIT.NOT_FOUND;
47912
+ if (!raw || raw.toLowerCase() === "latest") return {
47913
+ version: sorted[0]
47914
+ };
47915
+ const hit = sorted.find((v) => v.version === raw) ?? sorted.find((v) => v.id === raw);
47916
+ if (!hit) {
47917
+ console.error(`\u274C Version "${raw}" not found. Available: ${sorted.slice(0, 5).map((v) => v.version).join(", ")}`);
47918
+ return {
47919
+ exit: WORKFLOW_EXIT.NOT_FOUND
47920
+ };
47067
47921
  }
47922
+ return {
47923
+ version: hit
47924
+ };
47925
+ }
47926
+ __name(resolveVersionRef, "resolveVersionRef");
47927
+ async function deployCore(ctx, name, version) {
47928
+ const wf = await resolveWorkflow(ctx, name);
47929
+ if (!wf) return WORKFLOW_EXIT.NOT_FOUND;
47930
+ const ref = await resolveVersionRef(ctx, wf, version, "deploy");
47931
+ if ("exit" in ref) return ref.exit;
47932
+ const target = ref.version.version;
47068
47933
  if (!ctx.json) writeProgress(`\u{1F504} Deploying version ${target} of "${wf.name}"...`);
47069
47934
  const res = await ctx.api.publishWorkflowVersion(wf.id, target);
47070
47935
  if (!res.success) return apiFailure(ctx, res, "deploy");
@@ -47131,6 +47996,12 @@ async function startCore(ctx, name, o) {
47131
47996
  console.error("\u274C --tag: at most 10 tags");
47132
47997
  return WORKFLOW_EXIT.USAGE;
47133
47998
  }
47999
+ let workflowVersionId;
48000
+ if (o.workflowVersion) {
48001
+ const ref = await resolveVersionRef(ctx, wf, o.workflowVersion, "start");
48002
+ if ("exit" in ref) return ref.exit;
48003
+ workflowVersionId = ref.version.id;
48004
+ }
47134
48005
  const res = await ctx.api.startRun({
47135
48006
  workflowId: wf.id,
47136
48007
  input,
@@ -47141,7 +48012,7 @@ async function startCore(ctx, name, o) {
47141
48012
  maxCredits: budgetCredits
47142
48013
  } : void 0,
47143
48014
  waitSeconds,
47144
- workflowVersionId: o.workflowVersion && o.workflowVersion !== "latest" ? o.workflowVersion : void 0
48015
+ workflowVersionId
47145
48016
  });
47146
48017
  if (!res.success || !res.data) {
47147
48018
  if (res.error?.statusCode === 409 && (res.error.code ?? res.error.error) === "RUNS_IN_FLIGHT") {
@@ -47223,7 +48094,7 @@ async function runsCore(ctx, o) {
47223
48094
  "Correlation",
47224
48095
  "Tags"
47225
48096
  ], runs.map((r) => [
47226
- r.id,
48097
+ runIdOf(r),
47227
48098
  r.status,
47228
48099
  r.trigger,
47229
48100
  when(r.createdAt),
@@ -47236,6 +48107,30 @@ async function runsCore(ctx, o) {
47236
48107
  return WORKFLOW_EXIT.OK;
47237
48108
  }
47238
48109
  __name(runsCore, "runsCore");
48110
+ function formatRunConnections(connections, status) {
48111
+ if (!connections) return void 0;
48112
+ const unresolvedWord = status === "completed" ? "not needed" : "unresolved";
48113
+ const declared = connections.declared ?? [];
48114
+ const resolved = connections.resolved ?? [];
48115
+ if (!declared.length && !resolved.length) return void 0;
48116
+ const byKey = new Map(resolved.map((r) => [
48117
+ r.key,
48118
+ r
48119
+ ]));
48120
+ const keys = [
48121
+ .../* @__PURE__ */ new Set([
48122
+ ...declared.map((d) => d.key),
48123
+ ...resolved.map((r) => r.key)
48124
+ ])
48125
+ ];
48126
+ return keys.map((key) => {
48127
+ const r = byKey.get(key);
48128
+ if (r) return `${key} \u2192 ${r.connectionId} (${r.integrationType}, ${r.scope})`;
48129
+ const d = declared.find((x) => x.key === key);
48130
+ return `${key} \u2192 ${unresolvedWord} (${d?.integrationType ?? "?"})`;
48131
+ }).join(" \xB7 ");
48132
+ }
48133
+ __name(formatRunConnections, "formatRunConnections");
47239
48134
  async function statusCore(ctx, runId, o) {
47240
48135
  const res = await ctx.api.getRun(runId, o.steps ? {
47241
48136
  fields: "full"
@@ -47249,28 +48144,40 @@ async function statusCore(ctx, runId, o) {
47249
48144
  }
47250
48145
  __name(statusCore, "statusCore");
47251
48146
  function printRun(run, withSteps) {
48147
+ const id = runIdOf(run);
47252
48148
  console.log(`
47253
- \u{1F9ED} Run ${run.id} \xB7 ${run.status}`);
48149
+ \u{1F9ED} Run ${id} \xB7 ${run.status}`);
47254
48150
  console.log(` Workflow: ${run.workflowId} @ ${run.workflowVersionId} (${shortHash(run.graphHash)})`);
47255
48151
  console.log(` Trigger: ${run.trigger}${run.correlationKey ? ` \xB7 correlation ${run.correlationKey}` : ""}${run.tags?.length ? ` \xB7 tags ${run.tags.join(",")}` : ""}`);
47256
48152
  console.log(` Created: ${when(run.createdAt)}${run.startedAt ? ` \xB7 started ${when(run.startedAt)}` : ""}${run.completedAt ? ` \xB7 completed ${when(run.completedAt)}` : ""}`);
47257
- if (run.lineageId && run.lineageId !== run.id) console.log(` Lineage: ${run.lineageId}`);
48153
+ if (run.lineageId && run.lineageId !== id) console.log(` Lineage: ${run.lineageId}`);
47258
48154
  if (run.gate) {
47259
48155
  const kind = run.gate.kind;
47260
48156
  if (kind === "exception") {
47261
48157
  console.log("\n\u26A0\uFE0F Needs a decision \u2014 the run is parked on an exception gate:");
47262
- console.log(` lua workflows retry-step ${run.id} --step <id> \xB7 resolve-step ${run.id} --step <id> --outcome skip|complete|fail \xB7 repair ${run.id}`);
48158
+ console.log(` lua workflows retry-step ${id} --step <id> \xB7 resolve-step ${id} --step <id> --outcome skip|complete|fail \xB7 repair ${id}`);
47263
48159
  } else if (kind === "budget") {
47264
48160
  console.log("\n\u23F8\uFE0F Paused \u2014 run budget reached:");
47265
- console.log(` lua workflows raise-budget ${run.id} --credits <n>`);
48161
+ console.log(` lua workflows raise-budget ${id} --credits <n>`);
47266
48162
  } else {
47267
48163
  console.log(` Gate: ${kind}${run.gate.reason ? ` (${run.gate.reason})` : ""}${run.gate.since ? ` since ${when(run.gate.since)}` : ""}`);
47268
48164
  }
47269
48165
  }
47270
48166
  if (run.failureReason) console.log(` Failure: ${run.failureReason}`);
47271
- if (run.restricted) console.log(" Outputs: restricted (outputVisibility) \u2014 output not returned");
47272
- else if (run.output !== void 0) console.log(` Output: ${JSON.stringify(run.output).slice(0, 2e3)}`);
48167
+ const connectionsLine = formatRunConnections(run.connections, run.status);
48168
+ if (connectionsLine) console.log(` Connections: ${connectionsLine}`);
47273
48169
  const steps = run.steps;
48170
+ if (run.restricted) console.log(" Outputs: restricted (outputVisibility) \u2014 output not returned");
48171
+ else if (run.output !== void 0 && run.output !== null) {
48172
+ const text = typeof run.outputPreview === "string" ? `${run.outputPreview} (offloaded \u2014 preview)` : JSON.stringify(run.output).slice(0, 2e3);
48173
+ console.log(` Output: ${text}`);
48174
+ } else if (run.hasOutput) {
48175
+ console.log(` Output: present \u2014 lua workflows status ${run.id} --steps`);
48176
+ } else if (RUN_TERMINAL_STATUSES.has(run.status)) {
48177
+ const last = lastCompletedPreview(steps);
48178
+ console.log(` Output: (none${last ? "" : withSteps ? "" : " \u2014 pass --steps for per-step output previews"})`);
48179
+ if (last) console.log(` Last step output (preview): ${last.stepId} \u2192 ${JSON.stringify(last.output).slice(0, 2e3)}`);
48180
+ }
47274
48181
  if (withSteps) {
47275
48182
  if (!steps?.length) console.log("\n (no step rows in the response)");
47276
48183
  else {
@@ -47286,12 +48193,69 @@ function printRun(run, withSteps) {
47286
48193
  s.kind ?? "\u2014",
47287
48194
  s.status,
47288
48195
  String(s.attempt ?? ""),
47289
- s.error?.code ?? ""
48196
+ stepErrorCell(s.error)
47290
48197
  ])));
48198
+ for (const s of steps) {
48199
+ const cause = stepFailureLine(s.error);
48200
+ if (cause) console.log(` \u2716 ${s.stepId}: ${cause}`);
48201
+ for (const a of s.attempts ?? []) {
48202
+ if (a.status === "completed") continue;
48203
+ const what = stepErrorCell(a.error) || a.killReason || "\u2014";
48204
+ console.log(` \u21BA ${s.stepId}: attempt ${a.attempt} ${a.status}: ${what}${priorAttemptTail(a)}`);
48205
+ }
48206
+ }
47291
48207
  }
47292
48208
  }
47293
48209
  }
47294
48210
  __name(printRun, "printRun");
48211
+ var RUN_TERMINAL_STATUSES = /* @__PURE__ */ new Set([
48212
+ "completed",
48213
+ "failed",
48214
+ "cancelled",
48215
+ "abandoned",
48216
+ "timed_out"
48217
+ ]);
48218
+ function lastCompletedPreview(steps) {
48219
+ if (!steps?.length) return void 0;
48220
+ let best;
48221
+ for (const s of steps) {
48222
+ if (s.status !== "completed" || s.preview?.output === void 0) continue;
48223
+ if (!best || (s.completedAt ?? 0) >= (best.completedAt ?? 0)) best = s;
48224
+ }
48225
+ return best ? {
48226
+ stepId: best.stepId,
48227
+ output: best.preview.output
48228
+ } : void 0;
48229
+ }
48230
+ __name(lastCompletedPreview, "lastCompletedPreview");
48231
+ function stepErrorCell(e) {
48232
+ if (!e?.code) return "";
48233
+ return e.reason ? `${e.code} (${e.reason})` : e.code;
48234
+ }
48235
+ __name(stepErrorCell, "stepErrorCell");
48236
+ function priorAttemptTail(a) {
48237
+ const e = a.error;
48238
+ const parts = [
48239
+ e?.providerStatus !== void 0 ? `HTTP ${e.providerStatus}` : void 0,
48240
+ a.podName ? `pod ${a.podName}` : void 0
48241
+ ].filter((p) => !!p);
48242
+ return `${parts.length ? ` \u2014 ${parts.join(" \xB7 ")}` : ""}${e?.providerMessage ? ` \u2014 ${e.providerMessage}` : ""}`;
48243
+ }
48244
+ __name(priorAttemptTail, "priorAttemptTail");
48245
+ function stepFailureLine(e) {
48246
+ if (!e || !e.reason && e.providerStatus === void 0 && !e.providerMessage) return void 0;
48247
+ const b = e.budget;
48248
+ const parts = [
48249
+ e.reason,
48250
+ e.providerStatus !== void 0 ? `HTTP ${e.providerStatus}` : void 0,
48251
+ e.model ? `model ${e.model}` : void 0,
48252
+ e.turnIndex !== void 0 ? `message ${e.turnIndex}${e.assistantTurns !== void 0 ? ` (${e.assistantTurns} replies)` : ""}${b?.maxMessages !== void 0 ? ` of ${b.maxMessages}` : ""}` : void 0,
48253
+ b ? `${b.inputTokens.toLocaleString("en-US")}${b.maxInputTokens !== void 0 ? ` of ${b.maxInputTokens.toLocaleString("en-US")}` : ""} input tokens` : void 0,
48254
+ e.providerRetries ? `${e.providerRetries} provider ${e.providerRetries === 1 ? "retry" : "retries"}` : void 0
48255
+ ].filter((p) => !!p);
48256
+ return `${parts.join(" \xB7 ")}${e.providerMessage ? `${parts.length ? " \u2014 " : ""}${e.providerMessage}` : ""}`;
48257
+ }
48258
+ __name(stepFailureLine, "stepFailureLine");
47295
48259
  async function watchCore(ctx, runId, o) {
47296
48260
  let afterSeq;
47297
48261
  let timeoutS;
@@ -47342,6 +48306,8 @@ async function watchCore(ctx, runId, o) {
47342
48306
  if (!ctx.json) console.error(`\u2026 reconnecting (Last-Event-ID ${lastEventId ?? "none"})`);
47343
48307
  await new Promise((r) => setTimeout(r, Math.min(5e3, 250 * 2 ** reconnects)));
47344
48308
  }
48309
+ } catch (e) {
48310
+ if (!controller.signal.aborted) throw e;
47345
48311
  } finally {
47346
48312
  if (timer) clearTimeout(timer);
47347
48313
  process.off("SIGINT", onSigint);
@@ -47384,7 +48350,7 @@ function handleWatchFrame(ctx, runId, frame, o, throttled) {
47384
48350
  throttled.set(kind, (throttled.get(kind) ?? 0) + 1);
47385
48351
  if (!ctx.json && !o.events) process.stdout.write(`\r throttled \xB7 ${[
47386
48352
  ...throttled
47387
- ].map(([k, n]) => `${k} \xD7${n}`).join(" \xB7 ")}`);
48353
+ ].map(([k, n2]) => `${k} \xD7${n2}`).join(" \xB7 ")}`);
47388
48354
  return void 0;
47389
48355
  }
47390
48356
  if (!ctx.json && !o.events) {
@@ -47455,13 +48421,28 @@ async function resumeCore(ctx, runId, o) {
47455
48421
  const res = await ctx.api.resumeRun(runId, o.step, {
47456
48422
  resumeData
47457
48423
  });
48424
+ const resumeCode = !res.success ? res.error?.code ?? res.error?.error : void 0;
48425
+ const resumeDetail = res.error;
48426
+ if (!ctx.json && res.error?.statusCode === 400 && resumeCode === "RESUME_SCHEMA_INVALID") {
48427
+ console.error(`\u274C resume rejected \u2014 --data does not match the resumeSchema step "${resumeDetail?.stepId ?? o.step}" declares:`);
48428
+ for (const issue of resumeDetail?.issues ?? []) console.error(` \u2022 ${issue.path || "/"}: ${issue.message ?? "invalid"}`);
48429
+ console.error(" fix the data and resume again (the step is still waiting for input)");
48430
+ } else if (!ctx.json && res.error?.statusCode === 422 && resumeCode === "RESUME_SCHEMA_UNCOMPILABLE") {
48431
+ console.error(`\u274C resume could not be checked \u2014 the resumeSchema step "${resumeDetail?.stepId ?? o.step}" declares does not compile:`);
48432
+ for (const issue of resumeDetail?.issues ?? []) console.error(` \u2022 ${issue.message ?? "schema does not compile"}`);
48433
+ console.error(" this is a workflow definition defect, not a data problem \u2014 fix the `resumeSchema` on the step in the workflow source, push a new version, then resume again");
48434
+ }
47458
48435
  if (!res.success || !res.data) {
47459
- if (res.error?.statusCode === 409 && !ctx.json) {
47460
- const code = res.error.code ?? res.error.error;
48436
+ const code = res.error?.code ?? res.error?.error;
48437
+ if (!ctx.json && res.error?.statusCode === 404 && code === "STEP_NOT_FOUND") {
48438
+ console.error(`\u274C step "${o.step}" does not exist on run ${runId} \u2014 list its steps: lua workflows status ${runId} --steps`);
48439
+ } else if (!ctx.json && res.error?.statusCode === 409) {
47461
48440
  if (code === "approval_requires_human" || code === "APPROVAL_REQUIRES_HUMAN") {
47462
48441
  console.error(`\u274C step "${o.step}" is an approval \u2014 resolve it with: lua workflows approve ${runId} --approval ${o.step} --decision approve`);
47463
48442
  } else if (code === "use_signal_route" || code === "USE_SIGNAL_ROUTE") {
47464
48443
  console.error(`\u274C step "${o.step}" waits for a signal \u2014 deliver it with: lua workflows signal ${runId} <name> --payload '{}'`);
48444
+ } else if (code === "NOT_SUSPENDED") {
48445
+ console.error(`\u274C step "${o.step}" is not suspended${res.error?.status ? ` (it is ${res.error.status})` : ""} \u2014 nothing to resume: lua workflows status ${runId} --steps`);
47465
48446
  }
47466
48447
  }
47467
48448
  return apiFailure(ctx, res, "resume");
@@ -47526,7 +48507,9 @@ async function approveCore(ctx, runId, o) {
47526
48507
  });
47527
48508
  if (!res.success || !res.data) {
47528
48509
  const code = res.error?.code ?? res.error?.error;
47529
- if (!ctx.json && res.error?.statusCode === 409 && code === "PAYLOAD_MISMATCH") {
48510
+ if (!ctx.json && res.error?.statusCode === 404 && code === "APPROVAL_NOT_FOUND") {
48511
+ console.error(`\u274C approval "${o.approval}" not found on run ${runId} \u2014 pass the pending approval's wfa_\u2026 id: lua workflows status ${runId} --steps --json shows it as the suspended step's suspend.approvalId`);
48512
+ } else if (!ctx.json && res.error?.statusCode === 409 && code === "PAYLOAD_MISMATCH") {
47530
48513
  console.error(`\u274C the payload changed since you looked \u2014 refetch: lua workflows approval-payload ${runId} --approval ${o.approval}`);
47531
48514
  } else if (!ctx.json && res.error?.statusCode === 403 && code === "STEP_UP_REQUIRED") {
47532
48515
  console.error("\u274C approve from the desktop with a fresh login (step-up required)");
@@ -47557,7 +48540,25 @@ async function signalCore(ctx, runId, name, o) {
47557
48540
  payload,
47558
48541
  dedupeKey: o.dedupeKey
47559
48542
  });
47560
- if (!res.success || !res.data) return apiFailure(ctx, res, "signal");
48543
+ if (!res.success || !res.data) {
48544
+ const code = res.error?.code ?? res.error?.error;
48545
+ if (!ctx.json && res.error?.statusCode === 400 && code === "SIGNAL_SCHEMA_INVALID") {
48546
+ const where = res.error?.stepId ? `step "${res.error.stepId}"` : "the waiting step";
48547
+ console.error(`\u274C signal "${name}" rejected \u2014 the payload does not match the schema ${where} declares:`);
48548
+ for (const issue of res.error?.issues ?? []) {
48549
+ console.error(` \u2022 ${issue.path || "/"}: ${issue.message ?? "invalid"}`);
48550
+ }
48551
+ console.error(" fix the payload and send it again (a --dedupe-key is not consumed by a rejected signal)");
48552
+ } else if (!ctx.json && res.error?.statusCode === 422 && code === "SIGNAL_SCHEMA_UNCOMPILABLE") {
48553
+ const where = res.error?.stepId ? `step "${res.error.stepId}"` : "the waiting step";
48554
+ console.error(`\u274C signal "${name}" could not be checked \u2014 the schema ${where} declares does not compile:`);
48555
+ for (const issue of res.error?.issues ?? []) {
48556
+ console.error(` \u2022 ${issue.message ?? "schema does not compile"}`);
48557
+ }
48558
+ console.error(` this is a workflow definition defect, not a payload problem \u2014 fix the \`schema\` on the waitForSignal node in the workflow source, push a new version, then send the signal again`);
48559
+ }
48560
+ return apiFailure(ctx, res, "signal");
48561
+ }
47561
48562
  emitJson(ctx, res);
47562
48563
  const r = res.data;
47563
48564
  if (!ctx.json) {
@@ -47623,10 +48624,25 @@ async function replayCore(ctx, runId, o) {
47623
48624
  const rows3 = [];
47624
48625
  for (const stepId of plan.order) {
47625
48626
  const step3 = await ctx.api.getRunStep(runId, stepId);
47626
- if (step3.success && step3.data) rows3.push({
48627
+ if (!step3.success || !step3.data) continue;
48628
+ const error = step3.data.error;
48629
+ const onError = plan.steps[stepId]?.entry?.onError;
48630
+ rows3.push({
47627
48631
  stepId,
47628
48632
  status: step3.data.status,
47629
- output: step3.data.output
48633
+ output: step3.data.output,
48634
+ ...onError ? {
48635
+ onError
48636
+ } : {},
48637
+ ...error ? {
48638
+ error: {
48639
+ code: error.code,
48640
+ message: error.message
48641
+ }
48642
+ } : {},
48643
+ ...error?.killReason ? {
48644
+ killReason: error.killReason
48645
+ } : {}
47630
48646
  });
47631
48647
  }
47632
48648
  const result = replayLedger(wf.graph, {
@@ -47770,9 +48786,9 @@ function parseSinceFlag(raw, now = Date.now()) {
47770
48786
  if (!raw) return NaN;
47771
48787
  const m = /^(\d+)([dhm])$/i.exec(raw.trim());
47772
48788
  if (m) {
47773
- const n = Number(m[1]);
48789
+ const n2 = Number(m[1]);
47774
48790
  const unit = m[2].toLowerCase();
47775
- return now - n * (unit === "d" ? 864e5 : unit === "h" ? 36e5 : 6e4);
48791
+ return now - n2 * (unit === "d" ? 864e5 : unit === "h" ? 36e5 : 6e4);
47776
48792
  }
47777
48793
  return Date.parse(raw);
47778
48794
  }
@@ -47875,7 +48891,11 @@ async function archiveRunsCore(ctx, o) {
47875
48891
  }, "say");
47876
48892
  const sha256 = /* @__PURE__ */ __name((buf) => crypto5.createHash("sha256").update(buf).digest("hex"), "sha256");
47877
48893
  const sleep3 = /* @__PURE__ */ __name((ms) => new Promise((r) => setTimeout(r, ms)), "sleep");
47878
- const archiveOne = /* @__PURE__ */ __name(async (run) => {
48894
+ const archiveOne = /* @__PURE__ */ __name(async (apiRow) => {
48895
+ const run = {
48896
+ ...apiRow,
48897
+ id: runIdOf(apiRow)
48898
+ };
47879
48899
  const zipFile = path23.join(o.out, `${run.id}.zip`);
47880
48900
  const prior = indexed.get(run.id);
47881
48901
  const completedAt = run.completedAt ? Date.parse(run.completedAt) : NaN;
@@ -48128,6 +49148,9 @@ function printJobHeader(v) {
48128
49148
  \u{1F6E0} ${v.runId} \xB7 ${v.stepId} \xB7 attempt ${v.attempt} \xB7 ${v.status} \xB7 ${v.phase}${v.jobName ? ` \xB7 ${v.jobName}` : ""}${v.sizeClass ? ` (${v.sizeClass})` : ""}`);
48129
49149
  console.log(` Segment ${v.segment} of ${v.segmentsTotalMax}${v.checkpoint ? ` \xB7 resumed from the ${Math.round(v.checkpoint.elapsedSeconds / 60)} min checkpoint` : ""}${v.harness ? ` \xB7 ${v.harness}` : ""}${v.podName ? ` \xB7 pod ${v.podName}` : ""}${v.heartbeatAt ? ` \xB7 heartbeat ${ago(v.heartbeatAt)}` : ""}`);
48130
49150
  if (v.error) console.log(` Error: ${v.error.code}${v.error.killReason ? ` (${v.error.killReason})` : ""} \u2014 ${v.error.message}`);
49151
+ const cause = stepFailureLine(v.error);
49152
+ if (cause) console.log(` Cause: ${cause}`);
49153
+ if (v.note) console.log(` Note: ${v.note}`);
48131
49154
  console.log(` Logs (${v.logsSource}${v.logs.length ? `, ${v.logs.length} lines` : ""}):`);
48132
49155
  }
48133
49156
  __name(printJobHeader, "printJobHeader");
@@ -48153,6 +49176,14 @@ async function jobLogsCore(ctx, runId, stepId, o) {
48153
49176
  if (res.error?.statusCode === 404 && res.error?.code === "JOB_NOT_FOUND" && !ctx.json) {
48154
49177
  console.error(`\u274C ${stepId} is not a Job-tier step of ${runId} (try: lua workflows jobs ${runId})`);
48155
49178
  }
49179
+ if (res.error?.statusCode === 404 && res.error?.code === "JOB_LOGS_NOT_PERSISTED") {
49180
+ emitJson(ctx, res);
49181
+ if (!ctx.json) {
49182
+ console.error(`\u274C job-logs: ${res.error.message}`);
49183
+ writeInfo(` lua workflows status ${runId} --steps shows the step error; --attempt <n> reads another attempt.`);
49184
+ }
49185
+ return WORKFLOW_EXIT.NOT_FOUND;
49186
+ }
48156
49187
  return apiFailure(ctx, res, "job-logs");
48157
49188
  }
48158
49189
  const v = res.data;
@@ -48167,7 +49198,7 @@ async function jobLogsCore(ctx, runId, stepId, o) {
48167
49198
  for (const line of v.logs.slice(printed)) console.log(` ${line}`);
48168
49199
  printed = Math.max(printed, v.logs.length);
48169
49200
  const terminal = TERMINAL2.has(v.status) || /^(completed|failed|cancelled|skipped|reaped|deleted)$/.test(v.status);
48170
- if (!o.follow || terminal || v.phase === "deleted") {
49201
+ if (!o.follow || terminal || v.attemptFinished || v.phase === "deleted") {
48171
49202
  if (o.follow && !ctx.json) writeInfo(` \u2014 ${v.status} (${v.phase})`);
48172
49203
  return WORKFLOW_EXIT.OK;
48173
49204
  }
@@ -48175,6 +49206,659 @@ async function jobLogsCore(ctx, runId, stepId, o) {
48175
49206
  }
48176
49207
  }
48177
49208
  __name(jobLogsCore, "jobLogsCore");
49209
+ var GOAL_VERBS = [
49210
+ "list",
49211
+ "get",
49212
+ "create",
49213
+ "pause",
49214
+ "resume",
49215
+ "close"
49216
+ ];
49217
+ var SCHEDULE_VERBS = [
49218
+ "list",
49219
+ "delete"
49220
+ ];
49221
+ var GOAL_STATUSES = [
49222
+ "active",
49223
+ "paused",
49224
+ "done",
49225
+ "closed"
49226
+ ];
49227
+ var GOAL_PREDICATE_OPS = [
49228
+ "exists",
49229
+ "truthy",
49230
+ "eq",
49231
+ "neq",
49232
+ "gt",
49233
+ "gte",
49234
+ "lt",
49235
+ "lte"
49236
+ ];
49237
+ var GOAL_CADENCE_MAX = 5;
49238
+ var GOAL_MAX_RUNS_CAP = 100;
49239
+ var GOAL_PAGE_LIMIT = 100;
49240
+ var GOAL_PAGE_MAX = 50;
49241
+ function parseIntegerFlag(v, flag, range2 = {}) {
49242
+ if (v === void 0) return void 0;
49243
+ const text = String(v).trim();
49244
+ if (!/^-?\d+$/.test(text)) throw new WorkflowLocalUsageError("usage", `${flag}: expected an integer (got "${v}")`);
49245
+ const n2 = Number(text);
49246
+ const { min, max } = range2;
49247
+ if (min !== void 0 && n2 < min || max !== void 0 && n2 > max) {
49248
+ const bounds = max !== void 0 ? `${min ?? ""}..${max}` : `\u2265 ${min}`;
49249
+ throw new WorkflowLocalUsageError("usage", `${flag}: expected an integer ${bounds} (got "${v}")`);
49250
+ }
49251
+ return n2;
49252
+ }
49253
+ __name(parseIntegerFlag, "parseIntegerFlag");
49254
+ function goalScheduleRefusal(jobId, goalId) {
49255
+ return `Schedule ${jobId} is the cadence of goal ${goalId} and was NOT removed \u2014 nothing was changed. To stop the goal use \`lua workflows goals pause ${goalId}\` (it can come back) or \`lua workflows goals close ${goalId}\` (final); unscheduling a goal's job is recorded as a failure of the goal, never as stopping it.`;
49256
+ }
49257
+ __name(goalScheduleRefusal, "goalScheduleRefusal");
49258
+ function triggerLabel(t) {
49259
+ if (!t || typeof t !== "object") return "\u2014";
49260
+ if (t.type === "cron") return `cron ${t.expression ?? "?"}${t.timezone ? ` (${t.timezone})` : ""}`;
49261
+ if (t.type === "interval") return `every ${t.seconds ?? "?"}s`;
49262
+ if (t.type === "once") return `once ${t.executeAt ?? "?"}`;
49263
+ return String(t.type ?? "?");
49264
+ }
49265
+ __name(triggerLabel, "triggerLabel");
49266
+ var cadenceLabel = /* @__PURE__ */ __name((cadence) => cadence?.length ? cadence.map((c) => triggerLabel(c)).join(" \xB7 ") : "immediate", "cadenceLabel");
49267
+ var judgeLabel = /* @__PURE__ */ __name((judge) => {
49268
+ if (!judge) return "\u2014";
49269
+ if (judge.predicate) {
49270
+ const p = judge.predicate;
49271
+ return `predicate ${p.path} ${p.op}${p.value !== void 0 ? ` ${JSON.stringify(p.value)}` : ""}`;
49272
+ }
49273
+ return `agent ${judge.agentId ?? "?"}${judge.role?.name ? ` (${judge.role.name})` : ""}${judge.schema ? " \xB7 schema" : ""}`;
49274
+ }, "judgeLabel");
49275
+ var goalStatusLabel = /* @__PURE__ */ __name((g) => `${g.status}${g.pauseReason ? ` (${g.pauseReason})` : ""}`, "goalStatusLabel");
49276
+ var verdictLabel = /* @__PURE__ */ __name((v) => v ? `${v.done ? "done" : "continue"}${v.summary ? ` \xB7 ${v.summary.slice(0, 60)}` : ""}` : "\u2014", "verdictLabel");
49277
+ var scheduleStatus = /* @__PURE__ */ __name((r) => r.autoDisabled ? "auto-disabled" : r.paused ? "paused" : "active", "scheduleStatus");
49278
+ var errorLabel = /* @__PURE__ */ __name((e) => `${e.code ?? e.error ?? e.statusCode ?? "?"} \u2014 ${e.message}`, "errorLabel");
49279
+ async function loadGoals(ctx, workflowId) {
49280
+ const items = [];
49281
+ let cursor;
49282
+ for (let page = 0; page < GOAL_PAGE_MAX; page++) {
49283
+ const res = await ctx.api.getGoals({
49284
+ workflowId,
49285
+ cursor,
49286
+ limit: GOAL_PAGE_LIMIT
49287
+ });
49288
+ if (!res.success || !res.data) return {
49289
+ error: res.error ?? {
49290
+ message: "goals unavailable"
49291
+ }
49292
+ };
49293
+ items.push(...res.data.items ?? []);
49294
+ cursor = res.data.nextCursor;
49295
+ if (!cursor) break;
49296
+ }
49297
+ if (cursor) return {
49298
+ error: {
49299
+ code: "GOAL_LIST_TRUNCATED",
49300
+ message: "goal list truncated; ownership cannot be proven"
49301
+ }
49302
+ };
49303
+ return {
49304
+ items
49305
+ };
49306
+ }
49307
+ __name(loadGoals, "loadGoals");
49308
+ async function loadSchedules(ctx, workflowId, goals) {
49309
+ const res = await ctx.api.listSchedules();
49310
+ if (!res.success || !res.data) return {
49311
+ error: res.error ?? {
49312
+ message: "schedules unavailable"
49313
+ }
49314
+ };
49315
+ const byJob = /* @__PURE__ */ new Map();
49316
+ for (const g of goals ?? []) if (g.jobId) byJob.set(g.jobId, g.goalId);
49317
+ const items = (res.data.items ?? []).filter((r) => !workflowId || r.workflowId === workflowId).map((r) => {
49318
+ const goalId = r.goalId ?? byJob.get(r.jobId);
49319
+ return goalId ? {
49320
+ ...r,
49321
+ goalId
49322
+ } : r;
49323
+ });
49324
+ return {
49325
+ items
49326
+ };
49327
+ }
49328
+ __name(loadSchedules, "loadSchedules");
49329
+ function printSchedules(items, names) {
49330
+ if (!items.length) {
49331
+ console.log(" (no schedules)");
49332
+ return;
49333
+ }
49334
+ console.log(renderTable([
49335
+ "Job",
49336
+ "Workflow",
49337
+ "Trigger",
49338
+ "Next",
49339
+ "Last fired",
49340
+ "Status",
49341
+ "Strikes",
49342
+ "Goal"
49343
+ ], items.map((r) => [
49344
+ r.jobId,
49345
+ names?.get(r.workflowId) ?? r.workflowId,
49346
+ (r.trigger ?? []).map(triggerLabel).join(" \xB7 ") || "\u2014",
49347
+ when(r.nextRunAt ?? void 0),
49348
+ when(r.lastFiredAt),
49349
+ scheduleStatus(r),
49350
+ String(r.consecutiveFailures ?? 0),
49351
+ r.goalId ?? "\u2014"
49352
+ ])));
49353
+ const owned = items.filter((r) => r.goalId).length;
49354
+ if (owned) writeInfo(`\u{1F4A1} ${owned} goal-owned schedule(s) \u2014 stop those with lua workflows goals pause|close <goalId>, never schedules delete`);
49355
+ }
49356
+ __name(printSchedules, "printSchedules");
49357
+ function printGoals(items, names) {
49358
+ if (!items.length) {
49359
+ console.log(" (no goals)");
49360
+ return;
49361
+ }
49362
+ const head = [
49363
+ "Goal",
49364
+ ...names ? [
49365
+ "Workflow"
49366
+ ] : [],
49367
+ "Status",
49368
+ "Runs",
49369
+ "Iter",
49370
+ "Judge",
49371
+ "Cadence",
49372
+ "Verdict",
49373
+ "Updated"
49374
+ ];
49375
+ console.log(renderTable(head, items.map((g) => [
49376
+ g.goalId,
49377
+ ...names ? [
49378
+ names.get(g.workflowId) ?? g.workflowId
49379
+ ] : [],
49380
+ goalStatusLabel(g),
49381
+ `${g.runsUsed}/${g.maxRuns}`,
49382
+ String(g.iteration ?? 0),
49383
+ judgeLabel(g.judge),
49384
+ cadenceLabel(g.cadence),
49385
+ verdictLabel(g.verdict),
49386
+ when(g.updatedAt)
49387
+ ])));
49388
+ }
49389
+ __name(printGoals, "printGoals");
49390
+ function printViewSchedules(sched) {
49391
+ if (sched.error) {
49392
+ console.log(` Schedules: unavailable (${errorLabel(sched.error)})`);
49393
+ return;
49394
+ }
49395
+ const owned = sched.items.filter((r) => r.goalId).length;
49396
+ console.log(` Schedules: ${sched.items.length ? `${sched.items.length}${owned ? ` (${owned} goal-owned)` : ""}` : "none"}`);
49397
+ if (sched.items.length) {
49398
+ console.log("");
49399
+ printSchedules(sched.items);
49400
+ }
49401
+ }
49402
+ __name(printViewSchedules, "printViewSchedules");
49403
+ function printViewGoals(goals) {
49404
+ if (goals.error) {
49405
+ console.log(` Goals: unavailable (${errorLabel(goals.error)})`);
49406
+ return;
49407
+ }
49408
+ const counts = /* @__PURE__ */ new Map();
49409
+ for (const g of goals.items) counts.set(g.status, (counts.get(g.status) ?? 0) + 1);
49410
+ const summary = goals.items.length ? `${goals.items.length} (${[
49411
+ ...counts
49412
+ ].map(([s, n2]) => `${n2} ${s}`).join(" \xB7 ")})` : "none";
49413
+ console.log(` Goals: ${summary}`);
49414
+ if (goals.items.length) {
49415
+ console.log("");
49416
+ printGoals(goals.items);
49417
+ }
49418
+ }
49419
+ __name(printViewGoals, "printViewGoals");
49420
+ function subVerb(group, noun, raw, verbs) {
49421
+ if (!raw) {
49422
+ console.error(`\u274C ${noun}: usage \u2014 lua workflows ${noun} <${verbs.join("|")}> \u2026`);
49423
+ return void 0;
49424
+ }
49425
+ try {
49426
+ return validateOrSuggest(group, raw);
49427
+ } catch (e) {
49428
+ console.error(`\u274C ${e.message}`);
49429
+ return void 0;
49430
+ }
49431
+ }
49432
+ __name(subVerb, "subVerb");
49433
+ function goalFailure(ctx, res, verb, ref) {
49434
+ const err = res.error;
49435
+ const code = err?.code ?? err?.error;
49436
+ const status = err?.statusCode;
49437
+ if (!ctx.json && err) {
49438
+ if (status === 409 && code === "GOAL_NOT_ACTIVE") {
49439
+ const now = err.status ?? "not active";
49440
+ const rule = verb === "goals resume" ? "only a paused goal resumes" : verb === "goals pause" ? "only an active goal pauses" : "a done/closed goal stays closed";
49441
+ console.error(`\u274C goal ${ref ?? ""} is ${now} \u2014 ${rule}`);
49442
+ } else if (status === 409 && code === "GOAL_CAP") {
49443
+ console.error(`\u274C goal cap reached (${err.cap ?? "?"} per agent) \u2014 close or finish one first: lua workflows goals list --status active`);
49444
+ } else if (status === 400 && code === "VALIDATION_FAILED") {
49445
+ const issues = err.issues ?? [];
49446
+ for (const i of issues) console.error(` ${i.code}${i.path ? ` at ${i.path}` : ""}${i.message ? ` \u2014 ${i.message}` : ""}`);
49447
+ } else if (status === 400 && code === "GOAL_MAX_RUNS_INVALID") {
49448
+ console.error(`\u274C --max-runs must be 1..${GOAL_MAX_RUNS_CAP}`);
49449
+ } else if (status === 400 && code === "WORKFLOW_NOT_ON_AGENT") {
49450
+ console.error(`\u274C that workflow belongs to another agent \u2014 goals are scoped to ${ctx.agentId}`);
49451
+ } else if (status === 409 && code === "GOAL_SCHEDULE") {
49452
+ const goalId = err.goalId ?? "?";
49453
+ console.error(`\u274C GOAL_SCHEDULE: ${goalScheduleRefusal(ref ?? "<jobId>", goalId)}`);
49454
+ }
49455
+ }
49456
+ return apiFailure(ctx, res, verb);
49457
+ }
49458
+ __name(goalFailure, "goalFailure");
49459
+ async function goalsCore(ctx, target, extra, o) {
49460
+ const verb = subVerb("workflows.goals.action", "goals", target, GOAL_VERBS);
49461
+ if (!verb) return WORKFLOW_EXIT.USAGE;
49462
+ if (verb === "list") return goalsListCore(ctx, o, extra);
49463
+ if (verb === "create") return goalsCreateCore(ctx, o, extra);
49464
+ if (!extra) {
49465
+ console.error(`\u274C goals ${verb}: a goal id is required \u2014 lua workflows goals ${verb} <goalId>`);
49466
+ return WORKFLOW_EXIT.USAGE;
49467
+ }
49468
+ if (verb === "get") return goalsGetCore(ctx, extra);
49469
+ return goalsControlCore(ctx, verb, extra, o);
49470
+ }
49471
+ __name(goalsCore, "goalsCore");
49472
+ async function goalsListCore(ctx, o, positional) {
49473
+ if (o.status && !GOAL_STATUSES.includes(o.status)) {
49474
+ console.error(`\u274C goals list: --status must be ${GOAL_STATUSES.join("|")} (got "${o.status}")`);
49475
+ return WORKFLOW_EXIT.USAGE;
49476
+ }
49477
+ let limit;
49478
+ try {
49479
+ limit = parseIntegerFlag(o.limit, "--limit", {
49480
+ min: 1,
49481
+ max: GOAL_PAGE_LIMIT
49482
+ });
49483
+ } catch (e) {
49484
+ console.error(`\u274C ${e.message}`);
49485
+ return WORKFLOW_EXIT.USAGE;
49486
+ }
49487
+ const list = await loadWorkflowList(ctx);
49488
+ if (!list) return WORKFLOW_EXIT.API;
49489
+ const names = new Map(list.map((w) => [
49490
+ w.id,
49491
+ w.name
49492
+ ]));
49493
+ const target = positional ?? o.workflowName ?? o.workflow;
49494
+ let workflowId;
49495
+ if (target) {
49496
+ const wf = pickWorkflow(list, target);
49497
+ if (!wf) return WORKFLOW_EXIT.NOT_FOUND;
49498
+ workflowId = wf.id;
49499
+ }
49500
+ const res = await ctx.api.getGoals({
49501
+ status: o.status,
49502
+ workflowId,
49503
+ cursor: o.cursor,
49504
+ limit
49505
+ });
49506
+ if (!res.success || !res.data) return goalFailure(ctx, res, "goals list");
49507
+ emitJson(ctx, res);
49508
+ if (ctx.json) return WORKFLOW_EXIT.OK;
49509
+ console.log(`
49510
+ \u{1F3AF} Goals${target ? ` \u2014 ${names.get(workflowId) ?? target}` : ""}${o.status ? ` \xB7 ${o.status}` : ""}
49511
+ `);
49512
+ printGoals(res.data.items ?? [], names);
49513
+ if (res.data.nextCursor) {
49514
+ const flags = `${target ? ` -i ${target}` : ""}${o.status ? ` --status ${o.status}` : ""}${limit !== void 0 ? ` --limit ${limit}` : ""}`;
49515
+ console.log(`
49516
+ \u2026 more: lua workflows goals list${flags} --cursor ${res.data.nextCursor}`);
49517
+ }
49518
+ return WORKFLOW_EXIT.OK;
49519
+ }
49520
+ __name(goalsListCore, "goalsListCore");
49521
+ async function goalsGetCore(ctx, goalId) {
49522
+ const res = await ctx.api.getGoal(goalId);
49523
+ if (!res.success || !res.data) return goalFailure(ctx, res, "goals get", goalId);
49524
+ emitJson(ctx, res);
49525
+ if (!ctx.json) printGoalDetail(res.data);
49526
+ return WORKFLOW_EXIT.OK;
49527
+ }
49528
+ __name(goalsGetCore, "goalsGetCore");
49529
+ function printGoalDetail(g) {
49530
+ console.log(`
49531
+ \u{1F3AF} Goal ${g.goalId} \xB7 ${goalStatusLabel(g)}`);
49532
+ console.log(` Workflow: ${g.workflowId}${g.workflowVersionId ? ` @ ${g.workflowVersionId}` : ""}`);
49533
+ console.log(` Objective: ${g.objective}`);
49534
+ console.log(` Judge: ${judgeLabel(g.judge)}`);
49535
+ console.log(` Cadence: ${cadenceLabel(g.cadence)} \xB7 mode ${g.evaluation?.mode ?? "immediate"}${g.evaluation?.delaySeconds ? ` \xB7 delay ${g.evaluation.delaySeconds}s` : ""}`);
49536
+ console.log(` Runs: ${g.runsUsed}/${g.maxRuns} used \xB7 iteration ${g.iteration ?? 0}${g.consecutiveContinues ? ` \xB7 ${g.consecutiveContinues} consecutive continue(s)` : ""}${g.currentRunId ? ` \xB7 current ${g.currentRunId}` : ""}`);
49537
+ if (g.budget?.maxCredits || g.maxTotalCredits) console.log(` Budget: ${g.budget?.maxCredits ? `per-run ${g.budget.maxCredits} credits` : ""}${g.budget?.maxCredits && g.maxTotalCredits ? " \xB7 " : ""}${g.maxTotalCredits ? `lineage cap ${g.maxTotalCredits} credits` : ""}`);
49538
+ console.log(` Schedule: ${g.jobId ? `${g.jobId} (goal-owned \u2014 stop it with goals pause|close, never schedules delete)` : "none (immediate)"}`);
49539
+ if (g.verdict) console.log(` Verdict: ${verdictLabel(g.verdict)}${g.verdict.runId ? ` (run ${g.verdict.runId}${g.verdict.at ? ` at ${when(g.verdict.at)}` : ""})` : ""}`);
49540
+ if (g.input !== void 0) console.log(` Input: ${JSON.stringify(g.input).slice(0, 500)}`);
49541
+ console.log(` Created: ${when(g.createdAt)} \xB7 updated ${when(g.updatedAt)}${g.doneAt ? ` \xB7 done ${when(g.doneAt)}` : ""}${g.closedBy ? ` \xB7 closed by ${g.closedBy}` : ""}`);
49542
+ console.log("\n\u{1F4DC} Iterations");
49543
+ const runs = g.runs ?? [];
49544
+ if (!runs.length) console.log(" (no iteration runs yet)");
49545
+ else console.log(renderTable([
49546
+ "Run",
49547
+ "Iteration",
49548
+ "Status",
49549
+ "Verdict",
49550
+ "Credits",
49551
+ "Created"
49552
+ ], runs.map((r) => [
49553
+ `${r.runId}${r.purged ? " (purged)" : ""}`,
49554
+ String(r.iteration ?? ""),
49555
+ r.status,
49556
+ verdictLabel(r.verdict),
49557
+ String(r.creditsUsed ?? 0),
49558
+ when(r.createdAt)
49559
+ ])));
49560
+ }
49561
+ __name(printGoalDetail, "printGoalDetail");
49562
+ async function goalsControlCore(ctx, verb, goalId, o) {
49563
+ if (o.note && verb !== "close" && !ctx.json) writeInfo(`\u2139\uFE0F --note is recorded on close only; ${verb} takes no note`);
49564
+ const res = verb === "pause" ? await ctx.api.pauseGoal(goalId) : verb === "resume" ? await ctx.api.resumeGoal(goalId) : await ctx.api.closeGoal(goalId, o.note ? {
49565
+ note: o.note
49566
+ } : {});
49567
+ if (!res.success || !res.data) return goalFailure(ctx, res, `goals ${verb}`, goalId);
49568
+ emitJson(ctx, res);
49569
+ if (!ctx.json) {
49570
+ const g = res.data;
49571
+ const past = verb === "pause" ? "paused" : verb === "resume" ? "resumed" : "closed";
49572
+ writeSuccess(`\u2705 goal ${g.goalId} ${past} \xB7 ${goalStatusLabel(g)} \xB7 runs ${g.runsUsed}/${g.maxRuns}${g.jobId ? ` \xB7 schedule ${g.jobId}` : ""}`);
49573
+ if (verb === "pause") writeInfo(` the cadence stays with the goal \u2014 resume with: lua workflows goals resume ${g.goalId}`);
49574
+ if (verb === "resume") writeInfo(" no backfill: missed cadence fires are not replayed");
49575
+ }
49576
+ return WORKFLOW_EXIT.OK;
49577
+ }
49578
+ __name(goalsControlCore, "goalsControlCore");
49579
+ function parseJudgePredicateFlag(raw) {
49580
+ const opList = GOAL_PREDICATE_OPS.join("|");
49581
+ if (raw.startsWith("{") || raw.startsWith("@")) {
49582
+ const obj = parseJsonOrFile(raw, "--judge-predicate");
49583
+ if (!obj || typeof obj !== "object" || typeof obj.path !== "string" || !obj.path || !GOAL_PREDICATE_OPS.includes(String(obj.op))) throw new WorkflowLocalUsageError("usage", `--judge-predicate: expected { path, op: ${opList}, value? }`);
49584
+ return {
49585
+ path: obj.path,
49586
+ op: obj.op,
49587
+ ...obj.value !== void 0 ? {
49588
+ value: obj.value
49589
+ } : {}
49590
+ };
49591
+ }
49592
+ const [path23, op, ...rest] = raw.trim().split(/\s+/);
49593
+ if (!path23 || !op || !GOAL_PREDICATE_OPS.includes(op)) throw new WorkflowLocalUsageError("usage", `--judge-predicate: expected "<path> <op> [value]" with op ${opList} (got "${raw}")`);
49594
+ if (rest.length === 0) return {
49595
+ path: path23,
49596
+ op
49597
+ };
49598
+ const text = rest.join(" ");
49599
+ let value3 = text;
49600
+ try {
49601
+ value3 = JSON.parse(text);
49602
+ } catch {
49603
+ }
49604
+ return {
49605
+ path: path23,
49606
+ op,
49607
+ value: value3
49608
+ };
49609
+ }
49610
+ __name(parseJudgePredicateFlag, "parseJudgePredicateFlag");
49611
+ function parseCadenceFlag(entries, timezone) {
49612
+ const out = [];
49613
+ let bareCron = 0;
49614
+ for (const raw of entries ?? []) {
49615
+ const text = raw.trim();
49616
+ if (!text) continue;
49617
+ if (text.startsWith("{") || text.startsWith("[") || text.startsWith("@")) {
49618
+ const parsed = parseJsonOrFile(text, "--cadence");
49619
+ for (const c of Array.isArray(parsed) ? parsed : [
49620
+ parsed
49621
+ ]) {
49622
+ const slot = c;
49623
+ if (!slot || typeof slot !== "object" || ![
49624
+ "cron",
49625
+ "once",
49626
+ "interval"
49627
+ ].includes(String(slot.type))) throw new WorkflowLocalUsageError("usage", "--cadence: each JSON entry needs type cron|once|interval");
49628
+ out.push(slot);
49629
+ }
49630
+ continue;
49631
+ }
49632
+ if (!/^\S+(\s+\S+){4,5}$/.test(text)) throw new WorkflowLocalUsageError("usage", `--cadence: "${text}" is not a cron expression (5\u20136 fields); pass JSON for once/interval entries`);
49633
+ bareCron += 1;
49634
+ out.push({
49635
+ type: "cron",
49636
+ expression: text,
49637
+ ...timezone ? {
49638
+ timezone
49639
+ } : {}
49640
+ });
49641
+ }
49642
+ if (timezone && bareCron === 0) throw new WorkflowLocalUsageError("usage", '--timezone applies to bare cron cadences only \u2014 put "timezone" inside JSON entries, or pass a cron expression');
49643
+ if (out.length > GOAL_CADENCE_MAX) throw new WorkflowLocalUsageError("usage", `--cadence: at most ${GOAL_CADENCE_MAX} entries (goal-cadence-invalid)`);
49644
+ return out;
49645
+ }
49646
+ __name(parseCadenceFlag, "parseCadenceFlag");
49647
+ async function goalsCreateCore(ctx, o, positional) {
49648
+ const target = positional ?? o.workflowName ?? o.workflow;
49649
+ if (!target) {
49650
+ console.error("\u274C goals create: a workflow is required \u2014 lua workflows goals create <workflow> (or -i <workflow>)");
49651
+ return WORKFLOW_EXIT.USAGE;
49652
+ }
49653
+ if (!o.objective?.trim()) {
49654
+ console.error("\u274C goals create: --objective <text> is required");
49655
+ return WORKFLOW_EXIT.USAGE;
49656
+ }
49657
+ if (o.judgeAgent === "") {
49658
+ console.error("\u274C goals create: --judge-agent is empty \u2014 an unquoted $self is expanded by the shell to nothing; write '$self' (quoted) or self");
49659
+ return WORKFLOW_EXIT.USAGE;
49660
+ }
49661
+ if (!o.judgePredicate && !o.judgeAgent) {
49662
+ console.error("\u274C goals create: one of --judge-predicate <spec> | --judge-agent <agentId|'$self'> is required");
49663
+ return WORKFLOW_EXIT.USAGE;
49664
+ }
49665
+ const usage = /* @__PURE__ */ __name((m) => new WorkflowLocalUsageError("usage", m), "usage");
49666
+ let dto;
49667
+ try {
49668
+ const maxRuns = parseIntegerFlag(o.maxRuns, "--max-runs", {
49669
+ min: 1,
49670
+ max: GOAL_MAX_RUNS_CAP
49671
+ });
49672
+ if (maxRuns === void 0) throw usage(`--max-runs <n> is required (an integer 1..${GOAL_MAX_RUNS_CAP})`);
49673
+ const judge = {
49674
+ agentId: o.judgeAgent === "self" ? "$self" : o.judgeAgent ?? "$self"
49675
+ };
49676
+ if (o.judgePredicate) {
49677
+ if (o.schema || o.judgeRole) throw usage("--judge-predicate is the deterministic judge \u2014 --schema / --judge-role belong to --judge-agent");
49678
+ judge.predicate = parseJudgePredicateFlag(o.judgePredicate);
49679
+ } else {
49680
+ if (o.judgeRole) judge.role = parseJsonOrFile(o.judgeRole, "--judge-role");
49681
+ if (judge.agentId === "$self" && !judge.role) throw usage("--judge-role <json|@file> ({ name, instructions, tools }) is required for a '$self' judge");
49682
+ if (judge.agentId !== "$self" && judge.role) throw usage("--judge-role is only for a $self judge");
49683
+ if (!o.schema) throw usage("--schema <json|@file> is required for an agent judge (a JsonSchema with a boolean `done` at the root)");
49684
+ const schema = parseJsonOrFile(o.schema, "--schema");
49685
+ const done = schema?.properties?.done;
49686
+ if (!schema || typeof schema !== "object" || done?.type !== "boolean") throw usage('--schema: must declare a boolean `done` at the root (properties.done.type === "boolean")');
49687
+ judge.schema = schema;
49688
+ }
49689
+ const cadence = parseCadenceFlag(o.cadence, o.timezone);
49690
+ const input = o.input ? parseJsonOrFile(o.input, "--input") : void 0;
49691
+ const budgetCredits = parseIntegerFlag(o.budgetCredits, "--budget-credits", {
49692
+ min: 1
49693
+ });
49694
+ const maxTotalCredits = parseIntegerFlag(o.maxTotalCredits, "--max-total-credits", {
49695
+ min: 1
49696
+ });
49697
+ dto = {
49698
+ workflowId: "",
49699
+ objective: o.objective.trim(),
49700
+ judge,
49701
+ cadence,
49702
+ evaluation: {
49703
+ mode: cadence.length ? "cadence" : "immediate"
49704
+ },
49705
+ maxRuns,
49706
+ ...budgetCredits !== void 0 ? {
49707
+ budget: {
49708
+ maxCredits: budgetCredits
49709
+ }
49710
+ } : {},
49711
+ ...maxTotalCredits !== void 0 ? {
49712
+ maxTotalCredits
49713
+ } : {},
49714
+ ...input !== void 0 ? {
49715
+ input
49716
+ } : {},
49717
+ ...o.idempotencyKey ? {
49718
+ idempotencyKey: o.idempotencyKey
49719
+ } : {}
49720
+ };
49721
+ } catch (e) {
49722
+ if (e instanceof WorkflowLocalUsageError) {
49723
+ console.error(`\u274C ${e.message}`);
49724
+ return WORKFLOW_EXIT.USAGE;
49725
+ }
49726
+ throw e;
49727
+ }
49728
+ const wf = await resolveWorkflow(ctx, target);
49729
+ if (!wf) return WORKFLOW_EXIT.NOT_FOUND;
49730
+ dto.workflowId = wf.id;
49731
+ if (o.workflowVersion) {
49732
+ const ref = await resolveVersionRef(ctx, wf, o.workflowVersion, "goals create");
49733
+ if ("exit" in ref) return ref.exit;
49734
+ dto.workflowVersionId = ref.version.id;
49735
+ }
49736
+ const res = await ctx.api.createGoal(dto);
49737
+ if (!res.success || !res.data) return goalFailure(ctx, res, "goals create");
49738
+ emitJson(ctx, res);
49739
+ if (!ctx.json) {
49740
+ const g = res.data;
49741
+ writeSuccess(`\u2705 goal ${g.goalId} created on "${wf.name}" \xB7 ${goalStatusLabel(g)} \xB7 runs ${g.runsUsed}/${g.maxRuns} \xB7 ${cadenceLabel(g.cadence)}${g.jobId ? ` \xB7 schedule ${g.jobId}` : ""}`);
49742
+ writeHintBlock({
49743
+ headline: "Follow the goal:",
49744
+ lines: [
49745
+ {
49746
+ label: "Get:",
49747
+ command: `lua workflows goals get ${g.goalId}`
49748
+ },
49749
+ {
49750
+ label: "Pause:",
49751
+ command: `lua workflows goals pause ${g.goalId}`
49752
+ }
49753
+ ],
49754
+ when: "success"
49755
+ });
49756
+ }
49757
+ return WORKFLOW_EXIT.OK;
49758
+ }
49759
+ __name(goalsCreateCore, "goalsCreateCore");
49760
+ async function schedulesCore(ctx, target, extra, o) {
49761
+ const verb = subVerb("workflows.schedules.action", "schedules", target, SCHEDULE_VERBS);
49762
+ if (!verb) return WORKFLOW_EXIT.USAGE;
49763
+ if (verb === "list") return schedulesListCore(ctx, o);
49764
+ if (!extra) {
49765
+ console.error("\u274C schedules delete: a schedule (job) id is required \u2014 lua workflows schedules delete <jobId>");
49766
+ return WORKFLOW_EXIT.USAGE;
49767
+ }
49768
+ return schedulesDeleteCore(ctx, extra, o);
49769
+ }
49770
+ __name(schedulesCore, "schedulesCore");
49771
+ async function schedulesListCore(ctx, o) {
49772
+ const list = await loadWorkflowList(ctx);
49773
+ if (!list) return WORKFLOW_EXIT.API;
49774
+ const names = new Map(list.map((w) => [
49775
+ w.id,
49776
+ w.name
49777
+ ]));
49778
+ const target = o.workflowName ?? o.workflow;
49779
+ let wf;
49780
+ if (target) {
49781
+ wf = pickWorkflow(list, target);
49782
+ if (!wf) return WORKFLOW_EXIT.NOT_FOUND;
49783
+ }
49784
+ const goals = await loadGoals(ctx, wf?.id);
49785
+ const sched = await loadSchedules(ctx, wf?.id, goals.items);
49786
+ if (sched.error) return apiFailure(ctx, {
49787
+ success: false,
49788
+ error: sched.error
49789
+ }, "schedules list");
49790
+ if (ctx.json) {
49791
+ console.log(JSON.stringify({
49792
+ success: true,
49793
+ data: {
49794
+ items: sched.items,
49795
+ ...goals.error ? {
49796
+ goalsError: goals.error
49797
+ } : {}
49798
+ }
49799
+ }, null, 2));
49800
+ return WORKFLOW_EXIT.OK;
49801
+ }
49802
+ console.log(`
49803
+ \u{1F4C5} Schedules${wf ? ` \u2014 ${wf.name}` : ""}
49804
+ `);
49805
+ if (goals.error) console.error(`\u26A0\uFE0F goals unavailable (${errorLabel(goals.error)}) \u2014 goal-owned rows may be untagged`);
49806
+ printSchedules(sched.items, names);
49807
+ return WORKFLOW_EXIT.OK;
49808
+ }
49809
+ __name(schedulesListCore, "schedulesListCore");
49810
+ async function schedulesDeleteCore(ctx, jobId, o) {
49811
+ const rowRes = await ctx.api.getSchedule(jobId);
49812
+ if (!rowRes.success || !rowRes.data) return apiFailure(ctx, rowRes, "schedules delete");
49813
+ const row2 = rowRes.data;
49814
+ let goalId = row2.goalId;
49815
+ if (!goalId) {
49816
+ const goals = await loadGoals(ctx, row2.workflowId);
49817
+ if (goals.error) {
49818
+ if (!ctx.json) console.error(`\u274C cannot prove ${jobId} is not a goal's cadence (goals unavailable) \u2014 refusing to delete`);
49819
+ return apiFailure(ctx, {
49820
+ success: false,
49821
+ error: goals.error
49822
+ }, "schedules delete");
49823
+ }
49824
+ goalId = goals.items.find((g) => g.jobId === jobId)?.goalId;
49825
+ }
49826
+ if (goalId) {
49827
+ const message = goalScheduleRefusal(jobId, goalId);
49828
+ if (ctx.json) console.log(JSON.stringify({
49829
+ success: false,
49830
+ error: {
49831
+ code: "goal_schedule",
49832
+ statusCode: 409,
49833
+ message,
49834
+ jobId,
49835
+ goalId
49836
+ }
49837
+ }, null, 2));
49838
+ else console.error(`\u274C goal_schedule: ${message}`);
49839
+ return WORKFLOW_EXIT.API;
49840
+ }
49841
+ if (!o.yes) {
49842
+ const answer = await safePrompt([
49843
+ {
49844
+ type: "confirm",
49845
+ name: "confirm",
49846
+ message: `Delete schedule ${jobId} (${(row2.trigger ?? []).map(triggerLabel).join(" \xB7 ") || "no trigger"}) of workflow ${row2.workflowId}?`,
49847
+ default: false
49848
+ }
49849
+ ]);
49850
+ if (!answer?.confirm) {
49851
+ console.log("Cancelled.");
49852
+ return WORKFLOW_EXIT.OK;
49853
+ }
49854
+ }
49855
+ const res = await ctx.api.deleteSchedule(jobId);
49856
+ if (!res.success) return goalFailure(ctx, res, "schedules delete", jobId);
49857
+ emitJson(ctx, res);
49858
+ if (!ctx.json) writeSuccess(`\u2705 schedule ${jobId} deleted`);
49859
+ return WORKFLOW_EXIT.OK;
49860
+ }
49861
+ __name(schedulesDeleteCore, "schedulesDeleteCore");
48178
49862
 
48179
49863
  // src/commands/features.ts
48180
49864
  init_cli();
@@ -56841,7 +58525,7 @@ function getProjectToolNames() {
56841
58525
  if (!hasCompilationOutput(process.cwd())) return [];
56842
58526
  try {
56843
58527
  const manifest = loadManifest();
56844
- return getPrimitivesByKind(manifest, PrimitiveKind.TOOL).map((t) => t.name).filter((n) => typeof n === "string" && n.length > 0).sort();
58528
+ return getPrimitivesByKind(manifest, PrimitiveKind.TOOL).map((t) => t.name).filter((n2) => typeof n2 === "string" && n2.length > 0).sort();
56845
58529
  } catch {
56846
58530
  return [];
56847
58531
  }
@@ -58345,11 +60029,11 @@ init_constants();
58345
60029
  function parseVersion2(arg) {
58346
60030
  if (!arg) throw new Error(`Invalid version "${arg}": must be a positive integer like "3" or "v3".`);
58347
60031
  const stripped = arg.replace(/^v/i, "");
58348
- const n = Number.parseInt(stripped, 10);
58349
- if (!Number.isFinite(n) || n <= 0 || String(n) !== stripped) {
60032
+ const n2 = Number.parseInt(stripped, 10);
60033
+ if (!Number.isFinite(n2) || n2 <= 0 || String(n2) !== stripped) {
58350
60034
  throw new Error(`Invalid version "${arg}": must be a positive integer like "3" or "v3".`);
58351
60035
  }
58352
- return n;
60036
+ return n2;
58353
60037
  }
58354
60038
  __name(parseVersion2, "parseVersion");
58355
60039
 
@@ -59655,13 +61339,15 @@ Examples:
59655
61339
  $ lua jobs versions -i myJob View job versions
59656
61340
  $ lua jobs history -i myJob View execution history
59657
61341
  `).action(jobsCommand);
59658
- program2.command("workflows [action] [target] [extra]").description("\u{1F9ED} Manage workflows and their runs (list, start, watch, cancel, resume, approve, signal, replay)").option("-i, --workflow-name <name>", "Workflow name (or id)").option("-r, --run-id <id>", "Run id").option("-v, --workflow-version <ver>", "Version for deploy/start (or 'latest')").option("--json", "Print the raw {success,data} envelope").option("--all", "list: include dynamic workflows").option("--input <json|@file>", "start/run: run input").option("--idempotency-key <key>", "start: idempotency key").option("--correlation-key <key>", "start/runs: correlation key").option("--tag <tag>", "start/runs: tag (repeatable, \u2264 10)", collect, []).option("--budget-credits <n>", "start: run budget (credits)").option("--wait <s>", "start: server long-poll \u2264 55 s").option("--follow", "start/logs: attach watch").option("--status <status>", "runs: filter by status").option("--workflow <name>", "runs: filter by workflow").option("--limit <n>", "runs: page size").option("--cursor <c>", "runs: page cursor").option("--sort <field>", "runs: -createdAt|createdAt|-durationMs|durationMs").option("--steps", "status: per-step table").option("--strict", "status: exit 4/5 by terminal status").option("--after <seq>", "watch: replay from seq").option("--timeout <s>", "watch: give up after s seconds (exit 7)").option("--events", "watch: print raw frames").option("--reason <text>", "cancel: reason").option("--step <id>", "resume/logs: step id").option("--data <json|@file>", "resume: resumeData").option("--approval <id>", "approve: approval id").option("--decision <approve|deny>", "approve: decision (default approve)").option("--note <text>", "approve: note").option("--edit <json|@file>", "approve: edited payload (small inline edits)").option("--fingerprint <f>", "approve: payloadFingerprint (mandatory with --edit)").option("--payload <json|@file>", "signal: payload").option("--dedupe-key <key>", "signal: dedupe key").option("--local", "replay: replay locally against the compiled artifact").option("--since <dur>", "logs: window").option("--yes", "delete/delete-run: skip confirmation").option("--force", "delete: cancel in-flight runs first \xB7 run: seed from a changed graph").option("--step-output <id=json>", "run: complete a step with this output (repeatable)", collect, []).option("--approve <id[=@payload]>", "run: pre-answer an approval (repeatable)", collect, []).option("--deny <id[=@reason]>", "run: pre-deny an approval (repeatable)", collect, []).option("--signal <name=json>", "run: pre-supply a waitForSignal payload (repeatable)", collect, []).option("--from-run <runId>", "run: seed completed steps from a real run").option("--record <dir>", "run: record agent/tool outputs as fixtures").option("--fixtures <dir>", "run: replay recorded fixtures").option("--step-wall <s>", "run: per-step wall in seconds (default 600)").option("--job-wall <s>", "run: virtual wall for tier:'job' steps \u2014 splits at 14400 s segments, > 86400 is exit 2").option("--ledger-out <file>", "run: write the in-memory ledger as JSON").option("--agents <fake|live>", "run: fake agent steps (default) or call the dev API").option("--now <iso>", "run: virtual clock start").option("--park <id>", "run: simulate a platform-fault park of this step (repeatable)", collect, []).option("--fast-retries", "run: collapse retry backoff waits to 0").option("--real-time", "run: actually wait on sleeps/backoffs instead of fast-forwarding").option("--artefacts-dir <dir>", "run: back ctx.artefacts.* on disk").option("--env <KEY=value>", "run: local env.template() overlay (repeatable; missing key \u21D2 exit 2)", collect, []).option("--max-ticks <n>", "run (script form): tick cap before SCRIPT_TICK_LIMIT (default 64)").option("--until <iso>", "archive-runs: window end").option("--out <dir>", "archive-runs: destination dir (archive-index.ndjson + <runId>.zip)").option("--concurrency <n>", "archive-runs: parallel exports (default 2, max 5)").option("--no-inputs", "archive-runs: exclude run inputs from the bundle").option("--no-artefacts", "archive-runs: exclude artefacts from the bundle").option("--retention-days <n>", "archive-runs: org run retention (default 90) \u2014 --since must fit retention \u2212 7 d").option("--connection <id>", "archive-runs: org storage connection for s3:// / gs:// sinks").option("--release", "workspace: release the run workspace (R42)").option("--attempt <n>", "job-logs: attempt (default latest)").option("--tail <n>", "job-logs: log lines (1..2000, default 200)").addHelpText("after", `
61342
+ program2.command("workflows [action] [target] [extra]").description("\u{1F9ED} Manage workflows and their runs (list, start, watch, cancel, resume, approve, signal, replay)").option("-i, --workflow-name <name>", "Workflow name (or id)").option("-r, --run-id <id>", "Run id").option("-v, --workflow-version <ver>", "deploy/start/goals create: a semver, 'latest' (newest push) or a version id").option("--json", "Print the raw {success,data} envelope").option("--all", "list: include dynamic workflows").option("--input <json|@file>", "start/run/goals create: run input").option("--idempotency-key <key>", "start: idempotency key").option("--correlation-key <key>", "start/runs: correlation key").option("--tag <tag>", "start/runs: tag (repeatable, \u2264 10)", collect, []).option("--budget-credits <n>", "start: run budget (credits)").option("--wait <s>", "start: server long-poll \u2264 55 s").option("--follow", "start/logs: attach watch").option("--status <status>", "runs/goals list: filter by status").option("--workflow <name>", "runs: filter by workflow").option("--limit <n>", "runs/goals list: page size").option("--cursor <c>", "runs/goals list: page cursor").option("--sort <field>", "runs: -createdAt|createdAt|-durationMs|durationMs").option("--steps", "status: per-step table").option("--strict", "status: exit 4/5 by terminal status").option("--after <seq>", "watch: replay from seq").option("--timeout <s>", "watch: give up after s seconds (exit 7)").option("--events", "watch: print raw frames").option("--reason <text>", "cancel: reason").option("--step <id>", "resume/logs: step id").option("--data <json|@file>", "resume: resumeData").option("--approval <id>", "approve: approval id").option("--decision <approve|deny>", "approve: decision (default approve)").option("--note <text>", "approve/goals close: note").option("--edit <json|@file>", "approve: edited payload (small inline edits)").option("--fingerprint <f>", "approve: payloadFingerprint (mandatory with --edit)").option("--payload <json|@file>", "signal: payload").option("--dedupe-key <key>", "signal: dedupe key").option("--local", "replay: replay locally against the compiled artifact").option("--since <dur>", "logs: window").option("--yes", "delete/delete-run/schedules delete: skip confirmation").option("--force", "delete: cancel in-flight runs first \xB7 run: seed from a changed graph").option("--step-output <id=json>", "run: complete a step with this output (repeatable)", collect, []).option("--approve <id[=@payload]>", "run: pre-answer an approval (repeatable)", collect, []).option("--deny <id[=@reason]>", "run: pre-deny an approval (repeatable)", collect, []).option("--signal <name=json>", "run: pre-supply a waitForSignal payload (repeatable)", collect, []).option("--from-run <runId>", "run: seed completed steps from a real run").option("--record <dir>", "run: record agent/tool outputs as fixtures").option("--fixtures <dir>", "run: replay recorded fixtures").option("--step-wall <s>", "run: per-step wall in seconds (default 600)").option("--job-wall <s>", "run: virtual wall for tier:'job' steps \u2014 splits at 14400 s segments, > 86400 is exit 2").option("--ledger-out <file>", "run: write the in-memory ledger as JSON").option("--agents <fake|live>", "run: fake agent steps (default) or call the dev API").option("--now <iso>", "run: virtual clock start").option("--park <id>", "run: simulate a platform-fault park of this step (repeatable)", collect, []).option("--fast-retries", "run: collapse retry backoff waits to 0").option("--real-time", "run: actually wait on sleeps/backoffs instead of fast-forwarding").option("--artefacts-dir <dir>", "run: back ctx.artefacts.* on disk").option("--env <KEY=value>", "run: local env.template() overlay (repeatable; missing key \u21D2 exit 2)", collect, []).option("--max-ticks <n>", "run (script form): tick cap before SCRIPT_TICK_LIMIT (default 64)").option("--until <iso>", "archive-runs: window end").option("--out <dir>", "archive-runs: destination dir (archive-index.ndjson + <runId>.zip)").option("--concurrency <n>", "archive-runs: parallel exports (default 2, max 5)").option("--no-inputs", "archive-runs: exclude run inputs from the bundle").option("--no-artefacts", "archive-runs: exclude artefacts from the bundle").option("--retention-days <n>", "archive-runs: org run retention (default 90) \u2014 --since must fit retention \u2212 7 d").option("--connection <id>", "archive-runs: org storage connection for s3:// / gs:// sinks").option("--release", "workspace: release the run workspace (R42)").option("--attempt <n>", "job-logs: attempt (default latest)").option("--tail <n>", "job-logs: log lines (1..2000, default 200)").option("--objective <text>", "goals create: the objective (\u2264 2000 chars)").option("--judge-predicate <spec>", "goals create: deterministic judge \u2014 '<path> <op> [value]' (e.g. 'output.done truthy'), JSON or @file").option("--judge-agent <agentId|'$self'>", "goals create: judge agent \u2014 write '$self' quoted (or self); it needs --judge-role").option("--judge-role <json|@file>", "goals create: D25 role {name,instructions,tools} for a '$self' judge").option("--schema <json|@file>", "goals create: judge output JsonSchema (must declare a boolean `done`)").option("--cadence <cron|json|@file>", "goals create: cadence entry (repeatable, \u2264 5); none \u21D2 immediate", collect, []).option("--timezone <tz>", "goals create: IANA timezone for bare cron cadences").option("--max-runs <n>", "goals create: iteration cap (1..100)").option("--max-total-credits <n>", "goals create: lineage-wide credit gate").addHelpText("after", `
59659
61343
  Arguments:
59660
61344
  action list \xB7 view \xB7 versions \xB7 deploy \xB7 activate \xB7 deactivate \xB7 start \xB7 run \xB7 runs \xB7 status \xB7
59661
- watch \xB7 cancel \xB7 resume \xB7 approve \xB7 signal \xB7 replay \xB7 logs \xB7 delete \xB7 delete-run \xB7
59662
- env-overlay \xB7 archive-runs \xB7 workspace \xB7 jobs \xB7 job-logs
59663
- target workflow name (list/view/versions/deploy/activate/deactivate/start/run/delete) or run id (the rest)
59664
- extra signal name (signal <runId> <name>)
61345
+ watch \xB7 cancel \xB7 resume \xB7 retry-step \xB7 approve \xB7 signal \xB7 replay \xB7 logs \xB7 delete \xB7 delete-run \xB7
61346
+ env-overlay \xB7 archive-runs \xB7 workspace \xB7 jobs \xB7 job-logs \xB7
61347
+ goals <list|get|create|pause|resume|close> \xB7 schedules <list|delete>
61348
+ target workflow name (list/view/versions/deploy/activate/deactivate/start/run/delete) or run id (the rest);
61349
+ the sub-verb for goals / schedules
61350
+ extra signal name (signal <runId> <name>) \xB7 goal id (goals get/pause/resume/close) \xB7 job id (schedules delete)
59665
61351
 
59666
61352
  Exit codes: 0 ok \xB7 1 API failure \xB7 2 usage \xB7 3 not found \xB7 4 run failed \xB7 5 run cancelled \xB7
59667
61353
  6 run gated (consent) \xB7 7 --timeout reached \xB7 8 run parked (exception/budget gate)
@@ -59680,10 +61366,19 @@ Examples:
59680
61366
  $ lua workflows job-logs <runId> build --tail 500 --follow
59681
61367
  $ lua workflows cancel <runId> --reason "wrong input"
59682
61368
  $ lua workflows resume <runId> --step ask --data '{"answer":42}'
61369
+ $ lua workflows retry-step <runId> --step sendEmails --note "vendor back up"
59683
61370
  $ lua workflows approve <runId> --approval reviewDrafts --decision approve
59684
61371
  $ lua workflows signal <runId> review --payload '{"ok":true}'
59685
61372
  $ lua workflows replay <runId> --local
59686
61373
  $ lua workflows deploy outreach -v latest
61374
+ $ lua workflows view outreach --json | jq '.data.schedules, .data.goals'
61375
+ $ lua workflows goals list -i outreach --status active
61376
+ $ lua workflows goals get wfg_1234
61377
+ $ lua workflows goals create -i outreach --objective "Reach 50 signups" --judge-predicate 'output.signups gte 50' \\
61378
+ --cadence '0 9 * * 1' --timezone Europe/London --max-runs 12 --input '{"segment":"trial"}'
61379
+ $ lua workflows goals pause wfg_1234 \xB7 goals resume wfg_1234 \xB7 goals close wfg_1234 --note "shipped"
61380
+ $ lua workflows schedules list -i outreach
61381
+ $ lua workflows schedules delete <jobId> --yes (a goal's cadence is refused: goal_schedule)
59687
61382
  `).action(workflowsCommand);
59688
61383
  program2.command("features [action]").description("\u{1F3AF} Manage agent features (RAG, webSearch, inquiry, outboundChannels)").option("--feature-name <name>", "Feature name").option("--recipient-scope <scope>", "For configure: outboundChannels recipient scope (current_user | anyone)").addHelpText("after", `
59689
61384
  Arguments: