lua-cli 3.32.1 → 3.32.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api-exports.d.ts +414 -103
- package/dist/api-exports.js +2216 -622
- package/dist/api-exports.js.map +1 -1
- package/dist/index.js +5374 -2188
- package/dist/index.js.map +1 -1
- package/dist/voice/test/index.d.ts +54 -54
- package/dist/workflow-builder.d.ts +228 -45
- package/dist/workflow-builder.js +1945 -1025
- package/dist/workflow-builder.js.map +1 -1
- package/docs/CLI_REFERENCE.md +126 -4
- package/docs/README.md +2 -2
- package/docs/api/LuaWorkflow.md +16 -16
- package/docs/api/Workflows.md +10 -0
- package/docs/workflows/approvals.md +24 -6
- package/docs/workflows/correlation-keys.md +1 -0
- package/docs/workflows/goals.md +2 -2
- package/docs/workflows/limits.md +6 -0
- package/docs/workflows/replay-local.md +9 -3
- package/docs/workflows/schedules.md +1 -1
- package/docs/workflows/script-form.md +22 -12
- package/docs/workflows/testing-offline.md +35 -21
- package/docs/workflows/workspaces-and-long-steps.md +36 -2
- package/package.json +5 -4
- package/template/examples/workflows/CLAUDE.md +17 -11
- package/template/examples/workflows/adversarial-verify.workflow.script.js +20 -16
- package/template/examples/workflows/outreach.ts +31 -15
- package/template/examples/workflows/pr-review-round.ts +7 -3
- package/template/examples/workflows/refund-approval.ts +26 -12
- package/template/examples/workflows/ticket-to-pr.ts +43 -36
- 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);
|
|
@@ -535,6 +538,75 @@ function isKnownProfile(profiles, profileId) {
|
|
|
535
538
|
function hasCapability(profiles, profileId, required) {
|
|
536
539
|
return capabilitiesFor(profiles, profileId).includes(required);
|
|
537
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
|
+
}
|
|
538
610
|
function isSystemRun(identity) {
|
|
539
611
|
return identity.userId.startsWith(SYSTEM_USER_PREFIX);
|
|
540
612
|
}
|
|
@@ -581,6 +653,75 @@ function scheduledTimeKey(scheduledTime) {
|
|
|
581
653
|
function scheduledWorkflowRunIdForTime(jobId, scheduledTime) {
|
|
582
654
|
return scheduledWorkflowRunId(jobId, scheduledTime);
|
|
583
655
|
}
|
|
656
|
+
function workflowRetryMaxAttemptsMessage(got) {
|
|
657
|
+
const tail = got === void 0 ? "" : ` (got ${JSON.stringify(got)})`;
|
|
658
|
+
return `\`retry.maxAttempts\` must be an integer ${WORKFLOW_RETRY_MIN_ATTEMPTS}..${WORKFLOW_RETRY_MAX_ATTEMPTS}${tail}`;
|
|
659
|
+
}
|
|
660
|
+
function isWithinWorkflowRetryAttempts(value3) {
|
|
661
|
+
return typeof value3 === "number" && Number.isInteger(value3) && value3 >= WORKFLOW_RETRY_MIN_ATTEMPTS && value3 <= WORKFLOW_RETRY_MAX_ATTEMPTS;
|
|
662
|
+
}
|
|
663
|
+
function workflowRetryUnknownMembersMessage(keys) {
|
|
664
|
+
const named = keys.map((k) => {
|
|
665
|
+
const engine = WORKFLOW_RETRY_ENGINE_KEYS.includes(k);
|
|
666
|
+
return `\`${k}\`${engine ? " (engine-owned \u2014 stamped by resetAttempts, never authored)" : ""}`;
|
|
667
|
+
});
|
|
668
|
+
return `\`retry\` has no member ${named.join(", ")}; members: ${WORKFLOW_RETRY_POLICY_KEYS.join(", ")}`;
|
|
669
|
+
}
|
|
670
|
+
function unknownWorkflowRetryMembers(retry) {
|
|
671
|
+
if (!retry || typeof retry !== "object" || Array.isArray(retry)) return [];
|
|
672
|
+
return Object.keys(retry).filter((k) => !WORKFLOW_RETRY_POLICY_KEYS.includes(k));
|
|
673
|
+
}
|
|
674
|
+
function authoredRetryPolicy(retry) {
|
|
675
|
+
if (!retry || typeof retry !== "object" || Array.isArray(retry)) return void 0;
|
|
676
|
+
const src = retry;
|
|
677
|
+
const out = {};
|
|
678
|
+
for (const k of WORKFLOW_RETRY_POLICY_KEYS) if (src[k] !== void 0) out[k] = src[k];
|
|
679
|
+
return Object.keys(out).length ? out : void 0;
|
|
680
|
+
}
|
|
681
|
+
function retryBudgetBaseAttempt(row) {
|
|
682
|
+
const base = row.retry?.budgetBaseAttempt;
|
|
683
|
+
return typeof base === "number" && Number.isSafeInteger(base) && base > 0 ? base : 0;
|
|
684
|
+
}
|
|
685
|
+
function retryBudgetAttempt(row) {
|
|
686
|
+
return Math.max(0, row.attempt - retryBudgetBaseAttempt(row));
|
|
687
|
+
}
|
|
688
|
+
function retryBudgetMaxAttempts(row) {
|
|
689
|
+
const max = row.retry?.maxAttempts;
|
|
690
|
+
return typeof max === "number" && Number.isFinite(max) && max >= 1 ? max : 1;
|
|
691
|
+
}
|
|
692
|
+
function retryBudgetRemaining(row) {
|
|
693
|
+
return retryBudgetAttempt(row) < retryBudgetMaxAttempts(row);
|
|
694
|
+
}
|
|
695
|
+
function retriesRemaining(row) {
|
|
696
|
+
return Math.max(0, retryBudgetMaxAttempts(row) - retryBudgetAttempt(row));
|
|
697
|
+
}
|
|
698
|
+
function isWithinWorkflowJobRange(member, value3) {
|
|
699
|
+
const { min, max } = WORKFLOW_JOB_RANGES[member];
|
|
700
|
+
return typeof value3 === "number" && Number.isInteger(value3) && value3 >= min && value3 <= max;
|
|
701
|
+
}
|
|
702
|
+
function workflowJobRangeMessage(member) {
|
|
703
|
+
const { min, max } = WORKFLOW_JOB_RANGES[member];
|
|
704
|
+
return `\`${member}\` must be an integer ${min}..${max}`;
|
|
705
|
+
}
|
|
706
|
+
function isWorkflowSingleStepType(type) {
|
|
707
|
+
return typeof type === "string" && WORKFLOW_SINGLE_STEP_TYPES.includes(type);
|
|
708
|
+
}
|
|
709
|
+
function isWorkflowHitlEntryType(type) {
|
|
710
|
+
return typeof type === "string" && WORKFLOW_HITL_ENTRY_TYPES.includes(type);
|
|
711
|
+
}
|
|
712
|
+
function isWorkflowArmEntryType(type) {
|
|
713
|
+
return typeof type === "string" && WORKFLOW_ARM_ENTRY_TYPES.includes(type);
|
|
714
|
+
}
|
|
715
|
+
function workflowContainerRunsHitlArm(container) {
|
|
716
|
+
return typeof container === "string" && WORKFLOW_HITL_ARM_CONTAINERS.includes(container);
|
|
717
|
+
}
|
|
718
|
+
function workflowHitlArmUnsupportedMessage(type, id, container) {
|
|
719
|
+
const legal = WORKFLOW_HITL_ARM_CONTAINERS.map((c) => `\`${c}\``).join(" / ");
|
|
720
|
+
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\``;
|
|
721
|
+
}
|
|
722
|
+
function workflowHitlArmShapeMessage(type, id, shape) {
|
|
723
|
+
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\``;
|
|
724
|
+
}
|
|
584
725
|
function groupCount(re) {
|
|
585
726
|
let n2 = GROUP_COUNT.get(re);
|
|
586
727
|
if (n2 === void 0) {
|
|
@@ -589,6 +730,9 @@ function groupCount(re) {
|
|
|
589
730
|
}
|
|
590
731
|
return n2;
|
|
591
732
|
}
|
|
733
|
+
function isWorkflowSecretKey(key) {
|
|
734
|
+
return WORKFLOW_SECRET_KEY_RE.test(key);
|
|
735
|
+
}
|
|
592
736
|
function applyPatterns(text, patterns) {
|
|
593
737
|
let out = text;
|
|
594
738
|
for (const { re, suffix } of patterns) {
|
|
@@ -611,8 +755,15 @@ function scrubSecretText(text) {
|
|
|
611
755
|
if (typeof text !== "string" || text.length < 4) return text;
|
|
612
756
|
return applyPatterns(applyPatterns(text, SECRET_LITERAL_PATTERNS), SECRET_PAIR_PATTERNS);
|
|
613
757
|
}
|
|
758
|
+
function boundScrubInput(text, max = SCRUB_INPUT_MAX_CHARS) {
|
|
759
|
+
if (typeof text !== "string" || text.length <= max) return text;
|
|
760
|
+
const window = text.slice(Math.max(0, max - SCRUB_CUT_BACKOFF_CHARS), max);
|
|
761
|
+
const ws = window.search(/\s\S*$/);
|
|
762
|
+
const cut = ws >= 0 ? max - window.length + ws : max;
|
|
763
|
+
return `${text.slice(0, cut)}\u2026`;
|
|
764
|
+
}
|
|
614
765
|
function scrubSecretLines(lines) {
|
|
615
|
-
return lines.map((l) => typeof l === "string" ? scrubSecretText(l) : l);
|
|
766
|
+
return lines.map((l) => typeof l === "string" ? scrubSecretText(boundScrubInput(l)) : l);
|
|
616
767
|
}
|
|
617
768
|
function messageText(value3) {
|
|
618
769
|
if (typeof value3 === "string") return value3;
|
|
@@ -629,11 +780,14 @@ function messageText(value3) {
|
|
|
629
780
|
return value3 === void 0 || value3 === null ? "" : String(value3);
|
|
630
781
|
}
|
|
631
782
|
function scrubProviderMessage(raw, max = PROVIDER_MESSAGE_MAX_CHARS) {
|
|
632
|
-
const text = messageText(raw).replace(/\s+/g, " ").trim();
|
|
783
|
+
const text = boundScrubInput(messageText(raw)).replace(/\s+/g, " ").trim();
|
|
633
784
|
if (!text) return void 0;
|
|
634
785
|
const out = scrubSecretText(text);
|
|
635
786
|
return out.length > max ? `${out.slice(0, max - 1)}\u2026` : out;
|
|
636
787
|
}
|
|
788
|
+
function scrubStepErrorMessage(raw) {
|
|
789
|
+
return scrubProviderMessage(raw, ERROR_MESSAGE_MAX_CHARS);
|
|
790
|
+
}
|
|
637
791
|
function isWorkflowAuditEvent(action) {
|
|
638
792
|
return typeof action === "string" && WORKFLOW_AUDIT_EVENTS.includes(action);
|
|
639
793
|
}
|
|
@@ -728,7 +882,66 @@ ${PREAMBLE}
|
|
|
728
882
|
|
|
729
883
|
${items.join("\n\n")}`;
|
|
730
884
|
}
|
|
731
|
-
|
|
885
|
+
function workflowApprovalDecisionOf(resumeData) {
|
|
886
|
+
if (resumeData.timedOut === true) return "timed_out";
|
|
887
|
+
return resumeData.approved === true ? "approved" : "denied";
|
|
888
|
+
}
|
|
889
|
+
function workflowApprovalOutput(resumeData) {
|
|
890
|
+
const decision = typeof resumeData.decision === "string" ? resumeData.decision : workflowApprovalDecisionOf(resumeData);
|
|
891
|
+
const note = typeof resumeData.note === "string" && resumeData.note.trim() !== "" ? resumeData.note : void 0;
|
|
892
|
+
const text = typeof resumeData.text === "string" ? resumeData.text : note ?? decision;
|
|
893
|
+
return {
|
|
894
|
+
...resumeData,
|
|
895
|
+
decision,
|
|
896
|
+
text
|
|
897
|
+
};
|
|
898
|
+
}
|
|
899
|
+
function isWorkflowApprovalOutput(value3) {
|
|
900
|
+
if (typeof value3 !== "object" || value3 === null || Array.isArray(value3)) return false;
|
|
901
|
+
const v = value3;
|
|
902
|
+
return typeof v.approved === "boolean" && WORKFLOW_APPROVAL_OUTPUT_DECISIONS.includes(v.decision) && typeof v.text === "string";
|
|
903
|
+
}
|
|
904
|
+
function extractSingleJsonValue(text) {
|
|
905
|
+
const trimmed = (text ?? "").trim();
|
|
906
|
+
if (!trimmed) return {
|
|
907
|
+
reason: "reply is empty"
|
|
908
|
+
};
|
|
909
|
+
const fenced = [
|
|
910
|
+
...trimmed.matchAll(JSON_FENCE_RE)
|
|
911
|
+
];
|
|
912
|
+
if (fenced.length > 1) return {
|
|
913
|
+
reason: "reply carries more than one fenced block"
|
|
914
|
+
};
|
|
915
|
+
const candidate = fenced.length === 1 ? fenced[0][1].trim() : trimmed;
|
|
916
|
+
try {
|
|
917
|
+
return {
|
|
918
|
+
value: JSON.parse(candidate)
|
|
919
|
+
};
|
|
920
|
+
} catch {
|
|
921
|
+
if (fenced.length === 1) return {
|
|
922
|
+
reason: "fenced block is not valid JSON"
|
|
923
|
+
};
|
|
924
|
+
}
|
|
925
|
+
const opens = [
|
|
926
|
+
trimmed.indexOf("{"),
|
|
927
|
+
trimmed.indexOf("[")
|
|
928
|
+
].filter((i) => i !== -1);
|
|
929
|
+
const start = opens.length ? Math.min(...opens) : -1;
|
|
930
|
+
const end = Math.max(trimmed.lastIndexOf("}"), trimmed.lastIndexOf("]"));
|
|
931
|
+
if (start === -1 || end <= start) return {
|
|
932
|
+
reason: "reply is not JSON"
|
|
933
|
+
};
|
|
934
|
+
try {
|
|
935
|
+
return {
|
|
936
|
+
value: JSON.parse(trimmed.slice(start, end + 1))
|
|
937
|
+
};
|
|
938
|
+
} catch {
|
|
939
|
+
return {
|
|
940
|
+
reason: "reply does not contain a single JSON value"
|
|
941
|
+
};
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
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_RETRY_MIN_ATTEMPTS, WORKFLOW_RETRY_POLICY_KEYS, WORKFLOW_RETRY_MAX_ATTEMPTS, WORKFLOW_RETRY_ENGINE_KEYS, WORKFLOW_JOB_RESOURCES, WORKFLOW_SIDE_EFFECTS, WORKFLOW_JOB_RANGES, WORKFLOW_JOB_RANGE_MEMBERS, WORKFLOW_SINGLE_STEP_TYPES, WORKFLOW_HITL_ENTRY_TYPES, WORKFLOW_ARM_ENTRY_TYPES, WORKFLOW_HITL_ARM_CONTAINERS, WORKFLOW_GRAPH_ENTRY_STEP_KINDS, WORKFLOW_ARM_ENTRY_STEP_KINDS, WORKFLOW_BUDGET_MAX_DURATION_SECONDS, REDACTED_PLACEHOLDER, PROVIDER_MESSAGE_MAX_CHARS, ERROR_MESSAGE_MAX_CHARS, SECRET_LITERAL_PATTERNS, SECRET_NAME, SECRET_PAIR_PATTERNS, GROUP_COUNT, WORKFLOW_SECRET_KEY_RE, WORKFLOW_RESERVED_SECRET_KEYS, SCRUB_INPUT_MAX_CHARS, SCRUB_CUT_BACKOFF_CHARS, WORKFLOW_AUDIT_EVENTS, WORKFLOW_AUDIT_METADATA_MAX_BYTES, INDENT, WRAP_WIDTH, NOUNS, GET_TOOL_NAMES, PREAMBLE, WORKFLOW_APPROVAL_OUTPUT_DECISIONS, WORKFLOW_APPROVAL_OUTPUT_SCHEMA, JSON_FENCE_RE;
|
|
732
945
|
var init_dist = __esm({
|
|
733
946
|
"../shared-types/dist/index.mjs"() {
|
|
734
947
|
"use strict";
|
|
@@ -1059,6 +1272,9 @@ var init_dist = __esm({
|
|
|
1059
1272
|
];
|
|
1060
1273
|
__name(isImplicitModelSelectionSource, "isImplicitModelSelectionSource");
|
|
1061
1274
|
__name2(isImplicitModelSelectionSource, "isImplicitModelSelectionSource");
|
|
1275
|
+
PLATFORM_FALLBACK_MODEL_SOURCE = "platform-fallback";
|
|
1276
|
+
__name(isPlatformFallbackModelSource, "isPlatformFallbackModelSource");
|
|
1277
|
+
__name2(isPlatformFallbackModelSource, "isPlatformFallbackModelSource");
|
|
1062
1278
|
__name(resolveRequireToolApproval, "resolveRequireToolApproval");
|
|
1063
1279
|
__name2(resolveRequireToolApproval, "resolveRequireToolApproval");
|
|
1064
1280
|
AGENT_NAME_TOKEN = "[Your Agent Name]";
|
|
@@ -1711,6 +1927,18 @@ This text is who you are for this person. As you learn them, their name, their w
|
|
|
1711
1927
|
__name2(isKnownProfile, "isKnownProfile");
|
|
1712
1928
|
__name(hasCapability, "hasCapability");
|
|
1713
1929
|
__name2(hasCapability, "hasCapability");
|
|
1930
|
+
__name(workflowJobId, "workflowJobId");
|
|
1931
|
+
__name2(workflowJobId, "workflowJobId");
|
|
1932
|
+
__name(assertNever, "assertNever");
|
|
1933
|
+
__name2(assertNever, "assertNever");
|
|
1934
|
+
__name(agentStepJobId, "agentStepJobId");
|
|
1935
|
+
__name2(agentStepJobId, "agentStepJobId");
|
|
1936
|
+
__name(heavyStepJobId, "heavyStepJobId");
|
|
1937
|
+
__name2(heavyStepJobId, "heavyStepJobId");
|
|
1938
|
+
__name(jobSpawnJobId, "jobSpawnJobId");
|
|
1939
|
+
__name2(jobSpawnJobId, "jobSpawnJobId");
|
|
1940
|
+
__name(exportAutoJobId, "exportAutoJobId");
|
|
1941
|
+
__name2(exportAutoJobId, "exportAutoJobId");
|
|
1714
1942
|
SYSTEM_USER_PREFIX = "system:";
|
|
1715
1943
|
__name(isSystemRun, "isSystemRun");
|
|
1716
1944
|
__name2(isSystemRun, "isSystemRun");
|
|
@@ -1796,8 +2024,130 @@ This text is who you are for this person. As you learn them, their name, their w
|
|
|
1796
2024
|
__name(scheduledWorkflowRunIdForTime, "scheduledWorkflowRunIdForTime");
|
|
1797
2025
|
__name2(scheduledWorkflowRunIdForTime, "scheduledWorkflowRunIdForTime");
|
|
1798
2026
|
WORKFLOW_SIGNAL_PAYLOAD_MAX_BYTES = 64 * 1024;
|
|
2027
|
+
WORKFLOW_RESOLVE_OUTPUT_MAX_BYTES = 256 * 1024;
|
|
2028
|
+
WORKFLOW_RETRY_BACKOFFS = [
|
|
2029
|
+
"fixed",
|
|
2030
|
+
"exponential"
|
|
2031
|
+
];
|
|
2032
|
+
WORKFLOW_RETRY_MIN_ATTEMPTS = 1;
|
|
2033
|
+
WORKFLOW_RETRY_POLICY_KEYS = [
|
|
2034
|
+
"maxAttempts",
|
|
2035
|
+
"backoffSeconds",
|
|
2036
|
+
"backoff",
|
|
2037
|
+
"maxBackoffSeconds"
|
|
2038
|
+
];
|
|
2039
|
+
WORKFLOW_RETRY_MAX_ATTEMPTS = 20;
|
|
2040
|
+
__name(workflowRetryMaxAttemptsMessage, "workflowRetryMaxAttemptsMessage");
|
|
2041
|
+
__name2(workflowRetryMaxAttemptsMessage, "workflowRetryMaxAttemptsMessage");
|
|
2042
|
+
__name(isWithinWorkflowRetryAttempts, "isWithinWorkflowRetryAttempts");
|
|
2043
|
+
__name2(isWithinWorkflowRetryAttempts, "isWithinWorkflowRetryAttempts");
|
|
2044
|
+
WORKFLOW_RETRY_ENGINE_KEYS = [
|
|
2045
|
+
"budgetBaseAttempt"
|
|
2046
|
+
];
|
|
2047
|
+
__name(workflowRetryUnknownMembersMessage, "workflowRetryUnknownMembersMessage");
|
|
2048
|
+
__name2(workflowRetryUnknownMembersMessage, "workflowRetryUnknownMembersMessage");
|
|
2049
|
+
__name(unknownWorkflowRetryMembers, "unknownWorkflowRetryMembers");
|
|
2050
|
+
__name2(unknownWorkflowRetryMembers, "unknownWorkflowRetryMembers");
|
|
2051
|
+
__name(authoredRetryPolicy, "authoredRetryPolicy");
|
|
2052
|
+
__name2(authoredRetryPolicy, "authoredRetryPolicy");
|
|
2053
|
+
__name(retryBudgetBaseAttempt, "retryBudgetBaseAttempt");
|
|
2054
|
+
__name2(retryBudgetBaseAttempt, "retryBudgetBaseAttempt");
|
|
2055
|
+
__name(retryBudgetAttempt, "retryBudgetAttempt");
|
|
2056
|
+
__name2(retryBudgetAttempt, "retryBudgetAttempt");
|
|
2057
|
+
__name(retryBudgetMaxAttempts, "retryBudgetMaxAttempts");
|
|
2058
|
+
__name2(retryBudgetMaxAttempts, "retryBudgetMaxAttempts");
|
|
2059
|
+
__name(retryBudgetRemaining, "retryBudgetRemaining");
|
|
2060
|
+
__name2(retryBudgetRemaining, "retryBudgetRemaining");
|
|
2061
|
+
__name(retriesRemaining, "retriesRemaining");
|
|
2062
|
+
__name2(retriesRemaining, "retriesRemaining");
|
|
2063
|
+
WORKFLOW_JOB_RESOURCES = [
|
|
2064
|
+
"small",
|
|
2065
|
+
"medium",
|
|
2066
|
+
"large"
|
|
2067
|
+
];
|
|
2068
|
+
WORKFLOW_SIDE_EFFECTS = [
|
|
2069
|
+
"none",
|
|
2070
|
+
"external"
|
|
2071
|
+
];
|
|
2072
|
+
WORKFLOW_JOB_RANGES = Object.freeze({
|
|
2073
|
+
/** Coding-turn cap (`claude -p --max-turns`); absent ⇒ the cluster default (LUA_WF_JOB_MAX_TURNS). */
|
|
2074
|
+
maxTurns: Object.freeze({
|
|
2075
|
+
min: 1,
|
|
2076
|
+
max: 500
|
|
2077
|
+
}),
|
|
2078
|
+
/** Harness messages per attempt (one per content block); absent ⇒ the cluster default (400). */
|
|
2079
|
+
maxMessages: Object.freeze({
|
|
2080
|
+
min: 1,
|
|
2081
|
+
max: 5e3
|
|
2082
|
+
}),
|
|
2083
|
+
/** Input-side tokens per attempt (prompt + cache); absent ⇒ the cluster default (4M — LUA-708, was 30M). */
|
|
2084
|
+
maxInputTokens: Object.freeze({
|
|
2085
|
+
min: 1e6,
|
|
2086
|
+
max: 5e8
|
|
2087
|
+
})
|
|
2088
|
+
});
|
|
2089
|
+
WORKFLOW_JOB_RANGE_MEMBERS = Object.keys(WORKFLOW_JOB_RANGES);
|
|
2090
|
+
__name(isWithinWorkflowJobRange, "isWithinWorkflowJobRange");
|
|
2091
|
+
__name2(isWithinWorkflowJobRange, "isWithinWorkflowJobRange");
|
|
2092
|
+
__name(workflowJobRangeMessage, "workflowJobRangeMessage");
|
|
2093
|
+
__name2(workflowJobRangeMessage, "workflowJobRangeMessage");
|
|
2094
|
+
WORKFLOW_SINGLE_STEP_TYPES = [
|
|
2095
|
+
"agent",
|
|
2096
|
+
"tool",
|
|
2097
|
+
"workflow",
|
|
2098
|
+
"step"
|
|
2099
|
+
];
|
|
2100
|
+
WORKFLOW_HITL_ENTRY_TYPES = [
|
|
2101
|
+
"approval",
|
|
2102
|
+
"waitForSignal"
|
|
2103
|
+
];
|
|
2104
|
+
WORKFLOW_ARM_ENTRY_TYPES = [
|
|
2105
|
+
...WORKFLOW_SINGLE_STEP_TYPES,
|
|
2106
|
+
...WORKFLOW_HITL_ENTRY_TYPES
|
|
2107
|
+
];
|
|
2108
|
+
WORKFLOW_HITL_ARM_CONTAINERS = [
|
|
2109
|
+
"parallel",
|
|
2110
|
+
"conditional",
|
|
2111
|
+
"foreach"
|
|
2112
|
+
];
|
|
2113
|
+
__name(isWorkflowSingleStepType, "isWorkflowSingleStepType");
|
|
2114
|
+
__name2(isWorkflowSingleStepType, "isWorkflowSingleStepType");
|
|
2115
|
+
__name(isWorkflowHitlEntryType, "isWorkflowHitlEntryType");
|
|
2116
|
+
__name2(isWorkflowHitlEntryType, "isWorkflowHitlEntryType");
|
|
2117
|
+
__name(isWorkflowArmEntryType, "isWorkflowArmEntryType");
|
|
2118
|
+
__name2(isWorkflowArmEntryType, "isWorkflowArmEntryType");
|
|
2119
|
+
__name(workflowContainerRunsHitlArm, "workflowContainerRunsHitlArm");
|
|
2120
|
+
__name2(workflowContainerRunsHitlArm, "workflowContainerRunsHitlArm");
|
|
2121
|
+
__name(workflowHitlArmUnsupportedMessage, "workflowHitlArmUnsupportedMessage");
|
|
2122
|
+
__name2(workflowHitlArmUnsupportedMessage, "workflowHitlArmUnsupportedMessage");
|
|
2123
|
+
__name(workflowHitlArmShapeMessage, "workflowHitlArmShapeMessage");
|
|
2124
|
+
__name2(workflowHitlArmShapeMessage, "workflowHitlArmShapeMessage");
|
|
2125
|
+
WORKFLOW_GRAPH_ENTRY_STEP_KINDS = Object.freeze({
|
|
2126
|
+
agent: "agent",
|
|
2127
|
+
tool: "tool",
|
|
2128
|
+
workflow: "subrun",
|
|
2129
|
+
step: "code",
|
|
2130
|
+
mapping: "map",
|
|
2131
|
+
sleep: "sleep",
|
|
2132
|
+
sleepUntil: "sleepUntil",
|
|
2133
|
+
parallel: null,
|
|
2134
|
+
conditional: "branch",
|
|
2135
|
+
foreach: "foreach",
|
|
2136
|
+
loop: "loop",
|
|
2137
|
+
approval: "approval",
|
|
2138
|
+
waitForSignal: "signal"
|
|
2139
|
+
});
|
|
2140
|
+
WORKFLOW_ARM_ENTRY_STEP_KINDS = Object.freeze(Object.fromEntries(WORKFLOW_ARM_ENTRY_TYPES.map((t) => [
|
|
2141
|
+
t,
|
|
2142
|
+
WORKFLOW_GRAPH_ENTRY_STEP_KINDS[t]
|
|
2143
|
+
])));
|
|
2144
|
+
WORKFLOW_BUDGET_MAX_DURATION_SECONDS = Object.freeze({
|
|
2145
|
+
min: 60,
|
|
2146
|
+
max: 2592e3
|
|
2147
|
+
});
|
|
1799
2148
|
REDACTED_PLACEHOLDER = "[REDACTED]";
|
|
1800
2149
|
PROVIDER_MESSAGE_MAX_CHARS = 300;
|
|
2150
|
+
ERROR_MESSAGE_MAX_CHARS = 2e3;
|
|
1801
2151
|
SECRET_LITERAL_PATTERNS = [
|
|
1802
2152
|
{
|
|
1803
2153
|
re: /\b(github_pat_)[A-Za-z0-9_]{16,}/g
|
|
@@ -1808,6 +2158,10 @@ This text is who you are for this person. As you learn them, their name, their w
|
|
|
1808
2158
|
{
|
|
1809
2159
|
re: /\b(glpat-)[A-Za-z0-9_-]{16,}/g
|
|
1810
2160
|
},
|
|
2161
|
+
// LUA-696 review (3): the npm granular / classic token (`npm_` + 36 alphanumerics).
|
|
2162
|
+
{
|
|
2163
|
+
re: /\b(npm_)[A-Za-z0-9]{36}\b/g
|
|
2164
|
+
},
|
|
1811
2165
|
{
|
|
1812
2166
|
re: /\b(sk-ant-)[A-Za-z0-9_-]{16,}/g
|
|
1813
2167
|
},
|
|
@@ -1844,8 +2198,15 @@ This text is who you are for this person. As you learn them, their name, their w
|
|
|
1844
2198
|
// `FOO_TOKEN=x`, `secretKey=x`, `password=x`. Groups: the char before the name, the name, the separator
|
|
1845
2199
|
// (with its quotes), the scheme word — all kept; the value goes. A value a literal rule already replaced
|
|
1846
2200
|
// (`x-access-token:[REDACTED]@host`) is left alone so the host after it survives.
|
|
2201
|
+
// LUA-696: the second lookahead keeps a scrubbed `Authorization: Bearer [REDACTED]` as it is — without it the
|
|
2202
|
+
// optional scheme group backtracks to empty and `Bearer` itself becomes the value (`[REDACTED] [REDACTED]`).
|
|
2203
|
+
// The scrub is applied more than once on purpose (the Job site at its copy, the outcome table at the row
|
|
2204
|
+
// write, lua-api on the way out), so it must be idempotent.
|
|
2205
|
+
// LUA-696 review (HIGH): the identifier prefix is BOUNDED (`{1,64}`) — unbounded, `[A-Za-z0-9-]+` made the
|
|
2206
|
+
// scan quadratic on `-`-heavy text (100 KB of `-` took 12.7 s on the lua-core event loop; a worker-tier step
|
|
2207
|
+
// can throw that). No identifier that names a credential is longer; the probe list is unchanged.
|
|
1847
2208
|
{
|
|
1848
|
-
re: new RegExp(`(^|[^A-Za-z0-9])((?:[A-Za-z0-9-]
|
|
2209
|
+
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")
|
|
1849
2210
|
},
|
|
1850
2211
|
// `?token=x`, `&key=x`, `&X-Amz-Signature=x`, `&sig=x`
|
|
1851
2212
|
{
|
|
@@ -1864,16 +2225,47 @@ This text is who you are for this person. As you learn them, their name, their w
|
|
|
1864
2225
|
GROUP_COUNT = /* @__PURE__ */ new WeakMap();
|
|
1865
2226
|
__name(groupCount, "groupCount");
|
|
1866
2227
|
__name2(groupCount, "groupCount");
|
|
2228
|
+
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;
|
|
2229
|
+
WORKFLOW_RESERVED_SECRET_KEYS = Object.freeze([
|
|
2230
|
+
"secret",
|
|
2231
|
+
"token",
|
|
2232
|
+
"password",
|
|
2233
|
+
"passwd",
|
|
2234
|
+
"pwd",
|
|
2235
|
+
"passphrase",
|
|
2236
|
+
"api_key",
|
|
2237
|
+
"apikey",
|
|
2238
|
+
"access_key",
|
|
2239
|
+
"secret_key",
|
|
2240
|
+
"private_key",
|
|
2241
|
+
"client_secret",
|
|
2242
|
+
"authorization",
|
|
2243
|
+
"auth_token",
|
|
2244
|
+
"access_token",
|
|
2245
|
+
"id_token",
|
|
2246
|
+
"refresh_token",
|
|
2247
|
+
"session_key",
|
|
2248
|
+
"credential",
|
|
2249
|
+
"credentials"
|
|
2250
|
+
]);
|
|
2251
|
+
__name(isWorkflowSecretKey, "isWorkflowSecretKey");
|
|
2252
|
+
__name2(isWorkflowSecretKey, "isWorkflowSecretKey");
|
|
1867
2253
|
__name(applyPatterns, "applyPatterns");
|
|
1868
2254
|
__name2(applyPatterns, "applyPatterns");
|
|
1869
2255
|
__name(scrubSecretText, "scrubSecretText");
|
|
1870
2256
|
__name2(scrubSecretText, "scrubSecretText");
|
|
2257
|
+
SCRUB_INPUT_MAX_CHARS = 16 * 1024;
|
|
2258
|
+
SCRUB_CUT_BACKOFF_CHARS = 256;
|
|
2259
|
+
__name(boundScrubInput, "boundScrubInput");
|
|
2260
|
+
__name2(boundScrubInput, "boundScrubInput");
|
|
1871
2261
|
__name(scrubSecretLines, "scrubSecretLines");
|
|
1872
2262
|
__name2(scrubSecretLines, "scrubSecretLines");
|
|
1873
2263
|
__name(messageText, "messageText");
|
|
1874
2264
|
__name2(messageText, "messageText");
|
|
1875
2265
|
__name(scrubProviderMessage, "scrubProviderMessage");
|
|
1876
2266
|
__name2(scrubProviderMessage, "scrubProviderMessage");
|
|
2267
|
+
__name(scrubStepErrorMessage, "scrubStepErrorMessage");
|
|
2268
|
+
__name2(scrubStepErrorMessage, "scrubStepErrorMessage");
|
|
1877
2269
|
WORKFLOW_AUDIT_EVENTS = [
|
|
1878
2270
|
// --- definitions / versions / templates (13, 11 §11.11.5) ---
|
|
1879
2271
|
"workflow.published",
|
|
@@ -1940,6 +2332,8 @@ This text is who you are for this person. As you learn them, their name, their w
|
|
|
1940
2332
|
"workflow.goal.resumed",
|
|
1941
2333
|
"workflow.goal.done",
|
|
1942
2334
|
"workflow.goal.closed",
|
|
2335
|
+
// LUA-760: an ended goal's cadence Job retired (deleted) — inline on done / closed, by R21 / R28, or by sweep #30
|
|
2336
|
+
"workflow.goal.job_retired",
|
|
1943
2337
|
// --- org policy (02 §2.10 / 09 R23) ---
|
|
1944
2338
|
"workflow.policy.retention_changed",
|
|
1945
2339
|
"workflow.policy.pacing_changed",
|
|
@@ -1989,6 +2383,79 @@ listed here; never invent a target.`;
|
|
|
1989
2383
|
__name2(reachingIt, "reachingIt");
|
|
1990
2384
|
__name(renderTargetsBlock, "renderTargetsBlock");
|
|
1991
2385
|
__name2(renderTargetsBlock, "renderTargetsBlock");
|
|
2386
|
+
WORKFLOW_APPROVAL_OUTPUT_DECISIONS = [
|
|
2387
|
+
"approved",
|
|
2388
|
+
"denied",
|
|
2389
|
+
"timed_out"
|
|
2390
|
+
];
|
|
2391
|
+
WORKFLOW_APPROVAL_OUTPUT_SCHEMA = {
|
|
2392
|
+
type: "object",
|
|
2393
|
+
properties: {
|
|
2394
|
+
approved: {
|
|
2395
|
+
type: "boolean"
|
|
2396
|
+
},
|
|
2397
|
+
decision: {
|
|
2398
|
+
type: "string",
|
|
2399
|
+
enum: [
|
|
2400
|
+
...WORKFLOW_APPROVAL_OUTPUT_DECISIONS
|
|
2401
|
+
]
|
|
2402
|
+
},
|
|
2403
|
+
/** the approver's note when given, else the decision word — what `${stepResults.<id>.text}` reads */
|
|
2404
|
+
text: {
|
|
2405
|
+
type: "string"
|
|
2406
|
+
},
|
|
2407
|
+
note: {
|
|
2408
|
+
type: "string"
|
|
2409
|
+
},
|
|
2410
|
+
editedPayload: {},
|
|
2411
|
+
editRevision: {
|
|
2412
|
+
type: "integer"
|
|
2413
|
+
},
|
|
2414
|
+
decidedBy: {
|
|
2415
|
+
type: "object",
|
|
2416
|
+
properties: {
|
|
2417
|
+
id: {
|
|
2418
|
+
type: "string"
|
|
2419
|
+
},
|
|
2420
|
+
kind: {
|
|
2421
|
+
type: "string"
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2424
|
+
},
|
|
2425
|
+
timedOut: {
|
|
2426
|
+
type: "boolean"
|
|
2427
|
+
},
|
|
2428
|
+
escalations: {
|
|
2429
|
+
type: "integer"
|
|
2430
|
+
},
|
|
2431
|
+
evidence: {
|
|
2432
|
+
type: "array",
|
|
2433
|
+
items: {
|
|
2434
|
+
type: "string"
|
|
2435
|
+
}
|
|
2436
|
+
},
|
|
2437
|
+
items: {
|
|
2438
|
+
type: "array",
|
|
2439
|
+
items: {
|
|
2440
|
+
type: "object"
|
|
2441
|
+
}
|
|
2442
|
+
}
|
|
2443
|
+
},
|
|
2444
|
+
required: [
|
|
2445
|
+
"approved",
|
|
2446
|
+
"decision",
|
|
2447
|
+
"text"
|
|
2448
|
+
]
|
|
2449
|
+
};
|
|
2450
|
+
__name(workflowApprovalDecisionOf, "workflowApprovalDecisionOf");
|
|
2451
|
+
__name2(workflowApprovalDecisionOf, "workflowApprovalDecisionOf");
|
|
2452
|
+
__name(workflowApprovalOutput, "workflowApprovalOutput");
|
|
2453
|
+
__name2(workflowApprovalOutput, "workflowApprovalOutput");
|
|
2454
|
+
__name(isWorkflowApprovalOutput, "isWorkflowApprovalOutput");
|
|
2455
|
+
__name2(isWorkflowApprovalOutput, "isWorkflowApprovalOutput");
|
|
2456
|
+
JSON_FENCE_RE = /```(?:json)?[ \t]*\r?\n([\s\S]*?)\r?\n?```/g;
|
|
2457
|
+
__name(extractSingleJsonValue, "extractSingleJsonValue");
|
|
2458
|
+
__name2(extractSingleJsonValue, "extractSingleJsonValue");
|
|
1992
2459
|
}
|
|
1993
2460
|
});
|
|
1994
2461
|
|
|
@@ -1997,151 +2464,375 @@ import { createHash } from "crypto";
|
|
|
1997
2464
|
import { z as z4 } from "zod";
|
|
1998
2465
|
import { z as z22 } from "zod";
|
|
1999
2466
|
import { createHash as createHash2 } from "crypto";
|
|
2000
|
-
function
|
|
2001
|
-
return
|
|
2002
|
-
}
|
|
2003
|
-
function fillPolicy(node, defaultTimeout) {
|
|
2004
|
-
if (node.tier === void 0 && node.workspace !== void 0 && node.workspace !== "inherit") node.tier = "job";
|
|
2005
|
-
if (node.timeoutSeconds === void 0) node.timeoutSeconds = node.tier === "job" ? WORKFLOW_JOB_DEFAULT_TIMEOUT_SECONDS : defaultTimeout;
|
|
2006
|
-
if (node.retry === void 0) node.retry = {
|
|
2007
|
-
maxAttempts: 1
|
|
2008
|
-
};
|
|
2009
|
-
if (node.onError === void 0) node.onError = "fail";
|
|
2010
|
-
if ((node.type === "step" || node.type === "tool") && node.sideEffects === void 0) node.sideEffects = "none";
|
|
2467
|
+
function isMapConfigObject(v) {
|
|
2468
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
2011
2469
|
}
|
|
2012
|
-
function
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
const s = node;
|
|
2017
|
-
if (s.resumeTimeoutHours === void 0) s.resumeTimeoutHours = WORKFLOW_SUSPEND_DEFAULT_TIMEOUT_HOURS;
|
|
2018
|
-
if (s.onSuspendTimeout === void 0) s.onSuspendTimeout = "fail";
|
|
2019
|
-
return;
|
|
2020
|
-
}
|
|
2021
|
-
case "agent":
|
|
2022
|
-
fillPolicy(node, WORKFLOW_AGENT_DEFAULT_TIMEOUT_SECONDS);
|
|
2023
|
-
return;
|
|
2024
|
-
case "tool":
|
|
2025
|
-
fillPolicy(node, WORKFLOW_STEP_DEFAULT_TIMEOUT_SECONDS);
|
|
2026
|
-
return;
|
|
2027
|
-
case "workflow":
|
|
2028
|
-
if (node.workflowId === WORKFLOW_ARM_SUBRUN_ID && Array.isArray(node.graph) && node.graph[1]) fillSingle(node.graph[1]);
|
|
2029
|
-
return;
|
|
2470
|
+
function parseMapConfig(raw, stepId) {
|
|
2471
|
+
if (isMapConfigObject(raw)) return raw;
|
|
2472
|
+
if (typeof raw !== "string") {
|
|
2473
|
+
throw new Error(`Stored mapping step "${stepId}" has a mapConfig that is neither a JSON string nor an object.`);
|
|
2030
2474
|
}
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
}
|
|
2035
|
-
function fillEntry(entry) {
|
|
2036
|
-
switch (entry.type) {
|
|
2037
|
-
case "step":
|
|
2038
|
-
case "agent":
|
|
2039
|
-
case "tool":
|
|
2040
|
-
case "workflow":
|
|
2041
|
-
fillSingle(entry);
|
|
2042
|
-
return;
|
|
2043
|
-
case "parallel":
|
|
2044
|
-
entry.steps.forEach(fillSingle);
|
|
2045
|
-
return;
|
|
2046
|
-
case "conditional": {
|
|
2047
|
-
const c = entry;
|
|
2048
|
-
if (c.exclusive === void 0) c.exclusive = false;
|
|
2049
|
-
c.steps.forEach(fillArm);
|
|
2050
|
-
if (c.otherwise) fillArm(c.otherwise);
|
|
2051
|
-
return;
|
|
2052
|
-
}
|
|
2053
|
-
case "foreach": {
|
|
2054
|
-
const f = entry;
|
|
2055
|
-
f.opts = f.opts ?? {};
|
|
2056
|
-
if (f.opts.concurrency === void 0) f.opts.concurrency = WORKFLOW_FOREACH_DEFAULT_CONCURRENCY;
|
|
2057
|
-
if (f.opts.maxItems === void 0) f.opts.maxItems = WORKFLOW_FOREACH_DEFAULT_MAX_ITEMS;
|
|
2058
|
-
fillSingle(f.step);
|
|
2059
|
-
return;
|
|
2060
|
-
}
|
|
2061
|
-
case "loop": {
|
|
2062
|
-
const l = entry;
|
|
2063
|
-
if (l.maxIterations === void 0) l.maxIterations = WORKFLOW_LOOP_DEFAULT_MAX_ITERATIONS;
|
|
2064
|
-
fillSingle(l.step);
|
|
2065
|
-
return;
|
|
2066
|
-
}
|
|
2067
|
-
case "approval": {
|
|
2068
|
-
const a = entry;
|
|
2069
|
-
if (a.approver === void 0) a.approver = "creator";
|
|
2070
|
-
if (a.timeoutHours === void 0) a.timeoutHours = WORKFLOW_SUSPEND_DEFAULT_TIMEOUT_HOURS;
|
|
2071
|
-
if (a.onTimeout === void 0) a.onTimeout = "deny";
|
|
2072
|
-
if (a.onDeny === void 0) a.onDeny = "continue";
|
|
2073
|
-
if (a.excludeInitiator === void 0) a.excludeInitiator = false;
|
|
2074
|
-
if (a.editable === void 0) a.editable = false;
|
|
2075
|
-
return;
|
|
2076
|
-
}
|
|
2077
|
-
case "waitForSignal": {
|
|
2078
|
-
const w = entry;
|
|
2079
|
-
if (w.timeoutHours === void 0) w.timeoutHours = WORKFLOW_SUSPEND_DEFAULT_TIMEOUT_HOURS;
|
|
2080
|
-
if (w.onTimeout === void 0) w.onTimeout = "fail";
|
|
2081
|
-
if (w.acceptedSources === void 0) w.acceptedSources = [
|
|
2082
|
-
...WORKFLOW_SIGNAL_DEFAULT_SOURCES
|
|
2083
|
-
];
|
|
2084
|
-
return;
|
|
2085
|
-
}
|
|
2086
|
-
case "mapping":
|
|
2087
|
-
case "sleep":
|
|
2088
|
-
case "sleepUntil":
|
|
2089
|
-
return;
|
|
2475
|
+
try {
|
|
2476
|
+
return JSON.parse(raw);
|
|
2477
|
+
} catch (e) {
|
|
2478
|
+
throw new Error(`Stored mapping step "${stepId}" has invalid JSON mapConfig: ${e.message}`);
|
|
2090
2479
|
}
|
|
2091
2480
|
}
|
|
2092
|
-
function
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
return
|
|
2096
|
-
}
|
|
2097
|
-
function isConnectionKeyShaped(value22) {
|
|
2098
|
-
return WORKFLOW_CONNECTION_KEY_RE.test(value22) && !CONNECTION_ID_HEX_RE.test(value22);
|
|
2481
|
+
function mapConfigWire(raw) {
|
|
2482
|
+
if (typeof raw === "string") return raw;
|
|
2483
|
+
if (isMapConfigObject(raw)) return canonicalJson(raw);
|
|
2484
|
+
return void 0;
|
|
2099
2485
|
}
|
|
2100
|
-
function
|
|
2101
|
-
return
|
|
2486
|
+
function describeBadPlaceholder(template22, idx, rawExpr) {
|
|
2487
|
+
return `Template placeholder #${idx} (\${${rawExpr}}) in '${template22}'`;
|
|
2102
2488
|
}
|
|
2103
|
-
function
|
|
2104
|
-
const
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
return null;
|
|
2489
|
+
function parseTemplatePlaceholder(rawExpr) {
|
|
2490
|
+
const dot = rawExpr.indexOf(".");
|
|
2491
|
+
return {
|
|
2492
|
+
scope: dot === -1 ? rawExpr : rawExpr.slice(0, dot),
|
|
2493
|
+
rest: dot === -1 ? "" : rawExpr.slice(dot + 1)
|
|
2494
|
+
};
|
|
2110
2495
|
}
|
|
2111
|
-
function
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
cur = next;
|
|
2496
|
+
function traverseMappingPath(root, path3, errorLabel) {
|
|
2497
|
+
if (path3 === "" || path3 === ".") return root;
|
|
2498
|
+
const parts = path3.split(".");
|
|
2499
|
+
let value22 = root;
|
|
2500
|
+
for (const part of parts) {
|
|
2501
|
+
if (typeof value22 === "object" && value22 !== null) value22 = value22[part];
|
|
2502
|
+
else throw new WorkflowTemplateError(`Invalid path ${path3} in ${errorLabel}`, path3);
|
|
2119
2503
|
}
|
|
2120
|
-
return
|
|
2121
|
-
}
|
|
2122
|
-
function templateStepRefs(text) {
|
|
2123
|
-
const ids = [];
|
|
2124
|
-
for (const m of text.matchAll(TEMPLATE_STEP_REF)) ids.push(m[1]);
|
|
2125
|
-
return ids;
|
|
2504
|
+
return value22;
|
|
2126
2505
|
}
|
|
2127
|
-
function
|
|
2128
|
-
if (
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
const ids = [];
|
|
2136
|
-
for (const d of Object.values(cfg)) {
|
|
2137
|
-
if (!d || typeof d !== "object") continue;
|
|
2138
|
-
const desc = d;
|
|
2139
|
-
if (desc.step !== void 0) ids.push(...Array.isArray(desc.step) ? desc.step : [
|
|
2140
|
-
desc.step
|
|
2141
|
-
]);
|
|
2142
|
-
if (typeof desc.template === "string") ids.push(...templateStepRefs(desc.template));
|
|
2506
|
+
function stringifyTemplateValue(v, template22, idx, rawExpr) {
|
|
2507
|
+
if (v === null || v === void 0) return "";
|
|
2508
|
+
if (typeof v === "object") {
|
|
2509
|
+
try {
|
|
2510
|
+
return JSON.stringify(v);
|
|
2511
|
+
} catch (err) {
|
|
2512
|
+
throw new WorkflowTemplateError(`${describeBadPlaceholder(template22, idx, rawExpr)} resolved to a value that could not be JSON-stringified (${err.message}).`, rawExpr);
|
|
2513
|
+
}
|
|
2143
2514
|
}
|
|
2144
|
-
return
|
|
2515
|
+
return String(v);
|
|
2516
|
+
}
|
|
2517
|
+
function escapeFence(content) {
|
|
2518
|
+
return content.replace(/<\/lua-data/g, "<\\/lua-data");
|
|
2519
|
+
}
|
|
2520
|
+
function fenceBlock(name, source, content) {
|
|
2521
|
+
return `<lua-data name="${name}" source="${source}" untrusted="true">${escapeFence(content)}</lua-data>`;
|
|
2522
|
+
}
|
|
2523
|
+
function renderTemplate(template22, ctx, opts) {
|
|
2524
|
+
let idx = 0;
|
|
2525
|
+
return template22.replace(TEMPLATE_PLACEHOLDER, (_match, rawExpr) => {
|
|
2526
|
+
idx += 1;
|
|
2527
|
+
const { scope, rest } = parseTemplatePlaceholder(rawExpr);
|
|
2528
|
+
const label = describeBadPlaceholder(template22, idx, rawExpr);
|
|
2529
|
+
let rendered;
|
|
2530
|
+
let source;
|
|
2531
|
+
switch (scope) {
|
|
2532
|
+
case "initData":
|
|
2533
|
+
rendered = stringifyTemplateValue(traverseMappingPath(ctx.initData, rest, label), template22, idx, rawExpr);
|
|
2534
|
+
source = "initData";
|
|
2535
|
+
break;
|
|
2536
|
+
case "state":
|
|
2537
|
+
rendered = stringifyTemplateValue(traverseMappingPath(ctx.state, rest, label), template22, idx, rawExpr);
|
|
2538
|
+
source = "state";
|
|
2539
|
+
break;
|
|
2540
|
+
case "requestContext":
|
|
2541
|
+
rendered = stringifyTemplateValue(traverseMappingPath(ctx.requestContext, rest, label), template22, idx, rawExpr);
|
|
2542
|
+
source = "requestContext";
|
|
2543
|
+
break;
|
|
2544
|
+
case "stepResults": {
|
|
2545
|
+
const innerDot = rest.indexOf(".");
|
|
2546
|
+
const stepId = innerDot === -1 ? rest : rest.slice(0, innerDot);
|
|
2547
|
+
const subPath = innerDot === -1 ? "" : rest.slice(innerDot + 1);
|
|
2548
|
+
if (!stepId) throw new WorkflowTemplateError(`${label} must name a step: \${stepResults.<stepId>.<path>}.`, rawExpr);
|
|
2549
|
+
if (!(stepId in ctx.stepResults) || ctx.stepResults[stepId] == null) {
|
|
2550
|
+
throw new WorkflowTemplateError(`${label} references stepResults.${stepId} but step "${stepId}" has no resolvable output (not an ancestor, not run, failed, or produced no output).`, rawExpr);
|
|
2551
|
+
}
|
|
2552
|
+
rendered = stringifyTemplateValue(traverseMappingPath(ctx.stepResults[stepId], subPath, label), template22, idx, rawExpr);
|
|
2553
|
+
source = `step:${stepId}`;
|
|
2554
|
+
break;
|
|
2555
|
+
}
|
|
2556
|
+
default:
|
|
2557
|
+
throw new WorkflowTemplateError(`${label} references unknown namespace "${scope}". Use one of: ${TEMPLATE_NAMESPACES.join(", ")}.`, rawExpr);
|
|
2558
|
+
}
|
|
2559
|
+
return opts.fenced ? fenceBlock(rawExpr, source, rendered) : rendered;
|
|
2560
|
+
});
|
|
2561
|
+
}
|
|
2562
|
+
function isMapDescriptor(v) {
|
|
2563
|
+
if (v === null || typeof v !== "object" || Array.isArray(v)) return false;
|
|
2564
|
+
const d = v;
|
|
2565
|
+
const keys = Object.keys(d);
|
|
2566
|
+
const only = /* @__PURE__ */ __name3((...allowed) => keys.every((k) => allowed.includes(k)), "only");
|
|
2567
|
+
if ("value" in d) return keys.length === 1;
|
|
2568
|
+
if ("template" in d) return keys.length === 1 && typeof d.template === "string";
|
|
2569
|
+
if ("requestContextPath" in d) return keys.length === 1 && typeof d.requestContextPath === "string";
|
|
2570
|
+
if ("knowledge" in d) return keys.length === 1 && typeof d.knowledge === "object" && d.knowledge !== null;
|
|
2571
|
+
if ("initData" in d) return d.initData === true && typeof d.path === "string" && only("initData", "path");
|
|
2572
|
+
if ("step" in d) {
|
|
2573
|
+
const stepOk = typeof d.step === "string" || Array.isArray(d.step) && d.step.every((x) => typeof x === "string");
|
|
2574
|
+
return stepOk && typeof d.path === "string" && only("step", "path", "rows");
|
|
2575
|
+
}
|
|
2576
|
+
return false;
|
|
2577
|
+
}
|
|
2578
|
+
function malformedMapMembers(cfg) {
|
|
2579
|
+
if (!cfg || typeof cfg !== "object" || Array.isArray(cfg)) return [];
|
|
2580
|
+
const out = [];
|
|
2581
|
+
for (const [member, v] of Object.entries(cfg)) {
|
|
2582
|
+
if (!v || typeof v !== "object" || Array.isArray(v) || isMapDescriptor(v)) continue;
|
|
2583
|
+
const keys = Object.keys(v).filter((k) => MAP_DESCRIPTOR_KEYS.includes(k));
|
|
2584
|
+
if (keys.length > 0) out.push({
|
|
2585
|
+
member,
|
|
2586
|
+
keys
|
|
2587
|
+
});
|
|
2588
|
+
}
|
|
2589
|
+
return out;
|
|
2590
|
+
}
|
|
2591
|
+
function mapMemberMalformedMessage(id, m) {
|
|
2592
|
+
const keys = m.keys.map((k) => `\`${k}\``).join(", ");
|
|
2593
|
+
return `"${id}".${m.member} carries descriptor key${m.keys.length === 1 ? "" : "s"} ${keys} but is not an exact binding form ({initData:true, path} | {step, path[, rows]} | {value} | {template} | {requestContextPath} | {knowledge}) \u2014 it is passed to the step verbatim as a literal; fix the descriptor, or wrap it in {value: \u2026} if the literal is intended`;
|
|
2594
|
+
}
|
|
2595
|
+
function resolveDescriptor(key, m, ctx) {
|
|
2596
|
+
if (!isMapDescriptor(m)) return {
|
|
2597
|
+
value: m
|
|
2598
|
+
};
|
|
2599
|
+
try {
|
|
2600
|
+
if ("value" in m) return {
|
|
2601
|
+
value: m.value
|
|
2602
|
+
};
|
|
2603
|
+
if ("template" in m && typeof m.template === "string") {
|
|
2604
|
+
return {
|
|
2605
|
+
value: renderTemplate(m.template, ctx, {
|
|
2606
|
+
fenced: false
|
|
2607
|
+
})
|
|
2608
|
+
};
|
|
2609
|
+
}
|
|
2610
|
+
if ("knowledge" in m || "rows" in m && m.rows !== void 0) {
|
|
2611
|
+
return {
|
|
2612
|
+
error: "binding_unresolved",
|
|
2613
|
+
key
|
|
2614
|
+
};
|
|
2615
|
+
}
|
|
2616
|
+
if ("requestContextPath" in m) {
|
|
2617
|
+
const label = `requestContext path for key "${key}"`;
|
|
2618
|
+
return {
|
|
2619
|
+
value: traverseMappingPath(ctx.requestContext, m.requestContextPath, label)
|
|
2620
|
+
};
|
|
2621
|
+
}
|
|
2622
|
+
if ("path" in m) {
|
|
2623
|
+
const source = "initData" in m && m.initData ? "initData" : "step";
|
|
2624
|
+
if (source === "initData") {
|
|
2625
|
+
return {
|
|
2626
|
+
value: traverseMappingPath(ctx.initData, m.path, `initData for key "${key}"`)
|
|
2627
|
+
};
|
|
2628
|
+
}
|
|
2629
|
+
const stepRef = m.step;
|
|
2630
|
+
const candidates = Array.isArray(stepRef) ? stepRef : [
|
|
2631
|
+
stepRef
|
|
2632
|
+
];
|
|
2633
|
+
const stepId = candidates.find((s) => ctx.stepResults[s] !== void 0 && ctx.stepResults[s] !== null);
|
|
2634
|
+
if (stepId === void 0) return {
|
|
2635
|
+
error: "binding_unresolved",
|
|
2636
|
+
key
|
|
2637
|
+
};
|
|
2638
|
+
return {
|
|
2639
|
+
value: traverseMappingPath(ctx.stepResults[stepId], m.path, `step ${candidates.join("|")} for key "${key}"`)
|
|
2640
|
+
};
|
|
2641
|
+
}
|
|
2642
|
+
return {
|
|
2643
|
+
error: "binding_unresolved",
|
|
2644
|
+
key
|
|
2645
|
+
};
|
|
2646
|
+
} catch (err) {
|
|
2647
|
+
if (err instanceof WorkflowTemplateError) return {
|
|
2648
|
+
error: "binding_unresolved",
|
|
2649
|
+
key
|
|
2650
|
+
};
|
|
2651
|
+
throw err;
|
|
2652
|
+
}
|
|
2653
|
+
}
|
|
2654
|
+
function resolveMapping(cfg, ctx) {
|
|
2655
|
+
const keys = Object.keys(cfg);
|
|
2656
|
+
if (keys.length === 1 && keys[0] === "") {
|
|
2657
|
+
return resolveDescriptor("", cfg[""], ctx);
|
|
2658
|
+
}
|
|
2659
|
+
const result = {};
|
|
2660
|
+
for (const key of keys) {
|
|
2661
|
+
const resolved = resolveDescriptor(key, cfg[key], ctx);
|
|
2662
|
+
if ("error" in resolved) return resolved;
|
|
2663
|
+
result[key] = resolved.value;
|
|
2664
|
+
}
|
|
2665
|
+
return {
|
|
2666
|
+
value: result
|
|
2667
|
+
};
|
|
2668
|
+
}
|
|
2669
|
+
function workspaceTemplatePath(template22) {
|
|
2670
|
+
const key = template22.trim();
|
|
2671
|
+
const expr = WORKSPACE_TEMPLATE_EXPR_RE.exec(key);
|
|
2672
|
+
if (expr) return expr[1].split(".");
|
|
2673
|
+
if (key.includes("${")) return void 0;
|
|
2674
|
+
return key.replace(/^(?:input|initData)\./, "").split(".");
|
|
2675
|
+
}
|
|
2676
|
+
function retryBackoffs() {
|
|
2677
|
+
if (!Array.isArray(WORKFLOW_RETRY_BACKOFFS)) {
|
|
2678
|
+
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')");
|
|
2679
|
+
}
|
|
2680
|
+
return WORKFLOW_RETRY_BACKOFFS;
|
|
2681
|
+
}
|
|
2682
|
+
function sleepUntilUnsupportedMessage(id) {
|
|
2683
|
+
return `the engine does not execute \`sleepUntil\` yet (node "${id}") \u2014 replace it with a \`sleep\` node with a \`duration\` in ms, e.g. { type: 'sleep', id: '${id}', duration: ${SLEEP_UNTIL_REPLACEMENT.duration} }`;
|
|
2684
|
+
}
|
|
2685
|
+
function fillPolicy(node, defaultTimeout) {
|
|
2686
|
+
if (node.tier === void 0 && node.workspace !== void 0 && node.workspace !== "inherit") node.tier = "job";
|
|
2687
|
+
if (node.timeoutSeconds === void 0) node.timeoutSeconds = node.tier === "job" ? WORKFLOW_JOB_DEFAULT_TIMEOUT_SECONDS : defaultTimeout;
|
|
2688
|
+
if (node.retry === void 0) node.retry = {
|
|
2689
|
+
maxAttempts: 1
|
|
2690
|
+
};
|
|
2691
|
+
if (node.onError === void 0) node.onError = "fail";
|
|
2692
|
+
if ((node.type === "step" || node.type === "tool") && node.sideEffects === void 0) node.sideEffects = "none";
|
|
2693
|
+
}
|
|
2694
|
+
function fillSingle(node) {
|
|
2695
|
+
switch (node.type) {
|
|
2696
|
+
case "step": {
|
|
2697
|
+
fillPolicy(node, WORKFLOW_STEP_DEFAULT_TIMEOUT_SECONDS);
|
|
2698
|
+
const s = node;
|
|
2699
|
+
if (s.resumeTimeoutHours === void 0) s.resumeTimeoutHours = WORKFLOW_SUSPEND_DEFAULT_TIMEOUT_HOURS;
|
|
2700
|
+
if (s.onSuspendTimeout === void 0) s.onSuspendTimeout = "fail";
|
|
2701
|
+
return;
|
|
2702
|
+
}
|
|
2703
|
+
case "agent":
|
|
2704
|
+
fillPolicy(node, WORKFLOW_AGENT_DEFAULT_TIMEOUT_SECONDS);
|
|
2705
|
+
return;
|
|
2706
|
+
case "tool":
|
|
2707
|
+
fillPolicy(node, WORKFLOW_STEP_DEFAULT_TIMEOUT_SECONDS);
|
|
2708
|
+
return;
|
|
2709
|
+
case "workflow":
|
|
2710
|
+
if (node.workflowId === WORKFLOW_ARM_SUBRUN_ID && Array.isArray(node.graph) && node.graph[1]) fillSingle(node.graph[1]);
|
|
2711
|
+
return;
|
|
2712
|
+
}
|
|
2713
|
+
}
|
|
2714
|
+
function fillHitl(node) {
|
|
2715
|
+
if (node.type === "approval") {
|
|
2716
|
+
const a = node;
|
|
2717
|
+
if (a.approver === void 0) a.approver = "creator";
|
|
2718
|
+
if (a.timeoutHours === void 0) a.timeoutHours = WORKFLOW_SUSPEND_DEFAULT_TIMEOUT_HOURS;
|
|
2719
|
+
if (a.onTimeout === void 0) a.onTimeout = "deny";
|
|
2720
|
+
if (a.onDeny === void 0) a.onDeny = "continue";
|
|
2721
|
+
if (a.excludeInitiator === void 0) a.excludeInitiator = false;
|
|
2722
|
+
if (a.editable === void 0) a.editable = false;
|
|
2723
|
+
return;
|
|
2724
|
+
}
|
|
2725
|
+
const w = node;
|
|
2726
|
+
if (w.timeoutHours === void 0) w.timeoutHours = WORKFLOW_SUSPEND_DEFAULT_TIMEOUT_HOURS;
|
|
2727
|
+
if (w.onTimeout === void 0) w.onTimeout = "fail";
|
|
2728
|
+
if (w.acceptedSources === void 0) w.acceptedSources = [
|
|
2729
|
+
...WORKFLOW_SIGNAL_DEFAULT_SOURCES
|
|
2730
|
+
];
|
|
2731
|
+
}
|
|
2732
|
+
function fillArm(arm) {
|
|
2733
|
+
if (arm.type === "mapping") return;
|
|
2734
|
+
if (isHitlNode(arm)) fillHitl(arm);
|
|
2735
|
+
else fillSingle(arm);
|
|
2736
|
+
}
|
|
2737
|
+
function fillEntry(entry) {
|
|
2738
|
+
switch (entry.type) {
|
|
2739
|
+
case "step":
|
|
2740
|
+
case "agent":
|
|
2741
|
+
case "tool":
|
|
2742
|
+
case "workflow":
|
|
2743
|
+
fillSingle(entry);
|
|
2744
|
+
return;
|
|
2745
|
+
case "parallel":
|
|
2746
|
+
entry.steps.forEach(fillArm);
|
|
2747
|
+
return;
|
|
2748
|
+
case "conditional": {
|
|
2749
|
+
const c = entry;
|
|
2750
|
+
if (c.exclusive === void 0) c.exclusive = false;
|
|
2751
|
+
c.steps.forEach(fillArm);
|
|
2752
|
+
if (c.otherwise) fillArm(c.otherwise);
|
|
2753
|
+
return;
|
|
2754
|
+
}
|
|
2755
|
+
case "foreach": {
|
|
2756
|
+
const f = entry;
|
|
2757
|
+
f.opts = f.opts ?? {};
|
|
2758
|
+
if (f.opts.concurrency === void 0) f.opts.concurrency = WORKFLOW_FOREACH_DEFAULT_CONCURRENCY;
|
|
2759
|
+
if (f.opts.maxItems === void 0) f.opts.maxItems = WORKFLOW_FOREACH_DEFAULT_MAX_ITEMS;
|
|
2760
|
+
fillArm(f.step);
|
|
2761
|
+
return;
|
|
2762
|
+
}
|
|
2763
|
+
case "loop": {
|
|
2764
|
+
const l = entry;
|
|
2765
|
+
if (l.maxIterations === void 0) l.maxIterations = WORKFLOW_LOOP_DEFAULT_MAX_ITERATIONS;
|
|
2766
|
+
fillArm(l.step);
|
|
2767
|
+
return;
|
|
2768
|
+
}
|
|
2769
|
+
case "approval":
|
|
2770
|
+
case "waitForSignal":
|
|
2771
|
+
fillHitl(entry);
|
|
2772
|
+
return;
|
|
2773
|
+
case "mapping":
|
|
2774
|
+
case "sleep":
|
|
2775
|
+
case "sleepUntil":
|
|
2776
|
+
return;
|
|
2777
|
+
}
|
|
2778
|
+
}
|
|
2779
|
+
function withDefaultsFilled(g) {
|
|
2780
|
+
const out = clone(g);
|
|
2781
|
+
out.definition.graph.forEach(fillEntry);
|
|
2782
|
+
return out;
|
|
2783
|
+
}
|
|
2784
|
+
function isConnectionKeyShaped(value22) {
|
|
2785
|
+
return WORKFLOW_CONNECTION_KEY_RE.test(value22) && !CONNECTION_ID_HEX_RE.test(value22);
|
|
2786
|
+
}
|
|
2787
|
+
function connectionKeyUndeclaredMessage(path3, key) {
|
|
2788
|
+
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`;
|
|
2789
|
+
}
|
|
2790
|
+
function classifyModelProvider(model) {
|
|
2791
|
+
const m = (model ?? "").trim().toLowerCase();
|
|
2792
|
+
if (!m) return null;
|
|
2793
|
+
if (/^(anthropic\/|claude)/.test(m)) return "anthropic";
|
|
2794
|
+
if (/^(openai\/|gpt-|o[1-9](-|$)|chatgpt)/.test(m)) return "openai";
|
|
2795
|
+
if (/^(google\/|gemini)/.test(m)) return "google";
|
|
2796
|
+
return null;
|
|
2797
|
+
}
|
|
2798
|
+
function schemaAtPath(schema, path3) {
|
|
2799
|
+
let cur = schema;
|
|
2800
|
+
if (!cur || typeof cur !== "object") return void 0;
|
|
2801
|
+
for (const seg of path3.split(".").filter(Boolean)) {
|
|
2802
|
+
const props = cur.properties;
|
|
2803
|
+
const next = props?.[seg];
|
|
2804
|
+
if (!next || typeof next !== "object") return void 0;
|
|
2805
|
+
cur = next;
|
|
2806
|
+
}
|
|
2807
|
+
return cur;
|
|
2808
|
+
}
|
|
2809
|
+
function templateStepRefs(text) {
|
|
2810
|
+
const ids = [];
|
|
2811
|
+
for (const m of text.matchAll(TEMPLATE_STEP_REF)) ids.push(m[1]);
|
|
2812
|
+
return ids;
|
|
2813
|
+
}
|
|
2814
|
+
function readMapConfig(raw) {
|
|
2815
|
+
if (!raw) return void 0;
|
|
2816
|
+
if (typeof raw !== "string") return raw;
|
|
2817
|
+
try {
|
|
2818
|
+
const cfg = JSON.parse(raw);
|
|
2819
|
+
return cfg && typeof cfg === "object" && !Array.isArray(cfg) ? cfg : void 0;
|
|
2820
|
+
} catch {
|
|
2821
|
+
return void 0;
|
|
2822
|
+
}
|
|
2823
|
+
}
|
|
2824
|
+
function mapConfigStepRefs(raw) {
|
|
2825
|
+
const cfg = readMapConfig(raw);
|
|
2826
|
+
if (!cfg) return [];
|
|
2827
|
+
const ids = [];
|
|
2828
|
+
for (const d of Object.values(cfg)) {
|
|
2829
|
+
if (!isMapDescriptor(d)) continue;
|
|
2830
|
+
if ("step" in d) ids.push(...Array.isArray(d.step) ? d.step : [
|
|
2831
|
+
d.step
|
|
2832
|
+
]);
|
|
2833
|
+
if ("template" in d) ids.push(...templateStepRefs(d.template));
|
|
2834
|
+
}
|
|
2835
|
+
return ids;
|
|
2145
2836
|
}
|
|
2146
2837
|
function nodeStepRefs(entry) {
|
|
2147
2838
|
switch (entry.type) {
|
|
@@ -2202,6 +2893,16 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2202
2893
|
if (envelopeWorkspace?.backend && envelopeWorkspace.backend !== "ebs" && opts.policy?.workspaceBackends && !opts.policy.workspaceBackends.includes(envelopeWorkspace.backend)) {
|
|
2203
2894
|
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");
|
|
2204
2895
|
}
|
|
2896
|
+
for (const member of [
|
|
2897
|
+
"repo",
|
|
2898
|
+
"ref"
|
|
2899
|
+
]) {
|
|
2900
|
+
const v = envelopeWorkspace?.[member];
|
|
2901
|
+
const tpl = v && typeof v === "object" && typeof v.template === "string" ? v.template : void 0;
|
|
2902
|
+
if (tpl !== void 0 && workspaceTemplatePath(tpl) === void 0) {
|
|
2903
|
+
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}`);
|
|
2904
|
+
}
|
|
2905
|
+
}
|
|
2205
2906
|
const declaredKeys = new Set(opts.connectionKeys ?? []);
|
|
2206
2907
|
if (g.connections !== void 0 && !Array.isArray(g.connections)) {
|
|
2207
2908
|
err("connection-declaration-invalid", "`connections` must be an array of { key, integrationType }", "connections");
|
|
@@ -2254,8 +2955,16 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2254
2955
|
const r = node.retry;
|
|
2255
2956
|
if (!r) return;
|
|
2256
2957
|
const id = singleId(node);
|
|
2257
|
-
|
|
2258
|
-
|
|
2958
|
+
const unknown = unknownWorkflowRetryMembers(r);
|
|
2959
|
+
if (unknown.length) err("invalid-envelope", workflowRetryUnknownMembersMessage(unknown), `${path3}.retry`, id);
|
|
2960
|
+
if (r.maxAttempts !== void 0 && !isWithinWorkflowRetryAttempts(r.maxAttempts)) {
|
|
2961
|
+
const over = typeof r.maxAttempts === "number" && r.maxAttempts > WORKFLOW_RETRY_MAX_ATTEMPTS;
|
|
2962
|
+
err(over ? "cap-exceeded" : "invalid-envelope", workflowRetryMaxAttemptsMessage(r.maxAttempts), `${path3}.retry.maxAttempts`, id);
|
|
2963
|
+
}
|
|
2964
|
+
const backoffs = retryBackoffs();
|
|
2965
|
+
if (r.backoff !== void 0 && !backoffs.includes(r.backoff)) {
|
|
2966
|
+
const list = backoffs.map((b) => `'${b}'`).join(" | ");
|
|
2967
|
+
err("backoff-invalid", `retry.backoff must be ${list}`, `${path3}.retry.backoff`, id);
|
|
2259
2968
|
}
|
|
2260
2969
|
if (r.backoffSeconds !== void 0 && r.backoffSeconds < 0) {
|
|
2261
2970
|
err("backoff-invalid", "retry.backoffSeconds must be \u2265 0", `${path3}.retry.backoffSeconds`, id);
|
|
@@ -2394,6 +3103,21 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2394
3103
|
const schema = node.type === "step" ? node.step.outputSchema : node.type === "agent" ? node.outputSchema : void 0;
|
|
2395
3104
|
if (schema !== void 0) outputSchemas.set(singleId(node), schema);
|
|
2396
3105
|
}, "recordOutputSchema");
|
|
3106
|
+
const checkMapMembers = /* @__PURE__ */ __name3((cfg, basePath, id) => {
|
|
3107
|
+
for (const m of malformedMapMembers(cfg)) {
|
|
3108
|
+
warn(MAP_MEMBER_MALFORMED_CODE, mapMemberMalformedMessage(id, m), `${basePath}.${m.member}`, id);
|
|
3109
|
+
}
|
|
3110
|
+
}, "checkMapMembers");
|
|
3111
|
+
const checkInputShape = /* @__PURE__ */ __name3((node, path3) => {
|
|
3112
|
+
if (node.type !== "tool" && node.type !== "workflow") return;
|
|
3113
|
+
const input = node.input;
|
|
3114
|
+
if (input === void 0) return;
|
|
3115
|
+
if (input !== null && typeof input === "object" && !Array.isArray(input)) {
|
|
3116
|
+
checkMapMembers(input, `${path3}.input`, node.id);
|
|
3117
|
+
return;
|
|
3118
|
+
}
|
|
3119
|
+
err("invalid-envelope", `\`input\` must be an object map \u2014 each member a binding descriptor ({initData:true, path} | {step, path} | {value} | {template} | {requestContextPath}) or a JSON literal (got ${JSON.stringify(input)})`, `${path3}.input`, node.id);
|
|
3120
|
+
}, "checkInputShape");
|
|
2397
3121
|
const checkSingle = /* @__PURE__ */ __name3((node, path3, depth) => {
|
|
2398
3122
|
recordOutputSchema(node);
|
|
2399
3123
|
if (node.type === "workflow" && node.workflowId === WORKFLOW_ARM_SUBRUN_ID) {
|
|
@@ -2407,8 +3131,13 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2407
3131
|
err("container-arm-empty", "a bare mapping arm has nothing to run", `${path3}.graph.1`, node.id);
|
|
2408
3132
|
return;
|
|
2409
3133
|
}
|
|
3134
|
+
const inner = body[1];
|
|
3135
|
+
if (isHitlNode(inner)) {
|
|
3136
|
+
err("node-type-unsupported-in-container", workflowHitlArmShapeMessage(inner.type, inner.id, "mapped-arm"), `${path3}.graph.1`, inner.id);
|
|
3137
|
+
return;
|
|
3138
|
+
}
|
|
2410
3139
|
upstream.add(singleId(body[1]));
|
|
2411
|
-
checkArm(body[0], `${path3}.graph.0`, depth);
|
|
3140
|
+
checkArm(body[0], `${path3}.graph.0`, depth, "parallel");
|
|
2412
3141
|
checkSingle(body[1], `${path3}.graph.1`, depth);
|
|
2413
3142
|
upstream.add(body[0].id);
|
|
2414
3143
|
upstream.add(singleId(body[1]));
|
|
@@ -2416,6 +3145,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2416
3145
|
}
|
|
2417
3146
|
checkId(singleId(node), path3);
|
|
2418
3147
|
checkPolicyEnums(node, path3);
|
|
3148
|
+
checkInputShape(node, path3);
|
|
2419
3149
|
checkTimeout(node, path3);
|
|
2420
3150
|
checkTier(node, path3);
|
|
2421
3151
|
checkRetry(node, path3);
|
|
@@ -2440,20 +3170,72 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2440
3170
|
if (node.type === "workflow" && node.kind === "subrun" && depth > caps.maxNestingDepth) {
|
|
2441
3171
|
err("cap-exceeded", `nesting depth ${depth} exceeds ${caps.maxNestingDepth}`, path3, node.id);
|
|
2442
3172
|
}
|
|
3173
|
+
if (node.type === "workflow" && node.workflowId !== WORKFLOW_ARM_SUBRUN_ID && typeof g.definition?.id === "string" && node.workflowId === g.definition.id) {
|
|
3174
|
+
err("subrun-cycle", `"${node.id}" starts "${node.workflowId}", which is this workflow itself`, path3, node.id);
|
|
3175
|
+
}
|
|
2443
3176
|
for (const ref of nodeStepRefs(node)) {
|
|
2444
3177
|
if (!upstream.has(ref)) {
|
|
2445
3178
|
err("template-reference-unresolved", `"${singleId(node)}" references stepResults.${ref}, which is not upstream`, path3, singleId(node));
|
|
2446
3179
|
}
|
|
2447
3180
|
}
|
|
2448
|
-
}, "checkSingle");
|
|
2449
|
-
const
|
|
3181
|
+
}, "checkSingle");
|
|
3182
|
+
const checkHitl = /* @__PURE__ */ __name3((node, path3) => {
|
|
3183
|
+
if (node.type === "waitForSignal") {
|
|
3184
|
+
const w = node;
|
|
3185
|
+
checkId(w.id, path3);
|
|
3186
|
+
if (typeof w.signal !== "string" || w.signal.length === 0) err("invalid-envelope", "waitForSignal.signal is required", `${path3}.signal`, w.id);
|
|
3187
|
+
return;
|
|
3188
|
+
}
|
|
3189
|
+
const a = node;
|
|
3190
|
+
checkId(a.id, path3);
|
|
3191
|
+
if (a.approver === "creator" && a.excludeInitiator === true) {
|
|
3192
|
+
err("approver-excludes-only-candidate", "approver:'creator' with excludeInitiator:true always excludes the only candidate", path3, a.id);
|
|
3193
|
+
}
|
|
3194
|
+
if (a.fourEyes !== void 0 && a.editable !== true) {
|
|
3195
|
+
err("four-eyes-requires-editable", "`fourEyes` requires editable:true", `${path3}.fourEyes`, a.id);
|
|
3196
|
+
}
|
|
3197
|
+
if ((a.editablePaths !== void 0 || a.editedPayloadSchema !== void 0) && a.editable !== true) {
|
|
3198
|
+
err("editable-path-invalid", "`editablePaths` / `editedPayloadSchema` require editable:true", `${path3}.editablePaths`, a.id);
|
|
3199
|
+
}
|
|
3200
|
+
for (const p of a.editablePaths ?? []) {
|
|
3201
|
+
if (!EDITABLE_PATH_RE.test(p)) err("editable-path-invalid", `editablePaths entry "${p}" is outside the seg(.seg)*[*]/[n] grammar`, `${path3}.editablePaths`, a.id);
|
|
3202
|
+
}
|
|
3203
|
+
if (Array.isArray(a.onTimeout)) {
|
|
3204
|
+
const chain = a.onTimeout;
|
|
3205
|
+
const hops = chain.filter((h) => typeof h === "object" && h !== null && "escalateTo" in h);
|
|
3206
|
+
if (hops.length > 3) err("escalation-chain-too-long", "an onTimeout chain carries at most 3 escalation hops", `${path3}.onTimeout`, a.id);
|
|
3207
|
+
const last = chain[chain.length - 1];
|
|
3208
|
+
if (last === void 0 || typeof last === "object" && last !== null && "escalateTo" in last) {
|
|
3209
|
+
err("escalation-chain-not-terminal", "an onTimeout chain must end in a terminal member", `${path3}.onTimeout`, a.id);
|
|
3210
|
+
}
|
|
3211
|
+
}
|
|
3212
|
+
if (typeof a.details === "string") {
|
|
3213
|
+
for (const ref of templateStepRefs(a.details)) {
|
|
3214
|
+
if (!upstream.has(ref)) err("template-reference-unresolved", `"${a.id}" references stepResults.${ref}, which is not upstream`, path3, a.id);
|
|
3215
|
+
}
|
|
3216
|
+
}
|
|
3217
|
+
}, "checkHitl");
|
|
3218
|
+
const checkHitlArm = /* @__PURE__ */ __name3((node, path3, container) => {
|
|
3219
|
+
if (!workflowContainerRunsHitlArm(container)) {
|
|
3220
|
+
checkId(node.id, path3);
|
|
3221
|
+
err("node-type-unsupported-in-container", workflowHitlArmUnsupportedMessage(node.type, node.id, container), path3, node.id);
|
|
3222
|
+
return;
|
|
3223
|
+
}
|
|
3224
|
+
checkHitl(node, path3);
|
|
3225
|
+
}, "checkHitlArm");
|
|
3226
|
+
const checkArm = /* @__PURE__ */ __name3((arm, path3, depth, container) => {
|
|
2450
3227
|
if (arm.type === "mapping") {
|
|
2451
3228
|
checkId(arm.id, path3);
|
|
3229
|
+
checkMapMembers(readMapConfig(arm.mapConfig), `${path3}.mapConfig`, arm.id);
|
|
2452
3230
|
for (const ref of nodeStepRefs(arm)) {
|
|
2453
3231
|
if (!upstream.has(ref)) err("template-reference-unresolved", `"${arm.id}" references stepResults.${ref}, which is not upstream`, path3, arm.id);
|
|
2454
3232
|
}
|
|
2455
3233
|
return;
|
|
2456
3234
|
}
|
|
3235
|
+
if (isHitlNode(arm)) {
|
|
3236
|
+
checkHitlArm(arm, path3, container);
|
|
3237
|
+
return;
|
|
3238
|
+
}
|
|
2457
3239
|
checkSingle(arm, path3, depth);
|
|
2458
3240
|
}, "checkArm");
|
|
2459
3241
|
graph.forEach((entry, i) => {
|
|
@@ -2469,6 +3251,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2469
3251
|
break;
|
|
2470
3252
|
case "mapping":
|
|
2471
3253
|
checkId(entry.id, path3);
|
|
3254
|
+
checkMapMembers(readMapConfig(entry.mapConfig), `${path3}.mapConfig`, entry.id);
|
|
2472
3255
|
for (const ref of nodeStepRefs(entry)) {
|
|
2473
3256
|
if (!upstream.has(ref)) err("template-reference-unresolved", `"${entry.id}" references stepResults.${ref}, which is not upstream`, path3, entry.id);
|
|
2474
3257
|
}
|
|
@@ -2503,18 +3286,15 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2503
3286
|
err("mapping-placement", "a bare mapping cannot be a parallel arm \u2014 chain it as [map, step]", armPath);
|
|
2504
3287
|
return;
|
|
2505
3288
|
}
|
|
2506
|
-
|
|
2507
|
-
err("approval-inside-container", "approval / waitForSignal are top-level only in v1", armPath);
|
|
2508
|
-
return;
|
|
2509
|
-
}
|
|
2510
|
-
checkSingle(arm, armPath, 1);
|
|
3289
|
+
checkArm(arm, armPath, 1, "parallel");
|
|
2511
3290
|
declared.push(singleId(arm));
|
|
2512
3291
|
});
|
|
2513
|
-
const
|
|
3292
|
+
const executableArms = p.steps.filter(isSingleStep);
|
|
3293
|
+
const worktreeArms = executableArms.filter((a) => a.workspace?.isolation === "worktree");
|
|
2514
3294
|
if (worktreeArms.length > 0 && !p.merge) err("worktree-arms-require-merge", "worktree arms need a `merge` policy", path3);
|
|
2515
3295
|
if (worktreeArms.length === 0 && p.merge) err("merge-requires-worktree-arms", "`merge` needs at least one worktree arm", path3);
|
|
2516
3296
|
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);
|
|
2517
|
-
const sharedArms =
|
|
3297
|
+
const sharedArms = executableArms.filter((a) => {
|
|
2518
3298
|
const w = workspaceOf(a);
|
|
2519
3299
|
if (!w || w === "inherit" || w.isolation === "worktree") return false;
|
|
2520
3300
|
return !(envelopeWorkspace?.backend === "efs" && w.mount === "ro");
|
|
@@ -2531,11 +3311,11 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2531
3311
|
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}`);
|
|
2532
3312
|
});
|
|
2533
3313
|
c.steps.forEach((arm, j) => {
|
|
2534
|
-
checkArm(arm, `${path3}.steps.${j}`, 1);
|
|
3314
|
+
checkArm(arm, `${path3}.steps.${j}`, 1, "conditional");
|
|
2535
3315
|
declared.push(armId(arm));
|
|
2536
3316
|
});
|
|
2537
3317
|
if (c.otherwise) {
|
|
2538
|
-
checkArm(c.otherwise, `${path3}.otherwise`, 1);
|
|
3318
|
+
checkArm(c.otherwise, `${path3}.otherwise`, 1, "conditional");
|
|
2539
3319
|
declared.push(armId(c.otherwise));
|
|
2540
3320
|
}
|
|
2541
3321
|
break;
|
|
@@ -2558,8 +3338,9 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2558
3338
|
}
|
|
2559
3339
|
if (o.chunk !== void 0) {
|
|
2560
3340
|
const max = o.maxItems ?? WORKFLOW_FOREACH_DEFAULT_MAX_ITEMS;
|
|
2561
|
-
|
|
2562
|
-
|
|
3341
|
+
const size = typeof o.chunk === "number" ? o.chunk : o.chunk.size;
|
|
3342
|
+
if (!Number.isInteger(size) || size < 1 || size > max) {
|
|
3343
|
+
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`);
|
|
2563
3344
|
}
|
|
2564
3345
|
}
|
|
2565
3346
|
if (o.rateLimit !== void 0) {
|
|
@@ -2576,12 +3357,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2576
3357
|
const bodyType = f.step.type;
|
|
2577
3358
|
const prevEntry = i > 0 ? graph[i - 1] : void 0;
|
|
2578
3359
|
if (bodyType !== "mapping" && prevEntry?.type === "mapping" && prevEntry.id === `${singleId(f.step)}_items`) {
|
|
2579
|
-
|
|
2580
|
-
try {
|
|
2581
|
-
cfg = JSON.parse(prevEntry.mapConfig);
|
|
2582
|
-
} catch {
|
|
2583
|
-
cfg = void 0;
|
|
2584
|
-
}
|
|
3360
|
+
const cfg = readMapConfig(prevEntry.mapConfig);
|
|
2585
3361
|
const d = cfg?.[""];
|
|
2586
3362
|
let arrayLike;
|
|
2587
3363
|
if (d && "value" in d) arrayLike = Array.isArray(d.value);
|
|
@@ -2593,8 +3369,13 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2593
3369
|
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));
|
|
2594
3370
|
}
|
|
2595
3371
|
if (bodyType === "mapping") err("container-arm-empty", "a foreach body needs a step, not a bare mapping", `${path3}.step`);
|
|
2596
|
-
else if (
|
|
2597
|
-
|
|
3372
|
+
else if (isHitlNode(f.step)) {
|
|
3373
|
+
if (o.chunk !== void 0) {
|
|
3374
|
+
checkId(f.step.id, `${path3}.step`);
|
|
3375
|
+
err("node-type-unsupported-in-container", workflowHitlArmShapeMessage(f.step.type, f.step.id, "chunked-foreach"), `${path3}.step`, f.step.id);
|
|
3376
|
+
} else checkHitlArm(f.step, `${path3}.step`, "foreach");
|
|
3377
|
+
declared.push(f.step.id);
|
|
3378
|
+
} else {
|
|
2598
3379
|
checkSingle(f.step, `${path3}.step`, o.chunk ? 2 : 1);
|
|
2599
3380
|
declared.push(singleId(f.step));
|
|
2600
3381
|
}
|
|
@@ -2611,52 +3392,20 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2611
3392
|
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`);
|
|
2612
3393
|
const bodyType = l.step.type;
|
|
2613
3394
|
if (bodyType === "mapping") err("container-arm-empty", "a loop body needs a step, not a bare mapping", `${path3}.step`);
|
|
2614
|
-
else if (
|
|
2615
|
-
|
|
3395
|
+
else if (isHitlNode(l.step)) {
|
|
3396
|
+
checkHitlArm(l.step, `${path3}.step`, "loop");
|
|
3397
|
+
declared.push(l.step.id);
|
|
3398
|
+
} else {
|
|
2616
3399
|
checkSingle(l.step, `${path3}.step`, 1);
|
|
2617
3400
|
declared.push(singleId(l.step));
|
|
2618
3401
|
}
|
|
2619
3402
|
break;
|
|
2620
3403
|
}
|
|
2621
|
-
case "approval":
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
err("approver-excludes-only-candidate", "approver:'creator' with excludeInitiator:true always excludes the only candidate", path3, a.id);
|
|
2626
|
-
}
|
|
2627
|
-
if (a.fourEyes !== void 0 && a.editable !== true) {
|
|
2628
|
-
err("four-eyes-requires-editable", "`fourEyes` requires editable:true", `${path3}.fourEyes`, a.id);
|
|
2629
|
-
}
|
|
2630
|
-
if ((a.editablePaths !== void 0 || a.editedPayloadSchema !== void 0) && a.editable !== true) {
|
|
2631
|
-
err("editable-path-invalid", "`editablePaths` / `editedPayloadSchema` require editable:true", `${path3}.editablePaths`, a.id);
|
|
2632
|
-
}
|
|
2633
|
-
for (const p of a.editablePaths ?? []) {
|
|
2634
|
-
if (!EDITABLE_PATH_RE.test(p)) err("editable-path-invalid", `editablePaths entry "${p}" is outside the seg(.seg)*[*]/[n] grammar`, `${path3}.editablePaths`, a.id);
|
|
2635
|
-
}
|
|
2636
|
-
if (Array.isArray(a.onTimeout)) {
|
|
2637
|
-
const chain = a.onTimeout;
|
|
2638
|
-
const hops = chain.filter((h) => typeof h === "object" && h !== null && "escalateTo" in h);
|
|
2639
|
-
if (hops.length > 3) err("escalation-chain-too-long", "an onTimeout chain carries at most 3 escalation hops", `${path3}.onTimeout`, a.id);
|
|
2640
|
-
const last = chain[chain.length - 1];
|
|
2641
|
-
if (last === void 0 || typeof last === "object" && last !== null && "escalateTo" in last) {
|
|
2642
|
-
err("escalation-chain-not-terminal", "an onTimeout chain must end in a terminal member", `${path3}.onTimeout`, a.id);
|
|
2643
|
-
}
|
|
2644
|
-
}
|
|
2645
|
-
if (typeof a.details === "string") {
|
|
2646
|
-
for (const ref of templateStepRefs(a.details)) {
|
|
2647
|
-
if (!upstream.has(ref)) err("template-reference-unresolved", `"${a.id}" references stepResults.${ref}, which is not upstream`, path3, a.id);
|
|
2648
|
-
}
|
|
2649
|
-
}
|
|
2650
|
-
declared.push(a.id);
|
|
2651
|
-
break;
|
|
2652
|
-
}
|
|
2653
|
-
case "waitForSignal": {
|
|
2654
|
-
const w = entry;
|
|
2655
|
-
checkId(w.id, path3);
|
|
2656
|
-
if (typeof w.signal !== "string" || w.signal.length === 0) err("invalid-envelope", "waitForSignal.signal is required", `${path3}.signal`, w.id);
|
|
2657
|
-
declared.push(w.id);
|
|
3404
|
+
case "approval":
|
|
3405
|
+
case "waitForSignal":
|
|
3406
|
+
checkHitl(entry, path3);
|
|
3407
|
+
declared.push(entry.id);
|
|
2658
3408
|
break;
|
|
2659
|
-
}
|
|
2660
3409
|
default:
|
|
2661
3410
|
err("invalid-envelope", `unknown entry type "${entry.type}"`, path3);
|
|
2662
3411
|
}
|
|
@@ -2745,10 +3494,10 @@ function compilePlan(g) {
|
|
|
2745
3494
|
let prevTails = [];
|
|
2746
3495
|
const foreachJoinToEntry = /* @__PURE__ */ new Map();
|
|
2747
3496
|
g.definition.graph.forEach((entry, entryIndex) => {
|
|
2748
|
-
if (
|
|
2749
|
-
const id =
|
|
3497
|
+
if (isArmStep(entry)) {
|
|
3498
|
+
const id = armStepId(entry);
|
|
2750
3499
|
addNode(id, {
|
|
2751
|
-
kind:
|
|
3500
|
+
kind: armStepKind(entry),
|
|
2752
3501
|
dependsOn: prevTails,
|
|
2753
3502
|
downstream: [],
|
|
2754
3503
|
unsatisfiedDeps: prevTails.length,
|
|
@@ -2773,9 +3522,8 @@ function compilePlan(g) {
|
|
|
2773
3522
|
];
|
|
2774
3523
|
return;
|
|
2775
3524
|
case "sleep":
|
|
2776
|
-
case "sleepUntil":
|
|
2777
|
-
|
|
2778
|
-
const kind = entry.type === "approval" ? "approval" : entry.type;
|
|
3525
|
+
case "sleepUntil": {
|
|
3526
|
+
const kind = entry.type;
|
|
2779
3527
|
addNode(entry.id, {
|
|
2780
3528
|
kind,
|
|
2781
3529
|
dependsOn: prevTails,
|
|
@@ -2788,24 +3536,12 @@ function compilePlan(g) {
|
|
|
2788
3536
|
];
|
|
2789
3537
|
return;
|
|
2790
3538
|
}
|
|
2791
|
-
case "waitForSignal":
|
|
2792
|
-
addNode(entry.id, {
|
|
2793
|
-
kind: "signal",
|
|
2794
|
-
dependsOn: prevTails,
|
|
2795
|
-
downstream: [],
|
|
2796
|
-
unsatisfiedDeps: prevTails.length,
|
|
2797
|
-
entry
|
|
2798
|
-
});
|
|
2799
|
-
prevTails = [
|
|
2800
|
-
entry.id
|
|
2801
|
-
];
|
|
2802
|
-
return;
|
|
2803
3539
|
case "parallel": {
|
|
2804
3540
|
const entryId = containerIdOf("parallel", entryIndex);
|
|
2805
3541
|
const childIds = entry.steps.map((arm) => {
|
|
2806
|
-
const childId =
|
|
3542
|
+
const childId = armStepId(arm);
|
|
2807
3543
|
addNode(childId, {
|
|
2808
|
-
kind:
|
|
3544
|
+
kind: armStepKind(arm),
|
|
2809
3545
|
dependsOn: prevTails,
|
|
2810
3546
|
downstream: [],
|
|
2811
3547
|
unsatisfiedDeps: prevTails.length,
|
|
@@ -2843,9 +3579,9 @@ function compilePlan(g) {
|
|
|
2843
3579
|
node.otherwise
|
|
2844
3580
|
] : node.steps;
|
|
2845
3581
|
const childIds = arms.map((arm) => {
|
|
2846
|
-
const childId = arm.type === "mapping" ? arm.id :
|
|
3582
|
+
const childId = arm.type === "mapping" ? arm.id : armStepId(arm);
|
|
2847
3583
|
addNode(childId, {
|
|
2848
|
-
kind: arm.type === "mapping" ? "map" :
|
|
3584
|
+
kind: arm.type === "mapping" ? "map" : armStepKind(arm),
|
|
2849
3585
|
dependsOn: [
|
|
2850
3586
|
entryId
|
|
2851
3587
|
],
|
|
@@ -3022,250 +3758,96 @@ function compare(op, left, right) {
|
|
|
3022
3758
|
return left < right;
|
|
3023
3759
|
case "lte":
|
|
3024
3760
|
return left <= right;
|
|
3025
|
-
case "gt":
|
|
3026
|
-
return left > right;
|
|
3027
|
-
case "gte":
|
|
3028
|
-
return left >= right;
|
|
3029
|
-
}
|
|
3030
|
-
}
|
|
3031
|
-
return false;
|
|
3032
|
-
}
|
|
3033
|
-
function derivePredicateLabel(pred, maxLength = 80) {
|
|
3034
|
-
const raw = renderPredicate(pred);
|
|
3035
|
-
if (raw.length <= maxLength) return raw;
|
|
3036
|
-
return raw.slice(0, maxLength - 1) + "\u2026";
|
|
3037
|
-
}
|
|
3038
|
-
function renderPredicate(pred) {
|
|
3039
|
-
switch (pred.op) {
|
|
3040
|
-
case "and":
|
|
3041
|
-
case "or":
|
|
3042
|
-
return pred.args.map((arg) => wrapLabel(arg, renderPredicate(arg))).join(pred.op === "and" ? " AND " : " OR ");
|
|
3043
|
-
case "not":
|
|
3044
|
-
return `NOT ${wrapLabel(pred.arg, renderPredicate(pred.arg))}`;
|
|
3045
|
-
case "exists":
|
|
3046
|
-
return `${pred.path} exists`;
|
|
3047
|
-
case "notExists":
|
|
3048
|
-
return `${pred.path} missing`;
|
|
3049
|
-
case "truthy":
|
|
3050
|
-
return `${renderRef(pred.value)} is truthy`;
|
|
3051
|
-
case "falsy":
|
|
3052
|
-
return `${renderRef(pred.value)} is falsy`;
|
|
3053
|
-
case "in":
|
|
3054
|
-
return `${renderRef(pred.value)} in ${JSON.stringify(pred.set)}`;
|
|
3055
|
-
case "notIn":
|
|
3056
|
-
return `${renderRef(pred.value)} not in ${JSON.stringify(pred.set)}`;
|
|
3057
|
-
case "eq":
|
|
3058
|
-
return `${renderRef(pred.left)} == ${renderRef(pred.right)}`;
|
|
3059
|
-
case "ne":
|
|
3060
|
-
return `${renderRef(pred.left)} != ${renderRef(pred.right)}`;
|
|
3061
|
-
case "lt":
|
|
3062
|
-
return `${renderRef(pred.left)} < ${renderRef(pred.right)}`;
|
|
3063
|
-
case "lte":
|
|
3064
|
-
return `${renderRef(pred.left)} <= ${renderRef(pred.right)}`;
|
|
3065
|
-
case "gt":
|
|
3066
|
-
return `${renderRef(pred.left)} > ${renderRef(pred.right)}`;
|
|
3067
|
-
case "gte":
|
|
3068
|
-
return `${renderRef(pred.left)} >= ${renderRef(pred.right)}`;
|
|
3069
|
-
}
|
|
3070
|
-
}
|
|
3071
|
-
function wrapLabel(child, rendered) {
|
|
3072
|
-
return child.op === "and" || child.op === "or" || child.op === "not" ? `(${rendered})` : rendered;
|
|
3073
|
-
}
|
|
3074
|
-
function renderRef(ref) {
|
|
3075
|
-
if ("literal" in ref) return JSON.stringify(ref.literal);
|
|
3076
|
-
return ref.path;
|
|
3077
|
-
}
|
|
3078
|
-
function step(s) {
|
|
3079
|
-
const id = stepIdOf(s);
|
|
3080
|
-
return {
|
|
3081
|
-
path: /* @__PURE__ */ __name3((p) => ({
|
|
3082
|
-
path: p === "" ? `stepResults.${id}` : `stepResults.${id}.${p}`
|
|
3083
|
-
}), "path")
|
|
3084
|
-
};
|
|
3085
|
-
}
|
|
3086
|
-
function stepOf(id) {
|
|
3087
|
-
return step(id);
|
|
3088
|
-
}
|
|
3089
|
-
function init(path3) {
|
|
3090
|
-
return {
|
|
3091
|
-
path: path3 === "" ? "initData" : `initData.${path3}`
|
|
3092
|
-
};
|
|
3093
|
-
}
|
|
3094
|
-
function state(path3) {
|
|
3095
|
-
return {
|
|
3096
|
-
path: path3 === "" ? "state" : `state.${path3}`
|
|
3097
|
-
};
|
|
3098
|
-
}
|
|
3099
|
-
function lit(v) {
|
|
3100
|
-
return {
|
|
3101
|
-
literal: v
|
|
3102
|
-
};
|
|
3103
|
-
}
|
|
3104
|
-
function toPathOrLiteral(v) {
|
|
3105
|
-
if (typeof v === "object" && v !== null) {
|
|
3106
|
-
if ("path" in v) return {
|
|
3107
|
-
path: v.path
|
|
3108
|
-
};
|
|
3109
|
-
if ("literal" in v) return {
|
|
3110
|
-
literal: v.literal
|
|
3111
|
-
};
|
|
3112
|
-
}
|
|
3113
|
-
return {
|
|
3114
|
-
literal: v
|
|
3115
|
-
};
|
|
3116
|
-
}
|
|
3117
|
-
function parseMapConfig(raw, stepId) {
|
|
3118
|
-
try {
|
|
3119
|
-
return JSON.parse(raw);
|
|
3120
|
-
} catch (e) {
|
|
3121
|
-
throw new Error(`Stored mapping step "${stepId}" has invalid JSON mapConfig: ${e.message}`);
|
|
3122
|
-
}
|
|
3123
|
-
}
|
|
3124
|
-
function describeBadPlaceholder(template22, idx, rawExpr) {
|
|
3125
|
-
return `Template placeholder #${idx} (\${${rawExpr}}) in '${template22}'`;
|
|
3126
|
-
}
|
|
3127
|
-
function parseTemplatePlaceholder(rawExpr) {
|
|
3128
|
-
const dot = rawExpr.indexOf(".");
|
|
3129
|
-
return {
|
|
3130
|
-
scope: dot === -1 ? rawExpr : rawExpr.slice(0, dot),
|
|
3131
|
-
rest: dot === -1 ? "" : rawExpr.slice(dot + 1)
|
|
3132
|
-
};
|
|
3133
|
-
}
|
|
3134
|
-
function traverseMappingPath(root, path3, errorLabel) {
|
|
3135
|
-
if (path3 === "" || path3 === ".") return root;
|
|
3136
|
-
const parts = path3.split(".");
|
|
3137
|
-
let value22 = root;
|
|
3138
|
-
for (const part of parts) {
|
|
3139
|
-
if (typeof value22 === "object" && value22 !== null) value22 = value22[part];
|
|
3140
|
-
else throw new WorkflowTemplateError(`Invalid path ${path3} in ${errorLabel}`, path3);
|
|
3141
|
-
}
|
|
3142
|
-
return value22;
|
|
3143
|
-
}
|
|
3144
|
-
function stringifyTemplateValue(v, template22, idx, rawExpr) {
|
|
3145
|
-
if (v === null || v === void 0) return "";
|
|
3146
|
-
if (typeof v === "object") {
|
|
3147
|
-
try {
|
|
3148
|
-
return JSON.stringify(v);
|
|
3149
|
-
} catch (err) {
|
|
3150
|
-
throw new WorkflowTemplateError(`${describeBadPlaceholder(template22, idx, rawExpr)} resolved to a value that could not be JSON-stringified (${err.message}).`, rawExpr);
|
|
3151
|
-
}
|
|
3152
|
-
}
|
|
3153
|
-
return String(v);
|
|
3154
|
-
}
|
|
3155
|
-
function escapeFence(content) {
|
|
3156
|
-
return content.replace(/<\/lua-data/g, "<\\/lua-data");
|
|
3157
|
-
}
|
|
3158
|
-
function fenceBlock(name, source, content) {
|
|
3159
|
-
return `<lua-data name="${name}" source="${source}" untrusted="true">${escapeFence(content)}</lua-data>`;
|
|
3160
|
-
}
|
|
3161
|
-
function renderTemplate(template22, ctx, opts) {
|
|
3162
|
-
let idx = 0;
|
|
3163
|
-
return template22.replace(TEMPLATE_PLACEHOLDER, (_match, rawExpr) => {
|
|
3164
|
-
idx += 1;
|
|
3165
|
-
const { scope, rest } = parseTemplatePlaceholder(rawExpr);
|
|
3166
|
-
const label = describeBadPlaceholder(template22, idx, rawExpr);
|
|
3167
|
-
let rendered;
|
|
3168
|
-
let source;
|
|
3169
|
-
switch (scope) {
|
|
3170
|
-
case "initData":
|
|
3171
|
-
rendered = stringifyTemplateValue(traverseMappingPath(ctx.initData, rest, label), template22, idx, rawExpr);
|
|
3172
|
-
source = "initData";
|
|
3173
|
-
break;
|
|
3174
|
-
case "state":
|
|
3175
|
-
rendered = stringifyTemplateValue(traverseMappingPath(ctx.state, rest, label), template22, idx, rawExpr);
|
|
3176
|
-
source = "state";
|
|
3177
|
-
break;
|
|
3178
|
-
case "requestContext":
|
|
3179
|
-
rendered = stringifyTemplateValue(traverseMappingPath(ctx.requestContext, rest, label), template22, idx, rawExpr);
|
|
3180
|
-
source = "requestContext";
|
|
3181
|
-
break;
|
|
3182
|
-
case "stepResults": {
|
|
3183
|
-
const innerDot = rest.indexOf(".");
|
|
3184
|
-
const stepId = innerDot === -1 ? rest : rest.slice(0, innerDot);
|
|
3185
|
-
const subPath = innerDot === -1 ? "" : rest.slice(innerDot + 1);
|
|
3186
|
-
if (!stepId) throw new WorkflowTemplateError(`${label} must name a step: \${stepResults.<stepId>.<path>}.`, rawExpr);
|
|
3187
|
-
if (!(stepId in ctx.stepResults) || ctx.stepResults[stepId] == null) {
|
|
3188
|
-
throw new WorkflowTemplateError(`${label} references stepResults.${stepId} but step "${stepId}" has no resolvable output (not an ancestor, not run, failed, or produced no output).`, rawExpr);
|
|
3189
|
-
}
|
|
3190
|
-
rendered = stringifyTemplateValue(traverseMappingPath(ctx.stepResults[stepId], subPath, label), template22, idx, rawExpr);
|
|
3191
|
-
source = `step:${stepId}`;
|
|
3192
|
-
break;
|
|
3193
|
-
}
|
|
3194
|
-
default:
|
|
3195
|
-
throw new WorkflowTemplateError(`${label} references unknown namespace "${scope}". Use one of: ${TEMPLATE_NAMESPACES.join(", ")}.`, rawExpr);
|
|
3196
|
-
}
|
|
3197
|
-
return opts.fenced ? fenceBlock(rawExpr, source, rendered) : rendered;
|
|
3198
|
-
});
|
|
3199
|
-
}
|
|
3200
|
-
function resolveDescriptor(key, m, ctx) {
|
|
3201
|
-
try {
|
|
3202
|
-
if ("value" in m) return {
|
|
3203
|
-
value: m.value
|
|
3204
|
-
};
|
|
3205
|
-
if ("template" in m && typeof m.template === "string") {
|
|
3206
|
-
return {
|
|
3207
|
-
value: renderTemplate(m.template, ctx, {
|
|
3208
|
-
fenced: false
|
|
3209
|
-
})
|
|
3210
|
-
};
|
|
3211
|
-
}
|
|
3212
|
-
if ("knowledge" in m || "rows" in m && m.rows !== void 0) {
|
|
3213
|
-
return {
|
|
3214
|
-
error: "binding_unresolved",
|
|
3215
|
-
key
|
|
3216
|
-
};
|
|
3217
|
-
}
|
|
3218
|
-
if ("requestContextPath" in m) {
|
|
3219
|
-
const label = `requestContext path for key "${key}"`;
|
|
3220
|
-
return {
|
|
3221
|
-
value: traverseMappingPath(ctx.requestContext, m.requestContextPath, label)
|
|
3222
|
-
};
|
|
3223
|
-
}
|
|
3224
|
-
if ("path" in m) {
|
|
3225
|
-
const source = "initData" in m && m.initData ? "initData" : "step";
|
|
3226
|
-
if (source === "initData") {
|
|
3227
|
-
return {
|
|
3228
|
-
value: traverseMappingPath(ctx.initData, m.path, `initData for key "${key}"`)
|
|
3229
|
-
};
|
|
3230
|
-
}
|
|
3231
|
-
const stepRef = m.step;
|
|
3232
|
-
const candidates = Array.isArray(stepRef) ? stepRef : [
|
|
3233
|
-
stepRef
|
|
3234
|
-
];
|
|
3235
|
-
const stepId = candidates.find((s) => ctx.stepResults[s] !== void 0 && ctx.stepResults[s] !== null);
|
|
3236
|
-
if (stepId === void 0) return {
|
|
3237
|
-
error: "binding_unresolved",
|
|
3238
|
-
key
|
|
3239
|
-
};
|
|
3240
|
-
return {
|
|
3241
|
-
value: traverseMappingPath(ctx.stepResults[stepId], m.path, `step ${candidates.join("|")} for key "${key}"`)
|
|
3242
|
-
};
|
|
3761
|
+
case "gt":
|
|
3762
|
+
return left > right;
|
|
3763
|
+
case "gte":
|
|
3764
|
+
return left >= right;
|
|
3243
3765
|
}
|
|
3244
|
-
return {
|
|
3245
|
-
error: "binding_unresolved",
|
|
3246
|
-
key
|
|
3247
|
-
};
|
|
3248
|
-
} catch (err) {
|
|
3249
|
-
if (err instanceof WorkflowTemplateError) return {
|
|
3250
|
-
error: "binding_unresolved",
|
|
3251
|
-
key
|
|
3252
|
-
};
|
|
3253
|
-
throw err;
|
|
3254
3766
|
}
|
|
3767
|
+
return false;
|
|
3255
3768
|
}
|
|
3256
|
-
function
|
|
3257
|
-
const
|
|
3258
|
-
if (
|
|
3259
|
-
|
|
3769
|
+
function derivePredicateLabel(pred, maxLength = 80) {
|
|
3770
|
+
const raw = renderPredicate(pred);
|
|
3771
|
+
if (raw.length <= maxLength) return raw;
|
|
3772
|
+
return raw.slice(0, maxLength - 1) + "\u2026";
|
|
3773
|
+
}
|
|
3774
|
+
function renderPredicate(pred) {
|
|
3775
|
+
switch (pred.op) {
|
|
3776
|
+
case "and":
|
|
3777
|
+
case "or":
|
|
3778
|
+
return pred.args.map((arg) => wrapLabel(arg, renderPredicate(arg))).join(pred.op === "and" ? " AND " : " OR ");
|
|
3779
|
+
case "not":
|
|
3780
|
+
return `NOT ${wrapLabel(pred.arg, renderPredicate(pred.arg))}`;
|
|
3781
|
+
case "exists":
|
|
3782
|
+
return `${pred.path} exists`;
|
|
3783
|
+
case "notExists":
|
|
3784
|
+
return `${pred.path} missing`;
|
|
3785
|
+
case "truthy":
|
|
3786
|
+
return `${renderRef(pred.value)} is truthy`;
|
|
3787
|
+
case "falsy":
|
|
3788
|
+
return `${renderRef(pred.value)} is falsy`;
|
|
3789
|
+
case "in":
|
|
3790
|
+
return `${renderRef(pred.value)} in ${JSON.stringify(pred.set)}`;
|
|
3791
|
+
case "notIn":
|
|
3792
|
+
return `${renderRef(pred.value)} not in ${JSON.stringify(pred.set)}`;
|
|
3793
|
+
case "eq":
|
|
3794
|
+
return `${renderRef(pred.left)} == ${renderRef(pred.right)}`;
|
|
3795
|
+
case "ne":
|
|
3796
|
+
return `${renderRef(pred.left)} != ${renderRef(pred.right)}`;
|
|
3797
|
+
case "lt":
|
|
3798
|
+
return `${renderRef(pred.left)} < ${renderRef(pred.right)}`;
|
|
3799
|
+
case "lte":
|
|
3800
|
+
return `${renderRef(pred.left)} <= ${renderRef(pred.right)}`;
|
|
3801
|
+
case "gt":
|
|
3802
|
+
return `${renderRef(pred.left)} > ${renderRef(pred.right)}`;
|
|
3803
|
+
case "gte":
|
|
3804
|
+
return `${renderRef(pred.left)} >= ${renderRef(pred.right)}`;
|
|
3260
3805
|
}
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3806
|
+
}
|
|
3807
|
+
function wrapLabel(child, rendered) {
|
|
3808
|
+
return child.op === "and" || child.op === "or" || child.op === "not" ? `(${rendered})` : rendered;
|
|
3809
|
+
}
|
|
3810
|
+
function renderRef(ref) {
|
|
3811
|
+
if ("literal" in ref) return JSON.stringify(ref.literal);
|
|
3812
|
+
return ref.path;
|
|
3813
|
+
}
|
|
3814
|
+
function step(s) {
|
|
3815
|
+
const id = stepIdOf(s);
|
|
3816
|
+
return {
|
|
3817
|
+
path: /* @__PURE__ */ __name3((p) => ({
|
|
3818
|
+
path: p === "" ? `stepResults.${id}` : `stepResults.${id}.${p}`
|
|
3819
|
+
}), "path")
|
|
3820
|
+
};
|
|
3821
|
+
}
|
|
3822
|
+
function stepOf(id) {
|
|
3823
|
+
return step(id);
|
|
3824
|
+
}
|
|
3825
|
+
function init(path3) {
|
|
3826
|
+
return {
|
|
3827
|
+
path: path3 === "" ? "initData" : `initData.${path3}`
|
|
3828
|
+
};
|
|
3829
|
+
}
|
|
3830
|
+
function state(path3) {
|
|
3831
|
+
return {
|
|
3832
|
+
path: path3 === "" ? "state" : `state.${path3}`
|
|
3833
|
+
};
|
|
3834
|
+
}
|
|
3835
|
+
function lit(v) {
|
|
3836
|
+
return {
|
|
3837
|
+
literal: v
|
|
3838
|
+
};
|
|
3839
|
+
}
|
|
3840
|
+
function toPathOrLiteral(v) {
|
|
3841
|
+
if (typeof v === "object" && v !== null) {
|
|
3842
|
+
if ("path" in v) return {
|
|
3843
|
+
path: v.path
|
|
3844
|
+
};
|
|
3845
|
+
if ("literal" in v) return {
|
|
3846
|
+
literal: v.literal
|
|
3847
|
+
};
|
|
3266
3848
|
}
|
|
3267
3849
|
return {
|
|
3268
|
-
|
|
3850
|
+
literal: v
|
|
3269
3851
|
};
|
|
3270
3852
|
}
|
|
3271
3853
|
function continuedFailureValue(error, killReason) {
|
|
@@ -3367,7 +3949,28 @@ function resolvePlacements(calls) {
|
|
|
3367
3949
|
break;
|
|
3368
3950
|
}
|
|
3369
3951
|
});
|
|
3370
|
-
const
|
|
3952
|
+
const hitlPlacementIssue = /* @__PURE__ */ __name3((node, ref, i, container) => {
|
|
3953
|
+
if (!isHitlNode2(node)) return void 0;
|
|
3954
|
+
const id = node.id;
|
|
3955
|
+
if (ref.armMap) {
|
|
3956
|
+
return {
|
|
3957
|
+
code: "node-type-unsupported-in-container",
|
|
3958
|
+
message: workflowHitlArmShapeMessage(node.type, id, "mapped-arm"),
|
|
3959
|
+
callIndex: i,
|
|
3960
|
+
stepId: id
|
|
3961
|
+
};
|
|
3962
|
+
}
|
|
3963
|
+
if (container !== "place" && !workflowContainerRunsHitlArm(container)) {
|
|
3964
|
+
return {
|
|
3965
|
+
code: "node-type-unsupported-in-container",
|
|
3966
|
+
message: workflowHitlArmUnsupportedMessage(node.type, id, container),
|
|
3967
|
+
callIndex: i,
|
|
3968
|
+
stepId: id
|
|
3969
|
+
};
|
|
3970
|
+
}
|
|
3971
|
+
return void 0;
|
|
3972
|
+
}, "hitlPlacementIssue");
|
|
3973
|
+
const resolve3 = /* @__PURE__ */ __name3((ref, i, allowMapping, container) => {
|
|
3371
3974
|
if ("node" in ref) {
|
|
3372
3975
|
if (ref.node.type === "mapping" && !allowMapping) {
|
|
3373
3976
|
issues.push({
|
|
@@ -3384,7 +3987,7 @@ function resolvePlacements(calls) {
|
|
|
3384
3987
|
if (!d) {
|
|
3385
3988
|
issues.push({
|
|
3386
3989
|
code: "unknown-step-ref",
|
|
3387
|
-
message: `"${ref.ref}" is not declared anywhere in the chain \u2014 declare it with agentStep/specialistStep/toolStep/map(\u2026, { id })/workflow(\u2026)`,
|
|
3990
|
+
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)`,
|
|
3388
3991
|
callIndex: i,
|
|
3389
3992
|
stepId: ref.ref
|
|
3390
3993
|
});
|
|
@@ -3399,6 +4002,11 @@ function resolvePlacements(calls) {
|
|
|
3399
4002
|
});
|
|
3400
4003
|
return void 0;
|
|
3401
4004
|
}
|
|
4005
|
+
const hitl = hitlPlacementIssue(d.node, ref, i, container);
|
|
4006
|
+
if (hitl) {
|
|
4007
|
+
issues.push(hitl);
|
|
4008
|
+
return void 0;
|
|
4009
|
+
}
|
|
3402
4010
|
const prior = placedBy.get(ref.ref);
|
|
3403
4011
|
if (prior !== void 0 && prior !== i) {
|
|
3404
4012
|
issues.push({
|
|
@@ -3412,23 +4020,31 @@ function resolvePlacements(calls) {
|
|
|
3412
4020
|
placedBy.set(ref.ref, i);
|
|
3413
4021
|
return d.node;
|
|
3414
4022
|
}, "resolve");
|
|
4023
|
+
const claim = /* @__PURE__ */ __name3((ref, i, allowMapping, container) => {
|
|
4024
|
+
if ("ref" in ref) {
|
|
4025
|
+
resolve3(ref, i, allowMapping, container);
|
|
4026
|
+
return;
|
|
4027
|
+
}
|
|
4028
|
+
const hitl = hitlPlacementIssue(ref.node, ref, i, container);
|
|
4029
|
+
if (hitl) issues.push(hitl);
|
|
4030
|
+
}, "claim");
|
|
3415
4031
|
calls.forEach((call, i) => {
|
|
3416
4032
|
switch (call.kind) {
|
|
3417
4033
|
case "place":
|
|
3418
|
-
|
|
4034
|
+
claim({
|
|
3419
4035
|
ref: call.ref
|
|
3420
|
-
}, i, false);
|
|
4036
|
+
}, i, false, "place");
|
|
3421
4037
|
break;
|
|
3422
4038
|
case "parallel":
|
|
3423
|
-
for (const arm of call.arms)
|
|
4039
|
+
for (const arm of call.arms) claim(arm, i, false, "parallel");
|
|
3424
4040
|
break;
|
|
3425
4041
|
case "conditional":
|
|
3426
|
-
for (const a of call.arms)
|
|
3427
|
-
if (call.otherwise
|
|
4042
|
+
for (const a of call.arms) claim(a.target, i, true, "conditional");
|
|
4043
|
+
if (call.otherwise) claim(call.otherwise, i, true, "conditional");
|
|
3428
4044
|
break;
|
|
3429
4045
|
case "foreach":
|
|
3430
4046
|
case "loop":
|
|
3431
|
-
|
|
4047
|
+
claim(call.body, i, false, call.kind);
|
|
3432
4048
|
break;
|
|
3433
4049
|
default:
|
|
3434
4050
|
break;
|
|
@@ -3437,7 +4053,7 @@ function resolvePlacements(calls) {
|
|
|
3437
4053
|
const graph = [];
|
|
3438
4054
|
const lookup = /* @__PURE__ */ __name3((ref) => {
|
|
3439
4055
|
const n2 = "node" in ref ? ref.node : declared.get(ref.ref)?.node;
|
|
3440
|
-
if (!n2 || !ref.armMap || n2.type === "mapping") return n2;
|
|
4056
|
+
if (!n2 || !ref.armMap || n2.type === "mapping" || isHitlNode2(n2)) return n2;
|
|
3441
4057
|
return lowerContainerArm(ref.armMap, n2);
|
|
3442
4058
|
}, "lookup");
|
|
3443
4059
|
calls.forEach((call, i) => {
|
|
@@ -3517,6 +4133,57 @@ function resolvePlacements(calls) {
|
|
|
3517
4133
|
issues
|
|
3518
4134
|
};
|
|
3519
4135
|
}
|
|
4136
|
+
function isConditionalJoinId(stepId) {
|
|
4137
|
+
return CONDITIONAL_JOIN_ID.test(stepId);
|
|
4138
|
+
}
|
|
4139
|
+
function isPlainObject(v) {
|
|
4140
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
4141
|
+
}
|
|
4142
|
+
function leafValue(row) {
|
|
4143
|
+
if (row.status === "completed") return row.output === void 0 ? null : row.output;
|
|
4144
|
+
return continuedFailureValue(row.error, row.killReason);
|
|
4145
|
+
}
|
|
4146
|
+
function runOutputLeaves(steps) {
|
|
4147
|
+
const dependedOn = /* @__PURE__ */ new Set();
|
|
4148
|
+
for (const s of steps) {
|
|
4149
|
+
if (s.stepId === GOAL_JUDGE_STEP_ID) continue;
|
|
4150
|
+
for (const d of s.dependsOn ?? []) dependedOn.add(d);
|
|
4151
|
+
}
|
|
4152
|
+
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));
|
|
4153
|
+
}
|
|
4154
|
+
function deriveRunOutput(steps) {
|
|
4155
|
+
const leaves = runOutputLeaves(steps);
|
|
4156
|
+
if (leaves.length === 0) return void 0;
|
|
4157
|
+
if (leaves.length === 1) {
|
|
4158
|
+
const leaf = leaves[0];
|
|
4159
|
+
const value22 = leafValue(leaf);
|
|
4160
|
+
if (isConditionalJoinId(leaf.stepId) && isPlainObject(value22)) {
|
|
4161
|
+
const keys = Object.keys(value22);
|
|
4162
|
+
if (keys.length === 1) return {
|
|
4163
|
+
output: value22[keys[0]],
|
|
4164
|
+
leafIds: [
|
|
4165
|
+
keys[0]
|
|
4166
|
+
]
|
|
4167
|
+
};
|
|
4168
|
+
}
|
|
4169
|
+
return {
|
|
4170
|
+
output: value22,
|
|
4171
|
+
leafIds: [
|
|
4172
|
+
leaf.stepId
|
|
4173
|
+
]
|
|
4174
|
+
};
|
|
4175
|
+
}
|
|
4176
|
+
const output = {};
|
|
4177
|
+
for (const leaf of leaves) output[leaf.stepId] = leafValue(leaf);
|
|
4178
|
+
return {
|
|
4179
|
+
output,
|
|
4180
|
+
leafIds: leaves.map((l) => l.stepId)
|
|
4181
|
+
};
|
|
4182
|
+
}
|
|
4183
|
+
function subrunSettledOutput(child) {
|
|
4184
|
+
if (child.output !== void 0) return child.output;
|
|
4185
|
+
return deriveRunOutput(child.steps ?? [])?.output ?? null;
|
|
4186
|
+
}
|
|
3520
4187
|
function seedLedgerFromRun(run, steps, targetPlan, opts = {}) {
|
|
3521
4188
|
const byId = /* @__PURE__ */ new Map();
|
|
3522
4189
|
for (const s of steps) {
|
|
@@ -3539,15 +4206,15 @@ function seedLedgerFromRun(run, steps, targetPlan, opts = {}) {
|
|
|
3539
4206
|
parent
|
|
3540
4207
|
] : [];
|
|
3541
4208
|
}, "dependsOf");
|
|
3542
|
-
const
|
|
4209
|
+
const walk22 = [
|
|
3543
4210
|
...targetPlan.order
|
|
3544
4211
|
];
|
|
3545
4212
|
for (const s of steps) {
|
|
3546
|
-
if (!known.has(s.stepId) && parentOf(s.stepId) && known.has(parentOf(s.stepId)) && !
|
|
3547
|
-
|
|
4213
|
+
if (!known.has(s.stepId) && parentOf(s.stepId) && known.has(parentOf(s.stepId)) && !walk22.includes(s.stepId)) {
|
|
4214
|
+
walk22.push(s.stepId);
|
|
3548
4215
|
}
|
|
3549
4216
|
}
|
|
3550
|
-
for (const id of
|
|
4217
|
+
for (const id of walk22) {
|
|
3551
4218
|
const row = byId.get(id);
|
|
3552
4219
|
if (!row || row.status !== "completed") continue;
|
|
3553
4220
|
if (!dependsOf(id).every((d) => seededIds.has(d))) {
|
|
@@ -3667,6 +4334,15 @@ function replayLedger(g, ledger) {
|
|
|
3667
4334
|
local,
|
|
3668
4335
|
diverged: recordedCount !== local
|
|
3669
4336
|
});
|
|
4337
|
+
} else if (node.kind === "subrun" && recorded.status === "completed" && recorded.child) {
|
|
4338
|
+
const local = subrunSettledOutput(recorded.child);
|
|
4339
|
+
verdicts.push({
|
|
4340
|
+
stepId: id,
|
|
4341
|
+
kind: "subrun",
|
|
4342
|
+
recorded: recorded.output,
|
|
4343
|
+
local,
|
|
4344
|
+
diverged: canonical(recorded.output) !== canonical(local)
|
|
4345
|
+
});
|
|
3670
4346
|
}
|
|
3671
4347
|
}
|
|
3672
4348
|
return {
|
|
@@ -3708,7 +4384,7 @@ function ancestorResults(plan, id, rows22) {
|
|
|
3708
4384
|
}
|
|
3709
4385
|
return hit;
|
|
3710
4386
|
}, "take");
|
|
3711
|
-
const
|
|
4387
|
+
const walk22 = /* @__PURE__ */ __name3((ids) => {
|
|
3712
4388
|
for (const dep of ids) {
|
|
3713
4389
|
if (seen.has(dep)) continue;
|
|
3714
4390
|
seen.add(dep);
|
|
@@ -3733,10 +4409,10 @@ function ancestorResults(plan, id, rows22) {
|
|
|
3733
4409
|
}
|
|
3734
4410
|
}
|
|
3735
4411
|
}
|
|
3736
|
-
|
|
4412
|
+
walk22(node.dependsOn);
|
|
3737
4413
|
}
|
|
3738
4414
|
}, "walk");
|
|
3739
|
-
|
|
4415
|
+
walk22(plan.steps[id]?.dependsOn ?? []);
|
|
3740
4416
|
return out;
|
|
3741
4417
|
}
|
|
3742
4418
|
function inferTaken(entry, rows22) {
|
|
@@ -3808,21 +4484,86 @@ function runCounts(counts) {
|
|
|
3808
4484
|
pending: n(c.pending) + n(c.ready) + n(c.waiting)
|
|
3809
4485
|
};
|
|
3810
4486
|
}
|
|
3811
|
-
function
|
|
4487
|
+
function isPricedStepReceipt(receipt) {
|
|
4488
|
+
return typeof receipt?.multiplier === "number" && Number.isFinite(receipt.multiplier);
|
|
4489
|
+
}
|
|
4490
|
+
function receiptEngine(engine) {
|
|
4491
|
+
if (engine === "actions") return "seat";
|
|
4492
|
+
if (engine === "credits") return "legacy";
|
|
4493
|
+
return void 0;
|
|
4494
|
+
}
|
|
4495
|
+
function receiptTier(tier) {
|
|
4496
|
+
return tier === "light" || tier === "standard" || tier === "heavy" ? tier : void 0;
|
|
4497
|
+
}
|
|
4498
|
+
function stepBillingView(receipt) {
|
|
4499
|
+
const engine = receiptEngine(receipt?.engine);
|
|
4500
|
+
if (!receipt || engine === void 0) return void 0;
|
|
4501
|
+
return pruneUndefined({
|
|
4502
|
+
engine,
|
|
4503
|
+
attempt: typeof receipt.attempt === "number" ? receipt.attempt : void 0,
|
|
4504
|
+
credits: typeof receipt.credits === "number" ? receipt.credits : void 0,
|
|
4505
|
+
actions: typeof receipt.actionsEstimate === "number" ? receipt.actionsEstimate : void 0,
|
|
4506
|
+
model: typeof receipt.model === "string" ? receipt.model : void 0,
|
|
4507
|
+
tier: receiptTier(receipt.tier),
|
|
4508
|
+
multiplier: typeof receipt.multiplier === "number" ? receipt.multiplier : void 0,
|
|
4509
|
+
byok: typeof receipt.byok === "boolean" ? receipt.byok : void 0,
|
|
4510
|
+
calibrated: typeof receipt.calibrated === "boolean" ? receipt.calibrated : void 0
|
|
4511
|
+
});
|
|
4512
|
+
}
|
|
4513
|
+
function runUsage(run, receipts) {
|
|
4514
|
+
const actions = n(run.budget?.spent?.actionsEstimate);
|
|
4515
|
+
const stamped = run.budget?.engine;
|
|
4516
|
+
const priced = (receipts ?? []).filter(isPricedStepReceipt);
|
|
4517
|
+
const engine = stamped === "seat" || stamped === "legacy" ? stamped : actions > 0 || priced.some((r) => r.engine === "actions") ? "seat" : priced.some((r) => r.engine === "credits") ? "legacy" : void 0;
|
|
4518
|
+
const seat = engine === "seat";
|
|
4519
|
+
const metering = engine ? "priced" : "flat";
|
|
3812
4520
|
return {
|
|
3813
4521
|
creditsUsed: run.budget?.spent?.credits ?? 0,
|
|
3814
4522
|
actionsEstimate: run.budget?.spent?.actionsEstimate ?? 0,
|
|
4523
|
+
...seat ? {
|
|
4524
|
+
actionsUsed: actions
|
|
4525
|
+
} : {},
|
|
4526
|
+
metering,
|
|
4527
|
+
...engine ? {
|
|
4528
|
+
engine
|
|
4529
|
+
} : {},
|
|
3815
4530
|
steps: run.budget?.spent?.steps ?? 0,
|
|
3816
4531
|
inputTokens: run.usage?.inputTokens ?? 0,
|
|
3817
4532
|
outputTokens: run.usage?.outputTokens ?? 0
|
|
3818
4533
|
};
|
|
3819
4534
|
}
|
|
4535
|
+
function runBudgetCap(budget) {
|
|
4536
|
+
const cap = budget?.maxCredits;
|
|
4537
|
+
return typeof cap === "number" && Number.isFinite(cap) && cap > 0 ? cap : void 0;
|
|
4538
|
+
}
|
|
4539
|
+
function runBudgetRemaining(budget) {
|
|
4540
|
+
const cap = runBudgetCap(budget);
|
|
4541
|
+
if (cap === void 0) return void 0;
|
|
4542
|
+
const spent = budget?.spent;
|
|
4543
|
+
return Math.max(0, cap - n(spent?.credits) - n(spent?.actionsEstimate) - n(budget?.reserved));
|
|
4544
|
+
}
|
|
3820
4545
|
function runCancelView(cancel) {
|
|
3821
4546
|
if (!cancel) return void 0;
|
|
3822
4547
|
return {
|
|
3823
4548
|
requestedAt: cancel.requestedAt,
|
|
3824
4549
|
requestedBy: cancel.requestedBy ?? "",
|
|
3825
|
-
forceAvailableAt: cancel.forceAfter ?? cancel.requestedAt + FORCE_CANCEL_STALE_MS
|
|
4550
|
+
forceAvailableAt: cancel.forceAfter ?? cancel.requestedAt + FORCE_CANCEL_STALE_MS,
|
|
4551
|
+
// LUA-686: the wall's own request (LUA-683 stamps `wall:true` from a system actor only) rides the wire, so a
|
|
4552
|
+
// client can tell a wall-ended `timed_out` run's audit block from a cancel it should chip. Absent, never false.
|
|
4553
|
+
...cancel.wall === true ? {
|
|
4554
|
+
wall: true
|
|
4555
|
+
} : {},
|
|
4556
|
+
// LUA-704: a forced terminal's audit — who forced it, when, and a human's own reason text (the run's `reason`
|
|
4557
|
+
// is the terminal's). Absent on an unforced terminal, never null.
|
|
4558
|
+
...typeof cancel.forcedAt === "number" ? {
|
|
4559
|
+
forcedAt: cancel.forcedAt
|
|
4560
|
+
} : {},
|
|
4561
|
+
...typeof cancel.forcedBy === "string" && cancel.forcedBy ? {
|
|
4562
|
+
forcedBy: cancel.forcedBy
|
|
4563
|
+
} : {},
|
|
4564
|
+
...typeof cancel.forceReason === "string" && cancel.forceReason ? {
|
|
4565
|
+
forceReason: cancel.forceReason
|
|
4566
|
+
} : {}
|
|
3826
4567
|
};
|
|
3827
4568
|
}
|
|
3828
4569
|
function runWorkspaceView(ws) {
|
|
@@ -3841,7 +4582,6 @@ function runWorkspaceView(ws) {
|
|
|
3841
4582
|
function toWorkflowRunSummary(run) {
|
|
3842
4583
|
const status = run.status;
|
|
3843
4584
|
const principal = run.principal?.principal;
|
|
3844
|
-
const gated = status === "gated" || status === "suspended";
|
|
3845
4585
|
return pruneUndefined({
|
|
3846
4586
|
runId: run.id,
|
|
3847
4587
|
workflowId: run.workflowId,
|
|
@@ -3853,7 +4593,14 @@ function toWorkflowRunSummary(run) {
|
|
|
3853
4593
|
orgId: run.orgId,
|
|
3854
4594
|
spaceAgentId: run.spaceAgentId,
|
|
3855
4595
|
status,
|
|
3856
|
-
|
|
4596
|
+
// LUA-702 (pass-3B D6): the gate rides the wire whenever a NON-terminal doc carries one. In the LUA-681 window
|
|
4597
|
+
// (an approval resolved beside an open exception park) the run is `running` AND still holds `gate{exception}`
|
|
4598
|
+
// until the tick tail parks it back; projecting it only on gated|suspended hid that park from R4 / the CLI /
|
|
4599
|
+
// the desktop (prod 2026-09-05: `status: running, gate: null`). A terminal doc carries no gate by contract
|
|
4600
|
+
// (`terminalizeRun` drops `gate` / `suspendedFor` in every terminal write), but a row terminalized before that
|
|
4601
|
+
// held — pre-LUA-677/694 docs, a script-tier backstop that passed no `unset` — may still carry a stale one:
|
|
4602
|
+
// hidden here, so a `failed` run never reads "Needs a decision" on R3/R4 or the CLI (#2476 review).
|
|
4603
|
+
gate: run.gate && !isTerminalRunStatus(status) ? run.gate : void 0,
|
|
3857
4604
|
batchId: run.batchId,
|
|
3858
4605
|
goalId: run.goalId,
|
|
3859
4606
|
foreachOverflow: run.foreachOverflow,
|
|
@@ -3876,9 +4623,11 @@ function toWorkflowRunSummary(run) {
|
|
|
3876
4623
|
principalKind: run.principalKind ?? "user",
|
|
3877
4624
|
cancel: runCancelView(run.cancel),
|
|
3878
4625
|
usage: runUsage(run),
|
|
4626
|
+
// LUA-697: a row persisted before the write seams (#2406 / #2465 / the script tier) leaves scrubbed here too —
|
|
4627
|
+
// idempotent on a scrubbed message, bounded input; an empty message falls back to the code.
|
|
3879
4628
|
error: run.error ? {
|
|
3880
4629
|
code: run.error.code ?? "error",
|
|
3881
|
-
message: run.error.message ?? "",
|
|
4630
|
+
message: scrubStepErrorMessage(run.error.message) ?? run.error.code ?? "error",
|
|
3882
4631
|
stepId: run.error.stepId
|
|
3883
4632
|
} : void 0,
|
|
3884
4633
|
kind: "run",
|
|
@@ -3897,6 +4646,49 @@ function toWorkflowRunSummary(run) {
|
|
|
3897
4646
|
hasOutput: run.hasOutput === true || run.output !== void 0 || run.outputRef !== void 0 ? true : void 0
|
|
3898
4647
|
});
|
|
3899
4648
|
}
|
|
4649
|
+
function scrubDetailValue(value22, depth) {
|
|
4650
|
+
if (typeof value22 === "string") return scrubSecretText(value22);
|
|
4651
|
+
if (typeof value22 === "number" || typeof value22 === "boolean" || value22 === null) return value22;
|
|
4652
|
+
if (depth >= DETAIL_MAX_DEPTH) return void 0;
|
|
4653
|
+
if (Array.isArray(value22)) {
|
|
4654
|
+
return value22.slice(0, DETAIL_MAX_ITEMS).map((v) => scrubDetailValue(v, depth + 1)).filter((v) => v !== void 0);
|
|
4655
|
+
}
|
|
4656
|
+
if (typeof value22 === "object") {
|
|
4657
|
+
const out = {};
|
|
4658
|
+
for (const [k, v] of Object.entries(value22)) {
|
|
4659
|
+
const s = scrubDetailValue(v, depth + 1);
|
|
4660
|
+
if (s !== void 0) out[k] = s;
|
|
4661
|
+
}
|
|
4662
|
+
return out;
|
|
4663
|
+
}
|
|
4664
|
+
return void 0;
|
|
4665
|
+
}
|
|
4666
|
+
function stepErrorDetail(error) {
|
|
4667
|
+
if (!error || typeof error !== "object") return void 0;
|
|
4668
|
+
const d = error.detail;
|
|
4669
|
+
if (!d || typeof d !== "object" || Array.isArray(d)) return void 0;
|
|
4670
|
+
const bag = d;
|
|
4671
|
+
const out = {};
|
|
4672
|
+
for (const k of STEP_ERROR_DETAIL_KEYS) {
|
|
4673
|
+
if (!(k in bag)) continue;
|
|
4674
|
+
const v = scrubDetailValue(bag[k], 1);
|
|
4675
|
+
if (v !== void 0) out[k] = v;
|
|
4676
|
+
}
|
|
4677
|
+
if (!Object.keys(out).length) return void 0;
|
|
4678
|
+
let bytes;
|
|
4679
|
+
try {
|
|
4680
|
+
bytes = new TextEncoder().encode(JSON.stringify(out)).length;
|
|
4681
|
+
} catch {
|
|
4682
|
+
return void 0;
|
|
4683
|
+
}
|
|
4684
|
+
if (bytes <= STEP_ERROR_DETAIL_MAX_BYTES) return out;
|
|
4685
|
+
const scalars = Object.fromEntries(Object.entries(out).filter(([, v]) => v === null || typeof v !== "object"));
|
|
4686
|
+
return {
|
|
4687
|
+
...scalars,
|
|
4688
|
+
__truncated: true,
|
|
4689
|
+
bytes
|
|
4690
|
+
};
|
|
4691
|
+
}
|
|
3900
4692
|
function timeZoneSupported(tz) {
|
|
3901
4693
|
if (typeof tz !== "string" || !tz) return false;
|
|
3902
4694
|
const intl = Intl;
|
|
@@ -4360,6 +5152,22 @@ function rebaseItemPointer(pointer, itemsPath, index) {
|
|
|
4360
5152
|
const base = `/${itemsPath.split(".").map(escapePointer).join("/")}/${index}`;
|
|
4361
5153
|
return pointer === "/" || pointer === "" ? base : `${base}${pointer}`;
|
|
4362
5154
|
}
|
|
5155
|
+
function describeApproverSpecRefusal(spec) {
|
|
5156
|
+
const raw = spec === void 0 ? "undefined" : JSON.stringify(spec) ?? String(spec);
|
|
5157
|
+
const written = raw.length > APPROVER_WRITTEN_MAX ? `${raw.slice(0, APPROVER_WRITTEN_MAX - 1)}\u2026` : raw;
|
|
5158
|
+
const users = typeof spec === "object" && spec !== null ? spec.users : void 0;
|
|
5159
|
+
const approver = typeof users === "string" && USER_ID_SHAPED_RE.test(users) ? {
|
|
5160
|
+
users: [
|
|
5161
|
+
users
|
|
5162
|
+
]
|
|
5163
|
+
} : "creator";
|
|
5164
|
+
const message = `approver ${written} is not an approver \u2014 legal: ${APPROVER_SPEC_SHAPES.join(" | ")}. 'creator' is the person who started the run: write approver:'creator' for "ask me" / "I approve"; {users:[\u2026]} takes user ids, never emails, names or {type:'user'}` + (approver === "creator" ? "" : `; here: approver:${JSON.stringify(approver)}`);
|
|
5165
|
+
return {
|
|
5166
|
+
approver,
|
|
5167
|
+
written,
|
|
5168
|
+
message
|
|
5169
|
+
};
|
|
5170
|
+
}
|
|
4363
5171
|
function bindingRootsOk(template22) {
|
|
4364
5172
|
const refs = [
|
|
4365
5173
|
...template22.matchAll(/\$\{\s*([A-Za-z_$][\w$]*)/g)
|
|
@@ -4384,7 +5192,19 @@ function validateApproverBlock(node, opts = {
|
|
|
4384
5192
|
if (!r.success) {
|
|
4385
5193
|
const users = spec?.users;
|
|
4386
5194
|
if (Array.isArray(users) && users.length > APPROVER_SPEC_MAX_USERS) push("cap-exceeded", path3, `at most ${APPROVER_SPEC_MAX_USERS} users`);
|
|
4387
|
-
else
|
|
5195
|
+
else {
|
|
5196
|
+
const refusal = describeApproverSpecRefusal(spec);
|
|
5197
|
+
issues.push({
|
|
5198
|
+
code: "approver-invalid",
|
|
5199
|
+
path: path3,
|
|
5200
|
+
severity: "error",
|
|
5201
|
+
message: refusal.message,
|
|
5202
|
+
repair: {
|
|
5203
|
+
approver: refusal.approver,
|
|
5204
|
+
written: refusal.written
|
|
5205
|
+
}
|
|
5206
|
+
});
|
|
5207
|
+
}
|
|
4388
5208
|
return;
|
|
4389
5209
|
}
|
|
4390
5210
|
const s = r.data;
|
|
@@ -4452,7 +5272,7 @@ function liftRenderedApprover(row, rendered) {
|
|
|
4452
5272
|
}
|
|
4453
5273
|
function collectEnvTemplateKeys(value22) {
|
|
4454
5274
|
const keys = /* @__PURE__ */ new Set();
|
|
4455
|
-
const
|
|
5275
|
+
const walk22 = /* @__PURE__ */ __name3((v) => {
|
|
4456
5276
|
if (isEnvRef(v)) {
|
|
4457
5277
|
keys.add(v.__envRef);
|
|
4458
5278
|
return;
|
|
@@ -4460,26 +5280,26 @@ function collectEnvTemplateKeys(value22) {
|
|
|
4460
5280
|
if (typeof v === "string") {
|
|
4461
5281
|
if (looksLikeEmbeddedJson(v)) {
|
|
4462
5282
|
try {
|
|
4463
|
-
|
|
5283
|
+
walk22(JSON.parse(v));
|
|
4464
5284
|
} catch {
|
|
4465
5285
|
}
|
|
4466
5286
|
}
|
|
4467
5287
|
return;
|
|
4468
5288
|
}
|
|
4469
5289
|
if (Array.isArray(v)) {
|
|
4470
|
-
for (const e of v)
|
|
5290
|
+
for (const e of v) walk22(e);
|
|
4471
5291
|
return;
|
|
4472
5292
|
}
|
|
4473
|
-
if (v && typeof v === "object") for (const e of Object.values(v))
|
|
5293
|
+
if (v && typeof v === "object") for (const e of Object.values(v)) walk22(e);
|
|
4474
5294
|
}, "walk");
|
|
4475
|
-
|
|
5295
|
+
walk22(value22);
|
|
4476
5296
|
return [
|
|
4477
5297
|
...keys
|
|
4478
5298
|
].sort();
|
|
4479
5299
|
}
|
|
4480
5300
|
function substituteEnvRefs(value22, overlay) {
|
|
4481
5301
|
const missing = /* @__PURE__ */ new Set();
|
|
4482
|
-
const
|
|
5302
|
+
const walk22 = /* @__PURE__ */ __name3((v, slot = false) => {
|
|
4483
5303
|
if (isEnvRef(v)) {
|
|
4484
5304
|
if (Object.prototype.hasOwnProperty.call(overlay, v.__envRef)) {
|
|
4485
5305
|
const s = overlay[v.__envRef];
|
|
@@ -4496,22 +5316,22 @@ function substituteEnvRefs(value22, overlay) {
|
|
|
4496
5316
|
const cfg = JSON.parse(v);
|
|
4497
5317
|
if (!cfg || typeof cfg !== "object" || Array.isArray(cfg)) return v;
|
|
4498
5318
|
const out = {};
|
|
4499
|
-
for (const [k, e] of Object.entries(cfg)) out[k] =
|
|
5319
|
+
for (const [k, e] of Object.entries(cfg)) out[k] = walk22(e, true);
|
|
4500
5320
|
return canonicalJson(out);
|
|
4501
5321
|
} catch {
|
|
4502
5322
|
return v;
|
|
4503
5323
|
}
|
|
4504
5324
|
}
|
|
4505
|
-
if (Array.isArray(v)) return v.map((e) =>
|
|
5325
|
+
if (Array.isArray(v)) return v.map((e) => walk22(e));
|
|
4506
5326
|
if (v && typeof v === "object") {
|
|
4507
5327
|
const out = {};
|
|
4508
|
-
for (const [k, e] of Object.entries(v)) out[k] =
|
|
5328
|
+
for (const [k, e] of Object.entries(v)) out[k] = walk22(e);
|
|
4509
5329
|
return out;
|
|
4510
5330
|
}
|
|
4511
5331
|
return v;
|
|
4512
5332
|
}, "walk");
|
|
4513
5333
|
return {
|
|
4514
|
-
value:
|
|
5334
|
+
value: walk22(value22),
|
|
4515
5335
|
missing: [
|
|
4516
5336
|
...missing
|
|
4517
5337
|
].sort()
|
|
@@ -4740,26 +5560,115 @@ function needsInheritedWorkspace(graph) {
|
|
|
4740
5560
|
}
|
|
4741
5561
|
return false;
|
|
4742
5562
|
}
|
|
4743
|
-
var __defProp3, __name3,
|
|
5563
|
+
var __defProp3, __name3, WorkflowTemplateError, TEMPLATE_PLACEHOLDER, TEMPLATE_NAMESPACES, MAP_DESCRIPTOR_KEYS, MAP_MEMBER_MALFORMED_CODE, fromInit, fromStep, value, template, fromRequest, rows, fromKnowledge, SideEffectsSchema, JobResourcesSchema, WORKFLOW_ARM_SUBRUN_ID, WORKSPACE_TEMPLATE_EXPR_RE, SLEEP_UNTIL_REPLACEMENT, WORKFLOW_CAPS_DEFAULT, WORKFLOW_STEP_DEFAULT_TIMEOUT_SECONDS, WORKFLOW_AGENT_DEFAULT_TIMEOUT_SECONDS, WORKFLOW_JOB_DEFAULT_TIMEOUT_SECONDS, WORKFLOW_FOREACH_DEFAULT_CONCURRENCY, WORKFLOW_FOREACH_DEFAULT_MAX_ITEMS, WORKFLOW_LOOP_DEFAULT_MAX_ITERATIONS, WORKFLOW_SUSPEND_DEFAULT_TIMEOUT_HOURS, WORKFLOW_SIGNAL_DEFAULT_SOURCES, clone, CONNECTION_ID_HEX_RE, WORKFLOW_JOB_TOOLS, WORKFLOW_JOB_MAX_WORKTREE_ARMS, workspaceOf, mountsWorkspace, isJobTier, jobToolsOf, schemaIsArray, isHitlNode, isSingleStep, singleId, armId, TEMPLATE_STEP_REF, EDITABLE_PATH_RE, PREDICATE_OPS, isPredicateScalar, GRAPH_HASH_PREFIX, WorkflowPlanError, isArmStep, armStepId, armStepKind, joinIdOf, containerIdOf, PATH_PLACEHOLDER, MISSING, stepIdOf, cmp, eq, ne, gt, gte, lt, lte, inSet, notIn, exists, notExists, truthy, falsy, and, or, not, CONTINUED_FAILURE_TAG, CONTINUED_FAILURE_DEFAULT_CODE, CONTINUED_FAILURE_OUTPUT_SCHEMA, CONTINUED_FAILURE_LEAF_PATHS, isHitlNode2, nodeIdOf, GOAL_JUDGE_STEP_ID, NON_LEAF_KINDS, CONDITIONAL_JOIN_ID, branchArmId, canonical, sortKeys, JOIN, entryOfJoin, FORCE_CANCEL_STALE_MS, TERMINAL, IN_FLIGHT, n, STEP_ERROR_DETAIL_KEYS, STEP_ERROR_DETAIL_MAX_BYTES, DETAIL_MAX_DEPTH, DETAIL_MAX_ITEMS, MAX_HOLIDAYS, MAX_WALK_DAYS, HHMM, YMD, MS_PER_MIN, MS_PER_DAY, MON_FRI, supportedTz, fmtCache, WEEKDAYS, JSON_PATCH_OPS, JSON_PATCH_MAX_OPS, JSON_PATCH_MAX_VALUE_BYTES, JSON_PATCH_MAX_TOTAL_BYTES, SEGMENT_RE, APPROVER_SPEC_MAX_USERS, ESCALATION_MAX_HOPS, TemplateBindingSchema, ApproverSpecSchema, FourEyesSchema, EscalationHopSchema, TerminalOutcomeSchema, ApprovalOnTimeoutSchema, APPROVER_SPEC_SHAPES, APPROVER_WRITTEN_MAX, USER_ID_SHAPED_RE, BINDING_ROOTS, WORKFLOW_ENV_OVERLAY_MAX_KEYS, WORKFLOW_ENV_OVERLAY_MAX_VALUE_BYTES, WORKFLOW_ENV_TEMPLATE_SECRET_KEY_RE, isEnvRef, looksLikeEmbeddedJson, ZERO, isRecord2;
|
|
4744
5564
|
var init_dist2 = __esm({
|
|
4745
5565
|
"../workflow-graph/dist/index.mjs"() {
|
|
4746
5566
|
"use strict";
|
|
4747
5567
|
init_dist();
|
|
4748
5568
|
init_dist();
|
|
5569
|
+
init_dist();
|
|
5570
|
+
init_dist();
|
|
5571
|
+
init_dist();
|
|
5572
|
+
init_dist();
|
|
5573
|
+
init_dist();
|
|
4749
5574
|
__defProp3 = Object.defineProperty;
|
|
4750
5575
|
__name3 = /* @__PURE__ */ __name((target, value22) => __defProp3(target, "name", { value: value22, configurable: true }), "__name");
|
|
4751
|
-
|
|
4752
|
-
|
|
4753
|
-
|
|
5576
|
+
WorkflowTemplateError = class extends Error {
|
|
5577
|
+
static {
|
|
5578
|
+
__name(this, "WorkflowTemplateError");
|
|
5579
|
+
}
|
|
5580
|
+
static {
|
|
5581
|
+
__name3(this, "WorkflowTemplateError");
|
|
5582
|
+
}
|
|
5583
|
+
placeholder;
|
|
5584
|
+
constructor(message, placeholder) {
|
|
5585
|
+
super(message), this.placeholder = placeholder;
|
|
5586
|
+
this.name = "WorkflowTemplateError";
|
|
5587
|
+
}
|
|
5588
|
+
};
|
|
5589
|
+
__name(isMapConfigObject, "isMapConfigObject");
|
|
5590
|
+
__name3(isMapConfigObject, "isMapConfigObject");
|
|
5591
|
+
__name(parseMapConfig, "parseMapConfig");
|
|
5592
|
+
__name3(parseMapConfig, "parseMapConfig");
|
|
5593
|
+
__name(mapConfigWire, "mapConfigWire");
|
|
5594
|
+
__name3(mapConfigWire, "mapConfigWire");
|
|
5595
|
+
TEMPLATE_PLACEHOLDER = /\$\{([^}]*)\}/g;
|
|
5596
|
+
TEMPLATE_NAMESPACES = [
|
|
5597
|
+
"initData",
|
|
5598
|
+
"state",
|
|
5599
|
+
"requestContext",
|
|
5600
|
+
"stepResults"
|
|
4754
5601
|
];
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
|
|
4758
|
-
|
|
5602
|
+
__name(describeBadPlaceholder, "describeBadPlaceholder");
|
|
5603
|
+
__name3(describeBadPlaceholder, "describeBadPlaceholder");
|
|
5604
|
+
__name(parseTemplatePlaceholder, "parseTemplatePlaceholder");
|
|
5605
|
+
__name3(parseTemplatePlaceholder, "parseTemplatePlaceholder");
|
|
5606
|
+
__name(traverseMappingPath, "traverseMappingPath");
|
|
5607
|
+
__name3(traverseMappingPath, "traverseMappingPath");
|
|
5608
|
+
__name(stringifyTemplateValue, "stringifyTemplateValue");
|
|
5609
|
+
__name3(stringifyTemplateValue, "stringifyTemplateValue");
|
|
5610
|
+
__name(escapeFence, "escapeFence");
|
|
5611
|
+
__name3(escapeFence, "escapeFence");
|
|
5612
|
+
__name(fenceBlock, "fenceBlock");
|
|
5613
|
+
__name3(fenceBlock, "fenceBlock");
|
|
5614
|
+
__name(renderTemplate, "renderTemplate");
|
|
5615
|
+
__name3(renderTemplate, "renderTemplate");
|
|
5616
|
+
__name(isMapDescriptor, "isMapDescriptor");
|
|
5617
|
+
__name3(isMapDescriptor, "isMapDescriptor");
|
|
5618
|
+
MAP_DESCRIPTOR_KEYS = [
|
|
5619
|
+
"step",
|
|
5620
|
+
"path",
|
|
5621
|
+
"initData",
|
|
5622
|
+
"value",
|
|
5623
|
+
"template",
|
|
5624
|
+
"requestContextPath",
|
|
5625
|
+
"knowledge"
|
|
4759
5626
|
];
|
|
5627
|
+
MAP_MEMBER_MALFORMED_CODE = "map-member-malformed";
|
|
5628
|
+
__name(malformedMapMembers, "malformedMapMembers");
|
|
5629
|
+
__name3(malformedMapMembers, "malformedMapMembers");
|
|
5630
|
+
__name(mapMemberMalformedMessage, "mapMemberMalformedMessage");
|
|
5631
|
+
__name3(mapMemberMalformedMessage, "mapMemberMalformedMessage");
|
|
5632
|
+
__name(resolveDescriptor, "resolveDescriptor");
|
|
5633
|
+
__name3(resolveDescriptor, "resolveDescriptor");
|
|
5634
|
+
__name(resolveMapping, "resolveMapping");
|
|
5635
|
+
__name3(resolveMapping, "resolveMapping");
|
|
5636
|
+
fromInit = /* @__PURE__ */ __name3((path3) => ({
|
|
5637
|
+
initData: true,
|
|
5638
|
+
path: path3
|
|
5639
|
+
}), "fromInit");
|
|
5640
|
+
fromStep = /* @__PURE__ */ __name3((s, path3 = "") => {
|
|
5641
|
+
const idOf = /* @__PURE__ */ __name3((x) => typeof x === "string" ? x : x.id, "idOf");
|
|
5642
|
+
return {
|
|
5643
|
+
step: Array.isArray(s) ? s.map(idOf) : idOf(s),
|
|
5644
|
+
path: path3
|
|
5645
|
+
};
|
|
5646
|
+
}, "fromStep");
|
|
5647
|
+
value = /* @__PURE__ */ __name3((v) => ({
|
|
5648
|
+
value: v
|
|
5649
|
+
}), "value");
|
|
5650
|
+
template = /* @__PURE__ */ __name3((s) => ({
|
|
5651
|
+
template: s
|
|
5652
|
+
}), "template");
|
|
5653
|
+
fromRequest = /* @__PURE__ */ __name3((path3) => ({
|
|
5654
|
+
requestContextPath: path3
|
|
5655
|
+
}), "fromRequest");
|
|
5656
|
+
rows = /* @__PURE__ */ __name3((s, path3, page) => ({
|
|
5657
|
+
step: typeof s === "string" ? s : s.id,
|
|
5658
|
+
path: path3,
|
|
5659
|
+
rows: page
|
|
5660
|
+
}), "rows");
|
|
5661
|
+
fromKnowledge = /* @__PURE__ */ __name3((k) => ({
|
|
5662
|
+
knowledge: k
|
|
5663
|
+
}), "fromKnowledge");
|
|
4760
5664
|
SideEffectsSchema = z4.enum(WORKFLOW_SIDE_EFFECTS);
|
|
4761
5665
|
JobResourcesSchema = z4.enum(WORKFLOW_JOB_RESOURCES);
|
|
4762
5666
|
WORKFLOW_ARM_SUBRUN_ID = "$arm";
|
|
5667
|
+
WORKSPACE_TEMPLATE_EXPR_RE = /^\$\{\s*(?:initData|input)\.([^}]+?)\s*\}$/;
|
|
5668
|
+
__name(workspaceTemplatePath, "workspaceTemplatePath");
|
|
5669
|
+
__name3(workspaceTemplatePath, "workspaceTemplatePath");
|
|
5670
|
+
__name(retryBackoffs, "retryBackoffs");
|
|
5671
|
+
__name3(retryBackoffs, "retryBackoffs");
|
|
4763
5672
|
SLEEP_UNTIL_REPLACEMENT = Object.freeze({
|
|
4764
5673
|
type: "sleep",
|
|
4765
5674
|
duration: 6e4
|
|
@@ -4795,6 +5704,8 @@ var init_dist2 = __esm({
|
|
|
4795
5704
|
__name3(fillPolicy, "fillPolicy");
|
|
4796
5705
|
__name(fillSingle, "fillSingle");
|
|
4797
5706
|
__name3(fillSingle, "fillSingle");
|
|
5707
|
+
__name(fillHitl, "fillHitl");
|
|
5708
|
+
__name3(fillHitl, "fillHitl");
|
|
4798
5709
|
__name(fillArm, "fillArm");
|
|
4799
5710
|
__name3(fillArm, "fillArm");
|
|
4800
5711
|
__name(fillEntry, "fillEntry");
|
|
@@ -4838,11 +5749,15 @@ var init_dist2 = __esm({
|
|
|
4838
5749
|
if (t === void 0) return void 0;
|
|
4839
5750
|
return Array.isArray(t) ? t.includes("array") : t === "array";
|
|
4840
5751
|
}, "schemaIsArray");
|
|
5752
|
+
isHitlNode = /* @__PURE__ */ __name3((n2) => isWorkflowHitlEntryType(n2.type), "isHitlNode");
|
|
5753
|
+
isSingleStep = /* @__PURE__ */ __name3((n2) => !isHitlNode(n2), "isSingleStep");
|
|
4841
5754
|
singleId = /* @__PURE__ */ __name3((s) => s.type === "step" ? s.step.id : s.id, "singleId");
|
|
4842
5755
|
armId = /* @__PURE__ */ __name3((a) => a.type === "mapping" ? a.id : singleId(a), "armId");
|
|
4843
5756
|
TEMPLATE_STEP_REF = /\$\{\s*stepResults\.([A-Za-z0-9_\-]+)/g;
|
|
4844
5757
|
__name(templateStepRefs, "templateStepRefs");
|
|
4845
5758
|
__name3(templateStepRefs, "templateStepRefs");
|
|
5759
|
+
__name(readMapConfig, "readMapConfig");
|
|
5760
|
+
__name3(readMapConfig, "readMapConfig");
|
|
4846
5761
|
__name(mapConfigStepRefs, "mapConfigStepRefs");
|
|
4847
5762
|
__name3(mapConfigStepRefs, "mapConfigStepRefs");
|
|
4848
5763
|
__name(nodeStepRefs, "nodeStepRefs");
|
|
@@ -4892,14 +5807,9 @@ var init_dist2 = __esm({
|
|
|
4892
5807
|
this.name = "WorkflowPlanError";
|
|
4893
5808
|
}
|
|
4894
5809
|
};
|
|
4895
|
-
|
|
4896
|
-
|
|
4897
|
-
|
|
4898
|
-
tool: "tool",
|
|
4899
|
-
workflow: "subrun"
|
|
4900
|
-
};
|
|
4901
|
-
isSingleStep = /* @__PURE__ */ __name3((e) => e.type === "step" || e.type === "agent" || e.type === "tool" || e.type === "workflow", "isSingleStep");
|
|
4902
|
-
singleStepId = /* @__PURE__ */ __name3((e) => e.type === "step" ? e.step.id : e.id, "singleStepId");
|
|
5810
|
+
isArmStep = /* @__PURE__ */ __name3((e) => isWorkflowArmEntryType(e.type), "isArmStep");
|
|
5811
|
+
armStepId = /* @__PURE__ */ __name3((e) => e.type === "step" ? e.step.id : e.id, "armStepId");
|
|
5812
|
+
armStepKind = /* @__PURE__ */ __name3((e) => WORKFLOW_ARM_ENTRY_STEP_KINDS[e.type], "armStepKind");
|
|
4903
5813
|
joinIdOf = /* @__PURE__ */ __name3((entryId) => `${entryId}.join`, "joinIdOf");
|
|
4904
5814
|
containerIdOf = /* @__PURE__ */ __name3((type, entryIndex) => `${type}@${entryIndex}`, "containerIdOf");
|
|
4905
5815
|
__name(compilePlan, "compilePlan");
|
|
@@ -4994,74 +5904,6 @@ var init_dist2 = __esm({
|
|
|
4994
5904
|
op: "not",
|
|
4995
5905
|
arg
|
|
4996
5906
|
}), "not");
|
|
4997
|
-
WorkflowTemplateError = class extends Error {
|
|
4998
|
-
static {
|
|
4999
|
-
__name(this, "WorkflowTemplateError");
|
|
5000
|
-
}
|
|
5001
|
-
static {
|
|
5002
|
-
__name3(this, "WorkflowTemplateError");
|
|
5003
|
-
}
|
|
5004
|
-
placeholder;
|
|
5005
|
-
constructor(message, placeholder) {
|
|
5006
|
-
super(message), this.placeholder = placeholder;
|
|
5007
|
-
this.name = "WorkflowTemplateError";
|
|
5008
|
-
}
|
|
5009
|
-
};
|
|
5010
|
-
__name(parseMapConfig, "parseMapConfig");
|
|
5011
|
-
__name3(parseMapConfig, "parseMapConfig");
|
|
5012
|
-
TEMPLATE_PLACEHOLDER = /\$\{([^}]*)\}/g;
|
|
5013
|
-
TEMPLATE_NAMESPACES = [
|
|
5014
|
-
"initData",
|
|
5015
|
-
"state",
|
|
5016
|
-
"requestContext",
|
|
5017
|
-
"stepResults"
|
|
5018
|
-
];
|
|
5019
|
-
__name(describeBadPlaceholder, "describeBadPlaceholder");
|
|
5020
|
-
__name3(describeBadPlaceholder, "describeBadPlaceholder");
|
|
5021
|
-
__name(parseTemplatePlaceholder, "parseTemplatePlaceholder");
|
|
5022
|
-
__name3(parseTemplatePlaceholder, "parseTemplatePlaceholder");
|
|
5023
|
-
__name(traverseMappingPath, "traverseMappingPath");
|
|
5024
|
-
__name3(traverseMappingPath, "traverseMappingPath");
|
|
5025
|
-
__name(stringifyTemplateValue, "stringifyTemplateValue");
|
|
5026
|
-
__name3(stringifyTemplateValue, "stringifyTemplateValue");
|
|
5027
|
-
__name(escapeFence, "escapeFence");
|
|
5028
|
-
__name3(escapeFence, "escapeFence");
|
|
5029
|
-
__name(fenceBlock, "fenceBlock");
|
|
5030
|
-
__name3(fenceBlock, "fenceBlock");
|
|
5031
|
-
__name(renderTemplate, "renderTemplate");
|
|
5032
|
-
__name3(renderTemplate, "renderTemplate");
|
|
5033
|
-
__name(resolveDescriptor, "resolveDescriptor");
|
|
5034
|
-
__name3(resolveDescriptor, "resolveDescriptor");
|
|
5035
|
-
__name(resolveMapping, "resolveMapping");
|
|
5036
|
-
__name3(resolveMapping, "resolveMapping");
|
|
5037
|
-
fromInit = /* @__PURE__ */ __name3((path3) => ({
|
|
5038
|
-
initData: true,
|
|
5039
|
-
path: path3
|
|
5040
|
-
}), "fromInit");
|
|
5041
|
-
fromStep = /* @__PURE__ */ __name3((s, path3 = "") => {
|
|
5042
|
-
const idOf = /* @__PURE__ */ __name3((x) => typeof x === "string" ? x : x.id, "idOf");
|
|
5043
|
-
return {
|
|
5044
|
-
step: Array.isArray(s) ? s.map(idOf) : idOf(s),
|
|
5045
|
-
path: path3
|
|
5046
|
-
};
|
|
5047
|
-
}, "fromStep");
|
|
5048
|
-
value = /* @__PURE__ */ __name3((v) => ({
|
|
5049
|
-
value: v
|
|
5050
|
-
}), "value");
|
|
5051
|
-
template = /* @__PURE__ */ __name3((s) => ({
|
|
5052
|
-
template: s
|
|
5053
|
-
}), "template");
|
|
5054
|
-
fromRequest = /* @__PURE__ */ __name3((path3) => ({
|
|
5055
|
-
requestContextPath: path3
|
|
5056
|
-
}), "fromRequest");
|
|
5057
|
-
rows = /* @__PURE__ */ __name3((s, path3, page) => ({
|
|
5058
|
-
step: typeof s === "string" ? s : s.id,
|
|
5059
|
-
path: path3,
|
|
5060
|
-
rows: page
|
|
5061
|
-
}), "rows");
|
|
5062
|
-
fromKnowledge = /* @__PURE__ */ __name3((k) => ({
|
|
5063
|
-
knowledge: k
|
|
5064
|
-
}), "fromKnowledge");
|
|
5065
5907
|
CONTINUED_FAILURE_TAG = "continued_failure";
|
|
5066
5908
|
CONTINUED_FAILURE_DEFAULT_CODE = "step_failed";
|
|
5067
5909
|
CONTINUED_FAILURE_OUTPUT_SCHEMA = Object.freeze({
|
|
@@ -5113,6 +5955,7 @@ var init_dist2 = __esm({
|
|
|
5113
5955
|
__name3(continuedFailureValue, "continuedFailureValue");
|
|
5114
5956
|
__name(isContinuedFailureValue, "isContinuedFailureValue");
|
|
5115
5957
|
__name3(isContinuedFailureValue, "isContinuedFailureValue");
|
|
5958
|
+
isHitlNode2 = /* @__PURE__ */ __name3((n2) => isWorkflowHitlEntryType(n2.type), "isHitlNode");
|
|
5116
5959
|
__name(lowerContainerArm, "lowerContainerArm");
|
|
5117
5960
|
__name3(lowerContainerArm, "lowerContainerArm");
|
|
5118
5961
|
nodeIdOf = /* @__PURE__ */ __name3((n2) => n2.type === "step" ? n2.step.id : n2.id, "nodeIdOf");
|
|
@@ -5120,6 +5963,24 @@ var init_dist2 = __esm({
|
|
|
5120
5963
|
__name3(entryIds, "entryIds");
|
|
5121
5964
|
__name(resolvePlacements, "resolvePlacements");
|
|
5122
5965
|
__name3(resolvePlacements, "resolvePlacements");
|
|
5966
|
+
GOAL_JUDGE_STEP_ID = "__goal_judge";
|
|
5967
|
+
NON_LEAF_KINDS = /* @__PURE__ */ new Set([
|
|
5968
|
+
"foreach",
|
|
5969
|
+
"branch"
|
|
5970
|
+
]);
|
|
5971
|
+
CONDITIONAL_JOIN_ID = /^conditional@\d+\.join$/;
|
|
5972
|
+
__name(isConditionalJoinId, "isConditionalJoinId");
|
|
5973
|
+
__name3(isConditionalJoinId, "isConditionalJoinId");
|
|
5974
|
+
__name(isPlainObject, "isPlainObject");
|
|
5975
|
+
__name3(isPlainObject, "isPlainObject");
|
|
5976
|
+
__name(leafValue, "leafValue");
|
|
5977
|
+
__name3(leafValue, "leafValue");
|
|
5978
|
+
__name(runOutputLeaves, "runOutputLeaves");
|
|
5979
|
+
__name3(runOutputLeaves, "runOutputLeaves");
|
|
5980
|
+
__name(deriveRunOutput, "deriveRunOutput");
|
|
5981
|
+
__name3(deriveRunOutput, "deriveRunOutput");
|
|
5982
|
+
__name(subrunSettledOutput, "subrunSettledOutput");
|
|
5983
|
+
__name3(subrunSettledOutput, "subrunSettledOutput");
|
|
5123
5984
|
__name(seedLedgerFromRun, "seedLedgerFromRun");
|
|
5124
5985
|
__name3(seedLedgerFromRun, "seedLedgerFromRun");
|
|
5125
5986
|
branchArmId = /* @__PURE__ */ __name3((arm) => arm.type === "step" ? arm.step.id : arm.id, "branchArmId");
|
|
@@ -5168,14 +6029,63 @@ var init_dist2 = __esm({
|
|
|
5168
6029
|
n = /* @__PURE__ */ __name3((v) => typeof v === "number" && Number.isFinite(v) ? v : 0, "n");
|
|
5169
6030
|
__name(runCounts, "runCounts");
|
|
5170
6031
|
__name3(runCounts, "runCounts");
|
|
6032
|
+
__name(isPricedStepReceipt, "isPricedStepReceipt");
|
|
6033
|
+
__name3(isPricedStepReceipt, "isPricedStepReceipt");
|
|
6034
|
+
__name(receiptEngine, "receiptEngine");
|
|
6035
|
+
__name3(receiptEngine, "receiptEngine");
|
|
6036
|
+
__name(receiptTier, "receiptTier");
|
|
6037
|
+
__name3(receiptTier, "receiptTier");
|
|
6038
|
+
__name(stepBillingView, "stepBillingView");
|
|
6039
|
+
__name3(stepBillingView, "stepBillingView");
|
|
5171
6040
|
__name(runUsage, "runUsage");
|
|
5172
6041
|
__name3(runUsage, "runUsage");
|
|
6042
|
+
__name(runBudgetCap, "runBudgetCap");
|
|
6043
|
+
__name3(runBudgetCap, "runBudgetCap");
|
|
6044
|
+
__name(runBudgetRemaining, "runBudgetRemaining");
|
|
6045
|
+
__name3(runBudgetRemaining, "runBudgetRemaining");
|
|
5173
6046
|
__name(runCancelView, "runCancelView");
|
|
5174
6047
|
__name3(runCancelView, "runCancelView");
|
|
5175
6048
|
__name(runWorkspaceView, "runWorkspaceView");
|
|
5176
6049
|
__name3(runWorkspaceView, "runWorkspaceView");
|
|
5177
6050
|
__name(toWorkflowRunSummary, "toWorkflowRunSummary");
|
|
5178
6051
|
__name3(toWorkflowRunSummary, "toWorkflowRunSummary");
|
|
6052
|
+
STEP_ERROR_DETAIL_KEYS = [
|
|
6053
|
+
"reason",
|
|
6054
|
+
"key",
|
|
6055
|
+
"integrationType",
|
|
6056
|
+
"candidates",
|
|
6057
|
+
"connectionId",
|
|
6058
|
+
"status",
|
|
6059
|
+
"code",
|
|
6060
|
+
"providerStatus",
|
|
6061
|
+
"model",
|
|
6062
|
+
"turnIndex",
|
|
6063
|
+
"message",
|
|
6064
|
+
// LUA-655: `output_schema_invalid` — the Ajv issues ({path, message}, ≤ 20, scrubbed at the source too) and the
|
|
6065
|
+
// in-session repair rounds the attempt spent.
|
|
6066
|
+
"issues",
|
|
6067
|
+
"repairRounds",
|
|
6068
|
+
// LUA-669 (#2446 review 4): the subrun failures — `subrun_<status>` names the child run and how it ended
|
|
6069
|
+
// (`childStatus`, `childReason: 'max_duration'` under `subrun_timed_out`), `subrun_depth_exceeded` its `depth` /
|
|
6070
|
+
// `max`, and every refusal the target `workflowId`. Short scalars only: the child's whole `childError` and the
|
|
6071
|
+
// cycle walk's `ancestors` stay off the wire — the child run's own R4 carries its error.
|
|
6072
|
+
"childRunId",
|
|
6073
|
+
"childStatus",
|
|
6074
|
+
"childReason",
|
|
6075
|
+
"max",
|
|
6076
|
+
"depth",
|
|
6077
|
+
"workflowId",
|
|
6078
|
+
// LUA-696 (review 2): the `ctx.once` key of an `effect_in_doubt` park — the step site stamps it here (scrubbed)
|
|
6079
|
+
// beside `park.effectKey`; a key is user text and leaves scrubbed like every other string leaf.
|
|
6080
|
+
"effectKey"
|
|
6081
|
+
];
|
|
6082
|
+
STEP_ERROR_DETAIL_MAX_BYTES = 8 * 1024;
|
|
6083
|
+
DETAIL_MAX_DEPTH = 4;
|
|
6084
|
+
DETAIL_MAX_ITEMS = 100;
|
|
6085
|
+
__name(scrubDetailValue, "scrubDetailValue");
|
|
6086
|
+
__name3(scrubDetailValue, "scrubDetailValue");
|
|
6087
|
+
__name(stepErrorDetail, "stepErrorDetail");
|
|
6088
|
+
__name3(stepErrorDetail, "stepErrorDetail");
|
|
5179
6089
|
MAX_HOLIDAYS = 366;
|
|
5180
6090
|
MAX_WALK_DAYS = 400;
|
|
5181
6091
|
HHMM = /^([01]\d|2[0-3]):([0-5]\d)$/;
|
|
@@ -5321,6 +6231,18 @@ var init_dist2 = __esm({
|
|
|
5321
6231
|
EscalationHopSchema
|
|
5322
6232
|
])).min(1).max(ESCALATION_MAX_HOPS + 1)
|
|
5323
6233
|
]);
|
|
6234
|
+
APPROVER_SPEC_SHAPES = [
|
|
6235
|
+
"'creator'",
|
|
6236
|
+
"'org-admins'",
|
|
6237
|
+
"{users:[userId, \u2026]}",
|
|
6238
|
+
"{role:roleName}",
|
|
6239
|
+
"{group:groupName}",
|
|
6240
|
+
"{governance:{policyId}}"
|
|
6241
|
+
];
|
|
6242
|
+
APPROVER_WRITTEN_MAX = 120;
|
|
6243
|
+
USER_ID_SHAPED_RE = /^[^\s@]{1,128}$/;
|
|
6244
|
+
__name(describeApproverSpecRefusal, "describeApproverSpecRefusal");
|
|
6245
|
+
__name3(describeApproverSpecRefusal, "describeApproverSpecRefusal");
|
|
5324
6246
|
BINDING_ROOTS = [
|
|
5325
6247
|
"initData",
|
|
5326
6248
|
"stepResults",
|
|
@@ -5476,7 +6398,12 @@ function materializeEntry(entry, steps) {
|
|
|
5476
6398
|
}
|
|
5477
6399
|
}
|
|
5478
6400
|
function graphHasHitl(graph, steps) {
|
|
5479
|
-
|
|
6401
|
+
for (const e of graph) {
|
|
6402
|
+
if (isHitlEntry(e)) return true;
|
|
6403
|
+
if (e.type === "parallel" && e.steps.some(isHitlEntry)) return true;
|
|
6404
|
+
if (e.type === "conditional" && (e.steps.some(isHitlEntry) || isHitlEntry(e.otherwise))) return true;
|
|
6405
|
+
if ((e.type === "foreach" || e.type === "loop") && isHitlEntry(e.step)) return true;
|
|
6406
|
+
}
|
|
5480
6407
|
return Object.values(steps).some((s) => s.suspendSchema !== void 0);
|
|
5481
6408
|
}
|
|
5482
6409
|
function createWorkflow(cfg) {
|
|
@@ -5510,10 +6437,11 @@ function defineWorkflow(cfg, build) {
|
|
|
5510
6437
|
if (!(wf instanceof LuaWorkflow)) throw new LuaWorkflowBuildError("invalid-envelope", "defineWorkflow: the build callback must return `wf\u2026.commit()`");
|
|
5511
6438
|
return wf;
|
|
5512
6439
|
}
|
|
5513
|
-
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;
|
|
6440
|
+
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;
|
|
5514
6441
|
var init_workflow = __esm({
|
|
5515
6442
|
"src/types/workflow.ts"() {
|
|
5516
6443
|
"use strict";
|
|
6444
|
+
init_dist();
|
|
5517
6445
|
init_dist2();
|
|
5518
6446
|
__name(createStep, "createStep");
|
|
5519
6447
|
__name(step2, "step");
|
|
@@ -5596,7 +6524,8 @@ var init_workflow = __esm({
|
|
|
5596
6524
|
}, "assertPredicate");
|
|
5597
6525
|
assertRetry = /* @__PURE__ */ __name((r, id) => {
|
|
5598
6526
|
if (!r) return;
|
|
5599
|
-
if (
|
|
6527
|
+
if (typeof r.maxAttempts === "number" && r.maxAttempts > WORKFLOW_RETRY_MAX_ATTEMPTS) throw new LuaWorkflowBuildError("cap-exceeded", `"${id}": ${workflowRetryMaxAttemptsMessage(r.maxAttempts)}`);
|
|
6528
|
+
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(" | ")}`);
|
|
5600
6529
|
if (r.maxBackoffSeconds !== void 0) {
|
|
5601
6530
|
if (r.backoff !== "exponential") throw new LuaWorkflowBuildError("backoff-invalid", `"${id}": retry.maxBackoffSeconds is only meaningful with backoff:'exponential'`);
|
|
5602
6531
|
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`);
|
|
@@ -5718,6 +6647,7 @@ var init_workflow = __esm({
|
|
|
5718
6647
|
};
|
|
5719
6648
|
__name(stepNodeOf, "stepNodeOf");
|
|
5720
6649
|
__name(materializeEntry, "materializeEntry");
|
|
6650
|
+
isHitlEntry = /* @__PURE__ */ __name((n2) => isWorkflowHitlEntryType(n2?.type), "isHitlEntry");
|
|
5721
6651
|
__name(graphHasHitl, "graphHasHitl");
|
|
5722
6652
|
WorkflowBuilderImpl = class WorkflowBuilderImpl2 {
|
|
5723
6653
|
static {
|
|
@@ -5770,7 +6700,6 @@ var init_workflow = __esm({
|
|
|
5770
6700
|
const [mapping, target] = arm;
|
|
5771
6701
|
if (target === void 0 || arm.length < 2) throw new LuaWorkflowBuildError("container-arm-empty", `${where}: a bare mapping arm has nothing to run`);
|
|
5772
6702
|
if (!mapping || typeof mapping !== "object" || Array.isArray(mapping)) throw new LuaWorkflowBuildError("mapping-placement", `${where}: the arm head must be a map config object`);
|
|
5773
|
-
if (mapping.type === "approval" || target.type === "approval") throw new LuaWorkflowBuildError("approval-inside-container", `${where}: approval / waitForSignal are top-level only in v1`);
|
|
5774
6703
|
assertNoClosure(mapping, `${where} arm map`);
|
|
5775
6704
|
this.recordEnvRefs(mapping);
|
|
5776
6705
|
const inner = this.armRef(target, where);
|
|
@@ -5788,8 +6717,9 @@ var init_workflow = __esm({
|
|
|
5788
6717
|
if (typeof arm === "string") return {
|
|
5789
6718
|
ref: arm
|
|
5790
6719
|
};
|
|
5791
|
-
if (arm && typeof arm === "object" && arm
|
|
5792
|
-
|
|
6720
|
+
if (arm && typeof arm === "object" && isHitlEntry(arm)) {
|
|
6721
|
+
const id = arm.id;
|
|
6722
|
+
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`);
|
|
5793
6723
|
}
|
|
5794
6724
|
return {
|
|
5795
6725
|
node: this.registerStep(arm, where)
|
|
@@ -5990,17 +6920,13 @@ var init_workflow = __esm({
|
|
|
5990
6920
|
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'`);
|
|
5991
6921
|
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`);
|
|
5992
6922
|
if (opts.toolScope?.jobTools && tier !== "job") throw new LuaWorkflowBuildError("cap-exceeded", `"${id}": toolScope.jobTools require tier:'job' (job-tools-require-job-tier)`);
|
|
5993
|
-
|
|
5994
|
-
if (
|
|
5995
|
-
if (
|
|
5996
|
-
|
|
5997
|
-
|
|
5998
|
-
|
|
5999
|
-
|
|
6000
|
-
}
|
|
6001
|
-
if (opts.maxInputTokens !== void 0) {
|
|
6002
|
-
if (tier !== "job") throw new LuaWorkflowBuildError("max-turns-requires-job-tier", `"${id}": maxInputTokens is only legal on a tier:'job' agent step`);
|
|
6003
|
-
if (!Number.isInteger(opts.maxInputTokens) || opts.maxInputTokens < 1e6 || opts.maxInputTokens > 5e8) throw new LuaWorkflowBuildError("max-turns-invalid", `"${id}": maxInputTokens must be an integer 1000000..500000000`);
|
|
6923
|
+
for (const m of WORKFLOW_JOB_RANGE_MEMBERS) {
|
|
6924
|
+
if (opts[m] === void 0) continue;
|
|
6925
|
+
if (tier !== "job") throw new LuaWorkflowBuildError("max-turns-requires-job-tier", `"${id}": ${m} is only legal on a tier:'job' agent step`);
|
|
6926
|
+
if (!isWithinWorkflowJobRange(m, opts[m])) {
|
|
6927
|
+
const { min, max } = WORKFLOW_JOB_RANGES[m];
|
|
6928
|
+
throw new LuaWorkflowBuildError("max-turns-invalid", `"${id}": ${m} must be an integer ${min}..${max}`);
|
|
6929
|
+
}
|
|
6004
6930
|
}
|
|
6005
6931
|
assertTimeout({
|
|
6006
6932
|
id,
|
|
@@ -6152,8 +7078,8 @@ var init_workflow = __esm({
|
|
|
6152
7078
|
itemTimeout: opts.itemTimeout
|
|
6153
7079
|
});
|
|
6154
7080
|
return this.push({
|
|
6155
|
-
kind: "
|
|
6156
|
-
|
|
7081
|
+
kind: "declare",
|
|
7082
|
+
node
|
|
6157
7083
|
});
|
|
6158
7084
|
}
|
|
6159
7085
|
waitForSignal(id, opts) {
|
|
@@ -6171,12 +7097,13 @@ var init_workflow = __esm({
|
|
|
6171
7097
|
acceptedSources: opts.acceptedSources
|
|
6172
7098
|
});
|
|
6173
7099
|
return this.push({
|
|
6174
|
-
kind: "
|
|
6175
|
-
|
|
7100
|
+
kind: "declare",
|
|
7101
|
+
node
|
|
6176
7102
|
});
|
|
6177
7103
|
}
|
|
6178
7104
|
workflow(id, ref, input, opts) {
|
|
6179
7105
|
this.assertId(id, "workflow()");
|
|
7106
|
+
assertRetry(opts?.retry, id);
|
|
6180
7107
|
const name = typeof ref === "string" ? ref : ref instanceof LuaWorkflow ? ref.getName() : void 0;
|
|
6181
7108
|
if (!name) throw new LuaWorkflowBuildError("invalid-envelope", `workflow("${id}") needs a LuaWorkflow or a workflow name`);
|
|
6182
7109
|
if (opts?.workspace === "inherit") {
|
|
@@ -6198,7 +7125,8 @@ var init_workflow = __esm({
|
|
|
6198
7125
|
id,
|
|
6199
7126
|
workflowId: name,
|
|
6200
7127
|
input,
|
|
6201
|
-
workspace: opts?.workspace
|
|
7128
|
+
workspace: opts?.workspace,
|
|
7129
|
+
retry: opts?.retry
|
|
6202
7130
|
});
|
|
6203
7131
|
return this.push({
|
|
6204
7132
|
kind: "declare",
|
|
@@ -6212,7 +7140,7 @@ var init_workflow = __esm({
|
|
|
6212
7140
|
const { graph, issues } = resolvePlacements(this.calls);
|
|
6213
7141
|
const fatal = issues[0];
|
|
6214
7142
|
if (fatal) {
|
|
6215
|
-
const hint = fatal.code === "unknown-step-ref" ? "a string StepRef must name an entry declared by agentStep/specialistStep/toolStep/map(\u2026, { id })/workflow(\u2026) somewhere in the chain \u2014 before OR after the reference" : void 0;
|
|
7143
|
+
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;
|
|
6216
7144
|
throw new LuaWorkflowBuildError(fatal.code, fatal.message, hint);
|
|
6217
7145
|
}
|
|
6218
7146
|
if (graph.length === 0) throw new LuaWorkflowBuildError("empty-graph", `workflow "${this.config.name}" has no entries`);
|
|
@@ -6366,6 +7294,74 @@ var init_auth_error = __esm({
|
|
|
6366
7294
|
}
|
|
6367
7295
|
});
|
|
6368
7296
|
|
|
7297
|
+
// src/errors/cli.error.ts
|
|
7298
|
+
function isAccessDeniedError(error) {
|
|
7299
|
+
if (CliError.isCliError(error)) return error.statusCode === 403;
|
|
7300
|
+
return error instanceof Error && error.message.startsWith("Access denied (403)");
|
|
7301
|
+
}
|
|
7302
|
+
var CLI_EXIT, CliError;
|
|
7303
|
+
var init_cli_error = __esm({
|
|
7304
|
+
"src/errors/cli.error.ts"() {
|
|
7305
|
+
"use strict";
|
|
7306
|
+
init_auth_error();
|
|
7307
|
+
CLI_EXIT = {
|
|
7308
|
+
OK: 0,
|
|
7309
|
+
ERROR: 1,
|
|
7310
|
+
USAGE: 2,
|
|
7311
|
+
NOT_FOUND: 3,
|
|
7312
|
+
AUTH: 9,
|
|
7313
|
+
FORBIDDEN: 10,
|
|
7314
|
+
UNAVAILABLE: 11
|
|
7315
|
+
};
|
|
7316
|
+
CliError = class _CliError extends Error {
|
|
7317
|
+
static {
|
|
7318
|
+
__name(this, "CliError");
|
|
7319
|
+
}
|
|
7320
|
+
isCliError = true;
|
|
7321
|
+
code;
|
|
7322
|
+
exitCode;
|
|
7323
|
+
hint;
|
|
7324
|
+
statusCode;
|
|
7325
|
+
constructor(code, message, options = {}) {
|
|
7326
|
+
super(message);
|
|
7327
|
+
this.name = "CliError";
|
|
7328
|
+
this.code = code;
|
|
7329
|
+
this.exitCode = options.exitCode ?? CLI_EXIT.ERROR;
|
|
7330
|
+
this.hint = options.hint;
|
|
7331
|
+
this.statusCode = options.statusCode;
|
|
7332
|
+
if (Error.captureStackTrace) Error.captureStackTrace(this, _CliError);
|
|
7333
|
+
}
|
|
7334
|
+
/** Bad arguments, an unknown action, no project — exit 2. */
|
|
7335
|
+
static usage(message, hint) {
|
|
7336
|
+
return new _CliError("usage", message, {
|
|
7337
|
+
exitCode: CLI_EXIT.USAGE,
|
|
7338
|
+
hint
|
|
7339
|
+
});
|
|
7340
|
+
}
|
|
7341
|
+
/** The named thing does not exist — exit 3. */
|
|
7342
|
+
static notFound(message, hint) {
|
|
7343
|
+
return new _CliError("not_found", message, {
|
|
7344
|
+
exitCode: CLI_EXIT.NOT_FOUND,
|
|
7345
|
+
hint,
|
|
7346
|
+
statusCode: 404
|
|
7347
|
+
});
|
|
7348
|
+
}
|
|
7349
|
+
/** The credential may not do this — exit 10. */
|
|
7350
|
+
static forbidden(message, hint) {
|
|
7351
|
+
return new _CliError("forbidden", message, {
|
|
7352
|
+
exitCode: CLI_EXIT.FORBIDDEN,
|
|
7353
|
+
hint,
|
|
7354
|
+
statusCode: 403
|
|
7355
|
+
});
|
|
7356
|
+
}
|
|
7357
|
+
static isCliError(error) {
|
|
7358
|
+
return error instanceof _CliError || typeof error === "object" && error !== null && error.isCliError === true;
|
|
7359
|
+
}
|
|
7360
|
+
};
|
|
7361
|
+
__name(isAccessDeniedError, "isAccessDeniedError");
|
|
7362
|
+
}
|
|
7363
|
+
});
|
|
7364
|
+
|
|
6369
7365
|
// src/utils/package-root.ts
|
|
6370
7366
|
import { readFileSync, existsSync } from "fs";
|
|
6371
7367
|
import { fileURLToPath, pathToFileURL } from "url";
|
|
@@ -6610,7 +7606,7 @@ var init_firebase_session_store = __esm({
|
|
|
6610
7606
|
}), "currentFirebaseSessionEnvironment");
|
|
6611
7607
|
__name(environmentKey, "environmentKey");
|
|
6612
7608
|
__name(isMissing, "isMissing");
|
|
6613
|
-
wait = /* @__PURE__ */ __name((milliseconds) => new Promise((
|
|
7609
|
+
wait = /* @__PURE__ */ __name((milliseconds) => new Promise((resolve3) => setTimeout(resolve3, milliseconds)), "wait");
|
|
6614
7610
|
FirebaseSessionStore = class {
|
|
6615
7611
|
static {
|
|
6616
7612
|
__name(this, "FirebaseSessionStore");
|
|
@@ -6878,13 +7874,13 @@ async function* parseSseStream(body, signal) {
|
|
|
6878
7874
|
buffer += decoder.decode(value3, {
|
|
6879
7875
|
stream: true
|
|
6880
7876
|
});
|
|
6881
|
-
let
|
|
6882
|
-
while (
|
|
6883
|
-
const block = buffer.slice(0,
|
|
6884
|
-
buffer = buffer.slice(
|
|
7877
|
+
let sep4 = buffer.search(/\r?\n\r?\n/);
|
|
7878
|
+
while (sep4 !== -1) {
|
|
7879
|
+
const block = buffer.slice(0, sep4);
|
|
7880
|
+
buffer = buffer.slice(sep4).replace(/^\r?\n\r?\n/, "");
|
|
6885
7881
|
const frame = flush(block);
|
|
6886
7882
|
if (frame) yield frame;
|
|
6887
|
-
|
|
7883
|
+
sep4 = buffer.search(/\r?\n\r?\n/);
|
|
6888
7884
|
}
|
|
6889
7885
|
}
|
|
6890
7886
|
if (buffer.trim()) {
|
|
@@ -6892,6 +7888,10 @@ async function* parseSseStream(body, signal) {
|
|
|
6892
7888
|
if (frame) yield frame;
|
|
6893
7889
|
}
|
|
6894
7890
|
} finally {
|
|
7891
|
+
try {
|
|
7892
|
+
await reader.cancel();
|
|
7893
|
+
} catch {
|
|
7894
|
+
}
|
|
6895
7895
|
try {
|
|
6896
7896
|
reader.releaseLock();
|
|
6897
7897
|
} catch {
|
|
@@ -6921,6 +7921,7 @@ var init_http_client = __esm({
|
|
|
6921
7921
|
"use strict";
|
|
6922
7922
|
init_dist();
|
|
6923
7923
|
init_auth_error();
|
|
7924
|
+
init_cli_error();
|
|
6924
7925
|
init_lua_fetch();
|
|
6925
7926
|
init_request_credential();
|
|
6926
7927
|
DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
|
|
@@ -6984,7 +7985,7 @@ var init_http_client = __esm({
|
|
|
6984
7985
|
if (AuthenticationError.isAuthenticationError(error)) {
|
|
6985
7986
|
throw error;
|
|
6986
7987
|
}
|
|
6987
|
-
if (error
|
|
7988
|
+
if (isAccessDeniedError(error)) {
|
|
6988
7989
|
throw error;
|
|
6989
7990
|
}
|
|
6990
7991
|
if (error instanceof DOMException && error.name === "AbortError") {
|
|
@@ -7032,8 +8033,11 @@ var init_http_client = __esm({
|
|
|
7032
8033
|
}
|
|
7033
8034
|
if (response.status === 403) {
|
|
7034
8035
|
const detail = errorData.message || "You do not have permission to access this resource.";
|
|
7035
|
-
throw new
|
|
7036
|
-
|
|
8036
|
+
throw new CliError("forbidden", `Access denied (403): ${detail}`, {
|
|
8037
|
+
exitCode: CLI_EXIT.FORBIDDEN,
|
|
8038
|
+
statusCode: 403,
|
|
8039
|
+
hint: "Check that your Lua login has access to this agent or organization."
|
|
8040
|
+
});
|
|
7037
8041
|
}
|
|
7038
8042
|
return {
|
|
7039
8043
|
success: false,
|
|
@@ -7103,7 +8107,7 @@ Check that your Lua login has access to this agent or organization.`);
|
|
|
7103
8107
|
if (attempt < maxRetries) {
|
|
7104
8108
|
const serverDelay = Number(lastResult?.error?.retryAfterSeconds ?? 0) * 1e3;
|
|
7105
8109
|
const backoff = Math.max(this.calculateBackoff(attempt), serverDelay);
|
|
7106
|
-
await new Promise((
|
|
8110
|
+
await new Promise((resolve3) => setTimeout(resolve3, backoff));
|
|
7107
8111
|
}
|
|
7108
8112
|
}
|
|
7109
8113
|
return lastResult;
|
|
@@ -7152,7 +8156,7 @@ Check that your Lua login has access to this agent or organization.`);
|
|
|
7152
8156
|
async httpPostCoreDrainRetry(url, data, headers) {
|
|
7153
8157
|
const first = await this.httpPostOnce(url, data, headers);
|
|
7154
8158
|
if (!isCoreDrainApiError(first.error)) return first;
|
|
7155
|
-
await new Promise((
|
|
8159
|
+
await new Promise((resolve3) => setTimeout(resolve3, coreDrainApiRetryDelayMs(first.error)));
|
|
7156
8160
|
return this.httpPostOnce(url, data, headers);
|
|
7157
8161
|
}
|
|
7158
8162
|
/**
|
|
@@ -7275,6 +8279,7 @@ var init_auth = __esm({
|
|
|
7275
8279
|
init_auth_api_service();
|
|
7276
8280
|
init_constants();
|
|
7277
8281
|
init_auth_error();
|
|
8282
|
+
init_cli_error();
|
|
7278
8283
|
}
|
|
7279
8284
|
});
|
|
7280
8285
|
|
|
@@ -7502,6 +8507,9 @@ function buildSourceArchive(files) {
|
|
|
7502
8507
|
const gz = zlib.gzipSync(Buffer.from(json, "utf-8"));
|
|
7503
8508
|
return gz.toString("base64");
|
|
7504
8509
|
}
|
|
8510
|
+
function getPrimitivesByKind(manifest, kind) {
|
|
8511
|
+
return manifest.primitives.filter((p) => p.kind === kind);
|
|
8512
|
+
}
|
|
7505
8513
|
function findPrimitive(manifest, name, kind) {
|
|
7506
8514
|
return manifest.primitives.find((p) => {
|
|
7507
8515
|
if (kind && p.kind !== kind) return false;
|
|
@@ -7522,15 +8530,565 @@ var init_artifact_loader = __esm({
|
|
|
7522
8530
|
__name(loadOriginalSource, "loadOriginalSource");
|
|
7523
8531
|
__name(normalizeEntryFile, "normalizeEntryFile");
|
|
7524
8532
|
__name(buildSourceArchive, "buildSourceArchive");
|
|
8533
|
+
__name(getPrimitivesByKind, "getPrimitivesByKind");
|
|
7525
8534
|
__name(findPrimitive, "findPrimitive");
|
|
7526
8535
|
}
|
|
7527
8536
|
});
|
|
7528
8537
|
|
|
8538
|
+
// ../shared-source-sync/dist/index.mjs
|
|
8539
|
+
import { createHash as createHash4 } from "crypto";
|
|
8540
|
+
import { extname } from "path";
|
|
8541
|
+
import { readdirSync, readFileSync as readFileSync3, statSync } from "fs";
|
|
8542
|
+
import { join as join4, sep } from "path";
|
|
8543
|
+
import { gunzipSync, gzipSync } from "zlib";
|
|
8544
|
+
import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync22, statSync as statSync2, writeFileSync } from "fs";
|
|
8545
|
+
import { dirname as dirname2, join as join22, resolve, sep as sep2 } from "path";
|
|
8546
|
+
import { posix } from "path";
|
|
8547
|
+
import { mkdirSync as mkdirSync2, readdirSync as readdirSync2, readFileSync as readFileSync32, statSync as statSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
8548
|
+
import { dirname as dirname22, join as join32, resolve as resolve2, sep as sep3 } from "path";
|
|
8549
|
+
import { gunzipSync as gunzipSync2, gzipSync as gzipSync2 } from "zlib";
|
|
8550
|
+
function hashContentTruncated(content) {
|
|
8551
|
+
return createHash4("sha256").update(content).digest("hex").slice(0, FILE_HASH_LENGTH);
|
|
8552
|
+
}
|
|
8553
|
+
function sha256Hex(content) {
|
|
8554
|
+
return createHash4("sha256").update(content).digest("hex");
|
|
8555
|
+
}
|
|
8556
|
+
function matchesFileHash(hash, plaintext) {
|
|
8557
|
+
if (!/^(?:[a-f0-9]{16}|[a-f0-9]{64})$/.test(hash)) return false;
|
|
8558
|
+
if (sha256Hex(plaintext).startsWith(hash)) return true;
|
|
8559
|
+
return sha256Hex(plaintext.toString("utf-8")).startsWith(hash);
|
|
8560
|
+
}
|
|
8561
|
+
function combineFileHashes(files) {
|
|
8562
|
+
const sorted = [
|
|
8563
|
+
...files
|
|
8564
|
+
].sort((a, b) => a.relativePath.localeCompare(b.relativePath));
|
|
8565
|
+
return sha256Hex(sorted.map((f) => f.hash).join("|"));
|
|
8566
|
+
}
|
|
8567
|
+
function shouldSkipDirectory(name) {
|
|
8568
|
+
return SKIP_DIRECTORIES.has(name);
|
|
8569
|
+
}
|
|
8570
|
+
function shouldSkipFile(filePathOrName) {
|
|
8571
|
+
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";
|
|
8572
|
+
}
|
|
8573
|
+
function classifyFile(relPath) {
|
|
8574
|
+
return KIND_BY_EXT[extname(relPath)] ?? "other";
|
|
8575
|
+
}
|
|
8576
|
+
function walkWorkspace(rootDir, opts = {}) {
|
|
8577
|
+
const maxBytes = opts.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES;
|
|
8578
|
+
const refs = [];
|
|
8579
|
+
const contentByHash = /* @__PURE__ */ new Map();
|
|
8580
|
+
let totalSize = 0;
|
|
8581
|
+
const visit = /* @__PURE__ */ __name4((relPrefix) => {
|
|
8582
|
+
const absDir = relPrefix ? join4(rootDir, relPrefix) : rootDir;
|
|
8583
|
+
let entries;
|
|
8584
|
+
try {
|
|
8585
|
+
entries = readdirSync(absDir, {
|
|
8586
|
+
withFileTypes: true
|
|
8587
|
+
});
|
|
8588
|
+
} catch {
|
|
8589
|
+
return;
|
|
8590
|
+
}
|
|
8591
|
+
for (const entry of entries) {
|
|
8592
|
+
if (entry.isDirectory()) {
|
|
8593
|
+
if (shouldSkipDirectory(entry.name)) continue;
|
|
8594
|
+
if (shouldSkipFile(entry.name)) continue;
|
|
8595
|
+
visit(relPrefix ? `${relPrefix}${sep}${entry.name}` : entry.name);
|
|
8596
|
+
continue;
|
|
8597
|
+
}
|
|
8598
|
+
if (!entry.isFile()) continue;
|
|
8599
|
+
if (shouldSkipFile(entry.name)) continue;
|
|
8600
|
+
const rel = (relPrefix ? `${relPrefix}${sep}${entry.name}` : entry.name).split(sep).join("/");
|
|
8601
|
+
const abs = join4(rootDir, rel);
|
|
8602
|
+
let stats;
|
|
8603
|
+
try {
|
|
8604
|
+
stats = statSync(abs);
|
|
8605
|
+
} catch {
|
|
8606
|
+
continue;
|
|
8607
|
+
}
|
|
8608
|
+
if (stats.size > maxBytes) continue;
|
|
8609
|
+
let content;
|
|
8610
|
+
try {
|
|
8611
|
+
content = readFileSync3(abs);
|
|
8612
|
+
} catch {
|
|
8613
|
+
continue;
|
|
8614
|
+
}
|
|
8615
|
+
const hash = hashContentTruncated(content.toString("utf-8"));
|
|
8616
|
+
refs.push({
|
|
8617
|
+
relativePath: rel,
|
|
8618
|
+
hash,
|
|
8619
|
+
size: stats.size,
|
|
8620
|
+
type: classifyFile(rel)
|
|
8621
|
+
});
|
|
8622
|
+
if (!contentByHash.has(hash)) contentByHash.set(hash, content);
|
|
8623
|
+
totalSize += stats.size;
|
|
8624
|
+
}
|
|
8625
|
+
}, "visit");
|
|
8626
|
+
visit("");
|
|
8627
|
+
refs.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
|
|
8628
|
+
const projectHash = combineFileHashes(refs);
|
|
8629
|
+
return {
|
|
8630
|
+
files: refs,
|
|
8631
|
+
projectHash,
|
|
8632
|
+
totalSize,
|
|
8633
|
+
contentByHash
|
|
8634
|
+
};
|
|
8635
|
+
}
|
|
8636
|
+
async function uploadBlobs(opts) {
|
|
8637
|
+
const fetchFn = opts.fetch ?? fetch;
|
|
8638
|
+
const concurrency = opts.concurrency ?? DEFAULT_CONCURRENCY;
|
|
8639
|
+
const hashes = Object.keys(opts.uploadUrls);
|
|
8640
|
+
for (let i = 0; i < hashes.length; i += concurrency) {
|
|
8641
|
+
const batch = hashes.slice(i, i + concurrency);
|
|
8642
|
+
await Promise.all(batch.map(async (hash) => {
|
|
8643
|
+
const buf = opts.contentByHash.get(hash);
|
|
8644
|
+
if (!buf) {
|
|
8645
|
+
throw new Error(`uploadBlobs: no content for hash ${hash.slice(0, 12)}\u2026`);
|
|
8646
|
+
}
|
|
8647
|
+
const compressed = gzipSync(buf);
|
|
8648
|
+
const res = await fetchFn(opts.uploadUrls[hash], {
|
|
8649
|
+
method: "PUT",
|
|
8650
|
+
body: compressed,
|
|
8651
|
+
headers: {
|
|
8652
|
+
"Content-Type": "application/octet-stream"
|
|
8653
|
+
}
|
|
8654
|
+
});
|
|
8655
|
+
if (!res.ok) {
|
|
8656
|
+
throw new Error(`S3 upload failed for ${hash.slice(0, 12)}\u2026: ${res.status} ${res.statusText}`);
|
|
8657
|
+
}
|
|
8658
|
+
}));
|
|
8659
|
+
}
|
|
8660
|
+
return hashes.length;
|
|
8661
|
+
}
|
|
8662
|
+
function decodeBlob(buf) {
|
|
8663
|
+
if (buf.length >= 2 && buf[0] === 31 && buf[1] === 139) {
|
|
8664
|
+
try {
|
|
8665
|
+
return gunzipSync(buf);
|
|
8666
|
+
} catch {
|
|
8667
|
+
return buf;
|
|
8668
|
+
}
|
|
8669
|
+
}
|
|
8670
|
+
return buf;
|
|
8671
|
+
}
|
|
8672
|
+
function verifyDownloaded(hash, plaintext) {
|
|
8673
|
+
if (!matchesFileHash(hash, plaintext)) {
|
|
8674
|
+
throw new Error(`Blob ${hash.slice(0, 12)}\u2026 failed integrity verification: its content does not hash to its key (refusing to restore it)`);
|
|
8675
|
+
}
|
|
8676
|
+
return plaintext;
|
|
8677
|
+
}
|
|
8678
|
+
async function downloadBlobs(opts) {
|
|
8679
|
+
const fetchFn = opts.fetch ?? fetch;
|
|
8680
|
+
const concurrency = opts.concurrency ?? DEFAULT_CONCURRENCY;
|
|
8681
|
+
const out = /* @__PURE__ */ new Map();
|
|
8682
|
+
const entries = Object.entries(opts.urls);
|
|
8683
|
+
for (let i = 0; i < entries.length; i += concurrency) {
|
|
8684
|
+
const batch = entries.slice(i, i + concurrency);
|
|
8685
|
+
const downloaded = await Promise.all(batch.map(async ([hash, url]) => {
|
|
8686
|
+
const res = await fetchFn(url);
|
|
8687
|
+
if (!res.ok) {
|
|
8688
|
+
throw new Error(`S3 download failed for ${hash.slice(0, 12)}\u2026: ${res.status} ${res.statusText}`);
|
|
8689
|
+
}
|
|
8690
|
+
const ab = await res.arrayBuffer();
|
|
8691
|
+
const buf = Buffer.from(ab);
|
|
8692
|
+
return [
|
|
8693
|
+
hash,
|
|
8694
|
+
verifyDownloaded(hash, decodeBlob(buf))
|
|
8695
|
+
];
|
|
8696
|
+
}));
|
|
8697
|
+
for (const [hash, buf] of downloaded) out.set(hash, buf);
|
|
8698
|
+
}
|
|
8699
|
+
return out;
|
|
8700
|
+
}
|
|
8701
|
+
function resolveBackupFileTarget(file, targetDir) {
|
|
8702
|
+
const base = resolve(targetDir);
|
|
8703
|
+
const sandboxed = file.external ? join22(base, ".lua", "external", file.relativePath) : join22(base, file.relativePath);
|
|
8704
|
+
const resolved = resolve(sandboxed);
|
|
8705
|
+
if (resolved !== base && !resolved.startsWith(base + sep2)) {
|
|
8706
|
+
throw new Error(`Backup entry escapes target directory: ${file.relativePath}`);
|
|
8707
|
+
}
|
|
8708
|
+
return resolved;
|
|
8709
|
+
}
|
|
8710
|
+
function restoreFromBlobs(manifest, blobs, targetDir, opts = {}) {
|
|
8711
|
+
let filesWritten = 0;
|
|
8712
|
+
let filesUnchanged = 0;
|
|
8713
|
+
let filesSkipped = 0;
|
|
8714
|
+
for (const file of manifest.files) {
|
|
8715
|
+
const content = blobs.get(file.hash);
|
|
8716
|
+
if (!content) {
|
|
8717
|
+
throw new Error(`Blob not found for hash: ${file.hash} (${file.relativePath})`);
|
|
8718
|
+
}
|
|
8719
|
+
const targetPath = resolveBackupFileTarget(file, targetDir);
|
|
8720
|
+
if (existsSync2(targetPath)) {
|
|
8721
|
+
const sameSize = statSync2(targetPath).size === content.length;
|
|
8722
|
+
if (sameSize && readFileSync22(targetPath).equals(content)) {
|
|
8723
|
+
filesUnchanged++;
|
|
8724
|
+
continue;
|
|
8725
|
+
}
|
|
8726
|
+
if (!opts.overwrite) {
|
|
8727
|
+
filesSkipped++;
|
|
8728
|
+
continue;
|
|
8729
|
+
}
|
|
8730
|
+
}
|
|
8731
|
+
mkdirSync(dirname2(targetPath), {
|
|
8732
|
+
recursive: true
|
|
8733
|
+
});
|
|
8734
|
+
writeFileSync(targetPath, content);
|
|
8735
|
+
filesWritten++;
|
|
8736
|
+
}
|
|
8737
|
+
return {
|
|
8738
|
+
filesWritten,
|
|
8739
|
+
filesUnchanged,
|
|
8740
|
+
filesSkipped
|
|
8741
|
+
};
|
|
8742
|
+
}
|
|
8743
|
+
function normalizeWorkspaceRelativePath(value3) {
|
|
8744
|
+
if (!value3 || value3.includes("\0")) return void 0;
|
|
8745
|
+
const withPortableSeparators = value3.replace(/\\/g, "/");
|
|
8746
|
+
if (withPortableSeparators.startsWith("/") || WINDOWS_ABSOLUTE_PATH.test(withPortableSeparators)) return void 0;
|
|
8747
|
+
const normalized = posix.normalize(withPortableSeparators);
|
|
8748
|
+
if (!normalized || normalized === "." || normalized === ".." || normalized.startsWith("../")) return void 0;
|
|
8749
|
+
return normalized;
|
|
8750
|
+
}
|
|
8751
|
+
function isCredentialPersistencePath(value3) {
|
|
8752
|
+
const normalized = normalizeWorkspaceRelativePath(value3);
|
|
8753
|
+
return normalized !== void 0 && CREDENTIAL_PATHS.has(normalized.toLowerCase());
|
|
8754
|
+
}
|
|
8755
|
+
async function pushAgentBackup(opts) {
|
|
8756
|
+
const snapshot = walkWorkspace(opts.workspaceDir, opts.walkOptions);
|
|
8757
|
+
const client = new BackupHttpClient(opts.http);
|
|
8758
|
+
const uniqueHashes = [
|
|
8759
|
+
...new Set(snapshot.files.map((f) => f.hash))
|
|
8760
|
+
];
|
|
8761
|
+
let filesUploaded = 0;
|
|
8762
|
+
if (uniqueHashes.length > 0) {
|
|
8763
|
+
const checked = await client.checkBlobsExist(uniqueHashes);
|
|
8764
|
+
if (checked.missing.length > 0) {
|
|
8765
|
+
const urls = await client.getBlobUploadUrls(checked.missing);
|
|
8766
|
+
filesUploaded = await uploadBlobs({
|
|
8767
|
+
uploadUrls: urls.urls,
|
|
8768
|
+
contentByHash: snapshot.contentByHash,
|
|
8769
|
+
fetch: opts.http.fetch,
|
|
8770
|
+
concurrency: opts.concurrency
|
|
8771
|
+
});
|
|
8772
|
+
}
|
|
8773
|
+
}
|
|
8774
|
+
const metadata = await client.saveManifest({
|
|
8775
|
+
projectHash: snapshot.projectHash,
|
|
8776
|
+
files: snapshot.files,
|
|
8777
|
+
version: opts.version,
|
|
8778
|
+
orgId: opts.orgId,
|
|
8779
|
+
createdBy: opts.createdBy ?? "cli",
|
|
8780
|
+
triggeredBy: opts.triggeredBy
|
|
8781
|
+
});
|
|
8782
|
+
return {
|
|
8783
|
+
projectHash: snapshot.projectHash,
|
|
8784
|
+
fileCount: snapshot.files.length,
|
|
8785
|
+
filesUploaded,
|
|
8786
|
+
activeVersion: metadata.activeVersion,
|
|
8787
|
+
metadata,
|
|
8788
|
+
files: snapshot.files
|
|
8789
|
+
};
|
|
8790
|
+
}
|
|
8791
|
+
async function pullAgentBackup(opts) {
|
|
8792
|
+
const client = new BackupHttpClient(opts.http);
|
|
8793
|
+
const manifest = await client.getManifest();
|
|
8794
|
+
const uniqueHashes = [
|
|
8795
|
+
...new Set(manifest.files.map((f) => f.hash))
|
|
8796
|
+
];
|
|
8797
|
+
const blobs = uniqueHashes.length ? await (async () => {
|
|
8798
|
+
const urls = await client.getBlobUrls(uniqueHashes);
|
|
8799
|
+
return downloadBlobs({
|
|
8800
|
+
urls: urls.urls,
|
|
8801
|
+
fetch: opts.http.fetch,
|
|
8802
|
+
concurrency: opts.concurrency
|
|
8803
|
+
});
|
|
8804
|
+
})() : /* @__PURE__ */ new Map();
|
|
8805
|
+
const result = restoreFromBlobs(manifest, blobs, opts.targetDir, opts.restore);
|
|
8806
|
+
return {
|
|
8807
|
+
...result,
|
|
8808
|
+
manifest
|
|
8809
|
+
};
|
|
8810
|
+
}
|
|
8811
|
+
function encodeWorkspaceArchive(workspaceDir) {
|
|
8812
|
+
const files = {};
|
|
8813
|
+
try {
|
|
8814
|
+
walk2(workspaceDir, "", files);
|
|
8815
|
+
} catch {
|
|
8816
|
+
return null;
|
|
8817
|
+
}
|
|
8818
|
+
if (Object.keys(files).length === 0) return null;
|
|
8819
|
+
const gz = gzipSync2(Buffer.from(JSON.stringify(files), "utf8"));
|
|
8820
|
+
return gz.toString("base64");
|
|
8821
|
+
}
|
|
8822
|
+
function decodeWorkspaceArchive(archive, schemaVersion) {
|
|
8823
|
+
if (schemaVersion !== void 0 && schemaVersion !== ARCHIVE_SCHEMA_VERSION) {
|
|
8824
|
+
throw new Error(`Unsupported archive schema version: ${schemaVersion} (expected ${ARCHIVE_SCHEMA_VERSION})`);
|
|
8825
|
+
}
|
|
8826
|
+
const raw = gunzipSync2(Buffer.from(archive, "base64")).toString("utf8");
|
|
8827
|
+
const parsed = JSON.parse(raw);
|
|
8828
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
8829
|
+
throw new Error("Decoded archive is not a JSON object map");
|
|
8830
|
+
}
|
|
8831
|
+
return parsed;
|
|
8832
|
+
}
|
|
8833
|
+
function writeArchiveToWorkspace(workspaceDir, files) {
|
|
8834
|
+
const writes = [];
|
|
8835
|
+
for (const [relPath, content] of Object.entries(files)) {
|
|
8836
|
+
const normalized = normalizeWorkspaceRelativePath(relPath);
|
|
8837
|
+
if (!normalized) throw new Error(`Refusing to write invalid archived path: ${relPath}`);
|
|
8838
|
+
if (isCredentialPersistencePath(normalized)) {
|
|
8839
|
+
throw new Error(`Refusing to restore credential file: ${normalized}`);
|
|
8840
|
+
}
|
|
8841
|
+
writes.push({
|
|
8842
|
+
path: resolve2(workspaceDir, normalized),
|
|
8843
|
+
content
|
|
8844
|
+
});
|
|
8845
|
+
}
|
|
8846
|
+
for (const write of writes) {
|
|
8847
|
+
mkdirSync2(dirname22(write.path), {
|
|
8848
|
+
recursive: true
|
|
8849
|
+
});
|
|
8850
|
+
writeFileSync2(write.path, write.content, "utf8");
|
|
8851
|
+
}
|
|
8852
|
+
}
|
|
8853
|
+
function walk2(root, prefix, out) {
|
|
8854
|
+
const dir = prefix ? join32(root, prefix) : root;
|
|
8855
|
+
const entries = readdirSync2(dir, {
|
|
8856
|
+
withFileTypes: true
|
|
8857
|
+
});
|
|
8858
|
+
for (const entry of entries) {
|
|
8859
|
+
if (entry.isDirectory()) {
|
|
8860
|
+
if (shouldSkipDirectory(entry.name) || ARCHIVE_ONLY_SKIP_DIRECTORIES.has(entry.name) || shouldSkipFile(entry.name)) continue;
|
|
8861
|
+
walk2(root, prefix ? `${prefix}${sep3}${entry.name}` : entry.name, out);
|
|
8862
|
+
continue;
|
|
8863
|
+
}
|
|
8864
|
+
if (!entry.isFile()) continue;
|
|
8865
|
+
if (shouldSkipFile(entry.name)) continue;
|
|
8866
|
+
const relPath = (prefix ? `${prefix}${sep3}${entry.name}` : entry.name).split(sep3).join("/");
|
|
8867
|
+
const abs = join32(dir, entry.name);
|
|
8868
|
+
if (statSync3(abs).size > DEFAULT_MAX_FILE_BYTES) continue;
|
|
8869
|
+
try {
|
|
8870
|
+
out[relPath] = readFileSync32(abs, "utf8");
|
|
8871
|
+
} catch {
|
|
8872
|
+
}
|
|
8873
|
+
}
|
|
8874
|
+
}
|
|
8875
|
+
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;
|
|
8876
|
+
var init_dist3 = __esm({
|
|
8877
|
+
"../shared-source-sync/dist/index.mjs"() {
|
|
8878
|
+
"use strict";
|
|
8879
|
+
__defProp4 = Object.defineProperty;
|
|
8880
|
+
__name4 = /* @__PURE__ */ __name((target, value3) => __defProp4(target, "name", { value: value3, configurable: true }), "__name");
|
|
8881
|
+
FILE_HASH_LENGTH = 16;
|
|
8882
|
+
__name(hashContentTruncated, "hashContentTruncated");
|
|
8883
|
+
__name4(hashContentTruncated, "hashContentTruncated");
|
|
8884
|
+
__name(sha256Hex, "sha256Hex");
|
|
8885
|
+
__name4(sha256Hex, "sha256Hex");
|
|
8886
|
+
__name(matchesFileHash, "matchesFileHash");
|
|
8887
|
+
__name4(matchesFileHash, "matchesFileHash");
|
|
8888
|
+
__name(combineFileHashes, "combineFileHashes");
|
|
8889
|
+
__name4(combineFileHashes, "combineFileHashes");
|
|
8890
|
+
SKIP_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
8891
|
+
"node_modules",
|
|
8892
|
+
"dist",
|
|
8893
|
+
"dist-v2",
|
|
8894
|
+
".git",
|
|
8895
|
+
".lua",
|
|
8896
|
+
".temp",
|
|
8897
|
+
"coverage",
|
|
8898
|
+
".next",
|
|
8899
|
+
".turbo"
|
|
8900
|
+
]);
|
|
8901
|
+
ARCHIVE_ONLY_SKIP_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
8902
|
+
"build"
|
|
8903
|
+
]);
|
|
8904
|
+
DEFAULT_MAX_FILE_BYTES = 256 * 1024;
|
|
8905
|
+
__name(shouldSkipDirectory, "shouldSkipDirectory");
|
|
8906
|
+
__name4(shouldSkipDirectory, "shouldSkipDirectory");
|
|
8907
|
+
__name(shouldSkipFile, "shouldSkipFile");
|
|
8908
|
+
__name4(shouldSkipFile, "shouldSkipFile");
|
|
8909
|
+
KIND_BY_EXT = {
|
|
8910
|
+
".ts": "source",
|
|
8911
|
+
".tsx": "source",
|
|
8912
|
+
".js": "source",
|
|
8913
|
+
".jsx": "source",
|
|
8914
|
+
".yaml": "config",
|
|
8915
|
+
".yml": "config",
|
|
8916
|
+
".json": "config",
|
|
8917
|
+
".toml": "config"
|
|
8918
|
+
};
|
|
8919
|
+
__name(classifyFile, "classifyFile");
|
|
8920
|
+
__name4(classifyFile, "classifyFile");
|
|
8921
|
+
__name(walkWorkspace, "walkWorkspace");
|
|
8922
|
+
__name4(walkWorkspace, "walkWorkspace");
|
|
8923
|
+
CHECK_BLOBS_MAX_HASHES = 500;
|
|
8924
|
+
BackupHttpError = class extends Error {
|
|
8925
|
+
static {
|
|
8926
|
+
__name(this, "BackupHttpError");
|
|
8927
|
+
}
|
|
8928
|
+
static {
|
|
8929
|
+
__name4(this, "BackupHttpError");
|
|
8930
|
+
}
|
|
8931
|
+
status;
|
|
8932
|
+
endpoint;
|
|
8933
|
+
body;
|
|
8934
|
+
constructor(message, status, endpoint, body) {
|
|
8935
|
+
super(message), this.status = status, this.endpoint = endpoint, this.body = body;
|
|
8936
|
+
this.name = "BackupHttpError";
|
|
8937
|
+
}
|
|
8938
|
+
};
|
|
8939
|
+
BackupHttpClient = class {
|
|
8940
|
+
static {
|
|
8941
|
+
__name(this, "BackupHttpClient");
|
|
8942
|
+
}
|
|
8943
|
+
static {
|
|
8944
|
+
__name4(this, "BackupHttpClient");
|
|
8945
|
+
}
|
|
8946
|
+
options;
|
|
8947
|
+
fetchFn;
|
|
8948
|
+
constructor(options) {
|
|
8949
|
+
this.options = options;
|
|
8950
|
+
this.fetchFn = options.fetch ?? fetch;
|
|
8951
|
+
}
|
|
8952
|
+
url(path3) {
|
|
8953
|
+
const base = this.options.baseUrl.replace(/\/$/, "");
|
|
8954
|
+
return `${base}/developer/agents/${encodeURIComponent(this.options.agentId)}${path3}`;
|
|
8955
|
+
}
|
|
8956
|
+
async json(method, path3, body) {
|
|
8957
|
+
const endpoint = this.url(path3);
|
|
8958
|
+
const res = await this.fetchFn(endpoint, {
|
|
8959
|
+
method,
|
|
8960
|
+
headers: {
|
|
8961
|
+
Authorization: this.options.authHeader,
|
|
8962
|
+
...body !== void 0 ? {
|
|
8963
|
+
"Content-Type": "application/json"
|
|
8964
|
+
} : {},
|
|
8965
|
+
Accept: "application/json"
|
|
8966
|
+
},
|
|
8967
|
+
body: body !== void 0 ? JSON.stringify(body) : void 0
|
|
8968
|
+
});
|
|
8969
|
+
if (!res.ok) {
|
|
8970
|
+
let text2;
|
|
8971
|
+
try {
|
|
8972
|
+
text2 = await res.text();
|
|
8973
|
+
} catch {
|
|
8974
|
+
text2 = void 0;
|
|
8975
|
+
}
|
|
8976
|
+
throw new BackupHttpError(`Backup request failed: ${method} ${path3} \u2192 ${res.status} ${res.statusText}`, res.status, endpoint, text2);
|
|
8977
|
+
}
|
|
8978
|
+
const text = await res.text();
|
|
8979
|
+
if (!text) return void 0;
|
|
8980
|
+
return JSON.parse(text);
|
|
8981
|
+
}
|
|
8982
|
+
/**
|
|
8983
|
+
* `POST /backup/check-blobs` — returns existing vs missing partition.
|
|
8984
|
+
*
|
|
8985
|
+
* LUA-675: the server caps one request at `CHECK_BLOBS_MAX_HASHES`; a
|
|
8986
|
+
* larger set is sent in sequential chunks and the partitions merged, so a
|
|
8987
|
+
* big workspace still pushes and the client never fans out on its own.
|
|
8988
|
+
*/
|
|
8989
|
+
async checkBlobsExist(hashes) {
|
|
8990
|
+
if (hashes.length <= CHECK_BLOBS_MAX_HASHES) {
|
|
8991
|
+
return this.json("POST", "/backup/check-blobs", {
|
|
8992
|
+
hashes
|
|
8993
|
+
});
|
|
8994
|
+
}
|
|
8995
|
+
const merged = {
|
|
8996
|
+
missing: [],
|
|
8997
|
+
existing: [],
|
|
8998
|
+
results: {}
|
|
8999
|
+
};
|
|
9000
|
+
let unverified;
|
|
9001
|
+
for (let i = 0; i < hashes.length; i += CHECK_BLOBS_MAX_HASHES) {
|
|
9002
|
+
const part = await this.json("POST", "/backup/check-blobs", {
|
|
9003
|
+
hashes: hashes.slice(i, i + CHECK_BLOBS_MAX_HASHES)
|
|
9004
|
+
});
|
|
9005
|
+
merged.missing.push(...part.missing);
|
|
9006
|
+
merged.existing.push(...part.existing);
|
|
9007
|
+
Object.assign(merged.results, part.results);
|
|
9008
|
+
if (part.unverified) unverified = [
|
|
9009
|
+
...unverified ?? [],
|
|
9010
|
+
...part.unverified
|
|
9011
|
+
];
|
|
9012
|
+
}
|
|
9013
|
+
return unverified ? {
|
|
9014
|
+
...merged,
|
|
9015
|
+
unverified
|
|
9016
|
+
} : merged;
|
|
9017
|
+
}
|
|
9018
|
+
/** `POST /backup/blob-upload-urls` — presigned S3 PUT URLs. */
|
|
9019
|
+
getBlobUploadUrls(hashes) {
|
|
9020
|
+
return this.json("POST", "/backup/blob-upload-urls", {
|
|
9021
|
+
hashes
|
|
9022
|
+
});
|
|
9023
|
+
}
|
|
9024
|
+
/** `POST /backup/blob-urls` — presigned S3 GET URLs (for restore). */
|
|
9025
|
+
getBlobUrls(hashes) {
|
|
9026
|
+
return this.json("POST", "/backup/blob-urls", {
|
|
9027
|
+
hashes
|
|
9028
|
+
});
|
|
9029
|
+
}
|
|
9030
|
+
/** `POST /backup/manifest` — final step in a push; returns server metadata. */
|
|
9031
|
+
saveManifest(data) {
|
|
9032
|
+
return this.json("POST", "/backup/manifest", data);
|
|
9033
|
+
}
|
|
9034
|
+
/** `GET /backup` — metadata only, no file list. */
|
|
9035
|
+
getMetadata() {
|
|
9036
|
+
return this.json("GET", "/backup");
|
|
9037
|
+
}
|
|
9038
|
+
/** `GET /backup/manifest` — metadata + file list. */
|
|
9039
|
+
getManifest() {
|
|
9040
|
+
return this.json("GET", "/backup/manifest");
|
|
9041
|
+
}
|
|
9042
|
+
/** `GET /backup/check/:hash` — fast freshness probe. */
|
|
9043
|
+
checkBackupExists(hash) {
|
|
9044
|
+
return this.json("GET", `/backup/check/${encodeURIComponent(hash)}`);
|
|
9045
|
+
}
|
|
9046
|
+
};
|
|
9047
|
+
DEFAULT_CONCURRENCY = 10;
|
|
9048
|
+
__name(uploadBlobs, "uploadBlobs");
|
|
9049
|
+
__name4(uploadBlobs, "uploadBlobs");
|
|
9050
|
+
__name(decodeBlob, "decodeBlob");
|
|
9051
|
+
__name4(decodeBlob, "decodeBlob");
|
|
9052
|
+
__name(verifyDownloaded, "verifyDownloaded");
|
|
9053
|
+
__name4(verifyDownloaded, "verifyDownloaded");
|
|
9054
|
+
__name(downloadBlobs, "downloadBlobs");
|
|
9055
|
+
__name4(downloadBlobs, "downloadBlobs");
|
|
9056
|
+
__name(resolveBackupFileTarget, "resolveBackupFileTarget");
|
|
9057
|
+
__name4(resolveBackupFileTarget, "resolveBackupFileTarget");
|
|
9058
|
+
__name(restoreFromBlobs, "restoreFromBlobs");
|
|
9059
|
+
__name4(restoreFromBlobs, "restoreFromBlobs");
|
|
9060
|
+
CREDENTIAL_PATHS = /* @__PURE__ */ new Set([
|
|
9061
|
+
".env",
|
|
9062
|
+
".lua/config.json",
|
|
9063
|
+
".claude/settings.local.json"
|
|
9064
|
+
]);
|
|
9065
|
+
WINDOWS_ABSOLUTE_PATH = /^[a-z]:\//i;
|
|
9066
|
+
__name(normalizeWorkspaceRelativePath, "normalizeWorkspaceRelativePath");
|
|
9067
|
+
__name4(normalizeWorkspaceRelativePath, "normalizeWorkspaceRelativePath");
|
|
9068
|
+
__name(isCredentialPersistencePath, "isCredentialPersistencePath");
|
|
9069
|
+
__name4(isCredentialPersistencePath, "isCredentialPersistencePath");
|
|
9070
|
+
__name(pushAgentBackup, "pushAgentBackup");
|
|
9071
|
+
__name4(pushAgentBackup, "pushAgentBackup");
|
|
9072
|
+
__name(pullAgentBackup, "pullAgentBackup");
|
|
9073
|
+
__name4(pullAgentBackup, "pullAgentBackup");
|
|
9074
|
+
ARCHIVE_SCHEMA_VERSION = 1;
|
|
9075
|
+
__name(encodeWorkspaceArchive, "encodeWorkspaceArchive");
|
|
9076
|
+
__name4(encodeWorkspaceArchive, "encodeWorkspaceArchive");
|
|
9077
|
+
__name(decodeWorkspaceArchive, "decodeWorkspaceArchive");
|
|
9078
|
+
__name4(decodeWorkspaceArchive, "decodeWorkspaceArchive");
|
|
9079
|
+
__name(writeArchiveToWorkspace, "writeArchiveToWorkspace");
|
|
9080
|
+
__name4(writeArchiveToWorkspace, "writeArchiveToWorkspace");
|
|
9081
|
+
__name(walk2, "walk");
|
|
9082
|
+
__name4(walk2, "walk");
|
|
9083
|
+
}
|
|
9084
|
+
});
|
|
9085
|
+
|
|
7529
9086
|
// src/api/backup.api.service.ts
|
|
7530
9087
|
var init_backup_api_service = __esm({
|
|
7531
9088
|
"src/api/backup.api.service.ts"() {
|
|
7532
9089
|
"use strict";
|
|
7533
9090
|
init_http_client();
|
|
9091
|
+
init_dist3();
|
|
7534
9092
|
}
|
|
7535
9093
|
});
|
|
7536
9094
|
|
|
@@ -7619,6 +9177,7 @@ var init_base_handler = __esm({
|
|
|
7619
9177
|
init_bundle_upload();
|
|
7620
9178
|
init_semver();
|
|
7621
9179
|
init_auth_error();
|
|
9180
|
+
init_cli_error();
|
|
7622
9181
|
DEFAULT_VERSION = SKILL_DEFAULTS.VERSION;
|
|
7623
9182
|
BaseVersionedHandler = class {
|
|
7624
9183
|
static {
|
|
@@ -7700,7 +9259,7 @@ var init_base_handler = __esm({
|
|
|
7700
9259
|
serverItems
|
|
7701
9260
|
};
|
|
7702
9261
|
} catch (error) {
|
|
7703
|
-
if (AuthenticationError.isAuthenticationError(error)) throw error;
|
|
9262
|
+
if (AuthenticationError.isAuthenticationError(error) || isAccessDeniedError(error)) throw error;
|
|
7704
9263
|
return {
|
|
7705
9264
|
serverItems: null,
|
|
7706
9265
|
fetchError: error instanceof Error ? error.message : String(error)
|
|
@@ -7730,13 +9289,24 @@ var init_base_handler = __esm({
|
|
|
7730
9289
|
}
|
|
7731
9290
|
const yamlItems = this.getFromYaml(config);
|
|
7732
9291
|
const { yamlById, yamlByName, serverByName } = this.buildMaps(serverData.serverItems, yamlItems);
|
|
9292
|
+
const idField = this.yamlConfig.idField;
|
|
9293
|
+
const manifestNames = manifest ? new Set(getPrimitivesByKind(manifest, this.kind).map((p) => p.name)) : /* @__PURE__ */ new Set();
|
|
9294
|
+
for (const stale of this.staleYamlRows(yamlItems, serverData.serverItems, manifestNames)) {
|
|
9295
|
+
const idx = yamlItems.indexOf(stale);
|
|
9296
|
+
if (idx < 0) continue;
|
|
9297
|
+
const { [idField]: goneId, ...rest } = stale;
|
|
9298
|
+
yamlItems[idx] = rest;
|
|
9299
|
+
yamlUpdated = true;
|
|
9300
|
+
const msg = `\u2139\uFE0F ${this.displayName} "${stale.name}" (${goneId}) no longer exists on the server \u2014 re-registering it`;
|
|
9301
|
+
messages.push(msg);
|
|
9302
|
+
console.log(msg);
|
|
9303
|
+
}
|
|
7733
9304
|
const orphans = serverData.serverItems.filter((item) => {
|
|
7734
9305
|
const id = item.id;
|
|
7735
9306
|
const name = item.name;
|
|
7736
9307
|
return !yamlById.has(id) && !yamlByName.has(name) && this.isActive(item) && this.shouldConsiderForOrphan(item);
|
|
7737
9308
|
});
|
|
7738
9309
|
if (orphans.length > 0) {
|
|
7739
|
-
const idField = this.yamlConfig.idField;
|
|
7740
9310
|
const stubs = orphans.map((item) => this.cleanItem({
|
|
7741
9311
|
name: item.name,
|
|
7742
9312
|
version: this.getActiveVersion(item) || DEFAULT_VERSION,
|
|
@@ -7779,6 +9349,7 @@ var init_base_handler = __esm({
|
|
|
7779
9349
|
console.log(`\u2705 Server ${this.displayNamePlural} and YAML are fully in sync`);
|
|
7780
9350
|
}
|
|
7781
9351
|
} catch (error) {
|
|
9352
|
+
if (AuthenticationError.isAuthenticationError(error) || isAccessDeniedError(error)) throw error;
|
|
7782
9353
|
console.error(`\u274C Error syncing server ${this.displayNamePlural}:`, error);
|
|
7783
9354
|
}
|
|
7784
9355
|
return {
|
|
@@ -7835,6 +9406,7 @@ var init_base_handler = __esm({
|
|
|
7835
9406
|
console.error(` \u274C Failed to create "${item.name}" - no ID returned`);
|
|
7836
9407
|
}
|
|
7837
9408
|
} catch (error) {
|
|
9409
|
+
if (AuthenticationError.isAuthenticationError(error) || isAccessDeniedError(error)) throw error;
|
|
7838
9410
|
console.error(` \u274C Failed to create "${item.name}": ${error instanceof Error ? error.message : error}`);
|
|
7839
9411
|
}
|
|
7840
9412
|
}
|
|
@@ -7969,6 +9541,15 @@ var init_base_handler = __esm({
|
|
|
7969
9541
|
getItemId(item) {
|
|
7970
9542
|
return item[this.yamlConfig.idField] || "";
|
|
7971
9543
|
}
|
|
9544
|
+
/**
|
|
9545
|
+
* LUA-750: the yaml rows whose server id is gone (deleted server-side) and which `applySyncToYaml` should
|
|
9546
|
+
* re-register. Default none — a handler whose `fetchFromServer` lists EVERY live row of its kind for the
|
|
9547
|
+
* agent overrides this (a kind whose list omits inactive rows must not, or it would duplicate them).
|
|
9548
|
+
* `manifestNames` is the set of primitives in local code: only those are worth re-creating.
|
|
9549
|
+
*/
|
|
9550
|
+
staleYamlRows(_yamlItems, _serverItems, _manifestNames) {
|
|
9551
|
+
return [];
|
|
9552
|
+
}
|
|
7972
9553
|
buildMaps(serverItems, yamlItems) {
|
|
7973
9554
|
const yamlById = /* @__PURE__ */ new Map();
|
|
7974
9555
|
const yamlByName = /* @__PURE__ */ new Map();
|
|
@@ -8367,6 +9948,7 @@ var init_cli = __esm({
|
|
|
8367
9948
|
"src/utils/cli.ts"() {
|
|
8368
9949
|
"use strict";
|
|
8369
9950
|
init_auth_error();
|
|
9951
|
+
init_cli_error();
|
|
8370
9952
|
init_version_check();
|
|
8371
9953
|
init_package_root();
|
|
8372
9954
|
init_analytics();
|
|
@@ -8386,6 +9968,7 @@ var init_command_utils = __esm({
|
|
|
8386
9968
|
init_request_credential();
|
|
8387
9969
|
init_files();
|
|
8388
9970
|
init_cli();
|
|
9971
|
+
init_cli_error();
|
|
8389
9972
|
__name(requireAuth, "requireAuth");
|
|
8390
9973
|
}
|
|
8391
9974
|
});
|
|
@@ -11934,7 +13517,10 @@ var init_workflow_api_service = __esm({
|
|
|
11934
13517
|
async getRunReplayBundle(runId) {
|
|
11935
13518
|
return this.httpGet(`${this.runs}/${runId}/journal?format=replay`, await this.auth());
|
|
11936
13519
|
}
|
|
11937
|
-
/**
|
|
13520
|
+
/**
|
|
13521
|
+
* R11 — cancel (`mode:'request'` default). A `'force'` before `forceAvailableAt` is the 200 verdict
|
|
13522
|
+
* `{ transitioned:false, nextAction:'cancel_again', forceAvailableAt }` (PRO-979) — never a 409 (LUA-748).
|
|
13523
|
+
*/
|
|
11938
13524
|
async cancelRun(runId, data = {}) {
|
|
11939
13525
|
return this.httpPost(`${this.runs}/${runId}/cancel`, data, await this.auth());
|
|
11940
13526
|
}
|
|
@@ -12013,6 +13599,14 @@ var init_workflow_api_service = __esm({
|
|
|
12013
13599
|
async closeGoal(goalId, data = {}) {
|
|
12014
13600
|
return this.httpPost(`${this.goals}/${encodeURIComponent(goalId)}/close`, data, await this.auth());
|
|
12015
13601
|
}
|
|
13602
|
+
/** LUA-749 R63 — edit (objective / judge / cadence / caps / note); a `budget` / `max_runs` park re-arms when the cap clears (`rearmed:true`). */
|
|
13603
|
+
async updateGoal(goalId, data) {
|
|
13604
|
+
return this.httpPatch(`${this.goals}/${encodeURIComponent(goalId)}`, data, await this.auth());
|
|
13605
|
+
}
|
|
13606
|
+
/** LUA-749 R64 — raise `maxTotalCredits` / `maxRuns` (increases only; 400 `GOAL_RAISE_BELOW_SPENT{field, value, spent}`). */
|
|
13607
|
+
async raiseGoal(goalId, data) {
|
|
13608
|
+
return this.httpPost(`${this.goals}/${encodeURIComponent(goalId)}/raise`, data, await this.auth());
|
|
13609
|
+
}
|
|
12016
13610
|
// ─── Schedules (R4-MF-2 list/get + R28 delete — `/workflows/:agentId/schedules`; LUA-627 stanza) ───
|
|
12017
13611
|
/** Schedule tree (09 §9.5 — the write-only R27/R56/R28 family plus the R4-MF-2 read rows). */
|
|
12018
13612
|
get schedules() {
|
|
@@ -12026,7 +13620,7 @@ var init_workflow_api_service = __esm({
|
|
|
12026
13620
|
async getSchedule(jobId) {
|
|
12027
13621
|
return this.httpGet(`${this.schedules}/${encodeURIComponent(jobId)}`, await this.auth());
|
|
12028
13622
|
}
|
|
12029
|
-
/** R28 — delete a schedule Job (404 `SCHEDULE_NOT_FOUND`). The CLI refuses a goal
|
|
13623
|
+
/** R28 — delete a schedule Job (404 `SCHEDULE_NOT_FOUND`). The CLI refuses a LIVE goal's job BEFORE this call (`goal_schedule`); an ended goal's lingering Job is retired here (LUA-760). */
|
|
12030
13624
|
async deleteSchedule(jobId) {
|
|
12031
13625
|
return this.httpDelete(`${this.schedules}/${encodeURIComponent(jobId)}`, await this.auth());
|
|
12032
13626
|
}
|