lua-cli 3.32.2 → 3.32.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -64,6 +64,261 @@ var init_auth_error = __esm({
64
64
  }
65
65
  });
66
66
 
67
+ // src/errors/cli.error.ts
68
+ function isAccessDeniedError(error) {
69
+ if (CliError.isCliError(error)) return error.statusCode === 403;
70
+ return error instanceof Error && error.message.startsWith("Access denied (403)");
71
+ }
72
+ function isHandledCliError(error) {
73
+ return error instanceof HandledCliError || typeof error === "object" && error !== null && error.handled === true && typeof error.exitCode === "number";
74
+ }
75
+ function setDebugMode(enabled) {
76
+ debugFlag = enabled;
77
+ }
78
+ function debugEnabled() {
79
+ const v = process.env.LUA_DEBUG;
80
+ return debugFlag || v === "1" || v === "true" || v === "yes";
81
+ }
82
+ function authHint(error) {
83
+ if (error.suppressDefaultRemediation) return void 0;
84
+ if (error.reason === "no_agent_access") {
85
+ return [
86
+ "Your API key is valid, but it does not have access to the agentId in lua.skill.yaml \u2014 the agent belongs",
87
+ "to another account or organization, was deleted or transferred, or the yaml was copied from another project.",
88
+ "Check the configured agent and switch if needed:",
89
+ " lua agents (list agents you have access to)",
90
+ " lua init (re-select the agent for this project)"
91
+ ].join("\n");
92
+ }
93
+ return "Re-authenticate or check your API key: lua auth configure \xB7 https://admin.heylua.ai";
94
+ }
95
+ function numericStatus(error) {
96
+ const candidate = error.statusCode ?? error.status;
97
+ return typeof candidate === "number" && Number.isFinite(candidate) ? candidate : void 0;
98
+ }
99
+ function classifyCliError(error) {
100
+ if (CliError.isCliError(error)) {
101
+ return {
102
+ code: error.code,
103
+ exitCode: error.exitCode,
104
+ message: error.message,
105
+ hint: error.hint
106
+ };
107
+ }
108
+ if (AuthenticationError.isAuthenticationError(error)) {
109
+ return {
110
+ code: "auth",
111
+ exitCode: CLI_EXIT.AUTH,
112
+ message: error.message,
113
+ hint: authHint(error)
114
+ };
115
+ }
116
+ const e = typeof error === "object" && error !== null ? error : {};
117
+ const message = typeof e.message === "string" && e.message.length > 0 ? e.message : error instanceof Error ? error.name : String(error ?? "Unknown error");
118
+ if (e.name === "WorkflowLocalUsageError" || typeof e.code === "string" && e.code.startsWith("commander.")) {
119
+ return {
120
+ code: "usage",
121
+ exitCode: CLI_EXIT.USAGE,
122
+ message
123
+ };
124
+ }
125
+ const status = numericStatus(e);
126
+ if (status !== void 0) {
127
+ if (status === 401) return {
128
+ code: "auth",
129
+ exitCode: CLI_EXIT.AUTH,
130
+ message
131
+ };
132
+ if (status === 403) return {
133
+ code: "forbidden",
134
+ exitCode: CLI_EXIT.FORBIDDEN,
135
+ message
136
+ };
137
+ if (status === 404) return {
138
+ code: "not_found",
139
+ exitCode: CLI_EXIT.NOT_FOUND,
140
+ message
141
+ };
142
+ if (status >= 400 && status < 500) return {
143
+ code: `http_${status}`,
144
+ exitCode: CLI_EXIT.FORBIDDEN,
145
+ message
146
+ };
147
+ if (status >= 500 || status === 0) return {
148
+ code: "unavailable",
149
+ exitCode: CLI_EXIT.UNAVAILABLE,
150
+ message
151
+ };
152
+ }
153
+ const causeCode = e.cause?.code;
154
+ if (typeof e.code === "string" && NETWORK_ERRNO.has(e.code) || typeof causeCode === "string" && NETWORK_ERRNO.has(causeCode) || e.name === "AbortError" || e.name === "TimeoutError" || NETWORK_MESSAGE.test(message)) {
155
+ return {
156
+ code: "unavailable",
157
+ exitCode: CLI_EXIT.UNAVAILABLE,
158
+ message,
159
+ hint: UNAVAILABLE_HINT
160
+ };
161
+ }
162
+ return {
163
+ code: "error",
164
+ exitCode: CLI_EXIT.ERROR,
165
+ message
166
+ };
167
+ }
168
+ function renderCliError(reported, options = {}) {
169
+ const lines = [
170
+ `\u2716 ${reported.code}: ${reported.message}`
171
+ ];
172
+ for (const hint of [
173
+ reported.hint,
174
+ options.extraHint
175
+ ]) {
176
+ if (!hint) continue;
177
+ hint.split("\n").forEach((line, i) => lines.push(i === 0 ? `\u{1F4A1} ${line}` : ` ${line}`));
178
+ }
179
+ if (options.stack) lines.push("", options.stack);
180
+ return lines;
181
+ }
182
+ function reportCliError(error, options = {}) {
183
+ const reported = classifyCliError(error);
184
+ const stack = debugEnabled() && error instanceof Error ? error.stack : void 0;
185
+ for (const line of renderCliError(reported, {
186
+ extraHint: options.extraHint,
187
+ stack
188
+ })) console.error(line);
189
+ return reported;
190
+ }
191
+ function commanderExitCode(error) {
192
+ const e = error;
193
+ const code = e?.code;
194
+ if (typeof code !== "string" || !code.startsWith("commander.")) return void 0;
195
+ if (code === "commander.helpDisplayed" || code === "commander.version") return CLI_EXIT.OK;
196
+ if (code === "commander.help") return e?.exitCode === 0 ? CLI_EXIT.OK : CLI_EXIT.USAGE;
197
+ return CLI_EXIT.USAGE;
198
+ }
199
+ function reportUnhandledCliError(error) {
200
+ if (isHandledCliError(error)) {
201
+ process.exitCode = error.exitCode;
202
+ return error.exitCode;
203
+ }
204
+ const commander = commanderExitCode(error);
205
+ if (commander !== void 0) {
206
+ process.exitCode = commander;
207
+ return commander;
208
+ }
209
+ const reported = reportCliError(error);
210
+ process.exitCode = reported.exitCode;
211
+ return reported.exitCode;
212
+ }
213
+ var CLI_EXIT, CliError, HandledCliError, debugFlag, NETWORK_ERRNO, NETWORK_MESSAGE, UNAVAILABLE_HINT, CLI_EXIT_CODE_HELP;
214
+ var init_cli_error = __esm({
215
+ "src/errors/cli.error.ts"() {
216
+ "use strict";
217
+ init_auth_error();
218
+ CLI_EXIT = {
219
+ OK: 0,
220
+ ERROR: 1,
221
+ USAGE: 2,
222
+ NOT_FOUND: 3,
223
+ AUTH: 9,
224
+ FORBIDDEN: 10,
225
+ UNAVAILABLE: 11
226
+ };
227
+ CliError = class _CliError extends Error {
228
+ static {
229
+ __name(this, "CliError");
230
+ }
231
+ isCliError = true;
232
+ code;
233
+ exitCode;
234
+ hint;
235
+ statusCode;
236
+ constructor(code, message, options = {}) {
237
+ super(message);
238
+ this.name = "CliError";
239
+ this.code = code;
240
+ this.exitCode = options.exitCode ?? CLI_EXIT.ERROR;
241
+ this.hint = options.hint;
242
+ this.statusCode = options.statusCode;
243
+ if (Error.captureStackTrace) Error.captureStackTrace(this, _CliError);
244
+ }
245
+ /** Bad arguments, an unknown action, no project — exit 2. */
246
+ static usage(message, hint) {
247
+ return new _CliError("usage", message, {
248
+ exitCode: CLI_EXIT.USAGE,
249
+ hint
250
+ });
251
+ }
252
+ /** The named thing does not exist — exit 3. */
253
+ static notFound(message, hint) {
254
+ return new _CliError("not_found", message, {
255
+ exitCode: CLI_EXIT.NOT_FOUND,
256
+ hint,
257
+ statusCode: 404
258
+ });
259
+ }
260
+ /** The credential may not do this — exit 10. */
261
+ static forbidden(message, hint) {
262
+ return new _CliError("forbidden", message, {
263
+ exitCode: CLI_EXIT.FORBIDDEN,
264
+ hint,
265
+ statusCode: 403
266
+ });
267
+ }
268
+ static isCliError(error) {
269
+ return error instanceof _CliError || typeof error === "object" && error !== null && error.isCliError === true;
270
+ }
271
+ };
272
+ __name(isAccessDeniedError, "isAccessDeniedError");
273
+ HandledCliError = class extends Error {
274
+ static {
275
+ __name(this, "HandledCliError");
276
+ }
277
+ handled = true;
278
+ exitCode;
279
+ /** The error that was reported. */
280
+ original;
281
+ constructor(message, exitCode, original) {
282
+ super(message);
283
+ this.name = "HandledCliError";
284
+ this.exitCode = exitCode;
285
+ this.original = original;
286
+ }
287
+ };
288
+ __name(isHandledCliError, "isHandledCliError");
289
+ debugFlag = false;
290
+ __name(setDebugMode, "setDebugMode");
291
+ __name(debugEnabled, "debugEnabled");
292
+ NETWORK_ERRNO = /* @__PURE__ */ new Set([
293
+ "ECONNREFUSED",
294
+ "ECONNRESET",
295
+ "ENOTFOUND",
296
+ "ETIMEDOUT",
297
+ "EAI_AGAIN",
298
+ "EPIPE",
299
+ "EHOSTUNREACH",
300
+ "ENETUNREACH",
301
+ "UND_ERR_CONNECT_TIMEOUT",
302
+ "UND_ERR_HEADERS_TIMEOUT",
303
+ "UND_ERR_BODY_TIMEOUT",
304
+ "UND_ERR_SOCKET"
305
+ ]);
306
+ NETWORK_MESSAGE = /fetch failed|socket hang up|network request failed|request timeout|ECONNREFUSED|ENOTFOUND/i;
307
+ UNAVAILABLE_HINT = "The Lua API could not be reached \u2014 check your network and https://status.heylua.ai, then retry.";
308
+ __name(authHint, "authHint");
309
+ __name(numericStatus, "numericStatus");
310
+ __name(classifyCliError, "classifyCliError");
311
+ __name(renderCliError, "renderCliError");
312
+ __name(reportCliError, "reportCliError");
313
+ __name(commanderExitCode, "commanderExitCode");
314
+ __name(reportUnhandledCliError, "reportUnhandledCliError");
315
+ CLI_EXIT_CODE_HELP = `Exit codes: 0 ok \xB7 1 error \xB7 2 usage \xB7 3 not found \xB7 9 auth (401) \xB7 10 forbidden (403 / other 4xx) \xB7
316
+ 11 unavailable (5xx / network). lua workflows adds 4 run failed \xB7 5 run cancelled \xB7 6 run gated \xB7
317
+ 7 --timeout reached \xB7 8 run parked waiting for a human.
318
+ Errors: one line \u2014 \u2716 <code>: <message> \u2014 plus a \u{1F4A1} hint; LUA_DEBUG=1 prints the stack.`;
319
+ }
320
+ });
321
+
67
322
  // src/utils/semver.ts
68
323
  function parseVersion(version) {
69
324
  const [versionPart, preReleasePart] = version.split("-");
@@ -998,6 +1253,48 @@ function scheduledTimeKey(scheduledTime) {
998
1253
  function scheduledWorkflowRunIdForTime(jobId, scheduledTime) {
999
1254
  return scheduledWorkflowRunId(jobId, scheduledTime);
1000
1255
  }
1256
+ function workflowRetryMaxAttemptsMessage(got) {
1257
+ const tail = got === void 0 ? "" : ` (got ${JSON.stringify(got)})`;
1258
+ return `\`retry.maxAttempts\` must be an integer ${WORKFLOW_RETRY_MIN_ATTEMPTS}..${WORKFLOW_RETRY_MAX_ATTEMPTS}${tail}`;
1259
+ }
1260
+ function isWithinWorkflowRetryAttempts(value3) {
1261
+ return typeof value3 === "number" && Number.isInteger(value3) && value3 >= WORKFLOW_RETRY_MIN_ATTEMPTS && value3 <= WORKFLOW_RETRY_MAX_ATTEMPTS;
1262
+ }
1263
+ function workflowRetryUnknownMembersMessage(keys) {
1264
+ const named = keys.map((k) => {
1265
+ const engine = WORKFLOW_RETRY_ENGINE_KEYS.includes(k);
1266
+ return `\`${k}\`${engine ? " (engine-owned \u2014 stamped by resetAttempts, never authored)" : ""}`;
1267
+ });
1268
+ return `\`retry\` has no member ${named.join(", ")}; members: ${WORKFLOW_RETRY_POLICY_KEYS.join(", ")}`;
1269
+ }
1270
+ function unknownWorkflowRetryMembers(retry) {
1271
+ if (!retry || typeof retry !== "object" || Array.isArray(retry)) return [];
1272
+ return Object.keys(retry).filter((k) => !WORKFLOW_RETRY_POLICY_KEYS.includes(k));
1273
+ }
1274
+ function authoredRetryPolicy(retry) {
1275
+ if (!retry || typeof retry !== "object" || Array.isArray(retry)) return void 0;
1276
+ const src = retry;
1277
+ const out = {};
1278
+ for (const k of WORKFLOW_RETRY_POLICY_KEYS) if (src[k] !== void 0) out[k] = src[k];
1279
+ return Object.keys(out).length ? out : void 0;
1280
+ }
1281
+ function retryBudgetBaseAttempt(row2) {
1282
+ const base = row2.retry?.budgetBaseAttempt;
1283
+ return typeof base === "number" && Number.isSafeInteger(base) && base > 0 ? base : 0;
1284
+ }
1285
+ function retryBudgetAttempt(row2) {
1286
+ return Math.max(0, row2.attempt - retryBudgetBaseAttempt(row2));
1287
+ }
1288
+ function retryBudgetMaxAttempts(row2) {
1289
+ const max = row2.retry?.maxAttempts;
1290
+ return typeof max === "number" && Number.isFinite(max) && max >= 1 ? max : 1;
1291
+ }
1292
+ function retryBudgetRemaining(row2) {
1293
+ return retryBudgetAttempt(row2) < retryBudgetMaxAttempts(row2);
1294
+ }
1295
+ function retriesRemaining(row2) {
1296
+ return Math.max(0, retryBudgetMaxAttempts(row2) - retryBudgetAttempt(row2));
1297
+ }
1001
1298
  function isWithinWorkflowJobRange(member, value3) {
1002
1299
  const { min, max } = WORKFLOW_JOB_RANGES[member];
1003
1300
  return typeof value3 === "number" && Number.isInteger(value3) && value3 >= min && value3 <= max;
@@ -1185,7 +1482,66 @@ ${PREAMBLE}
1185
1482
 
1186
1483
  ${items.join("\n\n")}`;
1187
1484
  }
1188
- 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, PLATFORM_FALLBACK_MODEL_SOURCE, 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, WORKFLOW_RESOLVE_OUTPUT_MAX_BYTES, WORKFLOW_RETRY_BACKOFFS, WORKFLOW_JOB_RESOURCES, WORKFLOW_SIDE_EFFECTS, WORKFLOW_JOB_RANGES, WORKFLOW_JOB_RANGE_MEMBERS, WORKFLOW_SINGLE_STEP_TYPES, WORKFLOW_HITL_ENTRY_TYPES, WORKFLOW_ARM_ENTRY_TYPES, WORKFLOW_HITL_ARM_CONTAINERS, WORKFLOW_GRAPH_ENTRY_STEP_KINDS, WORKFLOW_ARM_ENTRY_STEP_KINDS, WORKFLOW_BUDGET_MAX_DURATION_SECONDS, REDACTED_PLACEHOLDER, PROVIDER_MESSAGE_MAX_CHARS, ERROR_MESSAGE_MAX_CHARS, SECRET_LITERAL_PATTERNS, SECRET_NAME, SECRET_PAIR_PATTERNS, GROUP_COUNT, WORKFLOW_SECRET_KEY_RE, WORKFLOW_RESERVED_SECRET_KEYS, SCRUB_INPUT_MAX_CHARS, SCRUB_CUT_BACKOFF_CHARS, WORKFLOW_AUDIT_EVENTS, WORKFLOW_AUDIT_METADATA_MAX_BYTES, INDENT, WRAP_WIDTH, NOUNS, GET_TOOL_NAMES, PREAMBLE;
1485
+ function workflowApprovalDecisionOf(resumeData) {
1486
+ if (resumeData.timedOut === true) return "timed_out";
1487
+ return resumeData.approved === true ? "approved" : "denied";
1488
+ }
1489
+ function workflowApprovalOutput(resumeData) {
1490
+ const decision = typeof resumeData.decision === "string" ? resumeData.decision : workflowApprovalDecisionOf(resumeData);
1491
+ const note = typeof resumeData.note === "string" && resumeData.note.trim() !== "" ? resumeData.note : void 0;
1492
+ const text = typeof resumeData.text === "string" ? resumeData.text : note ?? decision;
1493
+ return {
1494
+ ...resumeData,
1495
+ decision,
1496
+ text
1497
+ };
1498
+ }
1499
+ function isWorkflowApprovalOutput(value3) {
1500
+ if (typeof value3 !== "object" || value3 === null || Array.isArray(value3)) return false;
1501
+ const v = value3;
1502
+ return typeof v.approved === "boolean" && WORKFLOW_APPROVAL_OUTPUT_DECISIONS.includes(v.decision) && typeof v.text === "string";
1503
+ }
1504
+ function extractSingleJsonValue(text) {
1505
+ const trimmed = (text ?? "").trim();
1506
+ if (!trimmed) return {
1507
+ reason: "reply is empty"
1508
+ };
1509
+ const fenced = [
1510
+ ...trimmed.matchAll(JSON_FENCE_RE)
1511
+ ];
1512
+ if (fenced.length > 1) return {
1513
+ reason: "reply carries more than one fenced block"
1514
+ };
1515
+ const candidate = fenced.length === 1 ? fenced[0][1].trim() : trimmed;
1516
+ try {
1517
+ return {
1518
+ value: JSON.parse(candidate)
1519
+ };
1520
+ } catch {
1521
+ if (fenced.length === 1) return {
1522
+ reason: "fenced block is not valid JSON"
1523
+ };
1524
+ }
1525
+ const opens = [
1526
+ trimmed.indexOf("{"),
1527
+ trimmed.indexOf("[")
1528
+ ].filter((i) => i !== -1);
1529
+ const start = opens.length ? Math.min(...opens) : -1;
1530
+ const end = Math.max(trimmed.lastIndexOf("}"), trimmed.lastIndexOf("]"));
1531
+ if (start === -1 || end <= start) return {
1532
+ reason: "reply is not JSON"
1533
+ };
1534
+ try {
1535
+ return {
1536
+ value: JSON.parse(trimmed.slice(start, end + 1))
1537
+ };
1538
+ } catch {
1539
+ return {
1540
+ reason: "reply does not contain a single JSON value"
1541
+ };
1542
+ }
1543
+ }
1544
+ 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, PLATFORM_FALLBACK_MODEL_SOURCE, 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, WORKFLOW_RESOLVE_OUTPUT_MAX_BYTES, WORKFLOW_RETRY_BACKOFFS, WORKFLOW_RETRY_MIN_ATTEMPTS, WORKFLOW_RETRY_POLICY_KEYS, WORKFLOW_RETRY_MAX_ATTEMPTS, WORKFLOW_RETRY_ENGINE_KEYS, WORKFLOW_JOB_RESOURCES, WORKFLOW_SIDE_EFFECTS, WORKFLOW_JOB_RANGES, WORKFLOW_JOB_RANGE_MEMBERS, WORKFLOW_SINGLE_STEP_TYPES, WORKFLOW_HITL_ENTRY_TYPES, WORKFLOW_ARM_ENTRY_TYPES, WORKFLOW_HITL_ARM_CONTAINERS, WORKFLOW_GRAPH_ENTRY_STEP_KINDS, WORKFLOW_ARM_ENTRY_STEP_KINDS, WORKFLOW_BUDGET_MAX_DURATION_SECONDS, REDACTED_PLACEHOLDER, PROVIDER_MESSAGE_MAX_CHARS, ERROR_MESSAGE_MAX_CHARS, SECRET_LITERAL_PATTERNS, SECRET_NAME, SECRET_PAIR_PATTERNS, GROUP_COUNT, WORKFLOW_SECRET_KEY_RE, WORKFLOW_RESERVED_SECRET_KEYS, SCRUB_INPUT_MAX_CHARS, SCRUB_CUT_BACKOFF_CHARS, WORKFLOW_AUDIT_EVENTS, WORKFLOW_AUDIT_METADATA_MAX_BYTES, INDENT, WRAP_WIDTH, NOUNS, GET_TOOL_NAMES, PREAMBLE, WORKFLOW_APPROVAL_OUTPUT_DECISIONS, WORKFLOW_APPROVAL_OUTPUT_SCHEMA, JSON_FENCE_RE;
1189
1545
  var init_dist = __esm({
1190
1546
  "../shared-types/dist/index.mjs"() {
1191
1547
  "use strict";
@@ -2306,6 +2662,37 @@ This text is who you are for this person. As you learn them, their name, their w
2306
2662
  "fixed",
2307
2663
  "exponential"
2308
2664
  ];
2665
+ WORKFLOW_RETRY_MIN_ATTEMPTS = 1;
2666
+ WORKFLOW_RETRY_POLICY_KEYS = [
2667
+ "maxAttempts",
2668
+ "backoffSeconds",
2669
+ "backoff",
2670
+ "maxBackoffSeconds"
2671
+ ];
2672
+ WORKFLOW_RETRY_MAX_ATTEMPTS = 20;
2673
+ __name(workflowRetryMaxAttemptsMessage, "workflowRetryMaxAttemptsMessage");
2674
+ __name2(workflowRetryMaxAttemptsMessage, "workflowRetryMaxAttemptsMessage");
2675
+ __name(isWithinWorkflowRetryAttempts, "isWithinWorkflowRetryAttempts");
2676
+ __name2(isWithinWorkflowRetryAttempts, "isWithinWorkflowRetryAttempts");
2677
+ WORKFLOW_RETRY_ENGINE_KEYS = [
2678
+ "budgetBaseAttempt"
2679
+ ];
2680
+ __name(workflowRetryUnknownMembersMessage, "workflowRetryUnknownMembersMessage");
2681
+ __name2(workflowRetryUnknownMembersMessage, "workflowRetryUnknownMembersMessage");
2682
+ __name(unknownWorkflowRetryMembers, "unknownWorkflowRetryMembers");
2683
+ __name2(unknownWorkflowRetryMembers, "unknownWorkflowRetryMembers");
2684
+ __name(authoredRetryPolicy, "authoredRetryPolicy");
2685
+ __name2(authoredRetryPolicy, "authoredRetryPolicy");
2686
+ __name(retryBudgetBaseAttempt, "retryBudgetBaseAttempt");
2687
+ __name2(retryBudgetBaseAttempt, "retryBudgetBaseAttempt");
2688
+ __name(retryBudgetAttempt, "retryBudgetAttempt");
2689
+ __name2(retryBudgetAttempt, "retryBudgetAttempt");
2690
+ __name(retryBudgetMaxAttempts, "retryBudgetMaxAttempts");
2691
+ __name2(retryBudgetMaxAttempts, "retryBudgetMaxAttempts");
2692
+ __name(retryBudgetRemaining, "retryBudgetRemaining");
2693
+ __name2(retryBudgetRemaining, "retryBudgetRemaining");
2694
+ __name(retriesRemaining, "retriesRemaining");
2695
+ __name2(retriesRemaining, "retriesRemaining");
2309
2696
  WORKFLOW_JOB_RESOURCES = [
2310
2697
  "small",
2311
2698
  "medium",
@@ -2578,6 +2965,8 @@ This text is who you are for this person. As you learn them, their name, their w
2578
2965
  "workflow.goal.resumed",
2579
2966
  "workflow.goal.done",
2580
2967
  "workflow.goal.closed",
2968
+ // LUA-760: an ended goal's cadence Job retired (deleted) — inline on done / closed, by R21 / R28, or by sweep #30
2969
+ "workflow.goal.job_retired",
2581
2970
  // --- org policy (02 §2.10 / 09 R23) ---
2582
2971
  "workflow.policy.retention_changed",
2583
2972
  "workflow.policy.pacing_changed",
@@ -2627,6 +3016,79 @@ listed here; never invent a target.`;
2627
3016
  __name2(reachingIt, "reachingIt");
2628
3017
  __name(renderTargetsBlock, "renderTargetsBlock");
2629
3018
  __name2(renderTargetsBlock, "renderTargetsBlock");
3019
+ WORKFLOW_APPROVAL_OUTPUT_DECISIONS = [
3020
+ "approved",
3021
+ "denied",
3022
+ "timed_out"
3023
+ ];
3024
+ WORKFLOW_APPROVAL_OUTPUT_SCHEMA = {
3025
+ type: "object",
3026
+ properties: {
3027
+ approved: {
3028
+ type: "boolean"
3029
+ },
3030
+ decision: {
3031
+ type: "string",
3032
+ enum: [
3033
+ ...WORKFLOW_APPROVAL_OUTPUT_DECISIONS
3034
+ ]
3035
+ },
3036
+ /** the approver's note when given, else the decision word — what `${stepResults.<id>.text}` reads */
3037
+ text: {
3038
+ type: "string"
3039
+ },
3040
+ note: {
3041
+ type: "string"
3042
+ },
3043
+ editedPayload: {},
3044
+ editRevision: {
3045
+ type: "integer"
3046
+ },
3047
+ decidedBy: {
3048
+ type: "object",
3049
+ properties: {
3050
+ id: {
3051
+ type: "string"
3052
+ },
3053
+ kind: {
3054
+ type: "string"
3055
+ }
3056
+ }
3057
+ },
3058
+ timedOut: {
3059
+ type: "boolean"
3060
+ },
3061
+ escalations: {
3062
+ type: "integer"
3063
+ },
3064
+ evidence: {
3065
+ type: "array",
3066
+ items: {
3067
+ type: "string"
3068
+ }
3069
+ },
3070
+ items: {
3071
+ type: "array",
3072
+ items: {
3073
+ type: "object"
3074
+ }
3075
+ }
3076
+ },
3077
+ required: [
3078
+ "approved",
3079
+ "decision",
3080
+ "text"
3081
+ ]
3082
+ };
3083
+ __name(workflowApprovalDecisionOf, "workflowApprovalDecisionOf");
3084
+ __name2(workflowApprovalDecisionOf, "workflowApprovalDecisionOf");
3085
+ __name(workflowApprovalOutput, "workflowApprovalOutput");
3086
+ __name2(workflowApprovalOutput, "workflowApprovalOutput");
3087
+ __name(isWorkflowApprovalOutput, "isWorkflowApprovalOutput");
3088
+ __name2(isWorkflowApprovalOutput, "isWorkflowApprovalOutput");
3089
+ JSON_FENCE_RE = /```(?:json)?[ \t]*\r?\n([\s\S]*?)\r?\n?```/g;
3090
+ __name(extractSingleJsonValue, "extractSingleJsonValue");
3091
+ __name2(extractSingleJsonValue, "extractSingleJsonValue");
2630
3092
  }
2631
3093
  });
2632
3094
 
@@ -3133,6 +3595,208 @@ import { createHash as createHash2 } from "crypto";
3133
3595
  import { z as z6 } from "zod";
3134
3596
  import { z as z22 } from "zod";
3135
3597
  import { createHash as createHash22 } from "crypto";
3598
+ function isMapConfigObject(v) {
3599
+ return typeof v === "object" && v !== null && !Array.isArray(v);
3600
+ }
3601
+ function parseMapConfig(raw, stepId) {
3602
+ if (isMapConfigObject(raw)) return raw;
3603
+ if (typeof raw !== "string") {
3604
+ throw new Error(`Stored mapping step "${stepId}" has a mapConfig that is neither a JSON string nor an object.`);
3605
+ }
3606
+ try {
3607
+ return JSON.parse(raw);
3608
+ } catch (e) {
3609
+ throw new Error(`Stored mapping step "${stepId}" has invalid JSON mapConfig: ${e.message}`);
3610
+ }
3611
+ }
3612
+ function mapConfigWire(raw) {
3613
+ if (typeof raw === "string") return raw;
3614
+ if (isMapConfigObject(raw)) return canonicalJson(raw);
3615
+ return void 0;
3616
+ }
3617
+ function describeBadPlaceholder(template22, idx, rawExpr) {
3618
+ return `Template placeholder #${idx} (\${${rawExpr}}) in '${template22}'`;
3619
+ }
3620
+ function parseTemplatePlaceholder(rawExpr) {
3621
+ const dot = rawExpr.indexOf(".");
3622
+ return {
3623
+ scope: dot === -1 ? rawExpr : rawExpr.slice(0, dot),
3624
+ rest: dot === -1 ? "" : rawExpr.slice(dot + 1)
3625
+ };
3626
+ }
3627
+ function traverseMappingPath(root, path25, errorLabel2) {
3628
+ if (path25 === "" || path25 === ".") return root;
3629
+ const parts = path25.split(".");
3630
+ let value22 = root;
3631
+ for (const part of parts) {
3632
+ if (typeof value22 === "object" && value22 !== null) value22 = value22[part];
3633
+ else throw new WorkflowTemplateError(`Invalid path ${path25} in ${errorLabel2}`, path25);
3634
+ }
3635
+ return value22;
3636
+ }
3637
+ function stringifyTemplateValue(v, template22, idx, rawExpr) {
3638
+ if (v === null || v === void 0) return "";
3639
+ if (typeof v === "object") {
3640
+ try {
3641
+ return JSON.stringify(v);
3642
+ } catch (err) {
3643
+ throw new WorkflowTemplateError(`${describeBadPlaceholder(template22, idx, rawExpr)} resolved to a value that could not be JSON-stringified (${err.message}).`, rawExpr);
3644
+ }
3645
+ }
3646
+ return String(v);
3647
+ }
3648
+ function escapeFence(content) {
3649
+ return content.replace(/<\/lua-data/g, "<\\/lua-data");
3650
+ }
3651
+ function fenceBlock(name, source, content) {
3652
+ return `<lua-data name="${name}" source="${source}" untrusted="true">${escapeFence(content)}</lua-data>`;
3653
+ }
3654
+ function renderTemplate(template22, ctx, opts) {
3655
+ let idx = 0;
3656
+ return template22.replace(TEMPLATE_PLACEHOLDER, (_match, rawExpr) => {
3657
+ idx += 1;
3658
+ const { scope, rest } = parseTemplatePlaceholder(rawExpr);
3659
+ const label = describeBadPlaceholder(template22, idx, rawExpr);
3660
+ let rendered;
3661
+ let source;
3662
+ switch (scope) {
3663
+ case "initData":
3664
+ rendered = stringifyTemplateValue(traverseMappingPath(ctx.initData, rest, label), template22, idx, rawExpr);
3665
+ source = "initData";
3666
+ break;
3667
+ case "state":
3668
+ rendered = stringifyTemplateValue(traverseMappingPath(ctx.state, rest, label), template22, idx, rawExpr);
3669
+ source = "state";
3670
+ break;
3671
+ case "requestContext":
3672
+ rendered = stringifyTemplateValue(traverseMappingPath(ctx.requestContext, rest, label), template22, idx, rawExpr);
3673
+ source = "requestContext";
3674
+ break;
3675
+ case "stepResults": {
3676
+ const innerDot = rest.indexOf(".");
3677
+ const stepId = innerDot === -1 ? rest : rest.slice(0, innerDot);
3678
+ const subPath = innerDot === -1 ? "" : rest.slice(innerDot + 1);
3679
+ if (!stepId) throw new WorkflowTemplateError(`${label} must name a step: \${stepResults.<stepId>.<path>}.`, rawExpr);
3680
+ if (!(stepId in ctx.stepResults) || ctx.stepResults[stepId] == null) {
3681
+ throw new WorkflowTemplateError(`${label} references stepResults.${stepId} but step "${stepId}" has no resolvable output (not an ancestor, not run, failed, or produced no output).`, rawExpr);
3682
+ }
3683
+ rendered = stringifyTemplateValue(traverseMappingPath(ctx.stepResults[stepId], subPath, label), template22, idx, rawExpr);
3684
+ source = `step:${stepId}`;
3685
+ break;
3686
+ }
3687
+ default:
3688
+ throw new WorkflowTemplateError(`${label} references unknown namespace "${scope}". Use one of: ${TEMPLATE_NAMESPACES.join(", ")}.`, rawExpr);
3689
+ }
3690
+ return opts.fenced ? fenceBlock(rawExpr, source, rendered) : rendered;
3691
+ });
3692
+ }
3693
+ function isMapDescriptor(v) {
3694
+ if (v === null || typeof v !== "object" || Array.isArray(v)) return false;
3695
+ const d = v;
3696
+ const keys = Object.keys(d);
3697
+ const only = /* @__PURE__ */ __name3((...allowed) => keys.every((k) => allowed.includes(k)), "only");
3698
+ if ("value" in d) return keys.length === 1;
3699
+ if ("template" in d) return keys.length === 1 && typeof d.template === "string";
3700
+ if ("requestContextPath" in d) return keys.length === 1 && typeof d.requestContextPath === "string";
3701
+ if ("knowledge" in d) return keys.length === 1 && typeof d.knowledge === "object" && d.knowledge !== null;
3702
+ if ("initData" in d) return d.initData === true && typeof d.path === "string" && only("initData", "path");
3703
+ if ("step" in d) {
3704
+ const stepOk = typeof d.step === "string" || Array.isArray(d.step) && d.step.every((x) => typeof x === "string");
3705
+ return stepOk && typeof d.path === "string" && only("step", "path", "rows");
3706
+ }
3707
+ return false;
3708
+ }
3709
+ function malformedMapMembers(cfg) {
3710
+ if (!cfg || typeof cfg !== "object" || Array.isArray(cfg)) return [];
3711
+ const out = [];
3712
+ for (const [member, v] of Object.entries(cfg)) {
3713
+ if (!v || typeof v !== "object" || Array.isArray(v) || isMapDescriptor(v)) continue;
3714
+ const keys = Object.keys(v).filter((k) => MAP_DESCRIPTOR_KEYS.includes(k));
3715
+ if (keys.length > 0) out.push({
3716
+ member,
3717
+ keys
3718
+ });
3719
+ }
3720
+ return out;
3721
+ }
3722
+ function mapMemberMalformedMessage(id, m) {
3723
+ const keys = m.keys.map((k) => `\`${k}\``).join(", ");
3724
+ return `"${id}".${m.member} carries descriptor key${m.keys.length === 1 ? "" : "s"} ${keys} but is not an exact binding form ({initData:true, path} | {step, path[, rows]} | {value} | {template} | {requestContextPath} | {knowledge}) \u2014 it is passed to the step verbatim as a literal; fix the descriptor, or wrap it in {value: \u2026} if the literal is intended`;
3725
+ }
3726
+ function resolveDescriptor(key, m, ctx) {
3727
+ if (!isMapDescriptor(m)) return {
3728
+ value: m
3729
+ };
3730
+ try {
3731
+ if ("value" in m) return {
3732
+ value: m.value
3733
+ };
3734
+ if ("template" in m && typeof m.template === "string") {
3735
+ return {
3736
+ value: renderTemplate(m.template, ctx, {
3737
+ fenced: false
3738
+ })
3739
+ };
3740
+ }
3741
+ if ("knowledge" in m || "rows" in m && m.rows !== void 0) {
3742
+ return {
3743
+ error: "binding_unresolved",
3744
+ key
3745
+ };
3746
+ }
3747
+ if ("requestContextPath" in m) {
3748
+ const label = `requestContext path for key "${key}"`;
3749
+ return {
3750
+ value: traverseMappingPath(ctx.requestContext, m.requestContextPath, label)
3751
+ };
3752
+ }
3753
+ if ("path" in m) {
3754
+ const source = "initData" in m && m.initData ? "initData" : "step";
3755
+ if (source === "initData") {
3756
+ return {
3757
+ value: traverseMappingPath(ctx.initData, m.path, `initData for key "${key}"`)
3758
+ };
3759
+ }
3760
+ const stepRef = m.step;
3761
+ const candidates = Array.isArray(stepRef) ? stepRef : [
3762
+ stepRef
3763
+ ];
3764
+ const stepId = candidates.find((s) => ctx.stepResults[s] !== void 0 && ctx.stepResults[s] !== null);
3765
+ if (stepId === void 0) return {
3766
+ error: "binding_unresolved",
3767
+ key
3768
+ };
3769
+ return {
3770
+ value: traverseMappingPath(ctx.stepResults[stepId], m.path, `step ${candidates.join("|")} for key "${key}"`)
3771
+ };
3772
+ }
3773
+ return {
3774
+ error: "binding_unresolved",
3775
+ key
3776
+ };
3777
+ } catch (err) {
3778
+ if (err instanceof WorkflowTemplateError) return {
3779
+ error: "binding_unresolved",
3780
+ key
3781
+ };
3782
+ throw err;
3783
+ }
3784
+ }
3785
+ function resolveMapping(cfg, ctx) {
3786
+ const keys = Object.keys(cfg);
3787
+ if (keys.length === 1 && keys[0] === "") {
3788
+ return resolveDescriptor("", cfg[""], ctx);
3789
+ }
3790
+ const result = {};
3791
+ for (const key of keys) {
3792
+ const resolved = resolveDescriptor(key, cfg[key], ctx);
3793
+ if ("error" in resolved) return resolved;
3794
+ result[key] = resolved.value;
3795
+ }
3796
+ return {
3797
+ value: result
3798
+ };
3799
+ }
3136
3800
  function workspaceTemplatePath(template22) {
3137
3801
  const key = template22.trim();
3138
3802
  const expr = WORKSPACE_TEMPLATE_EXPR_RE.exec(key);
@@ -3293,12 +3957,11 @@ function mapConfigStepRefs(raw) {
3293
3957
  if (!cfg) return [];
3294
3958
  const ids = [];
3295
3959
  for (const d of Object.values(cfg)) {
3296
- if (!d || typeof d !== "object") continue;
3297
- const desc = d;
3298
- if (desc.step !== void 0) ids.push(...Array.isArray(desc.step) ? desc.step : [
3299
- desc.step
3960
+ if (!isMapDescriptor(d)) continue;
3961
+ if ("step" in d) ids.push(...Array.isArray(d.step) ? d.step : [
3962
+ d.step
3300
3963
  ]);
3301
- if (typeof desc.template === "string") ids.push(...templateStepRefs(desc.template));
3964
+ if ("template" in d) ids.push(...templateStepRefs(d.template));
3302
3965
  }
3303
3966
  return ids;
3304
3967
  }
@@ -3423,6 +4086,12 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
3423
4086
  const r = node.retry;
3424
4087
  if (!r) return;
3425
4088
  const id = singleId(node);
4089
+ const unknown = unknownWorkflowRetryMembers(r);
4090
+ if (unknown.length) err("invalid-envelope", workflowRetryUnknownMembersMessage(unknown), `${path25}.retry`, id);
4091
+ if (r.maxAttempts !== void 0 && !isWithinWorkflowRetryAttempts(r.maxAttempts)) {
4092
+ const over = typeof r.maxAttempts === "number" && r.maxAttempts > WORKFLOW_RETRY_MAX_ATTEMPTS;
4093
+ err(over ? "cap-exceeded" : "invalid-envelope", workflowRetryMaxAttemptsMessage(r.maxAttempts), `${path25}.retry.maxAttempts`, id);
4094
+ }
3426
4095
  const backoffs = retryBackoffs();
3427
4096
  if (r.backoff !== void 0 && !backoffs.includes(r.backoff)) {
3428
4097
  const list = backoffs.map((b) => `'${b}'`).join(" | ");
@@ -3565,6 +4234,21 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
3565
4234
  const schema = node.type === "step" ? node.step.outputSchema : node.type === "agent" ? node.outputSchema : void 0;
3566
4235
  if (schema !== void 0) outputSchemas.set(singleId(node), schema);
3567
4236
  }, "recordOutputSchema");
4237
+ const checkMapMembers = /* @__PURE__ */ __name3((cfg, basePath, id) => {
4238
+ for (const m of malformedMapMembers(cfg)) {
4239
+ warn(MAP_MEMBER_MALFORMED_CODE, mapMemberMalformedMessage(id, m), `${basePath}.${m.member}`, id);
4240
+ }
4241
+ }, "checkMapMembers");
4242
+ const checkInputShape = /* @__PURE__ */ __name3((node, path25) => {
4243
+ if (node.type !== "tool" && node.type !== "workflow") return;
4244
+ const input = node.input;
4245
+ if (input === void 0) return;
4246
+ if (input !== null && typeof input === "object" && !Array.isArray(input)) {
4247
+ checkMapMembers(input, `${path25}.input`, node.id);
4248
+ return;
4249
+ }
4250
+ err("invalid-envelope", `\`input\` must be an object map \u2014 each member a binding descriptor ({initData:true, path} | {step, path} | {value} | {template} | {requestContextPath}) or a JSON literal (got ${JSON.stringify(input)})`, `${path25}.input`, node.id);
4251
+ }, "checkInputShape");
3568
4252
  const checkSingle = /* @__PURE__ */ __name3((node, path25, depth) => {
3569
4253
  recordOutputSchema(node);
3570
4254
  if (node.type === "workflow" && node.workflowId === WORKFLOW_ARM_SUBRUN_ID) {
@@ -3592,6 +4276,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
3592
4276
  }
3593
4277
  checkId(singleId(node), path25);
3594
4278
  checkPolicyEnums(node, path25);
4279
+ checkInputShape(node, path25);
3595
4280
  checkTimeout(node, path25);
3596
4281
  checkTier(node, path25);
3597
4282
  checkRetry(node, path25);
@@ -3672,6 +4357,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
3672
4357
  const checkArm = /* @__PURE__ */ __name3((arm, path25, depth, container) => {
3673
4358
  if (arm.type === "mapping") {
3674
4359
  checkId(arm.id, path25);
4360
+ checkMapMembers(readMapConfig(arm.mapConfig), `${path25}.mapConfig`, arm.id);
3675
4361
  for (const ref of nodeStepRefs(arm)) {
3676
4362
  if (!upstream.has(ref)) err("template-reference-unresolved", `"${arm.id}" references stepResults.${ref}, which is not upstream`, path25, arm.id);
3677
4363
  }
@@ -3696,6 +4382,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
3696
4382
  break;
3697
4383
  case "mapping":
3698
4384
  checkId(entry.id, path25);
4385
+ checkMapMembers(readMapConfig(entry.mapConfig), `${path25}.mapConfig`, entry.id);
3699
4386
  for (const ref of nodeStepRefs(entry)) {
3700
4387
  if (!upstream.has(ref)) err("template-reference-unresolved", `"${entry.id}" references stepResults.${ref}, which is not upstream`, path25, entry.id);
3701
4388
  }
@@ -4294,172 +4981,6 @@ function toPathOrLiteral(v) {
4294
4981
  literal: v
4295
4982
  };
4296
4983
  }
4297
- function isMapConfigObject(v) {
4298
- return typeof v === "object" && v !== null && !Array.isArray(v);
4299
- }
4300
- function parseMapConfig(raw, stepId) {
4301
- if (isMapConfigObject(raw)) return raw;
4302
- if (typeof raw !== "string") {
4303
- throw new Error(`Stored mapping step "${stepId}" has a mapConfig that is neither a JSON string nor an object.`);
4304
- }
4305
- try {
4306
- return JSON.parse(raw);
4307
- } catch (e) {
4308
- throw new Error(`Stored mapping step "${stepId}" has invalid JSON mapConfig: ${e.message}`);
4309
- }
4310
- }
4311
- function mapConfigWire(raw) {
4312
- if (typeof raw === "string") return raw;
4313
- if (isMapConfigObject(raw)) return canonicalJson(raw);
4314
- return void 0;
4315
- }
4316
- function describeBadPlaceholder(template22, idx, rawExpr) {
4317
- return `Template placeholder #${idx} (\${${rawExpr}}) in '${template22}'`;
4318
- }
4319
- function parseTemplatePlaceholder(rawExpr) {
4320
- const dot = rawExpr.indexOf(".");
4321
- return {
4322
- scope: dot === -1 ? rawExpr : rawExpr.slice(0, dot),
4323
- rest: dot === -1 ? "" : rawExpr.slice(dot + 1)
4324
- };
4325
- }
4326
- function traverseMappingPath(root, path25, errorLabel2) {
4327
- if (path25 === "" || path25 === ".") return root;
4328
- const parts = path25.split(".");
4329
- let value22 = root;
4330
- for (const part of parts) {
4331
- if (typeof value22 === "object" && value22 !== null) value22 = value22[part];
4332
- else throw new WorkflowTemplateError(`Invalid path ${path25} in ${errorLabel2}`, path25);
4333
- }
4334
- return value22;
4335
- }
4336
- function stringifyTemplateValue(v, template22, idx, rawExpr) {
4337
- if (v === null || v === void 0) return "";
4338
- if (typeof v === "object") {
4339
- try {
4340
- return JSON.stringify(v);
4341
- } catch (err) {
4342
- throw new WorkflowTemplateError(`${describeBadPlaceholder(template22, idx, rawExpr)} resolved to a value that could not be JSON-stringified (${err.message}).`, rawExpr);
4343
- }
4344
- }
4345
- return String(v);
4346
- }
4347
- function escapeFence(content) {
4348
- return content.replace(/<\/lua-data/g, "<\\/lua-data");
4349
- }
4350
- function fenceBlock(name, source, content) {
4351
- return `<lua-data name="${name}" source="${source}" untrusted="true">${escapeFence(content)}</lua-data>`;
4352
- }
4353
- function renderTemplate(template22, ctx, opts) {
4354
- let idx = 0;
4355
- return template22.replace(TEMPLATE_PLACEHOLDER, (_match, rawExpr) => {
4356
- idx += 1;
4357
- const { scope, rest } = parseTemplatePlaceholder(rawExpr);
4358
- const label = describeBadPlaceholder(template22, idx, rawExpr);
4359
- let rendered;
4360
- let source;
4361
- switch (scope) {
4362
- case "initData":
4363
- rendered = stringifyTemplateValue(traverseMappingPath(ctx.initData, rest, label), template22, idx, rawExpr);
4364
- source = "initData";
4365
- break;
4366
- case "state":
4367
- rendered = stringifyTemplateValue(traverseMappingPath(ctx.state, rest, label), template22, idx, rawExpr);
4368
- source = "state";
4369
- break;
4370
- case "requestContext":
4371
- rendered = stringifyTemplateValue(traverseMappingPath(ctx.requestContext, rest, label), template22, idx, rawExpr);
4372
- source = "requestContext";
4373
- break;
4374
- case "stepResults": {
4375
- const innerDot = rest.indexOf(".");
4376
- const stepId = innerDot === -1 ? rest : rest.slice(0, innerDot);
4377
- const subPath = innerDot === -1 ? "" : rest.slice(innerDot + 1);
4378
- if (!stepId) throw new WorkflowTemplateError(`${label} must name a step: \${stepResults.<stepId>.<path>}.`, rawExpr);
4379
- if (!(stepId in ctx.stepResults) || ctx.stepResults[stepId] == null) {
4380
- throw new WorkflowTemplateError(`${label} references stepResults.${stepId} but step "${stepId}" has no resolvable output (not an ancestor, not run, failed, or produced no output).`, rawExpr);
4381
- }
4382
- rendered = stringifyTemplateValue(traverseMappingPath(ctx.stepResults[stepId], subPath, label), template22, idx, rawExpr);
4383
- source = `step:${stepId}`;
4384
- break;
4385
- }
4386
- default:
4387
- throw new WorkflowTemplateError(`${label} references unknown namespace "${scope}". Use one of: ${TEMPLATE_NAMESPACES.join(", ")}.`, rawExpr);
4388
- }
4389
- return opts.fenced ? fenceBlock(rawExpr, source, rendered) : rendered;
4390
- });
4391
- }
4392
- function resolveDescriptor(key, m, ctx) {
4393
- try {
4394
- if ("value" in m) return {
4395
- value: m.value
4396
- };
4397
- if ("template" in m && typeof m.template === "string") {
4398
- return {
4399
- value: renderTemplate(m.template, ctx, {
4400
- fenced: false
4401
- })
4402
- };
4403
- }
4404
- if ("knowledge" in m || "rows" in m && m.rows !== void 0) {
4405
- return {
4406
- error: "binding_unresolved",
4407
- key
4408
- };
4409
- }
4410
- if ("requestContextPath" in m) {
4411
- const label = `requestContext path for key "${key}"`;
4412
- return {
4413
- value: traverseMappingPath(ctx.requestContext, m.requestContextPath, label)
4414
- };
4415
- }
4416
- if ("path" in m) {
4417
- const source = "initData" in m && m.initData ? "initData" : "step";
4418
- if (source === "initData") {
4419
- return {
4420
- value: traverseMappingPath(ctx.initData, m.path, `initData for key "${key}"`)
4421
- };
4422
- }
4423
- const stepRef = m.step;
4424
- const candidates = Array.isArray(stepRef) ? stepRef : [
4425
- stepRef
4426
- ];
4427
- const stepId = candidates.find((s) => ctx.stepResults[s] !== void 0 && ctx.stepResults[s] !== null);
4428
- if (stepId === void 0) return {
4429
- error: "binding_unresolved",
4430
- key
4431
- };
4432
- return {
4433
- value: traverseMappingPath(ctx.stepResults[stepId], m.path, `step ${candidates.join("|")} for key "${key}"`)
4434
- };
4435
- }
4436
- return {
4437
- error: "binding_unresolved",
4438
- key
4439
- };
4440
- } catch (err) {
4441
- if (err instanceof WorkflowTemplateError) return {
4442
- error: "binding_unresolved",
4443
- key
4444
- };
4445
- throw err;
4446
- }
4447
- }
4448
- function resolveMapping(cfg, ctx) {
4449
- const keys = Object.keys(cfg);
4450
- if (keys.length === 1 && keys[0] === "") {
4451
- return resolveDescriptor("", cfg[""], ctx);
4452
- }
4453
- const result = {};
4454
- for (const key of keys) {
4455
- const resolved = resolveDescriptor(key, cfg[key], ctx);
4456
- if ("error" in resolved) return resolved;
4457
- result[key] = resolved.value;
4458
- }
4459
- return {
4460
- value: result
4461
- };
4462
- }
4463
4984
  function continuedFailureValue(error, killReason) {
4464
4985
  const code = typeof error?.code === "string" && error.code || typeof killReason === "string" && killReason || CONTINUED_FAILURE_DEFAULT_CODE;
4465
4986
  const message = typeof error?.message === "string" && error.message ? error.message : code;
@@ -5094,15 +5615,64 @@ function runCounts(counts) {
5094
5615
  pending: n(c.pending) + n(c.ready) + n(c.waiting)
5095
5616
  };
5096
5617
  }
5097
- function runUsage(run) {
5618
+ function isPricedStepReceipt(receipt) {
5619
+ return typeof receipt?.multiplier === "number" && Number.isFinite(receipt.multiplier);
5620
+ }
5621
+ function receiptEngine(engine) {
5622
+ if (engine === "actions") return "seat";
5623
+ if (engine === "credits") return "legacy";
5624
+ return void 0;
5625
+ }
5626
+ function receiptTier(tier) {
5627
+ return tier === "light" || tier === "standard" || tier === "heavy" ? tier : void 0;
5628
+ }
5629
+ function stepBillingView(receipt) {
5630
+ const engine = receiptEngine(receipt?.engine);
5631
+ if (!receipt || engine === void 0) return void 0;
5632
+ return pruneUndefined({
5633
+ engine,
5634
+ attempt: typeof receipt.attempt === "number" ? receipt.attempt : void 0,
5635
+ credits: typeof receipt.credits === "number" ? receipt.credits : void 0,
5636
+ actions: typeof receipt.actionsEstimate === "number" ? receipt.actionsEstimate : void 0,
5637
+ model: typeof receipt.model === "string" ? receipt.model : void 0,
5638
+ tier: receiptTier(receipt.tier),
5639
+ multiplier: typeof receipt.multiplier === "number" ? receipt.multiplier : void 0,
5640
+ byok: typeof receipt.byok === "boolean" ? receipt.byok : void 0,
5641
+ calibrated: typeof receipt.calibrated === "boolean" ? receipt.calibrated : void 0
5642
+ });
5643
+ }
5644
+ function runUsage(run, receipts) {
5645
+ const actions = n(run.budget?.spent?.actionsEstimate);
5646
+ const stamped = run.budget?.engine;
5647
+ const priced = (receipts ?? []).filter(isPricedStepReceipt);
5648
+ const engine = stamped === "seat" || stamped === "legacy" ? stamped : actions > 0 || priced.some((r) => r.engine === "actions") ? "seat" : priced.some((r) => r.engine === "credits") ? "legacy" : void 0;
5649
+ const seat = engine === "seat";
5650
+ const metering = engine ? "priced" : "flat";
5098
5651
  return {
5099
5652
  creditsUsed: run.budget?.spent?.credits ?? 0,
5100
5653
  actionsEstimate: run.budget?.spent?.actionsEstimate ?? 0,
5654
+ ...seat ? {
5655
+ actionsUsed: actions
5656
+ } : {},
5657
+ metering,
5658
+ ...engine ? {
5659
+ engine
5660
+ } : {},
5101
5661
  steps: run.budget?.spent?.steps ?? 0,
5102
5662
  inputTokens: run.usage?.inputTokens ?? 0,
5103
5663
  outputTokens: run.usage?.outputTokens ?? 0
5104
5664
  };
5105
5665
  }
5666
+ function runBudgetCap(budget) {
5667
+ const cap = budget?.maxCredits;
5668
+ return typeof cap === "number" && Number.isFinite(cap) && cap > 0 ? cap : void 0;
5669
+ }
5670
+ function runBudgetRemaining(budget) {
5671
+ const cap = runBudgetCap(budget);
5672
+ if (cap === void 0) return void 0;
5673
+ const spent = budget?.spent;
5674
+ return Math.max(0, cap - n(spent?.credits) - n(spent?.actionsEstimate) - n(budget?.reserved));
5675
+ }
5106
5676
  function runCancelView(cancel) {
5107
5677
  if (!cancel) return void 0;
5108
5678
  return {
@@ -6121,7 +6691,7 @@ function needsInheritedWorkspace(graph) {
6121
6691
  }
6122
6692
  return false;
6123
6693
  }
6124
- var __defProp3, __name3, SideEffectsSchema, JobResourcesSchema, WORKFLOW_ARM_SUBRUN_ID, WORKSPACE_TEMPLATE_EXPR_RE, SLEEP_UNTIL_REPLACEMENT, WORKFLOW_CAPS_DEFAULT, WORKFLOW_STEP_DEFAULT_TIMEOUT_SECONDS, WORKFLOW_AGENT_DEFAULT_TIMEOUT_SECONDS, WORKFLOW_JOB_DEFAULT_TIMEOUT_SECONDS, WORKFLOW_FOREACH_DEFAULT_CONCURRENCY, WORKFLOW_FOREACH_DEFAULT_MAX_ITEMS, WORKFLOW_LOOP_DEFAULT_MAX_ITERATIONS, WORKFLOW_SUSPEND_DEFAULT_TIMEOUT_HOURS, WORKFLOW_SIGNAL_DEFAULT_SOURCES, clone, CONNECTION_ID_HEX_RE, WORKFLOW_JOB_TOOLS, WORKFLOW_JOB_MAX_WORKTREE_ARMS, workspaceOf, mountsWorkspace, isJobTier, jobToolsOf, schemaIsArray, isHitlNode, isSingleStep, singleId, armId, TEMPLATE_STEP_REF, EDITABLE_PATH_RE, PREDICATE_OPS, isPredicateScalar, GRAPH_HASH_PREFIX, WorkflowPlanError, isArmStep, armStepId, armStepKind, joinIdOf, containerIdOf, PATH_PLACEHOLDER, MISSING, stepIdOf, cmp, eq, ne, gt, gte, lt, lte, inSet, notIn, exists, notExists, truthy, falsy, and, or, not, WorkflowTemplateError, TEMPLATE_PLACEHOLDER, TEMPLATE_NAMESPACES, fromInit, fromStep, value, template, fromRequest, rows, fromKnowledge, CONTINUED_FAILURE_TAG, CONTINUED_FAILURE_DEFAULT_CODE, CONTINUED_FAILURE_OUTPUT_SCHEMA, CONTINUED_FAILURE_LEAF_PATHS, isHitlNode2, nodeIdOf, GOAL_JUDGE_STEP_ID, NON_LEAF_KINDS, CONDITIONAL_JOIN_ID, branchArmId, canonical, sortKeys, JOIN, entryOfJoin, FORCE_CANCEL_STALE_MS, TERMINAL, IN_FLIGHT, n, STEP_ERROR_DETAIL_KEYS, STEP_ERROR_DETAIL_MAX_BYTES, DETAIL_MAX_DEPTH, DETAIL_MAX_ITEMS, MAX_HOLIDAYS, MAX_WALK_DAYS, HHMM, YMD, MS_PER_MIN, MS_PER_DAY, MON_FRI, supportedTz, fmtCache, WEEKDAYS, JSON_PATCH_OPS, JSON_PATCH_MAX_OPS, JSON_PATCH_MAX_VALUE_BYTES, JSON_PATCH_MAX_TOTAL_BYTES, SEGMENT_RE, APPROVER_SPEC_MAX_USERS, ESCALATION_MAX_HOPS, TemplateBindingSchema, ApproverSpecSchema, FourEyesSchema, EscalationHopSchema, TerminalOutcomeSchema, ApprovalOnTimeoutSchema, APPROVER_SPEC_SHAPES, APPROVER_WRITTEN_MAX, USER_ID_SHAPED_RE, BINDING_ROOTS, WORKFLOW_ENV_OVERLAY_MAX_KEYS, WORKFLOW_ENV_OVERLAY_MAX_VALUE_BYTES, WORKFLOW_ENV_TEMPLATE_SECRET_KEY_RE, isEnvRef, looksLikeEmbeddedJson, ZERO, isRecord2;
6694
+ var __defProp3, __name3, WorkflowTemplateError, TEMPLATE_PLACEHOLDER, TEMPLATE_NAMESPACES, MAP_DESCRIPTOR_KEYS, MAP_MEMBER_MALFORMED_CODE, fromInit, fromStep, value, template, fromRequest, rows, fromKnowledge, SideEffectsSchema, JobResourcesSchema, WORKFLOW_ARM_SUBRUN_ID, WORKSPACE_TEMPLATE_EXPR_RE, SLEEP_UNTIL_REPLACEMENT, WORKFLOW_CAPS_DEFAULT, WORKFLOW_STEP_DEFAULT_TIMEOUT_SECONDS, WORKFLOW_AGENT_DEFAULT_TIMEOUT_SECONDS, WORKFLOW_JOB_DEFAULT_TIMEOUT_SECONDS, WORKFLOW_FOREACH_DEFAULT_CONCURRENCY, WORKFLOW_FOREACH_DEFAULT_MAX_ITEMS, WORKFLOW_LOOP_DEFAULT_MAX_ITERATIONS, WORKFLOW_SUSPEND_DEFAULT_TIMEOUT_HOURS, WORKFLOW_SIGNAL_DEFAULT_SOURCES, clone, CONNECTION_ID_HEX_RE, WORKFLOW_JOB_TOOLS, WORKFLOW_JOB_MAX_WORKTREE_ARMS, workspaceOf, mountsWorkspace, isJobTier, jobToolsOf, schemaIsArray, isHitlNode, isSingleStep, singleId, armId, TEMPLATE_STEP_REF, EDITABLE_PATH_RE, PREDICATE_OPS, isPredicateScalar, GRAPH_HASH_PREFIX, WorkflowPlanError, isArmStep, armStepId, armStepKind, joinIdOf, containerIdOf, PATH_PLACEHOLDER, MISSING, stepIdOf, cmp, eq, ne, gt, gte, lt, lte, inSet, notIn, exists, notExists, truthy, falsy, and, or, not, CONTINUED_FAILURE_TAG, CONTINUED_FAILURE_DEFAULT_CODE, CONTINUED_FAILURE_OUTPUT_SCHEMA, CONTINUED_FAILURE_LEAF_PATHS, isHitlNode2, nodeIdOf, GOAL_JUDGE_STEP_ID, NON_LEAF_KINDS, CONDITIONAL_JOIN_ID, branchArmId, canonical, sortKeys, JOIN, entryOfJoin, FORCE_CANCEL_STALE_MS, TERMINAL, IN_FLIGHT, n, STEP_ERROR_DETAIL_KEYS, STEP_ERROR_DETAIL_MAX_BYTES, DETAIL_MAX_DEPTH, DETAIL_MAX_ITEMS, MAX_HOLIDAYS, MAX_WALK_DAYS, HHMM, YMD, MS_PER_MIN, MS_PER_DAY, MON_FRI, supportedTz, fmtCache, WEEKDAYS, JSON_PATCH_OPS, JSON_PATCH_MAX_OPS, JSON_PATCH_MAX_VALUE_BYTES, JSON_PATCH_MAX_TOTAL_BYTES, SEGMENT_RE, APPROVER_SPEC_MAX_USERS, ESCALATION_MAX_HOPS, TemplateBindingSchema, ApproverSpecSchema, FourEyesSchema, EscalationHopSchema, TerminalOutcomeSchema, ApprovalOnTimeoutSchema, APPROVER_SPEC_SHAPES, APPROVER_WRITTEN_MAX, USER_ID_SHAPED_RE, BINDING_ROOTS, WORKFLOW_ENV_OVERLAY_MAX_KEYS, WORKFLOW_ENV_OVERLAY_MAX_VALUE_BYTES, WORKFLOW_ENV_TEMPLATE_SECRET_KEY_RE, isEnvRef, looksLikeEmbeddedJson, ZERO, isRecord2;
6125
6695
  var init_dist2 = __esm({
6126
6696
  "../workflow-graph/dist/index.mjs"() {
6127
6697
  "use strict";
@@ -6134,6 +6704,94 @@ var init_dist2 = __esm({
6134
6704
  init_dist();
6135
6705
  __defProp3 = Object.defineProperty;
6136
6706
  __name3 = /* @__PURE__ */ __name((target, value22) => __defProp3(target, "name", { value: value22, configurable: true }), "__name");
6707
+ WorkflowTemplateError = class extends Error {
6708
+ static {
6709
+ __name(this, "WorkflowTemplateError");
6710
+ }
6711
+ static {
6712
+ __name3(this, "WorkflowTemplateError");
6713
+ }
6714
+ placeholder;
6715
+ constructor(message, placeholder) {
6716
+ super(message), this.placeholder = placeholder;
6717
+ this.name = "WorkflowTemplateError";
6718
+ }
6719
+ };
6720
+ __name(isMapConfigObject, "isMapConfigObject");
6721
+ __name3(isMapConfigObject, "isMapConfigObject");
6722
+ __name(parseMapConfig, "parseMapConfig");
6723
+ __name3(parseMapConfig, "parseMapConfig");
6724
+ __name(mapConfigWire, "mapConfigWire");
6725
+ __name3(mapConfigWire, "mapConfigWire");
6726
+ TEMPLATE_PLACEHOLDER = /\$\{([^}]*)\}/g;
6727
+ TEMPLATE_NAMESPACES = [
6728
+ "initData",
6729
+ "state",
6730
+ "requestContext",
6731
+ "stepResults"
6732
+ ];
6733
+ __name(describeBadPlaceholder, "describeBadPlaceholder");
6734
+ __name3(describeBadPlaceholder, "describeBadPlaceholder");
6735
+ __name(parseTemplatePlaceholder, "parseTemplatePlaceholder");
6736
+ __name3(parseTemplatePlaceholder, "parseTemplatePlaceholder");
6737
+ __name(traverseMappingPath, "traverseMappingPath");
6738
+ __name3(traverseMappingPath, "traverseMappingPath");
6739
+ __name(stringifyTemplateValue, "stringifyTemplateValue");
6740
+ __name3(stringifyTemplateValue, "stringifyTemplateValue");
6741
+ __name(escapeFence, "escapeFence");
6742
+ __name3(escapeFence, "escapeFence");
6743
+ __name(fenceBlock, "fenceBlock");
6744
+ __name3(fenceBlock, "fenceBlock");
6745
+ __name(renderTemplate, "renderTemplate");
6746
+ __name3(renderTemplate, "renderTemplate");
6747
+ __name(isMapDescriptor, "isMapDescriptor");
6748
+ __name3(isMapDescriptor, "isMapDescriptor");
6749
+ MAP_DESCRIPTOR_KEYS = [
6750
+ "step",
6751
+ "path",
6752
+ "initData",
6753
+ "value",
6754
+ "template",
6755
+ "requestContextPath",
6756
+ "knowledge"
6757
+ ];
6758
+ MAP_MEMBER_MALFORMED_CODE = "map-member-malformed";
6759
+ __name(malformedMapMembers, "malformedMapMembers");
6760
+ __name3(malformedMapMembers, "malformedMapMembers");
6761
+ __name(mapMemberMalformedMessage, "mapMemberMalformedMessage");
6762
+ __name3(mapMemberMalformedMessage, "mapMemberMalformedMessage");
6763
+ __name(resolveDescriptor, "resolveDescriptor");
6764
+ __name3(resolveDescriptor, "resolveDescriptor");
6765
+ __name(resolveMapping, "resolveMapping");
6766
+ __name3(resolveMapping, "resolveMapping");
6767
+ fromInit = /* @__PURE__ */ __name3((path25) => ({
6768
+ initData: true,
6769
+ path: path25
6770
+ }), "fromInit");
6771
+ fromStep = /* @__PURE__ */ __name3((s, path25 = "") => {
6772
+ const idOf = /* @__PURE__ */ __name3((x) => typeof x === "string" ? x : x.id, "idOf");
6773
+ return {
6774
+ step: Array.isArray(s) ? s.map(idOf) : idOf(s),
6775
+ path: path25
6776
+ };
6777
+ }, "fromStep");
6778
+ value = /* @__PURE__ */ __name3((v) => ({
6779
+ value: v
6780
+ }), "value");
6781
+ template = /* @__PURE__ */ __name3((s) => ({
6782
+ template: s
6783
+ }), "template");
6784
+ fromRequest = /* @__PURE__ */ __name3((path25) => ({
6785
+ requestContextPath: path25
6786
+ }), "fromRequest");
6787
+ rows = /* @__PURE__ */ __name3((s, path25, page) => ({
6788
+ step: typeof s === "string" ? s : s.id,
6789
+ path: path25,
6790
+ rows: page
6791
+ }), "rows");
6792
+ fromKnowledge = /* @__PURE__ */ __name3((k) => ({
6793
+ knowledge: k
6794
+ }), "fromKnowledge");
6137
6795
  SideEffectsSchema = z6.enum(WORKFLOW_SIDE_EFFECTS);
6138
6796
  JobResourcesSchema = z6.enum(WORKFLOW_JOB_RESOURCES);
6139
6797
  WORKFLOW_ARM_SUBRUN_ID = "$arm";
@@ -6377,78 +7035,6 @@ var init_dist2 = __esm({
6377
7035
  op: "not",
6378
7036
  arg
6379
7037
  }), "not");
6380
- WorkflowTemplateError = class extends Error {
6381
- static {
6382
- __name(this, "WorkflowTemplateError");
6383
- }
6384
- static {
6385
- __name3(this, "WorkflowTemplateError");
6386
- }
6387
- placeholder;
6388
- constructor(message, placeholder) {
6389
- super(message), this.placeholder = placeholder;
6390
- this.name = "WorkflowTemplateError";
6391
- }
6392
- };
6393
- __name(isMapConfigObject, "isMapConfigObject");
6394
- __name3(isMapConfigObject, "isMapConfigObject");
6395
- __name(parseMapConfig, "parseMapConfig");
6396
- __name3(parseMapConfig, "parseMapConfig");
6397
- __name(mapConfigWire, "mapConfigWire");
6398
- __name3(mapConfigWire, "mapConfigWire");
6399
- TEMPLATE_PLACEHOLDER = /\$\{([^}]*)\}/g;
6400
- TEMPLATE_NAMESPACES = [
6401
- "initData",
6402
- "state",
6403
- "requestContext",
6404
- "stepResults"
6405
- ];
6406
- __name(describeBadPlaceholder, "describeBadPlaceholder");
6407
- __name3(describeBadPlaceholder, "describeBadPlaceholder");
6408
- __name(parseTemplatePlaceholder, "parseTemplatePlaceholder");
6409
- __name3(parseTemplatePlaceholder, "parseTemplatePlaceholder");
6410
- __name(traverseMappingPath, "traverseMappingPath");
6411
- __name3(traverseMappingPath, "traverseMappingPath");
6412
- __name(stringifyTemplateValue, "stringifyTemplateValue");
6413
- __name3(stringifyTemplateValue, "stringifyTemplateValue");
6414
- __name(escapeFence, "escapeFence");
6415
- __name3(escapeFence, "escapeFence");
6416
- __name(fenceBlock, "fenceBlock");
6417
- __name3(fenceBlock, "fenceBlock");
6418
- __name(renderTemplate, "renderTemplate");
6419
- __name3(renderTemplate, "renderTemplate");
6420
- __name(resolveDescriptor, "resolveDescriptor");
6421
- __name3(resolveDescriptor, "resolveDescriptor");
6422
- __name(resolveMapping, "resolveMapping");
6423
- __name3(resolveMapping, "resolveMapping");
6424
- fromInit = /* @__PURE__ */ __name3((path25) => ({
6425
- initData: true,
6426
- path: path25
6427
- }), "fromInit");
6428
- fromStep = /* @__PURE__ */ __name3((s, path25 = "") => {
6429
- const idOf = /* @__PURE__ */ __name3((x) => typeof x === "string" ? x : x.id, "idOf");
6430
- return {
6431
- step: Array.isArray(s) ? s.map(idOf) : idOf(s),
6432
- path: path25
6433
- };
6434
- }, "fromStep");
6435
- value = /* @__PURE__ */ __name3((v) => ({
6436
- value: v
6437
- }), "value");
6438
- template = /* @__PURE__ */ __name3((s) => ({
6439
- template: s
6440
- }), "template");
6441
- fromRequest = /* @__PURE__ */ __name3((path25) => ({
6442
- requestContextPath: path25
6443
- }), "fromRequest");
6444
- rows = /* @__PURE__ */ __name3((s, path25, page) => ({
6445
- step: typeof s === "string" ? s : s.id,
6446
- path: path25,
6447
- rows: page
6448
- }), "rows");
6449
- fromKnowledge = /* @__PURE__ */ __name3((k) => ({
6450
- knowledge: k
6451
- }), "fromKnowledge");
6452
7038
  CONTINUED_FAILURE_TAG = "continued_failure";
6453
7039
  CONTINUED_FAILURE_DEFAULT_CODE = "step_failed";
6454
7040
  CONTINUED_FAILURE_OUTPUT_SCHEMA = Object.freeze({
@@ -6574,8 +7160,20 @@ var init_dist2 = __esm({
6574
7160
  n = /* @__PURE__ */ __name3((v) => typeof v === "number" && Number.isFinite(v) ? v : 0, "n");
6575
7161
  __name(runCounts, "runCounts");
6576
7162
  __name3(runCounts, "runCounts");
7163
+ __name(isPricedStepReceipt, "isPricedStepReceipt");
7164
+ __name3(isPricedStepReceipt, "isPricedStepReceipt");
7165
+ __name(receiptEngine, "receiptEngine");
7166
+ __name3(receiptEngine, "receiptEngine");
7167
+ __name(receiptTier, "receiptTier");
7168
+ __name3(receiptTier, "receiptTier");
7169
+ __name(stepBillingView, "stepBillingView");
7170
+ __name3(stepBillingView, "stepBillingView");
6577
7171
  __name(runUsage, "runUsage");
6578
7172
  __name3(runUsage, "runUsage");
7173
+ __name(runBudgetCap, "runBudgetCap");
7174
+ __name3(runBudgetCap, "runBudgetCap");
7175
+ __name(runBudgetRemaining, "runBudgetRemaining");
7176
+ __name3(runBudgetRemaining, "runBudgetRemaining");
6579
7177
  __name(runCancelView, "runCancelView");
6580
7178
  __name3(runCancelView, "runCancelView");
6581
7179
  __name(runWorkspaceView, "runWorkspaceView");
@@ -7060,6 +7658,7 @@ var init_workflow = __esm({
7060
7658
  }, "assertPredicate");
7061
7659
  assertRetry = /* @__PURE__ */ __name((r, id) => {
7062
7660
  if (!r) return;
7661
+ if (typeof r.maxAttempts === "number" && r.maxAttempts > WORKFLOW_RETRY_MAX_ATTEMPTS) throw new LuaWorkflowBuildError("cap-exceeded", `"${id}": ${workflowRetryMaxAttemptsMessage(r.maxAttempts)}`);
7063
7662
  if (r.backoff !== void 0 && !WORKFLOW_RETRY_BACKOFFS.includes(r.backoff)) throw new LuaWorkflowBuildError("backoff-invalid", `"${id}": retry.backoff must be ${WORKFLOW_RETRY_BACKOFFS.map((b) => `'${b}'`).join(" | ")}`);
7064
7663
  if (r.maxBackoffSeconds !== void 0) {
7065
7664
  if (r.backoff !== "exponential") throw new LuaWorkflowBuildError("backoff-invalid", `"${id}": retry.maxBackoffSeconds is only meaningful with backoff:'exponential'`);
@@ -7970,6 +8569,10 @@ async function* parseSseStream(body, signal) {
7970
8569
  if (frame) yield frame;
7971
8570
  }
7972
8571
  } finally {
8572
+ try {
8573
+ await reader.cancel();
8574
+ } catch {
8575
+ }
7973
8576
  try {
7974
8577
  reader.releaseLock();
7975
8578
  } catch {
@@ -7999,6 +8602,7 @@ var init_http_client = __esm({
7999
8602
  "use strict";
8000
8603
  init_dist();
8001
8604
  init_auth_error();
8605
+ init_cli_error();
8002
8606
  init_lua_fetch();
8003
8607
  init_request_credential();
8004
8608
  DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
@@ -8062,7 +8666,7 @@ var init_http_client = __esm({
8062
8666
  if (AuthenticationError.isAuthenticationError(error)) {
8063
8667
  throw error;
8064
8668
  }
8065
- if (error instanceof Error && error.message.startsWith("Access denied (403)")) {
8669
+ if (isAccessDeniedError(error)) {
8066
8670
  throw error;
8067
8671
  }
8068
8672
  if (error instanceof DOMException && error.name === "AbortError") {
@@ -8110,8 +8714,11 @@ var init_http_client = __esm({
8110
8714
  }
8111
8715
  if (response.status === 403) {
8112
8716
  const detail = errorData.message || "You do not have permission to access this resource.";
8113
- throw new Error(`Access denied (403): ${detail}
8114
- Check that your Lua login has access to this agent or organization.`);
8717
+ throw new CliError("forbidden", `Access denied (403): ${detail}`, {
8718
+ exitCode: CLI_EXIT.FORBIDDEN,
8719
+ statusCode: 403,
8720
+ hint: "Check that your Lua login has access to this agent or organization."
8721
+ });
8115
8722
  }
8116
8723
  return {
8117
8724
  success: false,
@@ -9379,6 +9986,7 @@ var init_base_handler = __esm({
9379
9986
  init_bundle_upload();
9380
9987
  init_semver();
9381
9988
  init_auth_error();
9989
+ init_cli_error();
9382
9990
  DEFAULT_VERSION = SKILL_DEFAULTS.VERSION;
9383
9991
  BaseVersionedHandler = class {
9384
9992
  static {
@@ -9460,7 +10068,7 @@ var init_base_handler = __esm({
9460
10068
  serverItems
9461
10069
  };
9462
10070
  } catch (error) {
9463
- if (AuthenticationError.isAuthenticationError(error)) throw error;
10071
+ if (AuthenticationError.isAuthenticationError(error) || isAccessDeniedError(error)) throw error;
9464
10072
  return {
9465
10073
  serverItems: null,
9466
10074
  fetchError: error instanceof Error ? error.message : String(error)
@@ -9490,13 +10098,24 @@ var init_base_handler = __esm({
9490
10098
  }
9491
10099
  const yamlItems = this.getFromYaml(config);
9492
10100
  const { yamlById, yamlByName, serverByName } = this.buildMaps(serverData.serverItems, yamlItems);
10101
+ const idField = this.yamlConfig.idField;
10102
+ const manifestNames = manifest ? new Set(getPrimitivesByKind(manifest, this.kind).map((p) => p.name)) : /* @__PURE__ */ new Set();
10103
+ for (const stale of this.staleYamlRows(yamlItems, serverData.serverItems, manifestNames)) {
10104
+ const idx = yamlItems.indexOf(stale);
10105
+ if (idx < 0) continue;
10106
+ const { [idField]: goneId, ...rest } = stale;
10107
+ yamlItems[idx] = rest;
10108
+ yamlUpdated = true;
10109
+ const msg = `\u2139\uFE0F ${this.displayName} "${stale.name}" (${goneId}) no longer exists on the server \u2014 re-registering it`;
10110
+ messages.push(msg);
10111
+ console.log(msg);
10112
+ }
9493
10113
  const orphans = serverData.serverItems.filter((item) => {
9494
10114
  const id = item.id;
9495
10115
  const name = item.name;
9496
10116
  return !yamlById.has(id) && !yamlByName.has(name) && this.isActive(item) && this.shouldConsiderForOrphan(item);
9497
10117
  });
9498
10118
  if (orphans.length > 0) {
9499
- const idField = this.yamlConfig.idField;
9500
10119
  const stubs = orphans.map((item) => this.cleanItem({
9501
10120
  name: item.name,
9502
10121
  version: this.getActiveVersion(item) || DEFAULT_VERSION,
@@ -9539,6 +10158,7 @@ var init_base_handler = __esm({
9539
10158
  console.log(`\u2705 Server ${this.displayNamePlural} and YAML are fully in sync`);
9540
10159
  }
9541
10160
  } catch (error) {
10161
+ if (AuthenticationError.isAuthenticationError(error) || isAccessDeniedError(error)) throw error;
9542
10162
  console.error(`\u274C Error syncing server ${this.displayNamePlural}:`, error);
9543
10163
  }
9544
10164
  return {
@@ -9595,6 +10215,7 @@ var init_base_handler = __esm({
9595
10215
  console.error(` \u274C Failed to create "${item.name}" - no ID returned`);
9596
10216
  }
9597
10217
  } catch (error) {
10218
+ if (AuthenticationError.isAuthenticationError(error) || isAccessDeniedError(error)) throw error;
9598
10219
  console.error(` \u274C Failed to create "${item.name}": ${error instanceof Error ? error.message : error}`);
9599
10220
  }
9600
10221
  }
@@ -9729,6 +10350,15 @@ var init_base_handler = __esm({
9729
10350
  getItemId(item) {
9730
10351
  return item[this.yamlConfig.idField] || "";
9731
10352
  }
10353
+ /**
10354
+ * LUA-750: the yaml rows whose server id is gone (deleted server-side) and which `applySyncToYaml` should
10355
+ * re-register. Default none — a handler whose `fetchFromServer` lists EVERY live row of its kind for the
10356
+ * agent overrides this (a kind whose list omits inactive rows must not, or it would duplicate them).
10357
+ * `manifestNames` is the set of primitives in local code: only those are worth re-creating.
10358
+ */
10359
+ staleYamlRows(_yamlItems, _serverItems, _manifestNames) {
10360
+ return [];
10361
+ }
9732
10362
  buildMaps(serverItems, yamlItems) {
9733
10363
  const yamlById = /* @__PURE__ */ new Map();
9734
10364
  const yamlByName = /* @__PURE__ */ new Map();
@@ -9818,7 +10448,7 @@ var init_base_handler = __esm({
9818
10448
  serverItems
9819
10449
  };
9820
10450
  } catch (error) {
9821
- if (AuthenticationError.isAuthenticationError(error)) throw error;
10451
+ if (AuthenticationError.isAuthenticationError(error) || isAccessDeniedError(error)) throw error;
9822
10452
  return {
9823
10453
  serverItems: null,
9824
10454
  fetchError: error instanceof Error ? error.message : String(error)
@@ -9887,6 +10517,7 @@ var init_base_handler = __esm({
9887
10517
  console.log(`\u2705 Server ${this.displayNamePlural} and YAML are fully in sync`);
9888
10518
  }
9889
10519
  } catch (error) {
10520
+ if (AuthenticationError.isAuthenticationError(error) || isAccessDeniedError(error)) throw error;
9890
10521
  console.error(`\u274C Error syncing server ${this.displayNamePlural}:`, error);
9891
10522
  }
9892
10523
  return {
@@ -10697,46 +11328,48 @@ async function withErrorHandling(commandFn, commandName, opts) {
10697
11328
  });
10698
11329
  await shutdownAnalytics();
10699
11330
  process.exit(0);
11331
+ return void 0;
11332
+ }
11333
+ if (isHandledCliError(error)) {
11334
+ trackCommand({
11335
+ commandName,
11336
+ success: false,
11337
+ durationMs: Date.now() - startTime,
11338
+ cliVersion: getCliVersion(),
11339
+ error: error.message,
11340
+ properties: {
11341
+ is_auth_error: AuthenticationError.isAuthenticationError(error.original),
11342
+ nested: true
11343
+ }
11344
+ });
11345
+ await showUpdateWarningIfNeeded(versionCheckPromise);
11346
+ await shutdownAnalytics();
11347
+ throw error;
10700
11348
  }
10701
11349
  trackCommand({
10702
11350
  commandName,
10703
11351
  success: false,
10704
11352
  durationMs: Date.now() - startTime,
10705
11353
  cliVersion: getCliVersion(),
10706
- error: error.message,
11354
+ error: error?.message,
10707
11355
  properties: {
10708
11356
  is_auth_error: AuthenticationError.isAuthenticationError(error)
10709
11357
  }
10710
11358
  });
10711
- if (AuthenticationError.isAuthenticationError(error)) {
10712
- console.error(`
10713
- \u274C ${error.message}`);
10714
- if (!error.suppressDefaultRemediation) {
10715
- if (error.reason === "no_agent_access") {
10716
- console.error("\n Your API key is valid, but it does not have access to the agentId\n configured in lua.skill.yaml. This usually means:\n\n \u2022 The agentId belongs to a different account or organization\n \u2022 The agent was deleted, transferred, or you lost access to it\n \u2022 You're using a lua.skill.yaml copied from another project\n\n Check the configured agent and switch if needed:\n\n \u279C lua agents (list agents you have access to)\n \u279C lua init (re-select the agent for this project)\n");
10717
- } else {
10718
- console.error("\n Re-authenticate or check your API key:\n\n \u279C lua auth configure\n \u279C https://admin.heylua.ai\n");
10719
- }
10720
- } else {
10721
- console.error("");
10722
- }
10723
- await showUpdateWarningIfNeeded(versionCheckPromise);
10724
- await shutdownAnalytics();
10725
- process.exit(1);
10726
- }
10727
- console.error(`\u274C Error during ${commandName}:`, error.message);
10728
- if (opts?.onError && !hintsDisabled()) {
11359
+ let extraHint;
11360
+ if (opts?.onError && !hintsDisabled() && !AuthenticationError.isAuthenticationError(error)) {
10729
11361
  try {
10730
- const hint = opts.onError(error);
10731
- if (hint) {
10732
- console.error(`\u{1F4A1} ${hint}`);
10733
- }
11362
+ extraHint = opts.onError(error) ?? void 0;
10734
11363
  } catch {
10735
11364
  }
10736
11365
  }
11366
+ const reported = reportCliError(error, {
11367
+ extraHint
11368
+ });
11369
+ process.exitCode = reported.exitCode;
10737
11370
  await showUpdateWarningIfNeeded(versionCheckPromise);
10738
11371
  await shutdownAnalytics();
10739
- throw new Error(`Error during ${commandName}: ${error.message}`);
11372
+ throw new HandledCliError(reported.message, reported.exitCode, error);
10740
11373
  }
10741
11374
  }
10742
11375
  function clearPromptLines(count = 1) {
@@ -10755,6 +11388,7 @@ var init_cli = __esm({
10755
11388
  "src/utils/cli.ts"() {
10756
11389
  "use strict";
10757
11390
  init_auth_error();
11391
+ init_cli_error();
10758
11392
  init_version_check();
10759
11393
  init_package_root();
10760
11394
  init_analytics();
@@ -10856,6 +11490,15 @@ async function checkApiKey(apiKey) {
10856
11490
  const authApi = new AuthApi(BASE_URLS.API);
10857
11491
  const result = await authApi.checkApiKey(apiKey);
10858
11492
  if (!result.success) {
11493
+ const status = result.error?.statusCode ?? 0;
11494
+ if (status === 0 || status >= 500) {
11495
+ const detail = result.error?.message ?? "no response";
11496
+ throw new CliError("unavailable", `Could not verify the API key \u2014 the Lua API at ${BASE_URLS.API} is unavailable (${detail})`, {
11497
+ exitCode: CLI_EXIT.UNAVAILABLE,
11498
+ statusCode: status,
11499
+ hint: UNAVAILABLE_HINT
11500
+ });
11501
+ }
10859
11502
  throw new AuthenticationError("Invalid API key");
10860
11503
  }
10861
11504
  return result.data;
@@ -10886,6 +11529,7 @@ var init_auth = __esm({
10886
11529
  init_auth_api_service();
10887
11530
  init_constants();
10888
11531
  init_auth_error();
11532
+ init_cli_error();
10889
11533
  __name(getToken, "getToken");
10890
11534
  __name(saveApiKey, "saveApiKey");
10891
11535
  __name(checkApiKey, "checkApiKey");
@@ -10898,31 +11542,27 @@ var init_auth = __esm({
10898
11542
  async function requireAuth() {
10899
11543
  return resolveRequestCredential();
10900
11544
  }
10901
- async function requireAuthOrExit(showProgress = true) {
10902
- const apiKey = await resolveRequestCredential();
10903
- if (showProgress) {
10904
- writeProgress("\u2705 Authenticated");
10905
- }
10906
- return apiKey;
11545
+ async function requireAuthOrExit(_showProgress = true) {
11546
+ return resolveRequestCredential();
10907
11547
  }
10908
11548
  async function initializeCommand(options = {}) {
10909
11549
  const { showProgress = true, validateAuth = false } = options;
10910
11550
  const config = readYamlConfig();
10911
11551
  if (!config) {
10912
- throw new Error("No lua.skill.yaml found. Please run this command from a skill directory.");
11552
+ throw CliError.usage("No lua.skill.yaml found \u2014 run this command from a Lua project directory.", "lua init creates one");
10913
11553
  }
10914
11554
  if (!config.agent?.agentId) {
10915
- throw new Error("Missing agentId in skill configuration. Please run 'lua init' first.");
11555
+ throw CliError.usage("Missing agentId in lua.skill.yaml.", "Run 'lua init' to select the agent for this project");
10916
11556
  }
10917
11557
  if (!config.agent?.orgId) {
10918
- throw new Error("Missing orgId in skill configuration. Please run 'lua init' first.");
11558
+ throw CliError.usage("Missing orgId in lua.skill.yaml.", "Run 'lua init' to select the agent for this project");
10919
11559
  }
10920
11560
  const apiKey = await resolveRequestCredential();
10921
11561
  let userData = void 0;
10922
11562
  if (validateAuth) {
10923
11563
  userData = await checkApiKey(await bearerFor(apiKey));
10924
11564
  }
10925
- if (showProgress) {
11565
+ if (showProgress && validateAuth) {
10926
11566
  writeProgress("\u2705 Authenticated");
10927
11567
  }
10928
11568
  return {
@@ -10940,6 +11580,7 @@ var init_command_utils = __esm({
10940
11580
  init_request_credential();
10941
11581
  init_files();
10942
11582
  init_cli();
11583
+ init_cli_error();
10943
11584
  __name(requireAuth, "requireAuth");
10944
11585
  __name(requireAuthOrExit, "requireAuthOrExit");
10945
11586
  __name(initializeCommand, "initializeCommand");
@@ -24078,7 +24719,10 @@ var init_workflow_api_service = __esm({
24078
24719
  async getRunReplayBundle(runId) {
24079
24720
  return this.httpGet(`${this.runs}/${runId}/journal?format=replay`, await this.auth());
24080
24721
  }
24081
- /** R11 — cancel (`mode:'request'` default; `'force'` after `forceAvailableAt`, 409 `FORCE_NOT_YET_AVAILABLE` before). */
24722
+ /**
24723
+ * R11 — cancel (`mode:'request'` default). A `'force'` before `forceAvailableAt` is the 200 verdict
24724
+ * `{ transitioned:false, nextAction:'cancel_again', forceAvailableAt }` (PRO-979) — never a 409 (LUA-748).
24725
+ */
24082
24726
  async cancelRun(runId, data = {}) {
24083
24727
  return this.httpPost(`${this.runs}/${runId}/cancel`, data, await this.auth());
24084
24728
  }
@@ -24157,6 +24801,14 @@ var init_workflow_api_service = __esm({
24157
24801
  async closeGoal(goalId, data = {}) {
24158
24802
  return this.httpPost(`${this.goals}/${encodeURIComponent(goalId)}/close`, data, await this.auth());
24159
24803
  }
24804
+ /** LUA-749 R63 — edit (objective / judge / cadence / caps / note); a `budget` / `max_runs` park re-arms when the cap clears (`rearmed:true`). */
24805
+ async updateGoal(goalId, data) {
24806
+ return this.httpPatch(`${this.goals}/${encodeURIComponent(goalId)}`, data, await this.auth());
24807
+ }
24808
+ /** LUA-749 R64 — raise `maxTotalCredits` / `maxRuns` (increases only; 400 `GOAL_RAISE_BELOW_SPENT{field, value, spent}`). */
24809
+ async raiseGoal(goalId, data) {
24810
+ return this.httpPost(`${this.goals}/${encodeURIComponent(goalId)}/raise`, data, await this.auth());
24811
+ }
24160
24812
  // ─── Schedules (R4-MF-2 list/get + R28 delete — `/workflows/:agentId/schedules`; LUA-627 stanza) ───
24161
24813
  /** Schedule tree (09 §9.5 — the write-only R27/R56/R28 family plus the R4-MF-2 read rows). */
24162
24814
  get schedules() {
@@ -24170,7 +24822,7 @@ var init_workflow_api_service = __esm({
24170
24822
  async getSchedule(jobId) {
24171
24823
  return this.httpGet(`${this.schedules}/${encodeURIComponent(jobId)}`, await this.auth());
24172
24824
  }
24173
- /** R28 — delete a schedule Job (404 `SCHEDULE_NOT_FOUND`). The CLI refuses a goal-owned job BEFORE this call (`goal_schedule`). */
24825
+ /** R28 — delete a schedule Job (404 `SCHEDULE_NOT_FOUND`). The CLI refuses a LIVE goal's job BEFORE this call (`goal_schedule`); an ended goal's lingering Job is retired here (LUA-760). */
24174
24826
  async deleteSchedule(jobId) {
24175
24827
  return this.httpDelete(`${this.schedules}/${encodeURIComponent(jobId)}`, await this.auth());
24176
24828
  }
@@ -25707,6 +26359,7 @@ __name(configureCommand, "configureCommand");
25707
26359
  // src/commands/init.ts
25708
26360
  import inquirer4 from "inquirer";
25709
26361
  init_command_utils();
26362
+ init_cli_error();
25710
26363
  init_cli();
25711
26364
  import { writeFileSync as writeFileSync7, existsSync as existsSync8 } from "fs";
25712
26365
  import { join as join8 } from "path";
@@ -27942,10 +28595,7 @@ function requireLegacyMutationCredential(credential, mutation) {
27942
28595
  }
27943
28596
  const action = mutation === "create" ? "create a new agent" : "duplicate an agent";
27944
28597
  const message = `Scoped CLI credentials cannot ${action}. Use your renewable first-party session in the Lua dashboard, then rerun \`lua init --agent-id <id>\`.`;
27945
- writeError(`
27946
- \u274C ${message}`);
27947
- writeInfo(" Dashboard: https://admin.heylua.ai\n");
27948
- throw new Error(message);
28598
+ throw CliError.forbidden(message, "Dashboard: https://admin.heylua.ai");
27949
28599
  }
27950
28600
  __name(requireLegacyMutationCredential, "requireLegacyMutationCredential");
27951
28601
  function toInitOrganizations(organizations) {
@@ -28274,9 +28924,8 @@ async function initCommand(options = {}) {
28274
28924
  return withErrorHandling(async () => {
28275
28925
  const mode = determineInitMode(options);
28276
28926
  if (mode.type === "error") {
28277
- console.error(`\u274C ${mode.message}`);
28278
28927
  showInitUsage();
28279
- throw new Error(`${mode.message}`);
28928
+ throw CliError.usage(mode.message);
28280
28929
  }
28281
28930
  const isNonInteractive = mode.type !== "interactive";
28282
28931
  const apiKey = await requireAuthOrExit(false);
@@ -28291,10 +28940,7 @@ async function initCommand(options = {}) {
28291
28940
  const existingAgentId = existingYaml?.agent?.agentId;
28292
28941
  if (existingAgentId) {
28293
28942
  if (isNonInteractive && !options.force) {
28294
- writeError("\n\u26A0\uFE0F Project already initialized with an agent");
28295
- writeInfo(` Current Agent ID: ${existingAgentId}`);
28296
- writeInfo("\n\u{1F4A1} Use --force to override the existing configuration\n");
28297
- throw new Error("Operation failed");
28943
+ throw CliError.usage(`Project already initialized with agent ${existingAgentId}`, "Pass --force to override the existing configuration");
28298
28944
  }
28299
28945
  if (isNonInteractive && options.force) {
28300
28946
  writeInfo("\n\u{1F504} Overriding existing project configuration...\n");
@@ -31809,32 +32455,48 @@ function validateJsonSchema(schema, value3, at = "") {
31809
32455
  return issues;
31810
32456
  }
31811
32457
  __name(validateJsonSchema, "validateJsonSchema");
31812
- function collectLeafPaths(value3, prefix = "") {
31813
- if (Array.isArray(value3)) {
31814
- if (value3.length === 0) return [
31815
- prefix
31816
- ];
31817
- return value3.flatMap((v, i) => collectLeafPaths(v, `${prefix}[${i}]`));
31818
- }
31819
- if (value3 && typeof value3 === "object") {
31820
- const entries = Object.entries(value3);
31821
- if (entries.length === 0) return [
31822
- prefix
31823
- ];
31824
- return entries.flatMap(([k, v]) => collectLeafPaths(v, prefix ? `${prefix}.${k}` : k));
31825
- }
31826
- return [
31827
- prefix
31828
- ];
31829
- }
31830
- __name(collectLeafPaths, "collectLeafPaths");
31831
- function matchesEditablePath2(leafPath, editablePaths) {
31832
- return editablePaths.some((pattern) => {
31833
- const re = new RegExp("^" + pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\\\[\\\*\\\]/g, "\\[\\d+\\]") + "(\\.|\\[|$)");
31834
- return re.test(leafPath);
31835
- });
32458
+ var AGENT_REPLY_ENVELOPE_KEYS = /* @__PURE__ */ new Set([
32459
+ "text",
32460
+ "object"
32461
+ ]);
32462
+ function isAgentReplyEnvelope(reply) {
32463
+ if (typeof reply !== "object" || reply === null || Array.isArray(reply)) return false;
32464
+ const keys = Object.keys(reply);
32465
+ if (keys.length === 0 || !keys.every((k) => AGENT_REPLY_ENVELOPE_KEYS.has(k))) return false;
32466
+ const text = reply.text;
32467
+ return text === void 0 || typeof text === "string";
32468
+ }
32469
+ __name(isAgentReplyEnvelope, "isAgentReplyEnvelope");
32470
+ function coerceAgentStepOutput(schema, reply) {
32471
+ const envelope = typeof reply === "string" ? {
32472
+ text: reply
32473
+ } : reply;
32474
+ if (!isAgentReplyEnvelope(envelope)) return {
32475
+ ok: true,
32476
+ output: reply
32477
+ };
32478
+ const { text, object } = envelope;
32479
+ if (object !== void 0) return {
32480
+ ok: true,
32481
+ output: object
32482
+ };
32483
+ if (!schema) return {
32484
+ ok: true,
32485
+ output: {
32486
+ text
32487
+ }
32488
+ };
32489
+ const extracted = extractSingleJsonValue(text ?? "");
32490
+ if ("reason" in extracted) return {
32491
+ ok: false,
32492
+ reason: extracted.reason
32493
+ };
32494
+ return {
32495
+ ok: true,
32496
+ output: extracted.value
32497
+ };
31836
32498
  }
31837
- __name(matchesEditablePath2, "matchesEditablePath");
32499
+ __name(coerceAgentStepOutput, "coerceAgentStepOutput");
31838
32500
  var armId2 = /* @__PURE__ */ __name((arm) => arm.type === "step" ? arm.step.id : arm.id, "armId");
31839
32501
  var baseIdOf = /* @__PURE__ */ __name((id) => id.replace(/(\[\d+\]|#\d+)$/, ""), "baseIdOf");
31840
32502
  var outputSchemaOf = /* @__PURE__ */ __name((entry) => {
@@ -31966,10 +32628,6 @@ async function runWorkflowLocallyInner(rawOpts) {
31966
32628
  if (!entry.editable && editable.length === 0) {
31967
32629
  throw new WorkflowLocalUsageError("approval-not-editable", `--approve ${id}: the approval is not editable`, id);
31968
32630
  }
31969
- const bad = collectLeafPaths(answer.payload).filter((p) => !matchesEditablePath2(p, editable));
31970
- if (bad.length) {
31971
- throw new WorkflowLocalUsageError("edit-path-not-allowed", `--approve ${id}: path "${bad[0]}" is outside editablePaths [${editable.join(", ")}]`, id);
31972
- }
31973
32631
  if (entry.editedPayloadSchema) {
31974
32632
  const issues = validateJsonSchema(entry.editedPayloadSchema, answer.payload);
31975
32633
  if (issues.length) {
@@ -32300,6 +32958,63 @@ async function runWorkflowLocallyInner(rawOpts) {
32300
32958
  if (!issues.length) return void 0;
32301
32959
  return new StepFailure("input_schema_invalid", `input does not match inputSchema of "${single.step.id}" \u2014 the previous node does not emit what the step declares: ${issues.join("; ")}`);
32302
32960
  }, "stepInputIssue");
32961
+ const agentReply = /* @__PURE__ */ __name(async (row2, single, input) => {
32962
+ const id = row2.stepId;
32963
+ const fixture = opts.fixturesDir ? readFixture(id, row2.attempt) : void 0;
32964
+ if (opts.fixturesDir) return fixture;
32965
+ const prompt = renderPrompt(single);
32966
+ if (opts.agents === "live") {
32967
+ if (!opts.agentInvoke) throw new StepFailure("AGENTS_LIVE_UNAVAILABLE", "--agents live needs a dev API session");
32968
+ const t0 = Date.now();
32969
+ const out = await withWall(id, single, () => opts.agentInvoke(single, prompt, input));
32970
+ record(id, row2.attempt, {
32971
+ prompt,
32972
+ input
32973
+ }, out, Date.now() - t0);
32974
+ return out;
32975
+ }
32976
+ if (single.tier === "job" && jobWallS !== void 0) {
32977
+ const segments = Math.max(1, Math.ceil(jobWallS / WORKFLOW_LOCAL_JOB_SEGMENT_MAX_S));
32978
+ let sessionRef;
32979
+ let elapsedS = 0;
32980
+ for (let s = 1; s <= segments; s++) {
32981
+ const wallS = Math.min(WORKFLOW_LOCAL_JOB_SEGMENT_MAX_S, jobWallS - elapsedS);
32982
+ elapsedS += wallS;
32983
+ clock += wallS * 1e3;
32984
+ const requeued = s < segments;
32985
+ const nextSession = `session-${id}-s${s}`;
32986
+ emit2("step.checkpointed", id, {
32987
+ segment: s,
32988
+ segmentsMax: segments,
32989
+ headSha: `local${s}`,
32990
+ elapsedSeconds: elapsedS,
32991
+ remainingSeconds: Math.max(0, jobWallS - elapsedS),
32992
+ requeued,
32993
+ ...requeued ? {
32994
+ nextSegment: s + 1
32995
+ } : {},
32996
+ ...sessionRef ? {
32997
+ resumedFrom: sessionRef
32998
+ } : {},
32999
+ sessionRef: nextSession,
33000
+ reason: requeued ? "segment_boundary" : "interval"
33001
+ });
33002
+ say(`[${stamp()}] ${id} \xB7 checkpointed \xB7 segment ${s}/${segments}${requeued ? ` \u2192 re-queued (resume ${nextSession})` : ""}`);
33003
+ sessionRef = nextSession;
33004
+ }
33005
+ return {
33006
+ text: `[fake:${single.id}] ${segments} segment(s) over ${jobWallS} s \u2014 ${prompt.slice(0, 40)}`,
33007
+ ...fakeObject(single)
33008
+ };
33009
+ }
33010
+ return {
33011
+ text: `[fake:${single.id}] ${prompt.slice(0, 80)}`,
33012
+ ...fakeObject(single)
33013
+ };
33014
+ }, "agentReply");
33015
+ const fakeObject = /* @__PURE__ */ __name((single) => single.outputSchema ? {
33016
+ object: stubFromSchema(single.outputSchema)
33017
+ } : {}, "fakeObject");
32303
33018
  const executeSingle = /* @__PURE__ */ __name(async (row2, single, input) => {
32304
33019
  const id = row2.stepId;
32305
33020
  const drift = stepInputIssue(single, input);
@@ -32344,57 +33059,12 @@ async function runWorkflowLocallyInner(rawOpts) {
32344
33059
  }
32345
33060
  }
32346
33061
  case "agent": {
32347
- const fixture = opts.fixturesDir ? readFixture(id, row2.attempt) : void 0;
32348
- if (opts.fixturesDir) return fixture;
32349
- const prompt = renderPrompt(single);
32350
- if (opts.agents === "live") {
32351
- if (!opts.agentInvoke) throw new StepFailure("AGENTS_LIVE_UNAVAILABLE", "--agents live needs a dev API session");
32352
- const t0 = Date.now();
32353
- const out = await withWall(id, single, () => opts.agentInvoke(single, prompt, input));
32354
- record(id, row2.attempt, {
32355
- prompt,
32356
- input
32357
- }, out, Date.now() - t0);
32358
- return out;
32359
- }
32360
- if (single.tier === "job" && jobWallS !== void 0) {
32361
- const segments = Math.max(1, Math.ceil(jobWallS / WORKFLOW_LOCAL_JOB_SEGMENT_MAX_S));
32362
- let sessionRef;
32363
- let elapsedS = 0;
32364
- for (let s = 1; s <= segments; s++) {
32365
- const wallS = Math.min(WORKFLOW_LOCAL_JOB_SEGMENT_MAX_S, jobWallS - elapsedS);
32366
- elapsedS += wallS;
32367
- clock += wallS * 1e3;
32368
- const requeued = s < segments;
32369
- const nextSession = `session-${id}-s${s}`;
32370
- emit2("step.checkpointed", id, {
32371
- segment: s,
32372
- segmentsMax: segments,
32373
- headSha: `local${s}`,
32374
- elapsedSeconds: elapsedS,
32375
- remainingSeconds: Math.max(0, jobWallS - elapsedS),
32376
- requeued,
32377
- ...requeued ? {
32378
- nextSegment: s + 1
32379
- } : {},
32380
- ...sessionRef ? {
32381
- resumedFrom: sessionRef
32382
- } : {},
32383
- sessionRef: nextSession,
32384
- reason: requeued ? "segment_boundary" : "interval"
32385
- });
32386
- say(`[${stamp()}] ${id} \xB7 checkpointed \xB7 segment ${s}/${segments}${requeued ? ` \u2192 re-queued (resume ${nextSession})` : ""}`);
32387
- sessionRef = nextSession;
32388
- }
32389
- return {
32390
- text: `[fake:${single.id}] ${segments} segment(s) over ${jobWallS} s \u2014 ${prompt.slice(0, 40)}`,
32391
- object: stubFromSchema(single.outputSchema)
32392
- };
33062
+ const reply = await agentReply(row2, single, input);
33063
+ const coerced = coerceAgentStepOutput(single.outputSchema, reply);
33064
+ if (!coerced.ok) {
33065
+ throw new StepFailure("OUTPUT_SCHEMA_INVALID", `agent reply is not the declared outputSchema object \u2014 ${coerced.reason}`);
32393
33066
  }
32394
- return {
32395
- text: `[fake:${single.id}] ${prompt.slice(0, 80)}`,
32396
- object: stubFromSchema(single.outputSchema)
32397
- };
33067
+ return coerced.output;
32398
33068
  }
32399
33069
  case "tool": {
32400
33070
  if (opts.fixturesDir) return readFixture(id, row2.attempt);
@@ -32649,33 +33319,35 @@ async function runWorkflowLocallyInner(rawOpts) {
32649
33319
  if (armRow && !taken.includes(a)) skipRow(armRow, "branch_not_taken");
32650
33320
  }
32651
33321
  }, "runBranch");
33322
+ const aliasJoin = /* @__PURE__ */ __name((containerId, containerRow, output2) => {
33323
+ stepResults[containerId] = output2;
33324
+ if (containerRow?.node.kind === "foreach") {
33325
+ stepResults[armId2(containerRow.node.entry.step)] = output2;
33326
+ }
33327
+ }, "aliasJoin");
32652
33328
  const runJoin = /* @__PURE__ */ __name((row2) => {
32653
33329
  const container = row2.node.container ?? row2.stepId.replace(/\.join$/, "");
32654
33330
  const containerRow = rows3.get(container);
32655
33331
  const children = row2.dependsOn.filter((d) => d !== container);
32656
33332
  const completed = children.filter((c) => rows3.get(c)?.status === "completed");
33333
+ let output2;
32657
33334
  if (containerRow?.node.kind === "branch") {
32658
- const output2 = completed.length === 1 ? stepResults[completed[0]] : Object.fromEntries(completed.map((c) => [
33335
+ output2 = completed.length === 1 ? stepResults[completed[0]] : Object.fromEntries(completed.map((c) => [
32659
33336
  c,
32660
33337
  stepResults[c]
32661
33338
  ]));
32662
- terminalize(row2, "completed", {
32663
- output: output2
32664
- });
32665
- return;
32666
- }
32667
- if (containerRow?.node.kind === "foreach") {
32668
- terminalize(row2, "completed", {
32669
- output: children.map((c) => stepResults[c])
32670
- });
32671
- return;
32672
- }
32673
- terminalize(row2, "completed", {
32674
- output: Object.fromEntries(completed.map((c) => [
33339
+ } else if (containerRow?.node.kind === "foreach") {
33340
+ output2 = children.map((c) => stepResults[c]);
33341
+ } else {
33342
+ output2 = Object.fromEntries(completed.map((c) => [
32675
33343
  c,
32676
33344
  stepResults[c]
32677
- ]))
33345
+ ]));
33346
+ }
33347
+ terminalize(row2, "completed", {
33348
+ output: output2
32678
33349
  });
33350
+ aliasJoin(container, containerRow, output2);
32679
33351
  }, "runJoin");
32680
33352
  const runForeach = /* @__PURE__ */ __name(async (row2, entry) => {
32681
33353
  const items = inputFor(row2);
@@ -32818,19 +33490,28 @@ async function runWorkflowLocallyInner(rawOpts) {
32818
33490
  say(`[${stamp()}] ${row2.stepId} \xB7 approval \xB7 "${entry.title ?? row2.stepId}" \u2014 awaiting decision`);
32819
33491
  answer = await opts.prompt("approval", row2.stepId, entry);
32820
33492
  }
33493
+ const decidedBy = {
33494
+ id: opts.userId ?? "local",
33495
+ kind: "user"
33496
+ };
32821
33497
  if (answer.decision === "deny") {
32822
- const output2 = {
33498
+ const resumeData2 = {
32823
33499
  approved: false,
32824
- reason: answer.reason
33500
+ ...answer.reason ? {
33501
+ note: answer.reason
33502
+ } : {},
33503
+ editRevision: 0,
33504
+ decidedBy
32825
33505
  };
32826
- if ((entry.onDeny ?? "fail") === "continue") {
33506
+ const output2 = workflowApprovalOutput(resumeData2);
33507
+ if ((entry.onDeny ?? "continue") !== "fail") {
32827
33508
  terminalize(row2, "completed", {
32828
33509
  output: output2
32829
33510
  });
32830
33511
  return;
32831
33512
  }
32832
33513
  row2.error = {
32833
- code: "APPROVAL_DENIED",
33514
+ code: "approval_denied",
32834
33515
  message: answer.reason ?? "denied"
32835
33516
  };
32836
33517
  terminalize(row2, "failed", {
@@ -32842,13 +33523,27 @@ async function runWorkflowLocallyInner(rawOpts) {
32842
33523
  };
32843
33524
  return;
32844
33525
  }
32845
- const payload = answer.payload !== void 0 ? answer.payload : inputFor(row2);
32846
- terminalize(row2, "completed", {
32847
- output: {
32848
- approved: true,
32849
- payload,
32850
- edited: answer.payload !== void 0
33526
+ const edited = answer.payload !== void 0;
33527
+ if (edited) {
33528
+ const shown = inputFor(row2);
33529
+ const editable = entry.editablePaths ?? [];
33530
+ if (editable.length) {
33531
+ const bad = changedPointers(shown, answer.payload).filter((p) => !matchesEditablePath(p, editable));
33532
+ if (bad.length) {
33533
+ throw new WorkflowLocalUsageError("edit-path-not-allowed", `--approve ${row2.stepId}: path "${pointerToDotPath(bad[0])}" changed outside editablePaths [${editable.join(", ")}] (the payload the approval showed: ${JSON.stringify(shown)})`, row2.stepId);
33534
+ }
32851
33535
  }
33536
+ }
33537
+ const resumeData = {
33538
+ approved: true,
33539
+ ...edited ? {
33540
+ editedPayload: answer.payload
33541
+ } : {},
33542
+ editRevision: edited ? 1 : 0,
33543
+ decidedBy
33544
+ };
33545
+ terminalize(row2, "completed", {
33546
+ output: workflowApprovalOutput(resumeData)
32852
33547
  });
32853
33548
  }, "runApproval");
32854
33549
  const runSignal = /* @__PURE__ */ __name(async (row2, entry) => {
@@ -33323,7 +34018,16 @@ function createSandbox(options) {
33323
34018
  },
33324
34019
  // Legacy `env('FOO')` getter — kept for backwards compatibility with skills
33325
34020
  // that use it instead of `process.env.FOO`. Both work.
33326
- env: /* @__PURE__ */ __name((key) => envVars[key], "env"),
34021
+ // LUA-751: `env.template(KEY)` too a graph-form workflow file calls it at MODULE scope (`schedule.timezone:
34022
+ // env.template('FINANCE_TZ')`, the shipped `vendor-invoices`), `env` is not one of the builder names esbuild
34023
+ // injects (`WORKFLOW_SHIM_NAMES`), so the artifact resolves it against THIS global when `lua workflows run`
34024
+ // loads it — and threw `env.template is not a function`. The placeholder is the SDK's (`types/skill.ts`) and the
34025
+ // compiler's tier-1 stub's (`graph-serializer.ts`); the local `--env` overlay substitutes it on the graph.
34026
+ env: Object.assign((key) => envVars[key], {
34027
+ template: /* @__PURE__ */ __name((key) => ({
34028
+ __envRef: key
34029
+ }), "template")
34030
+ }),
33327
34031
  BasketStatus,
33328
34032
  OrderStatus,
33329
34033
  AI: {
@@ -33850,9 +34554,23 @@ var WorkflowHandler = class extends BaseVersionedHandler {
33850
34554
  });
33851
34555
  return {
33852
34556
  success: response.success,
33853
- error: response.error?.message
34557
+ error: response.error?.message,
34558
+ statusCode: response.error?.statusCode,
34559
+ code: response.error?.code ?? response.error?.error
33854
34560
  };
33855
34561
  }
34562
+ /**
34563
+ * LUA-750: R1 (`getWorkflows`) lists EVERY non-deleted workflow of the agent, active or not, so a yaml
34564
+ * `workflowId` absent from it is a definition `lua workflows delete` soft-deleted. Only rows in local code (the
34565
+ * manifest) are re-registered; a server-only stub with a dead id is left for the user.
34566
+ */
34567
+ staleYamlRows(yamlItems, serverItems, manifestNames) {
34568
+ const serverIds = new Set(serverItems.map((s) => s?.id));
34569
+ return yamlItems.filter((row2) => {
34570
+ const id = this.getItemId(row2);
34571
+ return !!id && !serverIds.has(id) && manifestNames.has(row2.name);
34572
+ });
34573
+ }
33856
34574
  async publishVersion(apiKey, agentId, entityId, version) {
33857
34575
  const api = this.getApi(apiKey, agentId);
33858
34576
  const response = await api.publishWorkflowVersion(entityId, version);
@@ -34022,9 +34740,10 @@ var syncableHandlers = Object.values(primitiveHandlers);
34022
34740
 
34023
34741
  // src/commands/compile.ts
34024
34742
  init_analytics();
34743
+ init_cli_error();
34025
34744
  async function compileCommand(options) {
34026
34745
  return withErrorHandling(async () => {
34027
- const debugMode = options?.debug || process.env.LUA_DEBUG === "true";
34746
+ const debugMode = options?.debug || debugEnabled();
34028
34747
  const verboseMode = options?.verbose || debugMode;
34029
34748
  const doSync = options?.sync === true;
34030
34749
  const doServerSync = options?.serverSync !== false;
@@ -34095,7 +34814,7 @@ async function compileCommand(options) {
34095
34814
  when: "error"
34096
34815
  });
34097
34816
  }
34098
- throw new Error("Compilation failed \u2014 see errors above.");
34817
+ throw new CliError("compile_failed", "Compilation failed \u2014 see errors above.");
34099
34818
  }
34100
34819
  ensureGitignored(rootDir, "dist-v2/", "dist/");
34101
34820
  if (result.warnings.length > 0) {
@@ -34263,6 +34982,7 @@ function suggestClosest(input, candidates) {
34263
34982
  __name(suggestClosest, "suggestClosest");
34264
34983
 
34265
34984
  // src/utils/aliases.ts
34985
+ init_cli_error();
34266
34986
  function lowerKeys(aliases) {
34267
34987
  const out = {};
34268
34988
  for (const k of Object.keys(aliases)) {
@@ -34687,6 +35407,8 @@ var ALIAS_MAP = {
34687
35407
  "list",
34688
35408
  "get",
34689
35409
  "create",
35410
+ "edit",
35411
+ "raise",
34690
35412
  "pause",
34691
35413
  "resume",
34692
35414
  "close"
@@ -34699,6 +35421,12 @@ var ALIAS_MAP = {
34699
35421
  new: "create",
34700
35422
  add: "create",
34701
35423
  set: "create",
35424
+ // LUA-749: `edit` (R63) / `raise` (R64)
35425
+ update: "edit",
35426
+ patch: "edit",
35427
+ change: "edit",
35428
+ bump: "raise",
35429
+ increase: "raise",
34702
35430
  hold: "pause",
34703
35431
  stop: "pause",
34704
35432
  unpause: "resume",
@@ -35068,11 +35796,9 @@ function validateOrSuggest(group, input, opts) {
35068
35796
  }
35069
35797
  const suggestion = suggestClosest(normalized ?? input, candidates);
35070
35798
  if (suggestion) {
35071
- throw new Error(`lua: "${input}" is not a valid ${group} value. Did you mean "${suggestion}"?
35072
- Valid values: ${candidates.join(", ")}`);
35799
+ throw CliError.usage(`lua: "${input}" is not a valid ${group} value. Did you mean "${suggestion}"?`, `Valid values: ${candidates.join(", ")}`);
35073
35800
  }
35074
- throw new Error(`lua: "${input}" is not a valid ${group} value.
35075
- Valid values: ${candidates.join(", ")}`);
35801
+ throw CliError.usage(`lua: "${input}" is not a valid ${group} value.`, `Valid values: ${candidates.join(", ")}`);
35076
35802
  }
35077
35803
  __name(validateOrSuggest, "validateOrSuggest");
35078
35804
 
@@ -35080,6 +35806,27 @@ __name(validateOrSuggest, "validateOrSuggest");
35080
35806
  import fs17 from "fs";
35081
35807
  import path23 from "path";
35082
35808
  init_cli();
35809
+
35810
+ // src/utils/stdout-redirect.ts
35811
+ async function withStdoutToStderr(active, fn) {
35812
+ if (!active) return fn();
35813
+ const originalWrite = process.stdout.write;
35814
+ const originalLog = console.log;
35815
+ const originalInfo = console.info;
35816
+ process.stdout.write = (chunk, ...rest) => process.stderr.write(chunk, ...rest);
35817
+ console.log = (...args2) => console.error(...args2);
35818
+ console.info = (...args2) => console.error(...args2);
35819
+ try {
35820
+ return await fn();
35821
+ } finally {
35822
+ process.stdout.write = originalWrite;
35823
+ console.log = originalLog;
35824
+ console.info = originalInfo;
35825
+ }
35826
+ }
35827
+ __name(withStdoutToStderr, "withStdoutToStderr");
35828
+
35829
+ // src/commands/workflow-local-run.ts
35083
35830
  init_command_utils();
35084
35831
  init_artifact_loader();
35085
35832
  init_types();
@@ -35094,6 +35841,7 @@ __name(runIdOf, "runIdOf");
35094
35841
  init_dist2();
35095
35842
 
35096
35843
  // src/utils/workflow-script-local.ts
35844
+ init_dist();
35097
35845
  function fakeCompletionFor(intent) {
35098
35846
  const env = intent.argsEnvelope ?? {};
35099
35847
  switch (intent.kind) {
@@ -35115,10 +35863,14 @@ function fakeCompletionFor(intent) {
35115
35863
  case "approval":
35116
35864
  return {
35117
35865
  status: "completed",
35118
- output: {
35866
+ output: workflowApprovalOutput({
35119
35867
  approved: true,
35120
- decision: "approve"
35121
- }
35868
+ editRevision: 0,
35869
+ decidedBy: {
35870
+ id: "local",
35871
+ kind: "user"
35872
+ }
35873
+ })
35122
35874
  };
35123
35875
  case "waitForSignal":
35124
35876
  case "signal":
@@ -35269,8 +36021,426 @@ async function runScriptWorkflowLocally(opts, runner) {
35269
36021
  __name(runScriptWorkflowLocally, "runScriptWorkflowLocally");
35270
36022
 
35271
36023
  // src/utils/workflow-script-replay.ts
36024
+ init_dist4();
35272
36025
  import { createRequire as createRequire2 } from "module";
35273
36026
  import vm5 from "vm";
36027
+ import { createHash as createHash7, randomUUID as randomUUID4 } from "crypto";
36028
+
36029
+ // ../sandbox-runtime/dist/workflow-script-wrapper.mjs
36030
+ var __defProp7 = Object.defineProperty;
36031
+ var __name7 = /* @__PURE__ */ __name((target, value3) => __defProp7(target, "name", { value: value3, configurable: true }), "__name");
36032
+ var WORKFLOW_SCRIPT_RUNTIME_KEY = "__luaWorkflowScriptRuntime";
36033
+ var WORKFLOW_SCRIPT_PROTOCOL_VERSION = 1;
36034
+ var WORKFLOW_SCRIPT_DEFAULT_CAPS = {
36035
+ maxOutstanding: 64,
36036
+ maxEffects: 1e3,
36037
+ maxForeachItems: 4096,
36038
+ scriptWallMs: 3e4,
36039
+ argsMaxBytes: 32 * 1024,
36040
+ stepOutputMaxBytes: 256 * 1024,
36041
+ stepsPerTick: 64,
36042
+ logCap: 1e3,
36043
+ phaseCap: 64,
36044
+ envCap: 4096
36045
+ };
36046
+ var WORKFLOW_SCRIPT_REPLAY_SOURCE = String.raw`
36047
+ (function __luaWorkflowScriptReplay(__rt, __input, __bodyFactory) {
36048
+ 'use strict';
36049
+ var setImmediateReal = __rt.setImmediate, nowReal = __rt.now, randomReal = __rt.random, uuidReal = __rt.uuid, sha256Hex = __rt.sha256Hex;
36050
+ var caps = __input.caps || {};
36051
+ var MAX_OUTSTANDING = caps.maxOutstanding || 64;
36052
+ var MAX_EFFECTS = caps.maxEffects || 1000;
36053
+ var MAX_FOREACH = caps.maxForeachItems || 4096;
36054
+ var ARGS_MAX = caps.argsMaxBytes || 32768;
36055
+ var STEP_OUT_MAX = caps.stepOutputMaxBytes || 262144;
36056
+ var STEPS_PER_TICK = caps.stepsPerTick || 64;
36057
+ var LOG_CAP = caps.logCap || 1000, PHASE_CAP = caps.phaseCap || 64, ENV_CAP = caps.envCap || 4096;
36058
+ var INLINE_KINDS = { memo: 1, env: 1, log: 1, phase: 1, budget: 1 };
36059
+ var pendingCount = typeof __input.pendingCount === 'number' ? __input.pendingCount : 0;
36060
+
36061
+ // --- canonicalJson + callHash (04 §4.3.4) — byte-identical to lua-core's reference ---
36062
+ function canonicalJson(v) {
36063
+ if (v === null || typeof v === 'number' || typeof v === 'boolean' || typeof v === 'string') return JSON.stringify(v);
36064
+ if (Array.isArray(v)) { var parts = []; for (var i = 0; i < v.length; i++) parts.push(v[i] === undefined ? 'null' : canonicalJson(v[i])); return '[' + parts.join(',') + ']'; }
36065
+ if (typeof v === 'object') {
36066
+ var keys = Object.keys(v).filter(function (k) { return v[k] !== undefined; }).sort();
36067
+ return '{' + keys.map(function (k) { return JSON.stringify(k) + ':' + canonicalJson(v[k]); }).join(',') + '}';
36068
+ }
36069
+ return 'null';
36070
+ }
36071
+ function callHash(kind, argsForHash) { return 'sha256-cj1:' + sha256Hex(kind + '\n' + canonicalJson(argsForHash)); }
36072
+ function byteSize(v) { try { return JSON.stringify(v === undefined ? null : v).length; } catch (e) { return Infinity; } }
36073
+ function typedError(code, message, extra) { var e = new Error(message || code); e.code = code; if (extra) Object.assign(e, extra); return e; }
36074
+ function errShape(e) {
36075
+ if (e && typeof e === 'object') return { code: String(e.code || 'SCRIPT_THREW'), message: String(e.message || e).slice(0, 1024) };
36076
+ return { code: 'SCRIPT_THREW', message: String(e).slice(0, 1024) };
36077
+ }
36078
+
36079
+ // --- state (04 §4.3.5) ---
36080
+ var journalBySeq = {};
36081
+ var journal = Array.isArray(__input.journal) ? __input.journal : [];
36082
+ for (var j = 0; j < journal.length; j++) journalBySeq[journal[j].seq] = journal[j];
36083
+ var nextSeq = 0, cancelling = false, divergence = null, bailed = null;
36084
+ var issuedCount = 0, intents = [], pending = {};
36085
+ var stepCount = 0, logCount = 0, phaseCount = 0, envCount = 0, currentPhase = undefined;
36086
+ var narration = [];
36087
+ var inClosure = false;
36088
+
36089
+ function drain() { return new Promise(function (r) { setImmediateReal(r); }); }
36090
+ function never() { return new Promise(function () {}); }
36091
+ function deferred() { var d = {}; d.promise = new Promise(function (res, rej) { d.resolve = res; d.reject = rej; }); d.promise.catch(function () {}); return d; }
36092
+ function abort(d) { if (!divergence) divergence = d; }
36093
+
36094
+ // §4.3.5 issue(call): sync return for inline kinds, a promise for async kinds.
36095
+ function issue(call) {
36096
+ if (inClosure) throw typedError('SCRIPT_STEP_THREW', 'host call inside step()');
36097
+ if (divergence) { if (call.settle === 'inline') throw typedError('JOURNAL_DIVERGENCE', 'aborted'); return never(); }
36098
+ var seq = nextSeq;
36099
+ var entry = journalBySeq[seq];
36100
+ if (!entry && cancelling) throw typedError('RUN_CANCELLED', 'RUN_CANCELLED: the run is cancelling; no new intents may be issued');
36101
+ if (!entry && seq >= MAX_EFFECTS) { abort({ reason: 'effect_cap', seq: seq }); throw typedError('EFFECT_CAP_EXCEEDED', 'EFFECT_CAP_EXCEEDED: more than ' + MAX_EFFECTS + ' effects in one run'); }
36102
+ if (call.argsEnvelope !== undefined && byteSize(call.argsEnvelope) > ARGS_MAX) throw typedError('ARGS_TOO_LARGE', 'ARGS_TOO_LARGE: argsEnvelope over ' + ARGS_MAX + ' bytes');
36103
+ nextSeq++;
36104
+ issuedCount++;
36105
+ var hash = callHash(call.kind, call.argsForHash);
36106
+ if (entry) {
36107
+ if (entry.callHash !== hash) {
36108
+ abort({ reason: 'hash_mismatch', seq: seq, expected: entry.callHash, got: hash });
36109
+ if (call.settle === 'inline') throw typedError('JOURNAL_DIVERGENCE', 'hash mismatch at seq ' + seq);
36110
+ return never();
36111
+ }
36112
+ if (entry.settle === 'inline') {
36113
+ if (call.settlesAsPromise) { var d0 = deferred(); if (entry.status === 'failed' || entry.error) d0.reject(typedError((entry.error && entry.error.code) || 'MEMO_THREW', entry.error && entry.error.message)); else d0.resolve(entry.output); return d0.promise; }
36114
+ if (entry.error) throw typedError(entry.error.code, entry.error.message);
36115
+ return entry.output;
36116
+ }
36117
+ if (entry.status === 'pending') return never();
36118
+ var d = deferred(); pending[seq] = { d: d, kind: call.kind }; return d.promise;
36119
+ }
36120
+ // Fresh call — no journal row.
36121
+ if (call.settle === 'inline') {
36122
+ if (call.computeAsync) {
36123
+ var d1 = deferred();
36124
+ var started = nowReal();
36125
+ // memo/step(): host members are throwing stubs for the closure's
36126
+ // synchronous extent (05 §5.7.4 "effects are issued by the script,
36127
+ // never by a step"); the awaited tail cannot reach a timer or fetch.
36128
+ Promise.resolve().then(function () { if (call.closure) inClosure = true; try { return call.computeAsync(); } finally { inClosure = false; } }).then(function (out) {
36129
+ var rec = { seq: seq, kind: call.kind, settle: 'inline', callHash: hash, argsEnvelope: call.argsEnvelope, status: 'completed', output: out === undefined ? null : out, durationMs: nowReal() - started };
36130
+ if (call.narration) rec.narration = call.narration;
36131
+ if (byteSize(out) > STEP_OUT_MAX) { rec.status = 'failed'; delete rec.output; rec.error = { code: 'OUTPUT_TOO_LARGE', message: 'inline result over ' + STEP_OUT_MAX + ' bytes' }; intents.push(rec); d1.reject(typedError('OUTPUT_TOO_LARGE', rec.error.message)); return; }
36132
+ intents.push(rec); d1.resolve(out);
36133
+ }, function (err) {
36134
+ var es = errShape(err); es = call.kind === 'code' ? { code: 'SCRIPT_STEP_THREW', message: es.message } : { code: 'MEMO_THREW', message: es.message };
36135
+ var rec = { seq: seq, kind: call.kind, settle: 'inline', callHash: hash, argsEnvelope: call.argsEnvelope, status: 'failed', error: es, durationMs: nowReal() - started };
36136
+ if (call.narration) rec.narration = call.narration;
36137
+ intents.push(rec); d1.reject(typedError(es.code, es.message));
36138
+ });
36139
+ return d1.promise;
36140
+ }
36141
+ var output = call.compute ? call.compute() : undefined;
36142
+ var rec2 = { seq: seq, kind: call.kind, settle: 'inline', callHash: hash, argsEnvelope: call.argsEnvelope, status: 'completed', output: output === undefined ? null : output };
36143
+ if (call.narration) rec2.narration = call.narration;
36144
+ intents.push(rec2);
36145
+ return output;
36146
+ }
36147
+ var asyncRec = { seq: seq, kind: call.kind, settle: 'async', callHash: hash, argsEnvelope: call.argsEnvelope };
36148
+ if (call.narration) asyncRec.narration = call.narration;
36149
+ if (call.role !== undefined) asyncRec.role = call.role;
36150
+ if (call.model !== undefined) asyncRec.model = call.model;
36151
+ intents.push(asyncRec);
36152
+ var outstanding = 0; for (var q = 0; q < intents.length; q++) if (intents[q].settle === 'async') outstanding++;
36153
+ if (outstanding > MAX_OUTSTANDING) {
36154
+ // Thrown to the script with NO seq consumed (deterministic: the same
36155
+ // prefix always trips at the same call) — the over-cap intent is never proposed.
36156
+ intents.pop(); nextSeq--; issuedCount--;
36157
+ throw typedError('OUTSTANDING_INTENTS_EXCEEDED', 'OUTSTANDING_INTENTS_EXCEEDED: more than ' + MAX_OUTSTANDING + ' unsettled intents in one tick');
36158
+ }
36159
+ return never();
36160
+ }
36161
+
36162
+ function narr(opts) { var n = {}; if (opts && opts.label) n.label = String(opts.label); var ph = (opts && opts.phase) || currentPhase; if (ph) n.phase = String(ph); return (n.label || n.phase) ? n : undefined; }
36163
+ function resolveModel(opts) {
36164
+ if (opts && opts.model) return opts.model;
36165
+ var ph = (opts && opts.phase) || currentPhase; var phases = __input.meta && __input.meta.phases;
36166
+ if (ph && Array.isArray(phases)) for (var i = 0; i < phases.length; i++) if (phases[i] && phases[i].title === ph && phases[i].model) return phases[i].model;
36167
+ return undefined;
36168
+ }
36169
+ function resolveRole(role) {
36170
+ if (role === undefined) return undefined;
36171
+ if (role && typeof role === 'object' && typeof role.ref === 'string') {
36172
+ var keys = Object.keys(role); if (keys.length > 1) throw typedError('EPHEMERAL_ROLE_INVALID', 'role ref mixed with inline keys', { reason: 'ref_and_inline' });
36173
+ var b = __input.roleBindings && __input.roleBindings[role.ref];
36174
+ if (!b) throw typedError('EPHEMERAL_ROLE_INVALID', 'unknown role ref ' + role.ref, { reason: 'ref_unknown' });
36175
+ return b;
36176
+ }
36177
+ if (role && typeof role === 'object') {
36178
+ if (typeof role.instructions === 'string' && role.instructions.length > 4000) throw typedError('EPHEMERAL_ROLE_INVALID', 'role.instructions over 4000 chars', { reason: 'too_long' });
36179
+ if (Array.isArray(role.tools) && role.tools.length > 64) throw typedError('EPHEMERAL_ROLE_INVALID', 'role.tools over 64', { reason: 'too_many_tools' });
36180
+ }
36181
+ return role;
36182
+ }
36183
+ function checkItems(items) { if (!Array.isArray(items)) throw new TypeError('expected an array'); if (items.length > MAX_FOREACH) throw typedError('TOO_MANY_ITEMS', 'foreach_too_many_items', { itemCount: items.length, cap: MAX_FOREACH }); }
36184
+ function thunkOrNull(fn) { try { return Promise.resolve(fn()).catch(function () { return null; }); } catch (e) { return Promise.resolve(null); } }
36185
+
36186
+ var host = {
36187
+ args: __input.args,
36188
+ agent: function (prompt, opts) {
36189
+ var o = opts || {};
36190
+ var role = resolveRole(o.role);
36191
+ if (role !== undefined && o.agentId !== undefined && o.agentId !== '$self') throw typedError('EPHEMERAL_ROLE_INVALID', 'a role step must run on the owner', { reason: 'base_not_self' });
36192
+ var model = resolveModel(o);
36193
+ var afh = { prompt: prompt, agentId: o.agentId, schema: o.schema, model: model, toolScope: o.toolScope, timeoutSeconds: o.timeoutSeconds, data: o.data };
36194
+ if ('role' in o) afh.role = role;
36195
+ var env = { prompt: prompt }; for (var k in o) if (k !== 'role') env[k] = o[k]; if ('role' in o) env.role = role; if (model !== undefined) env.model = model;
36196
+ return issue({ kind: 'agent', settle: 'async', argsForHash: afh, argsEnvelope: env, narration: narr(o), role: ('role' in o) ? role : undefined, model: model });
36197
+ },
36198
+ tool: function (toolId, input, opts) { var o = opts || {}; var env = { toolId: toolId, input: input }; for (var k in o) env[k] = o[k]; return issue({ kind: 'tool', settle: 'async', argsForHash: { toolId: toolId, input: input, timeoutSeconds: o.timeoutSeconds }, argsEnvelope: env, narration: narr(o) }); },
36199
+ shell: function (command, opts) { var o = opts || {}; var env = { command: command }; for (var k in o) env[k] = o[k]; return issue({ kind: 'code', settle: 'async', argsForHash: { command: command, timeoutSeconds: o.timeoutSeconds, workspace: o.workspace, jobResources: o.jobResources }, argsEnvelope: env, narration: narr(o) }); },
36200
+ merge: function (opts) { var o = opts || {}; return issue({ kind: 'merge', settle: 'async', argsForHash: { strategy: o.strategy, onConflict: o.onConflict, arms: o.arms }, argsEnvelope: o, narration: narr(o) }); },
36201
+ workflow: function (nameOrGraph, input, opts) {
36202
+ var o = opts || {};
36203
+ var ref = typeof nameOrGraph === 'string' ? nameOrGraph : 'sha256-cj1:' + sha256Hex(canonicalJson(nameOrGraph && nameOrGraph.definition !== undefined ? { definition: nameOrGraph.definition } : nameOrGraph));
36204
+ return issue({ kind: 'workflow', settle: 'async', argsForHash: { ref: ref, input: input }, argsEnvelope: { workflow: nameOrGraph, input: input, label: o.label, phase: o.phase }, narration: narr(o) });
36205
+ },
36206
+ sleep: function (ms, opts) { var env = { ms: ms }; if (opts && opts.businessHours) env.businessHours = opts.businessHours; return issue({ kind: 'sleep', settle: 'async', argsForHash: { ms: ms }, argsEnvelope: env }); },
36207
+ sleepUntil: function (iso, opts) { var env = { iso: iso }; if (opts) for (var k in opts) env[k] = opts[k]; return issue({ kind: 'sleepUntil', settle: 'async', argsForHash: { iso: iso }, argsEnvelope: env }); },
36208
+ approval: function (req) { var r = req || {}; return issue({ kind: 'approval', settle: 'async', argsForHash: { title: r.title, details: r.details, approver: r.approver, timeoutHours: r.timeoutHours, editable: r.editable }, argsEnvelope: r, narration: narr(r) }); },
36209
+ waitForSignal: function (name, opts) { var o = opts || {}; var env = { name: name }; for (var k in o) env[k] = o[k]; return issue({ kind: 'signal', settle: 'async', argsForHash: { name: name, timeoutHours: o.timeoutHours, acceptedSources: o.acceptedSources }, argsEnvelope: env, narration: narr(o) }); },
36210
+ step: function (label, fn, opts) {
36211
+ var o = opts || {};
36212
+ if (o.timeoutSeconds !== undefined && !(o.timeoutSeconds >= 1 && o.timeoutSeconds <= 600)) throw typedError('SCRIPT_META_INVALID', 'step timeoutSeconds outside 1..600', { reason: 'step-timeout-invalid' });
36213
+ if (cancelling && journalBySeq[nextSeq] === undefined) throw typedError('RUN_CANCELLED', 'RUN_CANCELLED');
36214
+ if (journalBySeq[nextSeq] === undefined) { if (stepCount >= STEPS_PER_TICK) throw typedError('script-step-too-many', 'more than ' + STEPS_PER_TICK + ' step() closures in one tick'); stepCount++; }
36215
+ return issue({ kind: 'code', settle: 'inline', settlesAsPromise: true, closure: true, argsForHash: { label: label, fn: String(fn) }, argsEnvelope: { label: label, timeoutSeconds: o.timeoutSeconds }, computeAsync: fn, narration: { label: String(label) } });
36216
+ },
36217
+ memo: function (key, fn) { return issue({ kind: 'memo', settle: 'inline', settlesAsPromise: true, closure: true, argsForHash: { key: key }, argsEnvelope: { key: key }, computeAsync: fn }); },
36218
+ now: function () { if (envCount++ >= ENV_CAP && journalBySeq[nextSeq] === undefined) throw typedError('ENV_CAP_EXCEEDED', 'ENV_CAP_EXCEEDED'); return issue({ kind: 'env', settle: 'inline', argsForHash: { fn: 'now' }, argsEnvelope: { fn: 'now' }, compute: nowReal }); },
36219
+ random: function () { if (envCount++ >= ENV_CAP && journalBySeq[nextSeq] === undefined) throw typedError('ENV_CAP_EXCEEDED', 'ENV_CAP_EXCEEDED'); return issue({ kind: 'env', settle: 'inline', argsForHash: { fn: 'random' }, argsEnvelope: { fn: 'random' }, compute: randomReal }); },
36220
+ uuid: function () { if (envCount++ >= ENV_CAP && journalBySeq[nextSeq] === undefined) throw typedError('ENV_CAP_EXCEEDED', 'ENV_CAP_EXCEEDED'); return issue({ kind: 'env', settle: 'inline', argsForHash: { fn: 'uuid' }, argsEnvelope: { fn: 'uuid' }, compute: uuidReal }); },
36221
+ log: function (message) {
36222
+ var m = String(message).slice(0, 1024);
36223
+ if (logCount >= LOG_CAP) { if (logCount === LOG_CAP) narration.push({ kind: 'log', message: 'LOG_CAP' }); logCount++; return; }
36224
+ logCount++; narration.push({ kind: 'log', message: m });
36225
+ issue({ kind: 'log', settle: 'inline', argsForHash: { message: m }, argsEnvelope: { message: m }, compute: function () { return undefined; } });
36226
+ },
36227
+ phase: function (title) {
36228
+ var t = String(title);
36229
+ if (phaseCount >= PHASE_CAP) { if (phaseCount === PHASE_CAP) narration.push({ kind: 'phase', message: 'PHASE_CAP' }); phaseCount++; return; }
36230
+ phaseCount++; currentPhase = t; narration.push({ kind: 'phase', title: t });
36231
+ issue({ kind: 'phase', settle: 'inline', argsForHash: { title: t }, argsEnvelope: { title: t }, compute: function () { return undefined; } });
36232
+ },
36233
+ parallel: function (thunks) { checkItems(thunks); var ps = []; for (var i = 0; i < thunks.length; i++) ps.push(thunkOrNull(thunks[i])); return Promise.all(ps); },
36234
+ pipeline: function (items) {
36235
+ checkItems(items); var stages = Array.prototype.slice.call(arguments, 1);
36236
+ return Promise.all(items.map(function (item, i) { var p = Promise.resolve(undefined); stages.forEach(function (st) { p = p.then(function (prev) { return st(prev, item, i); }); }); return p; }));
36237
+ },
36238
+ foreach: function (items, opts, fn) {
36239
+ if (typeof opts === 'function') { fn = opts; opts = {}; }
36240
+ checkItems(items); var o = opts || {};
36241
+ var conc = Math.max(1, Math.min(32, o.concurrency || 8)); var results = new Array(items.length); var next = 0;
36242
+ function worker() { if (next >= items.length) return Promise.resolve(); var i = next++; return Promise.resolve().then(function () { return fn(items[i], i); }).then(function (v) { results[i] = v; }, function () { results[i] = null; }).then(worker); }
36243
+ var ws = []; for (var w = 0; w < Math.min(conc, items.length); w++) ws.push(worker());
36244
+ return Promise.all(ws).then(function () { return results; });
36245
+ },
36246
+ bailRun: function (output) { if (byteSize(output) > 262144) throw typedError('OUTPUT_TOO_LARGE', 'bailRun output over 256 KB'); bailed = { output: output === undefined ? null : output, seq: nextSeq }; throw typedError('__LUA_BAIL_RUN__', 'bailRun'); },
36247
+ artefacts: {
36248
+ put: function (name, data, opts) { var o = opts || {}; var digest = typeof data === 'string' ? sha256Hex(data) : sha256Hex(String(data)); return issue({ kind: 'artefact', settle: 'async', argsForHash: { op: 'put', name: name, sha256: digest, contentType: o.contentType, kind: o.kind, title: o.title, source: o.source }, argsEnvelope: { op: 'put', name: name, sha256: digest, contentType: o.contentType, kind: o.kind, title: o.title, source: o.source, bytes: typeof data === 'string' ? data.length : undefined } }); },
36249
+ get: function (artefactId) { return issue({ kind: 'artefact', settle: 'async', argsForHash: { op: 'get', artefactId: artefactId }, argsEnvelope: { op: 'get', artefactId: artefactId } }); },
36250
+ list: function () { return issue({ kind: 'artefact', settle: 'async', argsForHash: { op: 'list' }, argsEnvelope: { op: 'list' } }); },
36251
+ },
36252
+ };
36253
+
36254
+ // 04 §4.3.5 null-vs-reject table.
36255
+ function settle(p, entry) {
36256
+ if (entry.status === 'cancelled') { cancelling = true; p.d.resolve(null); return; }
36257
+ if (entry.status === 'completed') { p.d.resolve(p.kind === 'sleep' || p.kind === 'sleepUntil' ? undefined : entry.output); return; }
36258
+ switch (p.kind) {
36259
+ case 'agent': case 'tool': case 'code': case 'merge': p.d.resolve(null); return;
36260
+ case 'sleep': case 'sleepUntil': p.d.resolve(undefined); return;
36261
+ case 'approval': case 'signal': p.d.resolve(entry.output === undefined ? null : entry.output); return;
36262
+ default: p.d.reject(typedError((entry.error && entry.error.code) || 'UNKNOWN', (entry.error && entry.error.message) || 'journaled failure'));
36263
+ }
36264
+ }
36265
+
36266
+ var body = { settled: false, resolved: false, value: undefined, error: null };
36267
+ return (async function () {
36268
+ var bodyPromise;
36269
+ try { bodyPromise = Promise.resolve().then(function () { return __bodyFactory(host)(); }); }
36270
+ catch (e) { bodyPromise = Promise.reject(e); }
36271
+ bodyPromise.then(function (v) { body.settled = true; body.resolved = true; body.value = v; }, function (e) { body.settled = true; body.resolved = false; body.error = e; });
36272
+ // round 0
36273
+ await drain();
36274
+ // rounds 1..N in completionSeq order (inline entries never participate)
36275
+ var applied = journal.filter(function (e) { return e.settle === 'async' && e.status !== 'pending'; })
36276
+ .sort(function (a, b) { return (a.completionSeq == null ? Number.MAX_SAFE_INTEGER : a.completionSeq) - (b.completionSeq == null ? Number.MAX_SAFE_INTEGER : b.completionSeq); });
36277
+ for (var i = 0; i < applied.length; i++) {
36278
+ if (divergence || bailed) break;
36279
+ var entry = applied[i];
36280
+ if (entry.seq >= nextSeq) { abort({ reason: 'entry_before_issue', seq: entry.seq }); break; }
36281
+ var p = pending[entry.seq];
36282
+ if (p) { delete pending[entry.seq]; settle(p, entry); }
36283
+ await drain();
36284
+ }
36285
+ if (!divergence && !bailed) for (var k = 0; k < journal.length; k++) if (journal[k].settle === 'async' && journal[k].seq >= nextSeq) { abort({ reason: 'journal_longer_than_script', seq: journal[k].seq }); break; }
36286
+ var base = { __lua_workflow: 'script', intents: intents, issued: issuedCount, narration: narration };
36287
+ if (divergence) {
36288
+ if (divergence.reason === 'effect_cap') return Object.assign(base, { outcome: 'failed', error: { code: 'EFFECT_CAP_EXCEEDED', message: 'EFFECT_CAP_EXCEEDED at seq ' + divergence.seq } });
36289
+ return Object.assign(base, { outcome: 'failed', error: { code: 'JOURNAL_DIVERGENCE', message: 'JOURNAL_DIVERGENCE(' + divergence.reason + ') at seq ' + divergence.seq, divergence: divergence } });
36290
+ }
36291
+ if (bailed) return Object.assign(base, { outcome: 'bailed', output: bailed.output, bailSeq: bailed.seq });
36292
+ if (body.settled) {
36293
+ if (body.resolved) { if (byteSize(body.value) > 262144) return Object.assign(base, { outcome: 'failed', error: { code: 'OUTPUT_TOO_LARGE', message: 'script return value over 256 KB' } }); return Object.assign(base, { outcome: 'done', output: body.value === undefined ? null : body.value }); }
36294
+ var es = errShape(body.error); if (es.code === '__LUA_BAIL_RUN__' && bailed) return Object.assign(base, { outcome: 'bailed', output: bailed.output, bailSeq: bailed.seq });
36295
+ return Object.assign(base, { outcome: 'failed', error: { code: es.code === 'SCRIPT_THREW' || !es.code ? 'SCRIPT_THREW' : es.code, message: es.message } });
36296
+ }
36297
+ var asyncIntents = intents.filter(function (it) { return it.settle === 'async'; });
36298
+ if (asyncIntents.length === 0 && pendingCount === 0 && !journal.some(function (e) { return e.settle === 'async' && e.status === 'pending'; })) {
36299
+ return Object.assign(base, { outcome: 'failed', error: { code: 'ORCHESTRATION_DEADLOCK', message: 'script parked with no intents and no pending effects' } });
36300
+ }
36301
+ return Object.assign(base, { outcome: 'parked', phaseTree: narration.filter(function (n) { return n.kind === 'phase'; }), stateSnapshot: undefined });
36302
+ })();
36303
+ })`;
36304
+ var WORKFLOW_SCRIPT_REPLAY_SOURCES = Object.freeze({
36305
+ [WORKFLOW_SCRIPT_PROTOCOL_VERSION]: WORKFLOW_SCRIPT_REPLAY_SOURCE
36306
+ });
36307
+ var WORKFLOW_SCRIPT_PROTOCOL_VERSIONS = Object.freeze(Object.keys(WORKFLOW_SCRIPT_REPLAY_SOURCES).map(Number).filter((v) => v <= WORKFLOW_SCRIPT_PROTOCOL_VERSION && v >= WORKFLOW_SCRIPT_PROTOCOL_VERSION - 2).sort((a, b) => b - a));
36308
+ var WORKFLOW_SCRIPT_PROTOCOL_INTRODUCED_AT = Object.freeze({
36309
+ 1: "2026-08-30"
36310
+ });
36311
+ function computeWorkflowProtocolShipWindow(input) {
36312
+ const { current } = input;
36313
+ const held = [
36314
+ ...new Set(input.held)
36315
+ ].filter((v) => v <= current).sort((a, b) => b - a);
36316
+ const prev = held.includes(current - 1) ? current - 1 : void 0;
36317
+ const prev2 = held.includes(current - 2) ? current - 2 : void 0;
36318
+ const introduced = Date.parse(input.introducedAt[current] ?? "");
36319
+ const now = input.now ?? Date.now();
36320
+ const ageMs = Number.isFinite(introduced) ? Math.max(0, now - introduced) : 0;
36321
+ if (prev === void 0) {
36322
+ return {
36323
+ versions: [
36324
+ current
36325
+ ],
36326
+ dropped: held.filter((v) => v !== current),
36327
+ state: "single",
36328
+ ageMs
36329
+ };
36330
+ }
36331
+ const elapsed = Number.isFinite(introduced) && ageMs >= input.drainDays * 864e5;
36332
+ if (elapsed && input.stragglers === 0) {
36333
+ return {
36334
+ versions: [
36335
+ current
36336
+ ],
36337
+ dropped: held.filter((v) => v !== current),
36338
+ state: "drained",
36339
+ ageMs
36340
+ };
36341
+ }
36342
+ const versions = [
36343
+ current,
36344
+ prev,
36345
+ ...prev2 !== void 0 ? [
36346
+ prev2
36347
+ ] : []
36348
+ ];
36349
+ return {
36350
+ versions,
36351
+ dropped: held.filter((v) => !versions.includes(v)),
36352
+ state: elapsed ? "blocked" : "draining",
36353
+ ageMs
36354
+ };
36355
+ }
36356
+ __name(computeWorkflowProtocolShipWindow, "computeWorkflowProtocolShipWindow");
36357
+ __name7(computeWorkflowProtocolShipWindow, "computeWorkflowProtocolShipWindow");
36358
+ function workflowScriptReplaySource(journalProtocolVersion) {
36359
+ return WORKFLOW_SCRIPT_PROTOCOL_VERSIONS.includes(journalProtocolVersion) ? WORKFLOW_SCRIPT_REPLAY_SOURCES[journalProtocolVersion] : void 0;
36360
+ }
36361
+ __name(workflowScriptReplaySource, "workflowScriptReplaySource");
36362
+ __name7(workflowScriptReplaySource, "workflowScriptReplaySource");
36363
+ var HOST_BINDINGS = [
36364
+ "args",
36365
+ "agent",
36366
+ "tool",
36367
+ "shell",
36368
+ "merge",
36369
+ "workflow",
36370
+ "sleep",
36371
+ "sleepUntil",
36372
+ "approval",
36373
+ "waitForSignal",
36374
+ "step",
36375
+ "memo",
36376
+ "now",
36377
+ "random",
36378
+ "uuid",
36379
+ "log",
36380
+ "phase",
36381
+ "parallel",
36382
+ "pipeline",
36383
+ "foreach",
36384
+ "bailRun",
36385
+ "artefacts"
36386
+ ];
36387
+ function transformWorkflowScriptModule(script) {
36388
+ return script.replace(/^\s*export\s+const\s+meta\s*=/m, "const meta =");
36389
+ }
36390
+ __name(transformWorkflowScriptModule, "transformWorkflowScriptModule");
36391
+ __name7(transformWorkflowScriptModule, "transformWorkflowScriptModule");
36392
+ function buildWorkflowScriptWrapper(script, input) {
36393
+ const tick = input ?? {};
36394
+ const version = tick.journalProtocolVersion ?? WORKFLOW_SCRIPT_PROTOCOL_VERSION;
36395
+ const replaySource = workflowScriptReplaySource(version);
36396
+ if (replaySource === void 0) {
36397
+ return {
36398
+ code: `
36399
+ {
36400
+ const __platformErr = new Error('[Lua] workflow-script journalProtocolVersion ${JSON.stringify(version)} is outside this runner window ${JSON.stringify(WORKFLOW_SCRIPT_PROTOCOL_VERSIONS)}');
36401
+ __platformErr.code = 'runtime_incompatible';
36402
+ throw __platformErr;
36403
+ }
36404
+ `,
36405
+ contextName: "WorkflowScriptExecutionContext",
36406
+ contextOrigin: "workflow-script-execution",
36407
+ source: "workflow-script",
36408
+ timeoutMs: 6e5
36409
+ };
36410
+ }
36411
+ const bindings = HOST_BINDINGS.map((k) => `${k} = __host.${k}`).join(", ");
36412
+ const code = `
36413
+ const __luaWfRt = globalThis[${JSON.stringify(WORKFLOW_SCRIPT_RUNTIME_KEY)}];
36414
+ delete globalThis[${JSON.stringify(WORKFLOW_SCRIPT_RUNTIME_KEY)}];
36415
+ if (!__luaWfRt || typeof __luaWfRt.setImmediate !== 'function' || typeof __luaWfRt.sha256Hex !== 'function') {
36416
+ const __platformErr = new Error('[Lua] workflow-script runtime handle missing \u2014 the site was dispatched outside the workflows queue set');
36417
+ __platformErr.code = 'PLATFORM_VM_ERROR';
36418
+ throw __platformErr;
36419
+ }
36420
+ const __luaWfInput = ${JSON.stringify(input ?? null)};
36421
+ const __luaWfReplay = ${replaySource};
36422
+ __luaWfReplay(__luaWfRt, __luaWfInput, function (__host) {
36423
+ const ${bindings};
36424
+ return async function __luaWorkflowScriptBody() {
36425
+ ${transformWorkflowScriptModule(script)}
36426
+ };
36427
+ });
36428
+ `;
36429
+ return {
36430
+ code,
36431
+ contextName: "WorkflowScriptExecutionContext",
36432
+ contextOrigin: "workflow-script-execution",
36433
+ source: "workflow-script",
36434
+ // The tick wall is the sum of orchestration (LUA_WF_SCRIPT_WALL_MS, 30 s)
36435
+ // and step() closure walls; the process hard wall stays the site's
36436
+ // LUA_SANDBOX_MAX_EXECUTION_MS (05 §5.7.4 clock 3) — envelope wallMs overrides.
36437
+ timeoutMs: 6e5
36438
+ };
36439
+ }
36440
+ __name(buildWorkflowScriptWrapper, "buildWorkflowScriptWrapper");
36441
+ __name7(buildWorkflowScriptWrapper, "buildWorkflowScriptWrapper");
36442
+
36443
+ // src/utils/workflow-script-replay.ts
35274
36444
  var ReplayRuntimeUnavailableError = class extends Error {
35275
36445
  static {
35276
36446
  __name(this, "ReplayRuntimeUnavailableError");
@@ -35337,7 +36507,59 @@ function compareScriptReplay(bundle, result) {
35337
36507
  };
35338
36508
  }
35339
36509
  __name(compareScriptReplay, "compareScriptReplay");
35340
- function loadLocalScriptRunner(projectDir = process.cwd()) {
36510
+ var QUIET_CONSOLE = {
36511
+ log() {
36512
+ },
36513
+ info() {
36514
+ },
36515
+ warn() {
36516
+ },
36517
+ error() {
36518
+ },
36519
+ debug() {
36520
+ }
36521
+ };
36522
+ function runWrapper(code, sandbox, tick, filename) {
36523
+ const context = vm5.createContext(sandbox);
36524
+ const value3 = vm5.runInContext(code, context, {
36525
+ filename,
36526
+ timeout: Math.max(1e3, tick.caps?.scriptWallMs ?? 3e4)
36527
+ });
36528
+ return Promise.resolve(value3);
36529
+ }
36530
+ __name(runWrapper, "runWrapper");
36531
+ function localScriptRuntimeHandle() {
36532
+ const realSetImmediate = setImmediate;
36533
+ const RealDate = Date;
36534
+ const realRandom = Math.random;
36535
+ return {
36536
+ setImmediate: /* @__PURE__ */ __name((cb) => realSetImmediate(cb), "setImmediate"),
36537
+ now: /* @__PURE__ */ __name(() => RealDate.now(), "now"),
36538
+ random: /* @__PURE__ */ __name(() => realRandom(), "random"),
36539
+ uuid: /* @__PURE__ */ __name(() => randomUUID4(), "uuid"),
36540
+ sha256Hex: /* @__PURE__ */ __name((text) => createHash7("sha256").update(text).digest("hex"), "sha256Hex")
36541
+ };
36542
+ }
36543
+ __name(localScriptRuntimeHandle, "localScriptRuntimeHandle");
36544
+ function bundledScriptRunner() {
36545
+ const runner = /* @__PURE__ */ __name(async (tick) => {
36546
+ const sandbox = createBaseSandboxContext({
36547
+ envVars: {},
36548
+ customConsole: QUIET_CONSOLE,
36549
+ extraGlobals: {
36550
+ ...buildWorkflowScriptGlobals(QUIET_CONSOLE),
36551
+ [WORKFLOW_SCRIPT_RUNTIME_KEY]: localScriptRuntimeHandle()
36552
+ }
36553
+ });
36554
+ const wrapper = buildWorkflowScriptWrapper(tick.script, tick);
36555
+ return runWrapper(wrapper.code, sandbox, tick, "workflow-script-local.js");
36556
+ }, "runner");
36557
+ return Object.assign(runner, {
36558
+ runtime: "bundled"
36559
+ });
36560
+ }
36561
+ __name(bundledScriptRunner, "bundledScriptRunner");
36562
+ function projectScriptRunner(projectDir) {
35341
36563
  let runtime;
35342
36564
  try {
35343
36565
  const req = createRequire2(`${projectDir.replace(/\/$/, "")}/package.json`);
@@ -35348,35 +36570,37 @@ function loadLocalScriptRunner(projectDir = process.cwd()) {
35348
36570
  if (typeof runtime?.createSandboxContext !== "function" || typeof runtime?.buildWorkflowScriptWrapper !== "function") {
35349
36571
  throw new ReplayRuntimeUnavailableError("runtime exports missing");
35350
36572
  }
35351
- return async (tick) => {
35352
- const quiet = {
35353
- log() {
35354
- },
35355
- info() {
35356
- },
35357
- warn() {
35358
- },
35359
- error() {
35360
- },
35361
- debug() {
35362
- }
35363
- };
36573
+ const runner = /* @__PURE__ */ __name(async (tick) => {
35364
36574
  const sandbox = runtime.createSandboxContext({
35365
36575
  siteType: "workflow-script"
35366
- }, void 0, void 0, quiet, void 0, {});
36576
+ }, void 0, void 0, QUIET_CONSOLE, void 0, {});
35367
36577
  const wrapper = runtime.buildWorkflowScriptWrapper(tick.script, tick);
35368
- const context = vm5.createContext(sandbox);
35369
- const value3 = vm5.runInContext(wrapper.code, context, {
35370
- filename: "workflow-script-replay.js",
35371
- timeout: Math.max(1e3, tick.caps?.scriptWallMs ?? 3e4)
35372
- });
35373
- return await value3;
35374
- };
36578
+ return runWrapper(wrapper.code, sandbox, tick, "workflow-script-replay.js");
36579
+ }, "runner");
36580
+ return Object.assign(runner, {
36581
+ runtime: "project"
36582
+ });
36583
+ }
36584
+ __name(projectScriptRunner, "projectScriptRunner");
36585
+ function loadLocalScriptRunner(projectDir = process.cwd(), opts = {}) {
36586
+ const mode = opts.runtime ?? "auto";
36587
+ if (mode === "bundled") return bundledScriptRunner();
36588
+ try {
36589
+ return projectScriptRunner(projectDir);
36590
+ } catch (e) {
36591
+ if (mode === "project" || !(e instanceof ReplayRuntimeUnavailableError)) throw e;
36592
+ return bundledScriptRunner();
36593
+ }
35375
36594
  }
35376
36595
  __name(loadLocalScriptRunner, "loadLocalScriptRunner");
35377
36596
  async function replayScriptRunLocally(bundle, runner = loadLocalScriptRunner()) {
35378
36597
  const result = await runner(bundle.tick);
35379
- return compareScriptReplay(bundle, result);
36598
+ const report = compareScriptReplay(bundle, result);
36599
+ const runtime = runner.runtime;
36600
+ return runtime ? {
36601
+ ...report,
36602
+ runtime
36603
+ } : report;
35380
36604
  }
35381
36605
  __name(replayScriptRunLocally, "replayScriptRunLocally");
35382
36606
  function formatScriptReplayReport(r) {
@@ -35385,6 +36609,9 @@ function formatScriptReplayReport(r) {
35385
36609
  lines.push(`j${row2.seq} \xB7 ${row2.kind} \xB7 recorded=${row2.recordedHash.slice(0, 20)}\u2026 \xB7 local=${(row2.localHash ?? "-").slice(0, 20)}\u2026${row2.diverged ? ` \xB7 DIVERGED (${row2.reason})` : ""}`);
35386
36610
  }
35387
36611
  lines.push(`local outcome: ${r.localOutcome}${r.localError ? ` (${r.localError.code}: ${r.localError.message})` : ""} \xB7 server status: ${r.serverStatus} \xB7 new intents: ${r.newIntents}`);
36612
+ if (r.runtime) {
36613
+ lines.push(`wrapper: ${r.runtime === "project" ? "the project's @lua/sandbox-runtime" : "bundled with this lua-cli"} (LUA-751)`);
36614
+ }
35388
36615
  return lines;
35389
36616
  }
35390
36617
  __name(formatScriptReplayReport, "formatScriptReplayReport");
@@ -35529,15 +36756,15 @@ async function runWorkflowLocalFromProject(name, flags) {
35529
36756
  }, "say");
35530
36757
  if (!flags.noCompile) {
35531
36758
  say("\u{1F4E6} Compiling code first...");
35532
- await compileCommand({
36759
+ await withStdoutToStderr(asJson, () => compileCommand({
35533
36760
  serverSync: false
35534
- });
36761
+ }));
35535
36762
  }
35536
36763
  const manifest = loadManifest();
35537
36764
  const workflows = getPrimitivesByKind(manifest, PrimitiveKind.WORKFLOW);
35538
36765
  if (workflows.length === 0) {
35539
36766
  console.error("\u274C No workflows found in compiled output.");
35540
- console.log("\u{1F4A1} Create a workflow with createWorkflow(...).commit() in your code.");
36767
+ (asJson ? console.error : console.log)("\u{1F4A1} Create a workflow with createWorkflow(...).commit() in your code.");
35541
36768
  return {
35542
36769
  exitCode: 3
35543
36770
  };
@@ -36640,6 +37867,72 @@ init_command_utils();
36640
37867
  init_semver();
36641
37868
  init_auth_error();
36642
37869
  init_constants();
37870
+
37871
+ // src/api/agent-version.api.service.ts
37872
+ init_http_client();
37873
+ var AgentVersionApi = class extends HttpClient {
37874
+ static {
37875
+ __name(this, "AgentVersionApi");
37876
+ }
37877
+ agentId;
37878
+ constructor(baseUrl, credential, agentId) {
37879
+ super(baseUrl, credential);
37880
+ this.agentId = agentId;
37881
+ }
37882
+ get basePath() {
37883
+ return `/developer/agents/${encodeURIComponent(this.agentId)}`;
37884
+ }
37885
+ get authHeader() {
37886
+ return {};
37887
+ }
37888
+ // ---------------------------------------------------------------------------
37889
+ // Version CRUD
37890
+ // ---------------------------------------------------------------------------
37891
+ async createVersion(body) {
37892
+ return this.httpPost(`${this.basePath}/versions`, body, this.authHeader);
37893
+ }
37894
+ async listVersions(query) {
37895
+ const params = new URLSearchParams();
37896
+ if (query?.all !== void 0) params.append("all", String(query.all));
37897
+ if (query?.limit !== void 0) params.append("limit", String(query.limit));
37898
+ if (query?.status !== void 0) params.append("status", query.status);
37899
+ const qs = params.toString();
37900
+ const url = qs ? `${this.basePath}/versions?${qs}` : `${this.basePath}/versions`;
37901
+ return this.httpGet(url, this.authHeader);
37902
+ }
37903
+ async getVersion(version) {
37904
+ return this.httpGet(`${this.basePath}/versions/${version}`, this.authHeader);
37905
+ }
37906
+ async deleteVersion(version) {
37907
+ return this.httpDelete(`${this.basePath}/versions/${version}`, this.authHeader);
37908
+ }
37909
+ // ---------------------------------------------------------------------------
37910
+ // Diff
37911
+ // ---------------------------------------------------------------------------
37912
+ async diffVersions(from, to) {
37913
+ const url = `${this.basePath}/versions/diff?from=${from}&to=${to}`;
37914
+ return this.httpGet(url, this.authHeader);
37915
+ }
37916
+ // ---------------------------------------------------------------------------
37917
+ // Promote
37918
+ // ---------------------------------------------------------------------------
37919
+ async promoteVersion(version) {
37920
+ return this.httpPost(`${this.basePath}/versions/${version}/promote`, {}, this.authHeader);
37921
+ }
37922
+ // ---------------------------------------------------------------------------
37923
+ // Patch commit hash — called by `lua version create` after a successful git
37924
+ // commit + tag to propagate the SHA to the backend AgentVersion record.
37925
+ // Failure is non-fatal; the version snapshot is valid whether or not this
37926
+ // PATCH lands.
37927
+ // ---------------------------------------------------------------------------
37928
+ async patchCommitHash(version, commitHash) {
37929
+ return this.httpPatch(`${this.basePath}/versions/${version}/commit-hash`, {
37930
+ commitHash
37931
+ }, this.authHeader);
37932
+ }
37933
+ };
37934
+
37935
+ // src/commands/push.ts
36643
37936
  init_artifact_loader();
36644
37937
  init_bundle_upload();
36645
37938
  init_types();
@@ -37479,6 +38772,7 @@ var GIT_MESSAGES = {
37479
38772
  var tagName = /* @__PURE__ */ __name((version) => `lua/v${version}`, "tagName");
37480
38773
 
37481
38774
  // src/commands/push-helpers.ts
38775
+ init_cli_error();
37482
38776
  function pickPushAllNextStepVariant(opts) {
37483
38777
  if (opts.pushedSomething && opts.failedCount === 0) return "success";
37484
38778
  if (opts.pushedSomething && opts.failedCount > 0) return "partial";
@@ -37501,6 +38795,15 @@ function formatPushFailureSummary(failedItems) {
37501
38795
  return lines.join("\n");
37502
38796
  }
37503
38797
  __name(formatPushFailureSummary, "formatPushFailureSummary");
38798
+ function stalePushEntryError(handler, name, entityId, serverMessage) {
38799
+ const field = handler.yamlConfig.idField;
38800
+ return new CliError("not_found", `${handler.displayName} "${name}" (${entityId}) no longer exists on the server${serverMessage ? ` \u2014 ${serverMessage}` : ""}`, {
38801
+ exitCode: CLI_EXIT.NOT_FOUND,
38802
+ statusCode: 404,
38803
+ hint: `lua.skill.yaml still holds ${field}: ${entityId} for "${name}". Run \`lua compile\` \u2014 the server sync clears a deleted id and registers the definition afresh \u2014 then push again; or remove the ${field} line by hand.`
38804
+ });
38805
+ }
38806
+ __name(stalePushEntryError, "stalePushEntryError");
37504
38807
 
37505
38808
  // src/commands/push.ts
37506
38809
  function getDeployHint(type) {
@@ -37555,6 +38858,20 @@ async function authenticateOrFail() {
37555
38858
  return requireAuthOrExit(false);
37556
38859
  }
37557
38860
  __name(authenticateOrFail, "authenticateOrFail");
38861
+ async function agentHasVersions() {
38862
+ try {
38863
+ const agentId = readYamlConfig()?.agent?.agentId;
38864
+ if (!agentId) return false;
38865
+ const apiKey = await requireAuthOrExit(false);
38866
+ const res = await new AgentVersionApi(BASE_URLS.API, apiKey, agentId).listVersions({
38867
+ limit: 1
38868
+ });
38869
+ return !!res.success && Array.isArray(res.data) && res.data.length > 0;
38870
+ } catch {
38871
+ return false;
38872
+ }
38873
+ }
38874
+ __name(agentHasVersions, "agentHasVersions");
37558
38875
  async function selectEntityOrFail(items, options, config) {
37559
38876
  const { entityType, nameFields = [
37560
38877
  "id",
@@ -37635,7 +38952,6 @@ async function pushVersionedPrimitive(handler, options = {}) {
37635
38952
  await compileCommand();
37636
38953
  writeSuccess("\u2705 Compilation complete");
37637
38954
  const apiKey = await authenticateOrFail();
37638
- writeSuccess("\u2705 Authentication verified");
37639
38955
  const config = readYamlConfig();
37640
38956
  if (!config?.agent?.agentId) {
37641
38957
  throw new Error("No agent ID found in lua.skill.yaml. Please run 'lua init' first.");
@@ -37685,6 +39001,7 @@ async function pushVersionedPrimitive(handler, options = {}) {
37685
39001
  version: confirmedVersion
37686
39002
  });
37687
39003
  if (!result.success) {
39004
+ if (result.statusCode === 404) throw stalePushEntryError(handler, selected.name, entityId, result.error);
37688
39005
  const isVersionConflict = result.error?.toLowerCase().includes("already exists");
37689
39006
  if (isVersionConflict) {
37690
39007
  writeHintBlock({
@@ -37812,7 +39129,7 @@ async function pushCommand(type, cmdObj) {
37812
39129
  autoDeployNoopWarned: isAutoDeployNoOp
37813
39130
  });
37814
39131
  }
37815
- if (isGranular) {
39132
+ if (isGranular && await agentHasVersions()) {
37816
39133
  writeInfo("\u26A0\uFE0F Granular push is deprecated when agent versioning is on. Use `lua push` (no args) to stage all changes, then `lua version create`.");
37817
39134
  }
37818
39135
  if (type === "all") {
@@ -38091,7 +39408,6 @@ Model: ${model}`);
38091
39408
  }
38092
39409
  }
38093
39410
  const apiKey = await authenticateOrFail();
38094
- writeProgress("\u2705 Authenticated");
38095
39411
  const result = await agentHandler.pushAgentConfig(apiKey, agentId, manifest);
38096
39412
  let productionDeployedWithAutoDeploy = false;
38097
39413
  let personaDeployWasAttempted = false;
@@ -39830,70 +41146,6 @@ async function pushProcessorsToSandbox(apiKey, agentId, manifest, yamlConfig, is
39830
41146
  }
39831
41147
  __name(pushProcessorsToSandbox, "pushProcessorsToSandbox");
39832
41148
 
39833
- // src/api/agent-version.api.service.ts
39834
- init_http_client();
39835
- var AgentVersionApi = class extends HttpClient {
39836
- static {
39837
- __name(this, "AgentVersionApi");
39838
- }
39839
- agentId;
39840
- constructor(baseUrl, credential, agentId) {
39841
- super(baseUrl, credential);
39842
- this.agentId = agentId;
39843
- }
39844
- get basePath() {
39845
- return `/developer/agents/${encodeURIComponent(this.agentId)}`;
39846
- }
39847
- get authHeader() {
39848
- return {};
39849
- }
39850
- // ---------------------------------------------------------------------------
39851
- // Version CRUD
39852
- // ---------------------------------------------------------------------------
39853
- async createVersion(body) {
39854
- return this.httpPost(`${this.basePath}/versions`, body, this.authHeader);
39855
- }
39856
- async listVersions(query) {
39857
- const params = new URLSearchParams();
39858
- if (query?.all !== void 0) params.append("all", String(query.all));
39859
- if (query?.limit !== void 0) params.append("limit", String(query.limit));
39860
- if (query?.status !== void 0) params.append("status", query.status);
39861
- const qs = params.toString();
39862
- const url = qs ? `${this.basePath}/versions?${qs}` : `${this.basePath}/versions`;
39863
- return this.httpGet(url, this.authHeader);
39864
- }
39865
- async getVersion(version) {
39866
- return this.httpGet(`${this.basePath}/versions/${version}`, this.authHeader);
39867
- }
39868
- async deleteVersion(version) {
39869
- return this.httpDelete(`${this.basePath}/versions/${version}`, this.authHeader);
39870
- }
39871
- // ---------------------------------------------------------------------------
39872
- // Diff
39873
- // ---------------------------------------------------------------------------
39874
- async diffVersions(from, to) {
39875
- const url = `${this.basePath}/versions/diff?from=${from}&to=${to}`;
39876
- return this.httpGet(url, this.authHeader);
39877
- }
39878
- // ---------------------------------------------------------------------------
39879
- // Promote
39880
- // ---------------------------------------------------------------------------
39881
- async promoteVersion(version) {
39882
- return this.httpPost(`${this.basePath}/versions/${version}/promote`, {}, this.authHeader);
39883
- }
39884
- // ---------------------------------------------------------------------------
39885
- // Patch commit hash — called by `lua version create` after a successful git
39886
- // commit + tag to propagate the SHA to the backend AgentVersion record.
39887
- // Failure is non-fatal; the version snapshot is valid whether or not this
39888
- // PATCH lands.
39889
- // ---------------------------------------------------------------------------
39890
- async patchCommitHash(version, commitHash) {
39891
- return this.httpPatch(`${this.basePath}/versions/${version}/commit-hash`, {
39892
- commitHash
39893
- }, this.authHeader);
39894
- }
39895
- };
39896
-
39897
41149
  // src/commands/chat.ts
39898
41150
  init_constants();
39899
41151
 
@@ -49002,6 +50254,7 @@ __name(formatSchedule2, "formatSchedule");
49002
50254
 
49003
50255
  // src/commands/workflows.ts
49004
50256
  init_cli();
50257
+ init_cli_error();
49005
50258
  init_constants();
49006
50259
  init_command_utils();
49007
50260
  init_analytics();
@@ -49020,7 +50273,9 @@ var WORKFLOW_EXIT = {
49020
50273
  RUN_CANCELLED: 5,
49021
50274
  RUN_GATED: 6,
49022
50275
  TIMEOUT: 7,
49023
- RUN_PARKED: 8
50276
+ RUN_PARKED: 8,
50277
+ /** No server verdict — 5xx, connection refused, timeout: the global class (`errors/cli.error.ts`). */
50278
+ UNAVAILABLE: CLI_EXIT.UNAVAILABLE
49024
50279
  };
49025
50280
  var TERMINAL_FAILED = /* @__PURE__ */ new Set([
49026
50281
  "failed",
@@ -49218,13 +50473,14 @@ __name(emitJson, "emitJson");
49218
50473
  function apiFailure(ctx, res, verb) {
49219
50474
  emitJson(ctx, res);
49220
50475
  const err = res.error;
49221
- const status = err?.statusCode ?? 0;
50476
+ const status = err?.statusCode;
49222
50477
  const code = err?.code ?? err?.error;
49223
50478
  if (!ctx.json) {
49224
50479
  if (status === 503 && code === "CONTROL_UNAVAILABLE") console.error(`\u274C ${verb}: ${CONTROL_UNAVAILABLE_HINT}`);
49225
50480
  else console.error(`\u274C ${verb} failed${code ? ` (${code})` : ""}: ${err?.message ?? "Unknown error"}`);
49226
50481
  }
49227
50482
  if (status === 404) return WORKFLOW_EXIT.NOT_FOUND;
50483
+ if (status === 0 || status !== void 0 && status >= 500) return WORKFLOW_EXIT.UNAVAILABLE;
49228
50484
  return WORKFLOW_EXIT.API;
49229
50485
  }
49230
50486
  __name(apiFailure, "apiFailure");
@@ -49637,6 +50893,35 @@ async function statusCore(ctx, runId, o) {
49637
50893
  return WORKFLOW_EXIT.OK;
49638
50894
  }
49639
50895
  __name(statusCore, "statusCore");
50896
+ function runMetering(run) {
50897
+ return run.usage?.metering === "priced" ? "priced" : "flat";
50898
+ }
50899
+ __name(runMetering, "runMetering");
50900
+ function budgetCopy(run) {
50901
+ if (runMetering(run) === "priced") {
50902
+ if (run.usage?.engine === "seat") return "charged like chat \u2014 actions (tier \xD7 model multiplier) per model call on your seat plan";
50903
+ if (run.usage?.engine === "legacy") return "charged like chat \u2014 one credit per model call on the legacy plan";
50904
+ return "charged like chat \u2014 one credit per model call on the legacy plan, or actions (tier \xD7 model multiplier) on a seat plan";
50905
+ }
50906
+ return "a credit is one agent step; a Job-tier attempt is 4 \u2014 tokens are never metered";
50907
+ }
50908
+ __name(budgetCopy, "budgetCopy");
50909
+ var fmtNum = /* @__PURE__ */ __name((v) => v.toLocaleString("en-US"), "fmtNum");
50910
+ function budgetLine(run) {
50911
+ const u = run.usage;
50912
+ const b = run.budget;
50913
+ if (!u && !b) return void 0;
50914
+ const seat = u?.engine === "seat" || b?.unit === "actions";
50915
+ const spent = seat ? u?.actionsUsed ?? b?.spent?.actionsEstimate ?? 0 : u?.creditsUsed ?? b?.spent?.credits ?? 0;
50916
+ const cap = runBudgetCap(b);
50917
+ const parts = [
50918
+ `${fmtNum(spent)}${cap !== void 0 ? ` of ${fmtNum(cap)}` : ""} ${seat ? "actions" : "credits"} spent`,
50919
+ b?.remaining !== void 0 ? `${fmtNum(b.remaining)} remaining` : void 0,
50920
+ seat && u?.creditsUsed ? `${fmtNum(u.creditsUsed)} flat credits` : void 0
50921
+ ].filter((p) => !!p);
50922
+ return `${parts.join(" \xB7 ")} (${budgetCopy(run)})`;
50923
+ }
50924
+ __name(budgetLine, "budgetLine");
49640
50925
  function printRun(run, withSteps) {
49641
50926
  const id = runIdOf(run);
49642
50927
  console.log(`
@@ -49646,6 +50931,8 @@ function printRun(run, withSteps) {
49646
50931
  console.log(` Created: ${when(run.createdAt)}${run.startedAt ? ` \xB7 started ${when(run.startedAt)}` : ""}${run.completedAt ? ` \xB7 completed ${when(run.completedAt)}` : ""}`);
49647
50932
  if (run.lineageId && run.lineageId !== id) console.log(` Lineage: ${run.lineageId}`);
49648
50933
  if (run.parentRunId) console.log(` Parent: ${run.parentRunId} \u2014 lua workflows status ${run.parentRunId} --steps`);
50934
+ const budget = budgetLine(run);
50935
+ if (budget) console.log(` Budget: ${budget}`);
49649
50936
  if (run.cancel) console.log(` Cancel: ${formatRunCancel(run.cancel)}`);
49650
50937
  if (run.gate) {
49651
50938
  const kind = run.gate.kind;
@@ -49654,7 +50941,8 @@ function printRun(run, withSteps) {
49654
50941
  console.log(` lua workflows retry-step ${id} --step <id>`);
49655
50942
  console.log(" To skip, complete or fail the step, or start a repair run, decide it from the desktop run page.");
49656
50943
  } else if (kind === "budget") {
49657
- console.log("\n\u23F8\uFE0F Paused \u2014 run budget reached (a credit is one agent step; a Job-tier attempt is 4 \u2014 tokens are never metered):");
50944
+ console.log(`
50945
+ \u23F8\uFE0F Paused \u2014 run budget reached (${budgetCopy(run)}):`);
49658
50946
  console.log(` lua workflows raise-budget ${id} --credits <n>`);
49659
50947
  } else {
49660
50948
  console.log(` Gate: ${kind}${run.gate.reason ? ` (${run.gate.reason})` : ""}${run.gate.since ? ` since ${when(run.gate.since)}` : ""}`);
@@ -49693,7 +50981,7 @@ function printRun(run, withSteps) {
49693
50981
  s.stepId,
49694
50982
  s.kind ?? "\u2014",
49695
50983
  s.status,
49696
- String(s.attempt ?? ""),
50984
+ stepAttemptCell(s),
49697
50985
  stepErrorCell(s.error),
49698
50986
  ...withChildren ? [
49699
50987
  s.childRunId ?? ""
@@ -49710,6 +50998,8 @@ function stepAttemptLines(s) {
49710
50998
  if (cause) out.push(` \u2716 ${s.stepId}: ${cause}`);
49711
50999
  const burned = cause ? void 0 : attemptCountersLine(s);
49712
51000
  if (burned) out.push(` \u03A3 ${s.stepId}: attempt ${s.attempt ?? 1} ${s.status}: ${burned}`);
51001
+ const billed = stepBillingLine(s.billing);
51002
+ if (billed) out.push(` $ ${s.stepId}: attempt ${s.billing?.attempt ?? s.attempt ?? 1} billed: ${billed}`);
49713
51003
  for (const a of s.attempts ?? []) {
49714
51004
  if (a.status === "completed") continue;
49715
51005
  const what = stepErrorCell(a.error) || a.killReason || "\u2014";
@@ -49719,6 +51009,33 @@ function stepAttemptLines(s) {
49719
51009
  return out;
49720
51010
  }
49721
51011
  __name(stepAttemptLines, "stepAttemptLines");
51012
+ function stepAttemptCell(s) {
51013
+ const attempt = typeof s.attempt === "number" ? String(s.attempt) : "";
51014
+ if (typeof s.retriesRemaining === "number") {
51015
+ const left = s.retriesRemaining;
51016
+ return `${attempt} (${left === 0 ? "no retries" : left === 1 ? "1 retry" : `${left} retries`} left)`;
51017
+ }
51018
+ if (typeof s.maxAttempts === "number" && s.maxAttempts > 1 && attempt) return `${attempt}/${s.maxAttempts}`;
51019
+ return attempt;
51020
+ }
51021
+ __name(stepAttemptCell, "stepAttemptCell");
51022
+ function stepBillingLine(b) {
51023
+ if (!b) return void 0;
51024
+ const legs = [
51025
+ b.credits !== void 0 ? `${fmtNum(b.credits)} ${b.credits === 1 ? "credit" : "credits"}` : void 0,
51026
+ b.actions !== void 0 ? `${fmtNum(b.actions)} ${b.actions === 1 ? "action" : "actions"}` : void 0
51027
+ ].filter((p) => !!p);
51028
+ const parts = [
51029
+ legs.length ? legs.join(" + ") : "no charge",
51030
+ b.engine === "seat" ? "seat plan" : "legacy plan",
51031
+ b.tier ? `${b.tier} tier${b.multiplier !== void 0 ? ` \xD7 ${fmtNum(b.multiplier)}` : ""}` : void 0,
51032
+ b.model ? `model ${b.model}` : void 0,
51033
+ b.byok ? "BYOK" : void 0,
51034
+ b.calibrated === false ? "uncalibrated model \u2014 fallback rate" : void 0
51035
+ ].filter((p) => !!p);
51036
+ return parts.join(" \xB7 ");
51037
+ }
51038
+ __name(stepBillingLine, "stepBillingLine");
49722
51039
  var RUN_TERMINAL_STATUSES = /* @__PURE__ */ new Set([
49723
51040
  "completed",
49724
51041
  "failed",
@@ -49836,6 +51153,7 @@ async function watchCore(ctx, runId, o) {
49836
51153
  } finally {
49837
51154
  if (timer) clearTimeout(timer);
49838
51155
  process.off("SIGINT", onSigint);
51156
+ if (!controller.signal.aborted) controller.abort();
49839
51157
  }
49840
51158
  if (exit !== void 0) return exit;
49841
51159
  if (timedOut) {
@@ -49845,6 +51163,12 @@ async function watchCore(ctx, runId, o) {
49845
51163
  return WORKFLOW_EXIT.OK;
49846
51164
  }
49847
51165
  __name(watchCore, "watchCore");
51166
+ function throttledLine(throttled) {
51167
+ return `throttled \xB7 ${[
51168
+ ...throttled
51169
+ ].map(([k, n2]) => `${k} \xD7${n2}`).join(" \xB7 ")}`;
51170
+ }
51171
+ __name(throttledLine, "throttledLine");
49848
51172
  function handleWatchFrame(ctx, runId, frame, o, throttled) {
49849
51173
  const data = frame.data ?? {};
49850
51174
  if (o.events || ctx.json) console.log(JSON.stringify({
@@ -49852,6 +51176,12 @@ function handleWatchFrame(ctx, runId, frame, o, throttled) {
49852
51176
  event: frame.event,
49853
51177
  data: frame.data
49854
51178
  }));
51179
+ const humanBoundary = /* @__PURE__ */ __name((line, code) => {
51180
+ if (!ctx.json) console.error(line);
51181
+ if (!o.waitForHuman) return code;
51182
+ if (!ctx.json) console.error(" --wait-for-human: still following \u2014 Ctrl+C or --timeout to stop");
51183
+ return void 0;
51184
+ }, "humanBoundary");
49855
51185
  switch (frame.event) {
49856
51186
  case "heartbeat":
49857
51187
  return void 0;
@@ -49873,9 +51203,7 @@ function handleWatchFrame(ctx, runId, frame, o, throttled) {
49873
51203
  if (type === "step.throttled") {
49874
51204
  const kind = String(ev.data?.kind ?? "throttled");
49875
51205
  throttled.set(kind, (throttled.get(kind) ?? 0) + 1);
49876
- if (!ctx.json && !o.events) process.stdout.write(`\r throttled \xB7 ${[
49877
- ...throttled
49878
- ].map(([k, n2]) => `${k} \xD7${n2}`).join(" \xB7 ")}`);
51206
+ if (!ctx.json && !o.events) process.stdout.write(`\r ${throttledLine(throttled)}`);
49879
51207
  return void 0;
49880
51208
  }
49881
51209
  if (!ctx.json && !o.events) {
@@ -49887,29 +51215,30 @@ function handleWatchFrame(ctx, runId, frame, o, throttled) {
49887
51215
  const detail = subrun ? ` \xB7 ${subrun}` : ev.data ? ` ${JSON.stringify(scrubEventData(ev.data)).slice(0, 160)}` : "";
49888
51216
  console.log(`[${at}] ${type}${ev.stepId ? ` \xB7 ${ev.stepId}` : ""}${detail}`);
49889
51217
  }
49890
- if (type === "run.gated" && ev.data?.gate === "exception") {
49891
- if (!ctx.json) console.error(`\u26A0\uFE0F run parked on an exception gate \u2014 a human decides next: lua workflows status ${runId}`);
49892
- return WORKFLOW_EXIT.RUN_PARKED;
49893
- }
49894
- if (type === "run.gated" && ev.data?.gate === "billing") {
51218
+ if (type === "run.gated") {
51219
+ const gate = ev.data?.gate;
49895
51220
  const stepId = ev.data?.stepId;
49896
- if (!ctx.json) console.error(`\u23F8\uFE0F run parked on a billing gate \u2014 top up, then: lua workflows retry-step ${runId} --step ${stepId ?? "<stepId>"}`);
49897
- return WORKFLOW_EXIT.RUN_PARKED;
51221
+ if (gate === "exception") {
51222
+ return humanBoundary(`\u26A0\uFE0F run parked on an exception gate \u2014 a human decides next: lua workflows status ${runId}`, WORKFLOW_EXIT.RUN_PARKED);
51223
+ }
51224
+ if (gate === "billing") {
51225
+ return humanBoundary(`\u23F8\uFE0F run parked on a billing gate \u2014 top up, then: lua workflows retry-step ${runId} --step ${stepId ?? "<stepId>"}`, WORKFLOW_EXIT.RUN_PARKED);
51226
+ }
51227
+ if (gate === "budget") {
51228
+ return humanBoundary(`\u23F8\uFE0F run budget reached \u2014 lua workflows raise-budget ${runId} --credits <n> (lua workflows status ${runId} says what a credit buys)`, WORKFLOW_EXIT.RUN_PARKED);
51229
+ }
51230
+ return humanBoundary(`\u23F8\uFE0F run gated (${gate ?? "consent"}) \u2014 approve it from the desktop; then: lua workflows watch ${runId}`, WORKFLOW_EXIT.RUN_GATED);
49898
51231
  }
49899
51232
  if (type === "run.suspended") {
49900
51233
  const kind = ev.data?.kind;
49901
51234
  const stepId = ev.data?.stepId ?? ev.stepId;
49902
51235
  if (kind === "approval" || kind === "input" || kind === "signal") {
49903
- if (!ctx.json) {
49904
- const verb = kind === "approval" ? `lua workflows approve ${runId} --approval <id> --decision approve|deny` : kind === "input" ? `lua workflows resume ${runId} --step ${stepId ?? "<stepId>"} --data <json>` : `lua workflows signal ${runId} <name> --payload <json>`;
49905
- console.error(`\u23F8\uFE0F run waits for a person (${kind}${stepId ? ` \xB7 ${stepId}` : ""}) \u2014 ${verb}; then: lua workflows watch ${runId}`);
49906
- }
49907
- return WORKFLOW_EXIT.RUN_PARKED;
51236
+ const verb = kind === "approval" ? `lua workflows approve ${runId} --approval <id> --decision approve|deny` : kind === "input" ? `lua workflows resume ${runId} --step ${stepId ?? "<stepId>"} --data <json>` : `lua workflows signal ${runId} <name> --payload <json>`;
51237
+ return humanBoundary(`\u23F8\uFE0F run waits for a person (${kind}${stepId ? ` \xB7 ${stepId}` : ""}) \u2014 ${verb}; then: lua workflows watch ${runId}`, WORKFLOW_EXIT.RUN_PARKED);
49908
51238
  }
49909
51239
  }
49910
51240
  if (type === "run.budget_parked") {
49911
- if (!ctx.json) console.error(`\u23F8\uFE0F run budget reached (1 credit per agent step, 4 per Job-tier attempt) \u2014 lua workflows raise-budget ${runId} --credits <n>`);
49912
- return WORKFLOW_EXIT.RUN_PARKED;
51241
+ return humanBoundary(`\u23F8\uFE0F run budget reached \u2014 lua workflows raise-budget ${runId} --credits <n> (lua workflows status ${runId} says what a credit buys)`, WORKFLOW_EXIT.RUN_PARKED);
49913
51242
  }
49914
51243
  if (type === "run.completed") return WORKFLOW_EXIT.OK;
49915
51244
  if (type === "run.failed" || type === "run.timed_out") return WORKFLOW_EXIT.RUN_FAILED;
@@ -49928,6 +51257,10 @@ async function cancelCore(ctx, runId, o) {
49928
51257
  emitJson(ctx, res);
49929
51258
  if (!ctx.json) {
49930
51259
  const v = res.data;
51260
+ if (v.state === "terminal" || v.cancelRequested === false && v.transitioned === false && TERMINAL2.has(v.status)) {
51261
+ writeInfo(`\u2139\uFE0F run ${runId} was already ${v.status} \u2014 nothing to cancel`);
51262
+ return WORKFLOW_EXIT.OK;
51263
+ }
49931
51264
  writeSuccess(`\u2705 cancel accepted \xB7 run ${runId} is ${v.status}`);
49932
51265
  if (v.nextAction === "cancel_again") writeInfo(` next: cancel again to escalate \u2014 lua workflows cancel ${runId}`);
49933
51266
  else if (v.nextAction === "force") writeInfo(` next: force available${v.forceAvailableAt ? ` at ${when(v.forceAvailableAt)}` : ""} \u2014 lua workflows cancel ${runId} --force`);
@@ -49980,8 +51313,8 @@ async function resumeCore(ctx, runId, o) {
49980
51313
  if (!ctx.json) {
49981
51314
  if (res.data.resumed) writeSuccess(`\u2705 resumed \xB7 run is ${res.data.runStatus}`);
49982
51315
  else {
49983
- const by = res.data.recorded?.by;
49984
- writeInfo(`\u2139\uFE0F already resumed at ${when(res.data.recorded?.at)}${by ? ` by ${by.subjectType}:${by.subjectId}` : ""} \xB7 run is ${res.data.runStatus}`);
51316
+ const by = actorLabel(res.data.recorded?.by);
51317
+ writeInfo(`\u2139\uFE0F already resumed at ${when(res.data.recorded?.at)}${by ? ` by ${by}` : ""} \xB7 run is ${res.data.runStatus}`);
49985
51318
  }
49986
51319
  }
49987
51320
  return WORKFLOW_EXIT.OK;
@@ -50014,13 +51347,20 @@ async function retryStepCore(ctx, runId, o) {
50014
51347
  writeSuccess(`\u2705 re-armed ${o.step} (attempt ${d.attempt}) \xB7 run is ${d.runStatus}` + (d.remainingParks ? ` \xB7 ${d.remainingParks} other parked step(s)` : ""));
50015
51348
  } else {
50016
51349
  const standing = d.reason === "already_retried" ? "retried" : `resolved${d.recorded?.outcome ? ` (${d.recorded?.outcome})` : ""}`;
50017
- const by = d.recorded?.by;
50018
- writeInfo(`\u2139\uFE0F step "${o.step}" was already ${standing}` + (by ? ` by ${by.subjectType}:${by.subjectId}` : "") + (d.recorded?.at ? ` at ${when(d.recorded.at)}` : "") + ` \xB7 run is ${d.runStatus}`);
51350
+ const by = actorLabel(d.recorded?.by);
51351
+ writeInfo(`\u2139\uFE0F step "${o.step}" was already ${standing}` + (by ? ` by ${by}` : "") + (d.recorded?.at ? ` at ${when(d.recorded.at)}` : "") + ` \xB7 run is ${d.runStatus}`);
50019
51352
  }
50020
51353
  }
50021
51354
  return WORKFLOW_EXIT.OK;
50022
51355
  }
50023
51356
  __name(retryStepCore, "retryStepCore");
51357
+ function approveResultLine(d) {
51358
+ if (d.resolved) return `\u2705 ${d.outcome} \xB7 run is ${d.runStatus}`;
51359
+ const ref = d.decidedBy;
51360
+ const who = ref && typeof ref === "object" && ref.subjectId && ref.source ? `${ref.subjectId} via ${ref.source}` : actorLabel(ref);
51361
+ return `\u2139\uFE0F no-op (${d.reason})${who ? ` \u2014 decided by ${who}` : ""}`;
51362
+ }
51363
+ __name(approveResultLine, "approveResultLine");
50024
51364
  async function approveCore(ctx, runId, o) {
50025
51365
  if (!o.approval) {
50026
51366
  console.error("\u274C approve: --approval <id> is required");
@@ -50061,12 +51401,25 @@ async function approveCore(ctx, runId, o) {
50061
51401
  }
50062
51402
  emitJson(ctx, res);
50063
51403
  if (!ctx.json) {
50064
- if (res.data.resolved) writeSuccess(`\u2705 ${res.data.outcome} \xB7 run is ${res.data.runStatus}`);
50065
- else writeInfo(`\u2139\uFE0F no-op (${res.data.reason})${res.data.decidedBy ? ` \u2014 decided by ${res.data.decidedBy}` : ""}`);
51404
+ if (res.data.resolved) writeSuccess(approveResultLine(res.data));
51405
+ else writeInfo(approveResultLine(res.data));
50066
51406
  }
50067
51407
  return WORKFLOW_EXIT.OK;
50068
51408
  }
50069
51409
  __name(approveCore, "approveCore");
51410
+ function actorLabel(by, now = Date.now()) {
51411
+ if (by === void 0 || by === null) return void 0;
51412
+ if (typeof by === "string") return by;
51413
+ if (typeof by !== "object") return String(by);
51414
+ const a = by;
51415
+ const kind = a.subjectType ?? a.kind;
51416
+ const id = a.name ?? a.subjectId ?? a.id;
51417
+ const who = kind && id ? `${kind}:${id}` : kind ?? id;
51418
+ if (!who) return void 0;
51419
+ const at = typeof a.at === "number" ? a.at : typeof a.at === "string" ? Date.parse(a.at) : NaN;
51420
+ return Number.isFinite(at) ? `${who} (${agoFrom(at, now)})` : who;
51421
+ }
51422
+ __name(actorLabel, "actorLabel");
50070
51423
  function reservedSecretKeyPaths(value3, path25 = "", depth = 0, out = []) {
50071
51424
  if (depth > 64 || out.length >= 20 || value3 === null || typeof value3 !== "object") return out;
50072
51425
  if (Array.isArray(value3)) {
@@ -50345,6 +51698,17 @@ async function deleteCore(ctx, name, o) {
50345
51698
  force: "cancel"
50346
51699
  } : {});
50347
51700
  if (!res.success) {
51701
+ if (res.error?.statusCode === 409 && (res.error.code ?? res.error.error) === "WORKFLOW_REFERENCED_BY_JOBS") {
51702
+ if (!ctx.json) {
51703
+ const jobs = res.error.jobs ?? [];
51704
+ console.error(`\u274C "${wf.name}" is still referenced by ${jobs.length} job(s) \u2014 stop them first:`);
51705
+ for (const j of jobs) {
51706
+ console.error(j.kind === "goal" && j.goalId ? ` \u2022 goal ${j.goalId} (job ${j.jobId}): lua workflows goals close ${j.goalId}` : ` \u2022 schedule ${j.jobId}: lua workflows schedules delete ${j.jobId}`);
51707
+ }
51708
+ }
51709
+ emitJson(ctx, res);
51710
+ return WORKFLOW_EXIT.API;
51711
+ }
50348
51712
  if (res.error?.statusCode === 409 && !ctx.json) {
50349
51713
  const counts = res.error.counts;
50350
51714
  console.error(`\u274C "${wf.name}" has runs in flight${counts ? ` (${Object.entries(counts).map(([k, v]) => `${k}: ${v}`).join(", ")})` : ""} \u2014 cancel them first or pass --force`);
@@ -50678,10 +52042,14 @@ ${report.archived.length} archived \xB7 ${report.skipped.length} skipped \xB7 ${
50678
52042
  __name(archiveRunsCore, "archiveRunsCore");
50679
52043
  function ago(ms) {
50680
52044
  if (!ms) return "\u2014";
50681
- const s = Math.max(0, Math.round((Date.now() - ms) / 1e3));
50682
- return s < 90 ? `${s}s ago` : s < 5400 ? `${Math.round(s / 60)}m ago` : `${(s / 3600).toFixed(1)}h ago`;
52045
+ return agoFrom(ms, Date.now());
50683
52046
  }
50684
52047
  __name(ago, "ago");
52048
+ function agoFrom(ms, now) {
52049
+ const s = Math.max(0, Math.round((now - ms) / 1e3));
52050
+ return s < 90 ? `${s}s ago` : s < 5400 ? `${Math.round(s / 60)}m ago` : `${(s / 3600).toFixed(1)}h ago`;
52051
+ }
52052
+ __name(agoFrom, "agoFrom");
50685
52053
  function printWorkspace(ws) {
50686
52054
  console.log(`
50687
52055
  \u{1F4E6} Workspace of ${ws.runId} \xB7 ${ws.status} (${ws.kind}, ${ws.backend})`);
@@ -50862,6 +52230,8 @@ var GOAL_VERBS = [
50862
52230
  "list",
50863
52231
  "get",
50864
52232
  "create",
52233
+ "edit",
52234
+ "raise",
50865
52235
  "pause",
50866
52236
  "resume",
50867
52237
  "close"
@@ -50903,6 +52273,10 @@ function parseIntegerFlag(v, flag, range2 = {}) {
50903
52273
  return n2;
50904
52274
  }
50905
52275
  __name(parseIntegerFlag, "parseIntegerFlag");
52276
+ var GOAL_LIVE_STATUSES = /* @__PURE__ */ new Set([
52277
+ "active",
52278
+ "paused"
52279
+ ]);
50906
52280
  function goalScheduleRefusal(jobId, goalId) {
50907
52281
  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.`;
50908
52282
  }
@@ -50927,7 +52301,12 @@ var judgeLabel = /* @__PURE__ */ __name((judge) => {
50927
52301
  var goalStatusLabel = /* @__PURE__ */ __name((g) => `${g.status}${g.pauseReason ? ` (${g.pauseReason})` : ""}`, "goalStatusLabel");
50928
52302
  var goalJobStateLabel = /* @__PURE__ */ __name((g) => g.job ? ` \xB7 ${g.job.status}${g.job.status === "active" && g.job.nextRunAt ? `, next ${when(g.job.nextRunAt)}` : ""}` : "", "goalJobStateLabel");
50929
52303
  var verdictLabel = /* @__PURE__ */ __name((v) => v ? `${v.done ? "done" : "continue"}${v.summary ? ` \xB7 ${v.summary.slice(0, 60)}` : ""}` : "\u2014", "verdictLabel");
50930
- var scheduleStatus = /* @__PURE__ */ __name((r) => r.autoDisabled ? "auto-disabled" : r.status === "inactive" ? "inactive" : r.paused ? "paused" : "active", "scheduleStatus");
52304
+ var scheduleStatus = /* @__PURE__ */ __name((r) => r.autoDisabled ? "auto-disabled" : r.status === "inactive" ? "inactive" : r.status === "completed" || r.status === "missed" ? r.status : r.paused ? "paused" : "active", "scheduleStatus");
52305
+ function goalRaiseHint(goalId, reason) {
52306
+ const flag = reason === "budget" ? "--max-credits <n>" : reason === "max_runs" ? "--max-runs <n>" : "--max-runs <n> | --max-credits <n>";
52307
+ return `a goal parked at its cap (max_runs / budget) re-arms when the cap is raised: lua workflows goals raise ${goalId} ${flag} \u2014 or edit it: lua workflows goals edit ${goalId} \u2026`;
52308
+ }
52309
+ __name(goalRaiseHint, "goalRaiseHint");
50931
52310
  var errorLabel = /* @__PURE__ */ __name((e) => `${e.code ?? e.error ?? e.statusCode ?? "?"} \u2014 ${e.message}`, "errorLabel");
50932
52311
  async function loadGoals(ctx, workflowId) {
50933
52312
  const items = [];
@@ -51090,8 +52469,17 @@ function goalFailure(ctx, res, verb, ref) {
51090
52469
  if (!ctx.json && err) {
51091
52470
  if (status === 409 && code === "GOAL_NOT_ACTIVE") {
51092
52471
  const now = err.status ?? "not active";
51093
- const rule = verb === "goals resume" ? "only a paused goal resumes" : verb === "goals pause" ? "only an active goal pauses" : "a done/closed goal stays closed";
52472
+ const rule = verb === "goals resume" ? "only a paused goal resumes" : verb === "goals pause" ? "only an active goal pauses" : "a done/closed goal stays closed (a lingering cadence Job goes with `lua workflows schedules delete <jobId>`)";
51094
52473
  console.error(`\u274C goal ${ref ?? ""} is ${now} \u2014 ${rule}`);
52474
+ if (verb === "goals resume" && now === "paused") console.error(` ${goalRaiseHint(ref ?? "<goalId>")}`);
52475
+ } else if (status === 400 && code === "GOAL_RAISE_BELOW_SPENT") {
52476
+ const { field, value: value3, spent } = err;
52477
+ const flag = field === "maxRuns" ? "--max-runs" : "--max-credits";
52478
+ const used = field === "maxRuns" ? "run(s) already used" : "credit(s) already spent";
52479
+ console.error(`\u274C ${flag} ${value3 ?? "?"} is not above the ${spent ?? "?"} ${used} \u2014 raise it past ${spent ?? "?"}`);
52480
+ } else if (status === 409 && code === "GOAL_VERSION_CONFLICT") {
52481
+ const at = err.updatedAt;
52482
+ console.error(`\u274C goal ${ref ?? ""} changed since it was read${at ? ` (updatedAt ${at})` : ""} \u2014 re-run lua workflows goals get ${ref ?? "<goalId>"} and retry${at ? ` with --if-match ${at}` : ""}`);
51095
52483
  } else if (status === 409 && code === "GOAL_CAP") {
51096
52484
  console.error(`\u274C goal cap reached (${err.cap ?? "?"} per agent) \u2014 close or finish one first: lua workflows goals list --status active`);
51097
52485
  } else if (status === 400 && code === "VALIDATION_FAILED") {
@@ -51119,6 +52507,8 @@ async function goalsCore(ctx, target, extra, o) {
51119
52507
  return WORKFLOW_EXIT.USAGE;
51120
52508
  }
51121
52509
  if (verb === "get") return goalsGetCore(ctx, extra);
52510
+ if (verb === "edit") return goalsEditCore(ctx, extra, o);
52511
+ if (verb === "raise") return goalsRaiseCore(ctx, extra, o);
51122
52512
  return goalsControlCore(ctx, verb, extra, o);
51123
52513
  }
51124
52514
  __name(goalsCore, "goalsCore");
@@ -51188,6 +52578,10 @@ function printGoalDetail(g) {
51188
52578
  console.log(` Cadence: ${cadenceLabel(g.cadence)} \xB7 mode ${g.evaluation?.mode ?? "immediate"}${g.evaluation?.delaySeconds ? ` \xB7 delay ${g.evaluation.delaySeconds}s` : ""}`);
51189
52579
  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}` : ""}`);
51190
52580
  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` : ""}`);
52581
+ if (g.status === "paused" && (g.pauseReason === "budget" || g.pauseReason === "max_runs")) console.log(` \u23F8 parked at its ${g.pauseReason === "budget" ? "credit" : "run"} cap \u2014 ${goalRaiseHint(g.goalId, g.pauseReason)}`);
52582
+ if (g.note) console.log(` Note: ${g.note}`);
52583
+ if (g.closeNote) console.log(` Closed: ${g.closeNote}`);
52584
+ if (g.raises?.length) console.log(` Raises: ${g.raises.map((r) => `${goalCapsLabel(r.from)} \u2192 ${goalCapsLabel(r.to)} (${r.by}, ${when(r.at)}${r.note ? `, "${r.note}"` : ""})`).join(" \xB7 ")}`);
51191
52585
  console.log(` Schedule: ${g.jobId ? `${g.jobId} (goal-owned \u2014 stop it with goals pause|close, never schedules delete)${goalJobStateLabel(g)}` : "none (immediate)"}`);
51192
52586
  if (g.verdict) console.log(` Verdict: ${verdictLabel(g.verdict)}${g.verdict.runId ? ` (run ${g.verdict.runId}${g.verdict.at ? ` at ${when(g.verdict.at)}` : ""})` : ""}`);
51193
52587
  if (g.input !== void 0) console.log(` Input: ${JSON.stringify(g.input).slice(0, 500)}`);
@@ -51229,6 +52623,118 @@ async function goalsControlCore(ctx, verb, goalId, o) {
51229
52623
  return WORKFLOW_EXIT.OK;
51230
52624
  }
51231
52625
  __name(goalsControlCore, "goalsControlCore");
52626
+ var goalCapsLabel = /* @__PURE__ */ __name((caps) => [
52627
+ caps.maxRuns !== void 0 ? `runs ${caps.maxRuns}` : "",
52628
+ caps.maxTotalCredits !== void 0 ? `credits ${caps.maxTotalCredits}` : ""
52629
+ ].filter(Boolean).join(" \xB7 ") || "\u2014", "goalCapsLabel");
52630
+ function parseEveryFlag(raw) {
52631
+ const m = /^\s*(\d+)\s*(s|sec|secs|m|min|mins|h|hr|hrs)?\s*$/i.exec(String(raw));
52632
+ if (!m) throw new WorkflowLocalUsageError("usage", `--every: expected <seconds>, <n>m or <n>h (got "${raw}")`);
52633
+ const n2 = Number(m[1]);
52634
+ const unit = (m[2] ?? "s").toLowerCase();
52635
+ const seconds = unit.startsWith("h") ? n2 * 3600 : unit.startsWith("m") ? n2 * 60 : n2;
52636
+ if (seconds < 60 || seconds % 60 !== 0) throw new WorkflowLocalUsageError("usage", `--every: the interval must be whole minutes, at least 60s (got ${seconds}s)`);
52637
+ return [
52638
+ {
52639
+ type: "interval",
52640
+ seconds
52641
+ }
52642
+ ];
52643
+ }
52644
+ __name(parseEveryFlag, "parseEveryFlag");
52645
+ function printGoalWriteSummary(g, past) {
52646
+ writeSuccess(`\u2705 goal ${g.goalId} ${past} \xB7 ${goalStatusLabel(g)} \xB7 runs ${g.runsUsed}/${g.maxRuns}${g.maxTotalCredits ? ` \xB7 lineage cap ${g.maxTotalCredits} credits` : ""}${g.jobId ? ` \xB7 schedule ${g.jobId}${goalJobStateLabel(g)}` : ""}`);
52647
+ if (g.rearmed) writeInfo(" \u21BA re-armed \u2014 the cap park cleared and the goal is active again (no backfill)");
52648
+ if (g.resumed === false) writeInfo(` \u26A0\uFE0F the goal parked again (${g.pauseReason ?? "strikes"}) \u2014 see: lua workflows goals get ${g.goalId}`);
52649
+ if (!g.rearmed && g.status === "paused" && (g.pauseReason === "budget" || g.pauseReason === "max_runs")) writeInfo(` still parked (${g.pauseReason}) \u2014 ${goalRaiseHint(g.goalId, g.pauseReason)}`);
52650
+ }
52651
+ __name(printGoalWriteSummary, "printGoalWriteSummary");
52652
+ async function goalsEditCore(ctx, goalId, o) {
52653
+ let dto;
52654
+ try {
52655
+ const usage = /* @__PURE__ */ __name((m) => new WorkflowLocalUsageError("usage", m), "usage");
52656
+ dto = {};
52657
+ if (o.objective !== void 0) {
52658
+ if (!o.objective.trim()) throw usage("--objective: must not be empty");
52659
+ dto.objective = o.objective.trim();
52660
+ }
52661
+ if (o.judgePredicate) dto.judge = {
52662
+ agentId: "$self",
52663
+ predicate: parseJudgePredicateFlag(o.judgePredicate)
52664
+ };
52665
+ if (o.every && o.cadence?.length) throw usage("--every and --cadence are alternatives \u2014 pass one");
52666
+ if (o.every) dto.cadence = parseEveryFlag(o.every);
52667
+ else if (o.cadence?.length) dto.cadence = parseCadenceFlag(o.cadence, o.timezone);
52668
+ else if (o.timezone) throw usage("--timezone applies to a bare cron --cadence entry \u2014 none given");
52669
+ const maxRuns = parseIntegerFlag(o.maxRuns, "--max-runs", {
52670
+ min: 1,
52671
+ max: GOAL_MAX_RUNS_CAP
52672
+ });
52673
+ if (maxRuns !== void 0) dto.maxRuns = maxRuns;
52674
+ const rawCredits = o.maxCredits ?? o.maxTotalCredits;
52675
+ if (rawCredits !== void 0) {
52676
+ if (String(rawCredits).trim().toLowerCase() === "none") dto.maxTotalCredits = null;
52677
+ else dto.maxTotalCredits = parseIntegerFlag(rawCredits, "--max-credits", {
52678
+ min: 1
52679
+ });
52680
+ }
52681
+ if (o.note !== void 0) dto.note = o.note.trim() ? o.note : null;
52682
+ const ifMatch = parseIntegerFlag(o.ifMatch, "--if-match", {
52683
+ min: 1
52684
+ });
52685
+ if (ifMatch !== void 0) dto.ifMatch = ifMatch;
52686
+ if (Object.keys(dto).filter((k) => k !== "ifMatch").length === 0) throw usage("nothing to change \u2014 pass --objective, --judge-predicate, --cadence | --every, --max-runs, --max-credits <n|none> or --note");
52687
+ } catch (e) {
52688
+ if (e instanceof WorkflowLocalUsageError) {
52689
+ console.error(`\u274C goals edit: ${e.message}`);
52690
+ return WORKFLOW_EXIT.USAGE;
52691
+ }
52692
+ throw e;
52693
+ }
52694
+ const res = await ctx.api.updateGoal(goalId, dto);
52695
+ if (!res.success || !res.data) return goalFailure(ctx, res, "goals edit", goalId);
52696
+ emitJson(ctx, res);
52697
+ if (!ctx.json) printGoalWriteSummary(res.data, "updated");
52698
+ return WORKFLOW_EXIT.OK;
52699
+ }
52700
+ __name(goalsEditCore, "goalsEditCore");
52701
+ async function goalsRaiseCore(ctx, goalId, o) {
52702
+ let dto;
52703
+ try {
52704
+ dto = {};
52705
+ const maxRuns = parseIntegerFlag(o.maxRuns, "--max-runs", {
52706
+ min: 1,
52707
+ max: GOAL_MAX_RUNS_CAP
52708
+ });
52709
+ if (maxRuns !== void 0) dto.maxRuns = maxRuns;
52710
+ const maxTotalCredits = parseIntegerFlag(o.maxCredits ?? o.maxTotalCredits, "--max-credits", {
52711
+ min: 1
52712
+ });
52713
+ if (maxTotalCredits !== void 0) dto.maxTotalCredits = maxTotalCredits;
52714
+ if (dto.maxRuns === void 0 && dto.maxTotalCredits === void 0) throw new WorkflowLocalUsageError("usage", "--max-credits <n> and/or --max-runs <n> is required (increases only)");
52715
+ if (o.note) dto.note = o.note;
52716
+ const ifMatch = parseIntegerFlag(o.ifMatch, "--if-match", {
52717
+ min: 1
52718
+ });
52719
+ if (ifMatch !== void 0) dto.ifMatch = ifMatch;
52720
+ } catch (e) {
52721
+ if (e instanceof WorkflowLocalUsageError) {
52722
+ console.error(`\u274C goals raise: ${e.message}`);
52723
+ return WORKFLOW_EXIT.USAGE;
52724
+ }
52725
+ throw e;
52726
+ }
52727
+ const res = await ctx.api.raiseGoal(goalId, dto);
52728
+ if (!res.success || !res.data) return goalFailure(ctx, res, "goals raise", goalId);
52729
+ emitJson(ctx, res);
52730
+ if (!ctx.json) {
52731
+ const g = res.data;
52732
+ const last = g.raises?.[g.raises.length - 1];
52733
+ printGoalWriteSummary(g, `raised${last ? ` (${goalCapsLabel(last.from)} \u2192 ${goalCapsLabel(last.to)})` : ""}`);
52734
+ }
52735
+ return WORKFLOW_EXIT.OK;
52736
+ }
52737
+ __name(goalsRaiseCore, "goalsRaiseCore");
51232
52738
  function parseJudgePredicateFlag(raw) {
51233
52739
  const opList = GOAL_PREDICATE_OPS.join("|");
51234
52740
  if (raw.startsWith("{") || raw.startsWith("@")) {
@@ -51465,7 +52971,15 @@ async function schedulesDeleteCore(ctx, jobId, o) {
51465
52971
  if (!rowRes.success || !rowRes.data) return apiFailure(ctx, rowRes, "schedules delete");
51466
52972
  const row2 = rowRes.data;
51467
52973
  let goalId = row2.goalId;
51468
- if (!goalId) {
52974
+ let goalStatus;
52975
+ if (goalId) {
52976
+ const goalRes = await ctx.api.getGoal(goalId);
52977
+ if (goalRes.success && goalRes.data) goalStatus = goalRes.data.status;
52978
+ else if (goalRes.error?.statusCode !== 404) {
52979
+ if (!ctx.json) console.error(`\u274C cannot prove goal ${goalId} of ${jobId} has ended (goal unreadable) \u2014 refusing to delete`);
52980
+ return apiFailure(ctx, goalRes, "schedules delete");
52981
+ }
52982
+ } else {
51469
52983
  const goals = await loadGoals(ctx, row2.workflowId);
51470
52984
  if (goals.error) {
51471
52985
  if (!ctx.json) console.error(`\u274C cannot prove ${jobId} is not a goal's cadence (goals unavailable) \u2014 refusing to delete`);
@@ -51474,9 +52988,11 @@ async function schedulesDeleteCore(ctx, jobId, o) {
51474
52988
  error: goals.error
51475
52989
  }, "schedules delete");
51476
52990
  }
51477
- goalId = goals.items.find((g) => g.jobId === jobId)?.goalId;
52991
+ const owner = goals.items.find((g) => g.jobId === jobId);
52992
+ goalId = owner?.goalId;
52993
+ goalStatus = owner?.status;
51478
52994
  }
51479
- if (goalId) {
52995
+ if (goalId && GOAL_LIVE_STATUSES.has(goalStatus)) {
51480
52996
  const message = goalScheduleRefusal(jobId, goalId);
51481
52997
  if (ctx.json) console.log(JSON.stringify({
51482
52998
  success: false,
@@ -60387,6 +61903,7 @@ import chalk4 from "chalk";
60387
61903
  init_compiler2();
60388
61904
  init_artifact_loader();
60389
61905
  init_analytics();
61906
+ init_cli_error();
60390
61907
  async function resolveCurrentModel(apiKey, agentId) {
60391
61908
  const serverModel = await fetchServerModel(apiKey, agentId);
60392
61909
  if (serverModel) return serverModel;
@@ -60450,8 +61967,9 @@ async function modelsCommand(action, opts) {
60450
61967
  const models = await fetchApprovedModels(apiKey, agentId);
60451
61968
  writeProgress("");
60452
61969
  if (models === null) {
60453
- writeError("\u274C Could not fetch models from the server. Check your connection and API key.");
60454
- return;
61970
+ throw new CliError("error", "Could not fetch models from the server.", {
61971
+ hint: "Check your connection and API key, then retry."
61972
+ });
60455
61973
  }
60456
61974
  if (models.length === 0) {
60457
61975
  writeInfo("\u2139\uFE0F No models are currently available. If your organization restricts models, an admin may need to review its excluded-models list.");
@@ -62992,23 +64510,30 @@ Examples:
62992
64510
  $ lua jobs versions -i myJob View job versions
62993
64511
  $ lua jobs history -i myJob View execution history
62994
64512
  `).action(jobsCommand);
62995
- 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("--workspace <dir>", "run: the checkout Job-tier code steps run in (ctx.workspace + ctx.exec / ctx.$)").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", `
64513
+ 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 / start --follow: give up after s seconds while the run is still live (exit 7)").option("--events", "watch: print raw frames").option("--wait-for-human", "watch / start --follow: keep following through a human boundary (approval, input, signal, park) instead of exiting 8").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("--workspace <dir>", "run: the checkout Job-tier code steps run in (ctx.workspace + ctx.exec / ctx.$)").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/edit: IANA timezone for bare cron cadences").option("--max-runs <n>", "goals create/edit/raise: iteration cap (1..100)").option("--max-total-credits <n>", "goals create: lineage-wide credit gate").option("--max-credits <n|none>", "goals edit/raise: lineage-wide credit gate (edit: none clears it)").option("--every <interval>", "goals edit: interval cadence \u2014 900, 15m, 2h (whole minutes, \u2265 60s)").option("--if-match <updatedAt>", "goals edit/raise: refuse the write if the goal changed since this updatedAt").addHelpText("after", `
62996
64514
  Arguments:
62997
64515
  action list \xB7 view \xB7 versions \xB7 deploy \xB7 activate \xB7 deactivate \xB7 start \xB7 run \xB7 runs \xB7 status \xB7
62998
64516
  watch \xB7 cancel \xB7 resume \xB7 retry-step \xB7 approve \xB7 signal \xB7 replay \xB7 logs \xB7 delete \xB7 delete-run \xB7
62999
64517
  env-overlay \xB7 archive-runs \xB7 workspace \xB7 jobs \xB7 job-logs \xB7
63000
- goals <list|get|create|pause|resume|close> \xB7 schedules <list|delete>
64518
+ goals <list|get|create|edit|raise|pause|resume|close> \xB7 schedules <list|delete>
63001
64519
  target workflow name (list/view/versions/deploy/activate/deactivate/start/run/delete) or run id (the rest);
63002
64520
  the sub-verb for goals / schedules
63003
- extra signal name (signal <runId> <name>) \xB7 goal id (goals get/pause/resume/close) \xB7 job id (schedules delete)
64521
+ extra signal name (signal <runId> <name>) \xB7 goal id (goals get/edit/raise/pause/resume/close) \xB7 job id (schedules delete)
63004
64522
 
63005
- Exit codes: 0 ok \xB7 1 API failure \xB7 2 usage \xB7 3 not found \xB7 4 run failed \xB7 5 run cancelled \xB7
63006
- 6 run gated (consent) \xB7 7 --timeout reached \xB7 8 run parked (exception/budget gate)
64523
+ Exit codes: 0 ok \xB7 1 API refusal (a 4xx other than 404) \xB7 2 usage \xB7 3 not found \xB7 4 run failed \xB7 5 run cancelled \xB7
64524
+ 6 run gated (consent) \xB7 7 --timeout reached \xB7 8 run parked \u2014 waiting for a human
64525
+ (approval \xB7 input \xB7 signal \xB7 billing / budget / exception park; the resuming verb is printed).
64526
+ start --follow and watch EXIT at a human boundary (8, or 6 on a consent gate) unless
64527
+ --wait-for-human; a terminal run exits 0 completed \xB7 4 failed/timed out \xB7 5 cancelled.
64528
+ A failed request and every escaped error use the global classes: 9 auth (401) \xB7 10 forbidden (403) \xB7
64529
+ 11 unavailable (5xx / connection refused / timeout) \u2014 one line, \u2716 <code>: <message>; LUA_DEBUG=1
64530
+ prints the stack.
63007
64531
 
63008
64532
  Examples:
63009
64533
  $ lua workflows list
63010
64534
  $ lua workflows run outreach --input @leads.json --step-output draftEmail=@draft.json --approve reviewDrafts
63011
64535
  $ lua workflows start outreach --input '{"leads":[]}' --follow
64536
+ $ lua workflows start outreach --follow --wait-for-human --timeout 3600
63012
64537
  $ lua workflows runs --workflow outreach --status failed
63013
64538
  $ lua workflows status <runId> --steps
63014
64539
  $ lua workflows watch <runId> --timeout 600
@@ -63029,6 +64554,8 @@ Examples:
63029
64554
  $ lua workflows goals get wfg_1234
63030
64555
  $ lua workflows goals create -i outreach --objective "Reach 50 signups" --judge-predicate 'output.signups gte 50' \\
63031
64556
  --cadence '0 9 * * 1' --timezone Europe/London --max-runs 12 --input '{"segment":"trial"}'
64557
+ $ lua workflows goals edit wfg_1234 --max-credits 500 --every 30m --note "widened after the launch"
64558
+ $ lua workflows goals raise wfg_1234 --max-runs 24 (a goal parked max_runs / budget re-arms when its cap clears)
63032
64559
  $ lua workflows goals pause wfg_1234 \xB7 goals resume wfg_1234 \xB7 goals close wfg_1234 --note "shipped"
63033
64560
  $ lua workflows schedules list -i outreach
63034
64561
  $ lua workflows schedules delete <jobId> --yes (a goal's cadence is refused: goal_schedule)
@@ -63370,6 +64897,7 @@ __name(setupSkillCommands, "setupSkillCommands");
63370
64897
 
63371
64898
  // src/index.ts
63372
64899
  init_cli();
64900
+ init_cli_error();
63373
64901
 
63374
64902
  // src/utils/banner.ts
63375
64903
  var BOLD = "\x1B[1m";
@@ -63414,8 +64942,9 @@ if (isBareInvocation || isHelpInvocation) {
63414
64942
  version: CLI_VERSION
63415
64943
  });
63416
64944
  }
64945
+ if (args.includes("--debug") || debugEnabled()) setDebugMode(true);
63417
64946
  var program = new Command();
63418
- program.showSuggestionAfterError().name("lua").description("Lua AI - Build and deploy AI agents with superpowers").version(CLI_VERSION, "-V, --cli-version").option("--ci", "CI/CD mode: fail loudly on missing required flags instead of prompting").addHelpText("after", `
64947
+ program.exitOverride().showSuggestionAfterError().name("lua").description("Lua AI - Build and deploy AI agents with superpowers").version(CLI_VERSION, "-V, --cli-version").option("--ci", "CI/CD mode: fail loudly on missing required flags instead of prompting").addHelpText("after", `
63419
64948
  Categories:
63420
64949
  \u{1F510} Authentication Manage API keys and authentication
63421
64950
  \u{1F680} Project Setup Initialize and configure projects
@@ -63460,6 +64989,8 @@ Examples:
63460
64989
  $ lua marketplace \u{1F6CD}\uFE0F Interact with the Lua Marketplace (skills & agent templates)
63461
64990
  $ lua marketplace template \u{1F9E9} Create, publish, and apply marketplace agent templates
63462
64991
 
64992
+ ${CLI_EXIT_CODE_HELP}
64993
+
63463
64994
  \u{1F319} Documentation: https://docs.heylua.ai
63464
64995
  \u{1F319} Support: https://heylua.ai/support
63465
64996
  `);
@@ -63477,5 +65008,7 @@ if (rawArgs.length === 1 && rawArgs[0] === "--version") {
63477
65008
  console.log(CLI_VERSION);
63478
65009
  process.exit(0);
63479
65010
  }
63480
- program.parse(process.argv);
65011
+ program.parseAsync(process.argv).catch((error) => {
65012
+ reportUnhandledCliError(error);
65013
+ });
63481
65014
  //# sourceMappingURL=index.js.map