lua-cli 3.31.0 → 3.32.2
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 +416 -103
- package/dist/api-exports.js +1992 -299
- package/dist/api-exports.js.map +1 -1
- package/dist/index.js +5262 -1914
- package/dist/index.js.map +1 -1
- package/dist/voice/test/index.d.ts +54 -54
- package/dist/workflow-builder.d.ts +257 -44
- package/dist/workflow-builder.js +1382 -265
- package/dist/workflow-builder.js.map +1 -1
- package/docs/README.md +2 -2
- package/docs/api/LuaWorkflow.md +44 -28
- package/docs/api/Workflows.md +12 -1
- package/docs/workflows/approvals.md +14 -1
- package/docs/workflows/connections-in-coding-turns.md +1 -0
- package/docs/workflows/correlation-keys.md +1 -0
- package/docs/workflows/git-credentials.md +22 -1
- package/docs/workflows/goals.md +46 -0
- package/docs/workflows/limits.md +6 -0
- package/docs/workflows/recovery.md +6 -2
- package/docs/workflows/replay-local.md +10 -10
- package/docs/workflows/schedules.md +15 -0
- package/docs/workflows/script-form.md +20 -10
- package/docs/workflows/testing-offline.md +25 -20
- package/docs/workflows/workspaces-and-long-steps.md +38 -2
- package/package.json +2 -2
- package/template/examples/workflows/CLAUDE.md +16 -13
- package/template/examples/workflows/pr-review-round.ts +61 -20
- package/template/examples/workflows/provision-tenant.ts +25 -8
- package/template/examples/workflows/refund-approval.ts +30 -17
- package/template/examples/workflows/support-triage.ts +59 -22
- package/template/examples/workflows/ticket-to-pr.ts +125 -46
- package/template/examples/workflows/vendor-invoices.ts +69 -16
- package/template/package.json +1 -1
package/dist/api-exports.js
CHANGED
|
@@ -90,9 +90,9 @@ function mcpActionTokens(action) {
|
|
|
90
90
|
return action.replace(/([a-z0-9])([A-Z])/g, "$1 $2").toLowerCase().split(/[^a-z0-9]+/).filter(Boolean);
|
|
91
91
|
}
|
|
92
92
|
function isReviewableMcpDraftTool(tool) {
|
|
93
|
-
const
|
|
94
|
-
if (
|
|
95
|
-
const action = tool.slice(
|
|
93
|
+
const sep4 = tool.indexOf("_");
|
|
94
|
+
if (sep4 <= 0 || sep4 >= tool.length - 1) return false;
|
|
95
|
+
const action = tool.slice(sep4 + 1);
|
|
96
96
|
const tokens = mcpActionTokens(action);
|
|
97
97
|
if (!tokens.includes("draft") && !tokens.includes("drafts")) return false;
|
|
98
98
|
return !MCP_TOOL_READ_VERB_RE.test(action);
|
|
@@ -356,6 +356,9 @@ function isDesktopFileSessionId(value3) {
|
|
|
356
356
|
function isImplicitModelSelectionSource(source) {
|
|
357
357
|
return source !== void 0 && IMPLICIT_MODEL_SELECTION_SOURCES.includes(source);
|
|
358
358
|
}
|
|
359
|
+
function isPlatformFallbackModelSource(source) {
|
|
360
|
+
return source === PLATFORM_FALLBACK_MODEL_SOURCE;
|
|
361
|
+
}
|
|
359
362
|
function resolveRequireToolApproval(rules) {
|
|
360
363
|
const raw = rules?.requireToolApproval ?? rules?.requireApproval;
|
|
361
364
|
if (raw === void 0 || raw === null) return void 0;
|
|
@@ -395,14 +398,14 @@ async function readCoreDrainingRetryDelayMs(response) {
|
|
|
395
398
|
return coreDrainingRetryDelayMs(response.headers.get("Retry-After"));
|
|
396
399
|
}
|
|
397
400
|
function waitForCoreDrain(delayMs, signal) {
|
|
398
|
-
return new Promise((
|
|
401
|
+
return new Promise((resolve3, reject) => {
|
|
399
402
|
if (signal?.aborted) {
|
|
400
403
|
reject(signal.reason ?? new DOMException("Aborted", "AbortError"));
|
|
401
404
|
return;
|
|
402
405
|
}
|
|
403
406
|
const timer = setTimeout(() => {
|
|
404
407
|
signal?.removeEventListener("abort", onAbort);
|
|
405
|
-
|
|
408
|
+
resolve3();
|
|
406
409
|
}, delayMs);
|
|
407
410
|
function onAbort() {
|
|
408
411
|
clearTimeout(timer);
|
|
@@ -514,7 +517,6 @@ function luaClientMetricLabels(client) {
|
|
|
514
517
|
const canonical2 = parseLuaClientHeader(serializeLuaClientHeader(client));
|
|
515
518
|
return {
|
|
516
519
|
client_family: canonical2?.app ?? "unknown",
|
|
517
|
-
client_version: canonical2?.version ?? "unknown",
|
|
518
520
|
client_attribution: canonical2?.attribution ?? "unknown"
|
|
519
521
|
};
|
|
520
522
|
}
|
|
@@ -536,6 +538,75 @@ function isKnownProfile(profiles, profileId) {
|
|
|
536
538
|
function hasCapability(profiles, profileId, required) {
|
|
537
539
|
return capabilitiesFor(profiles, profileId).includes(required);
|
|
538
540
|
}
|
|
541
|
+
function workflowJobId(id) {
|
|
542
|
+
switch (id.kind) {
|
|
543
|
+
case "start":
|
|
544
|
+
return `wft_${id.runId}_0`;
|
|
545
|
+
case "start-redrive":
|
|
546
|
+
return `wft_${id.runId}_start_r${id.now}`;
|
|
547
|
+
case "tick":
|
|
548
|
+
return `wft_${id.runId}_${id.seq}`;
|
|
549
|
+
case "timer":
|
|
550
|
+
return `wft_${id.runId}_timer_${id.stepId}_${id.attempt}`;
|
|
551
|
+
case "timer-remainder":
|
|
552
|
+
return `wft_${id.runId}_timer_${id.stepId}_${id.attempt}_r${id.now}`;
|
|
553
|
+
case "timer-deferral":
|
|
554
|
+
return `wft_${id.runId}_timer_${id.stepId}_${id.attempt}_d${id.now}`;
|
|
555
|
+
case "timer-sibling":
|
|
556
|
+
return `wft_${id.runId}_timer_${id.stepId}_${id.attempt}_w${id.now}`;
|
|
557
|
+
case "retry":
|
|
558
|
+
return `wft_${id.runId}_retry_${id.stepId}_${id.attempt}`;
|
|
559
|
+
case "respawn":
|
|
560
|
+
return `wft_${id.runId}_respawn_${id.stepId}_${id.attempt}_s${id.segment}_r${id.now}`;
|
|
561
|
+
case "redrive":
|
|
562
|
+
return `wft_${id.runId}_redrive_${id.stepId}_${id.attempt}_r${id.now}`;
|
|
563
|
+
case "redispatch":
|
|
564
|
+
return `wft_${id.runId}_redispatch_${id.stepId}_${id.attempt}_x${id.executionId ?? "none"}_r${id.now}`;
|
|
565
|
+
case "expiry":
|
|
566
|
+
return `wft_${id.runId}_expire_${id.stepId}_${id.attempt}_t${id.suspendedAt}${id.hop > 0 ? `_h${id.hop}` : ""}`;
|
|
567
|
+
case "step-terminal":
|
|
568
|
+
return `wft_${id.runId}_term_${id.stepId}_${id.attempt}_r${id.now}`;
|
|
569
|
+
case "handback":
|
|
570
|
+
return `wft_${id.runId}_handback_${id.stepId}_${id.attempt}_${id.now}`;
|
|
571
|
+
case "foreach-rate":
|
|
572
|
+
return `wft_${id.runId}_fe_${id.stepId}_${id.attempt}_${id.bucket}`;
|
|
573
|
+
case "budget-expiry":
|
|
574
|
+
return `wft_${id.runId}_expiry_budget_r${id.now}`;
|
|
575
|
+
case "budget-resume":
|
|
576
|
+
return `wft_${id.runId}_resume_budget_${id.now}`;
|
|
577
|
+
case "deadline":
|
|
578
|
+
return `wft_${id.runId}_deadline_${id.deadlineAt}`;
|
|
579
|
+
case "deadline-remainder":
|
|
580
|
+
return `wft_${id.runId}_deadline_${id.deadlineAt}_r${id.now}`;
|
|
581
|
+
case "readmit":
|
|
582
|
+
return `wft_${id.runId}_readmit_${id.now}`;
|
|
583
|
+
case "cancel":
|
|
584
|
+
return `wft_${id.runId}_cancel`;
|
|
585
|
+
case "reconcile":
|
|
586
|
+
return `wft_${id.runId}_reconcile_${id.now}`;
|
|
587
|
+
case "replay":
|
|
588
|
+
return `wft_${id.runId}_replay_g${id.leaseGeneration}`;
|
|
589
|
+
case "migration":
|
|
590
|
+
return `wfm_${id.migrationId}`;
|
|
591
|
+
default:
|
|
592
|
+
return assertNever(id);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
function assertNever(id) {
|
|
596
|
+
throw new Error(`workflowJobId: unknown wake kind ${JSON.stringify(id)}`);
|
|
597
|
+
}
|
|
598
|
+
function agentStepJobId(msg) {
|
|
599
|
+
return `${msg.runId}_${msg.stepId}_${msg.attempt}_x${msg.executionId}`;
|
|
600
|
+
}
|
|
601
|
+
function heavyStepJobId(msg) {
|
|
602
|
+
return `${msg.runId}_${msg.stepId}_${msg.attempt}_x${msg.executionId}`;
|
|
603
|
+
}
|
|
604
|
+
function jobSpawnJobId(msg) {
|
|
605
|
+
return `${msg.runId}_${msg.stepId}_${msg.attempt}_s${msg.segment}_${msg.executionId}`;
|
|
606
|
+
}
|
|
607
|
+
function exportAutoJobId(exportId, suffix) {
|
|
608
|
+
return suffix ? `${exportId}:${suffix}` : exportId;
|
|
609
|
+
}
|
|
539
610
|
function isSystemRun(identity) {
|
|
540
611
|
return identity.userId.startsWith(SYSTEM_USER_PREFIX);
|
|
541
612
|
}
|
|
@@ -582,6 +653,99 @@ function scheduledTimeKey(scheduledTime) {
|
|
|
582
653
|
function scheduledWorkflowRunIdForTime(jobId, scheduledTime) {
|
|
583
654
|
return scheduledWorkflowRunId(jobId, scheduledTime);
|
|
584
655
|
}
|
|
656
|
+
function isWithinWorkflowJobRange(member, value3) {
|
|
657
|
+
const { min, max } = WORKFLOW_JOB_RANGES[member];
|
|
658
|
+
return typeof value3 === "number" && Number.isInteger(value3) && value3 >= min && value3 <= max;
|
|
659
|
+
}
|
|
660
|
+
function workflowJobRangeMessage(member) {
|
|
661
|
+
const { min, max } = WORKFLOW_JOB_RANGES[member];
|
|
662
|
+
return `\`${member}\` must be an integer ${min}..${max}`;
|
|
663
|
+
}
|
|
664
|
+
function isWorkflowSingleStepType(type) {
|
|
665
|
+
return typeof type === "string" && WORKFLOW_SINGLE_STEP_TYPES.includes(type);
|
|
666
|
+
}
|
|
667
|
+
function isWorkflowHitlEntryType(type) {
|
|
668
|
+
return typeof type === "string" && WORKFLOW_HITL_ENTRY_TYPES.includes(type);
|
|
669
|
+
}
|
|
670
|
+
function isWorkflowArmEntryType(type) {
|
|
671
|
+
return typeof type === "string" && WORKFLOW_ARM_ENTRY_TYPES.includes(type);
|
|
672
|
+
}
|
|
673
|
+
function workflowContainerRunsHitlArm(container) {
|
|
674
|
+
return typeof container === "string" && WORKFLOW_HITL_ARM_CONTAINERS.includes(container);
|
|
675
|
+
}
|
|
676
|
+
function workflowHitlArmUnsupportedMessage(type, id, container) {
|
|
677
|
+
const legal = WORKFLOW_HITL_ARM_CONTAINERS.map((c) => `\`${c}\``).join(" / ");
|
|
678
|
+
return `the engine does not run \`${type}\` inside a \`${container}\` yet (node "${id}") \u2014 place it at the top level, as a ${legal} arm, or inside a child \`workflow\``;
|
|
679
|
+
}
|
|
680
|
+
function workflowHitlArmShapeMessage(type, id, shape) {
|
|
681
|
+
return shape === "mapped-arm" ? `\`${type}\` arm "${id}" takes the previous output as its payload \u2014 it cannot head a [mapping, step] chain; map before the container instead` : `a chunked foreach hands each child a slice of items, not one \u2014 \`${type}\` body "${id}" takes one item; drop \`chunk\``;
|
|
682
|
+
}
|
|
683
|
+
function groupCount(re) {
|
|
684
|
+
let n2 = GROUP_COUNT.get(re);
|
|
685
|
+
if (n2 === void 0) {
|
|
686
|
+
n2 = new RegExp(`${re.source}|`, re.flags.replace("g", "")).exec("").length - 1;
|
|
687
|
+
GROUP_COUNT.set(re, n2);
|
|
688
|
+
}
|
|
689
|
+
return n2;
|
|
690
|
+
}
|
|
691
|
+
function isWorkflowSecretKey(key) {
|
|
692
|
+
return WORKFLOW_SECRET_KEY_RE.test(key);
|
|
693
|
+
}
|
|
694
|
+
function applyPatterns(text, patterns) {
|
|
695
|
+
let out = text;
|
|
696
|
+
for (const { re, suffix } of patterns) {
|
|
697
|
+
re.lastIndex = 0;
|
|
698
|
+
if (!re.test(out)) continue;
|
|
699
|
+
re.lastIndex = 0;
|
|
700
|
+
const groups = groupCount(re);
|
|
701
|
+
out = out.replace(re, (...args) => {
|
|
702
|
+
const kept = args.slice(1, 1 + groups).map((g) => typeof g === "string" ? g : "");
|
|
703
|
+
if (suffix && kept.length > 0) {
|
|
704
|
+
const tail = kept[kept.length - 1];
|
|
705
|
+
return `${kept.slice(0, -1).join("")}${REDACTED_PLACEHOLDER}${tail}`;
|
|
706
|
+
}
|
|
707
|
+
return `${kept.join("")}${REDACTED_PLACEHOLDER}`;
|
|
708
|
+
});
|
|
709
|
+
}
|
|
710
|
+
return out;
|
|
711
|
+
}
|
|
712
|
+
function scrubSecretText(text) {
|
|
713
|
+
if (typeof text !== "string" || text.length < 4) return text;
|
|
714
|
+
return applyPatterns(applyPatterns(text, SECRET_LITERAL_PATTERNS), SECRET_PAIR_PATTERNS);
|
|
715
|
+
}
|
|
716
|
+
function boundScrubInput(text, max = SCRUB_INPUT_MAX_CHARS) {
|
|
717
|
+
if (typeof text !== "string" || text.length <= max) return text;
|
|
718
|
+
const window = text.slice(Math.max(0, max - SCRUB_CUT_BACKOFF_CHARS), max);
|
|
719
|
+
const ws = window.search(/\s\S*$/);
|
|
720
|
+
const cut = ws >= 0 ? max - window.length + ws : max;
|
|
721
|
+
return `${text.slice(0, cut)}\u2026`;
|
|
722
|
+
}
|
|
723
|
+
function scrubSecretLines(lines) {
|
|
724
|
+
return lines.map((l) => typeof l === "string" ? scrubSecretText(boundScrubInput(l)) : l);
|
|
725
|
+
}
|
|
726
|
+
function messageText(value3) {
|
|
727
|
+
if (typeof value3 === "string") return value3;
|
|
728
|
+
if (value3 instanceof Error) return value3.message;
|
|
729
|
+
if (value3 && typeof value3 === "object") {
|
|
730
|
+
const m = value3.message;
|
|
731
|
+
if (typeof m === "string") return m;
|
|
732
|
+
try {
|
|
733
|
+
return JSON.stringify(value3);
|
|
734
|
+
} catch {
|
|
735
|
+
return "";
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
return value3 === void 0 || value3 === null ? "" : String(value3);
|
|
739
|
+
}
|
|
740
|
+
function scrubProviderMessage(raw, max = PROVIDER_MESSAGE_MAX_CHARS) {
|
|
741
|
+
const text = boundScrubInput(messageText(raw)).replace(/\s+/g, " ").trim();
|
|
742
|
+
if (!text) return void 0;
|
|
743
|
+
const out = scrubSecretText(text);
|
|
744
|
+
return out.length > max ? `${out.slice(0, max - 1)}\u2026` : out;
|
|
745
|
+
}
|
|
746
|
+
function scrubStepErrorMessage(raw) {
|
|
747
|
+
return scrubProviderMessage(raw, ERROR_MESSAGE_MAX_CHARS);
|
|
748
|
+
}
|
|
585
749
|
function isWorkflowAuditEvent(action) {
|
|
586
750
|
return typeof action === "string" && WORKFLOW_AUDIT_EVENTS.includes(action);
|
|
587
751
|
}
|
|
@@ -676,7 +840,7 @@ ${PREAMBLE}
|
|
|
676
840
|
|
|
677
841
|
${items.join("\n\n")}`;
|
|
678
842
|
}
|
|
679
|
-
var __defProp2, __name2, CHANNEL_SEND_CHANNELS, REVIEWABLE_ACTION_EXECUTE_TOOL_ALLOWLIST, REVIEWABLE_MCP_SEND_TOOL_SUFFIX, MCP_TOOL_READ_VERB_RE, MCP_DRAFT_CREATE_VERBS, NON_INTERACTIVE_CHANNELS, RICH_PARTS_MESSAGE_ID_PREFIX, SCREENSHOT_MESSAGE_ID_PREFIX, BROWSER_COMMANDS, BROWSER_COMMAND_NAMES, DESKTOP_FILE_COMMANDS, DESKTOP_FILE_COMMAND_SET, REASONING_EFFORT_VALUES, IMPLICIT_MODEL_SELECTION_SOURCES, AGENT_NAME_TOKEN, DEFAULT_PERSONA_GUIDE, PERSONAL_SPACE_STARTING_PERSONA, CORE_DRAINING_CODE, CORE_DRAINING_DEFAULT_RETRY_MS, CORE_DRAINING_MAX_RETRY_MS, VoiceNameSchema, PluginProviderSchema, RealtimeProviderSchema, PluginClassSchema, ModelDescriptorSchema, InferenceModelSchema, PluginModelSchema, RealtimeModelSchema, LuaVoiceModelSchema, TurnDetectionSchema, InterruptionSchema, BuiltinAudioClipSchema, AudioConfigSchema, BackgroundAudioEntrySchema, BackgroundAudioSchema, LuaVoiceConfigInnerSchema, LuaVoiceConfigSchema, LuaVoiceRefSchema, LUA_JOB_DEFAULT_TIMEOUT_SECONDS, LUA_JOB_MIN_TIMEOUT_SECONDS, LUA_JOB_MAX_TIMEOUT_SECONDS, TEMPLATE_TRIGGER_URL_ENV_PREFIX, SUBJECT_TYPES, SubjectTypeSchema, CREDENTIAL_TYPES, CredentialTypeSchema, DEVICE_OPERATIONS, DeviceOperationSchema, DEVICE_SCOPE_BY_OPERATION, DeviceBindingSchema, IdSchema, PrincipalDescriptorSchema, ActorDescriptorSchema, PrincipalOwnerSchema, CredentialLifecycleSchema, GeneralCredentialDescriptorSchema, DeviceCredentialDescriptorSchema, GeneralPrincipalContextSchema, DeviceCredentialPrincipalContextSchema, RawPrincipalContextSchema, PrincipalContextSchema, DeviceCredentialClaimSchema, LUA_CLIENT_HEADER, LUA_CLIENT_APPS, SEMVER_PATTERN, WEB_RELEASE_PATTERN, CLIENT_HEADER_PATTERN, AUTHZ_PROJECTION_VERSION, ProjectedScopeSchema, DisplayRoleSchema, AuthorizationPrincipalSchema, CredentialContextSchema, ProjectionAnomalySchema, ProjectedOrgSchema, ProjectedResourceSchema, CapabilityProfilesSchema, RoleCatalogSchema, EffectiveAuthorizationSchema, ResourcePageSchema, SYSTEM_USER_PREFIX, WORKFLOW_RUN_IN_FLIGHT, WORKFLOW_RUN_IDLE, WORKFLOW_RUN_TERMINAL, WORKFLOW_RUN_STATUSES, ARCHIVE_WINDOW_MARGIN_DAYS, WORKFLOW_ORG_PURGING_TTL_S, WORKFLOW_ORG_PURGE_FORCE_AFTER_MS, IDEMPOTENCY_HOLDING_STATUSES, WORKFLOW_SCHEDULED_RUN_ID_PREFIX, CLOUD_TASK_RUN_ID_PREFIX, WORKFLOW_OPERATION_ID_PREFIX, WORKFLOW_SIGNAL_PAYLOAD_MAX_BYTES, WORKFLOW_AUDIT_EVENTS, WORKFLOW_AUDIT_METADATA_MAX_BYTES, INDENT, WRAP_WIDTH, NOUNS, GET_TOOL_NAMES, PREAMBLE;
|
|
843
|
+
var __defProp2, __name2, CHANNEL_SEND_CHANNELS, REVIEWABLE_ACTION_EXECUTE_TOOL_ALLOWLIST, REVIEWABLE_MCP_SEND_TOOL_SUFFIX, MCP_TOOL_READ_VERB_RE, MCP_DRAFT_CREATE_VERBS, NON_INTERACTIVE_CHANNELS, RICH_PARTS_MESSAGE_ID_PREFIX, SCREENSHOT_MESSAGE_ID_PREFIX, BROWSER_COMMANDS, BROWSER_COMMAND_NAMES, DESKTOP_FILE_COMMANDS, DESKTOP_FILE_COMMAND_SET, REASONING_EFFORT_VALUES, IMPLICIT_MODEL_SELECTION_SOURCES, PLATFORM_FALLBACK_MODEL_SOURCE, AGENT_NAME_TOKEN, DEFAULT_PERSONA_GUIDE, PERSONAL_SPACE_STARTING_PERSONA, CORE_DRAINING_CODE, CORE_DRAINING_DEFAULT_RETRY_MS, CORE_DRAINING_MAX_RETRY_MS, VoiceNameSchema, PluginProviderSchema, RealtimeProviderSchema, PluginClassSchema, ModelDescriptorSchema, InferenceModelSchema, PluginModelSchema, RealtimeModelSchema, LuaVoiceModelSchema, TurnDetectionSchema, InterruptionSchema, BuiltinAudioClipSchema, AudioConfigSchema, BackgroundAudioEntrySchema, BackgroundAudioSchema, LuaVoiceConfigInnerSchema, LuaVoiceConfigSchema, LuaVoiceRefSchema, LUA_JOB_DEFAULT_TIMEOUT_SECONDS, LUA_JOB_MIN_TIMEOUT_SECONDS, LUA_JOB_MAX_TIMEOUT_SECONDS, TEMPLATE_TRIGGER_URL_ENV_PREFIX, SUBJECT_TYPES, SubjectTypeSchema, CREDENTIAL_TYPES, CredentialTypeSchema, DEVICE_OPERATIONS, DeviceOperationSchema, DEVICE_SCOPE_BY_OPERATION, DeviceBindingSchema, IdSchema, PrincipalDescriptorSchema, ActorDescriptorSchema, PrincipalOwnerSchema, CredentialLifecycleSchema, GeneralCredentialDescriptorSchema, DeviceCredentialDescriptorSchema, GeneralPrincipalContextSchema, DeviceCredentialPrincipalContextSchema, RawPrincipalContextSchema, PrincipalContextSchema, DeviceCredentialClaimSchema, LUA_CLIENT_HEADER, LUA_CLIENT_APPS, SEMVER_PATTERN, WEB_RELEASE_PATTERN, CLIENT_HEADER_PATTERN, AUTHZ_PROJECTION_VERSION, ProjectedScopeSchema, DisplayRoleSchema, AuthorizationPrincipalSchema, CredentialContextSchema, ProjectionAnomalySchema, ProjectedOrgSchema, ProjectedResourceSchema, CapabilityProfilesSchema, RoleCatalogSchema, EffectiveAuthorizationSchema, ResourcePageSchema, SYSTEM_USER_PREFIX, WORKFLOW_RUN_IN_FLIGHT, WORKFLOW_RUN_IDLE, WORKFLOW_RUN_TERMINAL, WORKFLOW_RUN_STATUSES, WORKFLOW_STEP_STATUSES, WORKFLOW_STEP_IN_FLIGHT, ARCHIVE_WINDOW_MARGIN_DAYS, WORKFLOW_ORG_PURGING_TTL_S, WORKFLOW_ORG_PURGE_FORCE_AFTER_MS, IDEMPOTENCY_HOLDING_STATUSES, WORKFLOW_SCHEDULED_RUN_ID_PREFIX, CLOUD_TASK_RUN_ID_PREFIX, WORKFLOW_OPERATION_ID_PREFIX, WORKFLOW_CONNECTION_KEY_RE, WORKFLOW_SIGNAL_PAYLOAD_MAX_BYTES, WORKFLOW_RESOLVE_OUTPUT_MAX_BYTES, WORKFLOW_RETRY_BACKOFFS, WORKFLOW_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;
|
|
680
844
|
var init_dist = __esm({
|
|
681
845
|
"../shared-types/dist/index.mjs"() {
|
|
682
846
|
"use strict";
|
|
@@ -1007,6 +1171,9 @@ var init_dist = __esm({
|
|
|
1007
1171
|
];
|
|
1008
1172
|
__name(isImplicitModelSelectionSource, "isImplicitModelSelectionSource");
|
|
1009
1173
|
__name2(isImplicitModelSelectionSource, "isImplicitModelSelectionSource");
|
|
1174
|
+
PLATFORM_FALLBACK_MODEL_SOURCE = "platform-fallback";
|
|
1175
|
+
__name(isPlatformFallbackModelSource, "isPlatformFallbackModelSource");
|
|
1176
|
+
__name2(isPlatformFallbackModelSource, "isPlatformFallbackModelSource");
|
|
1010
1177
|
__name(resolveRequireToolApproval, "resolveRequireToolApproval");
|
|
1011
1178
|
__name2(resolveRequireToolApproval, "resolveRequireToolApproval");
|
|
1012
1179
|
AGENT_NAME_TOKEN = "[Your Agent Name]";
|
|
@@ -1659,6 +1826,18 @@ This text is who you are for this person. As you learn them, their name, their w
|
|
|
1659
1826
|
__name2(isKnownProfile, "isKnownProfile");
|
|
1660
1827
|
__name(hasCapability, "hasCapability");
|
|
1661
1828
|
__name2(hasCapability, "hasCapability");
|
|
1829
|
+
__name(workflowJobId, "workflowJobId");
|
|
1830
|
+
__name2(workflowJobId, "workflowJobId");
|
|
1831
|
+
__name(assertNever, "assertNever");
|
|
1832
|
+
__name2(assertNever, "assertNever");
|
|
1833
|
+
__name(agentStepJobId, "agentStepJobId");
|
|
1834
|
+
__name2(agentStepJobId, "agentStepJobId");
|
|
1835
|
+
__name(heavyStepJobId, "heavyStepJobId");
|
|
1836
|
+
__name2(heavyStepJobId, "heavyStepJobId");
|
|
1837
|
+
__name(jobSpawnJobId, "jobSpawnJobId");
|
|
1838
|
+
__name2(jobSpawnJobId, "jobSpawnJobId");
|
|
1839
|
+
__name(exportAutoJobId, "exportAutoJobId");
|
|
1840
|
+
__name2(exportAutoJobId, "exportAutoJobId");
|
|
1662
1841
|
SYSTEM_USER_PREFIX = "system:";
|
|
1663
1842
|
__name(isSystemRun, "isSystemRun");
|
|
1664
1843
|
__name2(isSystemRun, "isSystemRun");
|
|
@@ -1688,6 +1867,27 @@ This text is who you are for this person. As you learn them, their name, their w
|
|
|
1688
1867
|
...WORKFLOW_RUN_IDLE,
|
|
1689
1868
|
...WORKFLOW_RUN_TERMINAL
|
|
1690
1869
|
];
|
|
1870
|
+
WORKFLOW_STEP_STATUSES = [
|
|
1871
|
+
"pending",
|
|
1872
|
+
"ready",
|
|
1873
|
+
"waiting",
|
|
1874
|
+
"dispatched",
|
|
1875
|
+
"claimed",
|
|
1876
|
+
"running",
|
|
1877
|
+
"suspended",
|
|
1878
|
+
"cancellation_requested",
|
|
1879
|
+
"completed",
|
|
1880
|
+
"failed",
|
|
1881
|
+
"skipped",
|
|
1882
|
+
"cancelled",
|
|
1883
|
+
"timeout",
|
|
1884
|
+
"reaped"
|
|
1885
|
+
];
|
|
1886
|
+
WORKFLOW_STEP_IN_FLIGHT = [
|
|
1887
|
+
"claimed",
|
|
1888
|
+
"running",
|
|
1889
|
+
"cancellation_requested"
|
|
1890
|
+
];
|
|
1691
1891
|
ARCHIVE_WINDOW_MARGIN_DAYS = 7;
|
|
1692
1892
|
__name(shouldSkipArchive, "shouldSkipArchive");
|
|
1693
1893
|
__name2(shouldSkipArchive, "shouldSkipArchive");
|
|
@@ -1717,11 +1917,223 @@ This text is who you are for this person. As you learn them, their name, their w
|
|
|
1717
1917
|
__name2(workflowOccurrenceId, "workflowOccurrenceId");
|
|
1718
1918
|
__name(workflowOrgSlotKey, "workflowOrgSlotKey");
|
|
1719
1919
|
__name2(workflowOrgSlotKey, "workflowOrgSlotKey");
|
|
1920
|
+
WORKFLOW_CONNECTION_KEY_RE = /^[a-z][a-z0-9_-]{0,63}$/;
|
|
1720
1921
|
__name(scheduledTimeKey, "scheduledTimeKey");
|
|
1721
1922
|
__name2(scheduledTimeKey, "scheduledTimeKey");
|
|
1722
1923
|
__name(scheduledWorkflowRunIdForTime, "scheduledWorkflowRunIdForTime");
|
|
1723
1924
|
__name2(scheduledWorkflowRunIdForTime, "scheduledWorkflowRunIdForTime");
|
|
1724
1925
|
WORKFLOW_SIGNAL_PAYLOAD_MAX_BYTES = 64 * 1024;
|
|
1926
|
+
WORKFLOW_RESOLVE_OUTPUT_MAX_BYTES = 256 * 1024;
|
|
1927
|
+
WORKFLOW_RETRY_BACKOFFS = [
|
|
1928
|
+
"fixed",
|
|
1929
|
+
"exponential"
|
|
1930
|
+
];
|
|
1931
|
+
WORKFLOW_JOB_RESOURCES = [
|
|
1932
|
+
"small",
|
|
1933
|
+
"medium",
|
|
1934
|
+
"large"
|
|
1935
|
+
];
|
|
1936
|
+
WORKFLOW_SIDE_EFFECTS = [
|
|
1937
|
+
"none",
|
|
1938
|
+
"external"
|
|
1939
|
+
];
|
|
1940
|
+
WORKFLOW_JOB_RANGES = Object.freeze({
|
|
1941
|
+
/** Coding-turn cap (`claude -p --max-turns`); absent ⇒ the cluster default (LUA_WF_JOB_MAX_TURNS). */
|
|
1942
|
+
maxTurns: Object.freeze({
|
|
1943
|
+
min: 1,
|
|
1944
|
+
max: 500
|
|
1945
|
+
}),
|
|
1946
|
+
/** Harness messages per attempt (one per content block); absent ⇒ the cluster default (400). */
|
|
1947
|
+
maxMessages: Object.freeze({
|
|
1948
|
+
min: 1,
|
|
1949
|
+
max: 5e3
|
|
1950
|
+
}),
|
|
1951
|
+
/** Input-side tokens per attempt (prompt + cache); absent ⇒ the cluster default (4M — LUA-708, was 30M). */
|
|
1952
|
+
maxInputTokens: Object.freeze({
|
|
1953
|
+
min: 1e6,
|
|
1954
|
+
max: 5e8
|
|
1955
|
+
})
|
|
1956
|
+
});
|
|
1957
|
+
WORKFLOW_JOB_RANGE_MEMBERS = Object.keys(WORKFLOW_JOB_RANGES);
|
|
1958
|
+
__name(isWithinWorkflowJobRange, "isWithinWorkflowJobRange");
|
|
1959
|
+
__name2(isWithinWorkflowJobRange, "isWithinWorkflowJobRange");
|
|
1960
|
+
__name(workflowJobRangeMessage, "workflowJobRangeMessage");
|
|
1961
|
+
__name2(workflowJobRangeMessage, "workflowJobRangeMessage");
|
|
1962
|
+
WORKFLOW_SINGLE_STEP_TYPES = [
|
|
1963
|
+
"agent",
|
|
1964
|
+
"tool",
|
|
1965
|
+
"workflow",
|
|
1966
|
+
"step"
|
|
1967
|
+
];
|
|
1968
|
+
WORKFLOW_HITL_ENTRY_TYPES = [
|
|
1969
|
+
"approval",
|
|
1970
|
+
"waitForSignal"
|
|
1971
|
+
];
|
|
1972
|
+
WORKFLOW_ARM_ENTRY_TYPES = [
|
|
1973
|
+
...WORKFLOW_SINGLE_STEP_TYPES,
|
|
1974
|
+
...WORKFLOW_HITL_ENTRY_TYPES
|
|
1975
|
+
];
|
|
1976
|
+
WORKFLOW_HITL_ARM_CONTAINERS = [
|
|
1977
|
+
"parallel",
|
|
1978
|
+
"conditional",
|
|
1979
|
+
"foreach"
|
|
1980
|
+
];
|
|
1981
|
+
__name(isWorkflowSingleStepType, "isWorkflowSingleStepType");
|
|
1982
|
+
__name2(isWorkflowSingleStepType, "isWorkflowSingleStepType");
|
|
1983
|
+
__name(isWorkflowHitlEntryType, "isWorkflowHitlEntryType");
|
|
1984
|
+
__name2(isWorkflowHitlEntryType, "isWorkflowHitlEntryType");
|
|
1985
|
+
__name(isWorkflowArmEntryType, "isWorkflowArmEntryType");
|
|
1986
|
+
__name2(isWorkflowArmEntryType, "isWorkflowArmEntryType");
|
|
1987
|
+
__name(workflowContainerRunsHitlArm, "workflowContainerRunsHitlArm");
|
|
1988
|
+
__name2(workflowContainerRunsHitlArm, "workflowContainerRunsHitlArm");
|
|
1989
|
+
__name(workflowHitlArmUnsupportedMessage, "workflowHitlArmUnsupportedMessage");
|
|
1990
|
+
__name2(workflowHitlArmUnsupportedMessage, "workflowHitlArmUnsupportedMessage");
|
|
1991
|
+
__name(workflowHitlArmShapeMessage, "workflowHitlArmShapeMessage");
|
|
1992
|
+
__name2(workflowHitlArmShapeMessage, "workflowHitlArmShapeMessage");
|
|
1993
|
+
WORKFLOW_GRAPH_ENTRY_STEP_KINDS = Object.freeze({
|
|
1994
|
+
agent: "agent",
|
|
1995
|
+
tool: "tool",
|
|
1996
|
+
workflow: "subrun",
|
|
1997
|
+
step: "code",
|
|
1998
|
+
mapping: "map",
|
|
1999
|
+
sleep: "sleep",
|
|
2000
|
+
sleepUntil: "sleepUntil",
|
|
2001
|
+
parallel: null,
|
|
2002
|
+
conditional: "branch",
|
|
2003
|
+
foreach: "foreach",
|
|
2004
|
+
loop: "loop",
|
|
2005
|
+
approval: "approval",
|
|
2006
|
+
waitForSignal: "signal"
|
|
2007
|
+
});
|
|
2008
|
+
WORKFLOW_ARM_ENTRY_STEP_KINDS = Object.freeze(Object.fromEntries(WORKFLOW_ARM_ENTRY_TYPES.map((t) => [
|
|
2009
|
+
t,
|
|
2010
|
+
WORKFLOW_GRAPH_ENTRY_STEP_KINDS[t]
|
|
2011
|
+
])));
|
|
2012
|
+
WORKFLOW_BUDGET_MAX_DURATION_SECONDS = Object.freeze({
|
|
2013
|
+
min: 60,
|
|
2014
|
+
max: 2592e3
|
|
2015
|
+
});
|
|
2016
|
+
REDACTED_PLACEHOLDER = "[REDACTED]";
|
|
2017
|
+
PROVIDER_MESSAGE_MAX_CHARS = 300;
|
|
2018
|
+
ERROR_MESSAGE_MAX_CHARS = 2e3;
|
|
2019
|
+
SECRET_LITERAL_PATTERNS = [
|
|
2020
|
+
{
|
|
2021
|
+
re: /\b(github_pat_)[A-Za-z0-9_]{16,}/g
|
|
2022
|
+
},
|
|
2023
|
+
{
|
|
2024
|
+
re: /\b(gh[pousr]_)[A-Za-z0-9]{16,}/g
|
|
2025
|
+
},
|
|
2026
|
+
{
|
|
2027
|
+
re: /\b(glpat-)[A-Za-z0-9_-]{16,}/g
|
|
2028
|
+
},
|
|
2029
|
+
// LUA-696 review (3): the npm granular / classic token (`npm_` + 36 alphanumerics).
|
|
2030
|
+
{
|
|
2031
|
+
re: /\b(npm_)[A-Za-z0-9]{36}\b/g
|
|
2032
|
+
},
|
|
2033
|
+
{
|
|
2034
|
+
re: /\b(sk-ant-)[A-Za-z0-9_-]{16,}/g
|
|
2035
|
+
},
|
|
2036
|
+
{
|
|
2037
|
+
re: /\b(sk-)(?!ant-)[A-Za-z0-9_-]{20,}/g
|
|
2038
|
+
},
|
|
2039
|
+
{
|
|
2040
|
+
re: /\b(AKIA)[A-Z0-9]{16}\b/g
|
|
2041
|
+
},
|
|
2042
|
+
{
|
|
2043
|
+
re: /\b(xox[abprs]-)[A-Za-z0-9-]{10,}/g
|
|
2044
|
+
},
|
|
2045
|
+
{
|
|
2046
|
+
re: /\b(AIza)[0-9A-Za-z_-]{35}/g
|
|
2047
|
+
},
|
|
2048
|
+
{
|
|
2049
|
+
re: /\b(ya29\.)[A-Za-z0-9_-]{20,}/g
|
|
2050
|
+
},
|
|
2051
|
+
{
|
|
2052
|
+
re: /\b(eyJ)[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}/g
|
|
2053
|
+
},
|
|
2054
|
+
{
|
|
2055
|
+
re: /(-----BEGIN [A-Z ]*PRIVATE KEY-----)[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g
|
|
2056
|
+
},
|
|
2057
|
+
// `https://x-access-token:<token>@github.com/…` (the git credential shape the pod scrubbed already)
|
|
2058
|
+
{
|
|
2059
|
+
re: /\b(x-access-token:)[^@\s]+(@)/g,
|
|
2060
|
+
suffix: true
|
|
2061
|
+
}
|
|
2062
|
+
];
|
|
2063
|
+
SECRET_NAME = "(?:authorization|proxy-authorization|x-api-key|x-wf-[a-z-]*key|x-internal-auth|api[_-]?key|apikey|access[_-]?key|secret[_-]?key|private[_-]?key|client[_-]?secret|secret|password|passwd|pwd|passphrase|token|credentials?)";
|
|
2064
|
+
SECRET_PAIR_PATTERNS = [
|
|
2065
|
+
// `Authorization: Bearer x`, `x-api-key: x`, `AWS_SECRET_ACCESS_KEY=x`, `SVC_JOB_KEY=x`, `"apiKey": "x"`,
|
|
2066
|
+
// `FOO_TOKEN=x`, `secretKey=x`, `password=x`. Groups: the char before the name, the name, the separator
|
|
2067
|
+
// (with its quotes), the scheme word — all kept; the value goes. A value a literal rule already replaced
|
|
2068
|
+
// (`x-access-token:[REDACTED]@host`) is left alone so the host after it survives.
|
|
2069
|
+
// LUA-696: the second lookahead keeps a scrubbed `Authorization: Bearer [REDACTED]` as it is — without it the
|
|
2070
|
+
// optional scheme group backtracks to empty and `Bearer` itself becomes the value (`[REDACTED] [REDACTED]`).
|
|
2071
|
+
// The scrub is applied more than once on purpose (the Job site at its copy, the outcome table at the row
|
|
2072
|
+
// write, lua-api on the way out), so it must be idempotent.
|
|
2073
|
+
// LUA-696 review (HIGH): the identifier prefix is BOUNDED (`{1,64}`) — unbounded, `[A-Za-z0-9-]+` made the
|
|
2074
|
+
// scan quadratic on `-`-heavy text (100 KB of `-` took 12.7 s on the lua-core event loop; a worker-tier step
|
|
2075
|
+
// can throw that). No identifier that names a credential is longer; the probe list is unchanged.
|
|
2076
|
+
{
|
|
2077
|
+
re: new RegExp(`(^|[^A-Za-z0-9])((?:[A-Za-z0-9-]{1,64}[_-])?${SECRET_NAME}|[A-Za-z0-9-]{1,64}_key)(["']?\\s*[:=]\\s*["']?)((?:basic\\s+|bearer\\s+|token\\s+)?)(?!\\[REDACTED\\]|(?:basic|bearer|token)\\s+\\[REDACTED\\])[^\\s"',;)}&]{4,}`, "gi")
|
|
2078
|
+
},
|
|
2079
|
+
// `?token=x`, `&key=x`, `&X-Amz-Signature=x`, `&sig=x`
|
|
2080
|
+
{
|
|
2081
|
+
re: /([?&](?:token|key|api[_-]?key|apikey|access[_-]?token|id[_-]?token|auth|sig|signature|secret|password|pwd|x-amz-signature|x-amz-credential|x-amz-security-token)=)[^&\s"'#]+/gi
|
|
2082
|
+
},
|
|
2083
|
+
// `mongodb+srv://user:pass@host`, `postgres://user:pass@host`, `https://user:pass@host`
|
|
2084
|
+
{
|
|
2085
|
+
re: /(\/\/[^\s/:@]+:)[^\s/@]+(@)/g,
|
|
2086
|
+
suffix: true
|
|
2087
|
+
},
|
|
2088
|
+
// `Basic <base64>` / `bearer <short token>` (the literal list needs ≥ 16 chars; any case)
|
|
2089
|
+
{
|
|
2090
|
+
re: /\b((?:Basic|Bearer)\s+)(?!\[REDACTED\])[A-Za-z0-9+/=_.-]{8,}/gi
|
|
2091
|
+
}
|
|
2092
|
+
];
|
|
2093
|
+
GROUP_COUNT = /* @__PURE__ */ new WeakMap();
|
|
2094
|
+
__name(groupCount, "groupCount");
|
|
2095
|
+
__name2(groupCount, "groupCount");
|
|
2096
|
+
WORKFLOW_SECRET_KEY_RE = /(^|[_\-.\s])(secret|token|password|passwd|pwd|passphrase|api[_-]?key|apikey|access[_-]?key|secret[_-]?key|private[_-]?key|client[_-]?secret|authorization|auth[_-]?token|access[_-]?token|id[_-]?token|refresh[_-]?token|session[_-]?key|credential(s)?)([_\-.\s]|$)|^(secret|token|password|passwd|pwd|passphrase|apikey|authorization|credentials?)$/i;
|
|
2097
|
+
WORKFLOW_RESERVED_SECRET_KEYS = Object.freeze([
|
|
2098
|
+
"secret",
|
|
2099
|
+
"token",
|
|
2100
|
+
"password",
|
|
2101
|
+
"passwd",
|
|
2102
|
+
"pwd",
|
|
2103
|
+
"passphrase",
|
|
2104
|
+
"api_key",
|
|
2105
|
+
"apikey",
|
|
2106
|
+
"access_key",
|
|
2107
|
+
"secret_key",
|
|
2108
|
+
"private_key",
|
|
2109
|
+
"client_secret",
|
|
2110
|
+
"authorization",
|
|
2111
|
+
"auth_token",
|
|
2112
|
+
"access_token",
|
|
2113
|
+
"id_token",
|
|
2114
|
+
"refresh_token",
|
|
2115
|
+
"session_key",
|
|
2116
|
+
"credential",
|
|
2117
|
+
"credentials"
|
|
2118
|
+
]);
|
|
2119
|
+
__name(isWorkflowSecretKey, "isWorkflowSecretKey");
|
|
2120
|
+
__name2(isWorkflowSecretKey, "isWorkflowSecretKey");
|
|
2121
|
+
__name(applyPatterns, "applyPatterns");
|
|
2122
|
+
__name2(applyPatterns, "applyPatterns");
|
|
2123
|
+
__name(scrubSecretText, "scrubSecretText");
|
|
2124
|
+
__name2(scrubSecretText, "scrubSecretText");
|
|
2125
|
+
SCRUB_INPUT_MAX_CHARS = 16 * 1024;
|
|
2126
|
+
SCRUB_CUT_BACKOFF_CHARS = 256;
|
|
2127
|
+
__name(boundScrubInput, "boundScrubInput");
|
|
2128
|
+
__name2(boundScrubInput, "boundScrubInput");
|
|
2129
|
+
__name(scrubSecretLines, "scrubSecretLines");
|
|
2130
|
+
__name2(scrubSecretLines, "scrubSecretLines");
|
|
2131
|
+
__name(messageText, "messageText");
|
|
2132
|
+
__name2(messageText, "messageText");
|
|
2133
|
+
__name(scrubProviderMessage, "scrubProviderMessage");
|
|
2134
|
+
__name2(scrubProviderMessage, "scrubProviderMessage");
|
|
2135
|
+
__name(scrubStepErrorMessage, "scrubStepErrorMessage");
|
|
2136
|
+
__name2(scrubStepErrorMessage, "scrubStepErrorMessage");
|
|
1725
2137
|
WORKFLOW_AUDIT_EVENTS = [
|
|
1726
2138
|
// --- definitions / versions / templates (13, 11 §11.11.5) ---
|
|
1727
2139
|
"workflow.published",
|
|
@@ -1843,7 +2255,24 @@ listed here; never invent a target.`;
|
|
|
1843
2255
|
// ../workflow-graph/dist/index.mjs
|
|
1844
2256
|
import { createHash } from "crypto";
|
|
1845
2257
|
import { z as z4 } from "zod";
|
|
2258
|
+
import { z as z22 } from "zod";
|
|
1846
2259
|
import { createHash as createHash2 } from "crypto";
|
|
2260
|
+
function workspaceTemplatePath(template22) {
|
|
2261
|
+
const key = template22.trim();
|
|
2262
|
+
const expr = WORKSPACE_TEMPLATE_EXPR_RE.exec(key);
|
|
2263
|
+
if (expr) return expr[1].split(".");
|
|
2264
|
+
if (key.includes("${")) return void 0;
|
|
2265
|
+
return key.replace(/^(?:input|initData)\./, "").split(".");
|
|
2266
|
+
}
|
|
2267
|
+
function retryBackoffs() {
|
|
2268
|
+
if (!Array.isArray(WORKFLOW_RETRY_BACKOFFS)) {
|
|
2269
|
+
throw new Error("@lua/shared-types.WORKFLOW_RETRY_BACKOFFS is not a tuple \u2014 a jest.mock('@lua/shared-types') must spread jest.requireActual('@lua/shared-types')");
|
|
2270
|
+
}
|
|
2271
|
+
return WORKFLOW_RETRY_BACKOFFS;
|
|
2272
|
+
}
|
|
2273
|
+
function sleepUntilUnsupportedMessage(id) {
|
|
2274
|
+
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} }`;
|
|
2275
|
+
}
|
|
1847
2276
|
function fillPolicy(node, defaultTimeout) {
|
|
1848
2277
|
if (node.tier === void 0 && node.workspace !== void 0 && node.workspace !== "inherit") node.tier = "job";
|
|
1849
2278
|
if (node.timeoutSeconds === void 0) node.timeoutSeconds = node.tier === "job" ? WORKFLOW_JOB_DEFAULT_TIMEOUT_SECONDS : defaultTimeout;
|
|
@@ -1873,8 +2302,28 @@ function fillSingle(node) {
|
|
|
1873
2302
|
return;
|
|
1874
2303
|
}
|
|
1875
2304
|
}
|
|
2305
|
+
function fillHitl(node) {
|
|
2306
|
+
if (node.type === "approval") {
|
|
2307
|
+
const a = node;
|
|
2308
|
+
if (a.approver === void 0) a.approver = "creator";
|
|
2309
|
+
if (a.timeoutHours === void 0) a.timeoutHours = WORKFLOW_SUSPEND_DEFAULT_TIMEOUT_HOURS;
|
|
2310
|
+
if (a.onTimeout === void 0) a.onTimeout = "deny";
|
|
2311
|
+
if (a.onDeny === void 0) a.onDeny = "continue";
|
|
2312
|
+
if (a.excludeInitiator === void 0) a.excludeInitiator = false;
|
|
2313
|
+
if (a.editable === void 0) a.editable = false;
|
|
2314
|
+
return;
|
|
2315
|
+
}
|
|
2316
|
+
const w = node;
|
|
2317
|
+
if (w.timeoutHours === void 0) w.timeoutHours = WORKFLOW_SUSPEND_DEFAULT_TIMEOUT_HOURS;
|
|
2318
|
+
if (w.onTimeout === void 0) w.onTimeout = "fail";
|
|
2319
|
+
if (w.acceptedSources === void 0) w.acceptedSources = [
|
|
2320
|
+
...WORKFLOW_SIGNAL_DEFAULT_SOURCES
|
|
2321
|
+
];
|
|
2322
|
+
}
|
|
1876
2323
|
function fillArm(arm) {
|
|
1877
|
-
if (arm.type
|
|
2324
|
+
if (arm.type === "mapping") return;
|
|
2325
|
+
if (isHitlNode(arm)) fillHitl(arm);
|
|
2326
|
+
else fillSingle(arm);
|
|
1878
2327
|
}
|
|
1879
2328
|
function fillEntry(entry) {
|
|
1880
2329
|
switch (entry.type) {
|
|
@@ -1885,7 +2334,7 @@ function fillEntry(entry) {
|
|
|
1885
2334
|
fillSingle(entry);
|
|
1886
2335
|
return;
|
|
1887
2336
|
case "parallel":
|
|
1888
|
-
entry.steps.forEach(
|
|
2337
|
+
entry.steps.forEach(fillArm);
|
|
1889
2338
|
return;
|
|
1890
2339
|
case "conditional": {
|
|
1891
2340
|
const c = entry;
|
|
@@ -1899,34 +2348,19 @@ function fillEntry(entry) {
|
|
|
1899
2348
|
f.opts = f.opts ?? {};
|
|
1900
2349
|
if (f.opts.concurrency === void 0) f.opts.concurrency = WORKFLOW_FOREACH_DEFAULT_CONCURRENCY;
|
|
1901
2350
|
if (f.opts.maxItems === void 0) f.opts.maxItems = WORKFLOW_FOREACH_DEFAULT_MAX_ITEMS;
|
|
1902
|
-
|
|
2351
|
+
fillArm(f.step);
|
|
1903
2352
|
return;
|
|
1904
2353
|
}
|
|
1905
2354
|
case "loop": {
|
|
1906
2355
|
const l = entry;
|
|
1907
2356
|
if (l.maxIterations === void 0) l.maxIterations = WORKFLOW_LOOP_DEFAULT_MAX_ITERATIONS;
|
|
1908
|
-
|
|
2357
|
+
fillArm(l.step);
|
|
1909
2358
|
return;
|
|
1910
2359
|
}
|
|
1911
|
-
case "approval":
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
if (a.timeoutHours === void 0) a.timeoutHours = WORKFLOW_SUSPEND_DEFAULT_TIMEOUT_HOURS;
|
|
1915
|
-
if (a.onTimeout === void 0) a.onTimeout = "deny";
|
|
1916
|
-
if (a.onDeny === void 0) a.onDeny = "continue";
|
|
1917
|
-
if (a.excludeInitiator === void 0) a.excludeInitiator = false;
|
|
1918
|
-
if (a.editable === void 0) a.editable = false;
|
|
1919
|
-
return;
|
|
1920
|
-
}
|
|
1921
|
-
case "waitForSignal": {
|
|
1922
|
-
const w = entry;
|
|
1923
|
-
if (w.timeoutHours === void 0) w.timeoutHours = WORKFLOW_SUSPEND_DEFAULT_TIMEOUT_HOURS;
|
|
1924
|
-
if (w.onTimeout === void 0) w.onTimeout = "fail";
|
|
1925
|
-
if (w.acceptedSources === void 0) w.acceptedSources = [
|
|
1926
|
-
...WORKFLOW_SIGNAL_DEFAULT_SOURCES
|
|
1927
|
-
];
|
|
2360
|
+
case "approval":
|
|
2361
|
+
case "waitForSignal":
|
|
2362
|
+
fillHitl(entry);
|
|
1928
2363
|
return;
|
|
1929
|
-
}
|
|
1930
2364
|
case "mapping":
|
|
1931
2365
|
case "sleep":
|
|
1932
2366
|
case "sleepUntil":
|
|
@@ -1938,6 +2372,12 @@ function withDefaultsFilled(g) {
|
|
|
1938
2372
|
out.definition.graph.forEach(fillEntry);
|
|
1939
2373
|
return out;
|
|
1940
2374
|
}
|
|
2375
|
+
function isConnectionKeyShaped(value22) {
|
|
2376
|
+
return WORKFLOW_CONNECTION_KEY_RE.test(value22) && !CONNECTION_ID_HEX_RE.test(value22);
|
|
2377
|
+
}
|
|
2378
|
+
function connectionKeyUndeclaredMessage(path3, key) {
|
|
2379
|
+
return `${path3} '${key}' is neither a connection id nor a declared connections[].key \u2014 declare it: connections: [{ key: '${key}', integrationType: '<catalog slug, e.g. github>' }] and it resolves on any agent`;
|
|
2380
|
+
}
|
|
1941
2381
|
function classifyModelProvider(model) {
|
|
1942
2382
|
const m = (model ?? "").trim().toLowerCase();
|
|
1943
2383
|
if (!m) return null;
|
|
@@ -1962,14 +2402,19 @@ function templateStepRefs(text) {
|
|
|
1962
2402
|
for (const m of text.matchAll(TEMPLATE_STEP_REF)) ids.push(m[1]);
|
|
1963
2403
|
return ids;
|
|
1964
2404
|
}
|
|
1965
|
-
function
|
|
1966
|
-
if (!raw) return
|
|
1967
|
-
|
|
2405
|
+
function readMapConfig(raw) {
|
|
2406
|
+
if (!raw) return void 0;
|
|
2407
|
+
if (typeof raw !== "string") return raw;
|
|
1968
2408
|
try {
|
|
1969
|
-
cfg =
|
|
2409
|
+
const cfg = JSON.parse(raw);
|
|
2410
|
+
return cfg && typeof cfg === "object" && !Array.isArray(cfg) ? cfg : void 0;
|
|
1970
2411
|
} catch {
|
|
1971
|
-
return
|
|
2412
|
+
return void 0;
|
|
1972
2413
|
}
|
|
2414
|
+
}
|
|
2415
|
+
function mapConfigStepRefs(raw) {
|
|
2416
|
+
const cfg = readMapConfig(raw);
|
|
2417
|
+
if (!cfg) return [];
|
|
1973
2418
|
const ids = [];
|
|
1974
2419
|
for (const d of Object.values(cfg)) {
|
|
1975
2420
|
if (!d || typeof d !== "object") continue;
|
|
@@ -2040,6 +2485,43 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2040
2485
|
if (envelopeWorkspace?.backend && envelopeWorkspace.backend !== "ebs" && opts.policy?.workspaceBackends && !opts.policy.workspaceBackends.includes(envelopeWorkspace.backend)) {
|
|
2041
2486
|
err("workspace-backend-unavailable", `workspace.backend '${envelopeWorkspace.backend}' is not enabled (LUA_WF_WORKSPACE_BACKENDS = ${opts.policy.workspaceBackends.join(",")}) \u2014 'ebs' is the normative fallback`, "workspace.backend");
|
|
2042
2487
|
}
|
|
2488
|
+
for (const member of [
|
|
2489
|
+
"repo",
|
|
2490
|
+
"ref"
|
|
2491
|
+
]) {
|
|
2492
|
+
const v = envelopeWorkspace?.[member];
|
|
2493
|
+
const tpl = v && typeof v === "object" && typeof v.template === "string" ? v.template : void 0;
|
|
2494
|
+
if (tpl !== void 0 && workspaceTemplatePath(tpl) === void 0) {
|
|
2495
|
+
warn("workspace-template-composite", `workspace.${member} template ${JSON.stringify(tpl)} is a composite \u2014 the engine binds one whole-string \${initData.<path>} (or an input.<path> key) and every start would fail workspace_provision_failed{binding_unresolved}; put the assembled value in the run input and bind that path`, `workspace.${member}`);
|
|
2496
|
+
}
|
|
2497
|
+
}
|
|
2498
|
+
const declaredKeys = new Set(opts.connectionKeys ?? []);
|
|
2499
|
+
if (g.connections !== void 0 && !Array.isArray(g.connections)) {
|
|
2500
|
+
err("connection-declaration-invalid", "`connections` must be an array of { key, integrationType }", "connections");
|
|
2501
|
+
}
|
|
2502
|
+
(Array.isArray(g.connections) ? g.connections : []).forEach((c, i) => {
|
|
2503
|
+
const path3 = `connections.${i}`;
|
|
2504
|
+
const key = c?.key;
|
|
2505
|
+
const integrationType = c?.integrationType;
|
|
2506
|
+
if (typeof key !== "string" || !WORKFLOW_CONNECTION_KEY_RE.test(key)) {
|
|
2507
|
+
err("connection-declaration-invalid", `connections[${i}].key must match ${WORKFLOW_CONNECTION_KEY_RE}`, `${path3}.key`);
|
|
2508
|
+
return;
|
|
2509
|
+
}
|
|
2510
|
+
if (declaredKeys.has(key)) {
|
|
2511
|
+
err("connection-declaration-invalid", `connections[${i}].key "${key}" is declared twice`, `${path3}.key`);
|
|
2512
|
+
return;
|
|
2513
|
+
}
|
|
2514
|
+
if (typeof integrationType !== "string" || !integrationType.trim()) {
|
|
2515
|
+
err("connection-declaration-invalid", `connections[${i}] ("${key}") needs an integrationType (the catalog slug, e.g. 'github')`, `${path3}.integrationType`);
|
|
2516
|
+
return;
|
|
2517
|
+
}
|
|
2518
|
+
declaredKeys.add(key);
|
|
2519
|
+
});
|
|
2520
|
+
const undeclaredKey = /* @__PURE__ */ __name3((ref) => typeof ref === "string" && !declaredKeys.has(ref) && isConnectionKeyShaped(ref) && opts.connectionIds?.has(ref) !== true, "undeclaredKey");
|
|
2521
|
+
const credentialsRef = envelopeWorkspace?.credentialsRef;
|
|
2522
|
+
if (undeclaredKey(credentialsRef)) {
|
|
2523
|
+
err("connection-key-undeclared", connectionKeyUndeclaredMessage("workspace.credentialsRef", credentialsRef), "workspace.credentialsRef");
|
|
2524
|
+
}
|
|
2043
2525
|
const seen = /* @__PURE__ */ new Map();
|
|
2044
2526
|
let nodeCount = 0;
|
|
2045
2527
|
const upstream = /* @__PURE__ */ new Set();
|
|
@@ -2051,12 +2533,27 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2051
2533
|
seen.set(id, path3);
|
|
2052
2534
|
}
|
|
2053
2535
|
}, "checkId");
|
|
2536
|
+
const checkPolicyEnums = /* @__PURE__ */ __name3((node, path3) => {
|
|
2537
|
+
const id = singleId(node);
|
|
2538
|
+
const check = /* @__PURE__ */ __name3((member, allowed) => {
|
|
2539
|
+
const value22 = node[member];
|
|
2540
|
+
if (value22 === void 0 || typeof value22 === "string" && allowed.includes(value22)) return;
|
|
2541
|
+
err("invalid-envelope", `\`${member}\` must be ${allowed.map((a) => `'${a}'`).join(" | ")} (got ${JSON.stringify(value22)})`, `${path3}.${member}`, id);
|
|
2542
|
+
}, "check");
|
|
2543
|
+
check("sideEffects", WORKFLOW_SIDE_EFFECTS);
|
|
2544
|
+
check("jobResources", WORKFLOW_JOB_RESOURCES);
|
|
2545
|
+
}, "checkPolicyEnums");
|
|
2054
2546
|
const checkRetry = /* @__PURE__ */ __name3((node, path3) => {
|
|
2055
2547
|
const r = node.retry;
|
|
2056
2548
|
if (!r) return;
|
|
2057
2549
|
const id = singleId(node);
|
|
2058
|
-
|
|
2059
|
-
|
|
2550
|
+
const backoffs = retryBackoffs();
|
|
2551
|
+
if (r.backoff !== void 0 && !backoffs.includes(r.backoff)) {
|
|
2552
|
+
const list = backoffs.map((b) => `'${b}'`).join(" | ");
|
|
2553
|
+
err("backoff-invalid", `retry.backoff must be ${list}`, `${path3}.retry.backoff`, id);
|
|
2554
|
+
}
|
|
2555
|
+
if (r.backoffSeconds !== void 0 && r.backoffSeconds < 0) {
|
|
2556
|
+
err("backoff-invalid", "retry.backoffSeconds must be \u2265 0", `${path3}.retry.backoffSeconds`, id);
|
|
2060
2557
|
}
|
|
2061
2558
|
if (r.maxBackoffSeconds !== void 0) {
|
|
2062
2559
|
if (r.backoff !== "exponential") {
|
|
@@ -2119,10 +2616,15 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2119
2616
|
}, "checkSpecialistRole");
|
|
2120
2617
|
const checkRequiredConnections = /* @__PURE__ */ __name3((node, path3) => {
|
|
2121
2618
|
const required = node.requiredConnections;
|
|
2122
|
-
if (!Array.isArray(required)
|
|
2123
|
-
const
|
|
2619
|
+
if (!Array.isArray(required)) return;
|
|
2620
|
+
const undeclared = required.filter(undeclaredKey);
|
|
2621
|
+
if (undeclared.length) {
|
|
2622
|
+
err("connection-key-undeclared", connectionKeyUndeclaredMessage(`${path3}.requiredConnections`, undeclared[0]) + (undeclared.length > 1 ? ` (also undeclared: ${JSON.stringify(undeclared.slice(1))})` : ""), `${path3}.requiredConnections`, singleId(node));
|
|
2623
|
+
}
|
|
2624
|
+
if (!opts.connectionIds) return;
|
|
2625
|
+
const unknown = required.filter((c) => typeof c !== "string" || !declaredKeys.has(c) && !opts.connectionIds.has(c));
|
|
2124
2626
|
if (unknown.length) {
|
|
2125
|
-
err("required-connection-unknown", `requiredConnections ${JSON.stringify(unknown)} are
|
|
2627
|
+
err("required-connection-unknown", `requiredConnections ${JSON.stringify(unknown)} are neither declared connections[].key values nor connections the owner can mount`, `${path3}.requiredConnections`, singleId(node));
|
|
2126
2628
|
}
|
|
2127
2629
|
}, "checkRequiredConnections");
|
|
2128
2630
|
const checkTier = /* @__PURE__ */ __name3((node, path3) => {
|
|
@@ -2200,14 +2702,20 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2200
2702
|
err("container-arm-empty", "a bare mapping arm has nothing to run", `${path3}.graph.1`, node.id);
|
|
2201
2703
|
return;
|
|
2202
2704
|
}
|
|
2705
|
+
const inner = body[1];
|
|
2706
|
+
if (isHitlNode(inner)) {
|
|
2707
|
+
err("node-type-unsupported-in-container", workflowHitlArmShapeMessage(inner.type, inner.id, "mapped-arm"), `${path3}.graph.1`, inner.id);
|
|
2708
|
+
return;
|
|
2709
|
+
}
|
|
2203
2710
|
upstream.add(singleId(body[1]));
|
|
2204
|
-
checkArm(body[0], `${path3}.graph.0`, depth);
|
|
2711
|
+
checkArm(body[0], `${path3}.graph.0`, depth, "parallel");
|
|
2205
2712
|
checkSingle(body[1], `${path3}.graph.1`, depth);
|
|
2206
2713
|
upstream.add(body[0].id);
|
|
2207
2714
|
upstream.add(singleId(body[1]));
|
|
2208
2715
|
return;
|
|
2209
2716
|
}
|
|
2210
2717
|
checkId(singleId(node), path3);
|
|
2718
|
+
checkPolicyEnums(node, path3);
|
|
2211
2719
|
checkTimeout(node, path3);
|
|
2212
2720
|
checkTier(node, path3);
|
|
2213
2721
|
checkRetry(node, path3);
|
|
@@ -2232,13 +2740,60 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2232
2740
|
if (node.type === "workflow" && node.kind === "subrun" && depth > caps.maxNestingDepth) {
|
|
2233
2741
|
err("cap-exceeded", `nesting depth ${depth} exceeds ${caps.maxNestingDepth}`, path3, node.id);
|
|
2234
2742
|
}
|
|
2743
|
+
if (node.type === "workflow" && node.workflowId !== WORKFLOW_ARM_SUBRUN_ID && typeof g.definition?.id === "string" && node.workflowId === g.definition.id) {
|
|
2744
|
+
err("subrun-cycle", `"${node.id}" starts "${node.workflowId}", which is this workflow itself`, path3, node.id);
|
|
2745
|
+
}
|
|
2235
2746
|
for (const ref of nodeStepRefs(node)) {
|
|
2236
2747
|
if (!upstream.has(ref)) {
|
|
2237
2748
|
err("template-reference-unresolved", `"${singleId(node)}" references stepResults.${ref}, which is not upstream`, path3, singleId(node));
|
|
2238
2749
|
}
|
|
2239
2750
|
}
|
|
2240
2751
|
}, "checkSingle");
|
|
2241
|
-
const
|
|
2752
|
+
const checkHitl = /* @__PURE__ */ __name3((node, path3) => {
|
|
2753
|
+
if (node.type === "waitForSignal") {
|
|
2754
|
+
const w = node;
|
|
2755
|
+
checkId(w.id, path3);
|
|
2756
|
+
if (typeof w.signal !== "string" || w.signal.length === 0) err("invalid-envelope", "waitForSignal.signal is required", `${path3}.signal`, w.id);
|
|
2757
|
+
return;
|
|
2758
|
+
}
|
|
2759
|
+
const a = node;
|
|
2760
|
+
checkId(a.id, path3);
|
|
2761
|
+
if (a.approver === "creator" && a.excludeInitiator === true) {
|
|
2762
|
+
err("approver-excludes-only-candidate", "approver:'creator' with excludeInitiator:true always excludes the only candidate", path3, a.id);
|
|
2763
|
+
}
|
|
2764
|
+
if (a.fourEyes !== void 0 && a.editable !== true) {
|
|
2765
|
+
err("four-eyes-requires-editable", "`fourEyes` requires editable:true", `${path3}.fourEyes`, a.id);
|
|
2766
|
+
}
|
|
2767
|
+
if ((a.editablePaths !== void 0 || a.editedPayloadSchema !== void 0) && a.editable !== true) {
|
|
2768
|
+
err("editable-path-invalid", "`editablePaths` / `editedPayloadSchema` require editable:true", `${path3}.editablePaths`, a.id);
|
|
2769
|
+
}
|
|
2770
|
+
for (const p of a.editablePaths ?? []) {
|
|
2771
|
+
if (!EDITABLE_PATH_RE.test(p)) err("editable-path-invalid", `editablePaths entry "${p}" is outside the seg(.seg)*[*]/[n] grammar`, `${path3}.editablePaths`, a.id);
|
|
2772
|
+
}
|
|
2773
|
+
if (Array.isArray(a.onTimeout)) {
|
|
2774
|
+
const chain = a.onTimeout;
|
|
2775
|
+
const hops = chain.filter((h) => typeof h === "object" && h !== null && "escalateTo" in h);
|
|
2776
|
+
if (hops.length > 3) err("escalation-chain-too-long", "an onTimeout chain carries at most 3 escalation hops", `${path3}.onTimeout`, a.id);
|
|
2777
|
+
const last = chain[chain.length - 1];
|
|
2778
|
+
if (last === void 0 || typeof last === "object" && last !== null && "escalateTo" in last) {
|
|
2779
|
+
err("escalation-chain-not-terminal", "an onTimeout chain must end in a terminal member", `${path3}.onTimeout`, a.id);
|
|
2780
|
+
}
|
|
2781
|
+
}
|
|
2782
|
+
if (typeof a.details === "string") {
|
|
2783
|
+
for (const ref of templateStepRefs(a.details)) {
|
|
2784
|
+
if (!upstream.has(ref)) err("template-reference-unresolved", `"${a.id}" references stepResults.${ref}, which is not upstream`, path3, a.id);
|
|
2785
|
+
}
|
|
2786
|
+
}
|
|
2787
|
+
}, "checkHitl");
|
|
2788
|
+
const checkHitlArm = /* @__PURE__ */ __name3((node, path3, container) => {
|
|
2789
|
+
if (!workflowContainerRunsHitlArm(container)) {
|
|
2790
|
+
checkId(node.id, path3);
|
|
2791
|
+
err("node-type-unsupported-in-container", workflowHitlArmUnsupportedMessage(node.type, node.id, container), path3, node.id);
|
|
2792
|
+
return;
|
|
2793
|
+
}
|
|
2794
|
+
checkHitl(node, path3);
|
|
2795
|
+
}, "checkHitlArm");
|
|
2796
|
+
const checkArm = /* @__PURE__ */ __name3((arm, path3, depth, container) => {
|
|
2242
2797
|
if (arm.type === "mapping") {
|
|
2243
2798
|
checkId(arm.id, path3);
|
|
2244
2799
|
for (const ref of nodeStepRefs(arm)) {
|
|
@@ -2246,6 +2801,10 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2246
2801
|
}
|
|
2247
2802
|
return;
|
|
2248
2803
|
}
|
|
2804
|
+
if (isHitlNode(arm)) {
|
|
2805
|
+
checkHitlArm(arm, path3, container);
|
|
2806
|
+
return;
|
|
2807
|
+
}
|
|
2249
2808
|
checkSingle(arm, path3, depth);
|
|
2250
2809
|
}, "checkArm");
|
|
2251
2810
|
graph.forEach((entry, i) => {
|
|
@@ -2274,6 +2833,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2274
2833
|
case "sleepUntil": {
|
|
2275
2834
|
const s = entry;
|
|
2276
2835
|
checkId(s.id, path3);
|
|
2836
|
+
err("node-type-unsupported-by-engine", sleepUntilUnsupportedMessage(s.id), path3, s.id);
|
|
2277
2837
|
if (s.date === void 0 === (s.dateFrom === void 0)) {
|
|
2278
2838
|
err("invalid-envelope", "sleepUntil needs exactly one of `date` | `dateFrom`", path3, s.id);
|
|
2279
2839
|
}
|
|
@@ -2294,18 +2854,15 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2294
2854
|
err("mapping-placement", "a bare mapping cannot be a parallel arm \u2014 chain it as [map, step]", armPath);
|
|
2295
2855
|
return;
|
|
2296
2856
|
}
|
|
2297
|
-
|
|
2298
|
-
err("approval-inside-container", "approval / waitForSignal are top-level only in v1", armPath);
|
|
2299
|
-
return;
|
|
2300
|
-
}
|
|
2301
|
-
checkSingle(arm, armPath, 1);
|
|
2857
|
+
checkArm(arm, armPath, 1, "parallel");
|
|
2302
2858
|
declared.push(singleId(arm));
|
|
2303
2859
|
});
|
|
2304
|
-
const
|
|
2860
|
+
const executableArms = p.steps.filter(isSingleStep);
|
|
2861
|
+
const worktreeArms = executableArms.filter((a) => a.workspace?.isolation === "worktree");
|
|
2305
2862
|
if (worktreeArms.length > 0 && !p.merge) err("worktree-arms-require-merge", "worktree arms need a `merge` policy", path3);
|
|
2306
2863
|
if (worktreeArms.length === 0 && p.merge) err("merge-requires-worktree-arms", "`merge` needs at least one worktree arm", path3);
|
|
2307
2864
|
if (worktreeArms.length > WORKFLOW_JOB_MAX_WORKTREE_ARMS) err("worktree-arms-exceed-cap", `at most ${WORKFLOW_JOB_MAX_WORKTREE_ARMS} worktree arms per parallel`, path3);
|
|
2308
|
-
const sharedArms =
|
|
2865
|
+
const sharedArms = executableArms.filter((a) => {
|
|
2309
2866
|
const w = workspaceOf(a);
|
|
2310
2867
|
if (!w || w === "inherit" || w.isolation === "worktree") return false;
|
|
2311
2868
|
return !(envelopeWorkspace?.backend === "efs" && w.mount === "ro");
|
|
@@ -2319,14 +2876,14 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2319
2876
|
err("invalid-envelope", "conditional.predicates must match conditional.steps one-to-one", path3);
|
|
2320
2877
|
}
|
|
2321
2878
|
(c.predicates ?? []).forEach((p, j) => {
|
|
2322
|
-
if (!
|
|
2879
|
+
if (!isWellFormedPredicate(p)) err("closure-predicate", "a conditional predicate must be a well-formed LuaPredicate object ({op, left/right | value | path | args | arg}), not a function or an expression string", `${path3}.predicates.${j}`);
|
|
2323
2880
|
});
|
|
2324
2881
|
c.steps.forEach((arm, j) => {
|
|
2325
|
-
checkArm(arm, `${path3}.steps.${j}`, 1);
|
|
2882
|
+
checkArm(arm, `${path3}.steps.${j}`, 1, "conditional");
|
|
2326
2883
|
declared.push(armId(arm));
|
|
2327
2884
|
});
|
|
2328
2885
|
if (c.otherwise) {
|
|
2329
|
-
checkArm(c.otherwise, `${path3}.otherwise`, 1);
|
|
2886
|
+
checkArm(c.otherwise, `${path3}.otherwise`, 1, "conditional");
|
|
2330
2887
|
declared.push(armId(c.otherwise));
|
|
2331
2888
|
}
|
|
2332
2889
|
break;
|
|
@@ -2349,8 +2906,9 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2349
2906
|
}
|
|
2350
2907
|
if (o.chunk !== void 0) {
|
|
2351
2908
|
const max = o.maxItems ?? WORKFLOW_FOREACH_DEFAULT_MAX_ITEMS;
|
|
2352
|
-
|
|
2353
|
-
|
|
2909
|
+
const size = typeof o.chunk === "number" ? o.chunk : o.chunk.size;
|
|
2910
|
+
if (!Number.isInteger(size) || size < 1 || size > max) {
|
|
2911
|
+
err("chunk-size-invalid", `foreach.chunk.size must be an integer in [1, ${max}]`, typeof o.chunk === "number" ? `${path3}.opts.chunk` : `${path3}.opts.chunk.size`);
|
|
2354
2912
|
}
|
|
2355
2913
|
}
|
|
2356
2914
|
if (o.rateLimit !== void 0) {
|
|
@@ -2367,12 +2925,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2367
2925
|
const bodyType = f.step.type;
|
|
2368
2926
|
const prevEntry = i > 0 ? graph[i - 1] : void 0;
|
|
2369
2927
|
if (bodyType !== "mapping" && prevEntry?.type === "mapping" && prevEntry.id === `${singleId(f.step)}_items`) {
|
|
2370
|
-
|
|
2371
|
-
try {
|
|
2372
|
-
cfg = JSON.parse(prevEntry.mapConfig);
|
|
2373
|
-
} catch {
|
|
2374
|
-
cfg = void 0;
|
|
2375
|
-
}
|
|
2928
|
+
const cfg = readMapConfig(prevEntry.mapConfig);
|
|
2376
2929
|
const d = cfg?.[""];
|
|
2377
2930
|
let arrayLike;
|
|
2378
2931
|
if (d && "value" in d) arrayLike = Array.isArray(d.value);
|
|
@@ -2384,8 +2937,13 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2384
2937
|
if (arrayLike === false) err("foreach-items-not-array", `foreach({items}) for "${singleId(f.step)}" binds a value that is not an array`, `${path3}.step`, singleId(f.step));
|
|
2385
2938
|
}
|
|
2386
2939
|
if (bodyType === "mapping") err("container-arm-empty", "a foreach body needs a step, not a bare mapping", `${path3}.step`);
|
|
2387
|
-
else if (
|
|
2388
|
-
|
|
2940
|
+
else if (isHitlNode(f.step)) {
|
|
2941
|
+
if (o.chunk !== void 0) {
|
|
2942
|
+
checkId(f.step.id, `${path3}.step`);
|
|
2943
|
+
err("node-type-unsupported-in-container", workflowHitlArmShapeMessage(f.step.type, f.step.id, "chunked-foreach"), `${path3}.step`, f.step.id);
|
|
2944
|
+
} else checkHitlArm(f.step, `${path3}.step`, "foreach");
|
|
2945
|
+
declared.push(f.step.id);
|
|
2946
|
+
} else {
|
|
2389
2947
|
checkSingle(f.step, `${path3}.step`, o.chunk ? 2 : 1);
|
|
2390
2948
|
declared.push(singleId(f.step));
|
|
2391
2949
|
}
|
|
@@ -2399,55 +2957,23 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2399
2957
|
if (l.intervalSeconds !== void 0 && (!Number.isInteger(l.intervalSeconds) || l.intervalSeconds < 1 || l.intervalSeconds > caps.maxLoopIntervalSeconds)) {
|
|
2400
2958
|
err("loop-interval-out-of-range", `loop.intervalSeconds must be an integer in 1..${caps.maxLoopIntervalSeconds}`, `${path3}.intervalSeconds`);
|
|
2401
2959
|
}
|
|
2402
|
-
if (!
|
|
2960
|
+
if (!isWellFormedPredicate(l.predicate)) err("closure-predicate", "a loop predicate must be a well-formed LuaPredicate object ({op, left/right | value | path | args | arg}), not a function or an expression string", `${path3}.predicate`);
|
|
2403
2961
|
const bodyType = l.step.type;
|
|
2404
2962
|
if (bodyType === "mapping") err("container-arm-empty", "a loop body needs a step, not a bare mapping", `${path3}.step`);
|
|
2405
|
-
else if (
|
|
2406
|
-
|
|
2963
|
+
else if (isHitlNode(l.step)) {
|
|
2964
|
+
checkHitlArm(l.step, `${path3}.step`, "loop");
|
|
2965
|
+
declared.push(l.step.id);
|
|
2966
|
+
} else {
|
|
2407
2967
|
checkSingle(l.step, `${path3}.step`, 1);
|
|
2408
2968
|
declared.push(singleId(l.step));
|
|
2409
2969
|
}
|
|
2410
2970
|
break;
|
|
2411
2971
|
}
|
|
2412
|
-
case "approval":
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
err("approver-excludes-only-candidate", "approver:'creator' with excludeInitiator:true always excludes the only candidate", path3, a.id);
|
|
2417
|
-
}
|
|
2418
|
-
if (a.fourEyes !== void 0 && a.editable !== true) {
|
|
2419
|
-
err("four-eyes-requires-editable", "`fourEyes` requires editable:true", `${path3}.fourEyes`, a.id);
|
|
2420
|
-
}
|
|
2421
|
-
if ((a.editablePaths !== void 0 || a.editedPayloadSchema !== void 0) && a.editable !== true) {
|
|
2422
|
-
err("editable-path-invalid", "`editablePaths` / `editedPayloadSchema` require editable:true", `${path3}.editablePaths`, a.id);
|
|
2423
|
-
}
|
|
2424
|
-
for (const p of a.editablePaths ?? []) {
|
|
2425
|
-
if (!EDITABLE_PATH_RE.test(p)) err("editable-path-invalid", `editablePaths entry "${p}" is outside the seg(.seg)*[*]/[n] grammar`, `${path3}.editablePaths`, a.id);
|
|
2426
|
-
}
|
|
2427
|
-
if (Array.isArray(a.onTimeout)) {
|
|
2428
|
-
const chain = a.onTimeout;
|
|
2429
|
-
const hops = chain.filter((h) => typeof h === "object" && h !== null && "escalateTo" in h);
|
|
2430
|
-
if (hops.length > 3) err("escalation-chain-too-long", "an onTimeout chain carries at most 3 escalation hops", `${path3}.onTimeout`, a.id);
|
|
2431
|
-
const last = chain[chain.length - 1];
|
|
2432
|
-
if (last === void 0 || typeof last === "object" && last !== null && "escalateTo" in last) {
|
|
2433
|
-
err("escalation-chain-not-terminal", "an onTimeout chain must end in a terminal member", `${path3}.onTimeout`, a.id);
|
|
2434
|
-
}
|
|
2435
|
-
}
|
|
2436
|
-
if (typeof a.details === "string") {
|
|
2437
|
-
for (const ref of templateStepRefs(a.details)) {
|
|
2438
|
-
if (!upstream.has(ref)) err("template-reference-unresolved", `"${a.id}" references stepResults.${ref}, which is not upstream`, path3, a.id);
|
|
2439
|
-
}
|
|
2440
|
-
}
|
|
2441
|
-
declared.push(a.id);
|
|
2442
|
-
break;
|
|
2443
|
-
}
|
|
2444
|
-
case "waitForSignal": {
|
|
2445
|
-
const w = entry;
|
|
2446
|
-
checkId(w.id, path3);
|
|
2447
|
-
if (typeof w.signal !== "string" || w.signal.length === 0) err("invalid-envelope", "waitForSignal.signal is required", `${path3}.signal`, w.id);
|
|
2448
|
-
declared.push(w.id);
|
|
2972
|
+
case "approval":
|
|
2973
|
+
case "waitForSignal":
|
|
2974
|
+
checkHitl(entry, path3);
|
|
2975
|
+
declared.push(entry.id);
|
|
2449
2976
|
break;
|
|
2450
|
-
}
|
|
2451
2977
|
default:
|
|
2452
2978
|
err("invalid-envelope", `unknown entry type "${entry.type}"`, path3);
|
|
2453
2979
|
}
|
|
@@ -2462,6 +2988,41 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2462
2988
|
function isPredicate(p) {
|
|
2463
2989
|
return typeof p === "object" && p !== null && typeof p.op === "string" && PREDICATE_OPS.has(p.op);
|
|
2464
2990
|
}
|
|
2991
|
+
function isPathOrLiteral(v) {
|
|
2992
|
+
if (typeof v !== "object" || v === null) return false;
|
|
2993
|
+
const r = v;
|
|
2994
|
+
if ("path" in r) return typeof r.path === "string" && r.path.length > 0;
|
|
2995
|
+
return "literal" in r && isPredicateScalar(r.literal);
|
|
2996
|
+
}
|
|
2997
|
+
function isWellFormedPredicate(p) {
|
|
2998
|
+
if (!isPredicate(p)) return false;
|
|
2999
|
+
const r = p;
|
|
3000
|
+
switch (r.op) {
|
|
3001
|
+
case "eq":
|
|
3002
|
+
case "ne":
|
|
3003
|
+
case "lt":
|
|
3004
|
+
case "lte":
|
|
3005
|
+
case "gt":
|
|
3006
|
+
case "gte":
|
|
3007
|
+
return isPathOrLiteral(r.left) && isPathOrLiteral(r.right);
|
|
3008
|
+
case "in":
|
|
3009
|
+
case "notIn":
|
|
3010
|
+
return isPathOrLiteral(r.value) && Array.isArray(r.set) && r.set.every(isPredicateScalar);
|
|
3011
|
+
case "exists":
|
|
3012
|
+
case "notExists":
|
|
3013
|
+
return typeof r.path === "string" && r.path.length > 0;
|
|
3014
|
+
case "truthy":
|
|
3015
|
+
case "falsy":
|
|
3016
|
+
return isPathOrLiteral(r.value);
|
|
3017
|
+
case "and":
|
|
3018
|
+
case "or":
|
|
3019
|
+
return Array.isArray(r.args) && r.args.every(isWellFormedPredicate);
|
|
3020
|
+
case "not":
|
|
3021
|
+
return isWellFormedPredicate(r.arg);
|
|
3022
|
+
default:
|
|
3023
|
+
return false;
|
|
3024
|
+
}
|
|
3025
|
+
}
|
|
2465
3026
|
function canonicalJson(value22) {
|
|
2466
3027
|
const seen = /* @__PURE__ */ new WeakSet();
|
|
2467
3028
|
const encode = /* @__PURE__ */ __name3((v) => {
|
|
@@ -2501,10 +3062,10 @@ function compilePlan(g) {
|
|
|
2501
3062
|
let prevTails = [];
|
|
2502
3063
|
const foreachJoinToEntry = /* @__PURE__ */ new Map();
|
|
2503
3064
|
g.definition.graph.forEach((entry, entryIndex) => {
|
|
2504
|
-
if (
|
|
2505
|
-
const id =
|
|
3065
|
+
if (isArmStep(entry)) {
|
|
3066
|
+
const id = armStepId(entry);
|
|
2506
3067
|
addNode(id, {
|
|
2507
|
-
kind:
|
|
3068
|
+
kind: armStepKind(entry),
|
|
2508
3069
|
dependsOn: prevTails,
|
|
2509
3070
|
downstream: [],
|
|
2510
3071
|
unsatisfiedDeps: prevTails.length,
|
|
@@ -2529,9 +3090,8 @@ function compilePlan(g) {
|
|
|
2529
3090
|
];
|
|
2530
3091
|
return;
|
|
2531
3092
|
case "sleep":
|
|
2532
|
-
case "sleepUntil":
|
|
2533
|
-
|
|
2534
|
-
const kind = entry.type === "approval" ? "approval" : entry.type;
|
|
3093
|
+
case "sleepUntil": {
|
|
3094
|
+
const kind = entry.type;
|
|
2535
3095
|
addNode(entry.id, {
|
|
2536
3096
|
kind,
|
|
2537
3097
|
dependsOn: prevTails,
|
|
@@ -2544,24 +3104,12 @@ function compilePlan(g) {
|
|
|
2544
3104
|
];
|
|
2545
3105
|
return;
|
|
2546
3106
|
}
|
|
2547
|
-
case "waitForSignal":
|
|
2548
|
-
addNode(entry.id, {
|
|
2549
|
-
kind: "signal",
|
|
2550
|
-
dependsOn: prevTails,
|
|
2551
|
-
downstream: [],
|
|
2552
|
-
unsatisfiedDeps: prevTails.length,
|
|
2553
|
-
entry
|
|
2554
|
-
});
|
|
2555
|
-
prevTails = [
|
|
2556
|
-
entry.id
|
|
2557
|
-
];
|
|
2558
|
-
return;
|
|
2559
3107
|
case "parallel": {
|
|
2560
3108
|
const entryId = containerIdOf("parallel", entryIndex);
|
|
2561
3109
|
const childIds = entry.steps.map((arm) => {
|
|
2562
|
-
const childId =
|
|
3110
|
+
const childId = armStepId(arm);
|
|
2563
3111
|
addNode(childId, {
|
|
2564
|
-
kind:
|
|
3112
|
+
kind: armStepKind(arm),
|
|
2565
3113
|
dependsOn: prevTails,
|
|
2566
3114
|
downstream: [],
|
|
2567
3115
|
unsatisfiedDeps: prevTails.length,
|
|
@@ -2599,9 +3147,9 @@ function compilePlan(g) {
|
|
|
2599
3147
|
node.otherwise
|
|
2600
3148
|
] : node.steps;
|
|
2601
3149
|
const childIds = arms.map((arm) => {
|
|
2602
|
-
const childId = arm.type === "mapping" ? arm.id :
|
|
3150
|
+
const childId = arm.type === "mapping" ? arm.id : armStepId(arm);
|
|
2603
3151
|
addNode(childId, {
|
|
2604
|
-
kind: arm.type === "mapping" ? "map" :
|
|
3152
|
+
kind: arm.type === "mapping" ? "map" : armStepKind(arm),
|
|
2605
3153
|
dependsOn: [
|
|
2606
3154
|
entryId
|
|
2607
3155
|
],
|
|
@@ -2870,13 +3418,25 @@ function toPathOrLiteral(v) {
|
|
|
2870
3418
|
literal: v
|
|
2871
3419
|
};
|
|
2872
3420
|
}
|
|
3421
|
+
function isMapConfigObject(v) {
|
|
3422
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
3423
|
+
}
|
|
2873
3424
|
function parseMapConfig(raw, stepId) {
|
|
3425
|
+
if (isMapConfigObject(raw)) return raw;
|
|
3426
|
+
if (typeof raw !== "string") {
|
|
3427
|
+
throw new Error(`Stored mapping step "${stepId}" has a mapConfig that is neither a JSON string nor an object.`);
|
|
3428
|
+
}
|
|
2874
3429
|
try {
|
|
2875
3430
|
return JSON.parse(raw);
|
|
2876
3431
|
} catch (e) {
|
|
2877
3432
|
throw new Error(`Stored mapping step "${stepId}" has invalid JSON mapConfig: ${e.message}`);
|
|
2878
3433
|
}
|
|
2879
3434
|
}
|
|
3435
|
+
function mapConfigWire(raw) {
|
|
3436
|
+
if (typeof raw === "string") return raw;
|
|
3437
|
+
if (isMapConfigObject(raw)) return canonicalJson(raw);
|
|
3438
|
+
return void 0;
|
|
3439
|
+
}
|
|
2880
3440
|
function describeBadPlaceholder(template22, idx, rawExpr) {
|
|
2881
3441
|
return `Template placeholder #${idx} (\${${rawExpr}}) in '${template22}'`;
|
|
2882
3442
|
}
|
|
@@ -3024,6 +3584,25 @@ function resolveMapping(cfg, ctx) {
|
|
|
3024
3584
|
value: result
|
|
3025
3585
|
};
|
|
3026
3586
|
}
|
|
3587
|
+
function continuedFailureValue(error, killReason) {
|
|
3588
|
+
const code = typeof error?.code === "string" && error.code || typeof killReason === "string" && killReason || CONTINUED_FAILURE_DEFAULT_CODE;
|
|
3589
|
+
const message = typeof error?.message === "string" && error.message ? error.message : code;
|
|
3590
|
+
return {
|
|
3591
|
+
__lua_workflow: CONTINUED_FAILURE_TAG,
|
|
3592
|
+
failed: true,
|
|
3593
|
+
error: {
|
|
3594
|
+
code,
|
|
3595
|
+
message
|
|
3596
|
+
},
|
|
3597
|
+
text: ""
|
|
3598
|
+
};
|
|
3599
|
+
}
|
|
3600
|
+
function isContinuedFailureValue(v) {
|
|
3601
|
+
if (v === null || typeof v !== "object" || Array.isArray(v)) return false;
|
|
3602
|
+
const o = v;
|
|
3603
|
+
const err = o.error;
|
|
3604
|
+
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";
|
|
3605
|
+
}
|
|
3027
3606
|
function lowerContainerArm(mapping, step22) {
|
|
3028
3607
|
const stepId = nodeIdOf(step22);
|
|
3029
3608
|
return {
|
|
@@ -3104,7 +3683,28 @@ function resolvePlacements(calls) {
|
|
|
3104
3683
|
break;
|
|
3105
3684
|
}
|
|
3106
3685
|
});
|
|
3107
|
-
const
|
|
3686
|
+
const hitlPlacementIssue = /* @__PURE__ */ __name3((node, ref, i, container) => {
|
|
3687
|
+
if (!isHitlNode2(node)) return void 0;
|
|
3688
|
+
const id = node.id;
|
|
3689
|
+
if (ref.armMap) {
|
|
3690
|
+
return {
|
|
3691
|
+
code: "node-type-unsupported-in-container",
|
|
3692
|
+
message: workflowHitlArmShapeMessage(node.type, id, "mapped-arm"),
|
|
3693
|
+
callIndex: i,
|
|
3694
|
+
stepId: id
|
|
3695
|
+
};
|
|
3696
|
+
}
|
|
3697
|
+
if (container !== "place" && !workflowContainerRunsHitlArm(container)) {
|
|
3698
|
+
return {
|
|
3699
|
+
code: "node-type-unsupported-in-container",
|
|
3700
|
+
message: workflowHitlArmUnsupportedMessage(node.type, id, container),
|
|
3701
|
+
callIndex: i,
|
|
3702
|
+
stepId: id
|
|
3703
|
+
};
|
|
3704
|
+
}
|
|
3705
|
+
return void 0;
|
|
3706
|
+
}, "hitlPlacementIssue");
|
|
3707
|
+
const resolve3 = /* @__PURE__ */ __name3((ref, i, allowMapping, container) => {
|
|
3108
3708
|
if ("node" in ref) {
|
|
3109
3709
|
if (ref.node.type === "mapping" && !allowMapping) {
|
|
3110
3710
|
issues.push({
|
|
@@ -3121,7 +3721,7 @@ function resolvePlacements(calls) {
|
|
|
3121
3721
|
if (!d) {
|
|
3122
3722
|
issues.push({
|
|
3123
3723
|
code: "unknown-step-ref",
|
|
3124
|
-
message: `"${ref.ref}" is not declared anywhere in the chain \u2014 declare it with agentStep/specialistStep/toolStep/map(\u2026, { id })`,
|
|
3724
|
+
message: `"${ref.ref}" is not declared anywhere in the chain \u2014 declare it with agentStep/specialistStep/toolStep/map(\u2026, { id })/workflow(\u2026)/approval(\u2026)/waitForSignal(\u2026)`,
|
|
3125
3725
|
callIndex: i,
|
|
3126
3726
|
stepId: ref.ref
|
|
3127
3727
|
});
|
|
@@ -3136,6 +3736,11 @@ function resolvePlacements(calls) {
|
|
|
3136
3736
|
});
|
|
3137
3737
|
return void 0;
|
|
3138
3738
|
}
|
|
3739
|
+
const hitl = hitlPlacementIssue(d.node, ref, i, container);
|
|
3740
|
+
if (hitl) {
|
|
3741
|
+
issues.push(hitl);
|
|
3742
|
+
return void 0;
|
|
3743
|
+
}
|
|
3139
3744
|
const prior = placedBy.get(ref.ref);
|
|
3140
3745
|
if (prior !== void 0 && prior !== i) {
|
|
3141
3746
|
issues.push({
|
|
@@ -3149,23 +3754,31 @@ function resolvePlacements(calls) {
|
|
|
3149
3754
|
placedBy.set(ref.ref, i);
|
|
3150
3755
|
return d.node;
|
|
3151
3756
|
}, "resolve");
|
|
3757
|
+
const claim = /* @__PURE__ */ __name3((ref, i, allowMapping, container) => {
|
|
3758
|
+
if ("ref" in ref) {
|
|
3759
|
+
resolve3(ref, i, allowMapping, container);
|
|
3760
|
+
return;
|
|
3761
|
+
}
|
|
3762
|
+
const hitl = hitlPlacementIssue(ref.node, ref, i, container);
|
|
3763
|
+
if (hitl) issues.push(hitl);
|
|
3764
|
+
}, "claim");
|
|
3152
3765
|
calls.forEach((call, i) => {
|
|
3153
3766
|
switch (call.kind) {
|
|
3154
3767
|
case "place":
|
|
3155
|
-
|
|
3768
|
+
claim({
|
|
3156
3769
|
ref: call.ref
|
|
3157
|
-
}, i, false);
|
|
3770
|
+
}, i, false, "place");
|
|
3158
3771
|
break;
|
|
3159
3772
|
case "parallel":
|
|
3160
|
-
for (const arm of call.arms)
|
|
3773
|
+
for (const arm of call.arms) claim(arm, i, false, "parallel");
|
|
3161
3774
|
break;
|
|
3162
3775
|
case "conditional":
|
|
3163
|
-
for (const a of call.arms)
|
|
3164
|
-
if (call.otherwise
|
|
3776
|
+
for (const a of call.arms) claim(a.target, i, true, "conditional");
|
|
3777
|
+
if (call.otherwise) claim(call.otherwise, i, true, "conditional");
|
|
3165
3778
|
break;
|
|
3166
3779
|
case "foreach":
|
|
3167
3780
|
case "loop":
|
|
3168
|
-
|
|
3781
|
+
claim(call.body, i, false, call.kind);
|
|
3169
3782
|
break;
|
|
3170
3783
|
default:
|
|
3171
3784
|
break;
|
|
@@ -3173,9 +3786,9 @@ function resolvePlacements(calls) {
|
|
|
3173
3786
|
});
|
|
3174
3787
|
const graph = [];
|
|
3175
3788
|
const lookup = /* @__PURE__ */ __name3((ref) => {
|
|
3176
|
-
const
|
|
3177
|
-
if (!
|
|
3178
|
-
return lowerContainerArm(ref.armMap,
|
|
3789
|
+
const n2 = "node" in ref ? ref.node : declared.get(ref.ref)?.node;
|
|
3790
|
+
if (!n2 || !ref.armMap || n2.type === "mapping" || isHitlNode2(n2)) return n2;
|
|
3791
|
+
return lowerContainerArm(ref.armMap, n2);
|
|
3179
3792
|
}, "lookup");
|
|
3180
3793
|
calls.forEach((call, i) => {
|
|
3181
3794
|
switch (call.kind) {
|
|
@@ -3194,7 +3807,7 @@ function resolvePlacements(calls) {
|
|
|
3194
3807
|
return;
|
|
3195
3808
|
}
|
|
3196
3809
|
case "parallel": {
|
|
3197
|
-
const steps = call.arms.map(lookup).filter((
|
|
3810
|
+
const steps = call.arms.map(lookup).filter((n2) => !!n2 && n2.type !== "mapping");
|
|
3198
3811
|
const node = {
|
|
3199
3812
|
type: "parallel",
|
|
3200
3813
|
steps
|
|
@@ -3207,9 +3820,9 @@ function resolvePlacements(calls) {
|
|
|
3207
3820
|
const steps = [];
|
|
3208
3821
|
const predicates = [];
|
|
3209
3822
|
for (const a of call.arms) {
|
|
3210
|
-
const
|
|
3211
|
-
if (!
|
|
3212
|
-
steps.push(
|
|
3823
|
+
const n2 = lookup(a.target);
|
|
3824
|
+
if (!n2) continue;
|
|
3825
|
+
steps.push(n2);
|
|
3213
3826
|
predicates.push(a.predicate);
|
|
3214
3827
|
}
|
|
3215
3828
|
const node = {
|
|
@@ -3254,6 +3867,57 @@ function resolvePlacements(calls) {
|
|
|
3254
3867
|
issues
|
|
3255
3868
|
};
|
|
3256
3869
|
}
|
|
3870
|
+
function isConditionalJoinId(stepId) {
|
|
3871
|
+
return CONDITIONAL_JOIN_ID.test(stepId);
|
|
3872
|
+
}
|
|
3873
|
+
function isPlainObject(v) {
|
|
3874
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
3875
|
+
}
|
|
3876
|
+
function leafValue(row) {
|
|
3877
|
+
if (row.status === "completed") return row.output === void 0 ? null : row.output;
|
|
3878
|
+
return continuedFailureValue(row.error, row.killReason);
|
|
3879
|
+
}
|
|
3880
|
+
function runOutputLeaves(steps) {
|
|
3881
|
+
const dependedOn = /* @__PURE__ */ new Set();
|
|
3882
|
+
for (const s of steps) {
|
|
3883
|
+
if (s.stepId === GOAL_JUDGE_STEP_ID) continue;
|
|
3884
|
+
for (const d of s.dependsOn ?? []) dependedOn.add(d);
|
|
3885
|
+
}
|
|
3886
|
+
return steps.filter((s) => s.stepId !== GOAL_JUDGE_STEP_ID && !NON_LEAF_KINDS.has(s.kind ?? "") && s.foreachIndex === void 0 && s.loopParentId === void 0 && s.status !== "skipped" && !dependedOn.has(s.stepId));
|
|
3887
|
+
}
|
|
3888
|
+
function deriveRunOutput(steps) {
|
|
3889
|
+
const leaves = runOutputLeaves(steps);
|
|
3890
|
+
if (leaves.length === 0) return void 0;
|
|
3891
|
+
if (leaves.length === 1) {
|
|
3892
|
+
const leaf = leaves[0];
|
|
3893
|
+
const value22 = leafValue(leaf);
|
|
3894
|
+
if (isConditionalJoinId(leaf.stepId) && isPlainObject(value22)) {
|
|
3895
|
+
const keys = Object.keys(value22);
|
|
3896
|
+
if (keys.length === 1) return {
|
|
3897
|
+
output: value22[keys[0]],
|
|
3898
|
+
leafIds: [
|
|
3899
|
+
keys[0]
|
|
3900
|
+
]
|
|
3901
|
+
};
|
|
3902
|
+
}
|
|
3903
|
+
return {
|
|
3904
|
+
output: value22,
|
|
3905
|
+
leafIds: [
|
|
3906
|
+
leaf.stepId
|
|
3907
|
+
]
|
|
3908
|
+
};
|
|
3909
|
+
}
|
|
3910
|
+
const output = {};
|
|
3911
|
+
for (const leaf of leaves) output[leaf.stepId] = leafValue(leaf);
|
|
3912
|
+
return {
|
|
3913
|
+
output,
|
|
3914
|
+
leafIds: leaves.map((l) => l.stepId)
|
|
3915
|
+
};
|
|
3916
|
+
}
|
|
3917
|
+
function subrunSettledOutput(child) {
|
|
3918
|
+
if (child.output !== void 0) return child.output;
|
|
3919
|
+
return deriveRunOutput(child.steps ?? [])?.output ?? null;
|
|
3920
|
+
}
|
|
3257
3921
|
function seedLedgerFromRun(run, steps, targetPlan, opts = {}) {
|
|
3258
3922
|
const byId = /* @__PURE__ */ new Map();
|
|
3259
3923
|
for (const s of steps) {
|
|
@@ -3276,15 +3940,15 @@ function seedLedgerFromRun(run, steps, targetPlan, opts = {}) {
|
|
|
3276
3940
|
parent
|
|
3277
3941
|
] : [];
|
|
3278
3942
|
}, "dependsOf");
|
|
3279
|
-
const
|
|
3943
|
+
const walk22 = [
|
|
3280
3944
|
...targetPlan.order
|
|
3281
3945
|
];
|
|
3282
3946
|
for (const s of steps) {
|
|
3283
|
-
if (!known.has(s.stepId) && parentOf(s.stepId) && known.has(parentOf(s.stepId)) && !
|
|
3284
|
-
|
|
3947
|
+
if (!known.has(s.stepId) && parentOf(s.stepId) && known.has(parentOf(s.stepId)) && !walk22.includes(s.stepId)) {
|
|
3948
|
+
walk22.push(s.stepId);
|
|
3285
3949
|
}
|
|
3286
3950
|
}
|
|
3287
|
-
for (const id of
|
|
3951
|
+
for (const id of walk22) {
|
|
3288
3952
|
const row = byId.get(id);
|
|
3289
3953
|
if (!row || row.status !== "completed") continue;
|
|
3290
3954
|
if (!dependsOf(id).every((d) => seededIds.has(d))) {
|
|
@@ -3313,31 +3977,54 @@ function seedLedgerFromRun(run, steps, targetPlan, opts = {}) {
|
|
|
3313
3977
|
graphChanged
|
|
3314
3978
|
};
|
|
3315
3979
|
}
|
|
3980
|
+
function branchSpecFromConditional(entry) {
|
|
3981
|
+
return {
|
|
3982
|
+
arms: entry.steps.map((arm, i) => ({
|
|
3983
|
+
stepId: branchArmId(arm),
|
|
3984
|
+
predicate: entry.predicates[i]
|
|
3985
|
+
})),
|
|
3986
|
+
...entry.exclusive === true ? {
|
|
3987
|
+
exclusive: true
|
|
3988
|
+
} : {},
|
|
3989
|
+
...entry.otherwise ? {
|
|
3990
|
+
otherwise: branchArmId(entry.otherwise)
|
|
3991
|
+
} : {}
|
|
3992
|
+
};
|
|
3993
|
+
}
|
|
3994
|
+
function selectBranchArms(spec, ctx) {
|
|
3995
|
+
const taken = [];
|
|
3996
|
+
for (const arm of spec.arms) {
|
|
3997
|
+
if (spec.exclusive && taken.length > 0) break;
|
|
3998
|
+
const hit = arm.predicate ? evaluatePredicate(arm.predicate, ctx) : false;
|
|
3999
|
+
if (hit) taken.push(arm.stepId);
|
|
4000
|
+
}
|
|
4001
|
+
if (taken.length === 0 && spec.otherwise) taken.push(spec.otherwise);
|
|
4002
|
+
return taken;
|
|
4003
|
+
}
|
|
3316
4004
|
function replayLedger(g, ledger) {
|
|
3317
4005
|
const plan = compilePlan(g);
|
|
3318
4006
|
const rows22 = new Map(ledger.steps.map((r) => [
|
|
3319
4007
|
r.stepId,
|
|
3320
4008
|
r
|
|
3321
4009
|
]));
|
|
3322
|
-
const
|
|
3323
|
-
|
|
3324
|
-
|
|
4010
|
+
const requestContext = {
|
|
4011
|
+
runId: "replay",
|
|
4012
|
+
workflowId: g.definition.id,
|
|
4013
|
+
orgId: "",
|
|
4014
|
+
agentId: "",
|
|
4015
|
+
userId: "",
|
|
4016
|
+
trigger: "sdk",
|
|
4017
|
+
threadId: "replay",
|
|
4018
|
+
depth: 0,
|
|
4019
|
+
startedAt: 0,
|
|
4020
|
+
...ledger.requestContext
|
|
4021
|
+
};
|
|
4022
|
+
const ctxFor = /* @__PURE__ */ __name3((id) => ({
|
|
3325
4023
|
initData: ledger.initData,
|
|
3326
|
-
stepResults,
|
|
4024
|
+
stepResults: ancestorResults(plan, id, rows22),
|
|
3327
4025
|
state: ledger.state ?? {},
|
|
3328
|
-
requestContext
|
|
3329
|
-
|
|
3330
|
-
workflowId: g.definition.id,
|
|
3331
|
-
orgId: "",
|
|
3332
|
-
agentId: "",
|
|
3333
|
-
userId: "",
|
|
3334
|
-
trigger: "sdk",
|
|
3335
|
-
threadId: "replay",
|
|
3336
|
-
depth: 0,
|
|
3337
|
-
startedAt: 0,
|
|
3338
|
-
...ledger.requestContext
|
|
3339
|
-
}
|
|
3340
|
-
};
|
|
4026
|
+
requestContext
|
|
4027
|
+
}), "ctxFor");
|
|
3341
4028
|
const verdicts = [];
|
|
3342
4029
|
for (const id of plan.order) {
|
|
3343
4030
|
const node = plan.steps[id];
|
|
@@ -3345,17 +4032,7 @@ function replayLedger(g, ledger) {
|
|
|
3345
4032
|
if (!recorded || recorded.status === "pending" || recorded.status === "skipped") continue;
|
|
3346
4033
|
if (node.kind === "branch") {
|
|
3347
4034
|
const entry = node.entry;
|
|
3348
|
-
const taken =
|
|
3349
|
-
entry.steps.forEach((arm, i) => {
|
|
3350
|
-
const pred = entry.predicates[i];
|
|
3351
|
-
const hit = pred ? evaluatePredicate(pred, {
|
|
3352
|
-
initData: ledger.initData,
|
|
3353
|
-
stepResults,
|
|
3354
|
-
state: ledger.state
|
|
3355
|
-
}) : false;
|
|
3356
|
-
if (hit && (!entry.exclusive || taken.length === 0)) taken.push(armId2(arm));
|
|
3357
|
-
});
|
|
3358
|
-
if (taken.length === 0 && entry.otherwise) taken.push(armId2(entry.otherwise));
|
|
4035
|
+
const taken = selectBranchArms(branchSpecFromConditional(entry), ctxFor(id));
|
|
3359
4036
|
const recordedTaken = recorded.taken ?? inferTaken(entry, rows22);
|
|
3360
4037
|
verdicts.push({
|
|
3361
4038
|
stepId: id,
|
|
@@ -3366,7 +4043,7 @@ function replayLedger(g, ledger) {
|
|
|
3366
4043
|
});
|
|
3367
4044
|
} else if (node.kind === "map" && !id.endsWith(".join") && recorded.status === "completed") {
|
|
3368
4045
|
const entry = node.entry;
|
|
3369
|
-
const resolved = resolveMapping(parseMapConfig(entry.mapConfig, id),
|
|
4046
|
+
const resolved = resolveMapping(parseMapConfig(entry.mapConfig, id), ctxFor(id));
|
|
3370
4047
|
const local = "error" in resolved ? {
|
|
3371
4048
|
error: resolved.error,
|
|
3372
4049
|
key: resolved.key
|
|
@@ -3381,7 +4058,7 @@ function replayLedger(g, ledger) {
|
|
|
3381
4058
|
} else if (node.kind === "foreach") {
|
|
3382
4059
|
const entry = node.entry;
|
|
3383
4060
|
const source = node.dependsOn[0];
|
|
3384
|
-
const items = source ? stepResults[source] : void 0;
|
|
4061
|
+
const items = source ? ctxFor(id).stepResults[source] : void 0;
|
|
3385
4062
|
const local = Array.isArray(items) ? items.length : void 0;
|
|
3386
4063
|
const recordedCount = recorded.itemCount ?? countChildren(entry, rows22);
|
|
3387
4064
|
verdicts.push({
|
|
@@ -3391,6 +4068,15 @@ function replayLedger(g, ledger) {
|
|
|
3391
4068
|
local,
|
|
3392
4069
|
diverged: recordedCount !== local
|
|
3393
4070
|
});
|
|
4071
|
+
} else if (node.kind === "subrun" && recorded.status === "completed" && recorded.child) {
|
|
4072
|
+
const local = subrunSettledOutput(recorded.child);
|
|
4073
|
+
verdicts.push({
|
|
4074
|
+
stepId: id,
|
|
4075
|
+
kind: "subrun",
|
|
4076
|
+
recorded: recorded.output,
|
|
4077
|
+
local,
|
|
4078
|
+
diverged: canonical(recorded.output) !== canonical(local)
|
|
4079
|
+
});
|
|
3394
4080
|
}
|
|
3395
4081
|
}
|
|
3396
4082
|
return {
|
|
@@ -3398,6 +4084,71 @@ function replayLedger(g, ledger) {
|
|
|
3398
4084
|
diverged: verdicts.some((v) => v.diverged)
|
|
3399
4085
|
};
|
|
3400
4086
|
}
|
|
4087
|
+
function replayResultOf(row, node) {
|
|
4088
|
+
if (!row) return void 0;
|
|
4089
|
+
if (row.status === "completed") return {
|
|
4090
|
+
value: row.output
|
|
4091
|
+
};
|
|
4092
|
+
if (row.status === "failed") {
|
|
4093
|
+
const onError = row.onError ?? node?.entry?.onError;
|
|
4094
|
+
if (onError === "continue") return {
|
|
4095
|
+
value: continuedFailureValue(row.error, row.killReason)
|
|
4096
|
+
};
|
|
4097
|
+
}
|
|
4098
|
+
return void 0;
|
|
4099
|
+
}
|
|
4100
|
+
function ancestorResults(plan, id, rows22) {
|
|
4101
|
+
const out = {};
|
|
4102
|
+
const joinAliased = /* @__PURE__ */ new Set();
|
|
4103
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4104
|
+
const take = /* @__PURE__ */ __name3((rowId) => {
|
|
4105
|
+
const hit = replayResultOf(rows22.get(rowId), plan.steps[rowId]);
|
|
4106
|
+
if (!hit) return void 0;
|
|
4107
|
+
if (!joinAliased.has(rowId)) out[rowId] = hit.value;
|
|
4108
|
+
const entryId = entryOfJoin(rowId);
|
|
4109
|
+
if (entryId) {
|
|
4110
|
+
out[entryId] = hit.value;
|
|
4111
|
+
joinAliased.add(entryId);
|
|
4112
|
+
const entryNode = plan.steps[entryId];
|
|
4113
|
+
if (entryNode?.kind === "foreach") {
|
|
4114
|
+
const body = branchArmId(entryNode.entry.step);
|
|
4115
|
+
out[body] = hit.value;
|
|
4116
|
+
joinAliased.add(body);
|
|
4117
|
+
}
|
|
4118
|
+
}
|
|
4119
|
+
return hit;
|
|
4120
|
+
}, "take");
|
|
4121
|
+
const walk22 = /* @__PURE__ */ __name3((ids) => {
|
|
4122
|
+
for (const dep of ids) {
|
|
4123
|
+
if (seen.has(dep)) continue;
|
|
4124
|
+
seen.add(dep);
|
|
4125
|
+
take(dep);
|
|
4126
|
+
const node = plan.steps[dep] ?? plan.steps[entryOfJoin(dep) ?? ""];
|
|
4127
|
+
if (!node) continue;
|
|
4128
|
+
if (node.kind === "foreach") {
|
|
4129
|
+
const body = branchArmId(node.entry.step);
|
|
4130
|
+
for (const rowId of rows22.keys()) if (rowId.startsWith(`${body}[`)) take(rowId);
|
|
4131
|
+
} else if (node.kind === "loop") {
|
|
4132
|
+
const body = branchArmId(node.entry.step);
|
|
4133
|
+
let best = -1;
|
|
4134
|
+
for (const rowId of rows22.keys()) {
|
|
4135
|
+
if (!rowId.startsWith(`${body}#`)) continue;
|
|
4136
|
+
const n2 = Number(rowId.slice(body.length + 1));
|
|
4137
|
+
if (!Number.isInteger(n2)) continue;
|
|
4138
|
+
const hit = take(rowId);
|
|
4139
|
+
if (!hit) continue;
|
|
4140
|
+
if (n2 > best) {
|
|
4141
|
+
best = n2;
|
|
4142
|
+
out[body] = hit.value;
|
|
4143
|
+
}
|
|
4144
|
+
}
|
|
4145
|
+
}
|
|
4146
|
+
walk22(node.dependsOn);
|
|
4147
|
+
}
|
|
4148
|
+
}, "walk");
|
|
4149
|
+
walk22(plan.steps[id]?.dependsOn ?? []);
|
|
4150
|
+
return out;
|
|
4151
|
+
}
|
|
3401
4152
|
function inferTaken(entry, rows22) {
|
|
3402
4153
|
const arms = [
|
|
3403
4154
|
...entry.steps,
|
|
@@ -3405,16 +4156,16 @@ function inferTaken(entry, rows22) {
|
|
|
3405
4156
|
entry.otherwise
|
|
3406
4157
|
] : []
|
|
3407
4158
|
];
|
|
3408
|
-
return arms.map(
|
|
4159
|
+
return arms.map(branchArmId).filter((id) => {
|
|
3409
4160
|
const r = rows22.get(id);
|
|
3410
4161
|
return r !== void 0 && r.status !== "skipped" && r.status !== "pending";
|
|
3411
4162
|
});
|
|
3412
4163
|
}
|
|
3413
4164
|
function countChildren(entry, rows22) {
|
|
3414
|
-
const body =
|
|
3415
|
-
let
|
|
3416
|
-
for (const id of rows22.keys()) if (id.startsWith(`${body}[`))
|
|
3417
|
-
return
|
|
4165
|
+
const body = branchArmId(entry.step);
|
|
4166
|
+
let n2 = 0;
|
|
4167
|
+
for (const id of rows22.keys()) if (id.startsWith(`${body}[`)) n2 += 1;
|
|
4168
|
+
return n2;
|
|
3418
4169
|
}
|
|
3419
4170
|
function isTerminalRunStatus(status) {
|
|
3420
4171
|
return TERMINAL.has(status);
|
|
@@ -3423,19 +4174,48 @@ function pruneUndefined(o) {
|
|
|
3423
4174
|
return Object.fromEntries(Object.entries(o).filter(([, v]) => v !== void 0));
|
|
3424
4175
|
}
|
|
3425
4176
|
function runNextAction(run) {
|
|
3426
|
-
if (isTerminalRunStatus(run.status)
|
|
4177
|
+
if (isTerminalRunStatus(run.status)) return "none";
|
|
4178
|
+
if (run.status === "suspended" && run.gate?.kind === "budget") return "raise_budget";
|
|
4179
|
+
if (!run.cancel?.requestedAt) return "none";
|
|
3427
4180
|
const forceAt = run.cancel.forceAfter ?? run.cancel.requestedAt + FORCE_CANCEL_STALE_MS;
|
|
3428
4181
|
return Date.now() >= forceAt ? "force" : "cancel_again";
|
|
3429
4182
|
}
|
|
4183
|
+
function emptyRunCounts() {
|
|
4184
|
+
const out = {
|
|
4185
|
+
total: 0,
|
|
4186
|
+
inFlight: 0
|
|
4187
|
+
};
|
|
4188
|
+
for (const status of WORKFLOW_STEP_STATUSES) out[status] = 0;
|
|
4189
|
+
return out;
|
|
4190
|
+
}
|
|
4191
|
+
function runCountsFromStatusTally(tally) {
|
|
4192
|
+
const out = emptyRunCounts();
|
|
4193
|
+
for (const [status, n2] of Object.entries(tally)) {
|
|
4194
|
+
if (!Number.isFinite(n2) || n2 <= 0) continue;
|
|
4195
|
+
out.total += n2;
|
|
4196
|
+
if (WORKFLOW_STEP_STATUSES.includes(status)) out[status] += n2;
|
|
4197
|
+
if (IN_FLIGHT.has(status)) out.inFlight += n2;
|
|
4198
|
+
}
|
|
4199
|
+
return out;
|
|
4200
|
+
}
|
|
4201
|
+
function runCountsFromStepStatuses(statuses) {
|
|
4202
|
+
const tally = {};
|
|
4203
|
+
for (const s of statuses) tally[s] = (tally[s] ?? 0) + 1;
|
|
4204
|
+
return runCountsFromStatusTally(tally);
|
|
4205
|
+
}
|
|
3430
4206
|
function runCounts(counts) {
|
|
4207
|
+
const c = counts ?? {};
|
|
4208
|
+
const rawInFlight = c.dispatched !== void 0 || c.claimed !== void 0 || c.running !== void 0 || c.cancellation_requested !== void 0;
|
|
3431
4209
|
return {
|
|
3432
|
-
total:
|
|
3433
|
-
completed:
|
|
3434
|
-
failed:
|
|
3435
|
-
skipped:
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
4210
|
+
total: n(c.total),
|
|
4211
|
+
completed: n(c.completed),
|
|
4212
|
+
failed: n(c.failed) + n(c.timeout) + n(c.reaped),
|
|
4213
|
+
skipped: n(c.skipped),
|
|
4214
|
+
// LUA-664: cancelled rows never ran — their own wire bucket, never a failure (the detail's map, verbatim).
|
|
4215
|
+
cancelled: n(c.cancelled),
|
|
4216
|
+
running: rawInFlight ? n(c.dispatched) + n(c.claimed) + n(c.running) + n(c.cancellation_requested) : n(c.inFlight),
|
|
4217
|
+
suspended: n(c.suspended),
|
|
4218
|
+
pending: n(c.pending) + n(c.ready) + n(c.waiting)
|
|
3439
4219
|
};
|
|
3440
4220
|
}
|
|
3441
4221
|
function runUsage(run) {
|
|
@@ -3452,14 +4232,30 @@ function runCancelView(cancel) {
|
|
|
3452
4232
|
return {
|
|
3453
4233
|
requestedAt: cancel.requestedAt,
|
|
3454
4234
|
requestedBy: cancel.requestedBy ?? "",
|
|
3455
|
-
forceAvailableAt: cancel.forceAfter ?? cancel.requestedAt + FORCE_CANCEL_STALE_MS
|
|
4235
|
+
forceAvailableAt: cancel.forceAfter ?? cancel.requestedAt + FORCE_CANCEL_STALE_MS,
|
|
4236
|
+
// LUA-686: the wall's own request (LUA-683 stamps `wall:true` from a system actor only) rides the wire, so a
|
|
4237
|
+
// client can tell a wall-ended `timed_out` run's audit block from a cancel it should chip. Absent, never false.
|
|
4238
|
+
...cancel.wall === true ? {
|
|
4239
|
+
wall: true
|
|
4240
|
+
} : {},
|
|
4241
|
+
// LUA-704: a forced terminal's audit — who forced it, when, and a human's own reason text (the run's `reason`
|
|
4242
|
+
// is the terminal's). Absent on an unforced terminal, never null.
|
|
4243
|
+
...typeof cancel.forcedAt === "number" ? {
|
|
4244
|
+
forcedAt: cancel.forcedAt
|
|
4245
|
+
} : {},
|
|
4246
|
+
...typeof cancel.forcedBy === "string" && cancel.forcedBy ? {
|
|
4247
|
+
forcedBy: cancel.forcedBy
|
|
4248
|
+
} : {},
|
|
4249
|
+
...typeof cancel.forceReason === "string" && cancel.forceReason ? {
|
|
4250
|
+
forceReason: cancel.forceReason
|
|
4251
|
+
} : {}
|
|
3456
4252
|
};
|
|
3457
4253
|
}
|
|
3458
4254
|
function runWorkspaceView(ws) {
|
|
3459
4255
|
if (!ws) return void 0;
|
|
3460
4256
|
const w = ws;
|
|
3461
4257
|
return pruneUndefined({
|
|
3462
|
-
kind: w.kind ?? "empty",
|
|
4258
|
+
kind: w.spec?.kind ?? w.kind ?? "empty",
|
|
3463
4259
|
backend: w.backend,
|
|
3464
4260
|
status: String(w.status ?? ""),
|
|
3465
4261
|
branch: w.branch,
|
|
@@ -3471,7 +4267,6 @@ function runWorkspaceView(ws) {
|
|
|
3471
4267
|
function toWorkflowRunSummary(run) {
|
|
3472
4268
|
const status = run.status;
|
|
3473
4269
|
const principal = run.principal?.principal;
|
|
3474
|
-
const gated = status === "gated" || status === "suspended";
|
|
3475
4270
|
return pruneUndefined({
|
|
3476
4271
|
runId: run.id,
|
|
3477
4272
|
workflowId: run.workflowId,
|
|
@@ -3483,7 +4278,14 @@ function toWorkflowRunSummary(run) {
|
|
|
3483
4278
|
orgId: run.orgId,
|
|
3484
4279
|
spaceAgentId: run.spaceAgentId,
|
|
3485
4280
|
status,
|
|
3486
|
-
|
|
4281
|
+
// LUA-702 (pass-3B D6): the gate rides the wire whenever a NON-terminal doc carries one. In the LUA-681 window
|
|
4282
|
+
// (an approval resolved beside an open exception park) the run is `running` AND still holds `gate{exception}`
|
|
4283
|
+
// until the tick tail parks it back; projecting it only on gated|suspended hid that park from R4 / the CLI /
|
|
4284
|
+
// the desktop (prod 2026-09-05: `status: running, gate: null`). A terminal doc carries no gate by contract
|
|
4285
|
+
// (`terminalizeRun` drops `gate` / `suspendedFor` in every terminal write), but a row terminalized before that
|
|
4286
|
+
// held — pre-LUA-677/694 docs, a script-tier backstop that passed no `unset` — may still carry a stale one:
|
|
4287
|
+
// hidden here, so a `failed` run never reads "Needs a decision" on R3/R4 or the CLI (#2476 review).
|
|
4288
|
+
gate: run.gate && !isTerminalRunStatus(status) ? run.gate : void 0,
|
|
3487
4289
|
batchId: run.batchId,
|
|
3488
4290
|
goalId: run.goalId,
|
|
3489
4291
|
foreachOverflow: run.foreachOverflow,
|
|
@@ -3506,9 +4308,11 @@ function toWorkflowRunSummary(run) {
|
|
|
3506
4308
|
principalKind: run.principalKind ?? "user",
|
|
3507
4309
|
cancel: runCancelView(run.cancel),
|
|
3508
4310
|
usage: runUsage(run),
|
|
4311
|
+
// LUA-697: a row persisted before the write seams (#2406 / #2465 / the script tier) leaves scrubbed here too —
|
|
4312
|
+
// idempotent on a scrubbed message, bounded input; an empty message falls back to the code.
|
|
3509
4313
|
error: run.error ? {
|
|
3510
4314
|
code: run.error.code ?? "error",
|
|
3511
|
-
message: run.error.message ?? "",
|
|
4315
|
+
message: scrubStepErrorMessage(run.error.message) ?? run.error.code ?? "error",
|
|
3512
4316
|
stepId: run.error.stepId
|
|
3513
4317
|
} : void 0,
|
|
3514
4318
|
kind: "run",
|
|
@@ -3521,9 +4325,55 @@ function toWorkflowRunSummary(run) {
|
|
|
3521
4325
|
eventSeq: run.eventSeq ?? 0,
|
|
3522
4326
|
cancellable: !isTerminalRunStatus(status),
|
|
3523
4327
|
nextAction: runNextAction(run),
|
|
3524
|
-
workspace: runWorkspaceView(run.workspace)
|
|
4328
|
+
workspace: runWorkspaceView(run.workspace),
|
|
4329
|
+
// LUA-643: the runs list says a result exists; R4 `fields:'full'` (output-ACL gated, audited) serves it. The
|
|
4330
|
+
// stamped flag is what a list page carries (it projects `output` out); the payload members cover R4's full row.
|
|
4331
|
+
hasOutput: run.hasOutput === true || run.output !== void 0 || run.outputRef !== void 0 ? true : void 0
|
|
3525
4332
|
});
|
|
3526
4333
|
}
|
|
4334
|
+
function scrubDetailValue(value22, depth) {
|
|
4335
|
+
if (typeof value22 === "string") return scrubSecretText(value22);
|
|
4336
|
+
if (typeof value22 === "number" || typeof value22 === "boolean" || value22 === null) return value22;
|
|
4337
|
+
if (depth >= DETAIL_MAX_DEPTH) return void 0;
|
|
4338
|
+
if (Array.isArray(value22)) {
|
|
4339
|
+
return value22.slice(0, DETAIL_MAX_ITEMS).map((v) => scrubDetailValue(v, depth + 1)).filter((v) => v !== void 0);
|
|
4340
|
+
}
|
|
4341
|
+
if (typeof value22 === "object") {
|
|
4342
|
+
const out = {};
|
|
4343
|
+
for (const [k, v] of Object.entries(value22)) {
|
|
4344
|
+
const s = scrubDetailValue(v, depth + 1);
|
|
4345
|
+
if (s !== void 0) out[k] = s;
|
|
4346
|
+
}
|
|
4347
|
+
return out;
|
|
4348
|
+
}
|
|
4349
|
+
return void 0;
|
|
4350
|
+
}
|
|
4351
|
+
function stepErrorDetail(error) {
|
|
4352
|
+
if (!error || typeof error !== "object") return void 0;
|
|
4353
|
+
const d = error.detail;
|
|
4354
|
+
if (!d || typeof d !== "object" || Array.isArray(d)) return void 0;
|
|
4355
|
+
const bag = d;
|
|
4356
|
+
const out = {};
|
|
4357
|
+
for (const k of STEP_ERROR_DETAIL_KEYS) {
|
|
4358
|
+
if (!(k in bag)) continue;
|
|
4359
|
+
const v = scrubDetailValue(bag[k], 1);
|
|
4360
|
+
if (v !== void 0) out[k] = v;
|
|
4361
|
+
}
|
|
4362
|
+
if (!Object.keys(out).length) return void 0;
|
|
4363
|
+
let bytes;
|
|
4364
|
+
try {
|
|
4365
|
+
bytes = new TextEncoder().encode(JSON.stringify(out)).length;
|
|
4366
|
+
} catch {
|
|
4367
|
+
return void 0;
|
|
4368
|
+
}
|
|
4369
|
+
if (bytes <= STEP_ERROR_DETAIL_MAX_BYTES) return out;
|
|
4370
|
+
const scalars = Object.fromEntries(Object.entries(out).filter(([, v]) => v === null || typeof v !== "object"));
|
|
4371
|
+
return {
|
|
4372
|
+
...scalars,
|
|
4373
|
+
__truncated: true,
|
|
4374
|
+
bytes
|
|
4375
|
+
};
|
|
4376
|
+
}
|
|
3527
4377
|
function timeZoneSupported(tz) {
|
|
3528
4378
|
if (typeof tz !== "string" || !tz) return false;
|
|
3529
4379
|
const intl = Intl;
|
|
@@ -3987,6 +4837,22 @@ function rebaseItemPointer(pointer, itemsPath, index) {
|
|
|
3987
4837
|
const base = `/${itemsPath.split(".").map(escapePointer).join("/")}/${index}`;
|
|
3988
4838
|
return pointer === "/" || pointer === "" ? base : `${base}${pointer}`;
|
|
3989
4839
|
}
|
|
4840
|
+
function describeApproverSpecRefusal(spec) {
|
|
4841
|
+
const raw = spec === void 0 ? "undefined" : JSON.stringify(spec) ?? String(spec);
|
|
4842
|
+
const written = raw.length > APPROVER_WRITTEN_MAX ? `${raw.slice(0, APPROVER_WRITTEN_MAX - 1)}\u2026` : raw;
|
|
4843
|
+
const users = typeof spec === "object" && spec !== null ? spec.users : void 0;
|
|
4844
|
+
const approver = typeof users === "string" && USER_ID_SHAPED_RE.test(users) ? {
|
|
4845
|
+
users: [
|
|
4846
|
+
users
|
|
4847
|
+
]
|
|
4848
|
+
} : "creator";
|
|
4849
|
+
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)}`);
|
|
4850
|
+
return {
|
|
4851
|
+
approver,
|
|
4852
|
+
written,
|
|
4853
|
+
message
|
|
4854
|
+
};
|
|
4855
|
+
}
|
|
3990
4856
|
function bindingRootsOk(template22) {
|
|
3991
4857
|
const refs = [
|
|
3992
4858
|
...template22.matchAll(/\$\{\s*([A-Za-z_$][\w$]*)/g)
|
|
@@ -4011,7 +4877,19 @@ function validateApproverBlock(node, opts = {
|
|
|
4011
4877
|
if (!r.success) {
|
|
4012
4878
|
const users = spec?.users;
|
|
4013
4879
|
if (Array.isArray(users) && users.length > APPROVER_SPEC_MAX_USERS) push("cap-exceeded", path3, `at most ${APPROVER_SPEC_MAX_USERS} users`);
|
|
4014
|
-
else
|
|
4880
|
+
else {
|
|
4881
|
+
const refusal = describeApproverSpecRefusal(spec);
|
|
4882
|
+
issues.push({
|
|
4883
|
+
code: "approver-invalid",
|
|
4884
|
+
path: path3,
|
|
4885
|
+
severity: "error",
|
|
4886
|
+
message: refusal.message,
|
|
4887
|
+
repair: {
|
|
4888
|
+
approver: refusal.approver,
|
|
4889
|
+
written: refusal.written
|
|
4890
|
+
}
|
|
4891
|
+
});
|
|
4892
|
+
}
|
|
4015
4893
|
return;
|
|
4016
4894
|
}
|
|
4017
4895
|
const s = r.data;
|
|
@@ -4079,7 +4957,7 @@ function liftRenderedApprover(row, rendered) {
|
|
|
4079
4957
|
}
|
|
4080
4958
|
function collectEnvTemplateKeys(value22) {
|
|
4081
4959
|
const keys = /* @__PURE__ */ new Set();
|
|
4082
|
-
const
|
|
4960
|
+
const walk22 = /* @__PURE__ */ __name3((v) => {
|
|
4083
4961
|
if (isEnvRef(v)) {
|
|
4084
4962
|
keys.add(v.__envRef);
|
|
4085
4963
|
return;
|
|
@@ -4087,26 +4965,26 @@ function collectEnvTemplateKeys(value22) {
|
|
|
4087
4965
|
if (typeof v === "string") {
|
|
4088
4966
|
if (looksLikeEmbeddedJson(v)) {
|
|
4089
4967
|
try {
|
|
4090
|
-
|
|
4968
|
+
walk22(JSON.parse(v));
|
|
4091
4969
|
} catch {
|
|
4092
4970
|
}
|
|
4093
4971
|
}
|
|
4094
4972
|
return;
|
|
4095
4973
|
}
|
|
4096
4974
|
if (Array.isArray(v)) {
|
|
4097
|
-
for (const e of v)
|
|
4975
|
+
for (const e of v) walk22(e);
|
|
4098
4976
|
return;
|
|
4099
4977
|
}
|
|
4100
|
-
if (v && typeof v === "object") for (const e of Object.values(v))
|
|
4978
|
+
if (v && typeof v === "object") for (const e of Object.values(v)) walk22(e);
|
|
4101
4979
|
}, "walk");
|
|
4102
|
-
|
|
4980
|
+
walk22(value22);
|
|
4103
4981
|
return [
|
|
4104
4982
|
...keys
|
|
4105
4983
|
].sort();
|
|
4106
4984
|
}
|
|
4107
4985
|
function substituteEnvRefs(value22, overlay) {
|
|
4108
4986
|
const missing = /* @__PURE__ */ new Set();
|
|
4109
|
-
const
|
|
4987
|
+
const walk22 = /* @__PURE__ */ __name3((v, slot = false) => {
|
|
4110
4988
|
if (isEnvRef(v)) {
|
|
4111
4989
|
if (Object.prototype.hasOwnProperty.call(overlay, v.__envRef)) {
|
|
4112
4990
|
const s = overlay[v.__envRef];
|
|
@@ -4123,22 +5001,22 @@ function substituteEnvRefs(value22, overlay) {
|
|
|
4123
5001
|
const cfg = JSON.parse(v);
|
|
4124
5002
|
if (!cfg || typeof cfg !== "object" || Array.isArray(cfg)) return v;
|
|
4125
5003
|
const out = {};
|
|
4126
|
-
for (const [k, e] of Object.entries(cfg)) out[k] =
|
|
5004
|
+
for (const [k, e] of Object.entries(cfg)) out[k] = walk22(e, true);
|
|
4127
5005
|
return canonicalJson(out);
|
|
4128
5006
|
} catch {
|
|
4129
5007
|
return v;
|
|
4130
5008
|
}
|
|
4131
5009
|
}
|
|
4132
|
-
if (Array.isArray(v)) return v.map((e) =>
|
|
5010
|
+
if (Array.isArray(v)) return v.map((e) => walk22(e));
|
|
4133
5011
|
if (v && typeof v === "object") {
|
|
4134
5012
|
const out = {};
|
|
4135
|
-
for (const [k, e] of Object.entries(v)) out[k] =
|
|
5013
|
+
for (const [k, e] of Object.entries(v)) out[k] = walk22(e);
|
|
4136
5014
|
return out;
|
|
4137
5015
|
}
|
|
4138
5016
|
return v;
|
|
4139
5017
|
}, "walk");
|
|
4140
5018
|
return {
|
|
4141
|
-
value:
|
|
5019
|
+
value: walk22(value22),
|
|
4142
5020
|
missing: [
|
|
4143
5021
|
...missing
|
|
4144
5022
|
].sort()
|
|
@@ -4216,8 +5094,8 @@ function armEntry(arm) {
|
|
|
4216
5094
|
}
|
|
4217
5095
|
function ofEntry(e) {
|
|
4218
5096
|
if (!e || typeof e !== "object") return ZERO;
|
|
4219
|
-
const
|
|
4220
|
-
switch (
|
|
5097
|
+
const n2 = e;
|
|
5098
|
+
switch (n2.type) {
|
|
4221
5099
|
case "agent":
|
|
4222
5100
|
return {
|
|
4223
5101
|
steps: {
|
|
@@ -4250,17 +5128,17 @@ function ofEntry(e) {
|
|
|
4250
5128
|
agentCalls: 0
|
|
4251
5129
|
};
|
|
4252
5130
|
case "parallel": {
|
|
4253
|
-
const arms = Array.isArray(
|
|
5131
|
+
const arms = Array.isArray(n2.steps) ? n2.steps : [];
|
|
4254
5132
|
return arms.map(armEntry).map(ofEntry).reduce(add, ZERO);
|
|
4255
5133
|
}
|
|
4256
5134
|
case "conditional": {
|
|
4257
|
-
const arms = (Array.isArray(
|
|
5135
|
+
const arms = (Array.isArray(n2.steps) ? n2.steps : []).map(armEntry).map(ofEntry);
|
|
4258
5136
|
if (arms.length === 0) return ZERO;
|
|
4259
|
-
const hasOtherwise =
|
|
5137
|
+
const hasOtherwise = n2.otherwise !== void 0;
|
|
4260
5138
|
const minArm = arms.reduce((a, b) => b.credits.min < a.credits.min ? b : a);
|
|
4261
5139
|
const maxArm = arms.reduce((a, b) => b.credits.max > a.credits.max ? b : a);
|
|
4262
5140
|
const summed = arms.reduce(add, ZERO);
|
|
4263
|
-
const max =
|
|
5141
|
+
const max = n2.exclusive === true ? maxArm : summed;
|
|
4264
5142
|
const min = hasOtherwise ? minArm : {
|
|
4265
5143
|
...ZERO
|
|
4266
5144
|
};
|
|
@@ -4277,17 +5155,14 @@ function ofEntry(e) {
|
|
|
4277
5155
|
};
|
|
4278
5156
|
}
|
|
4279
5157
|
case "foreach": {
|
|
4280
|
-
const body = ofEntry(armEntry(
|
|
4281
|
-
const opts =
|
|
4282
|
-
const cap = typeof opts.maxItems === "number" && opts.maxItems > 0 ? opts.maxItems :
|
|
5158
|
+
const body = ofEntry(armEntry(n2.step));
|
|
5159
|
+
const opts = n2.opts ?? {};
|
|
5160
|
+
const cap = typeof opts.maxItems === "number" && opts.maxItems > 0 ? opts.maxItems : WORKFLOW_FOREACH_DEFAULT_MAX_ITEMS;
|
|
4283
5161
|
return scale(body, 0, cap);
|
|
4284
5162
|
}
|
|
4285
|
-
case "loop":
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
const body = ofEntry(armEntry(n.step));
|
|
4289
|
-
const opts = n.options ?? {};
|
|
4290
|
-
const cap = typeof opts.maxIterations === "number" && opts.maxIterations > 0 ? opts.maxIterations : 1;
|
|
5163
|
+
case "loop": {
|
|
5164
|
+
const body = ofEntry(armEntry(n2.step));
|
|
5165
|
+
const cap = typeof n2.maxIterations === "number" && n2.maxIterations > 0 ? n2.maxIterations : WORKFLOW_LOOP_DEFAULT_MAX_ITERATIONS;
|
|
4291
5166
|
return scale(body, 1, cap);
|
|
4292
5167
|
}
|
|
4293
5168
|
default:
|
|
@@ -4318,14 +5193,85 @@ function estimateGraph(envelopeOrGraph) {
|
|
|
4318
5193
|
consentCredits: r.credits.max
|
|
4319
5194
|
};
|
|
4320
5195
|
}
|
|
4321
|
-
|
|
5196
|
+
function* singleStepsOf(entry) {
|
|
5197
|
+
if (!isRecord2(entry)) return;
|
|
5198
|
+
switch (entry.type) {
|
|
5199
|
+
case "step":
|
|
5200
|
+
case "agent":
|
|
5201
|
+
case "tool":
|
|
5202
|
+
yield entry;
|
|
5203
|
+
return;
|
|
5204
|
+
case "workflow":
|
|
5205
|
+
yield entry;
|
|
5206
|
+
if (Array.isArray(entry.graph)) yield* singleStepsOf(entry.graph[1]);
|
|
5207
|
+
return;
|
|
5208
|
+
case "parallel":
|
|
5209
|
+
case "conditional":
|
|
5210
|
+
if (Array.isArray(entry.steps)) for (const arm of entry.steps) yield* singleStepsOf(arm);
|
|
5211
|
+
yield* singleStepsOf(entry.otherwise);
|
|
5212
|
+
return;
|
|
5213
|
+
case "foreach":
|
|
5214
|
+
case "loop":
|
|
5215
|
+
yield* singleStepsOf(entry.step);
|
|
5216
|
+
return;
|
|
5217
|
+
default:
|
|
5218
|
+
return;
|
|
5219
|
+
}
|
|
5220
|
+
}
|
|
5221
|
+
function entriesOf(graph) {
|
|
5222
|
+
const definition = isRecord2(graph) ? graph.definition : void 0;
|
|
5223
|
+
const entries = isRecord2(definition) ? definition.graph : void 0;
|
|
5224
|
+
return Array.isArray(entries) ? entries : [];
|
|
5225
|
+
}
|
|
5226
|
+
function inheritTargets(graphs) {
|
|
5227
|
+
const targets = /* @__PURE__ */ new Set();
|
|
5228
|
+
for (const graph of graphs) {
|
|
5229
|
+
for (const entry of entriesOf(graph)) {
|
|
5230
|
+
for (const node of singleStepsOf(entry)) {
|
|
5231
|
+
if (node.type === "workflow" && node.workspace === "inherit" && typeof node.workflowId === "string" && node.workflowId.length > 0) {
|
|
5232
|
+
targets.add(node.workflowId);
|
|
5233
|
+
}
|
|
5234
|
+
}
|
|
5235
|
+
}
|
|
5236
|
+
}
|
|
5237
|
+
return targets;
|
|
5238
|
+
}
|
|
5239
|
+
function needsInheritedWorkspace(graph) {
|
|
5240
|
+
if (!isRecord2(graph) || graph.workspace !== void 0) return false;
|
|
5241
|
+
for (const entry of entriesOf(graph)) {
|
|
5242
|
+
for (const node of singleStepsOf(entry)) {
|
|
5243
|
+
if (node.workspace !== void 0 && node.workspace !== "inherit") return true;
|
|
5244
|
+
}
|
|
5245
|
+
}
|
|
5246
|
+
return false;
|
|
5247
|
+
}
|
|
5248
|
+
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;
|
|
4322
5249
|
var init_dist2 = __esm({
|
|
4323
5250
|
"../workflow-graph/dist/index.mjs"() {
|
|
4324
5251
|
"use strict";
|
|
4325
5252
|
init_dist();
|
|
5253
|
+
init_dist();
|
|
5254
|
+
init_dist();
|
|
5255
|
+
init_dist();
|
|
5256
|
+
init_dist();
|
|
5257
|
+
init_dist();
|
|
5258
|
+
init_dist();
|
|
4326
5259
|
__defProp3 = Object.defineProperty;
|
|
4327
5260
|
__name3 = /* @__PURE__ */ __name((target, value22) => __defProp3(target, "name", { value: value22, configurable: true }), "__name");
|
|
5261
|
+
SideEffectsSchema = z4.enum(WORKFLOW_SIDE_EFFECTS);
|
|
5262
|
+
JobResourcesSchema = z4.enum(WORKFLOW_JOB_RESOURCES);
|
|
4328
5263
|
WORKFLOW_ARM_SUBRUN_ID = "$arm";
|
|
5264
|
+
WORKSPACE_TEMPLATE_EXPR_RE = /^\$\{\s*(?:initData|input)\.([^}]+?)\s*\}$/;
|
|
5265
|
+
__name(workspaceTemplatePath, "workspaceTemplatePath");
|
|
5266
|
+
__name3(workspaceTemplatePath, "workspaceTemplatePath");
|
|
5267
|
+
__name(retryBackoffs, "retryBackoffs");
|
|
5268
|
+
__name3(retryBackoffs, "retryBackoffs");
|
|
5269
|
+
SLEEP_UNTIL_REPLACEMENT = Object.freeze({
|
|
5270
|
+
type: "sleep",
|
|
5271
|
+
duration: 6e4
|
|
5272
|
+
});
|
|
5273
|
+
__name(sleepUntilUnsupportedMessage, "sleepUntilUnsupportedMessage");
|
|
5274
|
+
__name3(sleepUntilUnsupportedMessage, "sleepUntilUnsupportedMessage");
|
|
4329
5275
|
WORKFLOW_CAPS_DEFAULT = Object.freeze({
|
|
4330
5276
|
maxParallelArms: 16,
|
|
4331
5277
|
maxForeachConcurrency: 16,
|
|
@@ -4355,12 +5301,19 @@ var init_dist2 = __esm({
|
|
|
4355
5301
|
__name3(fillPolicy, "fillPolicy");
|
|
4356
5302
|
__name(fillSingle, "fillSingle");
|
|
4357
5303
|
__name3(fillSingle, "fillSingle");
|
|
5304
|
+
__name(fillHitl, "fillHitl");
|
|
5305
|
+
__name3(fillHitl, "fillHitl");
|
|
4358
5306
|
__name(fillArm, "fillArm");
|
|
4359
5307
|
__name3(fillArm, "fillArm");
|
|
4360
5308
|
__name(fillEntry, "fillEntry");
|
|
4361
5309
|
__name3(fillEntry, "fillEntry");
|
|
4362
5310
|
__name(withDefaultsFilled, "withDefaultsFilled");
|
|
4363
5311
|
__name3(withDefaultsFilled, "withDefaultsFilled");
|
|
5312
|
+
CONNECTION_ID_HEX_RE = /^[0-9a-f]{24}$/;
|
|
5313
|
+
__name(isConnectionKeyShaped, "isConnectionKeyShaped");
|
|
5314
|
+
__name3(isConnectionKeyShaped, "isConnectionKeyShaped");
|
|
5315
|
+
__name(connectionKeyUndeclaredMessage, "connectionKeyUndeclaredMessage");
|
|
5316
|
+
__name3(connectionKeyUndeclaredMessage, "connectionKeyUndeclaredMessage");
|
|
4364
5317
|
WORKFLOW_JOB_TOOLS = [
|
|
4365
5318
|
"shell",
|
|
4366
5319
|
"read",
|
|
@@ -4393,11 +5346,15 @@ var init_dist2 = __esm({
|
|
|
4393
5346
|
if (t === void 0) return void 0;
|
|
4394
5347
|
return Array.isArray(t) ? t.includes("array") : t === "array";
|
|
4395
5348
|
}, "schemaIsArray");
|
|
5349
|
+
isHitlNode = /* @__PURE__ */ __name3((n2) => isWorkflowHitlEntryType(n2.type), "isHitlNode");
|
|
5350
|
+
isSingleStep = /* @__PURE__ */ __name3((n2) => !isHitlNode(n2), "isSingleStep");
|
|
4396
5351
|
singleId = /* @__PURE__ */ __name3((s) => s.type === "step" ? s.step.id : s.id, "singleId");
|
|
4397
5352
|
armId = /* @__PURE__ */ __name3((a) => a.type === "mapping" ? a.id : singleId(a), "armId");
|
|
4398
5353
|
TEMPLATE_STEP_REF = /\$\{\s*stepResults\.([A-Za-z0-9_\-]+)/g;
|
|
4399
5354
|
__name(templateStepRefs, "templateStepRefs");
|
|
4400
5355
|
__name3(templateStepRefs, "templateStepRefs");
|
|
5356
|
+
__name(readMapConfig, "readMapConfig");
|
|
5357
|
+
__name3(readMapConfig, "readMapConfig");
|
|
4401
5358
|
__name(mapConfigStepRefs, "mapConfigStepRefs");
|
|
4402
5359
|
__name3(mapConfigStepRefs, "mapConfigStepRefs");
|
|
4403
5360
|
__name(nodeStepRefs, "nodeStepRefs");
|
|
@@ -4424,6 +5381,11 @@ var init_dist2 = __esm({
|
|
|
4424
5381
|
]);
|
|
4425
5382
|
__name(isPredicate, "isPredicate");
|
|
4426
5383
|
__name3(isPredicate, "isPredicate");
|
|
5384
|
+
isPredicateScalar = /* @__PURE__ */ __name3((v) => v === null || typeof v === "string" || typeof v === "number" || typeof v === "boolean", "isPredicateScalar");
|
|
5385
|
+
__name(isPathOrLiteral, "isPathOrLiteral");
|
|
5386
|
+
__name3(isPathOrLiteral, "isPathOrLiteral");
|
|
5387
|
+
__name(isWellFormedPredicate, "isWellFormedPredicate");
|
|
5388
|
+
__name3(isWellFormedPredicate, "isWellFormedPredicate");
|
|
4427
5389
|
GRAPH_HASH_PREFIX = "sha256-cj1:";
|
|
4428
5390
|
__name(canonicalJson, "canonicalJson");
|
|
4429
5391
|
__name3(canonicalJson, "canonicalJson");
|
|
@@ -4442,14 +5404,9 @@ var init_dist2 = __esm({
|
|
|
4442
5404
|
this.name = "WorkflowPlanError";
|
|
4443
5405
|
}
|
|
4444
5406
|
};
|
|
4445
|
-
|
|
4446
|
-
|
|
4447
|
-
|
|
4448
|
-
tool: "tool",
|
|
4449
|
-
workflow: "subrun"
|
|
4450
|
-
};
|
|
4451
|
-
isSingleStep = /* @__PURE__ */ __name3((e) => e.type === "step" || e.type === "agent" || e.type === "tool" || e.type === "workflow", "isSingleStep");
|
|
4452
|
-
singleStepId = /* @__PURE__ */ __name3((e) => e.type === "step" ? e.step.id : e.id, "singleStepId");
|
|
5407
|
+
isArmStep = /* @__PURE__ */ __name3((e) => isWorkflowArmEntryType(e.type), "isArmStep");
|
|
5408
|
+
armStepId = /* @__PURE__ */ __name3((e) => e.type === "step" ? e.step.id : e.id, "armStepId");
|
|
5409
|
+
armStepKind = /* @__PURE__ */ __name3((e) => WORKFLOW_ARM_ENTRY_STEP_KINDS[e.type], "armStepKind");
|
|
4453
5410
|
joinIdOf = /* @__PURE__ */ __name3((entryId) => `${entryId}.join`, "joinIdOf");
|
|
4454
5411
|
containerIdOf = /* @__PURE__ */ __name3((type, entryIndex) => `${type}@${entryIndex}`, "containerIdOf");
|
|
4455
5412
|
__name(compilePlan, "compilePlan");
|
|
@@ -4557,8 +5514,12 @@ var init_dist2 = __esm({
|
|
|
4557
5514
|
this.name = "WorkflowTemplateError";
|
|
4558
5515
|
}
|
|
4559
5516
|
};
|
|
5517
|
+
__name(isMapConfigObject, "isMapConfigObject");
|
|
5518
|
+
__name3(isMapConfigObject, "isMapConfigObject");
|
|
4560
5519
|
__name(parseMapConfig, "parseMapConfig");
|
|
4561
5520
|
__name3(parseMapConfig, "parseMapConfig");
|
|
5521
|
+
__name(mapConfigWire, "mapConfigWire");
|
|
5522
|
+
__name3(mapConfigWire, "mapConfigWire");
|
|
4562
5523
|
TEMPLATE_PLACEHOLDER = /\$\{([^}]*)\}/g;
|
|
4563
5524
|
TEMPLATE_NAMESPACES = [
|
|
4564
5525
|
"initData",
|
|
@@ -4612,16 +5573,90 @@ var init_dist2 = __esm({
|
|
|
4612
5573
|
fromKnowledge = /* @__PURE__ */ __name3((k) => ({
|
|
4613
5574
|
knowledge: k
|
|
4614
5575
|
}), "fromKnowledge");
|
|
5576
|
+
CONTINUED_FAILURE_TAG = "continued_failure";
|
|
5577
|
+
CONTINUED_FAILURE_DEFAULT_CODE = "step_failed";
|
|
5578
|
+
CONTINUED_FAILURE_OUTPUT_SCHEMA = Object.freeze({
|
|
5579
|
+
type: "object",
|
|
5580
|
+
properties: {
|
|
5581
|
+
__lua_workflow: {
|
|
5582
|
+
type: "string",
|
|
5583
|
+
const: CONTINUED_FAILURE_TAG
|
|
5584
|
+
},
|
|
5585
|
+
failed: {
|
|
5586
|
+
type: "boolean",
|
|
5587
|
+
const: true
|
|
5588
|
+
},
|
|
5589
|
+
error: {
|
|
5590
|
+
type: "object",
|
|
5591
|
+
properties: {
|
|
5592
|
+
code: {
|
|
5593
|
+
type: "string"
|
|
5594
|
+
},
|
|
5595
|
+
message: {
|
|
5596
|
+
type: "string"
|
|
5597
|
+
}
|
|
5598
|
+
},
|
|
5599
|
+
required: [
|
|
5600
|
+
"code",
|
|
5601
|
+
"message"
|
|
5602
|
+
]
|
|
5603
|
+
},
|
|
5604
|
+
text: {
|
|
5605
|
+
type: "string",
|
|
5606
|
+
const: ""
|
|
5607
|
+
}
|
|
5608
|
+
},
|
|
5609
|
+
required: [
|
|
5610
|
+
"__lua_workflow",
|
|
5611
|
+
"failed",
|
|
5612
|
+
"error",
|
|
5613
|
+
"text"
|
|
5614
|
+
]
|
|
5615
|
+
});
|
|
5616
|
+
CONTINUED_FAILURE_LEAF_PATHS = Object.freeze([
|
|
5617
|
+
"failed",
|
|
5618
|
+
"error",
|
|
5619
|
+
"error.code",
|
|
5620
|
+
"error.message",
|
|
5621
|
+
"text"
|
|
5622
|
+
]);
|
|
5623
|
+
__name(continuedFailureValue, "continuedFailureValue");
|
|
5624
|
+
__name3(continuedFailureValue, "continuedFailureValue");
|
|
5625
|
+
__name(isContinuedFailureValue, "isContinuedFailureValue");
|
|
5626
|
+
__name3(isContinuedFailureValue, "isContinuedFailureValue");
|
|
5627
|
+
isHitlNode2 = /* @__PURE__ */ __name3((n2) => isWorkflowHitlEntryType(n2.type), "isHitlNode");
|
|
4615
5628
|
__name(lowerContainerArm, "lowerContainerArm");
|
|
4616
5629
|
__name3(lowerContainerArm, "lowerContainerArm");
|
|
4617
|
-
nodeIdOf = /* @__PURE__ */ __name3((
|
|
5630
|
+
nodeIdOf = /* @__PURE__ */ __name3((n2) => n2.type === "step" ? n2.step.id : n2.id, "nodeIdOf");
|
|
4618
5631
|
__name(entryIds, "entryIds");
|
|
4619
5632
|
__name3(entryIds, "entryIds");
|
|
4620
5633
|
__name(resolvePlacements, "resolvePlacements");
|
|
4621
5634
|
__name3(resolvePlacements, "resolvePlacements");
|
|
5635
|
+
GOAL_JUDGE_STEP_ID = "__goal_judge";
|
|
5636
|
+
NON_LEAF_KINDS = /* @__PURE__ */ new Set([
|
|
5637
|
+
"foreach",
|
|
5638
|
+
"branch"
|
|
5639
|
+
]);
|
|
5640
|
+
CONDITIONAL_JOIN_ID = /^conditional@\d+\.join$/;
|
|
5641
|
+
__name(isConditionalJoinId, "isConditionalJoinId");
|
|
5642
|
+
__name3(isConditionalJoinId, "isConditionalJoinId");
|
|
5643
|
+
__name(isPlainObject, "isPlainObject");
|
|
5644
|
+
__name3(isPlainObject, "isPlainObject");
|
|
5645
|
+
__name(leafValue, "leafValue");
|
|
5646
|
+
__name3(leafValue, "leafValue");
|
|
5647
|
+
__name(runOutputLeaves, "runOutputLeaves");
|
|
5648
|
+
__name3(runOutputLeaves, "runOutputLeaves");
|
|
5649
|
+
__name(deriveRunOutput, "deriveRunOutput");
|
|
5650
|
+
__name3(deriveRunOutput, "deriveRunOutput");
|
|
5651
|
+
__name(subrunSettledOutput, "subrunSettledOutput");
|
|
5652
|
+
__name3(subrunSettledOutput, "subrunSettledOutput");
|
|
4622
5653
|
__name(seedLedgerFromRun, "seedLedgerFromRun");
|
|
4623
5654
|
__name3(seedLedgerFromRun, "seedLedgerFromRun");
|
|
4624
|
-
|
|
5655
|
+
branchArmId = /* @__PURE__ */ __name3((arm) => arm.type === "step" ? arm.step.id : arm.id, "branchArmId");
|
|
5656
|
+
__name(branchSpecFromConditional, "branchSpecFromConditional");
|
|
5657
|
+
__name3(branchSpecFromConditional, "branchSpecFromConditional");
|
|
5658
|
+
__name(selectBranchArms, "selectBranchArms");
|
|
5659
|
+
__name3(selectBranchArms, "selectBranchArms");
|
|
4625
5660
|
canonical = /* @__PURE__ */ __name3((v) => JSON.stringify(sortKeys(v)), "canonical");
|
|
4626
5661
|
sortKeys = /* @__PURE__ */ __name3((v) => {
|
|
4627
5662
|
if (Array.isArray(v)) return v.map(sortKeys);
|
|
@@ -4635,6 +5670,12 @@ var init_dist2 = __esm({
|
|
|
4635
5670
|
}, "sortKeys");
|
|
4636
5671
|
__name(replayLedger, "replayLedger");
|
|
4637
5672
|
__name3(replayLedger, "replayLedger");
|
|
5673
|
+
JOIN = ".join";
|
|
5674
|
+
entryOfJoin = /* @__PURE__ */ __name3((id) => id.endsWith(JOIN) ? id.slice(0, -JOIN.length) : void 0, "entryOfJoin");
|
|
5675
|
+
__name(replayResultOf, "replayResultOf");
|
|
5676
|
+
__name3(replayResultOf, "replayResultOf");
|
|
5677
|
+
__name(ancestorResults, "ancestorResults");
|
|
5678
|
+
__name3(ancestorResults, "ancestorResults");
|
|
4638
5679
|
__name(inferTaken, "inferTaken");
|
|
4639
5680
|
__name3(inferTaken, "inferTaken");
|
|
4640
5681
|
__name(countChildren, "countChildren");
|
|
@@ -4647,6 +5688,14 @@ var init_dist2 = __esm({
|
|
|
4647
5688
|
__name3(pruneUndefined, "pruneUndefined");
|
|
4648
5689
|
__name(runNextAction, "runNextAction");
|
|
4649
5690
|
__name3(runNextAction, "runNextAction");
|
|
5691
|
+
IN_FLIGHT = new Set(WORKFLOW_STEP_IN_FLIGHT);
|
|
5692
|
+
__name(emptyRunCounts, "emptyRunCounts");
|
|
5693
|
+
__name3(emptyRunCounts, "emptyRunCounts");
|
|
5694
|
+
__name(runCountsFromStatusTally, "runCountsFromStatusTally");
|
|
5695
|
+
__name3(runCountsFromStatusTally, "runCountsFromStatusTally");
|
|
5696
|
+
__name(runCountsFromStepStatuses, "runCountsFromStepStatuses");
|
|
5697
|
+
__name3(runCountsFromStepStatuses, "runCountsFromStepStatuses");
|
|
5698
|
+
n = /* @__PURE__ */ __name3((v) => typeof v === "number" && Number.isFinite(v) ? v : 0, "n");
|
|
4650
5699
|
__name(runCounts, "runCounts");
|
|
4651
5700
|
__name3(runCounts, "runCounts");
|
|
4652
5701
|
__name(runUsage, "runUsage");
|
|
@@ -4657,6 +5706,43 @@ var init_dist2 = __esm({
|
|
|
4657
5706
|
__name3(runWorkspaceView, "runWorkspaceView");
|
|
4658
5707
|
__name(toWorkflowRunSummary, "toWorkflowRunSummary");
|
|
4659
5708
|
__name3(toWorkflowRunSummary, "toWorkflowRunSummary");
|
|
5709
|
+
STEP_ERROR_DETAIL_KEYS = [
|
|
5710
|
+
"reason",
|
|
5711
|
+
"key",
|
|
5712
|
+
"integrationType",
|
|
5713
|
+
"candidates",
|
|
5714
|
+
"connectionId",
|
|
5715
|
+
"status",
|
|
5716
|
+
"code",
|
|
5717
|
+
"providerStatus",
|
|
5718
|
+
"model",
|
|
5719
|
+
"turnIndex",
|
|
5720
|
+
"message",
|
|
5721
|
+
// LUA-655: `output_schema_invalid` — the Ajv issues ({path, message}, ≤ 20, scrubbed at the source too) and the
|
|
5722
|
+
// in-session repair rounds the attempt spent.
|
|
5723
|
+
"issues",
|
|
5724
|
+
"repairRounds",
|
|
5725
|
+
// LUA-669 (#2446 review 4): the subrun failures — `subrun_<status>` names the child run and how it ended
|
|
5726
|
+
// (`childStatus`, `childReason: 'max_duration'` under `subrun_timed_out`), `subrun_depth_exceeded` its `depth` /
|
|
5727
|
+
// `max`, and every refusal the target `workflowId`. Short scalars only: the child's whole `childError` and the
|
|
5728
|
+
// cycle walk's `ancestors` stay off the wire — the child run's own R4 carries its error.
|
|
5729
|
+
"childRunId",
|
|
5730
|
+
"childStatus",
|
|
5731
|
+
"childReason",
|
|
5732
|
+
"max",
|
|
5733
|
+
"depth",
|
|
5734
|
+
"workflowId",
|
|
5735
|
+
// LUA-696 (review 2): the `ctx.once` key of an `effect_in_doubt` park — the step site stamps it here (scrubbed)
|
|
5736
|
+
// beside `park.effectKey`; a key is user text and leaves scrubbed like every other string leaf.
|
|
5737
|
+
"effectKey"
|
|
5738
|
+
];
|
|
5739
|
+
STEP_ERROR_DETAIL_MAX_BYTES = 8 * 1024;
|
|
5740
|
+
DETAIL_MAX_DEPTH = 4;
|
|
5741
|
+
DETAIL_MAX_ITEMS = 100;
|
|
5742
|
+
__name(scrubDetailValue, "scrubDetailValue");
|
|
5743
|
+
__name3(scrubDetailValue, "scrubDetailValue");
|
|
5744
|
+
__name(stepErrorDetail, "stepErrorDetail");
|
|
5745
|
+
__name3(stepErrorDetail, "stepErrorDetail");
|
|
4660
5746
|
MAX_HOLIDAYS = 366;
|
|
4661
5747
|
MAX_WALK_DAYS = 400;
|
|
4662
5748
|
HHMM = /^([01]\d|2[0-3]):([0-5]\d)$/;
|
|
@@ -4750,58 +5836,70 @@ var init_dist2 = __esm({
|
|
|
4750
5836
|
__name3(rebaseItemPointer, "rebaseItemPointer");
|
|
4751
5837
|
APPROVER_SPEC_MAX_USERS = 20;
|
|
4752
5838
|
ESCALATION_MAX_HOPS = 3;
|
|
4753
|
-
TemplateBindingSchema =
|
|
4754
|
-
template:
|
|
5839
|
+
TemplateBindingSchema = z22.object({
|
|
5840
|
+
template: z22.string().min(1).max(2048)
|
|
4755
5841
|
}).strict();
|
|
4756
|
-
ApproverSpecSchema =
|
|
4757
|
-
|
|
4758
|
-
|
|
4759
|
-
|
|
4760
|
-
users:
|
|
4761
|
-
|
|
5842
|
+
ApproverSpecSchema = z22.union([
|
|
5843
|
+
z22.literal("creator"),
|
|
5844
|
+
z22.literal("org-admins"),
|
|
5845
|
+
z22.object({
|
|
5846
|
+
users: z22.union([
|
|
5847
|
+
z22.array(z22.string().min(1).max(128)).min(1).max(APPROVER_SPEC_MAX_USERS),
|
|
4762
5848
|
TemplateBindingSchema
|
|
4763
5849
|
])
|
|
4764
5850
|
}).strict(),
|
|
4765
|
-
|
|
4766
|
-
role:
|
|
4767
|
-
|
|
5851
|
+
z22.object({
|
|
5852
|
+
role: z22.union([
|
|
5853
|
+
z22.string().min(1).max(128),
|
|
4768
5854
|
TemplateBindingSchema
|
|
4769
5855
|
])
|
|
4770
5856
|
}).strict(),
|
|
4771
|
-
|
|
4772
|
-
group:
|
|
4773
|
-
|
|
5857
|
+
z22.object({
|
|
5858
|
+
group: z22.union([
|
|
5859
|
+
z22.string().min(1).max(128),
|
|
4774
5860
|
TemplateBindingSchema
|
|
4775
5861
|
])
|
|
4776
5862
|
}).strict(),
|
|
4777
|
-
|
|
4778
|
-
governance:
|
|
4779
|
-
policyId:
|
|
5863
|
+
z22.object({
|
|
5864
|
+
governance: z22.object({
|
|
5865
|
+
policyId: z22.string().min(1).max(128)
|
|
4780
5866
|
}).strict()
|
|
4781
5867
|
}).strict()
|
|
4782
5868
|
]);
|
|
4783
|
-
FourEyesSchema =
|
|
5869
|
+
FourEyesSchema = z22.object({
|
|
4784
5870
|
edit: ApproverSpecSchema,
|
|
4785
5871
|
approve: ApproverSpecSchema
|
|
4786
5872
|
}).strict();
|
|
4787
|
-
EscalationHopSchema =
|
|
5873
|
+
EscalationHopSchema = z22.object({
|
|
4788
5874
|
escalateTo: ApproverSpecSchema,
|
|
4789
|
-
timeoutHours:
|
|
5875
|
+
timeoutHours: z22.number().finite().min(1).max(720)
|
|
4790
5876
|
}).strict();
|
|
4791
|
-
TerminalOutcomeSchema =
|
|
5877
|
+
TerminalOutcomeSchema = z22.enum([
|
|
4792
5878
|
"deny",
|
|
4793
5879
|
"cancel-run",
|
|
4794
5880
|
"fail",
|
|
4795
5881
|
"continue"
|
|
4796
5882
|
]);
|
|
4797
|
-
ApprovalOnTimeoutSchema =
|
|
5883
|
+
ApprovalOnTimeoutSchema = z22.union([
|
|
4798
5884
|
TerminalOutcomeSchema,
|
|
4799
5885
|
EscalationHopSchema,
|
|
4800
|
-
|
|
5886
|
+
z22.array(z22.union([
|
|
4801
5887
|
TerminalOutcomeSchema,
|
|
4802
5888
|
EscalationHopSchema
|
|
4803
5889
|
])).min(1).max(ESCALATION_MAX_HOPS + 1)
|
|
4804
5890
|
]);
|
|
5891
|
+
APPROVER_SPEC_SHAPES = [
|
|
5892
|
+
"'creator'",
|
|
5893
|
+
"'org-admins'",
|
|
5894
|
+
"{users:[userId, \u2026]}",
|
|
5895
|
+
"{role:roleName}",
|
|
5896
|
+
"{group:groupName}",
|
|
5897
|
+
"{governance:{policyId}}"
|
|
5898
|
+
];
|
|
5899
|
+
APPROVER_WRITTEN_MAX = 120;
|
|
5900
|
+
USER_ID_SHAPED_RE = /^[^\s@]{1,128}$/;
|
|
5901
|
+
__name(describeApproverSpecRefusal, "describeApproverSpecRefusal");
|
|
5902
|
+
__name3(describeApproverSpecRefusal, "describeApproverSpecRefusal");
|
|
4805
5903
|
BINDING_ROOTS = [
|
|
4806
5904
|
"initData",
|
|
4807
5905
|
"stepResults",
|
|
@@ -4850,6 +5948,15 @@ var init_dist2 = __esm({
|
|
|
4850
5948
|
__name3(ofEntry, "ofEntry");
|
|
4851
5949
|
__name(estimateGraph, "estimateGraph");
|
|
4852
5950
|
__name3(estimateGraph, "estimateGraph");
|
|
5951
|
+
isRecord2 = /* @__PURE__ */ __name3((v) => !!v && typeof v === "object" && !Array.isArray(v), "isRecord");
|
|
5952
|
+
__name(singleStepsOf, "singleStepsOf");
|
|
5953
|
+
__name3(singleStepsOf, "singleStepsOf");
|
|
5954
|
+
__name(entriesOf, "entriesOf");
|
|
5955
|
+
__name3(entriesOf, "entriesOf");
|
|
5956
|
+
__name(inheritTargets, "inheritTargets");
|
|
5957
|
+
__name3(inheritTargets, "inheritTargets");
|
|
5958
|
+
__name(needsInheritedWorkspace, "needsInheritedWorkspace");
|
|
5959
|
+
__name3(needsInheritedWorkspace, "needsInheritedWorkspace");
|
|
4853
5960
|
}
|
|
4854
5961
|
});
|
|
4855
5962
|
|
|
@@ -4908,16 +6015,16 @@ function stepNodeOf(s) {
|
|
|
4908
6015
|
});
|
|
4909
6016
|
}
|
|
4910
6017
|
function materializeEntry(entry, steps) {
|
|
4911
|
-
const single = /* @__PURE__ */ __name((
|
|
4912
|
-
if (
|
|
4913
|
-
if (
|
|
4914
|
-
...
|
|
6018
|
+
const single = /* @__PURE__ */ __name((n2) => {
|
|
6019
|
+
if (n2.type === "step" && steps[n2.step.id]) return stepNodeOf(steps[n2.step.id]);
|
|
6020
|
+
if (n2.type === "workflow" && n2.workflowId === WORKFLOW_ARM_SUBRUN_ID && n2.graph) return {
|
|
6021
|
+
...n2,
|
|
4915
6022
|
graph: [
|
|
4916
|
-
|
|
4917
|
-
single(
|
|
6023
|
+
n2.graph[0],
|
|
6024
|
+
single(n2.graph[1])
|
|
4918
6025
|
]
|
|
4919
6026
|
};
|
|
4920
|
-
return
|
|
6027
|
+
return n2;
|
|
4921
6028
|
}, "single");
|
|
4922
6029
|
switch (entry.type) {
|
|
4923
6030
|
case "step":
|
|
@@ -4948,7 +6055,12 @@ function materializeEntry(entry, steps) {
|
|
|
4948
6055
|
}
|
|
4949
6056
|
}
|
|
4950
6057
|
function graphHasHitl(graph, steps) {
|
|
4951
|
-
|
|
6058
|
+
for (const e of graph) {
|
|
6059
|
+
if (isHitlEntry(e)) return true;
|
|
6060
|
+
if (e.type === "parallel" && e.steps.some(isHitlEntry)) return true;
|
|
6061
|
+
if (e.type === "conditional" && (e.steps.some(isHitlEntry) || isHitlEntry(e.otherwise))) return true;
|
|
6062
|
+
if ((e.type === "foreach" || e.type === "loop") && isHitlEntry(e.step)) return true;
|
|
6063
|
+
}
|
|
4952
6064
|
return Object.values(steps).some((s) => s.suspendSchema !== void 0);
|
|
4953
6065
|
}
|
|
4954
6066
|
function createWorkflow(cfg) {
|
|
@@ -4962,8 +6074,8 @@ function createWorkflow(cfg) {
|
|
|
4962
6074
|
if (v.roles.length > 20 || (v.users?.length ?? 0) > 50) throw new LuaWorkflowBuildError("cap-exceeded", "outputVisibility allows \u2264 20 roles and \u2264 50 users");
|
|
4963
6075
|
}
|
|
4964
6076
|
if (cfg.backfillOnEnable?.maxOccurrences !== void 0) {
|
|
4965
|
-
const
|
|
4966
|
-
if (!Number.isInteger(
|
|
6077
|
+
const n2 = cfg.backfillOnEnable.maxOccurrences;
|
|
6078
|
+
if (!Number.isInteger(n2) || n2 < 1 || n2 > 200) throw new LuaWorkflowBuildError("invalid-envelope", "backfillOnEnable.maxOccurrences must be an integer in 1..200 (backfill-max-occurrences-out-of-range; the org maxBatchItems twin is checked at publish/R56)");
|
|
4967
6079
|
}
|
|
4968
6080
|
const keys = /* @__PURE__ */ new Set();
|
|
4969
6081
|
envRefKeys(cfg.schedule, keys);
|
|
@@ -4982,10 +6094,11 @@ function defineWorkflow(cfg, build) {
|
|
|
4982
6094
|
if (!(wf instanceof LuaWorkflow)) throw new LuaWorkflowBuildError("invalid-envelope", "defineWorkflow: the build callback must return `wf\u2026.commit()`");
|
|
4983
6095
|
return wf;
|
|
4984
6096
|
}
|
|
4985
|
-
var init2, state2, lit2, eq2, ne2, gt2, gte2, lt2, lte2, inSet2, notIn2, exists2, notExists2, truthy2, falsy2, and2, or2, not2, fromInit2, fromStep2, value2, template2, fromRequest2, rows2, fromKnowledge2, LuaWorkflowBuildError, STEP_ID_RE, WORKFLOW_NAME_RE, WORKFLOW_MAX_PARALLEL_ARMS, WORKFLOW_MAX_FOREACH_CONCURRENCY, WORKFLOW_MAX_FOREACH_ITEMS, WORKFLOW_WORKER_MAX_TIMEOUT_SECONDS, WORKFLOW_JOB_SEGMENT_MAX_SECONDS, WORKFLOW_JOB_MAX_TIMEOUT_SECONDS, WORKFLOW_LOOP_INTERVAL_MAX_SECONDS, WORKFLOW_FOREACH_RATE_MAX_PER_SECOND, WORKFLOW_SPECIALIST_ROLE_MAX_INSTRUCTIONS, WORKFLOW_DEFAULT_MAX_DURATION_SECONDS, WORKFLOW_HITL_MAX_DURATION_SECONDS, SECRET_KEY_RE, isZod, defined, templateText, assertNoClosure, assertPredicate, assertRetry, assertTimeout, envRefKeys, refToDescriptor, __workflowCommitHook, LuaWorkflow, WorkflowBuilderImpl, EDITABLE_PATH_RE2;
|
|
6097
|
+
var init2, state2, lit2, eq2, ne2, gt2, gte2, lt2, lte2, inSet2, notIn2, exists2, notExists2, truthy2, falsy2, and2, or2, not2, fromInit2, fromStep2, value2, template2, fromRequest2, rows2, fromKnowledge2, LuaWorkflowBuildError, STEP_ID_RE, WORKFLOW_NAME_RE, WORKFLOW_MAX_PARALLEL_ARMS, WORKFLOW_MAX_FOREACH_CONCURRENCY, WORKFLOW_MAX_FOREACH_ITEMS, WORKFLOW_WORKER_MAX_TIMEOUT_SECONDS, WORKFLOW_JOB_SEGMENT_MAX_SECONDS, WORKFLOW_JOB_MAX_TIMEOUT_SECONDS, WORKFLOW_LOOP_INTERVAL_MAX_SECONDS, WORKFLOW_FOREACH_RATE_MAX_PER_SECOND, WORKFLOW_SPECIALIST_ROLE_MAX_INSTRUCTIONS, WORKFLOW_DEFAULT_MAX_DURATION_SECONDS, WORKFLOW_HITL_MAX_DURATION_SECONDS, SECRET_KEY_RE, isZod, defined, templateText, assertNoClosure, assertPredicate, assertRetry, assertTimeout, envRefKeys, refToDescriptor, __workflowCommitHook, LuaWorkflow, isHitlEntry, WorkflowBuilderImpl, EDITABLE_PATH_RE2;
|
|
4986
6098
|
var init_workflow = __esm({
|
|
4987
6099
|
"src/types/workflow.ts"() {
|
|
4988
6100
|
"use strict";
|
|
6101
|
+
init_dist();
|
|
4989
6102
|
init_dist2();
|
|
4990
6103
|
__name(createStep, "createStep");
|
|
4991
6104
|
__name(step2, "step");
|
|
@@ -5068,7 +6181,7 @@ var init_workflow = __esm({
|
|
|
5068
6181
|
}, "assertPredicate");
|
|
5069
6182
|
assertRetry = /* @__PURE__ */ __name((r, id) => {
|
|
5070
6183
|
if (!r) return;
|
|
5071
|
-
if (r.backoff !== void 0 &&
|
|
6184
|
+
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(" | ")}`);
|
|
5072
6185
|
if (r.maxBackoffSeconds !== void 0) {
|
|
5073
6186
|
if (r.backoff !== "exponential") throw new LuaWorkflowBuildError("backoff-invalid", `"${id}": retry.maxBackoffSeconds is only meaningful with backoff:'exponential'`);
|
|
5074
6187
|
if (r.maxBackoffSeconds <= 0 || r.backoffSeconds !== void 0 && r.maxBackoffSeconds < r.backoffSeconds) throw new LuaWorkflowBuildError("backoff-invalid", `"${id}": retry.maxBackoffSeconds must be > 0 and \u2265 backoffSeconds`);
|
|
@@ -5154,7 +6267,7 @@ var init_workflow = __esm({
|
|
|
5154
6267
|
getEnvTemplateKeys() {
|
|
5155
6268
|
return this.built.envTemplateKeys;
|
|
5156
6269
|
}
|
|
5157
|
-
/** `.workflow(id, ref)` targets by name — `ManifestWorkflow.workflowRefs
|
|
6270
|
+
/** `.workflow(id, ref)` targets by name — `ManifestWorkflow.workflowRefs`; `workspace:'inherit'` marks the inherit children the compiler defers `workspace-not-declared` for. */
|
|
5158
6271
|
getNestedWorkflowRefs() {
|
|
5159
6272
|
return this.built.nestedRefs;
|
|
5160
6273
|
}
|
|
@@ -5184,11 +6297,13 @@ var init_workflow = __esm({
|
|
|
5184
6297
|
};
|
|
5185
6298
|
if (cfg.concurrencyPolicy !== void 0) envelope.concurrencyPolicy = cfg.concurrencyPolicy;
|
|
5186
6299
|
if (cfg.workspace !== void 0) envelope.workspace = cfg.workspace;
|
|
6300
|
+
if (cfg.connections !== void 0) envelope.connections = cfg.connections;
|
|
5187
6301
|
return withDefaultsFilled(envelope);
|
|
5188
6302
|
}
|
|
5189
6303
|
};
|
|
5190
6304
|
__name(stepNodeOf, "stepNodeOf");
|
|
5191
6305
|
__name(materializeEntry, "materializeEntry");
|
|
6306
|
+
isHitlEntry = /* @__PURE__ */ __name((n2) => isWorkflowHitlEntryType(n2?.type), "isHitlEntry");
|
|
5192
6307
|
__name(graphHasHitl, "graphHasHitl");
|
|
5193
6308
|
WorkflowBuilderImpl = class WorkflowBuilderImpl2 {
|
|
5194
6309
|
static {
|
|
@@ -5219,7 +6334,6 @@ var init_workflow = __esm({
|
|
|
5219
6334
|
if (this.steps[s.id] && this.steps[s.id] !== s) throw new LuaWorkflowBuildError("duplicate-step-id", `step id "${s.id}" is declared twice`);
|
|
5220
6335
|
const tier = s.tier ?? (s.workspace ? "job" : void 0);
|
|
5221
6336
|
if (s.workspace && s.tier !== void 0 && s.tier !== "job") throw new LuaWorkflowBuildError("workspace-requires-job-tier", `"${s.id}": a step mounting a workspace must be tier:'job'`);
|
|
5222
|
-
if (s.workspace && !this.config.workspace) throw new LuaWorkflowBuildError("workspace-not-declared", `"${s.id}" mounts a workspace but createWorkflow declares none`);
|
|
5223
6337
|
if (s.jobTools && tier !== "job") throw new LuaWorkflowBuildError("cap-exceeded", `"${s.id}": jobTools require tier:'job' (job-tools-require-job-tier)`);
|
|
5224
6338
|
assertTimeout({
|
|
5225
6339
|
id: s.id,
|
|
@@ -5242,7 +6356,6 @@ var init_workflow = __esm({
|
|
|
5242
6356
|
const [mapping, target] = arm;
|
|
5243
6357
|
if (target === void 0 || arm.length < 2) throw new LuaWorkflowBuildError("container-arm-empty", `${where}: a bare mapping arm has nothing to run`);
|
|
5244
6358
|
if (!mapping || typeof mapping !== "object" || Array.isArray(mapping)) throw new LuaWorkflowBuildError("mapping-placement", `${where}: the arm head must be a map config object`);
|
|
5245
|
-
if (mapping.type === "approval" || target.type === "approval") throw new LuaWorkflowBuildError("approval-inside-container", `${where}: approval / waitForSignal are top-level only in v1`);
|
|
5246
6359
|
assertNoClosure(mapping, `${where} arm map`);
|
|
5247
6360
|
this.recordEnvRefs(mapping);
|
|
5248
6361
|
const inner = this.armRef(target, where);
|
|
@@ -5260,8 +6373,9 @@ var init_workflow = __esm({
|
|
|
5260
6373
|
if (typeof arm === "string") return {
|
|
5261
6374
|
ref: arm
|
|
5262
6375
|
};
|
|
5263
|
-
if (arm && typeof arm === "object" && arm
|
|
5264
|
-
|
|
6376
|
+
if (arm && typeof arm === "object" && isHitlEntry(arm)) {
|
|
6377
|
+
const id = arm.id;
|
|
6378
|
+
throw new LuaWorkflowBuildError("invalid-step", `${where}: an approval / waitForSignal arm is placed by id \u2014 declare it with .approval(${JSON.stringify(id ?? "id")}, \u2026) / .waitForSignal(\u2026) and pass the id string`);
|
|
5265
6379
|
}
|
|
5266
6380
|
return {
|
|
5267
6381
|
node: this.registerStep(arm, where)
|
|
@@ -5460,12 +6574,15 @@ var init_workflow = __esm({
|
|
|
5460
6574
|
}
|
|
5461
6575
|
const tier = opts.tier ?? (opts.workspace ? "job" : void 0);
|
|
5462
6576
|
if (opts.workspace && opts.tier !== void 0 && opts.tier !== "job") throw new LuaWorkflowBuildError("workspace-requires-job-tier", `"${id}": a step mounting a workspace must be tier:'job'`);
|
|
5463
|
-
if (opts.workspace && !this.config.workspace) throw new LuaWorkflowBuildError("workspace-not-declared", `"${id}" mounts a workspace but createWorkflow declares none`);
|
|
5464
6577
|
if (opts.harness !== void 0 && tier !== "job") throw new LuaWorkflowBuildError("harness-requires-job-tier", `"${id}": harness is only legal on a tier:'job' agent step`);
|
|
5465
6578
|
if (opts.toolScope?.jobTools && tier !== "job") throw new LuaWorkflowBuildError("cap-exceeded", `"${id}": toolScope.jobTools require tier:'job' (job-tools-require-job-tier)`);
|
|
5466
|
-
|
|
5467
|
-
if (
|
|
5468
|
-
if (
|
|
6579
|
+
for (const m of WORKFLOW_JOB_RANGE_MEMBERS) {
|
|
6580
|
+
if (opts[m] === void 0) continue;
|
|
6581
|
+
if (tier !== "job") throw new LuaWorkflowBuildError("max-turns-requires-job-tier", `"${id}": ${m} is only legal on a tier:'job' agent step`);
|
|
6582
|
+
if (!isWithinWorkflowJobRange(m, opts[m])) {
|
|
6583
|
+
const { min, max } = WORKFLOW_JOB_RANGES[m];
|
|
6584
|
+
throw new LuaWorkflowBuildError("max-turns-invalid", `"${id}": ${m} must be an integer ${min}..${max}`);
|
|
6585
|
+
}
|
|
5469
6586
|
}
|
|
5470
6587
|
assertTimeout({
|
|
5471
6588
|
id,
|
|
@@ -5493,7 +6610,9 @@ var init_workflow = __esm({
|
|
|
5493
6610
|
workspace: opts.workspace,
|
|
5494
6611
|
jobResources: opts.jobResources,
|
|
5495
6612
|
harness: opts.harness,
|
|
5496
|
-
maxTurns: opts.maxTurns
|
|
6613
|
+
maxTurns: opts.maxTurns,
|
|
6614
|
+
maxMessages: opts.maxMessages,
|
|
6615
|
+
maxInputTokens: opts.maxInputTokens
|
|
5497
6616
|
});
|
|
5498
6617
|
return this.push({
|
|
5499
6618
|
kind: "declare",
|
|
@@ -5615,8 +6734,8 @@ var init_workflow = __esm({
|
|
|
5615
6734
|
itemTimeout: opts.itemTimeout
|
|
5616
6735
|
});
|
|
5617
6736
|
return this.push({
|
|
5618
|
-
kind: "
|
|
5619
|
-
|
|
6737
|
+
kind: "declare",
|
|
6738
|
+
node
|
|
5620
6739
|
});
|
|
5621
6740
|
}
|
|
5622
6741
|
waitForSignal(id, opts) {
|
|
@@ -5634,12 +6753,13 @@ var init_workflow = __esm({
|
|
|
5634
6753
|
acceptedSources: opts.acceptedSources
|
|
5635
6754
|
});
|
|
5636
6755
|
return this.push({
|
|
5637
|
-
kind: "
|
|
5638
|
-
|
|
6756
|
+
kind: "declare",
|
|
6757
|
+
node
|
|
5639
6758
|
});
|
|
5640
6759
|
}
|
|
5641
6760
|
workflow(id, ref, input, opts) {
|
|
5642
6761
|
this.assertId(id, "workflow()");
|
|
6762
|
+
assertRetry(opts?.retry, id);
|
|
5643
6763
|
const name = typeof ref === "string" ? ref : ref instanceof LuaWorkflow ? ref.getName() : void 0;
|
|
5644
6764
|
if (!name) throw new LuaWorkflowBuildError("invalid-envelope", `workflow("${id}") needs a LuaWorkflow or a workflow name`);
|
|
5645
6765
|
if (opts?.workspace === "inherit") {
|
|
@@ -5648,7 +6768,11 @@ var init_workflow = __esm({
|
|
|
5648
6768
|
}
|
|
5649
6769
|
assertNoClosure(input, `workflow("${id}").input`);
|
|
5650
6770
|
this.recordEnvRefs(input);
|
|
5651
|
-
this.nestedRefs.push({
|
|
6771
|
+
this.nestedRefs.push(opts?.workspace === "inherit" ? {
|
|
6772
|
+
id,
|
|
6773
|
+
name,
|
|
6774
|
+
workspace: "inherit"
|
|
6775
|
+
} : {
|
|
5652
6776
|
id,
|
|
5653
6777
|
name
|
|
5654
6778
|
});
|
|
@@ -5657,11 +6781,12 @@ var init_workflow = __esm({
|
|
|
5657
6781
|
id,
|
|
5658
6782
|
workflowId: name,
|
|
5659
6783
|
input,
|
|
5660
|
-
workspace: opts?.workspace
|
|
6784
|
+
workspace: opts?.workspace,
|
|
6785
|
+
retry: opts?.retry
|
|
5661
6786
|
});
|
|
5662
6787
|
return this.push({
|
|
5663
|
-
kind: "
|
|
5664
|
-
|
|
6788
|
+
kind: "declare",
|
|
6789
|
+
node
|
|
5665
6790
|
});
|
|
5666
6791
|
}
|
|
5667
6792
|
commit() {
|
|
@@ -5671,7 +6796,7 @@ var init_workflow = __esm({
|
|
|
5671
6796
|
const { graph, issues } = resolvePlacements(this.calls);
|
|
5672
6797
|
const fatal = issues[0];
|
|
5673
6798
|
if (fatal) {
|
|
5674
|
-
const hint = fatal.code === "unknown-step-ref" ? "a string StepRef must name an entry declared by agentStep/specialistStep/toolStep/map(\u2026, { id }) somewhere in the chain \u2014 before OR after the reference" : void 0;
|
|
6799
|
+
const hint = fatal.code === "unknown-step-ref" ? "a string StepRef must name an entry declared by agentStep/specialistStep/toolStep/map(\u2026, { id })/workflow(\u2026)/approval(\u2026)/waitForSignal(\u2026) somewhere in the chain \u2014 before OR after the reference" : void 0;
|
|
5675
6800
|
throw new LuaWorkflowBuildError(fatal.code, fatal.message, hint);
|
|
5676
6801
|
}
|
|
5677
6802
|
if (graph.length === 0) throw new LuaWorkflowBuildError("empty-graph", `workflow "${this.config.name}" has no entries`);
|
|
@@ -5684,7 +6809,8 @@ var init_workflow = __esm({
|
|
|
5684
6809
|
if (graphHasHitl(graph, this.steps) && this.config.budget?.maxDurationSeconds === void 0) {
|
|
5685
6810
|
this.warnings.push({
|
|
5686
6811
|
code: "hitl-duration-defaulted",
|
|
5687
|
-
|
|
6812
|
+
// LUA-668: names the workflow — two HITL workflows in one project printed two identical lines that read as a duplicate.
|
|
6813
|
+
message: `workflow "${this.config.name}": budget.maxDurationSeconds defaulted to ${WORKFLOW_HITL_MAX_DURATION_SECONDS} s (30 d) because the graph contains an approval / waitForSignal / suspend-capable step \u2014 set it explicitly to silence`
|
|
5688
6814
|
});
|
|
5689
6815
|
}
|
|
5690
6816
|
this.checkScheduleInput();
|
|
@@ -6068,7 +7194,7 @@ var init_firebase_session_store = __esm({
|
|
|
6068
7194
|
}), "currentFirebaseSessionEnvironment");
|
|
6069
7195
|
__name(environmentKey, "environmentKey");
|
|
6070
7196
|
__name(isMissing, "isMissing");
|
|
6071
|
-
wait = /* @__PURE__ */ __name((milliseconds) => new Promise((
|
|
7197
|
+
wait = /* @__PURE__ */ __name((milliseconds) => new Promise((resolve3) => setTimeout(resolve3, milliseconds)), "wait");
|
|
6072
7198
|
FirebaseSessionStore = class {
|
|
6073
7199
|
static {
|
|
6074
7200
|
__name(this, "FirebaseSessionStore");
|
|
@@ -6336,13 +7462,13 @@ async function* parseSseStream(body, signal) {
|
|
|
6336
7462
|
buffer += decoder.decode(value3, {
|
|
6337
7463
|
stream: true
|
|
6338
7464
|
});
|
|
6339
|
-
let
|
|
6340
|
-
while (
|
|
6341
|
-
const block = buffer.slice(0,
|
|
6342
|
-
buffer = buffer.slice(
|
|
7465
|
+
let sep4 = buffer.search(/\r?\n\r?\n/);
|
|
7466
|
+
while (sep4 !== -1) {
|
|
7467
|
+
const block = buffer.slice(0, sep4);
|
|
7468
|
+
buffer = buffer.slice(sep4).replace(/^\r?\n\r?\n/, "");
|
|
6343
7469
|
const frame = flush(block);
|
|
6344
7470
|
if (frame) yield frame;
|
|
6345
|
-
|
|
7471
|
+
sep4 = buffer.search(/\r?\n\r?\n/);
|
|
6346
7472
|
}
|
|
6347
7473
|
}
|
|
6348
7474
|
if (buffer.trim()) {
|
|
@@ -6561,7 +7687,7 @@ Check that your Lua login has access to this agent or organization.`);
|
|
|
6561
7687
|
if (attempt < maxRetries) {
|
|
6562
7688
|
const serverDelay = Number(lastResult?.error?.retryAfterSeconds ?? 0) * 1e3;
|
|
6563
7689
|
const backoff = Math.max(this.calculateBackoff(attempt), serverDelay);
|
|
6564
|
-
await new Promise((
|
|
7690
|
+
await new Promise((resolve3) => setTimeout(resolve3, backoff));
|
|
6565
7691
|
}
|
|
6566
7692
|
}
|
|
6567
7693
|
return lastResult;
|
|
@@ -6610,7 +7736,7 @@ Check that your Lua login has access to this agent or organization.`);
|
|
|
6610
7736
|
async httpPostCoreDrainRetry(url, data, headers) {
|
|
6611
7737
|
const first = await this.httpPostOnce(url, data, headers);
|
|
6612
7738
|
if (!isCoreDrainApiError(first.error)) return first;
|
|
6613
|
-
await new Promise((
|
|
7739
|
+
await new Promise((resolve3) => setTimeout(resolve3, coreDrainApiRetryDelayMs(first.error)));
|
|
6614
7740
|
return this.httpPostOnce(url, data, headers);
|
|
6615
7741
|
}
|
|
6616
7742
|
/**
|
|
@@ -6984,11 +8110,560 @@ var init_artifact_loader = __esm({
|
|
|
6984
8110
|
}
|
|
6985
8111
|
});
|
|
6986
8112
|
|
|
8113
|
+
// ../shared-source-sync/dist/index.mjs
|
|
8114
|
+
import { createHash as createHash4 } from "crypto";
|
|
8115
|
+
import { extname } from "path";
|
|
8116
|
+
import { readdirSync, readFileSync as readFileSync3, statSync } from "fs";
|
|
8117
|
+
import { join as join4, sep } from "path";
|
|
8118
|
+
import { gunzipSync, gzipSync } from "zlib";
|
|
8119
|
+
import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync22, statSync as statSync2, writeFileSync } from "fs";
|
|
8120
|
+
import { dirname as dirname2, join as join22, resolve, sep as sep2 } from "path";
|
|
8121
|
+
import { posix } from "path";
|
|
8122
|
+
import { mkdirSync as mkdirSync2, readdirSync as readdirSync2, readFileSync as readFileSync32, statSync as statSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
8123
|
+
import { dirname as dirname22, join as join32, resolve as resolve2, sep as sep3 } from "path";
|
|
8124
|
+
import { gunzipSync as gunzipSync2, gzipSync as gzipSync2 } from "zlib";
|
|
8125
|
+
function hashContentTruncated(content) {
|
|
8126
|
+
return createHash4("sha256").update(content).digest("hex").slice(0, FILE_HASH_LENGTH);
|
|
8127
|
+
}
|
|
8128
|
+
function sha256Hex(content) {
|
|
8129
|
+
return createHash4("sha256").update(content).digest("hex");
|
|
8130
|
+
}
|
|
8131
|
+
function matchesFileHash(hash, plaintext) {
|
|
8132
|
+
if (!/^(?:[a-f0-9]{16}|[a-f0-9]{64})$/.test(hash)) return false;
|
|
8133
|
+
if (sha256Hex(plaintext).startsWith(hash)) return true;
|
|
8134
|
+
return sha256Hex(plaintext.toString("utf-8")).startsWith(hash);
|
|
8135
|
+
}
|
|
8136
|
+
function combineFileHashes(files) {
|
|
8137
|
+
const sorted = [
|
|
8138
|
+
...files
|
|
8139
|
+
].sort((a, b) => a.relativePath.localeCompare(b.relativePath));
|
|
8140
|
+
return sha256Hex(sorted.map((f) => f.hash).join("|"));
|
|
8141
|
+
}
|
|
8142
|
+
function shouldSkipDirectory(name) {
|
|
8143
|
+
return SKIP_DIRECTORIES.has(name);
|
|
8144
|
+
}
|
|
8145
|
+
function shouldSkipFile(filePathOrName) {
|
|
8146
|
+
return filePathOrName.includes("node_modules") || filePathOrName.includes(".test.") || filePathOrName.includes(".spec.") || filePathOrName.includes("__tests__") || filePathOrName.endsWith(".d.ts") || filePathOrName.endsWith(".map") || filePathOrName.startsWith(".") || filePathOrName === "package-lock.json" || filePathOrName === "yarn.lock" || filePathOrName === "pnpm-lock.yaml";
|
|
8147
|
+
}
|
|
8148
|
+
function classifyFile(relPath) {
|
|
8149
|
+
return KIND_BY_EXT[extname(relPath)] ?? "other";
|
|
8150
|
+
}
|
|
8151
|
+
function walkWorkspace(rootDir, opts = {}) {
|
|
8152
|
+
const maxBytes = opts.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES;
|
|
8153
|
+
const refs = [];
|
|
8154
|
+
const contentByHash = /* @__PURE__ */ new Map();
|
|
8155
|
+
let totalSize = 0;
|
|
8156
|
+
const visit = /* @__PURE__ */ __name4((relPrefix) => {
|
|
8157
|
+
const absDir = relPrefix ? join4(rootDir, relPrefix) : rootDir;
|
|
8158
|
+
let entries;
|
|
8159
|
+
try {
|
|
8160
|
+
entries = readdirSync(absDir, {
|
|
8161
|
+
withFileTypes: true
|
|
8162
|
+
});
|
|
8163
|
+
} catch {
|
|
8164
|
+
return;
|
|
8165
|
+
}
|
|
8166
|
+
for (const entry of entries) {
|
|
8167
|
+
if (entry.isDirectory()) {
|
|
8168
|
+
if (shouldSkipDirectory(entry.name)) continue;
|
|
8169
|
+
if (shouldSkipFile(entry.name)) continue;
|
|
8170
|
+
visit(relPrefix ? `${relPrefix}${sep}${entry.name}` : entry.name);
|
|
8171
|
+
continue;
|
|
8172
|
+
}
|
|
8173
|
+
if (!entry.isFile()) continue;
|
|
8174
|
+
if (shouldSkipFile(entry.name)) continue;
|
|
8175
|
+
const rel = (relPrefix ? `${relPrefix}${sep}${entry.name}` : entry.name).split(sep).join("/");
|
|
8176
|
+
const abs = join4(rootDir, rel);
|
|
8177
|
+
let stats;
|
|
8178
|
+
try {
|
|
8179
|
+
stats = statSync(abs);
|
|
8180
|
+
} catch {
|
|
8181
|
+
continue;
|
|
8182
|
+
}
|
|
8183
|
+
if (stats.size > maxBytes) continue;
|
|
8184
|
+
let content;
|
|
8185
|
+
try {
|
|
8186
|
+
content = readFileSync3(abs);
|
|
8187
|
+
} catch {
|
|
8188
|
+
continue;
|
|
8189
|
+
}
|
|
8190
|
+
const hash = hashContentTruncated(content.toString("utf-8"));
|
|
8191
|
+
refs.push({
|
|
8192
|
+
relativePath: rel,
|
|
8193
|
+
hash,
|
|
8194
|
+
size: stats.size,
|
|
8195
|
+
type: classifyFile(rel)
|
|
8196
|
+
});
|
|
8197
|
+
if (!contentByHash.has(hash)) contentByHash.set(hash, content);
|
|
8198
|
+
totalSize += stats.size;
|
|
8199
|
+
}
|
|
8200
|
+
}, "visit");
|
|
8201
|
+
visit("");
|
|
8202
|
+
refs.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
|
|
8203
|
+
const projectHash = combineFileHashes(refs);
|
|
8204
|
+
return {
|
|
8205
|
+
files: refs,
|
|
8206
|
+
projectHash,
|
|
8207
|
+
totalSize,
|
|
8208
|
+
contentByHash
|
|
8209
|
+
};
|
|
8210
|
+
}
|
|
8211
|
+
async function uploadBlobs(opts) {
|
|
8212
|
+
const fetchFn = opts.fetch ?? fetch;
|
|
8213
|
+
const concurrency = opts.concurrency ?? DEFAULT_CONCURRENCY;
|
|
8214
|
+
const hashes = Object.keys(opts.uploadUrls);
|
|
8215
|
+
for (let i = 0; i < hashes.length; i += concurrency) {
|
|
8216
|
+
const batch = hashes.slice(i, i + concurrency);
|
|
8217
|
+
await Promise.all(batch.map(async (hash) => {
|
|
8218
|
+
const buf = opts.contentByHash.get(hash);
|
|
8219
|
+
if (!buf) {
|
|
8220
|
+
throw new Error(`uploadBlobs: no content for hash ${hash.slice(0, 12)}\u2026`);
|
|
8221
|
+
}
|
|
8222
|
+
const compressed = gzipSync(buf);
|
|
8223
|
+
const res = await fetchFn(opts.uploadUrls[hash], {
|
|
8224
|
+
method: "PUT",
|
|
8225
|
+
body: compressed,
|
|
8226
|
+
headers: {
|
|
8227
|
+
"Content-Type": "application/octet-stream"
|
|
8228
|
+
}
|
|
8229
|
+
});
|
|
8230
|
+
if (!res.ok) {
|
|
8231
|
+
throw new Error(`S3 upload failed for ${hash.slice(0, 12)}\u2026: ${res.status} ${res.statusText}`);
|
|
8232
|
+
}
|
|
8233
|
+
}));
|
|
8234
|
+
}
|
|
8235
|
+
return hashes.length;
|
|
8236
|
+
}
|
|
8237
|
+
function decodeBlob(buf) {
|
|
8238
|
+
if (buf.length >= 2 && buf[0] === 31 && buf[1] === 139) {
|
|
8239
|
+
try {
|
|
8240
|
+
return gunzipSync(buf);
|
|
8241
|
+
} catch {
|
|
8242
|
+
return buf;
|
|
8243
|
+
}
|
|
8244
|
+
}
|
|
8245
|
+
return buf;
|
|
8246
|
+
}
|
|
8247
|
+
function verifyDownloaded(hash, plaintext) {
|
|
8248
|
+
if (!matchesFileHash(hash, plaintext)) {
|
|
8249
|
+
throw new Error(`Blob ${hash.slice(0, 12)}\u2026 failed integrity verification: its content does not hash to its key (refusing to restore it)`);
|
|
8250
|
+
}
|
|
8251
|
+
return plaintext;
|
|
8252
|
+
}
|
|
8253
|
+
async function downloadBlobs(opts) {
|
|
8254
|
+
const fetchFn = opts.fetch ?? fetch;
|
|
8255
|
+
const concurrency = opts.concurrency ?? DEFAULT_CONCURRENCY;
|
|
8256
|
+
const out = /* @__PURE__ */ new Map();
|
|
8257
|
+
const entries = Object.entries(opts.urls);
|
|
8258
|
+
for (let i = 0; i < entries.length; i += concurrency) {
|
|
8259
|
+
const batch = entries.slice(i, i + concurrency);
|
|
8260
|
+
const downloaded = await Promise.all(batch.map(async ([hash, url]) => {
|
|
8261
|
+
const res = await fetchFn(url);
|
|
8262
|
+
if (!res.ok) {
|
|
8263
|
+
throw new Error(`S3 download failed for ${hash.slice(0, 12)}\u2026: ${res.status} ${res.statusText}`);
|
|
8264
|
+
}
|
|
8265
|
+
const ab = await res.arrayBuffer();
|
|
8266
|
+
const buf = Buffer.from(ab);
|
|
8267
|
+
return [
|
|
8268
|
+
hash,
|
|
8269
|
+
verifyDownloaded(hash, decodeBlob(buf))
|
|
8270
|
+
];
|
|
8271
|
+
}));
|
|
8272
|
+
for (const [hash, buf] of downloaded) out.set(hash, buf);
|
|
8273
|
+
}
|
|
8274
|
+
return out;
|
|
8275
|
+
}
|
|
8276
|
+
function resolveBackupFileTarget(file, targetDir) {
|
|
8277
|
+
const base = resolve(targetDir);
|
|
8278
|
+
const sandboxed = file.external ? join22(base, ".lua", "external", file.relativePath) : join22(base, file.relativePath);
|
|
8279
|
+
const resolved = resolve(sandboxed);
|
|
8280
|
+
if (resolved !== base && !resolved.startsWith(base + sep2)) {
|
|
8281
|
+
throw new Error(`Backup entry escapes target directory: ${file.relativePath}`);
|
|
8282
|
+
}
|
|
8283
|
+
return resolved;
|
|
8284
|
+
}
|
|
8285
|
+
function restoreFromBlobs(manifest, blobs, targetDir, opts = {}) {
|
|
8286
|
+
let filesWritten = 0;
|
|
8287
|
+
let filesUnchanged = 0;
|
|
8288
|
+
let filesSkipped = 0;
|
|
8289
|
+
for (const file of manifest.files) {
|
|
8290
|
+
const content = blobs.get(file.hash);
|
|
8291
|
+
if (!content) {
|
|
8292
|
+
throw new Error(`Blob not found for hash: ${file.hash} (${file.relativePath})`);
|
|
8293
|
+
}
|
|
8294
|
+
const targetPath = resolveBackupFileTarget(file, targetDir);
|
|
8295
|
+
if (existsSync2(targetPath)) {
|
|
8296
|
+
const sameSize = statSync2(targetPath).size === content.length;
|
|
8297
|
+
if (sameSize && readFileSync22(targetPath).equals(content)) {
|
|
8298
|
+
filesUnchanged++;
|
|
8299
|
+
continue;
|
|
8300
|
+
}
|
|
8301
|
+
if (!opts.overwrite) {
|
|
8302
|
+
filesSkipped++;
|
|
8303
|
+
continue;
|
|
8304
|
+
}
|
|
8305
|
+
}
|
|
8306
|
+
mkdirSync(dirname2(targetPath), {
|
|
8307
|
+
recursive: true
|
|
8308
|
+
});
|
|
8309
|
+
writeFileSync(targetPath, content);
|
|
8310
|
+
filesWritten++;
|
|
8311
|
+
}
|
|
8312
|
+
return {
|
|
8313
|
+
filesWritten,
|
|
8314
|
+
filesUnchanged,
|
|
8315
|
+
filesSkipped
|
|
8316
|
+
};
|
|
8317
|
+
}
|
|
8318
|
+
function normalizeWorkspaceRelativePath(value3) {
|
|
8319
|
+
if (!value3 || value3.includes("\0")) return void 0;
|
|
8320
|
+
const withPortableSeparators = value3.replace(/\\/g, "/");
|
|
8321
|
+
if (withPortableSeparators.startsWith("/") || WINDOWS_ABSOLUTE_PATH.test(withPortableSeparators)) return void 0;
|
|
8322
|
+
const normalized = posix.normalize(withPortableSeparators);
|
|
8323
|
+
if (!normalized || normalized === "." || normalized === ".." || normalized.startsWith("../")) return void 0;
|
|
8324
|
+
return normalized;
|
|
8325
|
+
}
|
|
8326
|
+
function isCredentialPersistencePath(value3) {
|
|
8327
|
+
const normalized = normalizeWorkspaceRelativePath(value3);
|
|
8328
|
+
return normalized !== void 0 && CREDENTIAL_PATHS.has(normalized.toLowerCase());
|
|
8329
|
+
}
|
|
8330
|
+
async function pushAgentBackup(opts) {
|
|
8331
|
+
const snapshot = walkWorkspace(opts.workspaceDir, opts.walkOptions);
|
|
8332
|
+
const client = new BackupHttpClient(opts.http);
|
|
8333
|
+
const uniqueHashes = [
|
|
8334
|
+
...new Set(snapshot.files.map((f) => f.hash))
|
|
8335
|
+
];
|
|
8336
|
+
let filesUploaded = 0;
|
|
8337
|
+
if (uniqueHashes.length > 0) {
|
|
8338
|
+
const checked = await client.checkBlobsExist(uniqueHashes);
|
|
8339
|
+
if (checked.missing.length > 0) {
|
|
8340
|
+
const urls = await client.getBlobUploadUrls(checked.missing);
|
|
8341
|
+
filesUploaded = await uploadBlobs({
|
|
8342
|
+
uploadUrls: urls.urls,
|
|
8343
|
+
contentByHash: snapshot.contentByHash,
|
|
8344
|
+
fetch: opts.http.fetch,
|
|
8345
|
+
concurrency: opts.concurrency
|
|
8346
|
+
});
|
|
8347
|
+
}
|
|
8348
|
+
}
|
|
8349
|
+
const metadata = await client.saveManifest({
|
|
8350
|
+
projectHash: snapshot.projectHash,
|
|
8351
|
+
files: snapshot.files,
|
|
8352
|
+
version: opts.version,
|
|
8353
|
+
orgId: opts.orgId,
|
|
8354
|
+
createdBy: opts.createdBy ?? "cli",
|
|
8355
|
+
triggeredBy: opts.triggeredBy
|
|
8356
|
+
});
|
|
8357
|
+
return {
|
|
8358
|
+
projectHash: snapshot.projectHash,
|
|
8359
|
+
fileCount: snapshot.files.length,
|
|
8360
|
+
filesUploaded,
|
|
8361
|
+
activeVersion: metadata.activeVersion,
|
|
8362
|
+
metadata,
|
|
8363
|
+
files: snapshot.files
|
|
8364
|
+
};
|
|
8365
|
+
}
|
|
8366
|
+
async function pullAgentBackup(opts) {
|
|
8367
|
+
const client = new BackupHttpClient(opts.http);
|
|
8368
|
+
const manifest = await client.getManifest();
|
|
8369
|
+
const uniqueHashes = [
|
|
8370
|
+
...new Set(manifest.files.map((f) => f.hash))
|
|
8371
|
+
];
|
|
8372
|
+
const blobs = uniqueHashes.length ? await (async () => {
|
|
8373
|
+
const urls = await client.getBlobUrls(uniqueHashes);
|
|
8374
|
+
return downloadBlobs({
|
|
8375
|
+
urls: urls.urls,
|
|
8376
|
+
fetch: opts.http.fetch,
|
|
8377
|
+
concurrency: opts.concurrency
|
|
8378
|
+
});
|
|
8379
|
+
})() : /* @__PURE__ */ new Map();
|
|
8380
|
+
const result = restoreFromBlobs(manifest, blobs, opts.targetDir, opts.restore);
|
|
8381
|
+
return {
|
|
8382
|
+
...result,
|
|
8383
|
+
manifest
|
|
8384
|
+
};
|
|
8385
|
+
}
|
|
8386
|
+
function encodeWorkspaceArchive(workspaceDir) {
|
|
8387
|
+
const files = {};
|
|
8388
|
+
try {
|
|
8389
|
+
walk2(workspaceDir, "", files);
|
|
8390
|
+
} catch {
|
|
8391
|
+
return null;
|
|
8392
|
+
}
|
|
8393
|
+
if (Object.keys(files).length === 0) return null;
|
|
8394
|
+
const gz = gzipSync2(Buffer.from(JSON.stringify(files), "utf8"));
|
|
8395
|
+
return gz.toString("base64");
|
|
8396
|
+
}
|
|
8397
|
+
function decodeWorkspaceArchive(archive, schemaVersion) {
|
|
8398
|
+
if (schemaVersion !== void 0 && schemaVersion !== ARCHIVE_SCHEMA_VERSION) {
|
|
8399
|
+
throw new Error(`Unsupported archive schema version: ${schemaVersion} (expected ${ARCHIVE_SCHEMA_VERSION})`);
|
|
8400
|
+
}
|
|
8401
|
+
const raw = gunzipSync2(Buffer.from(archive, "base64")).toString("utf8");
|
|
8402
|
+
const parsed = JSON.parse(raw);
|
|
8403
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
8404
|
+
throw new Error("Decoded archive is not a JSON object map");
|
|
8405
|
+
}
|
|
8406
|
+
return parsed;
|
|
8407
|
+
}
|
|
8408
|
+
function writeArchiveToWorkspace(workspaceDir, files) {
|
|
8409
|
+
const writes = [];
|
|
8410
|
+
for (const [relPath, content] of Object.entries(files)) {
|
|
8411
|
+
const normalized = normalizeWorkspaceRelativePath(relPath);
|
|
8412
|
+
if (!normalized) throw new Error(`Refusing to write invalid archived path: ${relPath}`);
|
|
8413
|
+
if (isCredentialPersistencePath(normalized)) {
|
|
8414
|
+
throw new Error(`Refusing to restore credential file: ${normalized}`);
|
|
8415
|
+
}
|
|
8416
|
+
writes.push({
|
|
8417
|
+
path: resolve2(workspaceDir, normalized),
|
|
8418
|
+
content
|
|
8419
|
+
});
|
|
8420
|
+
}
|
|
8421
|
+
for (const write of writes) {
|
|
8422
|
+
mkdirSync2(dirname22(write.path), {
|
|
8423
|
+
recursive: true
|
|
8424
|
+
});
|
|
8425
|
+
writeFileSync2(write.path, write.content, "utf8");
|
|
8426
|
+
}
|
|
8427
|
+
}
|
|
8428
|
+
function walk2(root, prefix, out) {
|
|
8429
|
+
const dir = prefix ? join32(root, prefix) : root;
|
|
8430
|
+
const entries = readdirSync2(dir, {
|
|
8431
|
+
withFileTypes: true
|
|
8432
|
+
});
|
|
8433
|
+
for (const entry of entries) {
|
|
8434
|
+
if (entry.isDirectory()) {
|
|
8435
|
+
if (shouldSkipDirectory(entry.name) || ARCHIVE_ONLY_SKIP_DIRECTORIES.has(entry.name) || shouldSkipFile(entry.name)) continue;
|
|
8436
|
+
walk2(root, prefix ? `${prefix}${sep3}${entry.name}` : entry.name, out);
|
|
8437
|
+
continue;
|
|
8438
|
+
}
|
|
8439
|
+
if (!entry.isFile()) continue;
|
|
8440
|
+
if (shouldSkipFile(entry.name)) continue;
|
|
8441
|
+
const relPath = (prefix ? `${prefix}${sep3}${entry.name}` : entry.name).split(sep3).join("/");
|
|
8442
|
+
const abs = join32(dir, entry.name);
|
|
8443
|
+
if (statSync3(abs).size > DEFAULT_MAX_FILE_BYTES) continue;
|
|
8444
|
+
try {
|
|
8445
|
+
out[relPath] = readFileSync32(abs, "utf8");
|
|
8446
|
+
} catch {
|
|
8447
|
+
}
|
|
8448
|
+
}
|
|
8449
|
+
}
|
|
8450
|
+
var __defProp4, __name4, FILE_HASH_LENGTH, SKIP_DIRECTORIES, ARCHIVE_ONLY_SKIP_DIRECTORIES, DEFAULT_MAX_FILE_BYTES, KIND_BY_EXT, CHECK_BLOBS_MAX_HASHES, BackupHttpError, BackupHttpClient, DEFAULT_CONCURRENCY, CREDENTIAL_PATHS, WINDOWS_ABSOLUTE_PATH, ARCHIVE_SCHEMA_VERSION;
|
|
8451
|
+
var init_dist3 = __esm({
|
|
8452
|
+
"../shared-source-sync/dist/index.mjs"() {
|
|
8453
|
+
"use strict";
|
|
8454
|
+
__defProp4 = Object.defineProperty;
|
|
8455
|
+
__name4 = /* @__PURE__ */ __name((target, value3) => __defProp4(target, "name", { value: value3, configurable: true }), "__name");
|
|
8456
|
+
FILE_HASH_LENGTH = 16;
|
|
8457
|
+
__name(hashContentTruncated, "hashContentTruncated");
|
|
8458
|
+
__name4(hashContentTruncated, "hashContentTruncated");
|
|
8459
|
+
__name(sha256Hex, "sha256Hex");
|
|
8460
|
+
__name4(sha256Hex, "sha256Hex");
|
|
8461
|
+
__name(matchesFileHash, "matchesFileHash");
|
|
8462
|
+
__name4(matchesFileHash, "matchesFileHash");
|
|
8463
|
+
__name(combineFileHashes, "combineFileHashes");
|
|
8464
|
+
__name4(combineFileHashes, "combineFileHashes");
|
|
8465
|
+
SKIP_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
8466
|
+
"node_modules",
|
|
8467
|
+
"dist",
|
|
8468
|
+
"dist-v2",
|
|
8469
|
+
".git",
|
|
8470
|
+
".lua",
|
|
8471
|
+
".temp",
|
|
8472
|
+
"coverage",
|
|
8473
|
+
".next",
|
|
8474
|
+
".turbo"
|
|
8475
|
+
]);
|
|
8476
|
+
ARCHIVE_ONLY_SKIP_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
8477
|
+
"build"
|
|
8478
|
+
]);
|
|
8479
|
+
DEFAULT_MAX_FILE_BYTES = 256 * 1024;
|
|
8480
|
+
__name(shouldSkipDirectory, "shouldSkipDirectory");
|
|
8481
|
+
__name4(shouldSkipDirectory, "shouldSkipDirectory");
|
|
8482
|
+
__name(shouldSkipFile, "shouldSkipFile");
|
|
8483
|
+
__name4(shouldSkipFile, "shouldSkipFile");
|
|
8484
|
+
KIND_BY_EXT = {
|
|
8485
|
+
".ts": "source",
|
|
8486
|
+
".tsx": "source",
|
|
8487
|
+
".js": "source",
|
|
8488
|
+
".jsx": "source",
|
|
8489
|
+
".yaml": "config",
|
|
8490
|
+
".yml": "config",
|
|
8491
|
+
".json": "config",
|
|
8492
|
+
".toml": "config"
|
|
8493
|
+
};
|
|
8494
|
+
__name(classifyFile, "classifyFile");
|
|
8495
|
+
__name4(classifyFile, "classifyFile");
|
|
8496
|
+
__name(walkWorkspace, "walkWorkspace");
|
|
8497
|
+
__name4(walkWorkspace, "walkWorkspace");
|
|
8498
|
+
CHECK_BLOBS_MAX_HASHES = 500;
|
|
8499
|
+
BackupHttpError = class extends Error {
|
|
8500
|
+
static {
|
|
8501
|
+
__name(this, "BackupHttpError");
|
|
8502
|
+
}
|
|
8503
|
+
static {
|
|
8504
|
+
__name4(this, "BackupHttpError");
|
|
8505
|
+
}
|
|
8506
|
+
status;
|
|
8507
|
+
endpoint;
|
|
8508
|
+
body;
|
|
8509
|
+
constructor(message, status, endpoint, body) {
|
|
8510
|
+
super(message), this.status = status, this.endpoint = endpoint, this.body = body;
|
|
8511
|
+
this.name = "BackupHttpError";
|
|
8512
|
+
}
|
|
8513
|
+
};
|
|
8514
|
+
BackupHttpClient = class {
|
|
8515
|
+
static {
|
|
8516
|
+
__name(this, "BackupHttpClient");
|
|
8517
|
+
}
|
|
8518
|
+
static {
|
|
8519
|
+
__name4(this, "BackupHttpClient");
|
|
8520
|
+
}
|
|
8521
|
+
options;
|
|
8522
|
+
fetchFn;
|
|
8523
|
+
constructor(options) {
|
|
8524
|
+
this.options = options;
|
|
8525
|
+
this.fetchFn = options.fetch ?? fetch;
|
|
8526
|
+
}
|
|
8527
|
+
url(path3) {
|
|
8528
|
+
const base = this.options.baseUrl.replace(/\/$/, "");
|
|
8529
|
+
return `${base}/developer/agents/${encodeURIComponent(this.options.agentId)}${path3}`;
|
|
8530
|
+
}
|
|
8531
|
+
async json(method, path3, body) {
|
|
8532
|
+
const endpoint = this.url(path3);
|
|
8533
|
+
const res = await this.fetchFn(endpoint, {
|
|
8534
|
+
method,
|
|
8535
|
+
headers: {
|
|
8536
|
+
Authorization: this.options.authHeader,
|
|
8537
|
+
...body !== void 0 ? {
|
|
8538
|
+
"Content-Type": "application/json"
|
|
8539
|
+
} : {},
|
|
8540
|
+
Accept: "application/json"
|
|
8541
|
+
},
|
|
8542
|
+
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
8543
|
+
});
|
|
8544
|
+
if (!res.ok) {
|
|
8545
|
+
let text2;
|
|
8546
|
+
try {
|
|
8547
|
+
text2 = await res.text();
|
|
8548
|
+
} catch {
|
|
8549
|
+
text2 = void 0;
|
|
8550
|
+
}
|
|
8551
|
+
throw new BackupHttpError(`Backup request failed: ${method} ${path3} \u2192 ${res.status} ${res.statusText}`, res.status, endpoint, text2);
|
|
8552
|
+
}
|
|
8553
|
+
const text = await res.text();
|
|
8554
|
+
if (!text) return void 0;
|
|
8555
|
+
return JSON.parse(text);
|
|
8556
|
+
}
|
|
8557
|
+
/**
|
|
8558
|
+
* `POST /backup/check-blobs` — returns existing vs missing partition.
|
|
8559
|
+
*
|
|
8560
|
+
* LUA-675: the server caps one request at `CHECK_BLOBS_MAX_HASHES`; a
|
|
8561
|
+
* larger set is sent in sequential chunks and the partitions merged, so a
|
|
8562
|
+
* big workspace still pushes and the client never fans out on its own.
|
|
8563
|
+
*/
|
|
8564
|
+
async checkBlobsExist(hashes) {
|
|
8565
|
+
if (hashes.length <= CHECK_BLOBS_MAX_HASHES) {
|
|
8566
|
+
return this.json("POST", "/backup/check-blobs", {
|
|
8567
|
+
hashes
|
|
8568
|
+
});
|
|
8569
|
+
}
|
|
8570
|
+
const merged = {
|
|
8571
|
+
missing: [],
|
|
8572
|
+
existing: [],
|
|
8573
|
+
results: {}
|
|
8574
|
+
};
|
|
8575
|
+
let unverified;
|
|
8576
|
+
for (let i = 0; i < hashes.length; i += CHECK_BLOBS_MAX_HASHES) {
|
|
8577
|
+
const part = await this.json("POST", "/backup/check-blobs", {
|
|
8578
|
+
hashes: hashes.slice(i, i + CHECK_BLOBS_MAX_HASHES)
|
|
8579
|
+
});
|
|
8580
|
+
merged.missing.push(...part.missing);
|
|
8581
|
+
merged.existing.push(...part.existing);
|
|
8582
|
+
Object.assign(merged.results, part.results);
|
|
8583
|
+
if (part.unverified) unverified = [
|
|
8584
|
+
...unverified ?? [],
|
|
8585
|
+
...part.unverified
|
|
8586
|
+
];
|
|
8587
|
+
}
|
|
8588
|
+
return unverified ? {
|
|
8589
|
+
...merged,
|
|
8590
|
+
unverified
|
|
8591
|
+
} : merged;
|
|
8592
|
+
}
|
|
8593
|
+
/** `POST /backup/blob-upload-urls` — presigned S3 PUT URLs. */
|
|
8594
|
+
getBlobUploadUrls(hashes) {
|
|
8595
|
+
return this.json("POST", "/backup/blob-upload-urls", {
|
|
8596
|
+
hashes
|
|
8597
|
+
});
|
|
8598
|
+
}
|
|
8599
|
+
/** `POST /backup/blob-urls` — presigned S3 GET URLs (for restore). */
|
|
8600
|
+
getBlobUrls(hashes) {
|
|
8601
|
+
return this.json("POST", "/backup/blob-urls", {
|
|
8602
|
+
hashes
|
|
8603
|
+
});
|
|
8604
|
+
}
|
|
8605
|
+
/** `POST /backup/manifest` — final step in a push; returns server metadata. */
|
|
8606
|
+
saveManifest(data) {
|
|
8607
|
+
return this.json("POST", "/backup/manifest", data);
|
|
8608
|
+
}
|
|
8609
|
+
/** `GET /backup` — metadata only, no file list. */
|
|
8610
|
+
getMetadata() {
|
|
8611
|
+
return this.json("GET", "/backup");
|
|
8612
|
+
}
|
|
8613
|
+
/** `GET /backup/manifest` — metadata + file list. */
|
|
8614
|
+
getManifest() {
|
|
8615
|
+
return this.json("GET", "/backup/manifest");
|
|
8616
|
+
}
|
|
8617
|
+
/** `GET /backup/check/:hash` — fast freshness probe. */
|
|
8618
|
+
checkBackupExists(hash) {
|
|
8619
|
+
return this.json("GET", `/backup/check/${encodeURIComponent(hash)}`);
|
|
8620
|
+
}
|
|
8621
|
+
};
|
|
8622
|
+
DEFAULT_CONCURRENCY = 10;
|
|
8623
|
+
__name(uploadBlobs, "uploadBlobs");
|
|
8624
|
+
__name4(uploadBlobs, "uploadBlobs");
|
|
8625
|
+
__name(decodeBlob, "decodeBlob");
|
|
8626
|
+
__name4(decodeBlob, "decodeBlob");
|
|
8627
|
+
__name(verifyDownloaded, "verifyDownloaded");
|
|
8628
|
+
__name4(verifyDownloaded, "verifyDownloaded");
|
|
8629
|
+
__name(downloadBlobs, "downloadBlobs");
|
|
8630
|
+
__name4(downloadBlobs, "downloadBlobs");
|
|
8631
|
+
__name(resolveBackupFileTarget, "resolveBackupFileTarget");
|
|
8632
|
+
__name4(resolveBackupFileTarget, "resolveBackupFileTarget");
|
|
8633
|
+
__name(restoreFromBlobs, "restoreFromBlobs");
|
|
8634
|
+
__name4(restoreFromBlobs, "restoreFromBlobs");
|
|
8635
|
+
CREDENTIAL_PATHS = /* @__PURE__ */ new Set([
|
|
8636
|
+
".env",
|
|
8637
|
+
".lua/config.json",
|
|
8638
|
+
".claude/settings.local.json"
|
|
8639
|
+
]);
|
|
8640
|
+
WINDOWS_ABSOLUTE_PATH = /^[a-z]:\//i;
|
|
8641
|
+
__name(normalizeWorkspaceRelativePath, "normalizeWorkspaceRelativePath");
|
|
8642
|
+
__name4(normalizeWorkspaceRelativePath, "normalizeWorkspaceRelativePath");
|
|
8643
|
+
__name(isCredentialPersistencePath, "isCredentialPersistencePath");
|
|
8644
|
+
__name4(isCredentialPersistencePath, "isCredentialPersistencePath");
|
|
8645
|
+
__name(pushAgentBackup, "pushAgentBackup");
|
|
8646
|
+
__name4(pushAgentBackup, "pushAgentBackup");
|
|
8647
|
+
__name(pullAgentBackup, "pullAgentBackup");
|
|
8648
|
+
__name4(pullAgentBackup, "pullAgentBackup");
|
|
8649
|
+
ARCHIVE_SCHEMA_VERSION = 1;
|
|
8650
|
+
__name(encodeWorkspaceArchive, "encodeWorkspaceArchive");
|
|
8651
|
+
__name4(encodeWorkspaceArchive, "encodeWorkspaceArchive");
|
|
8652
|
+
__name(decodeWorkspaceArchive, "decodeWorkspaceArchive");
|
|
8653
|
+
__name4(decodeWorkspaceArchive, "decodeWorkspaceArchive");
|
|
8654
|
+
__name(writeArchiveToWorkspace, "writeArchiveToWorkspace");
|
|
8655
|
+
__name4(writeArchiveToWorkspace, "writeArchiveToWorkspace");
|
|
8656
|
+
__name(walk2, "walk");
|
|
8657
|
+
__name4(walk2, "walk");
|
|
8658
|
+
}
|
|
8659
|
+
});
|
|
8660
|
+
|
|
6987
8661
|
// src/api/backup.api.service.ts
|
|
6988
8662
|
var init_backup_api_service = __esm({
|
|
6989
8663
|
"src/api/backup.api.service.ts"() {
|
|
6990
8664
|
"use strict";
|
|
6991
8665
|
init_http_client();
|
|
8666
|
+
init_dist3();
|
|
6992
8667
|
}
|
|
6993
8668
|
});
|
|
6994
8669
|
|
|
@@ -11215,7 +12890,7 @@ function createWorkflowsRuntime(getApi) {
|
|
|
11215
12890
|
}
|
|
11216
12891
|
};
|
|
11217
12892
|
}
|
|
11218
|
-
var WORKFLOW_START_MAX_WAIT_SECONDS, WORKFLOW_START_CLIENT_DEADLINE_SLACK_MS, WorkflowApiError, unwrap, assertBoundAgent, unavailable, WorkflowApi;
|
|
12893
|
+
var WORKFLOW_START_MAX_WAIT_SECONDS, WORKFLOW_START_CLIENT_DEADLINE_SLACK_MS, WorkflowApiError, unwrap, assertBoundAgent, pathId, unavailable, WorkflowApi;
|
|
11219
12894
|
var init_workflow_api_service = __esm({
|
|
11220
12895
|
"src/api/workflow.api.service.ts"() {
|
|
11221
12896
|
"use strict";
|
|
@@ -11252,6 +12927,7 @@ var init_workflow_api_service = __esm({
|
|
|
11252
12927
|
throw new WorkflowApiError("FORBIDDEN", `Workflows.${member}: goals are scoped to the bound agent ${api.agentId}`, 403);
|
|
11253
12928
|
}
|
|
11254
12929
|
}, "assertBoundAgent");
|
|
12930
|
+
pathId = /* @__PURE__ */ __name((id) => encodeURIComponent(id), "pathId");
|
|
11255
12931
|
unavailable = /* @__PURE__ */ __name((member, route) => async () => {
|
|
11256
12932
|
throw new WorkflowApiError("WORKFLOWS_API_UNAVAILABLE", `Workflows.${member} is not available in this runtime yet (${route} lands with a later wave)`, 501);
|
|
11257
12933
|
}, "unavailable");
|
|
@@ -11377,7 +13053,7 @@ var init_workflow_api_service = __esm({
|
|
|
11377
13053
|
/** R5 — one step of a run (`?attempt=n` selects from the attempt history). */
|
|
11378
13054
|
async getRunStep(runId, stepId, options = {}) {
|
|
11379
13055
|
const qs = options.attempt !== void 0 ? `?attempt=${options.attempt}` : "";
|
|
11380
|
-
return this.httpGet(`${this.runs}/${runId}/steps/${stepId}${qs}`, await this.auth());
|
|
13056
|
+
return this.httpGet(`${this.runs}/${runId}/steps/${pathId(stepId)}${qs}`, await this.auth());
|
|
11381
13057
|
}
|
|
11382
13058
|
/** R6 — script-form journal page (404 `NOT_SCRIPT_RUN` for graph runs — IF-16). */
|
|
11383
13059
|
async getRunJournal(runId, options = {}) {
|
|
@@ -11397,7 +13073,7 @@ var init_workflow_api_service = __esm({
|
|
|
11397
13073
|
}
|
|
11398
13074
|
/** R12 — resume a suspended step; the loser of a race gets `{ resumed:false, reason:'already_resumed' }`, never a 4xx. */
|
|
11399
13075
|
async resumeRun(runId, stepId, data) {
|
|
11400
|
-
return this.httpPost(`${this.runs}/${runId}/steps/${stepId}/resume`, data, await this.auth());
|
|
13076
|
+
return this.httpPost(`${this.runs}/${runId}/steps/${pathId(stepId)}/resume`, data, await this.auth());
|
|
11401
13077
|
}
|
|
11402
13078
|
/**
|
|
11403
13079
|
* R36 — re-arm a parked step: a failed row past its retries, a gate-2 park, or a billing hold (the only exit
|
|
@@ -11405,15 +13081,15 @@ var init_workflow_api_service = __esm({
|
|
|
11405
13081
|
* STEP_NOT_PARKED.
|
|
11406
13082
|
*/
|
|
11407
13083
|
async retryStep(runId, stepId, data = {}) {
|
|
11408
|
-
return this.httpPost(`${this.runs}/${runId}/steps/${stepId}/retry`, data, await this.auth());
|
|
13084
|
+
return this.httpPost(`${this.runs}/${runId}/steps/${pathId(stepId)}/retry`, data, await this.auth());
|
|
11409
13085
|
}
|
|
11410
13086
|
/** R13 — resolve an approval (human; `expectedFingerprint` guards against an edited payload — 409 `PAYLOAD_MISMATCH`). */
|
|
11411
13087
|
async resolveApproval(runId, approvalId, data) {
|
|
11412
|
-
return this.httpPost(`${this.runs}/${runId}/approvals/${approvalId}/resolve`, data, await this.auth());
|
|
13088
|
+
return this.httpPost(`${this.runs}/${runId}/approvals/${pathId(approvalId)}/resolve`, data, await this.auth());
|
|
11413
13089
|
}
|
|
11414
13090
|
/** R14 — deliver a named signal (`dedupeKey` ⇒ 200 `duplicate:true` on replay). */
|
|
11415
13091
|
async signalRun(runId, name, data = {}) {
|
|
11416
|
-
return this.httpPost(`${this.runs}/${runId}/signals/${
|
|
13092
|
+
return this.httpPost(`${this.runs}/${runId}/signals/${pathId(name)}`, data, await this.auth());
|
|
11417
13093
|
}
|
|
11418
13094
|
/** R31 — `DELETE …/runs/:runId` (`eraseRun`; human principal) → 202 `{ accepted, purgeId }`; 409 `RUN_NOT_TERMINAL { nextAction:'cancel' }`. */
|
|
11419
13095
|
/** R50 — request an evidence-bundle export (202 `{exportId, status:'pending'}`; 409 `RUN_NOT_TERMINAL` / `EXPORT_IN_PROGRESS`). */
|
|
@@ -11470,6 +13146,23 @@ var init_workflow_api_service = __esm({
|
|
|
11470
13146
|
async closeGoal(goalId, data = {}) {
|
|
11471
13147
|
return this.httpPost(`${this.goals}/${encodeURIComponent(goalId)}/close`, data, await this.auth());
|
|
11472
13148
|
}
|
|
13149
|
+
// ─── Schedules (R4-MF-2 list/get + R28 delete — `/workflows/:agentId/schedules`; LUA-627 stanza) ───
|
|
13150
|
+
/** Schedule tree (09 §9.5 — the write-only R27/R56/R28 family plus the R4-MF-2 read rows). */
|
|
13151
|
+
get schedules() {
|
|
13152
|
+
return `/workflows/${this.agentId}/schedules`;
|
|
13153
|
+
}
|
|
13154
|
+
/** R4-MF-2 — every `Job{kind:'workflow'}` of the agent with the PRO-726 strike fields (`goalId` marks a goal's cadence). */
|
|
13155
|
+
async listSchedules() {
|
|
13156
|
+
return this.httpGet(this.schedules, await this.auth());
|
|
13157
|
+
}
|
|
13158
|
+
/** R4-MF-2 — one schedule row; unknown, cross-agent and non-workflow-kind jobs all 404 `SCHEDULE_NOT_FOUND`. */
|
|
13159
|
+
async getSchedule(jobId) {
|
|
13160
|
+
return this.httpGet(`${this.schedules}/${encodeURIComponent(jobId)}`, await this.auth());
|
|
13161
|
+
}
|
|
13162
|
+
/** R28 — delete a schedule Job (404 `SCHEDULE_NOT_FOUND`). The CLI refuses a goal-owned job BEFORE this call (`goal_schedule`). */
|
|
13163
|
+
async deleteSchedule(jobId) {
|
|
13164
|
+
return this.httpDelete(`${this.schedules}/${encodeURIComponent(jobId)}`, await this.auth());
|
|
13165
|
+
}
|
|
11473
13166
|
// ─── Events (R7 — SSE `watch`, WF-204) ───
|
|
11474
13167
|
/**
|
|
11475
13168
|
* R7 SSE — `GET …/runs/:runId/events` as a frame stream (`id:<seq>` / `event:<type>` /
|
|
@@ -11513,7 +13206,7 @@ var init_workflow_api_service = __esm({
|
|
|
11513
13206
|
if (options.attempt !== void 0) query.append("attempt", String(options.attempt));
|
|
11514
13207
|
if (options.tail !== void 0) query.append("tail", String(options.tail));
|
|
11515
13208
|
const qs = query.toString();
|
|
11516
|
-
return this.httpGet(`${this.runs}/${runId}/steps/${stepId}/job${qs ? `?${qs}` : ""}`, await this.auth());
|
|
13209
|
+
return this.httpGet(`${this.runs}/${runId}/steps/${pathId(stepId)}/job${qs ? `?${qs}` : ""}`, await this.auth());
|
|
11517
13210
|
}
|
|
11518
13211
|
};
|
|
11519
13212
|
}
|