lua-cli 3.32.3 → 3.32.5
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/api-exports.d.ts +16 -4
- package/dist/api-exports.js +813 -317
- package/dist/api-exports.js.map +1 -1
- package/dist/index.js +2258 -1350
- package/dist/index.js.map +1 -1
- package/dist/workflow-builder.d.ts +5 -3
- package/dist/workflow-builder.js +558 -258
- package/dist/workflow-builder.js.map +1 -1
- package/docs/CLI_REFERENCE.md +6 -2
- package/docs/README.md +2 -2
- package/docs/api/LuaWorkflow.md +1 -1
- package/docs/workflows/approvals.md +2 -0
- package/docs/workflows/limits.md +4 -0
- package/docs/workflows/recovery.md +1 -1
- package/docs/workflows/schedules.md +13 -4
- package/docs/workflows/testing-offline.md +1 -1
- package/package.json +4 -4
- package/template/examples/workflows/research-brief.ts +29 -16
- package/template/package.json +1 -1
package/dist/api-exports.js
CHANGED
|
@@ -353,6 +353,84 @@ function isDesktopFileCommandName(value3) {
|
|
|
353
353
|
function isDesktopFileSessionId(value3) {
|
|
354
354
|
return typeof value3 === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value3);
|
|
355
355
|
}
|
|
356
|
+
function isModelIdSentinel(input) {
|
|
357
|
+
const lower = (input ?? "").trim().toLowerCase();
|
|
358
|
+
return lower === "auto" || lower.startsWith("auto/");
|
|
359
|
+
}
|
|
360
|
+
function normalizeModelId(input, registry) {
|
|
361
|
+
const requested = typeof input === "string" ? input.trim() : "";
|
|
362
|
+
if (!requested) return {
|
|
363
|
+
ok: false,
|
|
364
|
+
reason: "empty",
|
|
365
|
+
requested,
|
|
366
|
+
candidates: []
|
|
367
|
+
};
|
|
368
|
+
const lower = requested.toLowerCase();
|
|
369
|
+
if (isModelIdSentinel(lower)) return {
|
|
370
|
+
ok: true,
|
|
371
|
+
id: lower,
|
|
372
|
+
form: "sentinel"
|
|
373
|
+
};
|
|
374
|
+
const slash = requested.indexOf("/");
|
|
375
|
+
const malformed = slash === 0;
|
|
376
|
+
const provider = slash > 0 ? lower.slice(0, slash) : void 0;
|
|
377
|
+
if (provider && MODEL_ID_BYOK_PROVIDERS.includes(provider)) {
|
|
378
|
+
return {
|
|
379
|
+
ok: true,
|
|
380
|
+
id: requested,
|
|
381
|
+
form: "byok"
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
const bareId = slash >= 0 ? lower.slice(slash + 1) : lower;
|
|
385
|
+
const lastSegment = bareId.slice(bareId.lastIndexOf("/") + 1);
|
|
386
|
+
const exact = /* @__PURE__ */ new Set();
|
|
387
|
+
const hints = /* @__PURE__ */ new Set();
|
|
388
|
+
for (const code of registry) {
|
|
389
|
+
if (typeof code !== "string" || !code) continue;
|
|
390
|
+
const codeLower = code.toLowerCase();
|
|
391
|
+
if (!malformed && codeLower === lower) return {
|
|
392
|
+
ok: true,
|
|
393
|
+
id: code,
|
|
394
|
+
form: provider ? "canonical" : "bare"
|
|
395
|
+
};
|
|
396
|
+
const i = codeLower.indexOf("/");
|
|
397
|
+
if (i < 0) continue;
|
|
398
|
+
const codeBare = codeLower.slice(i + 1);
|
|
399
|
+
if (bareId && codeBare === bareId) exact.add(code);
|
|
400
|
+
else if (lastSegment && codeBare.slice(codeBare.lastIndexOf("/") + 1) === lastSegment) hints.add(code);
|
|
401
|
+
}
|
|
402
|
+
const sorted = [
|
|
403
|
+
...exact
|
|
404
|
+
].sort();
|
|
405
|
+
if (!provider && !malformed && sorted.length === 1) return {
|
|
406
|
+
ok: true,
|
|
407
|
+
id: sorted[0],
|
|
408
|
+
form: "bare"
|
|
409
|
+
};
|
|
410
|
+
if (!provider && !malformed && sorted.length > 1) {
|
|
411
|
+
return {
|
|
412
|
+
ok: false,
|
|
413
|
+
reason: "ambiguous",
|
|
414
|
+
requested,
|
|
415
|
+
candidates: sorted
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
return {
|
|
419
|
+
ok: false,
|
|
420
|
+
reason: "unknown",
|
|
421
|
+
requested,
|
|
422
|
+
candidates: sorted.length ? sorted : [
|
|
423
|
+
...hints
|
|
424
|
+
].sort()
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
function modelUnresolvedMessage(r) {
|
|
428
|
+
if (r.reason === "empty") return "model pin is empty \u2014 pin an approved model (provider/model) or omit `model`";
|
|
429
|
+
if (r.reason === "ambiguous") {
|
|
430
|
+
return `model "${r.requested}" does not resolve to one approved model \u2014 it names ${r.candidates.length}; pin one of: ${r.candidates.join(", ")}`;
|
|
431
|
+
}
|
|
432
|
+
return r.candidates.length ? `model "${r.requested}" does not resolve to an approved model \u2014 accepted forms: ${r.candidates.join(", ")}` : `model "${r.requested}" does not resolve to an approved model \u2014 accepted forms are the registry's provider-prefixed ids (provider/model) or a bare id that names exactly one of them`;
|
|
433
|
+
}
|
|
356
434
|
function isImplicitModelSelectionSource(source) {
|
|
357
435
|
return source !== void 0 && IMPLICIT_MODEL_SELECTION_SOURCES.includes(source);
|
|
358
436
|
}
|
|
@@ -466,6 +544,11 @@ function isDeviceCredentialPrincipal(context) {
|
|
|
466
544
|
function hasDeviceCredentialType(value3) {
|
|
467
545
|
return DeviceCredentialClaimSchema.safeParse(value3).success;
|
|
468
546
|
}
|
|
547
|
+
function sessionAuthTime(context) {
|
|
548
|
+
if (!context || context.credential.type !== "firstPartySession") return void 0;
|
|
549
|
+
const authTime = context.authTime;
|
|
550
|
+
return typeof authTime === "number" && Number.isInteger(authTime) && authTime >= 0 && authTime <= SESSION_AUTH_TIME_MAX_S ? authTime : void 0;
|
|
551
|
+
}
|
|
469
552
|
function isTypedApiKeyPrincipal(context) {
|
|
470
553
|
return context?.subject.subjectType === "apiKey" && context.credential.type === "apiKey" && !context.compatibility;
|
|
471
554
|
}
|
|
@@ -638,6 +721,39 @@ function scheduledWorkflowRunId(jobId, scheduledTime) {
|
|
|
638
721
|
const key = typeof scheduledTime === "number" ? String(scheduledTime) : scheduledTimeKey(scheduledTime);
|
|
639
722
|
return `${WORKFLOW_SCHEDULED_RUN_ID_PREFIX}${jobId}_${key}`;
|
|
640
723
|
}
|
|
724
|
+
function renderWorkflowScheduleKeyTemplate(template3, ctx) {
|
|
725
|
+
if (!template3) return void 0;
|
|
726
|
+
const read = /* @__PURE__ */ __name2((path3) => path3.split(".").reduce((o, k) => o && typeof o === "object" ? o[k] : void 0, ctx.input), "read");
|
|
727
|
+
let unresolved = false;
|
|
728
|
+
const out = template3.replace(/\$\{\s*([a-zA-Z0-9_.]+)\s*\}/g, (_m, expr) => {
|
|
729
|
+
let v = "";
|
|
730
|
+
if (expr === "scheduledTime") v = ctx.scheduledTime ?? "";
|
|
731
|
+
else if (expr.startsWith("input.")) v = read(expr.slice("input.".length));
|
|
732
|
+
const s = v === void 0 || v === null ? "" : String(v);
|
|
733
|
+
if (s === "") unresolved = true;
|
|
734
|
+
return s;
|
|
735
|
+
});
|
|
736
|
+
if (unresolved) return void 0;
|
|
737
|
+
const key = out.slice(0, WORKFLOW_SCHEDULE_KEY_MAX);
|
|
738
|
+
return key && /^[A-Za-z0-9:_\-.\/]+$/.test(key) ? key : void 0;
|
|
739
|
+
}
|
|
740
|
+
function scheduledWorkflowIdempotencyKey(jobId, rendered) {
|
|
741
|
+
const head = `${WORKFLOW_SCHEDULE_IDEMPOTENCY_KEY_PREFIX}${jobId}:`;
|
|
742
|
+
if (head.length + rendered.length <= WORKFLOW_SCHEDULE_KEY_MAX) return `${head}${rendered}`;
|
|
743
|
+
const digest = stableKeyDigest(rendered);
|
|
744
|
+
const room = WORKFLOW_SCHEDULE_KEY_MAX - head.length - digest.length - 1;
|
|
745
|
+
return `${head}${rendered.slice(0, Math.max(0, room))}~${digest}`;
|
|
746
|
+
}
|
|
747
|
+
function stableKeyDigest(s) {
|
|
748
|
+
let a = 2166136261;
|
|
749
|
+
let b = 84696351;
|
|
750
|
+
for (let i = 0; i < s.length; i++) {
|
|
751
|
+
const c = s.charCodeAt(i);
|
|
752
|
+
a = Math.imul(a ^ c, 16777619);
|
|
753
|
+
b = Math.imul(b ^ c, 16777619) ^ b >>> 13;
|
|
754
|
+
}
|
|
755
|
+
return (a >>> 0).toString(16).padStart(8, "0") + (b >>> 0).toString(16).padStart(8, "0");
|
|
756
|
+
}
|
|
641
757
|
function workflowOperationId(runId, stepId, billingEpoch) {
|
|
642
758
|
return `${WORKFLOW_OPERATION_ID_PREFIX}${runId}:${stepId}:${billingEpoch}`;
|
|
643
759
|
}
|
|
@@ -941,7 +1057,59 @@ function extractSingleJsonValue(text) {
|
|
|
941
1057
|
};
|
|
942
1058
|
}
|
|
943
1059
|
}
|
|
944
|
-
|
|
1060
|
+
function agentFeatureCatalogDefault(featureName) {
|
|
1061
|
+
return DEFAULT_ON_AGENT_FEATURES.includes(featureName);
|
|
1062
|
+
}
|
|
1063
|
+
function hasExplicitFeatureActive(row) {
|
|
1064
|
+
return typeof row?.active === "boolean";
|
|
1065
|
+
}
|
|
1066
|
+
function effectiveFeatureActive(row, catalogDefault) {
|
|
1067
|
+
return hasExplicitFeatureActive(row) ? row.active : catalogDefault;
|
|
1068
|
+
}
|
|
1069
|
+
function resolveEffectiveFeature(row, catalogDefault) {
|
|
1070
|
+
return hasExplicitFeatureActive(row) ? {
|
|
1071
|
+
active: row.active,
|
|
1072
|
+
source: "agent",
|
|
1073
|
+
default: catalogDefault
|
|
1074
|
+
} : {
|
|
1075
|
+
active: catalogDefault,
|
|
1076
|
+
source: "default",
|
|
1077
|
+
default: catalogDefault
|
|
1078
|
+
};
|
|
1079
|
+
}
|
|
1080
|
+
function isFeatureRow(value3) {
|
|
1081
|
+
return typeof value3 === "object" && value3 !== null;
|
|
1082
|
+
}
|
|
1083
|
+
function effectiveAgentFeatureRows(base, override) {
|
|
1084
|
+
const merged = /* @__PURE__ */ new Map();
|
|
1085
|
+
for (const [name, row] of Object.entries(base ?? {})) {
|
|
1086
|
+
if (isFeatureRow(row)) merged.set(name, {
|
|
1087
|
+
row,
|
|
1088
|
+
origin: "baseAgent"
|
|
1089
|
+
});
|
|
1090
|
+
}
|
|
1091
|
+
for (const [name, row] of Object.entries(override ?? {})) {
|
|
1092
|
+
if (isFeatureRow(row)) merged.set(name, {
|
|
1093
|
+
row,
|
|
1094
|
+
origin: "subAgent"
|
|
1095
|
+
});
|
|
1096
|
+
}
|
|
1097
|
+
return {
|
|
1098
|
+
rows: Object.fromEntries([
|
|
1099
|
+
...merged
|
|
1100
|
+
].map(([name, e]) => [
|
|
1101
|
+
name,
|
|
1102
|
+
e.row
|
|
1103
|
+
])),
|
|
1104
|
+
origins: Object.fromEntries([
|
|
1105
|
+
...merged
|
|
1106
|
+
].map(([name, e]) => [
|
|
1107
|
+
name,
|
|
1108
|
+
e.origin
|
|
1109
|
+
]))
|
|
1110
|
+
};
|
|
1111
|
+
}
|
|
1112
|
+
var __defProp2, __name2, CHANNEL_SEND_CHANNELS, REVIEWABLE_ACTION_EXECUTE_TOOL_ALLOWLIST, REVIEWABLE_MCP_SEND_TOOL_SUFFIX, MCP_TOOL_READ_VERB_RE, MCP_DRAFT_CREATE_VERBS, NON_INTERACTIVE_CHANNELS, RICH_PARTS_MESSAGE_ID_PREFIX, SCREENSHOT_MESSAGE_ID_PREFIX, BROWSER_COMMANDS, BROWSER_COMMAND_NAMES, DESKTOP_FILE_COMMANDS, DESKTOP_FILE_COMMAND_SET, MODEL_ID_BYOK_PROVIDERS, REASONING_EFFORT_VALUES, IMPLICIT_MODEL_SELECTION_SOURCES, PLATFORM_FALLBACK_MODEL_SOURCE, AGENT_NAME_TOKEN, DEFAULT_PERSONA_GUIDE, PERSONAL_SPACE_STARTING_PERSONA, CORE_DRAINING_CODE, CORE_DRAINING_DEFAULT_RETRY_MS, CORE_DRAINING_MAX_RETRY_MS, VoiceNameSchema, PluginProviderSchema, RealtimeProviderSchema, PluginClassSchema, ModelDescriptorSchema, InferenceModelSchema, PluginModelSchema, RealtimeModelSchema, LuaVoiceModelSchema, TurnDetectionSchema, InterruptionSchema, BuiltinAudioClipSchema, AudioConfigSchema, BackgroundAudioEntrySchema, BackgroundAudioSchema, LuaVoiceConfigInnerSchema, LuaVoiceConfigSchema, LuaVoiceRefSchema, LUA_JOB_DEFAULT_TIMEOUT_SECONDS, LUA_JOB_MIN_TIMEOUT_SECONDS, LUA_JOB_MAX_TIMEOUT_SECONDS, TEMPLATE_TRIGGER_URL_ENV_PREFIX, SUBJECT_TYPES, SubjectTypeSchema, CREDENTIAL_TYPES, CredentialTypeSchema, DEVICE_OPERATIONS, DeviceOperationSchema, DEVICE_SCOPE_BY_OPERATION, DeviceBindingSchema, IdSchema, SESSION_AUTH_TIME_MAX_S, 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_SCHEDULE_KEY_MAX, WORKFLOW_SCHEDULE_IDEMPOTENCY_KEY_PREFIX, WORKFLOW_OPERATION_ID_PREFIX, WORKFLOW_CONNECTION_KEY_RE, WORKFLOW_SIGNAL_PAYLOAD_MAX_BYTES, WORKFLOW_RESOLVE_OUTPUT_MAX_BYTES, WORKFLOW_RETRY_BACKOFFS, WORKFLOW_RETRY_MIN_ATTEMPTS, WORKFLOW_RETRY_POLICY_KEYS, WORKFLOW_RETRY_MAX_ATTEMPTS, WORKFLOW_RETRY_ENGINE_KEYS, WORKFLOW_JOB_RESOURCES, WORKFLOW_SIDE_EFFECTS, WORKFLOW_JOB_RANGES, WORKFLOW_JOB_RANGE_MEMBERS, WORKFLOW_SINGLE_STEP_TYPES, WORKFLOW_HITL_ENTRY_TYPES, WORKFLOW_ARM_ENTRY_TYPES, WORKFLOW_HITL_ARM_CONTAINERS, WORKFLOW_GRAPH_ENTRY_STEP_KINDS, WORKFLOW_ARM_ENTRY_STEP_KINDS, WORKFLOW_BUDGET_MAX_DURATION_SECONDS, REDACTED_PLACEHOLDER, PROVIDER_MESSAGE_MAX_CHARS, ERROR_MESSAGE_MAX_CHARS, SECRET_LITERAL_PATTERNS, SECRET_NAME, SECRET_PAIR_PATTERNS, GROUP_COUNT, WORKFLOW_SECRET_KEY_RE, WORKFLOW_RESERVED_SECRET_KEYS, SCRUB_INPUT_MAX_CHARS, SCRUB_CUT_BACKOFF_CHARS, WORKFLOW_AUDIT_EVENTS, WORKFLOW_AUDIT_METADATA_MAX_BYTES, INDENT, WRAP_WIDTH, NOUNS, GET_TOOL_NAMES, PREAMBLE, WORKFLOW_APPROVAL_OUTPUT_DECISIONS, WORKFLOW_APPROVAL_OUTPUT_SCHEMA, JSON_FENCE_RE, DEFAULT_ON_AGENT_FEATURES;
|
|
945
1113
|
var init_dist = __esm({
|
|
946
1114
|
"../shared-types/dist/index.mjs"() {
|
|
947
1115
|
"use strict";
|
|
@@ -1258,6 +1426,16 @@ var init_dist = __esm({
|
|
|
1258
1426
|
__name2(isDesktopFileCommandName, "isDesktopFileCommandName");
|
|
1259
1427
|
__name(isDesktopFileSessionId, "isDesktopFileSessionId");
|
|
1260
1428
|
__name2(isDesktopFileSessionId, "isDesktopFileSessionId");
|
|
1429
|
+
MODEL_ID_BYOK_PROVIDERS = [
|
|
1430
|
+
"azure",
|
|
1431
|
+
"bedrock"
|
|
1432
|
+
];
|
|
1433
|
+
__name(isModelIdSentinel, "isModelIdSentinel");
|
|
1434
|
+
__name2(isModelIdSentinel, "isModelIdSentinel");
|
|
1435
|
+
__name(normalizeModelId, "normalizeModelId");
|
|
1436
|
+
__name2(normalizeModelId, "normalizeModelId");
|
|
1437
|
+
__name(modelUnresolvedMessage, "modelUnresolvedMessage");
|
|
1438
|
+
__name2(modelUnresolvedMessage, "modelUnresolvedMessage");
|
|
1261
1439
|
REASONING_EFFORT_VALUES = [
|
|
1262
1440
|
"off",
|
|
1263
1441
|
"minimal",
|
|
@@ -1676,6 +1854,7 @@ This text is who you are for this person. As you learn them, their name, their w
|
|
|
1676
1854
|
}
|
|
1677
1855
|
});
|
|
1678
1856
|
IdSchema = z2.string().min(1).max(256);
|
|
1857
|
+
SESSION_AUTH_TIME_MAX_S = 4102444800;
|
|
1679
1858
|
PrincipalDescriptorSchema = z2.object({
|
|
1680
1859
|
subjectType: SubjectTypeSchema,
|
|
1681
1860
|
subjectId: IdSchema
|
|
@@ -1724,7 +1903,8 @@ This text is who you are for this person. As you learn them, their name, their w
|
|
|
1724
1903
|
owner: PrincipalOwnerSchema.optional(),
|
|
1725
1904
|
compatibility: z2.object({
|
|
1726
1905
|
mode: z2.literal("legacy-owner-delegation")
|
|
1727
|
-
}).strict().optional()
|
|
1906
|
+
}).strict().optional(),
|
|
1907
|
+
authTime: z2.number().int().nonnegative().max(SESSION_AUTH_TIME_MAX_S).optional()
|
|
1728
1908
|
}).strict();
|
|
1729
1909
|
DeviceCredentialPrincipalContextSchema = z2.object({
|
|
1730
1910
|
version: z2.literal(1),
|
|
@@ -1780,6 +1960,8 @@ This text is who you are for this person. As you learn them, their name, their w
|
|
|
1780
1960
|
}).passthrough();
|
|
1781
1961
|
__name(hasDeviceCredentialType, "hasDeviceCredentialType");
|
|
1782
1962
|
__name2(hasDeviceCredentialType, "hasDeviceCredentialType");
|
|
1963
|
+
__name(sessionAuthTime, "sessionAuthTime");
|
|
1964
|
+
__name2(sessionAuthTime, "sessionAuthTime");
|
|
1783
1965
|
__name(isTypedApiKeyPrincipal, "isTypedApiKeyPrincipal");
|
|
1784
1966
|
__name2(isTypedApiKeyPrincipal, "isTypedApiKeyPrincipal");
|
|
1785
1967
|
__name(typedApiKeyPrincipalId, "typedApiKeyPrincipalId");
|
|
@@ -2011,6 +2193,14 @@ This text is who you are for this person. As you learn them, their name, their w
|
|
|
2011
2193
|
__name2(isScheduledWorkflowRunId, "isScheduledWorkflowRunId");
|
|
2012
2194
|
__name(scheduledWorkflowRunId, "scheduledWorkflowRunId");
|
|
2013
2195
|
__name2(scheduledWorkflowRunId, "scheduledWorkflowRunId");
|
|
2196
|
+
WORKFLOW_SCHEDULE_KEY_MAX = 128;
|
|
2197
|
+
__name(renderWorkflowScheduleKeyTemplate, "renderWorkflowScheduleKeyTemplate");
|
|
2198
|
+
__name2(renderWorkflowScheduleKeyTemplate, "renderWorkflowScheduleKeyTemplate");
|
|
2199
|
+
WORKFLOW_SCHEDULE_IDEMPOTENCY_KEY_PREFIX = "sched:";
|
|
2200
|
+
__name(scheduledWorkflowIdempotencyKey, "scheduledWorkflowIdempotencyKey");
|
|
2201
|
+
__name2(scheduledWorkflowIdempotencyKey, "scheduledWorkflowIdempotencyKey");
|
|
2202
|
+
__name(stableKeyDigest, "stableKeyDigest");
|
|
2203
|
+
__name2(stableKeyDigest, "stableKeyDigest");
|
|
2014
2204
|
WORKFLOW_OPERATION_ID_PREFIX = "wf:";
|
|
2015
2205
|
__name(workflowOperationId, "workflowOperationId");
|
|
2016
2206
|
__name2(workflowOperationId, "workflowOperationId");
|
|
@@ -2456,6 +2646,23 @@ listed here; never invent a target.`;
|
|
|
2456
2646
|
JSON_FENCE_RE = /```(?:json)?[ \t]*\r?\n([\s\S]*?)\r?\n?```/g;
|
|
2457
2647
|
__name(extractSingleJsonValue, "extractSingleJsonValue");
|
|
2458
2648
|
__name2(extractSingleJsonValue, "extractSingleJsonValue");
|
|
2649
|
+
DEFAULT_ON_AGENT_FEATURES = [
|
|
2650
|
+
"workflows",
|
|
2651
|
+
"workflowCompose",
|
|
2652
|
+
"observationalMemory"
|
|
2653
|
+
];
|
|
2654
|
+
__name(agentFeatureCatalogDefault, "agentFeatureCatalogDefault");
|
|
2655
|
+
__name2(agentFeatureCatalogDefault, "agentFeatureCatalogDefault");
|
|
2656
|
+
__name(hasExplicitFeatureActive, "hasExplicitFeatureActive");
|
|
2657
|
+
__name2(hasExplicitFeatureActive, "hasExplicitFeatureActive");
|
|
2658
|
+
__name(effectiveFeatureActive, "effectiveFeatureActive");
|
|
2659
|
+
__name2(effectiveFeatureActive, "effectiveFeatureActive");
|
|
2660
|
+
__name(resolveEffectiveFeature, "resolveEffectiveFeature");
|
|
2661
|
+
__name2(resolveEffectiveFeature, "resolveEffectiveFeature");
|
|
2662
|
+
__name(isFeatureRow, "isFeatureRow");
|
|
2663
|
+
__name2(isFeatureRow, "isFeatureRow");
|
|
2664
|
+
__name(effectiveAgentFeatureRows, "effectiveAgentFeatureRows");
|
|
2665
|
+
__name2(effectiveAgentFeatureRows, "effectiveAgentFeatureRows");
|
|
2459
2666
|
}
|
|
2460
2667
|
});
|
|
2461
2668
|
|
|
@@ -2666,6 +2873,129 @@ function resolveMapping(cfg, ctx) {
|
|
|
2666
2873
|
value: result
|
|
2667
2874
|
};
|
|
2668
2875
|
}
|
|
2876
|
+
function describeApproverSpecRefusal(spec) {
|
|
2877
|
+
const raw = spec === void 0 ? "undefined" : JSON.stringify(spec) ?? String(spec);
|
|
2878
|
+
const written = raw.length > APPROVER_WRITTEN_MAX ? `${raw.slice(0, APPROVER_WRITTEN_MAX - 1)}\u2026` : raw;
|
|
2879
|
+
const users = typeof spec === "object" && spec !== null ? spec.users : void 0;
|
|
2880
|
+
const approver = typeof users === "string" && USER_ID_SHAPED_RE.test(users) ? {
|
|
2881
|
+
users: [
|
|
2882
|
+
users
|
|
2883
|
+
]
|
|
2884
|
+
} : "creator";
|
|
2885
|
+
const message = `approver ${written} is not an approver \u2014 legal: ${APPROVER_SPEC_SHAPES.join(" | ")}. 'creator' is the person who started the run: write approver:'creator' for "ask me" / "I approve"; {users:[\u2026]} takes user ids, never emails, names or {type:'user'}` + (approver === "creator" ? "" : `; here: approver:${JSON.stringify(approver)}`);
|
|
2886
|
+
return {
|
|
2887
|
+
approver,
|
|
2888
|
+
written,
|
|
2889
|
+
message
|
|
2890
|
+
};
|
|
2891
|
+
}
|
|
2892
|
+
function bindingRootsOk(template22) {
|
|
2893
|
+
const refs = [
|
|
2894
|
+
...template22.matchAll(/\$\{\s*([A-Za-z_$][\w$]*)/g)
|
|
2895
|
+
].map((m) => m[1]);
|
|
2896
|
+
return refs.length > 0 && refs.every((r) => BINDING_ROOTS.includes(r));
|
|
2897
|
+
}
|
|
2898
|
+
function isTemplateBinding(v) {
|
|
2899
|
+
return typeof v === "object" && v !== null && typeof v.template === "string";
|
|
2900
|
+
}
|
|
2901
|
+
function approvalEditable(node) {
|
|
2902
|
+
if (node.editable === true) return true;
|
|
2903
|
+
if (node.editable === false) return false;
|
|
2904
|
+
return Array.isArray(node.editablePaths) && node.editablePaths.length > 0;
|
|
2905
|
+
}
|
|
2906
|
+
function validateApproverBlock(node, opts = {
|
|
2907
|
+
path: "approval"
|
|
2908
|
+
}) {
|
|
2909
|
+
const issues = [];
|
|
2910
|
+
const push = /* @__PURE__ */ __name3((code, path3, message, severity = "error") => issues.push({
|
|
2911
|
+
code,
|
|
2912
|
+
path: path3,
|
|
2913
|
+
severity,
|
|
2914
|
+
message
|
|
2915
|
+
}), "push");
|
|
2916
|
+
const checkSpec = /* @__PURE__ */ __name3((spec, path3) => {
|
|
2917
|
+
const r = ApproverSpecSchema.safeParse(spec);
|
|
2918
|
+
if (!r.success) {
|
|
2919
|
+
const users = spec?.users;
|
|
2920
|
+
if (Array.isArray(users) && users.length > APPROVER_SPEC_MAX_USERS) push("cap-exceeded", path3, `at most ${APPROVER_SPEC_MAX_USERS} users`);
|
|
2921
|
+
else {
|
|
2922
|
+
const refusal = describeApproverSpecRefusal(spec);
|
|
2923
|
+
issues.push({
|
|
2924
|
+
code: "approver-invalid",
|
|
2925
|
+
path: path3,
|
|
2926
|
+
severity: "error",
|
|
2927
|
+
message: refusal.message,
|
|
2928
|
+
repair: {
|
|
2929
|
+
approver: refusal.approver,
|
|
2930
|
+
written: refusal.written
|
|
2931
|
+
}
|
|
2932
|
+
});
|
|
2933
|
+
}
|
|
2934
|
+
return;
|
|
2935
|
+
}
|
|
2936
|
+
const s = r.data;
|
|
2937
|
+
if (typeof s === "object") {
|
|
2938
|
+
if ("governance" in s && !opts.governanceEnabled) push("approver-governance-unavailable", path3, "governance reviewer routing is not enabled for this deployment");
|
|
2939
|
+
if ("group" in s && typeof s.group === "string" && !opts.scimEnabled && opts.idpGroups?.includes(s.group)) push("approver-idp-group-unavailable", path3, "IdP-group approvers are not enabled for this deployment");
|
|
2940
|
+
const binding = "users" in s ? s.users : "role" in s ? s.role : "group" in s ? s.group : void 0;
|
|
2941
|
+
if (isTemplateBinding(binding)) {
|
|
2942
|
+
if (!bindingRootsOk(binding.template)) push("approver-binding-invalid", `${path3}.template`, "binding root must be initData / stepResults / requestContext / state");
|
|
2943
|
+
if ("users" in s && opts.customerReachable) push("approver-binding-customer-reachable", `${path3}.users`, "a customer-reachable workflow may not bind its approver list");
|
|
2944
|
+
}
|
|
2945
|
+
}
|
|
2946
|
+
}, "checkSpec");
|
|
2947
|
+
if (node.approver !== void 0) checkSpec(node.approver, `${opts.path}.approver`);
|
|
2948
|
+
if (node.fourEyes !== void 0) {
|
|
2949
|
+
const r = FourEyesSchema.safeParse(node.fourEyes);
|
|
2950
|
+
if (!r.success) push("approver-invalid", `${opts.path}.fourEyes`, "fourEyes needs { edit, approve } approver specs");
|
|
2951
|
+
else {
|
|
2952
|
+
checkSpec(r.data.edit, `${opts.path}.fourEyes.edit`);
|
|
2953
|
+
checkSpec(r.data.approve, `${opts.path}.fourEyes.approve`);
|
|
2954
|
+
}
|
|
2955
|
+
if (!approvalEditable(node)) push("four-eyes-requires-editable", `${opts.path}.fourEyes`, "fourEyes requires editable:true");
|
|
2956
|
+
if (node.approver !== void 0) push("four-eyes-overrides-approver", `${opts.path}.approver`, "fourEyes replaces approver", "warning");
|
|
2957
|
+
if (node.itemsPath) push("four-eyes-items-unsupported", `${opts.path}.fourEyes`, "fourEyes cannot combine with itemsPath");
|
|
2958
|
+
}
|
|
2959
|
+
if (node.excludeInitiator && (node.approver === void 0 || node.approver === "creator") && !node.fourEyes) push("approver-excludes-only-candidate", `${opts.path}.excludeInitiator`, "'creator' with excludeInitiator leaves no approver");
|
|
2960
|
+
if (Array.isArray(node.onTimeout)) {
|
|
2961
|
+
const chain = node.onTimeout;
|
|
2962
|
+
const hops = chain.filter((m) => typeof m === "object" && m !== null && "escalateTo" in m);
|
|
2963
|
+
if (hops.length > ESCALATION_MAX_HOPS) push("escalation-chain-too-long", `${opts.path}.onTimeout`, `at most ${ESCALATION_MAX_HOPS} hops`);
|
|
2964
|
+
const last = chain[chain.length - 1];
|
|
2965
|
+
if (typeof last === "object" && last !== null) push("escalation-chain-not-terminal", `${opts.path}.onTimeout`, "a chain must end in deny | cancel-run | fail");
|
|
2966
|
+
hops.forEach((h, i) => checkSpec(h.escalateTo, `${opts.path}.onTimeout[${i}].escalateTo`));
|
|
2967
|
+
} else if (typeof node.onTimeout === "object" && node.onTimeout !== null) {
|
|
2968
|
+
checkSpec(node.onTimeout.escalateTo, `${opts.path}.onTimeout.escalateTo`);
|
|
2969
|
+
}
|
|
2970
|
+
return issues;
|
|
2971
|
+
}
|
|
2972
|
+
function liftRenderedApprover(row, rendered) {
|
|
2973
|
+
const text = (rendered ?? "").trim();
|
|
2974
|
+
if (!text) return null;
|
|
2975
|
+
if (row === "users") {
|
|
2976
|
+
let members = null;
|
|
2977
|
+
if (text.startsWith("[")) {
|
|
2978
|
+
try {
|
|
2979
|
+
members = JSON.parse(text);
|
|
2980
|
+
} catch {
|
|
2981
|
+
return null;
|
|
2982
|
+
}
|
|
2983
|
+
} else members = text.split(",").map((s) => s.trim());
|
|
2984
|
+
if (!Array.isArray(members) || members.length === 0 || members.length > APPROVER_SPEC_MAX_USERS) return null;
|
|
2985
|
+
if (!members.every((m) => typeof m === "string" && m.length > 0 && m.length <= 128)) return null;
|
|
2986
|
+
return {
|
|
2987
|
+
users: [
|
|
2988
|
+
...new Set(members)
|
|
2989
|
+
].sort()
|
|
2990
|
+
};
|
|
2991
|
+
}
|
|
2992
|
+
if (text.length > 128 || text.startsWith("[") || text.startsWith("{")) return null;
|
|
2993
|
+
return row === "role" ? {
|
|
2994
|
+
role: text
|
|
2995
|
+
} : {
|
|
2996
|
+
group: text
|
|
2997
|
+
};
|
|
2998
|
+
}
|
|
2669
2999
|
function workspaceTemplatePath(template22) {
|
|
2670
3000
|
const key = template22.trim();
|
|
2671
3001
|
const expr = WORKSPACE_TEMPLATE_EXPR_RE.exec(key);
|
|
@@ -2682,6 +3012,9 @@ function retryBackoffs() {
|
|
|
2682
3012
|
function sleepUntilUnsupportedMessage(id) {
|
|
2683
3013
|
return `the engine does not execute \`sleepUntil\` yet (node "${id}") \u2014 replace it with a \`sleep\` node with a \`duration\` in ms, e.g. { type: 'sleep', id: '${id}', duration: ${SLEEP_UNTIL_REPLACEMENT.duration} }`;
|
|
2684
3014
|
}
|
|
3015
|
+
function armSubrunUnsupportedMessage(id, workflowId) {
|
|
3016
|
+
return `the engine does not execute the implicit \`${workflowId}\` arm subrun (node "${id}") \u2014 a [map, step] container arm is the step itself with the map as its \`input\` since lua-cli 3.32.4; re-run \`lua compile\` with the current CLI (a hand-written artifact: put the map on the arm node's \`input\` and drop the \`workflow\` wrapper)`;
|
|
3017
|
+
}
|
|
2685
3018
|
function fillPolicy(node, defaultTimeout) {
|
|
2686
3019
|
if (node.tier === void 0 && node.workspace !== void 0 && node.workspace !== "inherit") node.tier = "job";
|
|
2687
3020
|
if (node.timeoutSeconds === void 0) node.timeoutSeconds = node.tier === "job" ? WORKFLOW_JOB_DEFAULT_TIMEOUT_SECONDS : defaultTimeout;
|
|
@@ -2707,7 +3040,6 @@ function fillSingle(node) {
|
|
|
2707
3040
|
fillPolicy(node, WORKFLOW_STEP_DEFAULT_TIMEOUT_SECONDS);
|
|
2708
3041
|
return;
|
|
2709
3042
|
case "workflow":
|
|
2710
|
-
if (node.workflowId === WORKFLOW_ARM_SUBRUN_ID && Array.isArray(node.graph) && node.graph[1]) fillSingle(node.graph[1]);
|
|
2711
3043
|
return;
|
|
2712
3044
|
}
|
|
2713
3045
|
}
|
|
@@ -2839,8 +3171,14 @@ function nodeStepRefs(entry) {
|
|
|
2839
3171
|
case "agent": {
|
|
2840
3172
|
const a = entry;
|
|
2841
3173
|
const p = a.promptTemplate;
|
|
2842
|
-
|
|
3174
|
+
const prompt = typeof p === "string" ? templateStepRefs(p) : p && "template" in p ? templateStepRefs(p.template) : [];
|
|
3175
|
+
return [
|
|
3176
|
+
...prompt,
|
|
3177
|
+
...mapConfigStepRefs(a.input)
|
|
3178
|
+
];
|
|
2843
3179
|
}
|
|
3180
|
+
case "step":
|
|
3181
|
+
return mapConfigStepRefs(entry.input);
|
|
2844
3182
|
case "tool":
|
|
2845
3183
|
return mapConfigStepRefs(entry.input);
|
|
2846
3184
|
case "workflow":
|
|
@@ -2957,7 +3295,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2957
3295
|
const id = singleId(node);
|
|
2958
3296
|
const unknown = unknownWorkflowRetryMembers(r);
|
|
2959
3297
|
if (unknown.length) err("invalid-envelope", workflowRetryUnknownMembersMessage(unknown), `${path3}.retry`, id);
|
|
2960
|
-
if (
|
|
3298
|
+
if (!isWithinWorkflowRetryAttempts(r.maxAttempts)) {
|
|
2961
3299
|
const over = typeof r.maxAttempts === "number" && r.maxAttempts > WORKFLOW_RETRY_MAX_ATTEMPTS;
|
|
2962
3300
|
err(over ? "cap-exceeded" : "invalid-envelope", workflowRetryMaxAttemptsMessage(r.maxAttempts), `${path3}.retry.maxAttempts`, id);
|
|
2963
3301
|
}
|
|
@@ -3057,6 +3395,21 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
3057
3395
|
err("job-tier-provider-unsupported", `model provider '${provider}' is outside LUA_WF_JOB_PROVIDERS [${opts.policy.jobProviders.join(", ")}]`, `${path3}.model`, id);
|
|
3058
3396
|
}
|
|
3059
3397
|
}, "checkTier");
|
|
3398
|
+
const checkModel = /* @__PURE__ */ __name3((node, path3) => {
|
|
3399
|
+
if (node.type !== "agent" || typeof node.model !== "string") return;
|
|
3400
|
+
const registry = opts.approvedModels;
|
|
3401
|
+
if (registry === void 0) return;
|
|
3402
|
+
const id = singleId(node);
|
|
3403
|
+
if (registry === "unavailable") {
|
|
3404
|
+
const pin = node.model.trim();
|
|
3405
|
+
if (pin && !normalizeModelId(pin, []).ok) {
|
|
3406
|
+
warn("model-unresolved", `model "${pin}" could not be checked against the approved-model registry (unavailable at push) \u2014 it dispatches only if it resolves there (a provider/model registry code, or a bare id exactly one approved model carries)`, `${path3}.model`, id);
|
|
3407
|
+
}
|
|
3408
|
+
return;
|
|
3409
|
+
}
|
|
3410
|
+
const resolved = normalizeModelId(node.model, registry);
|
|
3411
|
+
if (!resolved.ok) err("model-unresolved", modelUnresolvedMessage(resolved), `${path3}.model`, id);
|
|
3412
|
+
}, "checkModel");
|
|
3060
3413
|
const checkWorkspace = /* @__PURE__ */ __name3((node, path3) => {
|
|
3061
3414
|
const id = singleId(node);
|
|
3062
3415
|
const ws = workspaceOf(node);
|
|
@@ -3109,38 +3462,29 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
3109
3462
|
}
|
|
3110
3463
|
}, "checkMapMembers");
|
|
3111
3464
|
const checkInputShape = /* @__PURE__ */ __name3((node, path3) => {
|
|
3112
|
-
if (node.type !== "tool" && node.type !== "workflow") return;
|
|
3113
3465
|
const input = node.input;
|
|
3114
3466
|
if (input === void 0) return;
|
|
3467
|
+
const id = singleId(node);
|
|
3115
3468
|
if (input !== null && typeof input === "object" && !Array.isArray(input)) {
|
|
3116
|
-
checkMapMembers(input, `${path3}.input`,
|
|
3469
|
+
checkMapMembers(input, `${path3}.input`, id);
|
|
3117
3470
|
return;
|
|
3118
3471
|
}
|
|
3119
|
-
err("invalid-envelope", `\`input\` must be an object map \u2014 each member a binding descriptor ({initData:true, path} | {step, path} | {value} | {template} | {requestContextPath}) or a JSON literal (got ${JSON.stringify(input)})`, `${path3}.input`,
|
|
3472
|
+
err("invalid-envelope", `\`input\` must be an object map \u2014 each member a binding descriptor ({initData:true, path} | {step, path} | {value} | {template} | {requestContextPath}) or a JSON literal (got ${JSON.stringify(input)})`, `${path3}.input`, id);
|
|
3120
3473
|
}, "checkInputShape");
|
|
3474
|
+
const checkBodyInput = /* @__PURE__ */ __name3((body, path3, container) => {
|
|
3475
|
+
if (body.type === "workflow" || body.input === void 0) return;
|
|
3476
|
+
err("arm-input-unsupported", container === "foreach" ? `a foreach body receives each item as its input \u2014 drop \`input\` on "${singleId(body)}" and map the items before the foreach instead` : `a loop body receives the previous output as its input \u2014 drop \`input\` on "${singleId(body)}" and put the map before the loop instead`, `${path3}.input`, singleId(body));
|
|
3477
|
+
}, "checkBodyInput");
|
|
3121
3478
|
const checkSingle = /* @__PURE__ */ __name3((node, path3, depth) => {
|
|
3122
3479
|
recordOutputSchema(node);
|
|
3123
|
-
if (node.type === "workflow" && node.workflowId ===
|
|
3480
|
+
if (node.type === "workflow" && (typeof node.workflowId !== "string" || node.workflowId.length === 0)) {
|
|
3124
3481
|
checkId(node.id, path3);
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
err("container-arm-empty", "a bare mapping arm has nothing to run", `${path3}.graph.1`, node.id);
|
|
3132
|
-
return;
|
|
3133
|
-
}
|
|
3134
|
-
const inner = body[1];
|
|
3135
|
-
if (isHitlNode(inner)) {
|
|
3136
|
-
err("node-type-unsupported-in-container", workflowHitlArmShapeMessage(inner.type, inner.id, "mapped-arm"), `${path3}.graph.1`, inner.id);
|
|
3137
|
-
return;
|
|
3138
|
-
}
|
|
3139
|
-
upstream.add(singleId(body[1]));
|
|
3140
|
-
checkArm(body[0], `${path3}.graph.0`, depth, "parallel");
|
|
3141
|
-
checkSingle(body[1], `${path3}.graph.1`, depth);
|
|
3142
|
-
upstream.add(body[0].id);
|
|
3143
|
-
upstream.add(singleId(body[1]));
|
|
3482
|
+
err("invalid-envelope", `\`workflowId\` must be a non-empty string naming the workflow to start (got ${JSON.stringify(node.workflowId)})`, `${path3}.workflowId`, node.id);
|
|
3483
|
+
return;
|
|
3484
|
+
}
|
|
3485
|
+
if (node.type === "workflow" && (node.workflowId.startsWith("$") || Array.isArray(node.graph))) {
|
|
3486
|
+
checkId(node.id, path3);
|
|
3487
|
+
err("node-type-unsupported-by-engine", armSubrunUnsupportedMessage(node.id, node.workflowId), path3, node.id);
|
|
3144
3488
|
return;
|
|
3145
3489
|
}
|
|
3146
3490
|
checkId(singleId(node), path3);
|
|
@@ -3148,6 +3492,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
3148
3492
|
checkInputShape(node, path3);
|
|
3149
3493
|
checkTimeout(node, path3);
|
|
3150
3494
|
checkTier(node, path3);
|
|
3495
|
+
checkModel(node, path3);
|
|
3151
3496
|
checkRetry(node, path3);
|
|
3152
3497
|
checkWorkspace(node, path3);
|
|
3153
3498
|
if (!opts.static) {
|
|
@@ -3170,7 +3515,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
3170
3515
|
if (node.type === "workflow" && node.kind === "subrun" && depth > caps.maxNestingDepth) {
|
|
3171
3516
|
err("cap-exceeded", `nesting depth ${depth} exceeds ${caps.maxNestingDepth}`, path3, node.id);
|
|
3172
3517
|
}
|
|
3173
|
-
if (node.type === "workflow" &&
|
|
3518
|
+
if (node.type === "workflow" && typeof g.definition?.id === "string" && node.workflowId === g.definition.id) {
|
|
3174
3519
|
err("subrun-cycle", `"${node.id}" starts "${node.workflowId}", which is this workflow itself`, path3, node.id);
|
|
3175
3520
|
}
|
|
3176
3521
|
for (const ref of nodeStepRefs(node)) {
|
|
@@ -3191,11 +3536,14 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
3191
3536
|
if (a.approver === "creator" && a.excludeInitiator === true) {
|
|
3192
3537
|
err("approver-excludes-only-candidate", "approver:'creator' with excludeInitiator:true always excludes the only candidate", path3, a.id);
|
|
3193
3538
|
}
|
|
3194
|
-
|
|
3539
|
+
const editable = approvalEditable(a);
|
|
3540
|
+
if (a.fourEyes !== void 0 && !editable) {
|
|
3195
3541
|
err("four-eyes-requires-editable", "`fourEyes` requires editable:true", `${path3}.fourEyes`, a.id);
|
|
3196
3542
|
}
|
|
3197
|
-
if (
|
|
3198
|
-
err("editable-path-invalid", "`editablePaths`
|
|
3543
|
+
if (a.editable === false && Array.isArray(a.editablePaths) && a.editablePaths.length > 0) {
|
|
3544
|
+
err("editable-path-invalid", "`editablePaths` beside editable:false is contradictory \u2014 drop the paths or set editable:true", `${path3}.editablePaths`, a.id);
|
|
3545
|
+
} else if ((a.editablePaths !== void 0 || a.editedPayloadSchema !== void 0) && !editable) {
|
|
3546
|
+
err("editable-path-invalid", "`editablePaths` / `editedPayloadSchema` require editable:true (a non-empty editablePaths implies it)", `${path3}.editablePaths`, a.id);
|
|
3199
3547
|
}
|
|
3200
3548
|
for (const p of a.editablePaths ?? []) {
|
|
3201
3549
|
if (!EDITABLE_PATH_RE.test(p)) err("editable-path-invalid", `editablePaths entry "${p}" is outside the seg(.seg)*[*]/[n] grammar`, `${path3}.editablePaths`, a.id);
|
|
@@ -3376,6 +3724,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
3376
3724
|
} else checkHitlArm(f.step, `${path3}.step`, "foreach");
|
|
3377
3725
|
declared.push(f.step.id);
|
|
3378
3726
|
} else {
|
|
3727
|
+
checkBodyInput(f.step, `${path3}.step`, "foreach");
|
|
3379
3728
|
checkSingle(f.step, `${path3}.step`, o.chunk ? 2 : 1);
|
|
3380
3729
|
declared.push(singleId(f.step));
|
|
3381
3730
|
}
|
|
@@ -3396,6 +3745,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
3396
3745
|
checkHitlArm(l.step, `${path3}.step`, "loop");
|
|
3397
3746
|
declared.push(l.step.id);
|
|
3398
3747
|
} else {
|
|
3748
|
+
checkBodyInput(l.step, `${path3}.step`, "loop");
|
|
3399
3749
|
checkSingle(l.step, `${path3}.step`, 1);
|
|
3400
3750
|
declared.push(singleId(l.step));
|
|
3401
3751
|
}
|
|
@@ -3869,17 +4219,10 @@ function isContinuedFailureValue(v) {
|
|
|
3869
4219
|
const err = o.error;
|
|
3870
4220
|
return o.__lua_workflow === CONTINUED_FAILURE_TAG && o.failed === true && o.text === "" && err !== null && typeof err === "object" && typeof err.code === "string" && typeof err.message === "string";
|
|
3871
4221
|
}
|
|
3872
|
-
function
|
|
3873
|
-
const stepId = nodeIdOf(step22);
|
|
4222
|
+
function inlineContainerArm(mapping, step22) {
|
|
3874
4223
|
return {
|
|
3875
|
-
|
|
3876
|
-
|
|
3877
|
-
workflowId: WORKFLOW_ARM_SUBRUN_ID,
|
|
3878
|
-
kind: "subrun",
|
|
3879
|
-
graph: [
|
|
3880
|
-
mapping,
|
|
3881
|
-
step22
|
|
3882
|
-
]
|
|
4224
|
+
...step22,
|
|
4225
|
+
input: parseMapConfig(mapping.mapConfig, mapping.id)
|
|
3883
4226
|
};
|
|
3884
4227
|
}
|
|
3885
4228
|
function entryIds(entry) {
|
|
@@ -3949,6 +4292,27 @@ function resolvePlacements(calls) {
|
|
|
3949
4292
|
break;
|
|
3950
4293
|
}
|
|
3951
4294
|
});
|
|
4295
|
+
const armMapPlacementIssue = /* @__PURE__ */ __name3((node, ref, i, container) => {
|
|
4296
|
+
if (!ref.armMap || node.type === "mapping" || isHitlNode2(node)) return void 0;
|
|
4297
|
+
const id = nodeIdOf(node);
|
|
4298
|
+
if ((container === "foreach" || container === "loop") && node.type !== "workflow") {
|
|
4299
|
+
return {
|
|
4300
|
+
code: "mapping-placement",
|
|
4301
|
+
message: container === "foreach" ? `foreach body "${id}": a [map, step] body is not supported \u2014 the body receives each item as its input; map the items before the foreach instead (foreach(step, { items: \u2026 }) or a .map() before it)` : `loop body "${id}": a [map, step] body is not supported \u2014 the body receives the previous output as its input; put the .map() before the loop instead`,
|
|
4302
|
+
callIndex: i,
|
|
4303
|
+
stepId: id
|
|
4304
|
+
};
|
|
4305
|
+
}
|
|
4306
|
+
if (node.input !== void 0) {
|
|
4307
|
+
return {
|
|
4308
|
+
code: "mapping-placement",
|
|
4309
|
+
message: `"${id}": the [map, step] arm mapping and the node's own input map would both bind its input \u2014 keep one (drop the arm map, or the \`input\` on the declaration)`,
|
|
4310
|
+
callIndex: i,
|
|
4311
|
+
stepId: id
|
|
4312
|
+
};
|
|
4313
|
+
}
|
|
4314
|
+
return void 0;
|
|
4315
|
+
}, "armMapPlacementIssue");
|
|
3952
4316
|
const hitlPlacementIssue = /* @__PURE__ */ __name3((node, ref, i, container) => {
|
|
3953
4317
|
if (!isHitlNode2(node)) return void 0;
|
|
3954
4318
|
const id = node.id;
|
|
@@ -3975,7 +4339,7 @@ function resolvePlacements(calls) {
|
|
|
3975
4339
|
if (ref.node.type === "mapping" && !allowMapping) {
|
|
3976
4340
|
issues.push({
|
|
3977
4341
|
code: "mapping-placement",
|
|
3978
|
-
message: `mapping "${ref.node.id}" cannot be a container arm \u2014 chain it as [map, step]`,
|
|
4342
|
+
message: `mapping "${ref.node.id}" cannot be a container arm \u2014 chain it as [map, step] in a parallel / conditional arm, or place the .map() before the container`,
|
|
3979
4343
|
callIndex: i,
|
|
3980
4344
|
stepId: ref.node.id
|
|
3981
4345
|
});
|
|
@@ -3996,7 +4360,7 @@ function resolvePlacements(calls) {
|
|
|
3996
4360
|
if (d.node.type === "mapping" && !allowMapping) {
|
|
3997
4361
|
issues.push({
|
|
3998
4362
|
code: "mapping-placement",
|
|
3999
|
-
message: `map "${ref.ref}" cannot be a parallel/foreach/loop arm \u2014 chain it as [map, step]`,
|
|
4363
|
+
message: `map "${ref.ref}" cannot be a parallel/foreach/loop arm \u2014 chain it as [map, step] in a parallel arm, or place the .map() before the container`,
|
|
4000
4364
|
callIndex: i,
|
|
4001
4365
|
stepId: ref.ref
|
|
4002
4366
|
});
|
|
@@ -4007,6 +4371,11 @@ function resolvePlacements(calls) {
|
|
|
4007
4371
|
issues.push(hitl);
|
|
4008
4372
|
return void 0;
|
|
4009
4373
|
}
|
|
4374
|
+
const mapped = armMapPlacementIssue(d.node, ref, i, container);
|
|
4375
|
+
if (mapped) {
|
|
4376
|
+
issues.push(mapped);
|
|
4377
|
+
return void 0;
|
|
4378
|
+
}
|
|
4010
4379
|
const prior = placedBy.get(ref.ref);
|
|
4011
4380
|
if (prior !== void 0 && prior !== i) {
|
|
4012
4381
|
issues.push({
|
|
@@ -4027,6 +4396,10 @@ function resolvePlacements(calls) {
|
|
|
4027
4396
|
}
|
|
4028
4397
|
const hitl = hitlPlacementIssue(ref.node, ref, i, container);
|
|
4029
4398
|
if (hitl) issues.push(hitl);
|
|
4399
|
+
else {
|
|
4400
|
+
const mapped = armMapPlacementIssue(ref.node, ref, i, container);
|
|
4401
|
+
if (mapped) issues.push(mapped);
|
|
4402
|
+
}
|
|
4030
4403
|
}, "claim");
|
|
4031
4404
|
calls.forEach((call, i) => {
|
|
4032
4405
|
switch (call.kind) {
|
|
@@ -4054,7 +4427,7 @@ function resolvePlacements(calls) {
|
|
|
4054
4427
|
const lookup = /* @__PURE__ */ __name3((ref) => {
|
|
4055
4428
|
const n2 = "node" in ref ? ref.node : declared.get(ref.ref)?.node;
|
|
4056
4429
|
if (!n2 || !ref.armMap || n2.type === "mapping" || isHitlNode2(n2)) return n2;
|
|
4057
|
-
return
|
|
4430
|
+
return inlineContainerArm(ref.armMap, n2);
|
|
4058
4431
|
}, "lookup");
|
|
4059
4432
|
calls.forEach((call, i) => {
|
|
4060
4433
|
switch (call.kind) {
|
|
@@ -4439,9 +4812,31 @@ function isTerminalRunStatus(status) {
|
|
|
4439
4812
|
function pruneUndefined(o) {
|
|
4440
4813
|
return Object.fromEntries(Object.entries(o).filter(([, v]) => v !== void 0));
|
|
4441
4814
|
}
|
|
4815
|
+
function runOrigin(run) {
|
|
4816
|
+
if (run.goalId) return "goal";
|
|
4817
|
+
if (run.jobId || run.trigger === "schedule") return "schedule";
|
|
4818
|
+
if (run.dynamic === true) return run.tags?.includes(WORKFLOW_INLINE_RUN_TAG) ? "inline" : "compose";
|
|
4819
|
+
return "definition";
|
|
4820
|
+
}
|
|
4821
|
+
function runErrorIssues(issues) {
|
|
4822
|
+
if (!Array.isArray(issues)) return void 0;
|
|
4823
|
+
const out = [];
|
|
4824
|
+
for (const raw of issues.slice(0, RUN_ERROR_ISSUES_MAX)) {
|
|
4825
|
+
if (!raw || typeof raw !== "object") continue;
|
|
4826
|
+
const o = raw;
|
|
4827
|
+
if (typeof o.code !== "string" || !o.code) continue;
|
|
4828
|
+
out.push(pruneUndefined({
|
|
4829
|
+
code: o.code,
|
|
4830
|
+
path: typeof o.path === "string" ? o.path : void 0,
|
|
4831
|
+
message: typeof o.message === "string" ? o.message : void 0
|
|
4832
|
+
}));
|
|
4833
|
+
}
|
|
4834
|
+
return out.length ? out : void 0;
|
|
4835
|
+
}
|
|
4442
4836
|
function runNextAction(run) {
|
|
4443
4837
|
if (isTerminalRunStatus(run.status)) return "none";
|
|
4444
4838
|
if (run.status === "suspended" && run.gate?.kind === "budget") return "raise_budget";
|
|
4839
|
+
if (run.status === "suspended" && run.gate?.kind === "billing") return "top_up";
|
|
4445
4840
|
if (!run.cancel?.requestedAt) return "none";
|
|
4446
4841
|
const forceAt = run.cancel.forceAfter ?? run.cancel.requestedAt + FORCE_CANCEL_STALE_MS;
|
|
4447
4842
|
return Date.now() >= forceAt ? "force" : "cancel_again";
|
|
@@ -4469,6 +4864,12 @@ function runCountsFromStepStatuses(statuses) {
|
|
|
4469
4864
|
for (const s of statuses) tally[s] = (tally[s] ?? 0) + 1;
|
|
4470
4865
|
return runCountsFromStatusTally(tally);
|
|
4471
4866
|
}
|
|
4867
|
+
function isBillingHeldStep(row) {
|
|
4868
|
+
return row.status === "ready" && row.billingHold === true;
|
|
4869
|
+
}
|
|
4870
|
+
function stepEffectiveStatus(row) {
|
|
4871
|
+
return isBillingHeldStep(row) ? "suspended" : row.status;
|
|
4872
|
+
}
|
|
4472
4873
|
function runCounts(counts) {
|
|
4473
4874
|
const c = counts ?? {};
|
|
4474
4875
|
const rawInFlight = c.dispatched !== void 0 || c.claimed !== void 0 || c.running !== void 0 || c.cancellation_requested !== void 0;
|
|
@@ -4608,6 +5009,7 @@ function toWorkflowRunSummary(run) {
|
|
|
4608
5009
|
repairOf: run.repairOf,
|
|
4609
5010
|
repairRunIds: run.repairRunIds,
|
|
4610
5011
|
trigger: run.trigger ?? "api",
|
|
5012
|
+
origin: runOrigin(run),
|
|
4611
5013
|
createdBy: {
|
|
4612
5014
|
subjectType: principal?.subjectType ?? "system",
|
|
4613
5015
|
subjectId: principal?.subjectId ?? run.userId ?? ""
|
|
@@ -4625,11 +5027,13 @@ function toWorkflowRunSummary(run) {
|
|
|
4625
5027
|
usage: runUsage(run),
|
|
4626
5028
|
// LUA-697: a row persisted before the write seams (#2406 / #2465 / the script tier) leaves scrubbed here too —
|
|
4627
5029
|
// idempotent on a scrubbed message, bounded input; an empty message falls back to the code.
|
|
4628
|
-
error: run.error ? {
|
|
5030
|
+
error: run.error ? pruneUndefined({
|
|
4629
5031
|
code: run.error.code ?? "error",
|
|
4630
5032
|
message: scrubStepErrorMessage(run.error.message) ?? run.error.code ?? "error",
|
|
4631
|
-
stepId: run.error.stepId
|
|
4632
|
-
|
|
5033
|
+
stepId: run.error.stepId,
|
|
5034
|
+
// LUA-784 (item 3): the unattended pre-start failure's refusal rows (`input_schema_invalid` and kin).
|
|
5035
|
+
issues: runErrorIssues(run.error.issues)
|
|
5036
|
+
}) : void 0,
|
|
4633
5037
|
kind: "run",
|
|
4634
5038
|
aclHash: run.aclHash,
|
|
4635
5039
|
migration: run.migration,
|
|
@@ -5129,147 +5533,29 @@ function applyJsonPatch(doc, ops) {
|
|
|
5129
5533
|
ok: false,
|
|
5130
5534
|
code: "PATCH_INVALID",
|
|
5131
5535
|
index: i,
|
|
5132
|
-
path: op.path,
|
|
5133
|
-
message: "path not allowed"
|
|
5134
|
-
};
|
|
5135
|
-
if (op.op === "add") obj[key] = structuredClone(op.value);
|
|
5136
|
-
else if (!(key in obj)) return {
|
|
5137
|
-
ok: false,
|
|
5138
|
-
code: "PATH_NOT_FOUND",
|
|
5139
|
-
index: i,
|
|
5140
|
-
path: op.path,
|
|
5141
|
-
message: "path not found"
|
|
5142
|
-
};
|
|
5143
|
-
else if (op.op === "replace") obj[key] = structuredClone(op.value);
|
|
5144
|
-
else delete obj[key];
|
|
5145
|
-
}
|
|
5146
|
-
return {
|
|
5147
|
-
ok: true,
|
|
5148
|
-
value: value22
|
|
5149
|
-
};
|
|
5150
|
-
}
|
|
5151
|
-
function rebaseItemPointer(pointer, itemsPath, index) {
|
|
5152
|
-
const base = `/${itemsPath.split(".").map(escapePointer).join("/")}/${index}`;
|
|
5153
|
-
return pointer === "/" || pointer === "" ? base : `${base}${pointer}`;
|
|
5154
|
-
}
|
|
5155
|
-
function describeApproverSpecRefusal(spec) {
|
|
5156
|
-
const raw = spec === void 0 ? "undefined" : JSON.stringify(spec) ?? String(spec);
|
|
5157
|
-
const written = raw.length > APPROVER_WRITTEN_MAX ? `${raw.slice(0, APPROVER_WRITTEN_MAX - 1)}\u2026` : raw;
|
|
5158
|
-
const users = typeof spec === "object" && spec !== null ? spec.users : void 0;
|
|
5159
|
-
const approver = typeof users === "string" && USER_ID_SHAPED_RE.test(users) ? {
|
|
5160
|
-
users: [
|
|
5161
|
-
users
|
|
5162
|
-
]
|
|
5163
|
-
} : "creator";
|
|
5164
|
-
const message = `approver ${written} is not an approver \u2014 legal: ${APPROVER_SPEC_SHAPES.join(" | ")}. 'creator' is the person who started the run: write approver:'creator' for "ask me" / "I approve"; {users:[\u2026]} takes user ids, never emails, names or {type:'user'}` + (approver === "creator" ? "" : `; here: approver:${JSON.stringify(approver)}`);
|
|
5165
|
-
return {
|
|
5166
|
-
approver,
|
|
5167
|
-
written,
|
|
5168
|
-
message
|
|
5169
|
-
};
|
|
5170
|
-
}
|
|
5171
|
-
function bindingRootsOk(template22) {
|
|
5172
|
-
const refs = [
|
|
5173
|
-
...template22.matchAll(/\$\{\s*([A-Za-z_$][\w$]*)/g)
|
|
5174
|
-
].map((m) => m[1]);
|
|
5175
|
-
return refs.length > 0 && refs.every((r) => BINDING_ROOTS.includes(r));
|
|
5176
|
-
}
|
|
5177
|
-
function isTemplateBinding(v) {
|
|
5178
|
-
return typeof v === "object" && v !== null && typeof v.template === "string";
|
|
5179
|
-
}
|
|
5180
|
-
function validateApproverBlock(node, opts = {
|
|
5181
|
-
path: "approval"
|
|
5182
|
-
}) {
|
|
5183
|
-
const issues = [];
|
|
5184
|
-
const push = /* @__PURE__ */ __name3((code, path3, message, severity = "error") => issues.push({
|
|
5185
|
-
code,
|
|
5186
|
-
path: path3,
|
|
5187
|
-
severity,
|
|
5188
|
-
message
|
|
5189
|
-
}), "push");
|
|
5190
|
-
const checkSpec = /* @__PURE__ */ __name3((spec, path3) => {
|
|
5191
|
-
const r = ApproverSpecSchema.safeParse(spec);
|
|
5192
|
-
if (!r.success) {
|
|
5193
|
-
const users = spec?.users;
|
|
5194
|
-
if (Array.isArray(users) && users.length > APPROVER_SPEC_MAX_USERS) push("cap-exceeded", path3, `at most ${APPROVER_SPEC_MAX_USERS} users`);
|
|
5195
|
-
else {
|
|
5196
|
-
const refusal = describeApproverSpecRefusal(spec);
|
|
5197
|
-
issues.push({
|
|
5198
|
-
code: "approver-invalid",
|
|
5199
|
-
path: path3,
|
|
5200
|
-
severity: "error",
|
|
5201
|
-
message: refusal.message,
|
|
5202
|
-
repair: {
|
|
5203
|
-
approver: refusal.approver,
|
|
5204
|
-
written: refusal.written
|
|
5205
|
-
}
|
|
5206
|
-
});
|
|
5207
|
-
}
|
|
5208
|
-
return;
|
|
5209
|
-
}
|
|
5210
|
-
const s = r.data;
|
|
5211
|
-
if (typeof s === "object") {
|
|
5212
|
-
if ("governance" in s && !opts.governanceEnabled) push("approver-governance-unavailable", path3, "governance reviewer routing is not enabled for this deployment");
|
|
5213
|
-
if ("group" in s && typeof s.group === "string" && !opts.scimEnabled && opts.idpGroups?.includes(s.group)) push("approver-idp-group-unavailable", path3, "IdP-group approvers are not enabled for this deployment");
|
|
5214
|
-
const binding = "users" in s ? s.users : "role" in s ? s.role : "group" in s ? s.group : void 0;
|
|
5215
|
-
if (isTemplateBinding(binding)) {
|
|
5216
|
-
if (!bindingRootsOk(binding.template)) push("approver-binding-invalid", `${path3}.template`, "binding root must be initData / stepResults / requestContext / state");
|
|
5217
|
-
if ("users" in s && opts.customerReachable) push("approver-binding-customer-reachable", `${path3}.users`, "a customer-reachable workflow may not bind its approver list");
|
|
5218
|
-
}
|
|
5219
|
-
}
|
|
5220
|
-
}, "checkSpec");
|
|
5221
|
-
if (node.approver !== void 0) checkSpec(node.approver, `${opts.path}.approver`);
|
|
5222
|
-
if (node.fourEyes !== void 0) {
|
|
5223
|
-
const r = FourEyesSchema.safeParse(node.fourEyes);
|
|
5224
|
-
if (!r.success) push("approver-invalid", `${opts.path}.fourEyes`, "fourEyes needs { edit, approve } approver specs");
|
|
5225
|
-
else {
|
|
5226
|
-
checkSpec(r.data.edit, `${opts.path}.fourEyes.edit`);
|
|
5227
|
-
checkSpec(r.data.approve, `${opts.path}.fourEyes.approve`);
|
|
5228
|
-
}
|
|
5229
|
-
if (!node.editable) push("four-eyes-requires-editable", `${opts.path}.fourEyes`, "fourEyes requires editable:true");
|
|
5230
|
-
if (node.approver !== void 0) push("four-eyes-overrides-approver", `${opts.path}.approver`, "fourEyes replaces approver", "warning");
|
|
5231
|
-
if (node.itemsPath) push("four-eyes-items-unsupported", `${opts.path}.fourEyes`, "fourEyes cannot combine with itemsPath");
|
|
5232
|
-
}
|
|
5233
|
-
if (node.excludeInitiator && (node.approver === void 0 || node.approver === "creator") && !node.fourEyes) push("approver-excludes-only-candidate", `${opts.path}.excludeInitiator`, "'creator' with excludeInitiator leaves no approver");
|
|
5234
|
-
if (Array.isArray(node.onTimeout)) {
|
|
5235
|
-
const chain = node.onTimeout;
|
|
5236
|
-
const hops = chain.filter((m) => typeof m === "object" && m !== null && "escalateTo" in m);
|
|
5237
|
-
if (hops.length > ESCALATION_MAX_HOPS) push("escalation-chain-too-long", `${opts.path}.onTimeout`, `at most ${ESCALATION_MAX_HOPS} hops`);
|
|
5238
|
-
const last = chain[chain.length - 1];
|
|
5239
|
-
if (typeof last === "object" && last !== null) push("escalation-chain-not-terminal", `${opts.path}.onTimeout`, "a chain must end in deny | cancel-run | fail");
|
|
5240
|
-
hops.forEach((h, i) => checkSpec(h.escalateTo, `${opts.path}.onTimeout[${i}].escalateTo`));
|
|
5241
|
-
} else if (typeof node.onTimeout === "object" && node.onTimeout !== null) {
|
|
5242
|
-
checkSpec(node.onTimeout.escalateTo, `${opts.path}.onTimeout.escalateTo`);
|
|
5243
|
-
}
|
|
5244
|
-
return issues;
|
|
5245
|
-
}
|
|
5246
|
-
function liftRenderedApprover(row, rendered) {
|
|
5247
|
-
const text = (rendered ?? "").trim();
|
|
5248
|
-
if (!text) return null;
|
|
5249
|
-
if (row === "users") {
|
|
5250
|
-
let members = null;
|
|
5251
|
-
if (text.startsWith("[")) {
|
|
5252
|
-
try {
|
|
5253
|
-
members = JSON.parse(text);
|
|
5254
|
-
} catch {
|
|
5255
|
-
return null;
|
|
5256
|
-
}
|
|
5257
|
-
} else members = text.split(",").map((s) => s.trim());
|
|
5258
|
-
if (!Array.isArray(members) || members.length === 0 || members.length > APPROVER_SPEC_MAX_USERS) return null;
|
|
5259
|
-
if (!members.every((m) => typeof m === "string" && m.length > 0 && m.length <= 128)) return null;
|
|
5260
|
-
return {
|
|
5261
|
-
users: [
|
|
5262
|
-
...new Set(members)
|
|
5263
|
-
].sort()
|
|
5536
|
+
path: op.path,
|
|
5537
|
+
message: "path not allowed"
|
|
5538
|
+
};
|
|
5539
|
+
if (op.op === "add") obj[key] = structuredClone(op.value);
|
|
5540
|
+
else if (!(key in obj)) return {
|
|
5541
|
+
ok: false,
|
|
5542
|
+
code: "PATH_NOT_FOUND",
|
|
5543
|
+
index: i,
|
|
5544
|
+
path: op.path,
|
|
5545
|
+
message: "path not found"
|
|
5264
5546
|
};
|
|
5547
|
+
else if (op.op === "replace") obj[key] = structuredClone(op.value);
|
|
5548
|
+
else delete obj[key];
|
|
5265
5549
|
}
|
|
5266
|
-
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
} : {
|
|
5270
|
-
group: text
|
|
5550
|
+
return {
|
|
5551
|
+
ok: true,
|
|
5552
|
+
value: value22
|
|
5271
5553
|
};
|
|
5272
5554
|
}
|
|
5555
|
+
function rebaseItemPointer(pointer, itemsPath, index) {
|
|
5556
|
+
const base = `/${itemsPath.split(".").map(escapePointer).join("/")}/${index}`;
|
|
5557
|
+
return pointer === "/" || pointer === "" ? base : `${base}${pointer}`;
|
|
5558
|
+
}
|
|
5273
5559
|
function collectEnvTemplateKeys(value22) {
|
|
5274
5560
|
const keys = /* @__PURE__ */ new Set();
|
|
5275
5561
|
const walk22 = /* @__PURE__ */ __name3((v) => {
|
|
@@ -5518,7 +5804,6 @@ function* singleStepsOf(entry) {
|
|
|
5518
5804
|
return;
|
|
5519
5805
|
case "workflow":
|
|
5520
5806
|
yield entry;
|
|
5521
|
-
if (Array.isArray(entry.graph)) yield* singleStepsOf(entry.graph[1]);
|
|
5522
5807
|
return;
|
|
5523
5808
|
case "parallel":
|
|
5524
5809
|
case "conditional":
|
|
@@ -5560,7 +5845,7 @@ function needsInheritedWorkspace(graph) {
|
|
|
5560
5845
|
}
|
|
5561
5846
|
return false;
|
|
5562
5847
|
}
|
|
5563
|
-
var __defProp3, __name3, WorkflowTemplateError, TEMPLATE_PLACEHOLDER, TEMPLATE_NAMESPACES, MAP_DESCRIPTOR_KEYS, MAP_MEMBER_MALFORMED_CODE, fromInit, fromStep, value, template, fromRequest, rows, fromKnowledge, SideEffectsSchema, JobResourcesSchema,
|
|
5848
|
+
var __defProp3, __name3, WorkflowTemplateError, TEMPLATE_PLACEHOLDER, TEMPLATE_NAMESPACES, MAP_DESCRIPTOR_KEYS, MAP_MEMBER_MALFORMED_CODE, fromInit, fromStep, value, template, fromRequest, rows, fromKnowledge, SideEffectsSchema, JobResourcesSchema, APPROVER_SPEC_MAX_USERS, ESCALATION_MAX_HOPS, TemplateBindingSchema, ApproverSpecSchema, FourEyesSchema, EscalationHopSchema, TerminalOutcomeSchema, ApprovalOnTimeoutSchema, APPROVER_SPEC_SHAPES, APPROVER_WRITTEN_MAX, USER_ID_SHAPED_RE, BINDING_ROOTS, WORKSPACE_TEMPLATE_EXPR_RE, SLEEP_UNTIL_REPLACEMENT, WORKFLOW_CAPS_DEFAULT, WORKFLOW_STEP_DEFAULT_TIMEOUT_SECONDS, WORKFLOW_AGENT_DEFAULT_TIMEOUT_SECONDS, WORKFLOW_JOB_DEFAULT_TIMEOUT_SECONDS, WORKFLOW_FOREACH_DEFAULT_CONCURRENCY, WORKFLOW_FOREACH_DEFAULT_MAX_ITEMS, WORKFLOW_LOOP_DEFAULT_MAX_ITERATIONS, WORKFLOW_SUSPEND_DEFAULT_TIMEOUT_HOURS, WORKFLOW_SIGNAL_DEFAULT_SOURCES, clone, CONNECTION_ID_HEX_RE, WORKFLOW_JOB_TOOLS, WORKFLOW_JOB_MAX_WORKTREE_ARMS, workspaceOf, mountsWorkspace, isJobTier, jobToolsOf, schemaIsArray, isHitlNode, isSingleStep, singleId, armId, TEMPLATE_STEP_REF, EDITABLE_PATH_RE, PREDICATE_OPS, isPredicateScalar, GRAPH_HASH_PREFIX, WorkflowPlanError, isArmStep, armStepId, armStepKind, joinIdOf, containerIdOf, PATH_PLACEHOLDER, MISSING, stepIdOf, cmp, eq, ne, gt, gte, lt, lte, inSet, notIn, exists, notExists, truthy, falsy, and, or, not, CONTINUED_FAILURE_TAG, CONTINUED_FAILURE_DEFAULT_CODE, CONTINUED_FAILURE_OUTPUT_SCHEMA, CONTINUED_FAILURE_LEAF_PATHS, isHitlNode2, nodeIdOf, GOAL_JUDGE_STEP_ID, NON_LEAF_KINDS, CONDITIONAL_JOIN_ID, branchArmId, canonical, sortKeys, JOIN, entryOfJoin, FORCE_CANCEL_STALE_MS, TERMINAL, WORKFLOW_INLINE_RUN_TAG, RUN_ERROR_ISSUES_MAX, IN_FLIGHT, n, STEP_ERROR_DETAIL_KEYS, STEP_ERROR_DETAIL_MAX_BYTES, DETAIL_MAX_DEPTH, DETAIL_MAX_ITEMS, MAX_HOLIDAYS, MAX_WALK_DAYS, HHMM, YMD, MS_PER_MIN, MS_PER_DAY, MON_FRI, supportedTz, fmtCache, WEEKDAYS, JSON_PATCH_OPS, JSON_PATCH_MAX_OPS, JSON_PATCH_MAX_VALUE_BYTES, JSON_PATCH_MAX_TOTAL_BYTES, SEGMENT_RE, WORKFLOW_ENV_OVERLAY_MAX_KEYS, WORKFLOW_ENV_OVERLAY_MAX_VALUE_BYTES, WORKFLOW_ENV_TEMPLATE_SECRET_KEY_RE, isEnvRef, looksLikeEmbeddedJson, ZERO, isRecord2;
|
|
5564
5849
|
var init_dist2 = __esm({
|
|
5565
5850
|
"../workflow-graph/dist/index.mjs"() {
|
|
5566
5851
|
"use strict";
|
|
@@ -5663,7 +5948,88 @@ var init_dist2 = __esm({
|
|
|
5663
5948
|
}), "fromKnowledge");
|
|
5664
5949
|
SideEffectsSchema = z4.enum(WORKFLOW_SIDE_EFFECTS);
|
|
5665
5950
|
JobResourcesSchema = z4.enum(WORKFLOW_JOB_RESOURCES);
|
|
5666
|
-
|
|
5951
|
+
APPROVER_SPEC_MAX_USERS = 20;
|
|
5952
|
+
ESCALATION_MAX_HOPS = 3;
|
|
5953
|
+
TemplateBindingSchema = z22.object({
|
|
5954
|
+
template: z22.string().min(1).max(2048)
|
|
5955
|
+
}).strict();
|
|
5956
|
+
ApproverSpecSchema = z22.union([
|
|
5957
|
+
z22.literal("creator"),
|
|
5958
|
+
z22.literal("org-admins"),
|
|
5959
|
+
z22.object({
|
|
5960
|
+
users: z22.union([
|
|
5961
|
+
z22.array(z22.string().min(1).max(128)).min(1).max(APPROVER_SPEC_MAX_USERS),
|
|
5962
|
+
TemplateBindingSchema
|
|
5963
|
+
])
|
|
5964
|
+
}).strict(),
|
|
5965
|
+
z22.object({
|
|
5966
|
+
role: z22.union([
|
|
5967
|
+
z22.string().min(1).max(128),
|
|
5968
|
+
TemplateBindingSchema
|
|
5969
|
+
])
|
|
5970
|
+
}).strict(),
|
|
5971
|
+
z22.object({
|
|
5972
|
+
group: z22.union([
|
|
5973
|
+
z22.string().min(1).max(128),
|
|
5974
|
+
TemplateBindingSchema
|
|
5975
|
+
])
|
|
5976
|
+
}).strict(),
|
|
5977
|
+
z22.object({
|
|
5978
|
+
governance: z22.object({
|
|
5979
|
+
policyId: z22.string().min(1).max(128)
|
|
5980
|
+
}).strict()
|
|
5981
|
+
}).strict()
|
|
5982
|
+
]);
|
|
5983
|
+
FourEyesSchema = z22.object({
|
|
5984
|
+
edit: ApproverSpecSchema,
|
|
5985
|
+
approve: ApproverSpecSchema
|
|
5986
|
+
}).strict();
|
|
5987
|
+
EscalationHopSchema = z22.object({
|
|
5988
|
+
escalateTo: ApproverSpecSchema,
|
|
5989
|
+
timeoutHours: z22.number().finite().min(1).max(720)
|
|
5990
|
+
}).strict();
|
|
5991
|
+
TerminalOutcomeSchema = z22.enum([
|
|
5992
|
+
"deny",
|
|
5993
|
+
"cancel-run",
|
|
5994
|
+
"fail",
|
|
5995
|
+
"continue"
|
|
5996
|
+
]);
|
|
5997
|
+
ApprovalOnTimeoutSchema = z22.union([
|
|
5998
|
+
TerminalOutcomeSchema,
|
|
5999
|
+
EscalationHopSchema,
|
|
6000
|
+
z22.array(z22.union([
|
|
6001
|
+
TerminalOutcomeSchema,
|
|
6002
|
+
EscalationHopSchema
|
|
6003
|
+
])).min(1).max(ESCALATION_MAX_HOPS + 1)
|
|
6004
|
+
]);
|
|
6005
|
+
APPROVER_SPEC_SHAPES = [
|
|
6006
|
+
"'creator'",
|
|
6007
|
+
"'org-admins'",
|
|
6008
|
+
"{users:[userId, \u2026]}",
|
|
6009
|
+
"{role:roleName}",
|
|
6010
|
+
"{group:groupName}",
|
|
6011
|
+
"{governance:{policyId}}"
|
|
6012
|
+
];
|
|
6013
|
+
APPROVER_WRITTEN_MAX = 120;
|
|
6014
|
+
USER_ID_SHAPED_RE = /^[^\s@]{1,128}$/;
|
|
6015
|
+
__name(describeApproverSpecRefusal, "describeApproverSpecRefusal");
|
|
6016
|
+
__name3(describeApproverSpecRefusal, "describeApproverSpecRefusal");
|
|
6017
|
+
BINDING_ROOTS = [
|
|
6018
|
+
"initData",
|
|
6019
|
+
"stepResults",
|
|
6020
|
+
"requestContext",
|
|
6021
|
+
"state"
|
|
6022
|
+
];
|
|
6023
|
+
__name(bindingRootsOk, "bindingRootsOk");
|
|
6024
|
+
__name3(bindingRootsOk, "bindingRootsOk");
|
|
6025
|
+
__name(isTemplateBinding, "isTemplateBinding");
|
|
6026
|
+
__name3(isTemplateBinding, "isTemplateBinding");
|
|
6027
|
+
__name(approvalEditable, "approvalEditable");
|
|
6028
|
+
__name3(approvalEditable, "approvalEditable");
|
|
6029
|
+
__name(validateApproverBlock, "validateApproverBlock");
|
|
6030
|
+
__name3(validateApproverBlock, "validateApproverBlock");
|
|
6031
|
+
__name(liftRenderedApprover, "liftRenderedApprover");
|
|
6032
|
+
__name3(liftRenderedApprover, "liftRenderedApprover");
|
|
5667
6033
|
WORKSPACE_TEMPLATE_EXPR_RE = /^\$\{\s*(?:initData|input)\.([^}]+?)\s*\}$/;
|
|
5668
6034
|
__name(workspaceTemplatePath, "workspaceTemplatePath");
|
|
5669
6035
|
__name3(workspaceTemplatePath, "workspaceTemplatePath");
|
|
@@ -5675,6 +6041,8 @@ var init_dist2 = __esm({
|
|
|
5675
6041
|
});
|
|
5676
6042
|
__name(sleepUntilUnsupportedMessage, "sleepUntilUnsupportedMessage");
|
|
5677
6043
|
__name3(sleepUntilUnsupportedMessage, "sleepUntilUnsupportedMessage");
|
|
6044
|
+
__name(armSubrunUnsupportedMessage, "armSubrunUnsupportedMessage");
|
|
6045
|
+
__name3(armSubrunUnsupportedMessage, "armSubrunUnsupportedMessage");
|
|
5678
6046
|
WORKFLOW_CAPS_DEFAULT = Object.freeze({
|
|
5679
6047
|
maxParallelArms: 16,
|
|
5680
6048
|
maxForeachConcurrency: 16,
|
|
@@ -5956,8 +6324,8 @@ var init_dist2 = __esm({
|
|
|
5956
6324
|
__name(isContinuedFailureValue, "isContinuedFailureValue");
|
|
5957
6325
|
__name3(isContinuedFailureValue, "isContinuedFailureValue");
|
|
5958
6326
|
isHitlNode2 = /* @__PURE__ */ __name3((n2) => isWorkflowHitlEntryType(n2.type), "isHitlNode");
|
|
5959
|
-
__name(
|
|
5960
|
-
__name3(
|
|
6327
|
+
__name(inlineContainerArm, "inlineContainerArm");
|
|
6328
|
+
__name3(inlineContainerArm, "inlineContainerArm");
|
|
5961
6329
|
nodeIdOf = /* @__PURE__ */ __name3((n2) => n2.type === "step" ? n2.step.id : n2.id, "nodeIdOf");
|
|
5962
6330
|
__name(entryIds, "entryIds");
|
|
5963
6331
|
__name3(entryIds, "entryIds");
|
|
@@ -6017,6 +6385,12 @@ var init_dist2 = __esm({
|
|
|
6017
6385
|
__name3(isTerminalRunStatus, "isTerminalRunStatus");
|
|
6018
6386
|
__name(pruneUndefined, "pruneUndefined");
|
|
6019
6387
|
__name3(pruneUndefined, "pruneUndefined");
|
|
6388
|
+
WORKFLOW_INLINE_RUN_TAG = "inline";
|
|
6389
|
+
__name(runOrigin, "runOrigin");
|
|
6390
|
+
__name3(runOrigin, "runOrigin");
|
|
6391
|
+
RUN_ERROR_ISSUES_MAX = 20;
|
|
6392
|
+
__name(runErrorIssues, "runErrorIssues");
|
|
6393
|
+
__name3(runErrorIssues, "runErrorIssues");
|
|
6020
6394
|
__name(runNextAction, "runNextAction");
|
|
6021
6395
|
__name3(runNextAction, "runNextAction");
|
|
6022
6396
|
IN_FLIGHT = new Set(WORKFLOW_STEP_IN_FLIGHT);
|
|
@@ -6026,6 +6400,10 @@ var init_dist2 = __esm({
|
|
|
6026
6400
|
__name3(runCountsFromStatusTally, "runCountsFromStatusTally");
|
|
6027
6401
|
__name(runCountsFromStepStatuses, "runCountsFromStepStatuses");
|
|
6028
6402
|
__name3(runCountsFromStepStatuses, "runCountsFromStepStatuses");
|
|
6403
|
+
__name(isBillingHeldStep, "isBillingHeldStep");
|
|
6404
|
+
__name3(isBillingHeldStep, "isBillingHeldStep");
|
|
6405
|
+
__name(stepEffectiveStatus, "stepEffectiveStatus");
|
|
6406
|
+
__name3(stepEffectiveStatus, "stepEffectiveStatus");
|
|
6029
6407
|
n = /* @__PURE__ */ __name3((v) => typeof v === "number" && Number.isFinite(v) ? v : 0, "n");
|
|
6030
6408
|
__name(runCounts, "runCounts");
|
|
6031
6409
|
__name3(runCounts, "runCounts");
|
|
@@ -6177,86 +6555,6 @@ var init_dist2 = __esm({
|
|
|
6177
6555
|
__name3(applyJsonPatch, "applyJsonPatch");
|
|
6178
6556
|
__name(rebaseItemPointer, "rebaseItemPointer");
|
|
6179
6557
|
__name3(rebaseItemPointer, "rebaseItemPointer");
|
|
6180
|
-
APPROVER_SPEC_MAX_USERS = 20;
|
|
6181
|
-
ESCALATION_MAX_HOPS = 3;
|
|
6182
|
-
TemplateBindingSchema = z22.object({
|
|
6183
|
-
template: z22.string().min(1).max(2048)
|
|
6184
|
-
}).strict();
|
|
6185
|
-
ApproverSpecSchema = z22.union([
|
|
6186
|
-
z22.literal("creator"),
|
|
6187
|
-
z22.literal("org-admins"),
|
|
6188
|
-
z22.object({
|
|
6189
|
-
users: z22.union([
|
|
6190
|
-
z22.array(z22.string().min(1).max(128)).min(1).max(APPROVER_SPEC_MAX_USERS),
|
|
6191
|
-
TemplateBindingSchema
|
|
6192
|
-
])
|
|
6193
|
-
}).strict(),
|
|
6194
|
-
z22.object({
|
|
6195
|
-
role: z22.union([
|
|
6196
|
-
z22.string().min(1).max(128),
|
|
6197
|
-
TemplateBindingSchema
|
|
6198
|
-
])
|
|
6199
|
-
}).strict(),
|
|
6200
|
-
z22.object({
|
|
6201
|
-
group: z22.union([
|
|
6202
|
-
z22.string().min(1).max(128),
|
|
6203
|
-
TemplateBindingSchema
|
|
6204
|
-
])
|
|
6205
|
-
}).strict(),
|
|
6206
|
-
z22.object({
|
|
6207
|
-
governance: z22.object({
|
|
6208
|
-
policyId: z22.string().min(1).max(128)
|
|
6209
|
-
}).strict()
|
|
6210
|
-
}).strict()
|
|
6211
|
-
]);
|
|
6212
|
-
FourEyesSchema = z22.object({
|
|
6213
|
-
edit: ApproverSpecSchema,
|
|
6214
|
-
approve: ApproverSpecSchema
|
|
6215
|
-
}).strict();
|
|
6216
|
-
EscalationHopSchema = z22.object({
|
|
6217
|
-
escalateTo: ApproverSpecSchema,
|
|
6218
|
-
timeoutHours: z22.number().finite().min(1).max(720)
|
|
6219
|
-
}).strict();
|
|
6220
|
-
TerminalOutcomeSchema = z22.enum([
|
|
6221
|
-
"deny",
|
|
6222
|
-
"cancel-run",
|
|
6223
|
-
"fail",
|
|
6224
|
-
"continue"
|
|
6225
|
-
]);
|
|
6226
|
-
ApprovalOnTimeoutSchema = z22.union([
|
|
6227
|
-
TerminalOutcomeSchema,
|
|
6228
|
-
EscalationHopSchema,
|
|
6229
|
-
z22.array(z22.union([
|
|
6230
|
-
TerminalOutcomeSchema,
|
|
6231
|
-
EscalationHopSchema
|
|
6232
|
-
])).min(1).max(ESCALATION_MAX_HOPS + 1)
|
|
6233
|
-
]);
|
|
6234
|
-
APPROVER_SPEC_SHAPES = [
|
|
6235
|
-
"'creator'",
|
|
6236
|
-
"'org-admins'",
|
|
6237
|
-
"{users:[userId, \u2026]}",
|
|
6238
|
-
"{role:roleName}",
|
|
6239
|
-
"{group:groupName}",
|
|
6240
|
-
"{governance:{policyId}}"
|
|
6241
|
-
];
|
|
6242
|
-
APPROVER_WRITTEN_MAX = 120;
|
|
6243
|
-
USER_ID_SHAPED_RE = /^[^\s@]{1,128}$/;
|
|
6244
|
-
__name(describeApproverSpecRefusal, "describeApproverSpecRefusal");
|
|
6245
|
-
__name3(describeApproverSpecRefusal, "describeApproverSpecRefusal");
|
|
6246
|
-
BINDING_ROOTS = [
|
|
6247
|
-
"initData",
|
|
6248
|
-
"stepResults",
|
|
6249
|
-
"requestContext",
|
|
6250
|
-
"state"
|
|
6251
|
-
];
|
|
6252
|
-
__name(bindingRootsOk, "bindingRootsOk");
|
|
6253
|
-
__name3(bindingRootsOk, "bindingRootsOk");
|
|
6254
|
-
__name(isTemplateBinding, "isTemplateBinding");
|
|
6255
|
-
__name3(isTemplateBinding, "isTemplateBinding");
|
|
6256
|
-
__name(validateApproverBlock, "validateApproverBlock");
|
|
6257
|
-
__name3(validateApproverBlock, "validateApproverBlock");
|
|
6258
|
-
__name(liftRenderedApprover, "liftRenderedApprover");
|
|
6259
|
-
__name3(liftRenderedApprover, "liftRenderedApprover");
|
|
6260
6558
|
WORKFLOW_ENV_OVERLAY_MAX_KEYS = 64;
|
|
6261
6559
|
WORKFLOW_ENV_OVERLAY_MAX_VALUE_BYTES = 4096;
|
|
6262
6560
|
WORKFLOW_ENV_TEMPLATE_SECRET_KEY_RE = /(SECRET|TOKEN|KEY|PASSWORD)$/;
|
|
@@ -6359,14 +6657,13 @@ function stepNodeOf(s) {
|
|
|
6359
6657
|
}
|
|
6360
6658
|
function materializeEntry(entry, steps) {
|
|
6361
6659
|
const single = /* @__PURE__ */ __name((n2) => {
|
|
6362
|
-
if (n2.type === "step" && steps[n2.step.id])
|
|
6363
|
-
|
|
6364
|
-
|
|
6365
|
-
|
|
6366
|
-
n2.
|
|
6367
|
-
|
|
6368
|
-
|
|
6369
|
-
};
|
|
6660
|
+
if (n2.type === "step" && steps[n2.step.id]) {
|
|
6661
|
+
const node = stepNodeOf(steps[n2.step.id]);
|
|
6662
|
+
return n2.input !== void 0 ? {
|
|
6663
|
+
...node,
|
|
6664
|
+
input: n2.input
|
|
6665
|
+
} : node;
|
|
6666
|
+
}
|
|
6370
6667
|
return n2;
|
|
6371
6668
|
}, "single");
|
|
6372
6669
|
switch (entry.type) {
|
|
@@ -6524,7 +6821,10 @@ var init_workflow = __esm({
|
|
|
6524
6821
|
}, "assertPredicate");
|
|
6525
6822
|
assertRetry = /* @__PURE__ */ __name((r, id) => {
|
|
6526
6823
|
if (!r) return;
|
|
6527
|
-
if (
|
|
6824
|
+
if (!isWithinWorkflowRetryAttempts(r.maxAttempts)) {
|
|
6825
|
+
const over = typeof r.maxAttempts === "number" && r.maxAttempts > WORKFLOW_RETRY_MAX_ATTEMPTS;
|
|
6826
|
+
throw new LuaWorkflowBuildError(over ? "cap-exceeded" : "invalid-envelope", `"${id}": ${workflowRetryMaxAttemptsMessage(r.maxAttempts)}`);
|
|
6827
|
+
}
|
|
6528
6828
|
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(" | ")}`);
|
|
6529
6829
|
if (r.maxBackoffSeconds !== void 0) {
|
|
6530
6830
|
if (r.backoff !== "exponential") throw new LuaWorkflowBuildError("backoff-invalid", `"${id}": retry.maxBackoffSeconds is only meaningful with backoff:'exponential'`);
|
|
@@ -7299,7 +7599,102 @@ function isAccessDeniedError(error) {
|
|
|
7299
7599
|
if (CliError.isCliError(error)) return error.statusCode === 403;
|
|
7300
7600
|
return error instanceof Error && error.message.startsWith("Access denied (403)");
|
|
7301
7601
|
}
|
|
7302
|
-
|
|
7602
|
+
function authHint(error) {
|
|
7603
|
+
if (error.suppressDefaultRemediation) return void 0;
|
|
7604
|
+
if (error.reason === "no_agent_access") {
|
|
7605
|
+
return [
|
|
7606
|
+
"Your API key is valid, but it does not have access to the agentId in lua.skill.yaml \u2014 the agent belongs",
|
|
7607
|
+
"to another account or organization, was deleted or transferred, or the yaml was copied from another project.",
|
|
7608
|
+
"Check the configured agent and switch if needed:",
|
|
7609
|
+
" lua agents (list agents you have access to)",
|
|
7610
|
+
" lua init (re-select the agent for this project)"
|
|
7611
|
+
].join("\n");
|
|
7612
|
+
}
|
|
7613
|
+
return "Re-authenticate or check your API key: lua auth configure \xB7 https://admin.heylua.ai";
|
|
7614
|
+
}
|
|
7615
|
+
function numericStatus(error) {
|
|
7616
|
+
const candidate = error.statusCode ?? error.status;
|
|
7617
|
+
return typeof candidate === "number" && Number.isFinite(candidate) ? candidate : void 0;
|
|
7618
|
+
}
|
|
7619
|
+
function classifyCliError(error) {
|
|
7620
|
+
if (CliError.isCliError(error)) {
|
|
7621
|
+
return {
|
|
7622
|
+
code: error.code,
|
|
7623
|
+
exitCode: error.exitCode,
|
|
7624
|
+
message: error.message,
|
|
7625
|
+
hint: error.hint,
|
|
7626
|
+
statusCode: error.statusCode,
|
|
7627
|
+
serverCode: error.serverCode,
|
|
7628
|
+
issues: error.issues
|
|
7629
|
+
};
|
|
7630
|
+
}
|
|
7631
|
+
if (AuthenticationError.isAuthenticationError(error)) {
|
|
7632
|
+
return {
|
|
7633
|
+
code: "auth",
|
|
7634
|
+
exitCode: CLI_EXIT.AUTH,
|
|
7635
|
+
message: error.message,
|
|
7636
|
+
hint: authHint(error)
|
|
7637
|
+
};
|
|
7638
|
+
}
|
|
7639
|
+
const e = typeof error === "object" && error !== null ? error : {};
|
|
7640
|
+
const message = typeof e.message === "string" && e.message.length > 0 ? e.message : error instanceof Error ? error.name : String(error ?? "Unknown error");
|
|
7641
|
+
if (e.name === "WorkflowLocalUsageError" || typeof e.code === "string" && e.code.startsWith("commander.")) {
|
|
7642
|
+
return {
|
|
7643
|
+
code: "usage",
|
|
7644
|
+
exitCode: CLI_EXIT.USAGE,
|
|
7645
|
+
message
|
|
7646
|
+
};
|
|
7647
|
+
}
|
|
7648
|
+
const status = numericStatus(e);
|
|
7649
|
+
if (status !== void 0) {
|
|
7650
|
+
const statusCode = status;
|
|
7651
|
+
if (status === 401) return {
|
|
7652
|
+
code: "auth",
|
|
7653
|
+
exitCode: CLI_EXIT.AUTH,
|
|
7654
|
+
message,
|
|
7655
|
+
statusCode
|
|
7656
|
+
};
|
|
7657
|
+
if (status === 403) return {
|
|
7658
|
+
code: "forbidden",
|
|
7659
|
+
exitCode: CLI_EXIT.FORBIDDEN,
|
|
7660
|
+
message,
|
|
7661
|
+
statusCode
|
|
7662
|
+
};
|
|
7663
|
+
if (status === 404) return {
|
|
7664
|
+
code: "not_found",
|
|
7665
|
+
exitCode: CLI_EXIT.NOT_FOUND,
|
|
7666
|
+
message,
|
|
7667
|
+
statusCode
|
|
7668
|
+
};
|
|
7669
|
+
if (status >= 400 && status < 500) return {
|
|
7670
|
+
code: `http_${status}`,
|
|
7671
|
+
exitCode: CLI_EXIT.FORBIDDEN,
|
|
7672
|
+
message,
|
|
7673
|
+
statusCode
|
|
7674
|
+
};
|
|
7675
|
+
if (status >= 500 || status === 0) return {
|
|
7676
|
+
code: "unavailable",
|
|
7677
|
+
exitCode: CLI_EXIT.UNAVAILABLE,
|
|
7678
|
+
message,
|
|
7679
|
+
statusCode
|
|
7680
|
+
};
|
|
7681
|
+
}
|
|
7682
|
+
const causeCode = e.cause?.code;
|
|
7683
|
+
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)) {
|
|
7684
|
+
return {
|
|
7685
|
+
code: "unavailable",
|
|
7686
|
+
exitCode: CLI_EXIT.UNAVAILABLE,
|
|
7687
|
+
message,
|
|
7688
|
+
hint: UNAVAILABLE_HINT
|
|
7689
|
+
};
|
|
7690
|
+
}
|
|
7691
|
+
return {
|
|
7692
|
+
code: "error",
|
|
7693
|
+
exitCode: CLI_EXIT.ERROR,
|
|
7694
|
+
message
|
|
7695
|
+
};
|
|
7696
|
+
}
|
|
7697
|
+
var CLI_EXIT, CliError, NETWORK_ERRNO, NETWORK_MESSAGE, UNAVAILABLE_HINT;
|
|
7303
7698
|
var init_cli_error = __esm({
|
|
7304
7699
|
"src/errors/cli.error.ts"() {
|
|
7305
7700
|
"use strict";
|
|
@@ -7322,6 +7717,8 @@ var init_cli_error = __esm({
|
|
|
7322
7717
|
exitCode;
|
|
7323
7718
|
hint;
|
|
7324
7719
|
statusCode;
|
|
7720
|
+
serverCode;
|
|
7721
|
+
issues;
|
|
7325
7722
|
constructor(code, message, options = {}) {
|
|
7326
7723
|
super(message);
|
|
7327
7724
|
this.name = "CliError";
|
|
@@ -7329,6 +7726,8 @@ var init_cli_error = __esm({
|
|
|
7329
7726
|
this.exitCode = options.exitCode ?? CLI_EXIT.ERROR;
|
|
7330
7727
|
this.hint = options.hint;
|
|
7331
7728
|
this.statusCode = options.statusCode;
|
|
7729
|
+
this.serverCode = options.serverCode;
|
|
7730
|
+
this.issues = options.issues?.length ? options.issues : void 0;
|
|
7332
7731
|
if (Error.captureStackTrace) Error.captureStackTrace(this, _CliError);
|
|
7333
7732
|
}
|
|
7334
7733
|
/** Bad arguments, an unknown action, no project — exit 2. */
|
|
@@ -7354,11 +7753,50 @@ var init_cli_error = __esm({
|
|
|
7354
7753
|
statusCode: 403
|
|
7355
7754
|
});
|
|
7356
7755
|
}
|
|
7756
|
+
/**
|
|
7757
|
+
* An API refusal the site already holds the status of (LUA-766) — classified by the same table the top-level
|
|
7758
|
+
* classifier applies to an untyped error: 401 auth · 403 forbidden · 404 not_found · other 4xx `http_<status>`
|
|
7759
|
+
* (10) · 5xx / 0 unavailable (11, with the network hint unless the site gives its own) · no status `error` (1).
|
|
7760
|
+
* A command that reads `response.error.statusCode` throws through here, so `lua logs` on a 503 exits 11 like
|
|
7761
|
+
* every other verb instead of printing the message itself and then throwing an exit-1 `Error`.
|
|
7762
|
+
*/
|
|
7763
|
+
static fromStatus(statusCode, message, hint, detail = {}) {
|
|
7764
|
+
const reported = classifyCliError(Object.assign(new Error(message), {
|
|
7765
|
+
statusCode
|
|
7766
|
+
}));
|
|
7767
|
+
const classHint = reported.exitCode === CLI_EXIT.UNAVAILABLE ? UNAVAILABLE_HINT : reported.hint;
|
|
7768
|
+
return new _CliError(reported.code, message, {
|
|
7769
|
+
exitCode: reported.exitCode,
|
|
7770
|
+
hint: hint ?? classHint,
|
|
7771
|
+
statusCode,
|
|
7772
|
+
serverCode: detail.serverCode,
|
|
7773
|
+
issues: detail.issues
|
|
7774
|
+
});
|
|
7775
|
+
}
|
|
7357
7776
|
static isCliError(error) {
|
|
7358
7777
|
return error instanceof _CliError || typeof error === "object" && error !== null && error.isCliError === true;
|
|
7359
7778
|
}
|
|
7360
7779
|
};
|
|
7361
7780
|
__name(isAccessDeniedError, "isAccessDeniedError");
|
|
7781
|
+
NETWORK_ERRNO = /* @__PURE__ */ new Set([
|
|
7782
|
+
"ECONNREFUSED",
|
|
7783
|
+
"ECONNRESET",
|
|
7784
|
+
"ENOTFOUND",
|
|
7785
|
+
"ETIMEDOUT",
|
|
7786
|
+
"EAI_AGAIN",
|
|
7787
|
+
"EPIPE",
|
|
7788
|
+
"EHOSTUNREACH",
|
|
7789
|
+
"ENETUNREACH",
|
|
7790
|
+
"UND_ERR_CONNECT_TIMEOUT",
|
|
7791
|
+
"UND_ERR_HEADERS_TIMEOUT",
|
|
7792
|
+
"UND_ERR_BODY_TIMEOUT",
|
|
7793
|
+
"UND_ERR_SOCKET"
|
|
7794
|
+
]);
|
|
7795
|
+
NETWORK_MESSAGE = /fetch failed|socket hang up|network request failed|request timeout|ECONNREFUSED|ENOTFOUND/i;
|
|
7796
|
+
UNAVAILABLE_HINT = "The Lua API could not be reached \u2014 check your network and https://status.heylua.ai, then retry.";
|
|
7797
|
+
__name(authHint, "authHint");
|
|
7798
|
+
__name(numericStatus, "numericStatus");
|
|
7799
|
+
__name(classifyCliError, "classifyCliError");
|
|
7362
7800
|
}
|
|
7363
7801
|
});
|
|
7364
7802
|
|
|
@@ -7833,6 +8271,52 @@ var init_request_credential = __esm({
|
|
|
7833
8271
|
|
|
7834
8272
|
// src/api/http.client.ts
|
|
7835
8273
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
8274
|
+
async function classifyErrorResponse(response) {
|
|
8275
|
+
let errorData;
|
|
8276
|
+
try {
|
|
8277
|
+
errorData = await response.json();
|
|
8278
|
+
} catch (jsonError) {
|
|
8279
|
+
errorData = {};
|
|
8280
|
+
}
|
|
8281
|
+
if (response.status === 401) {
|
|
8282
|
+
const serverMessage = typeof errorData.message === "string" ? errorData.message : void 0;
|
|
8283
|
+
if (serverMessage && /not an admin/i.test(serverMessage)) {
|
|
8284
|
+
throw new AuthenticationError(`Access denied for this agent: ${serverMessage}`, "no_agent_access", serverMessage);
|
|
8285
|
+
}
|
|
8286
|
+
const isExplicitCredential = !!serverMessage && /(invalid|expired|missing|no)\s+(api[\s_-]?key|token|credential)/i.test(serverMessage);
|
|
8287
|
+
const isBareAuthRejection = !serverMessage || /^unauthorized$/i.test(serverMessage);
|
|
8288
|
+
if (isExplicitCredential || isBareAuthRejection) {
|
|
8289
|
+
throw new AuthenticationError("Authentication failed. Your Lua credential may be invalid or expired.", "invalid_credentials", serverMessage);
|
|
8290
|
+
}
|
|
8291
|
+
throw new AuthenticationError(`Authentication failed: ${serverMessage}`, "unknown", serverMessage);
|
|
8292
|
+
}
|
|
8293
|
+
if (response.status === 403) {
|
|
8294
|
+
const detail = errorData.message || "You do not have permission to access this resource.";
|
|
8295
|
+
const serverCode = serverCodeOf(errorData.code, errorData.error);
|
|
8296
|
+
throw new CliError("forbidden", `Access denied (403): ${detail}${serverCode ? ` (${serverCode})` : ""}`, {
|
|
8297
|
+
exitCode: CLI_EXIT.FORBIDDEN,
|
|
8298
|
+
statusCode: 403,
|
|
8299
|
+
serverCode,
|
|
8300
|
+
issues: Array.isArray(errorData.issues) ? errorData.issues : void 0,
|
|
8301
|
+
hint: "Check that your Lua login has access to this agent or organization."
|
|
8302
|
+
});
|
|
8303
|
+
}
|
|
8304
|
+
return {
|
|
8305
|
+
success: false,
|
|
8306
|
+
error: {
|
|
8307
|
+
message: errorData.message || `HTTP ${response.status}: ${response.statusText}`,
|
|
8308
|
+
statusCode: response.status,
|
|
8309
|
+
error: errorData.error,
|
|
8310
|
+
retryAfterSeconds: parseRetryAfter(response.headers.get("retry-after")),
|
|
8311
|
+
...errorData
|
|
8312
|
+
}
|
|
8313
|
+
};
|
|
8314
|
+
}
|
|
8315
|
+
function serverCodeOf(code, error) {
|
|
8316
|
+
if (typeof code === "string" && code.length > 0) return code;
|
|
8317
|
+
if (typeof error === "string" && /^[A-Z0-9][A-Z0-9_]*$/.test(error)) return error;
|
|
8318
|
+
return void 0;
|
|
8319
|
+
}
|
|
7836
8320
|
async function* parseSseStream(body, signal) {
|
|
7837
8321
|
const reader = body.getReader();
|
|
7838
8322
|
const decoder = new TextDecoder();
|
|
@@ -8013,42 +8497,7 @@ var init_http_client = __esm({
|
|
|
8013
8497
|
* @private
|
|
8014
8498
|
*/
|
|
8015
8499
|
async classifyErrorResponse(response) {
|
|
8016
|
-
|
|
8017
|
-
try {
|
|
8018
|
-
errorData = await response.json();
|
|
8019
|
-
} catch (jsonError) {
|
|
8020
|
-
errorData = {};
|
|
8021
|
-
}
|
|
8022
|
-
if (response.status === 401) {
|
|
8023
|
-
const serverMessage = typeof errorData.message === "string" ? errorData.message : void 0;
|
|
8024
|
-
if (serverMessage && /not an admin/i.test(serverMessage)) {
|
|
8025
|
-
throw new AuthenticationError(`Access denied for this agent: ${serverMessage}`, "no_agent_access", serverMessage);
|
|
8026
|
-
}
|
|
8027
|
-
const isExplicitCredential = !!serverMessage && /(invalid|expired|missing|no)\s+(api[\s_-]?key|token|credential)/i.test(serverMessage);
|
|
8028
|
-
const isBareAuthRejection = !serverMessage || /^unauthorized$/i.test(serverMessage);
|
|
8029
|
-
if (isExplicitCredential || isBareAuthRejection) {
|
|
8030
|
-
throw new AuthenticationError("Authentication failed. Your Lua credential may be invalid or expired.", "invalid_credentials", serverMessage);
|
|
8031
|
-
}
|
|
8032
|
-
throw new AuthenticationError(`Authentication failed: ${serverMessage}`, "unknown", serverMessage);
|
|
8033
|
-
}
|
|
8034
|
-
if (response.status === 403) {
|
|
8035
|
-
const detail = errorData.message || "You do not have permission to access this resource.";
|
|
8036
|
-
throw new CliError("forbidden", `Access denied (403): ${detail}`, {
|
|
8037
|
-
exitCode: CLI_EXIT.FORBIDDEN,
|
|
8038
|
-
statusCode: 403,
|
|
8039
|
-
hint: "Check that your Lua login has access to this agent or organization."
|
|
8040
|
-
});
|
|
8041
|
-
}
|
|
8042
|
-
return {
|
|
8043
|
-
success: false,
|
|
8044
|
-
error: {
|
|
8045
|
-
message: errorData.message || `HTTP ${response.status}: ${response.statusText}`,
|
|
8046
|
-
statusCode: response.status,
|
|
8047
|
-
error: errorData.error,
|
|
8048
|
-
retryAfterSeconds: parseRetryAfter(response.headers.get("retry-after")),
|
|
8049
|
-
...errorData
|
|
8050
|
-
}
|
|
8051
|
-
};
|
|
8500
|
+
return classifyErrorResponse(response);
|
|
8052
8501
|
}
|
|
8053
8502
|
/**
|
|
8054
8503
|
* Checks if an HTTP status code is retryable
|
|
@@ -8256,6 +8705,8 @@ var init_http_client = __esm({
|
|
|
8256
8705
|
};
|
|
8257
8706
|
}
|
|
8258
8707
|
};
|
|
8708
|
+
__name(classifyErrorResponse, "classifyErrorResponse");
|
|
8709
|
+
__name(serverCodeOf, "serverCodeOf");
|
|
8259
8710
|
__name(parseSseStream, "parseSseStream");
|
|
8260
8711
|
__name(parseRetryAfter, "parseRetryAfter");
|
|
8261
8712
|
__name(isCoreDrainApiError, "isCoreDrainApiError");
|
|
@@ -12314,6 +12765,7 @@ var init_job_api_service = __esm({
|
|
|
12314
12765
|
"src/api/job.api.service.ts"() {
|
|
12315
12766
|
"use strict";
|
|
12316
12767
|
init_http_client();
|
|
12768
|
+
init_cli_error();
|
|
12317
12769
|
init_job_instance();
|
|
12318
12770
|
JobApi = class extends HttpClient {
|
|
12319
12771
|
static {
|
|
@@ -12370,7 +12822,7 @@ var init_job_api_service = __esm({
|
|
|
12370
12822
|
if (response.success && response.data) {
|
|
12371
12823
|
return new JobInstance(this, response.data);
|
|
12372
12824
|
}
|
|
12373
|
-
throw
|
|
12825
|
+
throw CliError.fromStatus(response.error?.statusCode, response.error?.message || "Failed to get job");
|
|
12374
12826
|
}
|
|
12375
12827
|
/**
|
|
12376
12828
|
* Creates a new job for the agent.
|
|
@@ -13440,6 +13892,14 @@ var init_workflow_api_service = __esm({
|
|
|
13440
13892
|
async getVersionEnvOverlay(workflowId, version) {
|
|
13441
13893
|
return this.httpGet(`${this.base}/${workflowId}/versions/${encodeURIComponent(version)}/env-overlay`, await this.auth());
|
|
13442
13894
|
}
|
|
13895
|
+
/**
|
|
13896
|
+
* WF-403 (13 §13.14; LUA-752) — `GET …/:workflowId/export?version=`: the active (or named) version as pushable
|
|
13897
|
+
* files (`{ form, version, files:[{ path, contents }], warnings }`). `lua workflows export` writes them to disk.
|
|
13898
|
+
*/
|
|
13899
|
+
async exportWorkflowFiles(workflowId, version) {
|
|
13900
|
+
const qs = version ? `?version=${encodeURIComponent(version)}` : "";
|
|
13901
|
+
return this.httpGet(`${this.base}/${workflowId}/export${qs}`, await this.auth());
|
|
13902
|
+
}
|
|
13443
13903
|
async getWorkflowVersions(workflowId) {
|
|
13444
13904
|
return this.httpGet(`${this.base}/${workflowId}/versions`, await this.auth());
|
|
13445
13905
|
}
|
|
@@ -13536,6 +13996,34 @@ var init_workflow_api_service = __esm({
|
|
|
13536
13996
|
async retryStep(runId, stepId, data = {}) {
|
|
13537
13997
|
return this.httpPost(`${this.runs}/${runId}/steps/${pathId(stepId)}/retry`, data, await this.auth());
|
|
13538
13998
|
}
|
|
13999
|
+
/**
|
|
14000
|
+
* R37 (LUA-752) — a human decides a parked step: `skip` it, `complete` it with the output it would have produced,
|
|
14001
|
+
* or `fail` it (the step's onError policy applies). 400 `VALIDATION_FAILED{output-required}` / `RESOLVE_OUTPUT_INVALID`,
|
|
14002
|
+
* 403 `APPROVAL_REQUIRES_HUMAN` / `NOT_RUN_CREATOR`, 404 `RUN_NOT_FOUND` / `STEP_NOT_FOUND`, 409 `STEP_NOT_PARKED` /
|
|
14003
|
+
* `RUN_TERMINAL`, 413 `OUTPUT_TOO_LARGE`; the CAS loser is a 200 `{ resolved:false, reason, recorded }`.
|
|
14004
|
+
*/
|
|
14005
|
+
async resolveStep(runId, stepId, data) {
|
|
14006
|
+
return this.httpPost(`${this.runs}/${runId}/steps/${pathId(stepId)}/resolve`, data, await this.auth());
|
|
14007
|
+
}
|
|
14008
|
+
/**
|
|
14009
|
+
* R45 (LUA-752) — raise a parked run's budget (`maxCredits` / `maxSteps` / `maxJobSeconds` / `maxDurationSeconds`;
|
|
14010
|
+
* increases only). 400 `VALIDATION_FAILED` / `CAP_EXCEEDED`, 403 `NOT_RUN_CREATOR`, 409 `BUDGET_NOT_RAISABLE`.
|
|
14011
|
+
*/
|
|
14012
|
+
async raiseBudget(runId, data) {
|
|
14013
|
+
return this.httpPost(`${this.runs}/${runId}/budget`, data, await this.auth());
|
|
14014
|
+
}
|
|
14015
|
+
/**
|
|
14016
|
+
* R39 (LUA-752) — the current approval payload with its `payloadFingerprint` / `editRevision` (what
|
|
14017
|
+
* `approve --edit --fingerprint` echoes). `path` pages one array of a large payload.
|
|
14018
|
+
*/
|
|
14019
|
+
async getApprovalPayload(runId, approvalId, query = {}) {
|
|
14020
|
+
const q = new URLSearchParams();
|
|
14021
|
+
if (query.path) q.append("path", query.path);
|
|
14022
|
+
if (query.cursor) q.append("cursor", query.cursor);
|
|
14023
|
+
if (query.limit !== void 0) q.append("limit", String(query.limit));
|
|
14024
|
+
const qs = q.toString();
|
|
14025
|
+
return this.httpGet(`${this.runs}/${runId}/approvals/${pathId(approvalId)}/payload${qs ? `?${qs}` : ""}`, await this.auth());
|
|
14026
|
+
}
|
|
13539
14027
|
/** R13 — resolve an approval (human; `expectedFingerprint` guards against an edited payload — 409 `PAYLOAD_MISMATCH`). */
|
|
13540
14028
|
async resolveApproval(runId, approvalId, data) {
|
|
13541
14029
|
return this.httpPost(`${this.runs}/${runId}/approvals/${pathId(approvalId)}/resolve`, data, await this.auth());
|
|
@@ -13620,6 +14108,14 @@ var init_workflow_api_service = __esm({
|
|
|
13620
14108
|
async getSchedule(jobId) {
|
|
13621
14109
|
return this.httpGet(`${this.schedules}/${encodeURIComponent(jobId)}`, await this.auth());
|
|
13622
14110
|
}
|
|
14111
|
+
/** R27 (LUA-752) — create or replace the plain schedule Job of a workflow (201; 400 `VALIDATION_FAILED` / `WORKFLOW_NOT_ON_AGENT`, 404 `WORKFLOW_NOT_FOUND`, 409 `SCHEDULE_CAP`). */
|
|
14112
|
+
async createSchedule(data) {
|
|
14113
|
+
return this.httpPost(this.schedules, data, await this.auth());
|
|
14114
|
+
}
|
|
14115
|
+
/** R56 (LUA-752) — pause / resume a schedule, persist `backfillOnEnable`, one-shot `backfillNow` (404 `SCHEDULE_NOT_FOUND`, 400 `VALIDATION_FAILED{issues}`). */
|
|
14116
|
+
async updateSchedule(jobId, data) {
|
|
14117
|
+
return this.httpPatch(`${this.schedules}/${encodeURIComponent(jobId)}`, data, await this.auth());
|
|
14118
|
+
}
|
|
13623
14119
|
/** 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). */
|
|
13624
14120
|
async deleteSchedule(jobId) {
|
|
13625
14121
|
return this.httpDelete(`${this.schedules}/${encodeURIComponent(jobId)}`, await this.auth());
|