lua-cli 3.31.0 → 3.32.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api-exports.d.ts +416 -103
- package/dist/api-exports.js +1992 -299
- package/dist/api-exports.js.map +1 -1
- package/dist/index.js +5262 -1914
- package/dist/index.js.map +1 -1
- package/dist/voice/test/index.d.ts +54 -54
- package/dist/workflow-builder.d.ts +257 -44
- package/dist/workflow-builder.js +1382 -265
- package/dist/workflow-builder.js.map +1 -1
- package/docs/README.md +2 -2
- package/docs/api/LuaWorkflow.md +44 -28
- package/docs/api/Workflows.md +12 -1
- package/docs/workflows/approvals.md +14 -1
- package/docs/workflows/connections-in-coding-turns.md +1 -0
- package/docs/workflows/correlation-keys.md +1 -0
- package/docs/workflows/git-credentials.md +22 -1
- package/docs/workflows/goals.md +46 -0
- package/docs/workflows/limits.md +6 -0
- package/docs/workflows/recovery.md +6 -2
- package/docs/workflows/replay-local.md +10 -10
- package/docs/workflows/schedules.md +15 -0
- package/docs/workflows/script-form.md +20 -10
- package/docs/workflows/testing-offline.md +25 -20
- package/docs/workflows/workspaces-and-long-steps.md +38 -2
- package/package.json +2 -2
- package/template/examples/workflows/CLAUDE.md +16 -13
- package/template/examples/workflows/pr-review-round.ts +61 -20
- package/template/examples/workflows/provision-tenant.ts +25 -8
- package/template/examples/workflows/refund-approval.ts +30 -17
- package/template/examples/workflows/support-triage.ts +59 -22
- package/template/examples/workflows/ticket-to-pr.ts +125 -46
- package/template/examples/workflows/vendor-invoices.ts +69 -16
- package/template/package.json +1 -1
package/dist/workflow-builder.js
CHANGED
|
@@ -4,9 +4,6 @@ var __name = (target, value3) => __defProp(target, "name", { value: value3, conf
|
|
|
4
4
|
// src/types/workflow.ts
|
|
5
5
|
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
6
6
|
|
|
7
|
-
// ../workflow-graph/dist/index.mjs
|
|
8
|
-
import { createHash } from "crypto";
|
|
9
|
-
|
|
10
7
|
// ../shared-types/dist/index.mjs
|
|
11
8
|
import { z } from "zod";
|
|
12
9
|
import { z as z2 } from "zod";
|
|
@@ -659,6 +656,12 @@ function isImplicitModelSelectionSource(source) {
|
|
|
659
656
|
}
|
|
660
657
|
__name(isImplicitModelSelectionSource, "isImplicitModelSelectionSource");
|
|
661
658
|
__name2(isImplicitModelSelectionSource, "isImplicitModelSelectionSource");
|
|
659
|
+
var PLATFORM_FALLBACK_MODEL_SOURCE = "platform-fallback";
|
|
660
|
+
function isPlatformFallbackModelSource(source) {
|
|
661
|
+
return source === PLATFORM_FALLBACK_MODEL_SOURCE;
|
|
662
|
+
}
|
|
663
|
+
__name(isPlatformFallbackModelSource, "isPlatformFallbackModelSource");
|
|
664
|
+
__name2(isPlatformFallbackModelSource, "isPlatformFallbackModelSource");
|
|
662
665
|
function resolveRequireToolApproval(rules) {
|
|
663
666
|
const raw = rules?.requireToolApproval ?? rules?.requireApproval;
|
|
664
667
|
if (raw === void 0 || raw === null) return void 0;
|
|
@@ -1348,7 +1351,6 @@ function luaClientMetricLabels(client) {
|
|
|
1348
1351
|
const canonical2 = parseLuaClientHeader(serializeLuaClientHeader(client));
|
|
1349
1352
|
return {
|
|
1350
1353
|
client_family: canonical2?.app ?? "unknown",
|
|
1351
|
-
client_version: canonical2?.version ?? "unknown",
|
|
1352
1354
|
client_attribution: canonical2?.attribution ?? "unknown"
|
|
1353
1355
|
};
|
|
1354
1356
|
}
|
|
@@ -1490,6 +1492,87 @@ function hasCapability(profiles, profileId, required) {
|
|
|
1490
1492
|
}
|
|
1491
1493
|
__name(hasCapability, "hasCapability");
|
|
1492
1494
|
__name2(hasCapability, "hasCapability");
|
|
1495
|
+
function workflowJobId(id) {
|
|
1496
|
+
switch (id.kind) {
|
|
1497
|
+
case "start":
|
|
1498
|
+
return `wft_${id.runId}_0`;
|
|
1499
|
+
case "start-redrive":
|
|
1500
|
+
return `wft_${id.runId}_start_r${id.now}`;
|
|
1501
|
+
case "tick":
|
|
1502
|
+
return `wft_${id.runId}_${id.seq}`;
|
|
1503
|
+
case "timer":
|
|
1504
|
+
return `wft_${id.runId}_timer_${id.stepId}_${id.attempt}`;
|
|
1505
|
+
case "timer-remainder":
|
|
1506
|
+
return `wft_${id.runId}_timer_${id.stepId}_${id.attempt}_r${id.now}`;
|
|
1507
|
+
case "timer-deferral":
|
|
1508
|
+
return `wft_${id.runId}_timer_${id.stepId}_${id.attempt}_d${id.now}`;
|
|
1509
|
+
case "timer-sibling":
|
|
1510
|
+
return `wft_${id.runId}_timer_${id.stepId}_${id.attempt}_w${id.now}`;
|
|
1511
|
+
case "retry":
|
|
1512
|
+
return `wft_${id.runId}_retry_${id.stepId}_${id.attempt}`;
|
|
1513
|
+
case "respawn":
|
|
1514
|
+
return `wft_${id.runId}_respawn_${id.stepId}_${id.attempt}_s${id.segment}_r${id.now}`;
|
|
1515
|
+
case "redrive":
|
|
1516
|
+
return `wft_${id.runId}_redrive_${id.stepId}_${id.attempt}_r${id.now}`;
|
|
1517
|
+
case "redispatch":
|
|
1518
|
+
return `wft_${id.runId}_redispatch_${id.stepId}_${id.attempt}_x${id.executionId ?? "none"}_r${id.now}`;
|
|
1519
|
+
case "expiry":
|
|
1520
|
+
return `wft_${id.runId}_expire_${id.stepId}_${id.attempt}_t${id.suspendedAt}${id.hop > 0 ? `_h${id.hop}` : ""}`;
|
|
1521
|
+
case "step-terminal":
|
|
1522
|
+
return `wft_${id.runId}_term_${id.stepId}_${id.attempt}_r${id.now}`;
|
|
1523
|
+
case "handback":
|
|
1524
|
+
return `wft_${id.runId}_handback_${id.stepId}_${id.attempt}_${id.now}`;
|
|
1525
|
+
case "foreach-rate":
|
|
1526
|
+
return `wft_${id.runId}_fe_${id.stepId}_${id.attempt}_${id.bucket}`;
|
|
1527
|
+
case "budget-expiry":
|
|
1528
|
+
return `wft_${id.runId}_expiry_budget_r${id.now}`;
|
|
1529
|
+
case "budget-resume":
|
|
1530
|
+
return `wft_${id.runId}_resume_budget_${id.now}`;
|
|
1531
|
+
case "deadline":
|
|
1532
|
+
return `wft_${id.runId}_deadline_${id.deadlineAt}`;
|
|
1533
|
+
case "deadline-remainder":
|
|
1534
|
+
return `wft_${id.runId}_deadline_${id.deadlineAt}_r${id.now}`;
|
|
1535
|
+
case "readmit":
|
|
1536
|
+
return `wft_${id.runId}_readmit_${id.now}`;
|
|
1537
|
+
case "cancel":
|
|
1538
|
+
return `wft_${id.runId}_cancel`;
|
|
1539
|
+
case "reconcile":
|
|
1540
|
+
return `wft_${id.runId}_reconcile_${id.now}`;
|
|
1541
|
+
case "replay":
|
|
1542
|
+
return `wft_${id.runId}_replay_g${id.leaseGeneration}`;
|
|
1543
|
+
case "migration":
|
|
1544
|
+
return `wfm_${id.migrationId}`;
|
|
1545
|
+
default:
|
|
1546
|
+
return assertNever(id);
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
__name(workflowJobId, "workflowJobId");
|
|
1550
|
+
__name2(workflowJobId, "workflowJobId");
|
|
1551
|
+
function assertNever(id) {
|
|
1552
|
+
throw new Error(`workflowJobId: unknown wake kind ${JSON.stringify(id)}`);
|
|
1553
|
+
}
|
|
1554
|
+
__name(assertNever, "assertNever");
|
|
1555
|
+
__name2(assertNever, "assertNever");
|
|
1556
|
+
function agentStepJobId(msg) {
|
|
1557
|
+
return `${msg.runId}_${msg.stepId}_${msg.attempt}_x${msg.executionId}`;
|
|
1558
|
+
}
|
|
1559
|
+
__name(agentStepJobId, "agentStepJobId");
|
|
1560
|
+
__name2(agentStepJobId, "agentStepJobId");
|
|
1561
|
+
function heavyStepJobId(msg) {
|
|
1562
|
+
return `${msg.runId}_${msg.stepId}_${msg.attempt}_x${msg.executionId}`;
|
|
1563
|
+
}
|
|
1564
|
+
__name(heavyStepJobId, "heavyStepJobId");
|
|
1565
|
+
__name2(heavyStepJobId, "heavyStepJobId");
|
|
1566
|
+
function jobSpawnJobId(msg) {
|
|
1567
|
+
return `${msg.runId}_${msg.stepId}_${msg.attempt}_s${msg.segment}_${msg.executionId}`;
|
|
1568
|
+
}
|
|
1569
|
+
__name(jobSpawnJobId, "jobSpawnJobId");
|
|
1570
|
+
__name2(jobSpawnJobId, "jobSpawnJobId");
|
|
1571
|
+
function exportAutoJobId(exportId, suffix) {
|
|
1572
|
+
return suffix ? `${exportId}:${suffix}` : exportId;
|
|
1573
|
+
}
|
|
1574
|
+
__name(exportAutoJobId, "exportAutoJobId");
|
|
1575
|
+
__name2(exportAutoJobId, "exportAutoJobId");
|
|
1493
1576
|
var SYSTEM_USER_PREFIX = "system:";
|
|
1494
1577
|
function isSystemRun(identity) {
|
|
1495
1578
|
return identity.userId.startsWith(SYSTEM_USER_PREFIX);
|
|
@@ -1528,6 +1611,27 @@ var WORKFLOW_RUN_STATUSES = [
|
|
|
1528
1611
|
...WORKFLOW_RUN_IDLE,
|
|
1529
1612
|
...WORKFLOW_RUN_TERMINAL
|
|
1530
1613
|
];
|
|
1614
|
+
var WORKFLOW_STEP_STATUSES = [
|
|
1615
|
+
"pending",
|
|
1616
|
+
"ready",
|
|
1617
|
+
"waiting",
|
|
1618
|
+
"dispatched",
|
|
1619
|
+
"claimed",
|
|
1620
|
+
"running",
|
|
1621
|
+
"suspended",
|
|
1622
|
+
"cancellation_requested",
|
|
1623
|
+
"completed",
|
|
1624
|
+
"failed",
|
|
1625
|
+
"skipped",
|
|
1626
|
+
"cancelled",
|
|
1627
|
+
"timeout",
|
|
1628
|
+
"reaped"
|
|
1629
|
+
];
|
|
1630
|
+
var WORKFLOW_STEP_IN_FLIGHT = [
|
|
1631
|
+
"claimed",
|
|
1632
|
+
"running",
|
|
1633
|
+
"cancellation_requested"
|
|
1634
|
+
];
|
|
1531
1635
|
var ARCHIVE_WINDOW_MARGIN_DAYS = 7;
|
|
1532
1636
|
function shouldSkipArchive(run, manifestSha256, sinkSha256) {
|
|
1533
1637
|
if (!run.completedAt || !run.exportedAt || run.exportedAt < run.completedAt) return false;
|
|
@@ -1588,6 +1692,7 @@ function workflowOrgSlotKey(orgId, kind) {
|
|
|
1588
1692
|
}
|
|
1589
1693
|
__name(workflowOrgSlotKey, "workflowOrgSlotKey");
|
|
1590
1694
|
__name2(workflowOrgSlotKey, "workflowOrgSlotKey");
|
|
1695
|
+
var WORKFLOW_CONNECTION_KEY_RE = /^[a-z][a-z0-9_-]{0,63}$/;
|
|
1591
1696
|
function scheduledTimeKey(scheduledTime) {
|
|
1592
1697
|
return scheduledTime.replace(/[^0-9TZ]/g, "");
|
|
1593
1698
|
}
|
|
@@ -1599,6 +1704,310 @@ function scheduledWorkflowRunIdForTime(jobId, scheduledTime) {
|
|
|
1599
1704
|
__name(scheduledWorkflowRunIdForTime, "scheduledWorkflowRunIdForTime");
|
|
1600
1705
|
__name2(scheduledWorkflowRunIdForTime, "scheduledWorkflowRunIdForTime");
|
|
1601
1706
|
var WORKFLOW_SIGNAL_PAYLOAD_MAX_BYTES = 64 * 1024;
|
|
1707
|
+
var WORKFLOW_RESOLVE_OUTPUT_MAX_BYTES = 256 * 1024;
|
|
1708
|
+
var WORKFLOW_RETRY_BACKOFFS = [
|
|
1709
|
+
"fixed",
|
|
1710
|
+
"exponential"
|
|
1711
|
+
];
|
|
1712
|
+
var WORKFLOW_JOB_RESOURCES = [
|
|
1713
|
+
"small",
|
|
1714
|
+
"medium",
|
|
1715
|
+
"large"
|
|
1716
|
+
];
|
|
1717
|
+
var WORKFLOW_SIDE_EFFECTS = [
|
|
1718
|
+
"none",
|
|
1719
|
+
"external"
|
|
1720
|
+
];
|
|
1721
|
+
var WORKFLOW_JOB_RANGES = Object.freeze({
|
|
1722
|
+
/** Coding-turn cap (`claude -p --max-turns`); absent ⇒ the cluster default (LUA_WF_JOB_MAX_TURNS). */
|
|
1723
|
+
maxTurns: Object.freeze({
|
|
1724
|
+
min: 1,
|
|
1725
|
+
max: 500
|
|
1726
|
+
}),
|
|
1727
|
+
/** Harness messages per attempt (one per content block); absent ⇒ the cluster default (400). */
|
|
1728
|
+
maxMessages: Object.freeze({
|
|
1729
|
+
min: 1,
|
|
1730
|
+
max: 5e3
|
|
1731
|
+
}),
|
|
1732
|
+
/** Input-side tokens per attempt (prompt + cache); absent ⇒ the cluster default (4M — LUA-708, was 30M). */
|
|
1733
|
+
maxInputTokens: Object.freeze({
|
|
1734
|
+
min: 1e6,
|
|
1735
|
+
max: 5e8
|
|
1736
|
+
})
|
|
1737
|
+
});
|
|
1738
|
+
var WORKFLOW_JOB_RANGE_MEMBERS = Object.keys(WORKFLOW_JOB_RANGES);
|
|
1739
|
+
function isWithinWorkflowJobRange(member, value3) {
|
|
1740
|
+
const { min, max } = WORKFLOW_JOB_RANGES[member];
|
|
1741
|
+
return typeof value3 === "number" && Number.isInteger(value3) && value3 >= min && value3 <= max;
|
|
1742
|
+
}
|
|
1743
|
+
__name(isWithinWorkflowJobRange, "isWithinWorkflowJobRange");
|
|
1744
|
+
__name2(isWithinWorkflowJobRange, "isWithinWorkflowJobRange");
|
|
1745
|
+
function workflowJobRangeMessage(member) {
|
|
1746
|
+
const { min, max } = WORKFLOW_JOB_RANGES[member];
|
|
1747
|
+
return `\`${member}\` must be an integer ${min}..${max}`;
|
|
1748
|
+
}
|
|
1749
|
+
__name(workflowJobRangeMessage, "workflowJobRangeMessage");
|
|
1750
|
+
__name2(workflowJobRangeMessage, "workflowJobRangeMessage");
|
|
1751
|
+
var WORKFLOW_SINGLE_STEP_TYPES = [
|
|
1752
|
+
"agent",
|
|
1753
|
+
"tool",
|
|
1754
|
+
"workflow",
|
|
1755
|
+
"step"
|
|
1756
|
+
];
|
|
1757
|
+
var WORKFLOW_HITL_ENTRY_TYPES = [
|
|
1758
|
+
"approval",
|
|
1759
|
+
"waitForSignal"
|
|
1760
|
+
];
|
|
1761
|
+
var WORKFLOW_ARM_ENTRY_TYPES = [
|
|
1762
|
+
...WORKFLOW_SINGLE_STEP_TYPES,
|
|
1763
|
+
...WORKFLOW_HITL_ENTRY_TYPES
|
|
1764
|
+
];
|
|
1765
|
+
var WORKFLOW_HITL_ARM_CONTAINERS = [
|
|
1766
|
+
"parallel",
|
|
1767
|
+
"conditional",
|
|
1768
|
+
"foreach"
|
|
1769
|
+
];
|
|
1770
|
+
function isWorkflowSingleStepType(type) {
|
|
1771
|
+
return typeof type === "string" && WORKFLOW_SINGLE_STEP_TYPES.includes(type);
|
|
1772
|
+
}
|
|
1773
|
+
__name(isWorkflowSingleStepType, "isWorkflowSingleStepType");
|
|
1774
|
+
__name2(isWorkflowSingleStepType, "isWorkflowSingleStepType");
|
|
1775
|
+
function isWorkflowHitlEntryType(type) {
|
|
1776
|
+
return typeof type === "string" && WORKFLOW_HITL_ENTRY_TYPES.includes(type);
|
|
1777
|
+
}
|
|
1778
|
+
__name(isWorkflowHitlEntryType, "isWorkflowHitlEntryType");
|
|
1779
|
+
__name2(isWorkflowHitlEntryType, "isWorkflowHitlEntryType");
|
|
1780
|
+
function isWorkflowArmEntryType(type) {
|
|
1781
|
+
return typeof type === "string" && WORKFLOW_ARM_ENTRY_TYPES.includes(type);
|
|
1782
|
+
}
|
|
1783
|
+
__name(isWorkflowArmEntryType, "isWorkflowArmEntryType");
|
|
1784
|
+
__name2(isWorkflowArmEntryType, "isWorkflowArmEntryType");
|
|
1785
|
+
function workflowContainerRunsHitlArm(container) {
|
|
1786
|
+
return typeof container === "string" && WORKFLOW_HITL_ARM_CONTAINERS.includes(container);
|
|
1787
|
+
}
|
|
1788
|
+
__name(workflowContainerRunsHitlArm, "workflowContainerRunsHitlArm");
|
|
1789
|
+
__name2(workflowContainerRunsHitlArm, "workflowContainerRunsHitlArm");
|
|
1790
|
+
function workflowHitlArmUnsupportedMessage(type, id, container) {
|
|
1791
|
+
const legal = WORKFLOW_HITL_ARM_CONTAINERS.map((c) => `\`${c}\``).join(" / ");
|
|
1792
|
+
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\``;
|
|
1793
|
+
}
|
|
1794
|
+
__name(workflowHitlArmUnsupportedMessage, "workflowHitlArmUnsupportedMessage");
|
|
1795
|
+
__name2(workflowHitlArmUnsupportedMessage, "workflowHitlArmUnsupportedMessage");
|
|
1796
|
+
function workflowHitlArmShapeMessage(type, id, shape) {
|
|
1797
|
+
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\``;
|
|
1798
|
+
}
|
|
1799
|
+
__name(workflowHitlArmShapeMessage, "workflowHitlArmShapeMessage");
|
|
1800
|
+
__name2(workflowHitlArmShapeMessage, "workflowHitlArmShapeMessage");
|
|
1801
|
+
var WORKFLOW_GRAPH_ENTRY_STEP_KINDS = Object.freeze({
|
|
1802
|
+
agent: "agent",
|
|
1803
|
+
tool: "tool",
|
|
1804
|
+
workflow: "subrun",
|
|
1805
|
+
step: "code",
|
|
1806
|
+
mapping: "map",
|
|
1807
|
+
sleep: "sleep",
|
|
1808
|
+
sleepUntil: "sleepUntil",
|
|
1809
|
+
parallel: null,
|
|
1810
|
+
conditional: "branch",
|
|
1811
|
+
foreach: "foreach",
|
|
1812
|
+
loop: "loop",
|
|
1813
|
+
approval: "approval",
|
|
1814
|
+
waitForSignal: "signal"
|
|
1815
|
+
});
|
|
1816
|
+
var WORKFLOW_ARM_ENTRY_STEP_KINDS = Object.freeze(Object.fromEntries(WORKFLOW_ARM_ENTRY_TYPES.map((t) => [
|
|
1817
|
+
t,
|
|
1818
|
+
WORKFLOW_GRAPH_ENTRY_STEP_KINDS[t]
|
|
1819
|
+
])));
|
|
1820
|
+
var WORKFLOW_BUDGET_MAX_DURATION_SECONDS = Object.freeze({
|
|
1821
|
+
min: 60,
|
|
1822
|
+
max: 2592e3
|
|
1823
|
+
});
|
|
1824
|
+
var REDACTED_PLACEHOLDER = "[REDACTED]";
|
|
1825
|
+
var PROVIDER_MESSAGE_MAX_CHARS = 300;
|
|
1826
|
+
var ERROR_MESSAGE_MAX_CHARS = 2e3;
|
|
1827
|
+
var SECRET_LITERAL_PATTERNS = [
|
|
1828
|
+
{
|
|
1829
|
+
re: /\b(github_pat_)[A-Za-z0-9_]{16,}/g
|
|
1830
|
+
},
|
|
1831
|
+
{
|
|
1832
|
+
re: /\b(gh[pousr]_)[A-Za-z0-9]{16,}/g
|
|
1833
|
+
},
|
|
1834
|
+
{
|
|
1835
|
+
re: /\b(glpat-)[A-Za-z0-9_-]{16,}/g
|
|
1836
|
+
},
|
|
1837
|
+
// LUA-696 review (3): the npm granular / classic token (`npm_` + 36 alphanumerics).
|
|
1838
|
+
{
|
|
1839
|
+
re: /\b(npm_)[A-Za-z0-9]{36}\b/g
|
|
1840
|
+
},
|
|
1841
|
+
{
|
|
1842
|
+
re: /\b(sk-ant-)[A-Za-z0-9_-]{16,}/g
|
|
1843
|
+
},
|
|
1844
|
+
{
|
|
1845
|
+
re: /\b(sk-)(?!ant-)[A-Za-z0-9_-]{20,}/g
|
|
1846
|
+
},
|
|
1847
|
+
{
|
|
1848
|
+
re: /\b(AKIA)[A-Z0-9]{16}\b/g
|
|
1849
|
+
},
|
|
1850
|
+
{
|
|
1851
|
+
re: /\b(xox[abprs]-)[A-Za-z0-9-]{10,}/g
|
|
1852
|
+
},
|
|
1853
|
+
{
|
|
1854
|
+
re: /\b(AIza)[0-9A-Za-z_-]{35}/g
|
|
1855
|
+
},
|
|
1856
|
+
{
|
|
1857
|
+
re: /\b(ya29\.)[A-Za-z0-9_-]{20,}/g
|
|
1858
|
+
},
|
|
1859
|
+
{
|
|
1860
|
+
re: /\b(eyJ)[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}/g
|
|
1861
|
+
},
|
|
1862
|
+
{
|
|
1863
|
+
re: /(-----BEGIN [A-Z ]*PRIVATE KEY-----)[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g
|
|
1864
|
+
},
|
|
1865
|
+
// `https://x-access-token:<token>@github.com/…` (the git credential shape the pod scrubbed already)
|
|
1866
|
+
{
|
|
1867
|
+
re: /\b(x-access-token:)[^@\s]+(@)/g,
|
|
1868
|
+
suffix: true
|
|
1869
|
+
}
|
|
1870
|
+
];
|
|
1871
|
+
var SECRET_NAME = "(?:authorization|proxy-authorization|x-api-key|x-wf-[a-z-]*key|x-internal-auth|api[_-]?key|apikey|access[_-]?key|secret[_-]?key|private[_-]?key|client[_-]?secret|secret|password|passwd|pwd|passphrase|token|credentials?)";
|
|
1872
|
+
var SECRET_PAIR_PATTERNS = [
|
|
1873
|
+
// `Authorization: Bearer x`, `x-api-key: x`, `AWS_SECRET_ACCESS_KEY=x`, `SVC_JOB_KEY=x`, `"apiKey": "x"`,
|
|
1874
|
+
// `FOO_TOKEN=x`, `secretKey=x`, `password=x`. Groups: the char before the name, the name, the separator
|
|
1875
|
+
// (with its quotes), the scheme word — all kept; the value goes. A value a literal rule already replaced
|
|
1876
|
+
// (`x-access-token:[REDACTED]@host`) is left alone so the host after it survives.
|
|
1877
|
+
// LUA-696: the second lookahead keeps a scrubbed `Authorization: Bearer [REDACTED]` as it is — without it the
|
|
1878
|
+
// optional scheme group backtracks to empty and `Bearer` itself becomes the value (`[REDACTED] [REDACTED]`).
|
|
1879
|
+
// The scrub is applied more than once on purpose (the Job site at its copy, the outcome table at the row
|
|
1880
|
+
// write, lua-api on the way out), so it must be idempotent.
|
|
1881
|
+
// LUA-696 review (HIGH): the identifier prefix is BOUNDED (`{1,64}`) — unbounded, `[A-Za-z0-9-]+` made the
|
|
1882
|
+
// scan quadratic on `-`-heavy text (100 KB of `-` took 12.7 s on the lua-core event loop; a worker-tier step
|
|
1883
|
+
// can throw that). No identifier that names a credential is longer; the probe list is unchanged.
|
|
1884
|
+
{
|
|
1885
|
+
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")
|
|
1886
|
+
},
|
|
1887
|
+
// `?token=x`, `&key=x`, `&X-Amz-Signature=x`, `&sig=x`
|
|
1888
|
+
{
|
|
1889
|
+
re: /([?&](?:token|key|api[_-]?key|apikey|access[_-]?token|id[_-]?token|auth|sig|signature|secret|password|pwd|x-amz-signature|x-amz-credential|x-amz-security-token)=)[^&\s"'#]+/gi
|
|
1890
|
+
},
|
|
1891
|
+
// `mongodb+srv://user:pass@host`, `postgres://user:pass@host`, `https://user:pass@host`
|
|
1892
|
+
{
|
|
1893
|
+
re: /(\/\/[^\s/:@]+:)[^\s/@]+(@)/g,
|
|
1894
|
+
suffix: true
|
|
1895
|
+
},
|
|
1896
|
+
// `Basic <base64>` / `bearer <short token>` (the literal list needs ≥ 16 chars; any case)
|
|
1897
|
+
{
|
|
1898
|
+
re: /\b((?:Basic|Bearer)\s+)(?!\[REDACTED\])[A-Za-z0-9+/=_.-]{8,}/gi
|
|
1899
|
+
}
|
|
1900
|
+
];
|
|
1901
|
+
var GROUP_COUNT = /* @__PURE__ */ new WeakMap();
|
|
1902
|
+
function groupCount(re) {
|
|
1903
|
+
let n2 = GROUP_COUNT.get(re);
|
|
1904
|
+
if (n2 === void 0) {
|
|
1905
|
+
n2 = new RegExp(`${re.source}|`, re.flags.replace("g", "")).exec("").length - 1;
|
|
1906
|
+
GROUP_COUNT.set(re, n2);
|
|
1907
|
+
}
|
|
1908
|
+
return n2;
|
|
1909
|
+
}
|
|
1910
|
+
__name(groupCount, "groupCount");
|
|
1911
|
+
__name2(groupCount, "groupCount");
|
|
1912
|
+
var 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;
|
|
1913
|
+
var WORKFLOW_RESERVED_SECRET_KEYS = Object.freeze([
|
|
1914
|
+
"secret",
|
|
1915
|
+
"token",
|
|
1916
|
+
"password",
|
|
1917
|
+
"passwd",
|
|
1918
|
+
"pwd",
|
|
1919
|
+
"passphrase",
|
|
1920
|
+
"api_key",
|
|
1921
|
+
"apikey",
|
|
1922
|
+
"access_key",
|
|
1923
|
+
"secret_key",
|
|
1924
|
+
"private_key",
|
|
1925
|
+
"client_secret",
|
|
1926
|
+
"authorization",
|
|
1927
|
+
"auth_token",
|
|
1928
|
+
"access_token",
|
|
1929
|
+
"id_token",
|
|
1930
|
+
"refresh_token",
|
|
1931
|
+
"session_key",
|
|
1932
|
+
"credential",
|
|
1933
|
+
"credentials"
|
|
1934
|
+
]);
|
|
1935
|
+
function isWorkflowSecretKey(key) {
|
|
1936
|
+
return WORKFLOW_SECRET_KEY_RE.test(key);
|
|
1937
|
+
}
|
|
1938
|
+
__name(isWorkflowSecretKey, "isWorkflowSecretKey");
|
|
1939
|
+
__name2(isWorkflowSecretKey, "isWorkflowSecretKey");
|
|
1940
|
+
function applyPatterns(text, patterns) {
|
|
1941
|
+
let out = text;
|
|
1942
|
+
for (const { re, suffix } of patterns) {
|
|
1943
|
+
re.lastIndex = 0;
|
|
1944
|
+
if (!re.test(out)) continue;
|
|
1945
|
+
re.lastIndex = 0;
|
|
1946
|
+
const groups = groupCount(re);
|
|
1947
|
+
out = out.replace(re, (...args) => {
|
|
1948
|
+
const kept = args.slice(1, 1 + groups).map((g) => typeof g === "string" ? g : "");
|
|
1949
|
+
if (suffix && kept.length > 0) {
|
|
1950
|
+
const tail = kept[kept.length - 1];
|
|
1951
|
+
return `${kept.slice(0, -1).join("")}${REDACTED_PLACEHOLDER}${tail}`;
|
|
1952
|
+
}
|
|
1953
|
+
return `${kept.join("")}${REDACTED_PLACEHOLDER}`;
|
|
1954
|
+
});
|
|
1955
|
+
}
|
|
1956
|
+
return out;
|
|
1957
|
+
}
|
|
1958
|
+
__name(applyPatterns, "applyPatterns");
|
|
1959
|
+
__name2(applyPatterns, "applyPatterns");
|
|
1960
|
+
function scrubSecretText(text) {
|
|
1961
|
+
if (typeof text !== "string" || text.length < 4) return text;
|
|
1962
|
+
return applyPatterns(applyPatterns(text, SECRET_LITERAL_PATTERNS), SECRET_PAIR_PATTERNS);
|
|
1963
|
+
}
|
|
1964
|
+
__name(scrubSecretText, "scrubSecretText");
|
|
1965
|
+
__name2(scrubSecretText, "scrubSecretText");
|
|
1966
|
+
var SCRUB_INPUT_MAX_CHARS = 16 * 1024;
|
|
1967
|
+
var SCRUB_CUT_BACKOFF_CHARS = 256;
|
|
1968
|
+
function boundScrubInput(text, max = SCRUB_INPUT_MAX_CHARS) {
|
|
1969
|
+
if (typeof text !== "string" || text.length <= max) return text;
|
|
1970
|
+
const window = text.slice(Math.max(0, max - SCRUB_CUT_BACKOFF_CHARS), max);
|
|
1971
|
+
const ws = window.search(/\s\S*$/);
|
|
1972
|
+
const cut = ws >= 0 ? max - window.length + ws : max;
|
|
1973
|
+
return `${text.slice(0, cut)}\u2026`;
|
|
1974
|
+
}
|
|
1975
|
+
__name(boundScrubInput, "boundScrubInput");
|
|
1976
|
+
__name2(boundScrubInput, "boundScrubInput");
|
|
1977
|
+
function scrubSecretLines(lines) {
|
|
1978
|
+
return lines.map((l) => typeof l === "string" ? scrubSecretText(boundScrubInput(l)) : l);
|
|
1979
|
+
}
|
|
1980
|
+
__name(scrubSecretLines, "scrubSecretLines");
|
|
1981
|
+
__name2(scrubSecretLines, "scrubSecretLines");
|
|
1982
|
+
function messageText(value3) {
|
|
1983
|
+
if (typeof value3 === "string") return value3;
|
|
1984
|
+
if (value3 instanceof Error) return value3.message;
|
|
1985
|
+
if (value3 && typeof value3 === "object") {
|
|
1986
|
+
const m = value3.message;
|
|
1987
|
+
if (typeof m === "string") return m;
|
|
1988
|
+
try {
|
|
1989
|
+
return JSON.stringify(value3);
|
|
1990
|
+
} catch {
|
|
1991
|
+
return "";
|
|
1992
|
+
}
|
|
1993
|
+
}
|
|
1994
|
+
return value3 === void 0 || value3 === null ? "" : String(value3);
|
|
1995
|
+
}
|
|
1996
|
+
__name(messageText, "messageText");
|
|
1997
|
+
__name2(messageText, "messageText");
|
|
1998
|
+
function scrubProviderMessage(raw, max = PROVIDER_MESSAGE_MAX_CHARS) {
|
|
1999
|
+
const text = boundScrubInput(messageText(raw)).replace(/\s+/g, " ").trim();
|
|
2000
|
+
if (!text) return void 0;
|
|
2001
|
+
const out = scrubSecretText(text);
|
|
2002
|
+
return out.length > max ? `${out.slice(0, max - 1)}\u2026` : out;
|
|
2003
|
+
}
|
|
2004
|
+
__name(scrubProviderMessage, "scrubProviderMessage");
|
|
2005
|
+
__name2(scrubProviderMessage, "scrubProviderMessage");
|
|
2006
|
+
function scrubStepErrorMessage(raw) {
|
|
2007
|
+
return scrubProviderMessage(raw, ERROR_MESSAGE_MAX_CHARS);
|
|
2008
|
+
}
|
|
2009
|
+
__name(scrubStepErrorMessage, "scrubStepErrorMessage");
|
|
2010
|
+
__name2(scrubStepErrorMessage, "scrubStepErrorMessage");
|
|
1602
2011
|
var WORKFLOW_AUDIT_EVENTS = [
|
|
1603
2012
|
// --- definitions / versions / templates (13, 11 §11.11.5) ---
|
|
1604
2013
|
"workflow.published",
|
|
@@ -1810,11 +2219,42 @@ __name(renderTargetsBlock, "renderTargetsBlock");
|
|
|
1810
2219
|
__name2(renderTargetsBlock, "renderTargetsBlock");
|
|
1811
2220
|
|
|
1812
2221
|
// ../workflow-graph/dist/index.mjs
|
|
2222
|
+
import { createHash } from "crypto";
|
|
1813
2223
|
import { z as z4 } from "zod";
|
|
2224
|
+
import { z as z22 } from "zod";
|
|
1814
2225
|
import { createHash as createHash2 } from "crypto";
|
|
1815
2226
|
var __defProp3 = Object.defineProperty;
|
|
1816
2227
|
var __name3 = /* @__PURE__ */ __name((target, value22) => __defProp3(target, "name", { value: value22, configurable: true }), "__name");
|
|
2228
|
+
var SideEffectsSchema = z4.enum(WORKFLOW_SIDE_EFFECTS);
|
|
2229
|
+
var JobResourcesSchema = z4.enum(WORKFLOW_JOB_RESOURCES);
|
|
1817
2230
|
var WORKFLOW_ARM_SUBRUN_ID = "$arm";
|
|
2231
|
+
var WORKSPACE_TEMPLATE_EXPR_RE = /^\$\{\s*(?:initData|input)\.([^}]+?)\s*\}$/;
|
|
2232
|
+
function workspaceTemplatePath(template22) {
|
|
2233
|
+
const key = template22.trim();
|
|
2234
|
+
const expr = WORKSPACE_TEMPLATE_EXPR_RE.exec(key);
|
|
2235
|
+
if (expr) return expr[1].split(".");
|
|
2236
|
+
if (key.includes("${")) return void 0;
|
|
2237
|
+
return key.replace(/^(?:input|initData)\./, "").split(".");
|
|
2238
|
+
}
|
|
2239
|
+
__name(workspaceTemplatePath, "workspaceTemplatePath");
|
|
2240
|
+
__name3(workspaceTemplatePath, "workspaceTemplatePath");
|
|
2241
|
+
function retryBackoffs() {
|
|
2242
|
+
if (!Array.isArray(WORKFLOW_RETRY_BACKOFFS)) {
|
|
2243
|
+
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')");
|
|
2244
|
+
}
|
|
2245
|
+
return WORKFLOW_RETRY_BACKOFFS;
|
|
2246
|
+
}
|
|
2247
|
+
__name(retryBackoffs, "retryBackoffs");
|
|
2248
|
+
__name3(retryBackoffs, "retryBackoffs");
|
|
2249
|
+
var SLEEP_UNTIL_REPLACEMENT = Object.freeze({
|
|
2250
|
+
type: "sleep",
|
|
2251
|
+
duration: 6e4
|
|
2252
|
+
});
|
|
2253
|
+
function sleepUntilUnsupportedMessage(id) {
|
|
2254
|
+
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} }`;
|
|
2255
|
+
}
|
|
2256
|
+
__name(sleepUntilUnsupportedMessage, "sleepUntilUnsupportedMessage");
|
|
2257
|
+
__name3(sleepUntilUnsupportedMessage, "sleepUntilUnsupportedMessage");
|
|
1818
2258
|
var WORKFLOW_CAPS_DEFAULT = Object.freeze({
|
|
1819
2259
|
maxParallelArms: 16,
|
|
1820
2260
|
maxForeachConcurrency: 16,
|
|
@@ -1873,8 +2313,30 @@ function fillSingle(node) {
|
|
|
1873
2313
|
}
|
|
1874
2314
|
__name(fillSingle, "fillSingle");
|
|
1875
2315
|
__name3(fillSingle, "fillSingle");
|
|
2316
|
+
function fillHitl(node) {
|
|
2317
|
+
if (node.type === "approval") {
|
|
2318
|
+
const a = node;
|
|
2319
|
+
if (a.approver === void 0) a.approver = "creator";
|
|
2320
|
+
if (a.timeoutHours === void 0) a.timeoutHours = WORKFLOW_SUSPEND_DEFAULT_TIMEOUT_HOURS;
|
|
2321
|
+
if (a.onTimeout === void 0) a.onTimeout = "deny";
|
|
2322
|
+
if (a.onDeny === void 0) a.onDeny = "continue";
|
|
2323
|
+
if (a.excludeInitiator === void 0) a.excludeInitiator = false;
|
|
2324
|
+
if (a.editable === void 0) a.editable = false;
|
|
2325
|
+
return;
|
|
2326
|
+
}
|
|
2327
|
+
const w = node;
|
|
2328
|
+
if (w.timeoutHours === void 0) w.timeoutHours = WORKFLOW_SUSPEND_DEFAULT_TIMEOUT_HOURS;
|
|
2329
|
+
if (w.onTimeout === void 0) w.onTimeout = "fail";
|
|
2330
|
+
if (w.acceptedSources === void 0) w.acceptedSources = [
|
|
2331
|
+
...WORKFLOW_SIGNAL_DEFAULT_SOURCES
|
|
2332
|
+
];
|
|
2333
|
+
}
|
|
2334
|
+
__name(fillHitl, "fillHitl");
|
|
2335
|
+
__name3(fillHitl, "fillHitl");
|
|
1876
2336
|
function fillArm(arm) {
|
|
1877
|
-
if (arm.type
|
|
2337
|
+
if (arm.type === "mapping") return;
|
|
2338
|
+
if (isHitlNode(arm)) fillHitl(arm);
|
|
2339
|
+
else fillSingle(arm);
|
|
1878
2340
|
}
|
|
1879
2341
|
__name(fillArm, "fillArm");
|
|
1880
2342
|
__name3(fillArm, "fillArm");
|
|
@@ -1887,7 +2349,7 @@ function fillEntry(entry) {
|
|
|
1887
2349
|
fillSingle(entry);
|
|
1888
2350
|
return;
|
|
1889
2351
|
case "parallel":
|
|
1890
|
-
entry.steps.forEach(
|
|
2352
|
+
entry.steps.forEach(fillArm);
|
|
1891
2353
|
return;
|
|
1892
2354
|
case "conditional": {
|
|
1893
2355
|
const c = entry;
|
|
@@ -1901,34 +2363,19 @@ function fillEntry(entry) {
|
|
|
1901
2363
|
f.opts = f.opts ?? {};
|
|
1902
2364
|
if (f.opts.concurrency === void 0) f.opts.concurrency = WORKFLOW_FOREACH_DEFAULT_CONCURRENCY;
|
|
1903
2365
|
if (f.opts.maxItems === void 0) f.opts.maxItems = WORKFLOW_FOREACH_DEFAULT_MAX_ITEMS;
|
|
1904
|
-
|
|
2366
|
+
fillArm(f.step);
|
|
1905
2367
|
return;
|
|
1906
2368
|
}
|
|
1907
2369
|
case "loop": {
|
|
1908
2370
|
const l = entry;
|
|
1909
2371
|
if (l.maxIterations === void 0) l.maxIterations = WORKFLOW_LOOP_DEFAULT_MAX_ITERATIONS;
|
|
1910
|
-
|
|
2372
|
+
fillArm(l.step);
|
|
1911
2373
|
return;
|
|
1912
2374
|
}
|
|
1913
|
-
case "approval":
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
if (a.timeoutHours === void 0) a.timeoutHours = WORKFLOW_SUSPEND_DEFAULT_TIMEOUT_HOURS;
|
|
1917
|
-
if (a.onTimeout === void 0) a.onTimeout = "deny";
|
|
1918
|
-
if (a.onDeny === void 0) a.onDeny = "continue";
|
|
1919
|
-
if (a.excludeInitiator === void 0) a.excludeInitiator = false;
|
|
1920
|
-
if (a.editable === void 0) a.editable = false;
|
|
1921
|
-
return;
|
|
1922
|
-
}
|
|
1923
|
-
case "waitForSignal": {
|
|
1924
|
-
const w = entry;
|
|
1925
|
-
if (w.timeoutHours === void 0) w.timeoutHours = WORKFLOW_SUSPEND_DEFAULT_TIMEOUT_HOURS;
|
|
1926
|
-
if (w.onTimeout === void 0) w.onTimeout = "fail";
|
|
1927
|
-
if (w.acceptedSources === void 0) w.acceptedSources = [
|
|
1928
|
-
...WORKFLOW_SIGNAL_DEFAULT_SOURCES
|
|
1929
|
-
];
|
|
2375
|
+
case "approval":
|
|
2376
|
+
case "waitForSignal":
|
|
2377
|
+
fillHitl(entry);
|
|
1930
2378
|
return;
|
|
1931
|
-
}
|
|
1932
2379
|
case "mapping":
|
|
1933
2380
|
case "sleep":
|
|
1934
2381
|
case "sleepUntil":
|
|
@@ -1944,6 +2391,17 @@ function withDefaultsFilled(g) {
|
|
|
1944
2391
|
}
|
|
1945
2392
|
__name(withDefaultsFilled, "withDefaultsFilled");
|
|
1946
2393
|
__name3(withDefaultsFilled, "withDefaultsFilled");
|
|
2394
|
+
var CONNECTION_ID_HEX_RE = /^[0-9a-f]{24}$/;
|
|
2395
|
+
function isConnectionKeyShaped(value22) {
|
|
2396
|
+
return WORKFLOW_CONNECTION_KEY_RE.test(value22) && !CONNECTION_ID_HEX_RE.test(value22);
|
|
2397
|
+
}
|
|
2398
|
+
__name(isConnectionKeyShaped, "isConnectionKeyShaped");
|
|
2399
|
+
__name3(isConnectionKeyShaped, "isConnectionKeyShaped");
|
|
2400
|
+
function connectionKeyUndeclaredMessage(path, key) {
|
|
2401
|
+
return `${path} '${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`;
|
|
2402
|
+
}
|
|
2403
|
+
__name(connectionKeyUndeclaredMessage, "connectionKeyUndeclaredMessage");
|
|
2404
|
+
__name3(connectionKeyUndeclaredMessage, "connectionKeyUndeclaredMessage");
|
|
1947
2405
|
var WORKFLOW_JOB_TOOLS = [
|
|
1948
2406
|
"shell",
|
|
1949
2407
|
"read",
|
|
@@ -1995,6 +2453,8 @@ var schemaIsArray = /* @__PURE__ */ __name3((schema) => {
|
|
|
1995
2453
|
if (t === void 0) return void 0;
|
|
1996
2454
|
return Array.isArray(t) ? t.includes("array") : t === "array";
|
|
1997
2455
|
}, "schemaIsArray");
|
|
2456
|
+
var isHitlNode = /* @__PURE__ */ __name3((n2) => isWorkflowHitlEntryType(n2.type), "isHitlNode");
|
|
2457
|
+
var isSingleStep = /* @__PURE__ */ __name3((n2) => !isHitlNode(n2), "isSingleStep");
|
|
1998
2458
|
var singleId = /* @__PURE__ */ __name3((s) => s.type === "step" ? s.step.id : s.id, "singleId");
|
|
1999
2459
|
var armId = /* @__PURE__ */ __name3((a) => a.type === "mapping" ? a.id : singleId(a), "armId");
|
|
2000
2460
|
var TEMPLATE_STEP_REF = /\$\{\s*stepResults\.([A-Za-z0-9_\-]+)/g;
|
|
@@ -2005,14 +2465,21 @@ function templateStepRefs(text) {
|
|
|
2005
2465
|
}
|
|
2006
2466
|
__name(templateStepRefs, "templateStepRefs");
|
|
2007
2467
|
__name3(templateStepRefs, "templateStepRefs");
|
|
2008
|
-
function
|
|
2009
|
-
if (!raw) return
|
|
2010
|
-
|
|
2468
|
+
function readMapConfig(raw) {
|
|
2469
|
+
if (!raw) return void 0;
|
|
2470
|
+
if (typeof raw !== "string") return raw;
|
|
2011
2471
|
try {
|
|
2012
|
-
cfg =
|
|
2472
|
+
const cfg = JSON.parse(raw);
|
|
2473
|
+
return cfg && typeof cfg === "object" && !Array.isArray(cfg) ? cfg : void 0;
|
|
2013
2474
|
} catch {
|
|
2014
|
-
return
|
|
2475
|
+
return void 0;
|
|
2015
2476
|
}
|
|
2477
|
+
}
|
|
2478
|
+
__name(readMapConfig, "readMapConfig");
|
|
2479
|
+
__name3(readMapConfig, "readMapConfig");
|
|
2480
|
+
function mapConfigStepRefs(raw) {
|
|
2481
|
+
const cfg = readMapConfig(raw);
|
|
2482
|
+
if (!cfg) return [];
|
|
2016
2483
|
const ids = [];
|
|
2017
2484
|
for (const d of Object.values(cfg)) {
|
|
2018
2485
|
if (!d || typeof d !== "object") continue;
|
|
@@ -2087,6 +2554,43 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2087
2554
|
if (envelopeWorkspace?.backend && envelopeWorkspace.backend !== "ebs" && opts.policy?.workspaceBackends && !opts.policy.workspaceBackends.includes(envelopeWorkspace.backend)) {
|
|
2088
2555
|
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");
|
|
2089
2556
|
}
|
|
2557
|
+
for (const member of [
|
|
2558
|
+
"repo",
|
|
2559
|
+
"ref"
|
|
2560
|
+
]) {
|
|
2561
|
+
const v = envelopeWorkspace?.[member];
|
|
2562
|
+
const tpl = v && typeof v === "object" && typeof v.template === "string" ? v.template : void 0;
|
|
2563
|
+
if (tpl !== void 0 && workspaceTemplatePath(tpl) === void 0) {
|
|
2564
|
+
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}`);
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2567
|
+
const declaredKeys = new Set(opts.connectionKeys ?? []);
|
|
2568
|
+
if (g.connections !== void 0 && !Array.isArray(g.connections)) {
|
|
2569
|
+
err("connection-declaration-invalid", "`connections` must be an array of { key, integrationType }", "connections");
|
|
2570
|
+
}
|
|
2571
|
+
(Array.isArray(g.connections) ? g.connections : []).forEach((c, i) => {
|
|
2572
|
+
const path = `connections.${i}`;
|
|
2573
|
+
const key = c?.key;
|
|
2574
|
+
const integrationType = c?.integrationType;
|
|
2575
|
+
if (typeof key !== "string" || !WORKFLOW_CONNECTION_KEY_RE.test(key)) {
|
|
2576
|
+
err("connection-declaration-invalid", `connections[${i}].key must match ${WORKFLOW_CONNECTION_KEY_RE}`, `${path}.key`);
|
|
2577
|
+
return;
|
|
2578
|
+
}
|
|
2579
|
+
if (declaredKeys.has(key)) {
|
|
2580
|
+
err("connection-declaration-invalid", `connections[${i}].key "${key}" is declared twice`, `${path}.key`);
|
|
2581
|
+
return;
|
|
2582
|
+
}
|
|
2583
|
+
if (typeof integrationType !== "string" || !integrationType.trim()) {
|
|
2584
|
+
err("connection-declaration-invalid", `connections[${i}] ("${key}") needs an integrationType (the catalog slug, e.g. 'github')`, `${path}.integrationType`);
|
|
2585
|
+
return;
|
|
2586
|
+
}
|
|
2587
|
+
declaredKeys.add(key);
|
|
2588
|
+
});
|
|
2589
|
+
const undeclaredKey = /* @__PURE__ */ __name3((ref) => typeof ref === "string" && !declaredKeys.has(ref) && isConnectionKeyShaped(ref) && opts.connectionIds?.has(ref) !== true, "undeclaredKey");
|
|
2590
|
+
const credentialsRef = envelopeWorkspace?.credentialsRef;
|
|
2591
|
+
if (undeclaredKey(credentialsRef)) {
|
|
2592
|
+
err("connection-key-undeclared", connectionKeyUndeclaredMessage("workspace.credentialsRef", credentialsRef), "workspace.credentialsRef");
|
|
2593
|
+
}
|
|
2090
2594
|
const seen = /* @__PURE__ */ new Map();
|
|
2091
2595
|
let nodeCount = 0;
|
|
2092
2596
|
const upstream = /* @__PURE__ */ new Set();
|
|
@@ -2098,12 +2602,27 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2098
2602
|
seen.set(id, path);
|
|
2099
2603
|
}
|
|
2100
2604
|
}, "checkId");
|
|
2605
|
+
const checkPolicyEnums = /* @__PURE__ */ __name3((node, path) => {
|
|
2606
|
+
const id = singleId(node);
|
|
2607
|
+
const check = /* @__PURE__ */ __name3((member, allowed) => {
|
|
2608
|
+
const value22 = node[member];
|
|
2609
|
+
if (value22 === void 0 || typeof value22 === "string" && allowed.includes(value22)) return;
|
|
2610
|
+
err("invalid-envelope", `\`${member}\` must be ${allowed.map((a) => `'${a}'`).join(" | ")} (got ${JSON.stringify(value22)})`, `${path}.${member}`, id);
|
|
2611
|
+
}, "check");
|
|
2612
|
+
check("sideEffects", WORKFLOW_SIDE_EFFECTS);
|
|
2613
|
+
check("jobResources", WORKFLOW_JOB_RESOURCES);
|
|
2614
|
+
}, "checkPolicyEnums");
|
|
2101
2615
|
const checkRetry = /* @__PURE__ */ __name3((node, path) => {
|
|
2102
2616
|
const r = node.retry;
|
|
2103
2617
|
if (!r) return;
|
|
2104
2618
|
const id = singleId(node);
|
|
2105
|
-
|
|
2106
|
-
|
|
2619
|
+
const backoffs = retryBackoffs();
|
|
2620
|
+
if (r.backoff !== void 0 && !backoffs.includes(r.backoff)) {
|
|
2621
|
+
const list = backoffs.map((b) => `'${b}'`).join(" | ");
|
|
2622
|
+
err("backoff-invalid", `retry.backoff must be ${list}`, `${path}.retry.backoff`, id);
|
|
2623
|
+
}
|
|
2624
|
+
if (r.backoffSeconds !== void 0 && r.backoffSeconds < 0) {
|
|
2625
|
+
err("backoff-invalid", "retry.backoffSeconds must be \u2265 0", `${path}.retry.backoffSeconds`, id);
|
|
2107
2626
|
}
|
|
2108
2627
|
if (r.maxBackoffSeconds !== void 0) {
|
|
2109
2628
|
if (r.backoff !== "exponential") {
|
|
@@ -2166,10 +2685,15 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2166
2685
|
}, "checkSpecialistRole");
|
|
2167
2686
|
const checkRequiredConnections = /* @__PURE__ */ __name3((node, path) => {
|
|
2168
2687
|
const required = node.requiredConnections;
|
|
2169
|
-
if (!Array.isArray(required)
|
|
2170
|
-
const
|
|
2688
|
+
if (!Array.isArray(required)) return;
|
|
2689
|
+
const undeclared = required.filter(undeclaredKey);
|
|
2690
|
+
if (undeclared.length) {
|
|
2691
|
+
err("connection-key-undeclared", connectionKeyUndeclaredMessage(`${path}.requiredConnections`, undeclared[0]) + (undeclared.length > 1 ? ` (also undeclared: ${JSON.stringify(undeclared.slice(1))})` : ""), `${path}.requiredConnections`, singleId(node));
|
|
2692
|
+
}
|
|
2693
|
+
if (!opts.connectionIds) return;
|
|
2694
|
+
const unknown = required.filter((c) => typeof c !== "string" || !declaredKeys.has(c) && !opts.connectionIds.has(c));
|
|
2171
2695
|
if (unknown.length) {
|
|
2172
|
-
err("required-connection-unknown", `requiredConnections ${JSON.stringify(unknown)} are
|
|
2696
|
+
err("required-connection-unknown", `requiredConnections ${JSON.stringify(unknown)} are neither declared connections[].key values nor connections the owner can mount`, `${path}.requiredConnections`, singleId(node));
|
|
2173
2697
|
}
|
|
2174
2698
|
}, "checkRequiredConnections");
|
|
2175
2699
|
const checkTier = /* @__PURE__ */ __name3((node, path) => {
|
|
@@ -2247,14 +2771,20 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2247
2771
|
err("container-arm-empty", "a bare mapping arm has nothing to run", `${path}.graph.1`, node.id);
|
|
2248
2772
|
return;
|
|
2249
2773
|
}
|
|
2774
|
+
const inner = body[1];
|
|
2775
|
+
if (isHitlNode(inner)) {
|
|
2776
|
+
err("node-type-unsupported-in-container", workflowHitlArmShapeMessage(inner.type, inner.id, "mapped-arm"), `${path}.graph.1`, inner.id);
|
|
2777
|
+
return;
|
|
2778
|
+
}
|
|
2250
2779
|
upstream.add(singleId(body[1]));
|
|
2251
|
-
checkArm(body[0], `${path}.graph.0`, depth);
|
|
2780
|
+
checkArm(body[0], `${path}.graph.0`, depth, "parallel");
|
|
2252
2781
|
checkSingle(body[1], `${path}.graph.1`, depth);
|
|
2253
2782
|
upstream.add(body[0].id);
|
|
2254
2783
|
upstream.add(singleId(body[1]));
|
|
2255
2784
|
return;
|
|
2256
2785
|
}
|
|
2257
2786
|
checkId(singleId(node), path);
|
|
2787
|
+
checkPolicyEnums(node, path);
|
|
2258
2788
|
checkTimeout(node, path);
|
|
2259
2789
|
checkTier(node, path);
|
|
2260
2790
|
checkRetry(node, path);
|
|
@@ -2279,13 +2809,60 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2279
2809
|
if (node.type === "workflow" && node.kind === "subrun" && depth > caps.maxNestingDepth) {
|
|
2280
2810
|
err("cap-exceeded", `nesting depth ${depth} exceeds ${caps.maxNestingDepth}`, path, node.id);
|
|
2281
2811
|
}
|
|
2812
|
+
if (node.type === "workflow" && node.workflowId !== WORKFLOW_ARM_SUBRUN_ID && typeof g.definition?.id === "string" && node.workflowId === g.definition.id) {
|
|
2813
|
+
err("subrun-cycle", `"${node.id}" starts "${node.workflowId}", which is this workflow itself`, path, node.id);
|
|
2814
|
+
}
|
|
2282
2815
|
for (const ref of nodeStepRefs(node)) {
|
|
2283
2816
|
if (!upstream.has(ref)) {
|
|
2284
2817
|
err("template-reference-unresolved", `"${singleId(node)}" references stepResults.${ref}, which is not upstream`, path, singleId(node));
|
|
2285
2818
|
}
|
|
2286
2819
|
}
|
|
2287
2820
|
}, "checkSingle");
|
|
2288
|
-
const
|
|
2821
|
+
const checkHitl = /* @__PURE__ */ __name3((node, path) => {
|
|
2822
|
+
if (node.type === "waitForSignal") {
|
|
2823
|
+
const w = node;
|
|
2824
|
+
checkId(w.id, path);
|
|
2825
|
+
if (typeof w.signal !== "string" || w.signal.length === 0) err("invalid-envelope", "waitForSignal.signal is required", `${path}.signal`, w.id);
|
|
2826
|
+
return;
|
|
2827
|
+
}
|
|
2828
|
+
const a = node;
|
|
2829
|
+
checkId(a.id, path);
|
|
2830
|
+
if (a.approver === "creator" && a.excludeInitiator === true) {
|
|
2831
|
+
err("approver-excludes-only-candidate", "approver:'creator' with excludeInitiator:true always excludes the only candidate", path, a.id);
|
|
2832
|
+
}
|
|
2833
|
+
if (a.fourEyes !== void 0 && a.editable !== true) {
|
|
2834
|
+
err("four-eyes-requires-editable", "`fourEyes` requires editable:true", `${path}.fourEyes`, a.id);
|
|
2835
|
+
}
|
|
2836
|
+
if ((a.editablePaths !== void 0 || a.editedPayloadSchema !== void 0) && a.editable !== true) {
|
|
2837
|
+
err("editable-path-invalid", "`editablePaths` / `editedPayloadSchema` require editable:true", `${path}.editablePaths`, a.id);
|
|
2838
|
+
}
|
|
2839
|
+
for (const p of a.editablePaths ?? []) {
|
|
2840
|
+
if (!EDITABLE_PATH_RE.test(p)) err("editable-path-invalid", `editablePaths entry "${p}" is outside the seg(.seg)*[*]/[n] grammar`, `${path}.editablePaths`, a.id);
|
|
2841
|
+
}
|
|
2842
|
+
if (Array.isArray(a.onTimeout)) {
|
|
2843
|
+
const chain = a.onTimeout;
|
|
2844
|
+
const hops = chain.filter((h) => typeof h === "object" && h !== null && "escalateTo" in h);
|
|
2845
|
+
if (hops.length > 3) err("escalation-chain-too-long", "an onTimeout chain carries at most 3 escalation hops", `${path}.onTimeout`, a.id);
|
|
2846
|
+
const last = chain[chain.length - 1];
|
|
2847
|
+
if (last === void 0 || typeof last === "object" && last !== null && "escalateTo" in last) {
|
|
2848
|
+
err("escalation-chain-not-terminal", "an onTimeout chain must end in a terminal member", `${path}.onTimeout`, a.id);
|
|
2849
|
+
}
|
|
2850
|
+
}
|
|
2851
|
+
if (typeof a.details === "string") {
|
|
2852
|
+
for (const ref of templateStepRefs(a.details)) {
|
|
2853
|
+
if (!upstream.has(ref)) err("template-reference-unresolved", `"${a.id}" references stepResults.${ref}, which is not upstream`, path, a.id);
|
|
2854
|
+
}
|
|
2855
|
+
}
|
|
2856
|
+
}, "checkHitl");
|
|
2857
|
+
const checkHitlArm = /* @__PURE__ */ __name3((node, path, container) => {
|
|
2858
|
+
if (!workflowContainerRunsHitlArm(container)) {
|
|
2859
|
+
checkId(node.id, path);
|
|
2860
|
+
err("node-type-unsupported-in-container", workflowHitlArmUnsupportedMessage(node.type, node.id, container), path, node.id);
|
|
2861
|
+
return;
|
|
2862
|
+
}
|
|
2863
|
+
checkHitl(node, path);
|
|
2864
|
+
}, "checkHitlArm");
|
|
2865
|
+
const checkArm = /* @__PURE__ */ __name3((arm, path, depth, container) => {
|
|
2289
2866
|
if (arm.type === "mapping") {
|
|
2290
2867
|
checkId(arm.id, path);
|
|
2291
2868
|
for (const ref of nodeStepRefs(arm)) {
|
|
@@ -2293,6 +2870,10 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2293
2870
|
}
|
|
2294
2871
|
return;
|
|
2295
2872
|
}
|
|
2873
|
+
if (isHitlNode(arm)) {
|
|
2874
|
+
checkHitlArm(arm, path, container);
|
|
2875
|
+
return;
|
|
2876
|
+
}
|
|
2296
2877
|
checkSingle(arm, path, depth);
|
|
2297
2878
|
}, "checkArm");
|
|
2298
2879
|
graph.forEach((entry, i) => {
|
|
@@ -2321,6 +2902,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2321
2902
|
case "sleepUntil": {
|
|
2322
2903
|
const s = entry;
|
|
2323
2904
|
checkId(s.id, path);
|
|
2905
|
+
err("node-type-unsupported-by-engine", sleepUntilUnsupportedMessage(s.id), path, s.id);
|
|
2324
2906
|
if (s.date === void 0 === (s.dateFrom === void 0)) {
|
|
2325
2907
|
err("invalid-envelope", "sleepUntil needs exactly one of `date` | `dateFrom`", path, s.id);
|
|
2326
2908
|
}
|
|
@@ -2341,18 +2923,15 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2341
2923
|
err("mapping-placement", "a bare mapping cannot be a parallel arm \u2014 chain it as [map, step]", armPath);
|
|
2342
2924
|
return;
|
|
2343
2925
|
}
|
|
2344
|
-
|
|
2345
|
-
err("approval-inside-container", "approval / waitForSignal are top-level only in v1", armPath);
|
|
2346
|
-
return;
|
|
2347
|
-
}
|
|
2348
|
-
checkSingle(arm, armPath, 1);
|
|
2926
|
+
checkArm(arm, armPath, 1, "parallel");
|
|
2349
2927
|
declared.push(singleId(arm));
|
|
2350
2928
|
});
|
|
2351
|
-
const
|
|
2929
|
+
const executableArms = p.steps.filter(isSingleStep);
|
|
2930
|
+
const worktreeArms = executableArms.filter((a) => a.workspace?.isolation === "worktree");
|
|
2352
2931
|
if (worktreeArms.length > 0 && !p.merge) err("worktree-arms-require-merge", "worktree arms need a `merge` policy", path);
|
|
2353
2932
|
if (worktreeArms.length === 0 && p.merge) err("merge-requires-worktree-arms", "`merge` needs at least one worktree arm", path);
|
|
2354
2933
|
if (worktreeArms.length > WORKFLOW_JOB_MAX_WORKTREE_ARMS) err("worktree-arms-exceed-cap", `at most ${WORKFLOW_JOB_MAX_WORKTREE_ARMS} worktree arms per parallel`, path);
|
|
2355
|
-
const sharedArms =
|
|
2934
|
+
const sharedArms = executableArms.filter((a) => {
|
|
2356
2935
|
const w = workspaceOf(a);
|
|
2357
2936
|
if (!w || w === "inherit" || w.isolation === "worktree") return false;
|
|
2358
2937
|
return !(envelopeWorkspace?.backend === "efs" && w.mount === "ro");
|
|
@@ -2366,14 +2945,14 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2366
2945
|
err("invalid-envelope", "conditional.predicates must match conditional.steps one-to-one", path);
|
|
2367
2946
|
}
|
|
2368
2947
|
(c.predicates ?? []).forEach((p, j) => {
|
|
2369
|
-
if (!
|
|
2948
|
+
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", `${path}.predicates.${j}`);
|
|
2370
2949
|
});
|
|
2371
2950
|
c.steps.forEach((arm, j) => {
|
|
2372
|
-
checkArm(arm, `${path}.steps.${j}`, 1);
|
|
2951
|
+
checkArm(arm, `${path}.steps.${j}`, 1, "conditional");
|
|
2373
2952
|
declared.push(armId(arm));
|
|
2374
2953
|
});
|
|
2375
2954
|
if (c.otherwise) {
|
|
2376
|
-
checkArm(c.otherwise, `${path}.otherwise`, 1);
|
|
2955
|
+
checkArm(c.otherwise, `${path}.otherwise`, 1, "conditional");
|
|
2377
2956
|
declared.push(armId(c.otherwise));
|
|
2378
2957
|
}
|
|
2379
2958
|
break;
|
|
@@ -2396,8 +2975,9 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2396
2975
|
}
|
|
2397
2976
|
if (o.chunk !== void 0) {
|
|
2398
2977
|
const max = o.maxItems ?? WORKFLOW_FOREACH_DEFAULT_MAX_ITEMS;
|
|
2399
|
-
|
|
2400
|
-
|
|
2978
|
+
const size = typeof o.chunk === "number" ? o.chunk : o.chunk.size;
|
|
2979
|
+
if (!Number.isInteger(size) || size < 1 || size > max) {
|
|
2980
|
+
err("chunk-size-invalid", `foreach.chunk.size must be an integer in [1, ${max}]`, typeof o.chunk === "number" ? `${path}.opts.chunk` : `${path}.opts.chunk.size`);
|
|
2401
2981
|
}
|
|
2402
2982
|
}
|
|
2403
2983
|
if (o.rateLimit !== void 0) {
|
|
@@ -2414,12 +2994,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2414
2994
|
const bodyType = f.step.type;
|
|
2415
2995
|
const prevEntry = i > 0 ? graph[i - 1] : void 0;
|
|
2416
2996
|
if (bodyType !== "mapping" && prevEntry?.type === "mapping" && prevEntry.id === `${singleId(f.step)}_items`) {
|
|
2417
|
-
|
|
2418
|
-
try {
|
|
2419
|
-
cfg = JSON.parse(prevEntry.mapConfig);
|
|
2420
|
-
} catch {
|
|
2421
|
-
cfg = void 0;
|
|
2422
|
-
}
|
|
2997
|
+
const cfg = readMapConfig(prevEntry.mapConfig);
|
|
2423
2998
|
const d = cfg?.[""];
|
|
2424
2999
|
let arrayLike;
|
|
2425
3000
|
if (d && "value" in d) arrayLike = Array.isArray(d.value);
|
|
@@ -2431,8 +3006,13 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2431
3006
|
if (arrayLike === false) err("foreach-items-not-array", `foreach({items}) for "${singleId(f.step)}" binds a value that is not an array`, `${path}.step`, singleId(f.step));
|
|
2432
3007
|
}
|
|
2433
3008
|
if (bodyType === "mapping") err("container-arm-empty", "a foreach body needs a step, not a bare mapping", `${path}.step`);
|
|
2434
|
-
else if (
|
|
2435
|
-
|
|
3009
|
+
else if (isHitlNode(f.step)) {
|
|
3010
|
+
if (o.chunk !== void 0) {
|
|
3011
|
+
checkId(f.step.id, `${path}.step`);
|
|
3012
|
+
err("node-type-unsupported-in-container", workflowHitlArmShapeMessage(f.step.type, f.step.id, "chunked-foreach"), `${path}.step`, f.step.id);
|
|
3013
|
+
} else checkHitlArm(f.step, `${path}.step`, "foreach");
|
|
3014
|
+
declared.push(f.step.id);
|
|
3015
|
+
} else {
|
|
2436
3016
|
checkSingle(f.step, `${path}.step`, o.chunk ? 2 : 1);
|
|
2437
3017
|
declared.push(singleId(f.step));
|
|
2438
3018
|
}
|
|
@@ -2446,55 +3026,23 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2446
3026
|
if (l.intervalSeconds !== void 0 && (!Number.isInteger(l.intervalSeconds) || l.intervalSeconds < 1 || l.intervalSeconds > caps.maxLoopIntervalSeconds)) {
|
|
2447
3027
|
err("loop-interval-out-of-range", `loop.intervalSeconds must be an integer in 1..${caps.maxLoopIntervalSeconds}`, `${path}.intervalSeconds`);
|
|
2448
3028
|
}
|
|
2449
|
-
if (!
|
|
3029
|
+
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", `${path}.predicate`);
|
|
2450
3030
|
const bodyType = l.step.type;
|
|
2451
3031
|
if (bodyType === "mapping") err("container-arm-empty", "a loop body needs a step, not a bare mapping", `${path}.step`);
|
|
2452
|
-
else if (
|
|
2453
|
-
|
|
3032
|
+
else if (isHitlNode(l.step)) {
|
|
3033
|
+
checkHitlArm(l.step, `${path}.step`, "loop");
|
|
3034
|
+
declared.push(l.step.id);
|
|
3035
|
+
} else {
|
|
2454
3036
|
checkSingle(l.step, `${path}.step`, 1);
|
|
2455
3037
|
declared.push(singleId(l.step));
|
|
2456
3038
|
}
|
|
2457
3039
|
break;
|
|
2458
3040
|
}
|
|
2459
|
-
case "approval":
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
err("approver-excludes-only-candidate", "approver:'creator' with excludeInitiator:true always excludes the only candidate", path, a.id);
|
|
2464
|
-
}
|
|
2465
|
-
if (a.fourEyes !== void 0 && a.editable !== true) {
|
|
2466
|
-
err("four-eyes-requires-editable", "`fourEyes` requires editable:true", `${path}.fourEyes`, a.id);
|
|
2467
|
-
}
|
|
2468
|
-
if ((a.editablePaths !== void 0 || a.editedPayloadSchema !== void 0) && a.editable !== true) {
|
|
2469
|
-
err("editable-path-invalid", "`editablePaths` / `editedPayloadSchema` require editable:true", `${path}.editablePaths`, a.id);
|
|
2470
|
-
}
|
|
2471
|
-
for (const p of a.editablePaths ?? []) {
|
|
2472
|
-
if (!EDITABLE_PATH_RE.test(p)) err("editable-path-invalid", `editablePaths entry "${p}" is outside the seg(.seg)*[*]/[n] grammar`, `${path}.editablePaths`, a.id);
|
|
2473
|
-
}
|
|
2474
|
-
if (Array.isArray(a.onTimeout)) {
|
|
2475
|
-
const chain = a.onTimeout;
|
|
2476
|
-
const hops = chain.filter((h) => typeof h === "object" && h !== null && "escalateTo" in h);
|
|
2477
|
-
if (hops.length > 3) err("escalation-chain-too-long", "an onTimeout chain carries at most 3 escalation hops", `${path}.onTimeout`, a.id);
|
|
2478
|
-
const last = chain[chain.length - 1];
|
|
2479
|
-
if (last === void 0 || typeof last === "object" && last !== null && "escalateTo" in last) {
|
|
2480
|
-
err("escalation-chain-not-terminal", "an onTimeout chain must end in a terminal member", `${path}.onTimeout`, a.id);
|
|
2481
|
-
}
|
|
2482
|
-
}
|
|
2483
|
-
if (typeof a.details === "string") {
|
|
2484
|
-
for (const ref of templateStepRefs(a.details)) {
|
|
2485
|
-
if (!upstream.has(ref)) err("template-reference-unresolved", `"${a.id}" references stepResults.${ref}, which is not upstream`, path, a.id);
|
|
2486
|
-
}
|
|
2487
|
-
}
|
|
2488
|
-
declared.push(a.id);
|
|
2489
|
-
break;
|
|
2490
|
-
}
|
|
2491
|
-
case "waitForSignal": {
|
|
2492
|
-
const w = entry;
|
|
2493
|
-
checkId(w.id, path);
|
|
2494
|
-
if (typeof w.signal !== "string" || w.signal.length === 0) err("invalid-envelope", "waitForSignal.signal is required", `${path}.signal`, w.id);
|
|
2495
|
-
declared.push(w.id);
|
|
3041
|
+
case "approval":
|
|
3042
|
+
case "waitForSignal":
|
|
3043
|
+
checkHitl(entry, path);
|
|
3044
|
+
declared.push(entry.id);
|
|
2496
3045
|
break;
|
|
2497
|
-
}
|
|
2498
3046
|
default:
|
|
2499
3047
|
err("invalid-envelope", `unknown entry type "${entry.type}"`, path);
|
|
2500
3048
|
}
|
|
@@ -2531,6 +3079,46 @@ function isPredicate(p) {
|
|
|
2531
3079
|
}
|
|
2532
3080
|
__name(isPredicate, "isPredicate");
|
|
2533
3081
|
__name3(isPredicate, "isPredicate");
|
|
3082
|
+
var isPredicateScalar = /* @__PURE__ */ __name3((v) => v === null || typeof v === "string" || typeof v === "number" || typeof v === "boolean", "isPredicateScalar");
|
|
3083
|
+
function isPathOrLiteral(v) {
|
|
3084
|
+
if (typeof v !== "object" || v === null) return false;
|
|
3085
|
+
const r = v;
|
|
3086
|
+
if ("path" in r) return typeof r.path === "string" && r.path.length > 0;
|
|
3087
|
+
return "literal" in r && isPredicateScalar(r.literal);
|
|
3088
|
+
}
|
|
3089
|
+
__name(isPathOrLiteral, "isPathOrLiteral");
|
|
3090
|
+
__name3(isPathOrLiteral, "isPathOrLiteral");
|
|
3091
|
+
function isWellFormedPredicate(p) {
|
|
3092
|
+
if (!isPredicate(p)) return false;
|
|
3093
|
+
const r = p;
|
|
3094
|
+
switch (r.op) {
|
|
3095
|
+
case "eq":
|
|
3096
|
+
case "ne":
|
|
3097
|
+
case "lt":
|
|
3098
|
+
case "lte":
|
|
3099
|
+
case "gt":
|
|
3100
|
+
case "gte":
|
|
3101
|
+
return isPathOrLiteral(r.left) && isPathOrLiteral(r.right);
|
|
3102
|
+
case "in":
|
|
3103
|
+
case "notIn":
|
|
3104
|
+
return isPathOrLiteral(r.value) && Array.isArray(r.set) && r.set.every(isPredicateScalar);
|
|
3105
|
+
case "exists":
|
|
3106
|
+
case "notExists":
|
|
3107
|
+
return typeof r.path === "string" && r.path.length > 0;
|
|
3108
|
+
case "truthy":
|
|
3109
|
+
case "falsy":
|
|
3110
|
+
return isPathOrLiteral(r.value);
|
|
3111
|
+
case "and":
|
|
3112
|
+
case "or":
|
|
3113
|
+
return Array.isArray(r.args) && r.args.every(isWellFormedPredicate);
|
|
3114
|
+
case "not":
|
|
3115
|
+
return isWellFormedPredicate(r.arg);
|
|
3116
|
+
default:
|
|
3117
|
+
return false;
|
|
3118
|
+
}
|
|
3119
|
+
}
|
|
3120
|
+
__name(isWellFormedPredicate, "isWellFormedPredicate");
|
|
3121
|
+
__name3(isWellFormedPredicate, "isWellFormedPredicate");
|
|
2534
3122
|
var GRAPH_HASH_PREFIX = "sha256-cj1:";
|
|
2535
3123
|
function canonicalJson(value22) {
|
|
2536
3124
|
const seen = /* @__PURE__ */ new WeakSet();
|
|
@@ -2575,14 +3163,9 @@ var WorkflowPlanError = class extends Error {
|
|
|
2575
3163
|
this.name = "WorkflowPlanError";
|
|
2576
3164
|
}
|
|
2577
3165
|
};
|
|
2578
|
-
var
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
tool: "tool",
|
|
2582
|
-
workflow: "subrun"
|
|
2583
|
-
};
|
|
2584
|
-
var isSingleStep = /* @__PURE__ */ __name3((e) => e.type === "step" || e.type === "agent" || e.type === "tool" || e.type === "workflow", "isSingleStep");
|
|
2585
|
-
var singleStepId = /* @__PURE__ */ __name3((e) => e.type === "step" ? e.step.id : e.id, "singleStepId");
|
|
3166
|
+
var isArmStep = /* @__PURE__ */ __name3((e) => isWorkflowArmEntryType(e.type), "isArmStep");
|
|
3167
|
+
var armStepId = /* @__PURE__ */ __name3((e) => e.type === "step" ? e.step.id : e.id, "armStepId");
|
|
3168
|
+
var armStepKind = /* @__PURE__ */ __name3((e) => WORKFLOW_ARM_ENTRY_STEP_KINDS[e.type], "armStepKind");
|
|
2586
3169
|
var joinIdOf = /* @__PURE__ */ __name3((entryId) => `${entryId}.join`, "joinIdOf");
|
|
2587
3170
|
var containerIdOf = /* @__PURE__ */ __name3((type, entryIndex) => `${type}@${entryIndex}`, "containerIdOf");
|
|
2588
3171
|
function compilePlan(g) {
|
|
@@ -2598,10 +3181,10 @@ function compilePlan(g) {
|
|
|
2598
3181
|
let prevTails = [];
|
|
2599
3182
|
const foreachJoinToEntry = /* @__PURE__ */ new Map();
|
|
2600
3183
|
g.definition.graph.forEach((entry, entryIndex) => {
|
|
2601
|
-
if (
|
|
2602
|
-
const id =
|
|
3184
|
+
if (isArmStep(entry)) {
|
|
3185
|
+
const id = armStepId(entry);
|
|
2603
3186
|
addNode(id, {
|
|
2604
|
-
kind:
|
|
3187
|
+
kind: armStepKind(entry),
|
|
2605
3188
|
dependsOn: prevTails,
|
|
2606
3189
|
downstream: [],
|
|
2607
3190
|
unsatisfiedDeps: prevTails.length,
|
|
@@ -2626,9 +3209,8 @@ function compilePlan(g) {
|
|
|
2626
3209
|
];
|
|
2627
3210
|
return;
|
|
2628
3211
|
case "sleep":
|
|
2629
|
-
case "sleepUntil":
|
|
2630
|
-
|
|
2631
|
-
const kind = entry.type === "approval" ? "approval" : entry.type;
|
|
3212
|
+
case "sleepUntil": {
|
|
3213
|
+
const kind = entry.type;
|
|
2632
3214
|
addNode(entry.id, {
|
|
2633
3215
|
kind,
|
|
2634
3216
|
dependsOn: prevTails,
|
|
@@ -2641,24 +3223,12 @@ function compilePlan(g) {
|
|
|
2641
3223
|
];
|
|
2642
3224
|
return;
|
|
2643
3225
|
}
|
|
2644
|
-
case "waitForSignal":
|
|
2645
|
-
addNode(entry.id, {
|
|
2646
|
-
kind: "signal",
|
|
2647
|
-
dependsOn: prevTails,
|
|
2648
|
-
downstream: [],
|
|
2649
|
-
unsatisfiedDeps: prevTails.length,
|
|
2650
|
-
entry
|
|
2651
|
-
});
|
|
2652
|
-
prevTails = [
|
|
2653
|
-
entry.id
|
|
2654
|
-
];
|
|
2655
|
-
return;
|
|
2656
3226
|
case "parallel": {
|
|
2657
3227
|
const entryId = containerIdOf("parallel", entryIndex);
|
|
2658
3228
|
const childIds = entry.steps.map((arm) => {
|
|
2659
|
-
const childId =
|
|
3229
|
+
const childId = armStepId(arm);
|
|
2660
3230
|
addNode(childId, {
|
|
2661
|
-
kind:
|
|
3231
|
+
kind: armStepKind(arm),
|
|
2662
3232
|
dependsOn: prevTails,
|
|
2663
3233
|
downstream: [],
|
|
2664
3234
|
unsatisfiedDeps: prevTails.length,
|
|
@@ -2696,9 +3266,9 @@ function compilePlan(g) {
|
|
|
2696
3266
|
node.otherwise
|
|
2697
3267
|
] : node.steps;
|
|
2698
3268
|
const childIds = arms.map((arm) => {
|
|
2699
|
-
const childId = arm.type === "mapping" ? arm.id :
|
|
3269
|
+
const childId = arm.type === "mapping" ? arm.id : armStepId(arm);
|
|
2700
3270
|
addNode(childId, {
|
|
2701
|
-
kind: arm.type === "mapping" ? "map" :
|
|
3271
|
+
kind: arm.type === "mapping" ? "map" : armStepKind(arm),
|
|
2702
3272
|
dependsOn: [
|
|
2703
3273
|
entryId
|
|
2704
3274
|
],
|
|
@@ -3072,7 +3642,16 @@ var WorkflowTemplateError = class extends Error {
|
|
|
3072
3642
|
this.name = "WorkflowTemplateError";
|
|
3073
3643
|
}
|
|
3074
3644
|
};
|
|
3645
|
+
function isMapConfigObject(v) {
|
|
3646
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
3647
|
+
}
|
|
3648
|
+
__name(isMapConfigObject, "isMapConfigObject");
|
|
3649
|
+
__name3(isMapConfigObject, "isMapConfigObject");
|
|
3075
3650
|
function parseMapConfig(raw, stepId) {
|
|
3651
|
+
if (isMapConfigObject(raw)) return raw;
|
|
3652
|
+
if (typeof raw !== "string") {
|
|
3653
|
+
throw new Error(`Stored mapping step "${stepId}" has a mapConfig that is neither a JSON string nor an object.`);
|
|
3654
|
+
}
|
|
3076
3655
|
try {
|
|
3077
3656
|
return JSON.parse(raw);
|
|
3078
3657
|
} catch (e) {
|
|
@@ -3081,6 +3660,13 @@ function parseMapConfig(raw, stepId) {
|
|
|
3081
3660
|
}
|
|
3082
3661
|
__name(parseMapConfig, "parseMapConfig");
|
|
3083
3662
|
__name3(parseMapConfig, "parseMapConfig");
|
|
3663
|
+
function mapConfigWire(raw) {
|
|
3664
|
+
if (typeof raw === "string") return raw;
|
|
3665
|
+
if (isMapConfigObject(raw)) return canonicalJson(raw);
|
|
3666
|
+
return void 0;
|
|
3667
|
+
}
|
|
3668
|
+
__name(mapConfigWire, "mapConfigWire");
|
|
3669
|
+
__name3(mapConfigWire, "mapConfigWire");
|
|
3084
3670
|
var TEMPLATE_PLACEHOLDER = /\$\{([^}]*)\}/g;
|
|
3085
3671
|
var TEMPLATE_NAMESPACES = [
|
|
3086
3672
|
"initData",
|
|
@@ -3281,6 +3867,77 @@ var rows = /* @__PURE__ */ __name3((s, path, page) => ({
|
|
|
3281
3867
|
var fromKnowledge = /* @__PURE__ */ __name3((k) => ({
|
|
3282
3868
|
knowledge: k
|
|
3283
3869
|
}), "fromKnowledge");
|
|
3870
|
+
var CONTINUED_FAILURE_TAG = "continued_failure";
|
|
3871
|
+
var CONTINUED_FAILURE_DEFAULT_CODE = "step_failed";
|
|
3872
|
+
var CONTINUED_FAILURE_OUTPUT_SCHEMA = Object.freeze({
|
|
3873
|
+
type: "object",
|
|
3874
|
+
properties: {
|
|
3875
|
+
__lua_workflow: {
|
|
3876
|
+
type: "string",
|
|
3877
|
+
const: CONTINUED_FAILURE_TAG
|
|
3878
|
+
},
|
|
3879
|
+
failed: {
|
|
3880
|
+
type: "boolean",
|
|
3881
|
+
const: true
|
|
3882
|
+
},
|
|
3883
|
+
error: {
|
|
3884
|
+
type: "object",
|
|
3885
|
+
properties: {
|
|
3886
|
+
code: {
|
|
3887
|
+
type: "string"
|
|
3888
|
+
},
|
|
3889
|
+
message: {
|
|
3890
|
+
type: "string"
|
|
3891
|
+
}
|
|
3892
|
+
},
|
|
3893
|
+
required: [
|
|
3894
|
+
"code",
|
|
3895
|
+
"message"
|
|
3896
|
+
]
|
|
3897
|
+
},
|
|
3898
|
+
text: {
|
|
3899
|
+
type: "string",
|
|
3900
|
+
const: ""
|
|
3901
|
+
}
|
|
3902
|
+
},
|
|
3903
|
+
required: [
|
|
3904
|
+
"__lua_workflow",
|
|
3905
|
+
"failed",
|
|
3906
|
+
"error",
|
|
3907
|
+
"text"
|
|
3908
|
+
]
|
|
3909
|
+
});
|
|
3910
|
+
var CONTINUED_FAILURE_LEAF_PATHS = Object.freeze([
|
|
3911
|
+
"failed",
|
|
3912
|
+
"error",
|
|
3913
|
+
"error.code",
|
|
3914
|
+
"error.message",
|
|
3915
|
+
"text"
|
|
3916
|
+
]);
|
|
3917
|
+
function continuedFailureValue(error, killReason) {
|
|
3918
|
+
const code = typeof error?.code === "string" && error.code || typeof killReason === "string" && killReason || CONTINUED_FAILURE_DEFAULT_CODE;
|
|
3919
|
+
const message = typeof error?.message === "string" && error.message ? error.message : code;
|
|
3920
|
+
return {
|
|
3921
|
+
__lua_workflow: CONTINUED_FAILURE_TAG,
|
|
3922
|
+
failed: true,
|
|
3923
|
+
error: {
|
|
3924
|
+
code,
|
|
3925
|
+
message
|
|
3926
|
+
},
|
|
3927
|
+
text: ""
|
|
3928
|
+
};
|
|
3929
|
+
}
|
|
3930
|
+
__name(continuedFailureValue, "continuedFailureValue");
|
|
3931
|
+
__name3(continuedFailureValue, "continuedFailureValue");
|
|
3932
|
+
function isContinuedFailureValue(v) {
|
|
3933
|
+
if (v === null || typeof v !== "object" || Array.isArray(v)) return false;
|
|
3934
|
+
const o = v;
|
|
3935
|
+
const err = o.error;
|
|
3936
|
+
return o.__lua_workflow === CONTINUED_FAILURE_TAG && o.failed === true && o.text === "" && err !== null && typeof err === "object" && typeof err.code === "string" && typeof err.message === "string";
|
|
3937
|
+
}
|
|
3938
|
+
__name(isContinuedFailureValue, "isContinuedFailureValue");
|
|
3939
|
+
__name3(isContinuedFailureValue, "isContinuedFailureValue");
|
|
3940
|
+
var isHitlNode2 = /* @__PURE__ */ __name3((n2) => isWorkflowHitlEntryType(n2.type), "isHitlNode");
|
|
3284
3941
|
function lowerContainerArm(mapping, step22) {
|
|
3285
3942
|
const stepId = nodeIdOf(step22);
|
|
3286
3943
|
return {
|
|
@@ -3296,7 +3953,7 @@ function lowerContainerArm(mapping, step22) {
|
|
|
3296
3953
|
}
|
|
3297
3954
|
__name(lowerContainerArm, "lowerContainerArm");
|
|
3298
3955
|
__name3(lowerContainerArm, "lowerContainerArm");
|
|
3299
|
-
var nodeIdOf = /* @__PURE__ */ __name3((
|
|
3956
|
+
var nodeIdOf = /* @__PURE__ */ __name3((n2) => n2.type === "step" ? n2.step.id : n2.id, "nodeIdOf");
|
|
3300
3957
|
function entryIds(entry) {
|
|
3301
3958
|
switch (entry.type) {
|
|
3302
3959
|
case "parallel":
|
|
@@ -3366,7 +4023,28 @@ function resolvePlacements(calls) {
|
|
|
3366
4023
|
break;
|
|
3367
4024
|
}
|
|
3368
4025
|
});
|
|
3369
|
-
const
|
|
4026
|
+
const hitlPlacementIssue = /* @__PURE__ */ __name3((node, ref, i, container) => {
|
|
4027
|
+
if (!isHitlNode2(node)) return void 0;
|
|
4028
|
+
const id = node.id;
|
|
4029
|
+
if (ref.armMap) {
|
|
4030
|
+
return {
|
|
4031
|
+
code: "node-type-unsupported-in-container",
|
|
4032
|
+
message: workflowHitlArmShapeMessage(node.type, id, "mapped-arm"),
|
|
4033
|
+
callIndex: i,
|
|
4034
|
+
stepId: id
|
|
4035
|
+
};
|
|
4036
|
+
}
|
|
4037
|
+
if (container !== "place" && !workflowContainerRunsHitlArm(container)) {
|
|
4038
|
+
return {
|
|
4039
|
+
code: "node-type-unsupported-in-container",
|
|
4040
|
+
message: workflowHitlArmUnsupportedMessage(node.type, id, container),
|
|
4041
|
+
callIndex: i,
|
|
4042
|
+
stepId: id
|
|
4043
|
+
};
|
|
4044
|
+
}
|
|
4045
|
+
return void 0;
|
|
4046
|
+
}, "hitlPlacementIssue");
|
|
4047
|
+
const resolve = /* @__PURE__ */ __name3((ref, i, allowMapping, container) => {
|
|
3370
4048
|
if ("node" in ref) {
|
|
3371
4049
|
if (ref.node.type === "mapping" && !allowMapping) {
|
|
3372
4050
|
issues.push({
|
|
@@ -3383,7 +4061,7 @@ function resolvePlacements(calls) {
|
|
|
3383
4061
|
if (!d) {
|
|
3384
4062
|
issues.push({
|
|
3385
4063
|
code: "unknown-step-ref",
|
|
3386
|
-
message: `"${ref.ref}" is not declared anywhere in the chain \u2014 declare it with agentStep/specialistStep/toolStep/map(\u2026, { id })`,
|
|
4064
|
+
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)`,
|
|
3387
4065
|
callIndex: i,
|
|
3388
4066
|
stepId: ref.ref
|
|
3389
4067
|
});
|
|
@@ -3398,6 +4076,11 @@ function resolvePlacements(calls) {
|
|
|
3398
4076
|
});
|
|
3399
4077
|
return void 0;
|
|
3400
4078
|
}
|
|
4079
|
+
const hitl = hitlPlacementIssue(d.node, ref, i, container);
|
|
4080
|
+
if (hitl) {
|
|
4081
|
+
issues.push(hitl);
|
|
4082
|
+
return void 0;
|
|
4083
|
+
}
|
|
3401
4084
|
const prior = placedBy.get(ref.ref);
|
|
3402
4085
|
if (prior !== void 0 && prior !== i) {
|
|
3403
4086
|
issues.push({
|
|
@@ -3411,23 +4094,31 @@ function resolvePlacements(calls) {
|
|
|
3411
4094
|
placedBy.set(ref.ref, i);
|
|
3412
4095
|
return d.node;
|
|
3413
4096
|
}, "resolve");
|
|
4097
|
+
const claim = /* @__PURE__ */ __name3((ref, i, allowMapping, container) => {
|
|
4098
|
+
if ("ref" in ref) {
|
|
4099
|
+
resolve(ref, i, allowMapping, container);
|
|
4100
|
+
return;
|
|
4101
|
+
}
|
|
4102
|
+
const hitl = hitlPlacementIssue(ref.node, ref, i, container);
|
|
4103
|
+
if (hitl) issues.push(hitl);
|
|
4104
|
+
}, "claim");
|
|
3414
4105
|
calls.forEach((call, i) => {
|
|
3415
4106
|
switch (call.kind) {
|
|
3416
4107
|
case "place":
|
|
3417
|
-
|
|
4108
|
+
claim({
|
|
3418
4109
|
ref: call.ref
|
|
3419
|
-
}, i, false);
|
|
4110
|
+
}, i, false, "place");
|
|
3420
4111
|
break;
|
|
3421
4112
|
case "parallel":
|
|
3422
|
-
for (const arm of call.arms)
|
|
4113
|
+
for (const arm of call.arms) claim(arm, i, false, "parallel");
|
|
3423
4114
|
break;
|
|
3424
4115
|
case "conditional":
|
|
3425
|
-
for (const a of call.arms)
|
|
3426
|
-
if (call.otherwise
|
|
4116
|
+
for (const a of call.arms) claim(a.target, i, true, "conditional");
|
|
4117
|
+
if (call.otherwise) claim(call.otherwise, i, true, "conditional");
|
|
3427
4118
|
break;
|
|
3428
4119
|
case "foreach":
|
|
3429
4120
|
case "loop":
|
|
3430
|
-
|
|
4121
|
+
claim(call.body, i, false, call.kind);
|
|
3431
4122
|
break;
|
|
3432
4123
|
default:
|
|
3433
4124
|
break;
|
|
@@ -3435,9 +4126,9 @@ function resolvePlacements(calls) {
|
|
|
3435
4126
|
});
|
|
3436
4127
|
const graph = [];
|
|
3437
4128
|
const lookup = /* @__PURE__ */ __name3((ref) => {
|
|
3438
|
-
const
|
|
3439
|
-
if (!
|
|
3440
|
-
return lowerContainerArm(ref.armMap,
|
|
4129
|
+
const n2 = "node" in ref ? ref.node : declared.get(ref.ref)?.node;
|
|
4130
|
+
if (!n2 || !ref.armMap || n2.type === "mapping" || isHitlNode2(n2)) return n2;
|
|
4131
|
+
return lowerContainerArm(ref.armMap, n2);
|
|
3441
4132
|
}, "lookup");
|
|
3442
4133
|
calls.forEach((call, i) => {
|
|
3443
4134
|
switch (call.kind) {
|
|
@@ -3456,7 +4147,7 @@ function resolvePlacements(calls) {
|
|
|
3456
4147
|
return;
|
|
3457
4148
|
}
|
|
3458
4149
|
case "parallel": {
|
|
3459
|
-
const steps = call.arms.map(lookup).filter((
|
|
4150
|
+
const steps = call.arms.map(lookup).filter((n2) => !!n2 && n2.type !== "mapping");
|
|
3460
4151
|
const node = {
|
|
3461
4152
|
type: "parallel",
|
|
3462
4153
|
steps
|
|
@@ -3469,9 +4160,9 @@ function resolvePlacements(calls) {
|
|
|
3469
4160
|
const steps = [];
|
|
3470
4161
|
const predicates = [];
|
|
3471
4162
|
for (const a of call.arms) {
|
|
3472
|
-
const
|
|
3473
|
-
if (!
|
|
3474
|
-
steps.push(
|
|
4163
|
+
const n2 = lookup(a.target);
|
|
4164
|
+
if (!n2) continue;
|
|
4165
|
+
steps.push(n2);
|
|
3475
4166
|
predicates.push(a.predicate);
|
|
3476
4167
|
}
|
|
3477
4168
|
const node = {
|
|
@@ -3518,6 +4209,75 @@ function resolvePlacements(calls) {
|
|
|
3518
4209
|
}
|
|
3519
4210
|
__name(resolvePlacements, "resolvePlacements");
|
|
3520
4211
|
__name3(resolvePlacements, "resolvePlacements");
|
|
4212
|
+
var GOAL_JUDGE_STEP_ID = "__goal_judge";
|
|
4213
|
+
var NON_LEAF_KINDS = /* @__PURE__ */ new Set([
|
|
4214
|
+
"foreach",
|
|
4215
|
+
"branch"
|
|
4216
|
+
]);
|
|
4217
|
+
var CONDITIONAL_JOIN_ID = /^conditional@\d+\.join$/;
|
|
4218
|
+
function isConditionalJoinId(stepId) {
|
|
4219
|
+
return CONDITIONAL_JOIN_ID.test(stepId);
|
|
4220
|
+
}
|
|
4221
|
+
__name(isConditionalJoinId, "isConditionalJoinId");
|
|
4222
|
+
__name3(isConditionalJoinId, "isConditionalJoinId");
|
|
4223
|
+
function isPlainObject(v) {
|
|
4224
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
4225
|
+
}
|
|
4226
|
+
__name(isPlainObject, "isPlainObject");
|
|
4227
|
+
__name3(isPlainObject, "isPlainObject");
|
|
4228
|
+
function leafValue(row) {
|
|
4229
|
+
if (row.status === "completed") return row.output === void 0 ? null : row.output;
|
|
4230
|
+
return continuedFailureValue(row.error, row.killReason);
|
|
4231
|
+
}
|
|
4232
|
+
__name(leafValue, "leafValue");
|
|
4233
|
+
__name3(leafValue, "leafValue");
|
|
4234
|
+
function runOutputLeaves(steps) {
|
|
4235
|
+
const dependedOn = /* @__PURE__ */ new Set();
|
|
4236
|
+
for (const s of steps) {
|
|
4237
|
+
if (s.stepId === GOAL_JUDGE_STEP_ID) continue;
|
|
4238
|
+
for (const d of s.dependsOn ?? []) dependedOn.add(d);
|
|
4239
|
+
}
|
|
4240
|
+
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));
|
|
4241
|
+
}
|
|
4242
|
+
__name(runOutputLeaves, "runOutputLeaves");
|
|
4243
|
+
__name3(runOutputLeaves, "runOutputLeaves");
|
|
4244
|
+
function deriveRunOutput(steps) {
|
|
4245
|
+
const leaves = runOutputLeaves(steps);
|
|
4246
|
+
if (leaves.length === 0) return void 0;
|
|
4247
|
+
if (leaves.length === 1) {
|
|
4248
|
+
const leaf = leaves[0];
|
|
4249
|
+
const value22 = leafValue(leaf);
|
|
4250
|
+
if (isConditionalJoinId(leaf.stepId) && isPlainObject(value22)) {
|
|
4251
|
+
const keys = Object.keys(value22);
|
|
4252
|
+
if (keys.length === 1) return {
|
|
4253
|
+
output: value22[keys[0]],
|
|
4254
|
+
leafIds: [
|
|
4255
|
+
keys[0]
|
|
4256
|
+
]
|
|
4257
|
+
};
|
|
4258
|
+
}
|
|
4259
|
+
return {
|
|
4260
|
+
output: value22,
|
|
4261
|
+
leafIds: [
|
|
4262
|
+
leaf.stepId
|
|
4263
|
+
]
|
|
4264
|
+
};
|
|
4265
|
+
}
|
|
4266
|
+
const output = {};
|
|
4267
|
+
for (const leaf of leaves) output[leaf.stepId] = leafValue(leaf);
|
|
4268
|
+
return {
|
|
4269
|
+
output,
|
|
4270
|
+
leafIds: leaves.map((l) => l.stepId)
|
|
4271
|
+
};
|
|
4272
|
+
}
|
|
4273
|
+
__name(deriveRunOutput, "deriveRunOutput");
|
|
4274
|
+
__name3(deriveRunOutput, "deriveRunOutput");
|
|
4275
|
+
function subrunSettledOutput(child) {
|
|
4276
|
+
if (child.output !== void 0) return child.output;
|
|
4277
|
+
return deriveRunOutput(child.steps ?? [])?.output ?? null;
|
|
4278
|
+
}
|
|
4279
|
+
__name(subrunSettledOutput, "subrunSettledOutput");
|
|
4280
|
+
__name3(subrunSettledOutput, "subrunSettledOutput");
|
|
3521
4281
|
function seedLedgerFromRun(run, steps, targetPlan, opts = {}) {
|
|
3522
4282
|
const byId = /* @__PURE__ */ new Map();
|
|
3523
4283
|
for (const s of steps) {
|
|
@@ -3579,7 +4339,35 @@ function seedLedgerFromRun(run, steps, targetPlan, opts = {}) {
|
|
|
3579
4339
|
}
|
|
3580
4340
|
__name(seedLedgerFromRun, "seedLedgerFromRun");
|
|
3581
4341
|
__name3(seedLedgerFromRun, "seedLedgerFromRun");
|
|
3582
|
-
var
|
|
4342
|
+
var branchArmId = /* @__PURE__ */ __name3((arm) => arm.type === "step" ? arm.step.id : arm.id, "branchArmId");
|
|
4343
|
+
function branchSpecFromConditional(entry) {
|
|
4344
|
+
return {
|
|
4345
|
+
arms: entry.steps.map((arm, i) => ({
|
|
4346
|
+
stepId: branchArmId(arm),
|
|
4347
|
+
predicate: entry.predicates[i]
|
|
4348
|
+
})),
|
|
4349
|
+
...entry.exclusive === true ? {
|
|
4350
|
+
exclusive: true
|
|
4351
|
+
} : {},
|
|
4352
|
+
...entry.otherwise ? {
|
|
4353
|
+
otherwise: branchArmId(entry.otherwise)
|
|
4354
|
+
} : {}
|
|
4355
|
+
};
|
|
4356
|
+
}
|
|
4357
|
+
__name(branchSpecFromConditional, "branchSpecFromConditional");
|
|
4358
|
+
__name3(branchSpecFromConditional, "branchSpecFromConditional");
|
|
4359
|
+
function selectBranchArms(spec, ctx) {
|
|
4360
|
+
const taken = [];
|
|
4361
|
+
for (const arm of spec.arms) {
|
|
4362
|
+
if (spec.exclusive && taken.length > 0) break;
|
|
4363
|
+
const hit = arm.predicate ? evaluatePredicate(arm.predicate, ctx) : false;
|
|
4364
|
+
if (hit) taken.push(arm.stepId);
|
|
4365
|
+
}
|
|
4366
|
+
if (taken.length === 0 && spec.otherwise) taken.push(spec.otherwise);
|
|
4367
|
+
return taken;
|
|
4368
|
+
}
|
|
4369
|
+
__name(selectBranchArms, "selectBranchArms");
|
|
4370
|
+
__name3(selectBranchArms, "selectBranchArms");
|
|
3583
4371
|
var canonical = /* @__PURE__ */ __name3((v) => JSON.stringify(sortKeys(v)), "canonical");
|
|
3584
4372
|
var sortKeys = /* @__PURE__ */ __name3((v) => {
|
|
3585
4373
|
if (Array.isArray(v)) return v.map(sortKeys);
|
|
@@ -3597,25 +4385,24 @@ function replayLedger(g, ledger) {
|
|
|
3597
4385
|
r.stepId,
|
|
3598
4386
|
r
|
|
3599
4387
|
]));
|
|
3600
|
-
const
|
|
3601
|
-
|
|
3602
|
-
|
|
4388
|
+
const requestContext = {
|
|
4389
|
+
runId: "replay",
|
|
4390
|
+
workflowId: g.definition.id,
|
|
4391
|
+
orgId: "",
|
|
4392
|
+
agentId: "",
|
|
4393
|
+
userId: "",
|
|
4394
|
+
trigger: "sdk",
|
|
4395
|
+
threadId: "replay",
|
|
4396
|
+
depth: 0,
|
|
4397
|
+
startedAt: 0,
|
|
4398
|
+
...ledger.requestContext
|
|
4399
|
+
};
|
|
4400
|
+
const ctxFor = /* @__PURE__ */ __name3((id) => ({
|
|
3603
4401
|
initData: ledger.initData,
|
|
3604
|
-
stepResults,
|
|
4402
|
+
stepResults: ancestorResults(plan, id, rows22),
|
|
3605
4403
|
state: ledger.state ?? {},
|
|
3606
|
-
requestContext
|
|
3607
|
-
|
|
3608
|
-
workflowId: g.definition.id,
|
|
3609
|
-
orgId: "",
|
|
3610
|
-
agentId: "",
|
|
3611
|
-
userId: "",
|
|
3612
|
-
trigger: "sdk",
|
|
3613
|
-
threadId: "replay",
|
|
3614
|
-
depth: 0,
|
|
3615
|
-
startedAt: 0,
|
|
3616
|
-
...ledger.requestContext
|
|
3617
|
-
}
|
|
3618
|
-
};
|
|
4404
|
+
requestContext
|
|
4405
|
+
}), "ctxFor");
|
|
3619
4406
|
const verdicts = [];
|
|
3620
4407
|
for (const id of plan.order) {
|
|
3621
4408
|
const node = plan.steps[id];
|
|
@@ -3623,17 +4410,7 @@ function replayLedger(g, ledger) {
|
|
|
3623
4410
|
if (!recorded || recorded.status === "pending" || recorded.status === "skipped") continue;
|
|
3624
4411
|
if (node.kind === "branch") {
|
|
3625
4412
|
const entry = node.entry;
|
|
3626
|
-
const taken =
|
|
3627
|
-
entry.steps.forEach((arm, i) => {
|
|
3628
|
-
const pred = entry.predicates[i];
|
|
3629
|
-
const hit = pred ? evaluatePredicate(pred, {
|
|
3630
|
-
initData: ledger.initData,
|
|
3631
|
-
stepResults,
|
|
3632
|
-
state: ledger.state
|
|
3633
|
-
}) : false;
|
|
3634
|
-
if (hit && (!entry.exclusive || taken.length === 0)) taken.push(armId2(arm));
|
|
3635
|
-
});
|
|
3636
|
-
if (taken.length === 0 && entry.otherwise) taken.push(armId2(entry.otherwise));
|
|
4413
|
+
const taken = selectBranchArms(branchSpecFromConditional(entry), ctxFor(id));
|
|
3637
4414
|
const recordedTaken = recorded.taken ?? inferTaken(entry, rows22);
|
|
3638
4415
|
verdicts.push({
|
|
3639
4416
|
stepId: id,
|
|
@@ -3644,7 +4421,7 @@ function replayLedger(g, ledger) {
|
|
|
3644
4421
|
});
|
|
3645
4422
|
} else if (node.kind === "map" && !id.endsWith(".join") && recorded.status === "completed") {
|
|
3646
4423
|
const entry = node.entry;
|
|
3647
|
-
const resolved = resolveMapping(parseMapConfig(entry.mapConfig, id),
|
|
4424
|
+
const resolved = resolveMapping(parseMapConfig(entry.mapConfig, id), ctxFor(id));
|
|
3648
4425
|
const local = "error" in resolved ? {
|
|
3649
4426
|
error: resolved.error,
|
|
3650
4427
|
key: resolved.key
|
|
@@ -3659,7 +4436,7 @@ function replayLedger(g, ledger) {
|
|
|
3659
4436
|
} else if (node.kind === "foreach") {
|
|
3660
4437
|
const entry = node.entry;
|
|
3661
4438
|
const source = node.dependsOn[0];
|
|
3662
|
-
const items = source ? stepResults[source] : void 0;
|
|
4439
|
+
const items = source ? ctxFor(id).stepResults[source] : void 0;
|
|
3663
4440
|
const local = Array.isArray(items) ? items.length : void 0;
|
|
3664
4441
|
const recordedCount = recorded.itemCount ?? countChildren(entry, rows22);
|
|
3665
4442
|
verdicts.push({
|
|
@@ -3669,6 +4446,15 @@ function replayLedger(g, ledger) {
|
|
|
3669
4446
|
local,
|
|
3670
4447
|
diverged: recordedCount !== local
|
|
3671
4448
|
});
|
|
4449
|
+
} else if (node.kind === "subrun" && recorded.status === "completed" && recorded.child) {
|
|
4450
|
+
const local = subrunSettledOutput(recorded.child);
|
|
4451
|
+
verdicts.push({
|
|
4452
|
+
stepId: id,
|
|
4453
|
+
kind: "subrun",
|
|
4454
|
+
recorded: recorded.output,
|
|
4455
|
+
local,
|
|
4456
|
+
diverged: canonical(recorded.output) !== canonical(local)
|
|
4457
|
+
});
|
|
3672
4458
|
}
|
|
3673
4459
|
}
|
|
3674
4460
|
return {
|
|
@@ -3678,6 +4464,77 @@ function replayLedger(g, ledger) {
|
|
|
3678
4464
|
}
|
|
3679
4465
|
__name(replayLedger, "replayLedger");
|
|
3680
4466
|
__name3(replayLedger, "replayLedger");
|
|
4467
|
+
var JOIN = ".join";
|
|
4468
|
+
var entryOfJoin = /* @__PURE__ */ __name3((id) => id.endsWith(JOIN) ? id.slice(0, -JOIN.length) : void 0, "entryOfJoin");
|
|
4469
|
+
function replayResultOf(row, node) {
|
|
4470
|
+
if (!row) return void 0;
|
|
4471
|
+
if (row.status === "completed") return {
|
|
4472
|
+
value: row.output
|
|
4473
|
+
};
|
|
4474
|
+
if (row.status === "failed") {
|
|
4475
|
+
const onError = row.onError ?? node?.entry?.onError;
|
|
4476
|
+
if (onError === "continue") return {
|
|
4477
|
+
value: continuedFailureValue(row.error, row.killReason)
|
|
4478
|
+
};
|
|
4479
|
+
}
|
|
4480
|
+
return void 0;
|
|
4481
|
+
}
|
|
4482
|
+
__name(replayResultOf, "replayResultOf");
|
|
4483
|
+
__name3(replayResultOf, "replayResultOf");
|
|
4484
|
+
function ancestorResults(plan, id, rows22) {
|
|
4485
|
+
const out = {};
|
|
4486
|
+
const joinAliased = /* @__PURE__ */ new Set();
|
|
4487
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4488
|
+
const take = /* @__PURE__ */ __name3((rowId) => {
|
|
4489
|
+
const hit = replayResultOf(rows22.get(rowId), plan.steps[rowId]);
|
|
4490
|
+
if (!hit) return void 0;
|
|
4491
|
+
if (!joinAliased.has(rowId)) out[rowId] = hit.value;
|
|
4492
|
+
const entryId = entryOfJoin(rowId);
|
|
4493
|
+
if (entryId) {
|
|
4494
|
+
out[entryId] = hit.value;
|
|
4495
|
+
joinAliased.add(entryId);
|
|
4496
|
+
const entryNode = plan.steps[entryId];
|
|
4497
|
+
if (entryNode?.kind === "foreach") {
|
|
4498
|
+
const body = branchArmId(entryNode.entry.step);
|
|
4499
|
+
out[body] = hit.value;
|
|
4500
|
+
joinAliased.add(body);
|
|
4501
|
+
}
|
|
4502
|
+
}
|
|
4503
|
+
return hit;
|
|
4504
|
+
}, "take");
|
|
4505
|
+
const walk2 = /* @__PURE__ */ __name3((ids) => {
|
|
4506
|
+
for (const dep of ids) {
|
|
4507
|
+
if (seen.has(dep)) continue;
|
|
4508
|
+
seen.add(dep);
|
|
4509
|
+
take(dep);
|
|
4510
|
+
const node = plan.steps[dep] ?? plan.steps[entryOfJoin(dep) ?? ""];
|
|
4511
|
+
if (!node) continue;
|
|
4512
|
+
if (node.kind === "foreach") {
|
|
4513
|
+
const body = branchArmId(node.entry.step);
|
|
4514
|
+
for (const rowId of rows22.keys()) if (rowId.startsWith(`${body}[`)) take(rowId);
|
|
4515
|
+
} else if (node.kind === "loop") {
|
|
4516
|
+
const body = branchArmId(node.entry.step);
|
|
4517
|
+
let best = -1;
|
|
4518
|
+
for (const rowId of rows22.keys()) {
|
|
4519
|
+
if (!rowId.startsWith(`${body}#`)) continue;
|
|
4520
|
+
const n2 = Number(rowId.slice(body.length + 1));
|
|
4521
|
+
if (!Number.isInteger(n2)) continue;
|
|
4522
|
+
const hit = take(rowId);
|
|
4523
|
+
if (!hit) continue;
|
|
4524
|
+
if (n2 > best) {
|
|
4525
|
+
best = n2;
|
|
4526
|
+
out[body] = hit.value;
|
|
4527
|
+
}
|
|
4528
|
+
}
|
|
4529
|
+
}
|
|
4530
|
+
walk2(node.dependsOn);
|
|
4531
|
+
}
|
|
4532
|
+
}, "walk");
|
|
4533
|
+
walk2(plan.steps[id]?.dependsOn ?? []);
|
|
4534
|
+
return out;
|
|
4535
|
+
}
|
|
4536
|
+
__name(ancestorResults, "ancestorResults");
|
|
4537
|
+
__name3(ancestorResults, "ancestorResults");
|
|
3681
4538
|
function inferTaken(entry, rows22) {
|
|
3682
4539
|
const arms = [
|
|
3683
4540
|
...entry.steps,
|
|
@@ -3685,7 +4542,7 @@ function inferTaken(entry, rows22) {
|
|
|
3685
4542
|
entry.otherwise
|
|
3686
4543
|
] : []
|
|
3687
4544
|
];
|
|
3688
|
-
return arms.map(
|
|
4545
|
+
return arms.map(branchArmId).filter((id) => {
|
|
3689
4546
|
const r = rows22.get(id);
|
|
3690
4547
|
return r !== void 0 && r.status !== "skipped" && r.status !== "pending";
|
|
3691
4548
|
});
|
|
@@ -3693,10 +4550,10 @@ function inferTaken(entry, rows22) {
|
|
|
3693
4550
|
__name(inferTaken, "inferTaken");
|
|
3694
4551
|
__name3(inferTaken, "inferTaken");
|
|
3695
4552
|
function countChildren(entry, rows22) {
|
|
3696
|
-
const body =
|
|
3697
|
-
let
|
|
3698
|
-
for (const id of rows22.keys()) if (id.startsWith(`${body}[`))
|
|
3699
|
-
return
|
|
4553
|
+
const body = branchArmId(entry.step);
|
|
4554
|
+
let n2 = 0;
|
|
4555
|
+
for (const id of rows22.keys()) if (id.startsWith(`${body}[`)) n2 += 1;
|
|
4556
|
+
return n2;
|
|
3700
4557
|
}
|
|
3701
4558
|
__name(countChildren, "countChildren");
|
|
3702
4559
|
__name3(countChildren, "countChildren");
|
|
@@ -3713,21 +4570,58 @@ function pruneUndefined(o) {
|
|
|
3713
4570
|
__name(pruneUndefined, "pruneUndefined");
|
|
3714
4571
|
__name3(pruneUndefined, "pruneUndefined");
|
|
3715
4572
|
function runNextAction(run) {
|
|
3716
|
-
if (isTerminalRunStatus(run.status)
|
|
4573
|
+
if (isTerminalRunStatus(run.status)) return "none";
|
|
4574
|
+
if (run.status === "suspended" && run.gate?.kind === "budget") return "raise_budget";
|
|
4575
|
+
if (!run.cancel?.requestedAt) return "none";
|
|
3717
4576
|
const forceAt = run.cancel.forceAfter ?? run.cancel.requestedAt + FORCE_CANCEL_STALE_MS;
|
|
3718
4577
|
return Date.now() >= forceAt ? "force" : "cancel_again";
|
|
3719
4578
|
}
|
|
3720
4579
|
__name(runNextAction, "runNextAction");
|
|
3721
4580
|
__name3(runNextAction, "runNextAction");
|
|
4581
|
+
var IN_FLIGHT = new Set(WORKFLOW_STEP_IN_FLIGHT);
|
|
4582
|
+
function emptyRunCounts() {
|
|
4583
|
+
const out = {
|
|
4584
|
+
total: 0,
|
|
4585
|
+
inFlight: 0
|
|
4586
|
+
};
|
|
4587
|
+
for (const status of WORKFLOW_STEP_STATUSES) out[status] = 0;
|
|
4588
|
+
return out;
|
|
4589
|
+
}
|
|
4590
|
+
__name(emptyRunCounts, "emptyRunCounts");
|
|
4591
|
+
__name3(emptyRunCounts, "emptyRunCounts");
|
|
4592
|
+
function runCountsFromStatusTally(tally) {
|
|
4593
|
+
const out = emptyRunCounts();
|
|
4594
|
+
for (const [status, n2] of Object.entries(tally)) {
|
|
4595
|
+
if (!Number.isFinite(n2) || n2 <= 0) continue;
|
|
4596
|
+
out.total += n2;
|
|
4597
|
+
if (WORKFLOW_STEP_STATUSES.includes(status)) out[status] += n2;
|
|
4598
|
+
if (IN_FLIGHT.has(status)) out.inFlight += n2;
|
|
4599
|
+
}
|
|
4600
|
+
return out;
|
|
4601
|
+
}
|
|
4602
|
+
__name(runCountsFromStatusTally, "runCountsFromStatusTally");
|
|
4603
|
+
__name3(runCountsFromStatusTally, "runCountsFromStatusTally");
|
|
4604
|
+
function runCountsFromStepStatuses(statuses) {
|
|
4605
|
+
const tally = {};
|
|
4606
|
+
for (const s of statuses) tally[s] = (tally[s] ?? 0) + 1;
|
|
4607
|
+
return runCountsFromStatusTally(tally);
|
|
4608
|
+
}
|
|
4609
|
+
__name(runCountsFromStepStatuses, "runCountsFromStepStatuses");
|
|
4610
|
+
__name3(runCountsFromStepStatuses, "runCountsFromStepStatuses");
|
|
4611
|
+
var n = /* @__PURE__ */ __name3((v) => typeof v === "number" && Number.isFinite(v) ? v : 0, "n");
|
|
3722
4612
|
function runCounts(counts) {
|
|
4613
|
+
const c = counts ?? {};
|
|
4614
|
+
const rawInFlight = c.dispatched !== void 0 || c.claimed !== void 0 || c.running !== void 0 || c.cancellation_requested !== void 0;
|
|
3723
4615
|
return {
|
|
3724
|
-
total:
|
|
3725
|
-
completed:
|
|
3726
|
-
failed:
|
|
3727
|
-
skipped:
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
4616
|
+
total: n(c.total),
|
|
4617
|
+
completed: n(c.completed),
|
|
4618
|
+
failed: n(c.failed) + n(c.timeout) + n(c.reaped),
|
|
4619
|
+
skipped: n(c.skipped),
|
|
4620
|
+
// LUA-664: cancelled rows never ran — their own wire bucket, never a failure (the detail's map, verbatim).
|
|
4621
|
+
cancelled: n(c.cancelled),
|
|
4622
|
+
running: rawInFlight ? n(c.dispatched) + n(c.claimed) + n(c.running) + n(c.cancellation_requested) : n(c.inFlight),
|
|
4623
|
+
suspended: n(c.suspended),
|
|
4624
|
+
pending: n(c.pending) + n(c.ready) + n(c.waiting)
|
|
3731
4625
|
};
|
|
3732
4626
|
}
|
|
3733
4627
|
__name(runCounts, "runCounts");
|
|
@@ -3748,7 +4642,23 @@ function runCancelView(cancel) {
|
|
|
3748
4642
|
return {
|
|
3749
4643
|
requestedAt: cancel.requestedAt,
|
|
3750
4644
|
requestedBy: cancel.requestedBy ?? "",
|
|
3751
|
-
forceAvailableAt: cancel.forceAfter ?? cancel.requestedAt + FORCE_CANCEL_STALE_MS
|
|
4645
|
+
forceAvailableAt: cancel.forceAfter ?? cancel.requestedAt + FORCE_CANCEL_STALE_MS,
|
|
4646
|
+
// LUA-686: the wall's own request (LUA-683 stamps `wall:true` from a system actor only) rides the wire, so a
|
|
4647
|
+
// client can tell a wall-ended `timed_out` run's audit block from a cancel it should chip. Absent, never false.
|
|
4648
|
+
...cancel.wall === true ? {
|
|
4649
|
+
wall: true
|
|
4650
|
+
} : {},
|
|
4651
|
+
// LUA-704: a forced terminal's audit — who forced it, when, and a human's own reason text (the run's `reason`
|
|
4652
|
+
// is the terminal's). Absent on an unforced terminal, never null.
|
|
4653
|
+
...typeof cancel.forcedAt === "number" ? {
|
|
4654
|
+
forcedAt: cancel.forcedAt
|
|
4655
|
+
} : {},
|
|
4656
|
+
...typeof cancel.forcedBy === "string" && cancel.forcedBy ? {
|
|
4657
|
+
forcedBy: cancel.forcedBy
|
|
4658
|
+
} : {},
|
|
4659
|
+
...typeof cancel.forceReason === "string" && cancel.forceReason ? {
|
|
4660
|
+
forceReason: cancel.forceReason
|
|
4661
|
+
} : {}
|
|
3752
4662
|
};
|
|
3753
4663
|
}
|
|
3754
4664
|
__name(runCancelView, "runCancelView");
|
|
@@ -3757,7 +4667,7 @@ function runWorkspaceView(ws) {
|
|
|
3757
4667
|
if (!ws) return void 0;
|
|
3758
4668
|
const w = ws;
|
|
3759
4669
|
return pruneUndefined({
|
|
3760
|
-
kind: w.kind ?? "empty",
|
|
4670
|
+
kind: w.spec?.kind ?? w.kind ?? "empty",
|
|
3761
4671
|
backend: w.backend,
|
|
3762
4672
|
status: String(w.status ?? ""),
|
|
3763
4673
|
branch: w.branch,
|
|
@@ -3771,7 +4681,6 @@ __name3(runWorkspaceView, "runWorkspaceView");
|
|
|
3771
4681
|
function toWorkflowRunSummary(run) {
|
|
3772
4682
|
const status = run.status;
|
|
3773
4683
|
const principal = run.principal?.principal;
|
|
3774
|
-
const gated = status === "gated" || status === "suspended";
|
|
3775
4684
|
return pruneUndefined({
|
|
3776
4685
|
runId: run.id,
|
|
3777
4686
|
workflowId: run.workflowId,
|
|
@@ -3783,7 +4692,14 @@ function toWorkflowRunSummary(run) {
|
|
|
3783
4692
|
orgId: run.orgId,
|
|
3784
4693
|
spaceAgentId: run.spaceAgentId,
|
|
3785
4694
|
status,
|
|
3786
|
-
|
|
4695
|
+
// LUA-702 (pass-3B D6): the gate rides the wire whenever a NON-terminal doc carries one. In the LUA-681 window
|
|
4696
|
+
// (an approval resolved beside an open exception park) the run is `running` AND still holds `gate{exception}`
|
|
4697
|
+
// until the tick tail parks it back; projecting it only on gated|suspended hid that park from R4 / the CLI /
|
|
4698
|
+
// the desktop (prod 2026-09-05: `status: running, gate: null`). A terminal doc carries no gate by contract
|
|
4699
|
+
// (`terminalizeRun` drops `gate` / `suspendedFor` in every terminal write), but a row terminalized before that
|
|
4700
|
+
// held — pre-LUA-677/694 docs, a script-tier backstop that passed no `unset` — may still carry a stale one:
|
|
4701
|
+
// hidden here, so a `failed` run never reads "Needs a decision" on R3/R4 or the CLI (#2476 review).
|
|
4702
|
+
gate: run.gate && !isTerminalRunStatus(status) ? run.gate : void 0,
|
|
3787
4703
|
batchId: run.batchId,
|
|
3788
4704
|
goalId: run.goalId,
|
|
3789
4705
|
foreachOverflow: run.foreachOverflow,
|
|
@@ -3806,9 +4722,11 @@ function toWorkflowRunSummary(run) {
|
|
|
3806
4722
|
principalKind: run.principalKind ?? "user",
|
|
3807
4723
|
cancel: runCancelView(run.cancel),
|
|
3808
4724
|
usage: runUsage(run),
|
|
4725
|
+
// LUA-697: a row persisted before the write seams (#2406 / #2465 / the script tier) leaves scrubbed here too —
|
|
4726
|
+
// idempotent on a scrubbed message, bounded input; an empty message falls back to the code.
|
|
3809
4727
|
error: run.error ? {
|
|
3810
4728
|
code: run.error.code ?? "error",
|
|
3811
|
-
message: run.error.message ?? "",
|
|
4729
|
+
message: scrubStepErrorMessage(run.error.message) ?? run.error.code ?? "error",
|
|
3812
4730
|
stepId: run.error.stepId
|
|
3813
4731
|
} : void 0,
|
|
3814
4732
|
kind: "run",
|
|
@@ -3821,11 +4739,94 @@ function toWorkflowRunSummary(run) {
|
|
|
3821
4739
|
eventSeq: run.eventSeq ?? 0,
|
|
3822
4740
|
cancellable: !isTerminalRunStatus(status),
|
|
3823
4741
|
nextAction: runNextAction(run),
|
|
3824
|
-
workspace: runWorkspaceView(run.workspace)
|
|
4742
|
+
workspace: runWorkspaceView(run.workspace),
|
|
4743
|
+
// LUA-643: the runs list says a result exists; R4 `fields:'full'` (output-ACL gated, audited) serves it. The
|
|
4744
|
+
// stamped flag is what a list page carries (it projects `output` out); the payload members cover R4's full row.
|
|
4745
|
+
hasOutput: run.hasOutput === true || run.output !== void 0 || run.outputRef !== void 0 ? true : void 0
|
|
3825
4746
|
});
|
|
3826
4747
|
}
|
|
3827
4748
|
__name(toWorkflowRunSummary, "toWorkflowRunSummary");
|
|
3828
4749
|
__name3(toWorkflowRunSummary, "toWorkflowRunSummary");
|
|
4750
|
+
var STEP_ERROR_DETAIL_KEYS = [
|
|
4751
|
+
"reason",
|
|
4752
|
+
"key",
|
|
4753
|
+
"integrationType",
|
|
4754
|
+
"candidates",
|
|
4755
|
+
"connectionId",
|
|
4756
|
+
"status",
|
|
4757
|
+
"code",
|
|
4758
|
+
"providerStatus",
|
|
4759
|
+
"model",
|
|
4760
|
+
"turnIndex",
|
|
4761
|
+
"message",
|
|
4762
|
+
// LUA-655: `output_schema_invalid` — the Ajv issues ({path, message}, ≤ 20, scrubbed at the source too) and the
|
|
4763
|
+
// in-session repair rounds the attempt spent.
|
|
4764
|
+
"issues",
|
|
4765
|
+
"repairRounds",
|
|
4766
|
+
// LUA-669 (#2446 review 4): the subrun failures — `subrun_<status>` names the child run and how it ended
|
|
4767
|
+
// (`childStatus`, `childReason: 'max_duration'` under `subrun_timed_out`), `subrun_depth_exceeded` its `depth` /
|
|
4768
|
+
// `max`, and every refusal the target `workflowId`. Short scalars only: the child's whole `childError` and the
|
|
4769
|
+
// cycle walk's `ancestors` stay off the wire — the child run's own R4 carries its error.
|
|
4770
|
+
"childRunId",
|
|
4771
|
+
"childStatus",
|
|
4772
|
+
"childReason",
|
|
4773
|
+
"max",
|
|
4774
|
+
"depth",
|
|
4775
|
+
"workflowId",
|
|
4776
|
+
// LUA-696 (review 2): the `ctx.once` key of an `effect_in_doubt` park — the step site stamps it here (scrubbed)
|
|
4777
|
+
// beside `park.effectKey`; a key is user text and leaves scrubbed like every other string leaf.
|
|
4778
|
+
"effectKey"
|
|
4779
|
+
];
|
|
4780
|
+
var STEP_ERROR_DETAIL_MAX_BYTES = 8 * 1024;
|
|
4781
|
+
var DETAIL_MAX_DEPTH = 4;
|
|
4782
|
+
var DETAIL_MAX_ITEMS = 100;
|
|
4783
|
+
function scrubDetailValue(value22, depth) {
|
|
4784
|
+
if (typeof value22 === "string") return scrubSecretText(value22);
|
|
4785
|
+
if (typeof value22 === "number" || typeof value22 === "boolean" || value22 === null) return value22;
|
|
4786
|
+
if (depth >= DETAIL_MAX_DEPTH) return void 0;
|
|
4787
|
+
if (Array.isArray(value22)) {
|
|
4788
|
+
return value22.slice(0, DETAIL_MAX_ITEMS).map((v) => scrubDetailValue(v, depth + 1)).filter((v) => v !== void 0);
|
|
4789
|
+
}
|
|
4790
|
+
if (typeof value22 === "object") {
|
|
4791
|
+
const out = {};
|
|
4792
|
+
for (const [k, v] of Object.entries(value22)) {
|
|
4793
|
+
const s = scrubDetailValue(v, depth + 1);
|
|
4794
|
+
if (s !== void 0) out[k] = s;
|
|
4795
|
+
}
|
|
4796
|
+
return out;
|
|
4797
|
+
}
|
|
4798
|
+
return void 0;
|
|
4799
|
+
}
|
|
4800
|
+
__name(scrubDetailValue, "scrubDetailValue");
|
|
4801
|
+
__name3(scrubDetailValue, "scrubDetailValue");
|
|
4802
|
+
function stepErrorDetail(error) {
|
|
4803
|
+
if (!error || typeof error !== "object") return void 0;
|
|
4804
|
+
const d = error.detail;
|
|
4805
|
+
if (!d || typeof d !== "object" || Array.isArray(d)) return void 0;
|
|
4806
|
+
const bag = d;
|
|
4807
|
+
const out = {};
|
|
4808
|
+
for (const k of STEP_ERROR_DETAIL_KEYS) {
|
|
4809
|
+
if (!(k in bag)) continue;
|
|
4810
|
+
const v = scrubDetailValue(bag[k], 1);
|
|
4811
|
+
if (v !== void 0) out[k] = v;
|
|
4812
|
+
}
|
|
4813
|
+
if (!Object.keys(out).length) return void 0;
|
|
4814
|
+
let bytes;
|
|
4815
|
+
try {
|
|
4816
|
+
bytes = new TextEncoder().encode(JSON.stringify(out)).length;
|
|
4817
|
+
} catch {
|
|
4818
|
+
return void 0;
|
|
4819
|
+
}
|
|
4820
|
+
if (bytes <= STEP_ERROR_DETAIL_MAX_BYTES) return out;
|
|
4821
|
+
const scalars = Object.fromEntries(Object.entries(out).filter(([, v]) => v === null || typeof v !== "object"));
|
|
4822
|
+
return {
|
|
4823
|
+
...scalars,
|
|
4824
|
+
__truncated: true,
|
|
4825
|
+
bytes
|
|
4826
|
+
};
|
|
4827
|
+
}
|
|
4828
|
+
__name(stepErrorDetail, "stepErrorDetail");
|
|
4829
|
+
__name3(stepErrorDetail, "stepErrorDetail");
|
|
3829
4830
|
var MAX_HOLIDAYS = 366;
|
|
3830
4831
|
var MAX_WALK_DAYS = 400;
|
|
3831
4832
|
var HHMM = /^([01]\d|2[0-3]):([0-5]\d)$/;
|
|
@@ -4382,58 +5383,86 @@ __name(rebaseItemPointer, "rebaseItemPointer");
|
|
|
4382
5383
|
__name3(rebaseItemPointer, "rebaseItemPointer");
|
|
4383
5384
|
var APPROVER_SPEC_MAX_USERS = 20;
|
|
4384
5385
|
var ESCALATION_MAX_HOPS = 3;
|
|
4385
|
-
var TemplateBindingSchema =
|
|
4386
|
-
template:
|
|
5386
|
+
var TemplateBindingSchema = z22.object({
|
|
5387
|
+
template: z22.string().min(1).max(2048)
|
|
4387
5388
|
}).strict();
|
|
4388
|
-
var ApproverSpecSchema =
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
users:
|
|
4393
|
-
|
|
5389
|
+
var ApproverSpecSchema = z22.union([
|
|
5390
|
+
z22.literal("creator"),
|
|
5391
|
+
z22.literal("org-admins"),
|
|
5392
|
+
z22.object({
|
|
5393
|
+
users: z22.union([
|
|
5394
|
+
z22.array(z22.string().min(1).max(128)).min(1).max(APPROVER_SPEC_MAX_USERS),
|
|
4394
5395
|
TemplateBindingSchema
|
|
4395
5396
|
])
|
|
4396
5397
|
}).strict(),
|
|
4397
|
-
|
|
4398
|
-
role:
|
|
4399
|
-
|
|
5398
|
+
z22.object({
|
|
5399
|
+
role: z22.union([
|
|
5400
|
+
z22.string().min(1).max(128),
|
|
4400
5401
|
TemplateBindingSchema
|
|
4401
5402
|
])
|
|
4402
5403
|
}).strict(),
|
|
4403
|
-
|
|
4404
|
-
group:
|
|
4405
|
-
|
|
5404
|
+
z22.object({
|
|
5405
|
+
group: z22.union([
|
|
5406
|
+
z22.string().min(1).max(128),
|
|
4406
5407
|
TemplateBindingSchema
|
|
4407
5408
|
])
|
|
4408
5409
|
}).strict(),
|
|
4409
|
-
|
|
4410
|
-
governance:
|
|
4411
|
-
policyId:
|
|
5410
|
+
z22.object({
|
|
5411
|
+
governance: z22.object({
|
|
5412
|
+
policyId: z22.string().min(1).max(128)
|
|
4412
5413
|
}).strict()
|
|
4413
5414
|
}).strict()
|
|
4414
5415
|
]);
|
|
4415
|
-
var FourEyesSchema =
|
|
5416
|
+
var FourEyesSchema = z22.object({
|
|
4416
5417
|
edit: ApproverSpecSchema,
|
|
4417
5418
|
approve: ApproverSpecSchema
|
|
4418
5419
|
}).strict();
|
|
4419
|
-
var EscalationHopSchema =
|
|
5420
|
+
var EscalationHopSchema = z22.object({
|
|
4420
5421
|
escalateTo: ApproverSpecSchema,
|
|
4421
|
-
timeoutHours:
|
|
5422
|
+
timeoutHours: z22.number().finite().min(1).max(720)
|
|
4422
5423
|
}).strict();
|
|
4423
|
-
var TerminalOutcomeSchema =
|
|
5424
|
+
var TerminalOutcomeSchema = z22.enum([
|
|
4424
5425
|
"deny",
|
|
4425
5426
|
"cancel-run",
|
|
4426
5427
|
"fail",
|
|
4427
5428
|
"continue"
|
|
4428
5429
|
]);
|
|
4429
|
-
var ApprovalOnTimeoutSchema =
|
|
5430
|
+
var ApprovalOnTimeoutSchema = z22.union([
|
|
4430
5431
|
TerminalOutcomeSchema,
|
|
4431
5432
|
EscalationHopSchema,
|
|
4432
|
-
|
|
5433
|
+
z22.array(z22.union([
|
|
4433
5434
|
TerminalOutcomeSchema,
|
|
4434
5435
|
EscalationHopSchema
|
|
4435
5436
|
])).min(1).max(ESCALATION_MAX_HOPS + 1)
|
|
4436
5437
|
]);
|
|
5438
|
+
var APPROVER_SPEC_SHAPES = [
|
|
5439
|
+
"'creator'",
|
|
5440
|
+
"'org-admins'",
|
|
5441
|
+
"{users:[userId, \u2026]}",
|
|
5442
|
+
"{role:roleName}",
|
|
5443
|
+
"{group:groupName}",
|
|
5444
|
+
"{governance:{policyId}}"
|
|
5445
|
+
];
|
|
5446
|
+
var APPROVER_WRITTEN_MAX = 120;
|
|
5447
|
+
var USER_ID_SHAPED_RE = /^[^\s@]{1,128}$/;
|
|
5448
|
+
function describeApproverSpecRefusal(spec) {
|
|
5449
|
+
const raw = spec === void 0 ? "undefined" : JSON.stringify(spec) ?? String(spec);
|
|
5450
|
+
const written = raw.length > APPROVER_WRITTEN_MAX ? `${raw.slice(0, APPROVER_WRITTEN_MAX - 1)}\u2026` : raw;
|
|
5451
|
+
const users = typeof spec === "object" && spec !== null ? spec.users : void 0;
|
|
5452
|
+
const approver = typeof users === "string" && USER_ID_SHAPED_RE.test(users) ? {
|
|
5453
|
+
users: [
|
|
5454
|
+
users
|
|
5455
|
+
]
|
|
5456
|
+
} : "creator";
|
|
5457
|
+
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)}`);
|
|
5458
|
+
return {
|
|
5459
|
+
approver,
|
|
5460
|
+
written,
|
|
5461
|
+
message
|
|
5462
|
+
};
|
|
5463
|
+
}
|
|
5464
|
+
__name(describeApproverSpecRefusal, "describeApproverSpecRefusal");
|
|
5465
|
+
__name3(describeApproverSpecRefusal, "describeApproverSpecRefusal");
|
|
4437
5466
|
var BINDING_ROOTS = [
|
|
4438
5467
|
"initData",
|
|
4439
5468
|
"stepResults",
|
|
@@ -4468,7 +5497,19 @@ function validateApproverBlock(node, opts = {
|
|
|
4468
5497
|
if (!r.success) {
|
|
4469
5498
|
const users = spec?.users;
|
|
4470
5499
|
if (Array.isArray(users) && users.length > APPROVER_SPEC_MAX_USERS) push("cap-exceeded", path, `at most ${APPROVER_SPEC_MAX_USERS} users`);
|
|
4471
|
-
else
|
|
5500
|
+
else {
|
|
5501
|
+
const refusal = describeApproverSpecRefusal(spec);
|
|
5502
|
+
issues.push({
|
|
5503
|
+
code: "approver-invalid",
|
|
5504
|
+
path,
|
|
5505
|
+
severity: "error",
|
|
5506
|
+
message: refusal.message,
|
|
5507
|
+
repair: {
|
|
5508
|
+
approver: refusal.approver,
|
|
5509
|
+
written: refusal.written
|
|
5510
|
+
}
|
|
5511
|
+
});
|
|
5512
|
+
}
|
|
4472
5513
|
return;
|
|
4473
5514
|
}
|
|
4474
5515
|
const s = r.data;
|
|
@@ -4707,8 +5748,8 @@ __name(armEntry, "armEntry");
|
|
|
4707
5748
|
__name3(armEntry, "armEntry");
|
|
4708
5749
|
function ofEntry(e) {
|
|
4709
5750
|
if (!e || typeof e !== "object") return ZERO;
|
|
4710
|
-
const
|
|
4711
|
-
switch (
|
|
5751
|
+
const n2 = e;
|
|
5752
|
+
switch (n2.type) {
|
|
4712
5753
|
case "agent":
|
|
4713
5754
|
return {
|
|
4714
5755
|
steps: {
|
|
@@ -4741,17 +5782,17 @@ function ofEntry(e) {
|
|
|
4741
5782
|
agentCalls: 0
|
|
4742
5783
|
};
|
|
4743
5784
|
case "parallel": {
|
|
4744
|
-
const arms = Array.isArray(
|
|
5785
|
+
const arms = Array.isArray(n2.steps) ? n2.steps : [];
|
|
4745
5786
|
return arms.map(armEntry).map(ofEntry).reduce(add, ZERO);
|
|
4746
5787
|
}
|
|
4747
5788
|
case "conditional": {
|
|
4748
|
-
const arms = (Array.isArray(
|
|
5789
|
+
const arms = (Array.isArray(n2.steps) ? n2.steps : []).map(armEntry).map(ofEntry);
|
|
4749
5790
|
if (arms.length === 0) return ZERO;
|
|
4750
|
-
const hasOtherwise =
|
|
5791
|
+
const hasOtherwise = n2.otherwise !== void 0;
|
|
4751
5792
|
const minArm = arms.reduce((a, b) => b.credits.min < a.credits.min ? b : a);
|
|
4752
5793
|
const maxArm = arms.reduce((a, b) => b.credits.max > a.credits.max ? b : a);
|
|
4753
5794
|
const summed = arms.reduce(add, ZERO);
|
|
4754
|
-
const max =
|
|
5795
|
+
const max = n2.exclusive === true ? maxArm : summed;
|
|
4755
5796
|
const min = hasOtherwise ? minArm : {
|
|
4756
5797
|
...ZERO
|
|
4757
5798
|
};
|
|
@@ -4768,17 +5809,14 @@ function ofEntry(e) {
|
|
|
4768
5809
|
};
|
|
4769
5810
|
}
|
|
4770
5811
|
case "foreach": {
|
|
4771
|
-
const body = ofEntry(armEntry(
|
|
4772
|
-
const opts =
|
|
4773
|
-
const cap = typeof opts.maxItems === "number" && opts.maxItems > 0 ? opts.maxItems :
|
|
5812
|
+
const body = ofEntry(armEntry(n2.step));
|
|
5813
|
+
const opts = n2.opts ?? {};
|
|
5814
|
+
const cap = typeof opts.maxItems === "number" && opts.maxItems > 0 ? opts.maxItems : WORKFLOW_FOREACH_DEFAULT_MAX_ITEMS;
|
|
4774
5815
|
return scale(body, 0, cap);
|
|
4775
5816
|
}
|
|
4776
|
-
case "loop":
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
const body = ofEntry(armEntry(n.step));
|
|
4780
|
-
const opts = n.options ?? {};
|
|
4781
|
-
const cap = typeof opts.maxIterations === "number" && opts.maxIterations > 0 ? opts.maxIterations : 1;
|
|
5817
|
+
case "loop": {
|
|
5818
|
+
const body = ofEntry(armEntry(n2.step));
|
|
5819
|
+
const cap = typeof n2.maxIterations === "number" && n2.maxIterations > 0 ? n2.maxIterations : WORKFLOW_LOOP_DEFAULT_MAX_ITERATIONS;
|
|
4782
5820
|
return scale(body, 1, cap);
|
|
4783
5821
|
}
|
|
4784
5822
|
default:
|
|
@@ -4813,6 +5851,67 @@ function estimateGraph(envelopeOrGraph) {
|
|
|
4813
5851
|
}
|
|
4814
5852
|
__name(estimateGraph, "estimateGraph");
|
|
4815
5853
|
__name3(estimateGraph, "estimateGraph");
|
|
5854
|
+
var isRecord2 = /* @__PURE__ */ __name3((v) => !!v && typeof v === "object" && !Array.isArray(v), "isRecord");
|
|
5855
|
+
function* singleStepsOf(entry) {
|
|
5856
|
+
if (!isRecord2(entry)) return;
|
|
5857
|
+
switch (entry.type) {
|
|
5858
|
+
case "step":
|
|
5859
|
+
case "agent":
|
|
5860
|
+
case "tool":
|
|
5861
|
+
yield entry;
|
|
5862
|
+
return;
|
|
5863
|
+
case "workflow":
|
|
5864
|
+
yield entry;
|
|
5865
|
+
if (Array.isArray(entry.graph)) yield* singleStepsOf(entry.graph[1]);
|
|
5866
|
+
return;
|
|
5867
|
+
case "parallel":
|
|
5868
|
+
case "conditional":
|
|
5869
|
+
if (Array.isArray(entry.steps)) for (const arm of entry.steps) yield* singleStepsOf(arm);
|
|
5870
|
+
yield* singleStepsOf(entry.otherwise);
|
|
5871
|
+
return;
|
|
5872
|
+
case "foreach":
|
|
5873
|
+
case "loop":
|
|
5874
|
+
yield* singleStepsOf(entry.step);
|
|
5875
|
+
return;
|
|
5876
|
+
default:
|
|
5877
|
+
return;
|
|
5878
|
+
}
|
|
5879
|
+
}
|
|
5880
|
+
__name(singleStepsOf, "singleStepsOf");
|
|
5881
|
+
__name3(singleStepsOf, "singleStepsOf");
|
|
5882
|
+
function entriesOf(graph) {
|
|
5883
|
+
const definition = isRecord2(graph) ? graph.definition : void 0;
|
|
5884
|
+
const entries = isRecord2(definition) ? definition.graph : void 0;
|
|
5885
|
+
return Array.isArray(entries) ? entries : [];
|
|
5886
|
+
}
|
|
5887
|
+
__name(entriesOf, "entriesOf");
|
|
5888
|
+
__name3(entriesOf, "entriesOf");
|
|
5889
|
+
function inheritTargets(graphs) {
|
|
5890
|
+
const targets = /* @__PURE__ */ new Set();
|
|
5891
|
+
for (const graph of graphs) {
|
|
5892
|
+
for (const entry of entriesOf(graph)) {
|
|
5893
|
+
for (const node of singleStepsOf(entry)) {
|
|
5894
|
+
if (node.type === "workflow" && node.workspace === "inherit" && typeof node.workflowId === "string" && node.workflowId.length > 0) {
|
|
5895
|
+
targets.add(node.workflowId);
|
|
5896
|
+
}
|
|
5897
|
+
}
|
|
5898
|
+
}
|
|
5899
|
+
}
|
|
5900
|
+
return targets;
|
|
5901
|
+
}
|
|
5902
|
+
__name(inheritTargets, "inheritTargets");
|
|
5903
|
+
__name3(inheritTargets, "inheritTargets");
|
|
5904
|
+
function needsInheritedWorkspace(graph) {
|
|
5905
|
+
if (!isRecord2(graph) || graph.workspace !== void 0) return false;
|
|
5906
|
+
for (const entry of entriesOf(graph)) {
|
|
5907
|
+
for (const node of singleStepsOf(entry)) {
|
|
5908
|
+
if (node.workspace !== void 0 && node.workspace !== "inherit") return true;
|
|
5909
|
+
}
|
|
5910
|
+
}
|
|
5911
|
+
return false;
|
|
5912
|
+
}
|
|
5913
|
+
__name(needsInheritedWorkspace, "needsInheritedWorkspace");
|
|
5914
|
+
__name3(needsInheritedWorkspace, "needsInheritedWorkspace");
|
|
4816
5915
|
|
|
4817
5916
|
// src/types/workflow.ts
|
|
4818
5917
|
function createStep(s) {
|
|
@@ -4922,7 +6021,7 @@ var assertPredicate = /* @__PURE__ */ __name((p, where) => {
|
|
|
4922
6021
|
}, "assertPredicate");
|
|
4923
6022
|
var assertRetry = /* @__PURE__ */ __name((r, id) => {
|
|
4924
6023
|
if (!r) return;
|
|
4925
|
-
if (r.backoff !== void 0 &&
|
|
6024
|
+
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(" | ")}`);
|
|
4926
6025
|
if (r.maxBackoffSeconds !== void 0) {
|
|
4927
6026
|
if (r.backoff !== "exponential") throw new LuaWorkflowBuildError("backoff-invalid", `"${id}": retry.maxBackoffSeconds is only meaningful with backoff:'exponential'`);
|
|
4928
6027
|
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`);
|
|
@@ -5008,7 +6107,7 @@ var LuaWorkflow = class {
|
|
|
5008
6107
|
getEnvTemplateKeys() {
|
|
5009
6108
|
return this.built.envTemplateKeys;
|
|
5010
6109
|
}
|
|
5011
|
-
/** `.workflow(id, ref)` targets by name — `ManifestWorkflow.workflowRefs
|
|
6110
|
+
/** `.workflow(id, ref)` targets by name — `ManifestWorkflow.workflowRefs`; `workspace:'inherit'` marks the inherit children the compiler defers `workspace-not-declared` for. */
|
|
5012
6111
|
getNestedWorkflowRefs() {
|
|
5013
6112
|
return this.built.nestedRefs;
|
|
5014
6113
|
}
|
|
@@ -5038,6 +6137,7 @@ var LuaWorkflow = class {
|
|
|
5038
6137
|
};
|
|
5039
6138
|
if (cfg.concurrencyPolicy !== void 0) envelope.concurrencyPolicy = cfg.concurrencyPolicy;
|
|
5040
6139
|
if (cfg.workspace !== void 0) envelope.workspace = cfg.workspace;
|
|
6140
|
+
if (cfg.connections !== void 0) envelope.connections = cfg.connections;
|
|
5041
6141
|
return withDefaultsFilled(envelope);
|
|
5042
6142
|
}
|
|
5043
6143
|
};
|
|
@@ -5069,16 +6169,16 @@ function stepNodeOf(s) {
|
|
|
5069
6169
|
}
|
|
5070
6170
|
__name(stepNodeOf, "stepNodeOf");
|
|
5071
6171
|
function materializeEntry(entry, steps) {
|
|
5072
|
-
const single = /* @__PURE__ */ __name((
|
|
5073
|
-
if (
|
|
5074
|
-
if (
|
|
5075
|
-
...
|
|
6172
|
+
const single = /* @__PURE__ */ __name((n2) => {
|
|
6173
|
+
if (n2.type === "step" && steps[n2.step.id]) return stepNodeOf(steps[n2.step.id]);
|
|
6174
|
+
if (n2.type === "workflow" && n2.workflowId === WORKFLOW_ARM_SUBRUN_ID && n2.graph) return {
|
|
6175
|
+
...n2,
|
|
5076
6176
|
graph: [
|
|
5077
|
-
|
|
5078
|
-
single(
|
|
6177
|
+
n2.graph[0],
|
|
6178
|
+
single(n2.graph[1])
|
|
5079
6179
|
]
|
|
5080
6180
|
};
|
|
5081
|
-
return
|
|
6181
|
+
return n2;
|
|
5082
6182
|
}, "single");
|
|
5083
6183
|
switch (entry.type) {
|
|
5084
6184
|
case "step":
|
|
@@ -5109,8 +6209,14 @@ function materializeEntry(entry, steps) {
|
|
|
5109
6209
|
}
|
|
5110
6210
|
}
|
|
5111
6211
|
__name(materializeEntry, "materializeEntry");
|
|
6212
|
+
var isHitlEntry = /* @__PURE__ */ __name((n2) => isWorkflowHitlEntryType(n2?.type), "isHitlEntry");
|
|
5112
6213
|
function graphHasHitl(graph, steps) {
|
|
5113
|
-
|
|
6214
|
+
for (const e of graph) {
|
|
6215
|
+
if (isHitlEntry(e)) return true;
|
|
6216
|
+
if (e.type === "parallel" && e.steps.some(isHitlEntry)) return true;
|
|
6217
|
+
if (e.type === "conditional" && (e.steps.some(isHitlEntry) || isHitlEntry(e.otherwise))) return true;
|
|
6218
|
+
if ((e.type === "foreach" || e.type === "loop") && isHitlEntry(e.step)) return true;
|
|
6219
|
+
}
|
|
5114
6220
|
return Object.values(steps).some((s) => s.suspendSchema !== void 0);
|
|
5115
6221
|
}
|
|
5116
6222
|
__name(graphHasHitl, "graphHasHitl");
|
|
@@ -5143,7 +6249,6 @@ var WorkflowBuilderImpl = class WorkflowBuilderImpl2 {
|
|
|
5143
6249
|
if (this.steps[s.id] && this.steps[s.id] !== s) throw new LuaWorkflowBuildError("duplicate-step-id", `step id "${s.id}" is declared twice`);
|
|
5144
6250
|
const tier = s.tier ?? (s.workspace ? "job" : void 0);
|
|
5145
6251
|
if (s.workspace && s.tier !== void 0 && s.tier !== "job") throw new LuaWorkflowBuildError("workspace-requires-job-tier", `"${s.id}": a step mounting a workspace must be tier:'job'`);
|
|
5146
|
-
if (s.workspace && !this.config.workspace) throw new LuaWorkflowBuildError("workspace-not-declared", `"${s.id}" mounts a workspace but createWorkflow declares none`);
|
|
5147
6252
|
if (s.jobTools && tier !== "job") throw new LuaWorkflowBuildError("cap-exceeded", `"${s.id}": jobTools require tier:'job' (job-tools-require-job-tier)`);
|
|
5148
6253
|
assertTimeout({
|
|
5149
6254
|
id: s.id,
|
|
@@ -5166,7 +6271,6 @@ var WorkflowBuilderImpl = class WorkflowBuilderImpl2 {
|
|
|
5166
6271
|
const [mapping, target] = arm;
|
|
5167
6272
|
if (target === void 0 || arm.length < 2) throw new LuaWorkflowBuildError("container-arm-empty", `${where}: a bare mapping arm has nothing to run`);
|
|
5168
6273
|
if (!mapping || typeof mapping !== "object" || Array.isArray(mapping)) throw new LuaWorkflowBuildError("mapping-placement", `${where}: the arm head must be a map config object`);
|
|
5169
|
-
if (mapping.type === "approval" || target.type === "approval") throw new LuaWorkflowBuildError("approval-inside-container", `${where}: approval / waitForSignal are top-level only in v1`);
|
|
5170
6274
|
assertNoClosure(mapping, `${where} arm map`);
|
|
5171
6275
|
this.recordEnvRefs(mapping);
|
|
5172
6276
|
const inner = this.armRef(target, where);
|
|
@@ -5184,8 +6288,9 @@ var WorkflowBuilderImpl = class WorkflowBuilderImpl2 {
|
|
|
5184
6288
|
if (typeof arm === "string") return {
|
|
5185
6289
|
ref: arm
|
|
5186
6290
|
};
|
|
5187
|
-
if (arm && typeof arm === "object" && arm
|
|
5188
|
-
|
|
6291
|
+
if (arm && typeof arm === "object" && isHitlEntry(arm)) {
|
|
6292
|
+
const id = arm.id;
|
|
6293
|
+
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`);
|
|
5189
6294
|
}
|
|
5190
6295
|
return {
|
|
5191
6296
|
node: this.registerStep(arm, where)
|
|
@@ -5384,12 +6489,15 @@ var WorkflowBuilderImpl = class WorkflowBuilderImpl2 {
|
|
|
5384
6489
|
}
|
|
5385
6490
|
const tier = opts.tier ?? (opts.workspace ? "job" : void 0);
|
|
5386
6491
|
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'`);
|
|
5387
|
-
if (opts.workspace && !this.config.workspace) throw new LuaWorkflowBuildError("workspace-not-declared", `"${id}" mounts a workspace but createWorkflow declares none`);
|
|
5388
6492
|
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`);
|
|
5389
6493
|
if (opts.toolScope?.jobTools && tier !== "job") throw new LuaWorkflowBuildError("cap-exceeded", `"${id}": toolScope.jobTools require tier:'job' (job-tools-require-job-tier)`);
|
|
5390
|
-
|
|
5391
|
-
if (
|
|
5392
|
-
if (
|
|
6494
|
+
for (const m of WORKFLOW_JOB_RANGE_MEMBERS) {
|
|
6495
|
+
if (opts[m] === void 0) continue;
|
|
6496
|
+
if (tier !== "job") throw new LuaWorkflowBuildError("max-turns-requires-job-tier", `"${id}": ${m} is only legal on a tier:'job' agent step`);
|
|
6497
|
+
if (!isWithinWorkflowJobRange(m, opts[m])) {
|
|
6498
|
+
const { min, max } = WORKFLOW_JOB_RANGES[m];
|
|
6499
|
+
throw new LuaWorkflowBuildError("max-turns-invalid", `"${id}": ${m} must be an integer ${min}..${max}`);
|
|
6500
|
+
}
|
|
5393
6501
|
}
|
|
5394
6502
|
assertTimeout({
|
|
5395
6503
|
id,
|
|
@@ -5417,7 +6525,9 @@ var WorkflowBuilderImpl = class WorkflowBuilderImpl2 {
|
|
|
5417
6525
|
workspace: opts.workspace,
|
|
5418
6526
|
jobResources: opts.jobResources,
|
|
5419
6527
|
harness: opts.harness,
|
|
5420
|
-
maxTurns: opts.maxTurns
|
|
6528
|
+
maxTurns: opts.maxTurns,
|
|
6529
|
+
maxMessages: opts.maxMessages,
|
|
6530
|
+
maxInputTokens: opts.maxInputTokens
|
|
5421
6531
|
});
|
|
5422
6532
|
return this.push({
|
|
5423
6533
|
kind: "declare",
|
|
@@ -5539,8 +6649,8 @@ var WorkflowBuilderImpl = class WorkflowBuilderImpl2 {
|
|
|
5539
6649
|
itemTimeout: opts.itemTimeout
|
|
5540
6650
|
});
|
|
5541
6651
|
return this.push({
|
|
5542
|
-
kind: "
|
|
5543
|
-
|
|
6652
|
+
kind: "declare",
|
|
6653
|
+
node
|
|
5544
6654
|
});
|
|
5545
6655
|
}
|
|
5546
6656
|
waitForSignal(id, opts) {
|
|
@@ -5558,12 +6668,13 @@ var WorkflowBuilderImpl = class WorkflowBuilderImpl2 {
|
|
|
5558
6668
|
acceptedSources: opts.acceptedSources
|
|
5559
6669
|
});
|
|
5560
6670
|
return this.push({
|
|
5561
|
-
kind: "
|
|
5562
|
-
|
|
6671
|
+
kind: "declare",
|
|
6672
|
+
node
|
|
5563
6673
|
});
|
|
5564
6674
|
}
|
|
5565
6675
|
workflow(id, ref, input, opts) {
|
|
5566
6676
|
this.assertId(id, "workflow()");
|
|
6677
|
+
assertRetry(opts?.retry, id);
|
|
5567
6678
|
const name = typeof ref === "string" ? ref : ref instanceof LuaWorkflow ? ref.getName() : void 0;
|
|
5568
6679
|
if (!name) throw new LuaWorkflowBuildError("invalid-envelope", `workflow("${id}") needs a LuaWorkflow or a workflow name`);
|
|
5569
6680
|
if (opts?.workspace === "inherit") {
|
|
@@ -5572,7 +6683,11 @@ var WorkflowBuilderImpl = class WorkflowBuilderImpl2 {
|
|
|
5572
6683
|
}
|
|
5573
6684
|
assertNoClosure(input, `workflow("${id}").input`);
|
|
5574
6685
|
this.recordEnvRefs(input);
|
|
5575
|
-
this.nestedRefs.push({
|
|
6686
|
+
this.nestedRefs.push(opts?.workspace === "inherit" ? {
|
|
6687
|
+
id,
|
|
6688
|
+
name,
|
|
6689
|
+
workspace: "inherit"
|
|
6690
|
+
} : {
|
|
5576
6691
|
id,
|
|
5577
6692
|
name
|
|
5578
6693
|
});
|
|
@@ -5581,11 +6696,12 @@ var WorkflowBuilderImpl = class WorkflowBuilderImpl2 {
|
|
|
5581
6696
|
id,
|
|
5582
6697
|
workflowId: name,
|
|
5583
6698
|
input,
|
|
5584
|
-
workspace: opts?.workspace
|
|
6699
|
+
workspace: opts?.workspace,
|
|
6700
|
+
retry: opts?.retry
|
|
5585
6701
|
});
|
|
5586
6702
|
return this.push({
|
|
5587
|
-
kind: "
|
|
5588
|
-
|
|
6703
|
+
kind: "declare",
|
|
6704
|
+
node
|
|
5589
6705
|
});
|
|
5590
6706
|
}
|
|
5591
6707
|
commit() {
|
|
@@ -5595,7 +6711,7 @@ var WorkflowBuilderImpl = class WorkflowBuilderImpl2 {
|
|
|
5595
6711
|
const { graph, issues } = resolvePlacements(this.calls);
|
|
5596
6712
|
const fatal = issues[0];
|
|
5597
6713
|
if (fatal) {
|
|
5598
|
-
const hint = fatal.code === "unknown-step-ref" ? "a string StepRef must name an entry declared by agentStep/specialistStep/toolStep/map(\u2026, { id }) somewhere in the chain \u2014 before OR after the reference" : void 0;
|
|
6714
|
+
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;
|
|
5599
6715
|
throw new LuaWorkflowBuildError(fatal.code, fatal.message, hint);
|
|
5600
6716
|
}
|
|
5601
6717
|
if (graph.length === 0) throw new LuaWorkflowBuildError("empty-graph", `workflow "${this.config.name}" has no entries`);
|
|
@@ -5608,7 +6724,8 @@ var WorkflowBuilderImpl = class WorkflowBuilderImpl2 {
|
|
|
5608
6724
|
if (graphHasHitl(graph, this.steps) && this.config.budget?.maxDurationSeconds === void 0) {
|
|
5609
6725
|
this.warnings.push({
|
|
5610
6726
|
code: "hitl-duration-defaulted",
|
|
5611
|
-
|
|
6727
|
+
// LUA-668: names the workflow — two HITL workflows in one project printed two identical lines that read as a duplicate.
|
|
6728
|
+
message: `workflow "${this.config.name}": budget.maxDurationSeconds defaulted to ${WORKFLOW_HITL_MAX_DURATION_SECONDS} s (30 d) because the graph contains an approval / waitForSignal / suspend-capable step \u2014 set it explicitly to silence`
|
|
5612
6729
|
});
|
|
5613
6730
|
}
|
|
5614
6731
|
this.checkScheduleInput();
|
|
@@ -5671,8 +6788,8 @@ function createWorkflow(cfg) {
|
|
|
5671
6788
|
if (v.roles.length > 20 || (v.users?.length ?? 0) > 50) throw new LuaWorkflowBuildError("cap-exceeded", "outputVisibility allows \u2264 20 roles and \u2264 50 users");
|
|
5672
6789
|
}
|
|
5673
6790
|
if (cfg.backfillOnEnable?.maxOccurrences !== void 0) {
|
|
5674
|
-
const
|
|
5675
|
-
if (!Number.isInteger(
|
|
6791
|
+
const n2 = cfg.backfillOnEnable.maxOccurrences;
|
|
6792
|
+
if (!Number.isInteger(n2) || n2 < 1 || n2 > 200) throw new LuaWorkflowBuildError("invalid-envelope", "backfillOnEnable.maxOccurrences must be an integer in 1..200 (backfill-max-occurrences-out-of-range; the org maxBatchItems twin is checked at publish/R56)");
|
|
5676
6793
|
}
|
|
5677
6794
|
const keys = /* @__PURE__ */ new Set();
|
|
5678
6795
|
envRefKeys(cfg.schedule, keys);
|