lua-cli 3.32.1 → 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 +339 -102
- package/dist/api-exports.js +1334 -201
- package/dist/api-exports.js.map +1 -1
- package/dist/index.js +3287 -1634
- package/dist/index.js.map +1 -1
- package/dist/voice/test/index.d.ts +54 -54
- package/dist/workflow-builder.d.ts +224 -45
- package/dist/workflow-builder.js +747 -171
- package/dist/workflow-builder.js.map +1 -1
- package/docs/README.md +2 -2
- package/docs/api/LuaWorkflow.md +16 -16
- package/docs/api/Workflows.md +10 -0
- package/docs/workflows/correlation-keys.md +1 -0
- package/docs/workflows/limits.md +6 -0
- package/docs/workflows/script-form.md +20 -10
- package/docs/workflows/testing-offline.md +22 -20
- package/docs/workflows/workspaces-and-long-steps.md +36 -2
- package/package.json +3 -3
- package/template/examples/workflows/pr-review-round.ts +7 -3
- package/template/examples/workflows/ticket-to-pr.ts +43 -36
- 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;
|
|
@@ -1489,6 +1492,87 @@ function hasCapability(profiles, profileId, required) {
|
|
|
1489
1492
|
}
|
|
1490
1493
|
__name(hasCapability, "hasCapability");
|
|
1491
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");
|
|
1492
1576
|
var SYSTEM_USER_PREFIX = "system:";
|
|
1493
1577
|
function isSystemRun(identity) {
|
|
1494
1578
|
return identity.userId.startsWith(SYSTEM_USER_PREFIX);
|
|
@@ -1620,8 +1704,126 @@ function scheduledWorkflowRunIdForTime(jobId, scheduledTime) {
|
|
|
1620
1704
|
__name(scheduledWorkflowRunIdForTime, "scheduledWorkflowRunIdForTime");
|
|
1621
1705
|
__name2(scheduledWorkflowRunIdForTime, "scheduledWorkflowRunIdForTime");
|
|
1622
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
|
+
});
|
|
1623
1824
|
var REDACTED_PLACEHOLDER = "[REDACTED]";
|
|
1624
1825
|
var PROVIDER_MESSAGE_MAX_CHARS = 300;
|
|
1826
|
+
var ERROR_MESSAGE_MAX_CHARS = 2e3;
|
|
1625
1827
|
var SECRET_LITERAL_PATTERNS = [
|
|
1626
1828
|
{
|
|
1627
1829
|
re: /\b(github_pat_)[A-Za-z0-9_]{16,}/g
|
|
@@ -1632,6 +1834,10 @@ var SECRET_LITERAL_PATTERNS = [
|
|
|
1632
1834
|
{
|
|
1633
1835
|
re: /\b(glpat-)[A-Za-z0-9_-]{16,}/g
|
|
1634
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
|
+
},
|
|
1635
1841
|
{
|
|
1636
1842
|
re: /\b(sk-ant-)[A-Za-z0-9_-]{16,}/g
|
|
1637
1843
|
},
|
|
@@ -1668,8 +1874,15 @@ var SECRET_PAIR_PATTERNS = [
|
|
|
1668
1874
|
// `FOO_TOKEN=x`, `secretKey=x`, `password=x`. Groups: the char before the name, the name, the separator
|
|
1669
1875
|
// (with its quotes), the scheme word — all kept; the value goes. A value a literal rule already replaced
|
|
1670
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.
|
|
1671
1884
|
{
|
|
1672
|
-
re: new RegExp(`(^|[^A-Za-z0-9])((?:[A-Za-z0-9-]
|
|
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")
|
|
1673
1886
|
},
|
|
1674
1887
|
// `?token=x`, `&key=x`, `&X-Amz-Signature=x`, `&sig=x`
|
|
1675
1888
|
{
|
|
@@ -1696,6 +1909,34 @@ function groupCount(re) {
|
|
|
1696
1909
|
}
|
|
1697
1910
|
__name(groupCount, "groupCount");
|
|
1698
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");
|
|
1699
1940
|
function applyPatterns(text, patterns) {
|
|
1700
1941
|
let out = text;
|
|
1701
1942
|
for (const { re, suffix } of patterns) {
|
|
@@ -1722,8 +1963,19 @@ function scrubSecretText(text) {
|
|
|
1722
1963
|
}
|
|
1723
1964
|
__name(scrubSecretText, "scrubSecretText");
|
|
1724
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");
|
|
1725
1977
|
function scrubSecretLines(lines) {
|
|
1726
|
-
return lines.map((l) => typeof l === "string" ? scrubSecretText(l) : l);
|
|
1978
|
+
return lines.map((l) => typeof l === "string" ? scrubSecretText(boundScrubInput(l)) : l);
|
|
1727
1979
|
}
|
|
1728
1980
|
__name(scrubSecretLines, "scrubSecretLines");
|
|
1729
1981
|
__name2(scrubSecretLines, "scrubSecretLines");
|
|
@@ -1744,13 +1996,18 @@ function messageText(value3) {
|
|
|
1744
1996
|
__name(messageText, "messageText");
|
|
1745
1997
|
__name2(messageText, "messageText");
|
|
1746
1998
|
function scrubProviderMessage(raw, max = PROVIDER_MESSAGE_MAX_CHARS) {
|
|
1747
|
-
const text = messageText(raw).replace(/\s+/g, " ").trim();
|
|
1999
|
+
const text = boundScrubInput(messageText(raw)).replace(/\s+/g, " ").trim();
|
|
1748
2000
|
if (!text) return void 0;
|
|
1749
2001
|
const out = scrubSecretText(text);
|
|
1750
2002
|
return out.length > max ? `${out.slice(0, max - 1)}\u2026` : out;
|
|
1751
2003
|
}
|
|
1752
2004
|
__name(scrubProviderMessage, "scrubProviderMessage");
|
|
1753
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");
|
|
1754
2011
|
var WORKFLOW_AUDIT_EVENTS = [
|
|
1755
2012
|
// --- definitions / versions / templates (13, 11 §11.11.5) ---
|
|
1756
2013
|
"workflow.published",
|
|
@@ -1962,23 +2219,33 @@ __name(renderTargetsBlock, "renderTargetsBlock");
|
|
|
1962
2219
|
__name2(renderTargetsBlock, "renderTargetsBlock");
|
|
1963
2220
|
|
|
1964
2221
|
// ../workflow-graph/dist/index.mjs
|
|
2222
|
+
import { createHash } from "crypto";
|
|
1965
2223
|
import { z as z4 } from "zod";
|
|
1966
2224
|
import { z as z22 } from "zod";
|
|
1967
2225
|
import { createHash as createHash2 } from "crypto";
|
|
1968
2226
|
var __defProp3 = Object.defineProperty;
|
|
1969
2227
|
var __name3 = /* @__PURE__ */ __name((target, value22) => __defProp3(target, "name", { value: value22, configurable: true }), "__name");
|
|
1970
|
-
var WORKFLOW_SIDE_EFFECTS = [
|
|
1971
|
-
"none",
|
|
1972
|
-
"external"
|
|
1973
|
-
];
|
|
1974
|
-
var WORKFLOW_JOB_RESOURCES = [
|
|
1975
|
-
"small",
|
|
1976
|
-
"medium",
|
|
1977
|
-
"large"
|
|
1978
|
-
];
|
|
1979
2228
|
var SideEffectsSchema = z4.enum(WORKFLOW_SIDE_EFFECTS);
|
|
1980
2229
|
var JobResourcesSchema = z4.enum(WORKFLOW_JOB_RESOURCES);
|
|
1981
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");
|
|
1982
2249
|
var SLEEP_UNTIL_REPLACEMENT = Object.freeze({
|
|
1983
2250
|
type: "sleep",
|
|
1984
2251
|
duration: 6e4
|
|
@@ -2046,8 +2313,30 @@ function fillSingle(node) {
|
|
|
2046
2313
|
}
|
|
2047
2314
|
__name(fillSingle, "fillSingle");
|
|
2048
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");
|
|
2049
2336
|
function fillArm(arm) {
|
|
2050
|
-
if (arm.type
|
|
2337
|
+
if (arm.type === "mapping") return;
|
|
2338
|
+
if (isHitlNode(arm)) fillHitl(arm);
|
|
2339
|
+
else fillSingle(arm);
|
|
2051
2340
|
}
|
|
2052
2341
|
__name(fillArm, "fillArm");
|
|
2053
2342
|
__name3(fillArm, "fillArm");
|
|
@@ -2060,7 +2349,7 @@ function fillEntry(entry) {
|
|
|
2060
2349
|
fillSingle(entry);
|
|
2061
2350
|
return;
|
|
2062
2351
|
case "parallel":
|
|
2063
|
-
entry.steps.forEach(
|
|
2352
|
+
entry.steps.forEach(fillArm);
|
|
2064
2353
|
return;
|
|
2065
2354
|
case "conditional": {
|
|
2066
2355
|
const c = entry;
|
|
@@ -2074,34 +2363,19 @@ function fillEntry(entry) {
|
|
|
2074
2363
|
f.opts = f.opts ?? {};
|
|
2075
2364
|
if (f.opts.concurrency === void 0) f.opts.concurrency = WORKFLOW_FOREACH_DEFAULT_CONCURRENCY;
|
|
2076
2365
|
if (f.opts.maxItems === void 0) f.opts.maxItems = WORKFLOW_FOREACH_DEFAULT_MAX_ITEMS;
|
|
2077
|
-
|
|
2366
|
+
fillArm(f.step);
|
|
2078
2367
|
return;
|
|
2079
2368
|
}
|
|
2080
2369
|
case "loop": {
|
|
2081
2370
|
const l = entry;
|
|
2082
2371
|
if (l.maxIterations === void 0) l.maxIterations = WORKFLOW_LOOP_DEFAULT_MAX_ITERATIONS;
|
|
2083
|
-
|
|
2084
|
-
return;
|
|
2085
|
-
}
|
|
2086
|
-
case "approval": {
|
|
2087
|
-
const a = entry;
|
|
2088
|
-
if (a.approver === void 0) a.approver = "creator";
|
|
2089
|
-
if (a.timeoutHours === void 0) a.timeoutHours = WORKFLOW_SUSPEND_DEFAULT_TIMEOUT_HOURS;
|
|
2090
|
-
if (a.onTimeout === void 0) a.onTimeout = "deny";
|
|
2091
|
-
if (a.onDeny === void 0) a.onDeny = "continue";
|
|
2092
|
-
if (a.excludeInitiator === void 0) a.excludeInitiator = false;
|
|
2093
|
-
if (a.editable === void 0) a.editable = false;
|
|
2372
|
+
fillArm(l.step);
|
|
2094
2373
|
return;
|
|
2095
2374
|
}
|
|
2096
|
-
case "
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
if (w.onTimeout === void 0) w.onTimeout = "fail";
|
|
2100
|
-
if (w.acceptedSources === void 0) w.acceptedSources = [
|
|
2101
|
-
...WORKFLOW_SIGNAL_DEFAULT_SOURCES
|
|
2102
|
-
];
|
|
2375
|
+
case "approval":
|
|
2376
|
+
case "waitForSignal":
|
|
2377
|
+
fillHitl(entry);
|
|
2103
2378
|
return;
|
|
2104
|
-
}
|
|
2105
2379
|
case "mapping":
|
|
2106
2380
|
case "sleep":
|
|
2107
2381
|
case "sleepUntil":
|
|
@@ -2179,6 +2453,8 @@ var schemaIsArray = /* @__PURE__ */ __name3((schema) => {
|
|
|
2179
2453
|
if (t === void 0) return void 0;
|
|
2180
2454
|
return Array.isArray(t) ? t.includes("array") : t === "array";
|
|
2181
2455
|
}, "schemaIsArray");
|
|
2456
|
+
var isHitlNode = /* @__PURE__ */ __name3((n2) => isWorkflowHitlEntryType(n2.type), "isHitlNode");
|
|
2457
|
+
var isSingleStep = /* @__PURE__ */ __name3((n2) => !isHitlNode(n2), "isSingleStep");
|
|
2182
2458
|
var singleId = /* @__PURE__ */ __name3((s) => s.type === "step" ? s.step.id : s.id, "singleId");
|
|
2183
2459
|
var armId = /* @__PURE__ */ __name3((a) => a.type === "mapping" ? a.id : singleId(a), "armId");
|
|
2184
2460
|
var TEMPLATE_STEP_REF = /\$\{\s*stepResults\.([A-Za-z0-9_\-]+)/g;
|
|
@@ -2189,14 +2465,21 @@ function templateStepRefs(text) {
|
|
|
2189
2465
|
}
|
|
2190
2466
|
__name(templateStepRefs, "templateStepRefs");
|
|
2191
2467
|
__name3(templateStepRefs, "templateStepRefs");
|
|
2192
|
-
function
|
|
2193
|
-
if (!raw) return
|
|
2194
|
-
|
|
2468
|
+
function readMapConfig(raw) {
|
|
2469
|
+
if (!raw) return void 0;
|
|
2470
|
+
if (typeof raw !== "string") return raw;
|
|
2195
2471
|
try {
|
|
2196
|
-
cfg =
|
|
2472
|
+
const cfg = JSON.parse(raw);
|
|
2473
|
+
return cfg && typeof cfg === "object" && !Array.isArray(cfg) ? cfg : void 0;
|
|
2197
2474
|
} catch {
|
|
2198
|
-
return
|
|
2475
|
+
return void 0;
|
|
2199
2476
|
}
|
|
2477
|
+
}
|
|
2478
|
+
__name(readMapConfig, "readMapConfig");
|
|
2479
|
+
__name3(readMapConfig, "readMapConfig");
|
|
2480
|
+
function mapConfigStepRefs(raw) {
|
|
2481
|
+
const cfg = readMapConfig(raw);
|
|
2482
|
+
if (!cfg) return [];
|
|
2200
2483
|
const ids = [];
|
|
2201
2484
|
for (const d of Object.values(cfg)) {
|
|
2202
2485
|
if (!d || typeof d !== "object") continue;
|
|
@@ -2271,6 +2554,16 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2271
2554
|
if (envelopeWorkspace?.backend && envelopeWorkspace.backend !== "ebs" && opts.policy?.workspaceBackends && !opts.policy.workspaceBackends.includes(envelopeWorkspace.backend)) {
|
|
2272
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");
|
|
2273
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
|
+
}
|
|
2274
2567
|
const declaredKeys = new Set(opts.connectionKeys ?? []);
|
|
2275
2568
|
if (g.connections !== void 0 && !Array.isArray(g.connections)) {
|
|
2276
2569
|
err("connection-declaration-invalid", "`connections` must be an array of { key, integrationType }", "connections");
|
|
@@ -2323,8 +2616,10 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2323
2616
|
const r = node.retry;
|
|
2324
2617
|
if (!r) return;
|
|
2325
2618
|
const id = singleId(node);
|
|
2326
|
-
|
|
2327
|
-
|
|
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);
|
|
2328
2623
|
}
|
|
2329
2624
|
if (r.backoffSeconds !== void 0 && r.backoffSeconds < 0) {
|
|
2330
2625
|
err("backoff-invalid", "retry.backoffSeconds must be \u2265 0", `${path}.retry.backoffSeconds`, id);
|
|
@@ -2476,8 +2771,13 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2476
2771
|
err("container-arm-empty", "a bare mapping arm has nothing to run", `${path}.graph.1`, node.id);
|
|
2477
2772
|
return;
|
|
2478
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
|
+
}
|
|
2479
2779
|
upstream.add(singleId(body[1]));
|
|
2480
|
-
checkArm(body[0], `${path}.graph.0`, depth);
|
|
2780
|
+
checkArm(body[0], `${path}.graph.0`, depth, "parallel");
|
|
2481
2781
|
checkSingle(body[1], `${path}.graph.1`, depth);
|
|
2482
2782
|
upstream.add(body[0].id);
|
|
2483
2783
|
upstream.add(singleId(body[1]));
|
|
@@ -2509,13 +2809,60 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2509
2809
|
if (node.type === "workflow" && node.kind === "subrun" && depth > caps.maxNestingDepth) {
|
|
2510
2810
|
err("cap-exceeded", `nesting depth ${depth} exceeds ${caps.maxNestingDepth}`, path, node.id);
|
|
2511
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
|
+
}
|
|
2512
2815
|
for (const ref of nodeStepRefs(node)) {
|
|
2513
2816
|
if (!upstream.has(ref)) {
|
|
2514
2817
|
err("template-reference-unresolved", `"${singleId(node)}" references stepResults.${ref}, which is not upstream`, path, singleId(node));
|
|
2515
2818
|
}
|
|
2516
2819
|
}
|
|
2517
2820
|
}, "checkSingle");
|
|
2518
|
-
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) => {
|
|
2519
2866
|
if (arm.type === "mapping") {
|
|
2520
2867
|
checkId(arm.id, path);
|
|
2521
2868
|
for (const ref of nodeStepRefs(arm)) {
|
|
@@ -2523,6 +2870,10 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2523
2870
|
}
|
|
2524
2871
|
return;
|
|
2525
2872
|
}
|
|
2873
|
+
if (isHitlNode(arm)) {
|
|
2874
|
+
checkHitlArm(arm, path, container);
|
|
2875
|
+
return;
|
|
2876
|
+
}
|
|
2526
2877
|
checkSingle(arm, path, depth);
|
|
2527
2878
|
}, "checkArm");
|
|
2528
2879
|
graph.forEach((entry, i) => {
|
|
@@ -2572,18 +2923,15 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2572
2923
|
err("mapping-placement", "a bare mapping cannot be a parallel arm \u2014 chain it as [map, step]", armPath);
|
|
2573
2924
|
return;
|
|
2574
2925
|
}
|
|
2575
|
-
|
|
2576
|
-
err("approval-inside-container", "approval / waitForSignal are top-level only in v1", armPath);
|
|
2577
|
-
return;
|
|
2578
|
-
}
|
|
2579
|
-
checkSingle(arm, armPath, 1);
|
|
2926
|
+
checkArm(arm, armPath, 1, "parallel");
|
|
2580
2927
|
declared.push(singleId(arm));
|
|
2581
2928
|
});
|
|
2582
|
-
const
|
|
2929
|
+
const executableArms = p.steps.filter(isSingleStep);
|
|
2930
|
+
const worktreeArms = executableArms.filter((a) => a.workspace?.isolation === "worktree");
|
|
2583
2931
|
if (worktreeArms.length > 0 && !p.merge) err("worktree-arms-require-merge", "worktree arms need a `merge` policy", path);
|
|
2584
2932
|
if (worktreeArms.length === 0 && p.merge) err("merge-requires-worktree-arms", "`merge` needs at least one worktree arm", path);
|
|
2585
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);
|
|
2586
|
-
const sharedArms =
|
|
2934
|
+
const sharedArms = executableArms.filter((a) => {
|
|
2587
2935
|
const w = workspaceOf(a);
|
|
2588
2936
|
if (!w || w === "inherit" || w.isolation === "worktree") return false;
|
|
2589
2937
|
return !(envelopeWorkspace?.backend === "efs" && w.mount === "ro");
|
|
@@ -2600,11 +2948,11 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2600
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}`);
|
|
2601
2949
|
});
|
|
2602
2950
|
c.steps.forEach((arm, j) => {
|
|
2603
|
-
checkArm(arm, `${path}.steps.${j}`, 1);
|
|
2951
|
+
checkArm(arm, `${path}.steps.${j}`, 1, "conditional");
|
|
2604
2952
|
declared.push(armId(arm));
|
|
2605
2953
|
});
|
|
2606
2954
|
if (c.otherwise) {
|
|
2607
|
-
checkArm(c.otherwise, `${path}.otherwise`, 1);
|
|
2955
|
+
checkArm(c.otherwise, `${path}.otherwise`, 1, "conditional");
|
|
2608
2956
|
declared.push(armId(c.otherwise));
|
|
2609
2957
|
}
|
|
2610
2958
|
break;
|
|
@@ -2627,8 +2975,9 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2627
2975
|
}
|
|
2628
2976
|
if (o.chunk !== void 0) {
|
|
2629
2977
|
const max = o.maxItems ?? WORKFLOW_FOREACH_DEFAULT_MAX_ITEMS;
|
|
2630
|
-
|
|
2631
|
-
|
|
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`);
|
|
2632
2981
|
}
|
|
2633
2982
|
}
|
|
2634
2983
|
if (o.rateLimit !== void 0) {
|
|
@@ -2645,12 +2994,7 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2645
2994
|
const bodyType = f.step.type;
|
|
2646
2995
|
const prevEntry = i > 0 ? graph[i - 1] : void 0;
|
|
2647
2996
|
if (bodyType !== "mapping" && prevEntry?.type === "mapping" && prevEntry.id === `${singleId(f.step)}_items`) {
|
|
2648
|
-
|
|
2649
|
-
try {
|
|
2650
|
-
cfg = JSON.parse(prevEntry.mapConfig);
|
|
2651
|
-
} catch {
|
|
2652
|
-
cfg = void 0;
|
|
2653
|
-
}
|
|
2997
|
+
const cfg = readMapConfig(prevEntry.mapConfig);
|
|
2654
2998
|
const d = cfg?.[""];
|
|
2655
2999
|
let arrayLike;
|
|
2656
3000
|
if (d && "value" in d) arrayLike = Array.isArray(d.value);
|
|
@@ -2662,8 +3006,13 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2662
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));
|
|
2663
3007
|
}
|
|
2664
3008
|
if (bodyType === "mapping") err("container-arm-empty", "a foreach body needs a step, not a bare mapping", `${path}.step`);
|
|
2665
|
-
else if (
|
|
2666
|
-
|
|
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 {
|
|
2667
3016
|
checkSingle(f.step, `${path}.step`, o.chunk ? 2 : 1);
|
|
2668
3017
|
declared.push(singleId(f.step));
|
|
2669
3018
|
}
|
|
@@ -2680,52 +3029,20 @@ function validateLuaExtensions(g, caps = WORKFLOW_CAPS_DEFAULT, opts = {
|
|
|
2680
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`);
|
|
2681
3030
|
const bodyType = l.step.type;
|
|
2682
3031
|
if (bodyType === "mapping") err("container-arm-empty", "a loop body needs a step, not a bare mapping", `${path}.step`);
|
|
2683
|
-
else if (
|
|
2684
|
-
|
|
3032
|
+
else if (isHitlNode(l.step)) {
|
|
3033
|
+
checkHitlArm(l.step, `${path}.step`, "loop");
|
|
3034
|
+
declared.push(l.step.id);
|
|
3035
|
+
} else {
|
|
2685
3036
|
checkSingle(l.step, `${path}.step`, 1);
|
|
2686
3037
|
declared.push(singleId(l.step));
|
|
2687
3038
|
}
|
|
2688
3039
|
break;
|
|
2689
3040
|
}
|
|
2690
|
-
case "approval":
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
err("approver-excludes-only-candidate", "approver:'creator' with excludeInitiator:true always excludes the only candidate", path, a.id);
|
|
2695
|
-
}
|
|
2696
|
-
if (a.fourEyes !== void 0 && a.editable !== true) {
|
|
2697
|
-
err("four-eyes-requires-editable", "`fourEyes` requires editable:true", `${path}.fourEyes`, a.id);
|
|
2698
|
-
}
|
|
2699
|
-
if ((a.editablePaths !== void 0 || a.editedPayloadSchema !== void 0) && a.editable !== true) {
|
|
2700
|
-
err("editable-path-invalid", "`editablePaths` / `editedPayloadSchema` require editable:true", `${path}.editablePaths`, a.id);
|
|
2701
|
-
}
|
|
2702
|
-
for (const p of a.editablePaths ?? []) {
|
|
2703
|
-
if (!EDITABLE_PATH_RE.test(p)) err("editable-path-invalid", `editablePaths entry "${p}" is outside the seg(.seg)*[*]/[n] grammar`, `${path}.editablePaths`, a.id);
|
|
2704
|
-
}
|
|
2705
|
-
if (Array.isArray(a.onTimeout)) {
|
|
2706
|
-
const chain = a.onTimeout;
|
|
2707
|
-
const hops = chain.filter((h) => typeof h === "object" && h !== null && "escalateTo" in h);
|
|
2708
|
-
if (hops.length > 3) err("escalation-chain-too-long", "an onTimeout chain carries at most 3 escalation hops", `${path}.onTimeout`, a.id);
|
|
2709
|
-
const last = chain[chain.length - 1];
|
|
2710
|
-
if (last === void 0 || typeof last === "object" && last !== null && "escalateTo" in last) {
|
|
2711
|
-
err("escalation-chain-not-terminal", "an onTimeout chain must end in a terminal member", `${path}.onTimeout`, a.id);
|
|
2712
|
-
}
|
|
2713
|
-
}
|
|
2714
|
-
if (typeof a.details === "string") {
|
|
2715
|
-
for (const ref of templateStepRefs(a.details)) {
|
|
2716
|
-
if (!upstream.has(ref)) err("template-reference-unresolved", `"${a.id}" references stepResults.${ref}, which is not upstream`, path, a.id);
|
|
2717
|
-
}
|
|
2718
|
-
}
|
|
2719
|
-
declared.push(a.id);
|
|
2720
|
-
break;
|
|
2721
|
-
}
|
|
2722
|
-
case "waitForSignal": {
|
|
2723
|
-
const w = entry;
|
|
2724
|
-
checkId(w.id, path);
|
|
2725
|
-
if (typeof w.signal !== "string" || w.signal.length === 0) err("invalid-envelope", "waitForSignal.signal is required", `${path}.signal`, w.id);
|
|
2726
|
-
declared.push(w.id);
|
|
3041
|
+
case "approval":
|
|
3042
|
+
case "waitForSignal":
|
|
3043
|
+
checkHitl(entry, path);
|
|
3044
|
+
declared.push(entry.id);
|
|
2727
3045
|
break;
|
|
2728
|
-
}
|
|
2729
3046
|
default:
|
|
2730
3047
|
err("invalid-envelope", `unknown entry type "${entry.type}"`, path);
|
|
2731
3048
|
}
|
|
@@ -2846,14 +3163,9 @@ var WorkflowPlanError = class extends Error {
|
|
|
2846
3163
|
this.name = "WorkflowPlanError";
|
|
2847
3164
|
}
|
|
2848
3165
|
};
|
|
2849
|
-
var
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
tool: "tool",
|
|
2853
|
-
workflow: "subrun"
|
|
2854
|
-
};
|
|
2855
|
-
var isSingleStep = /* @__PURE__ */ __name3((e) => e.type === "step" || e.type === "agent" || e.type === "tool" || e.type === "workflow", "isSingleStep");
|
|
2856
|
-
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");
|
|
2857
3169
|
var joinIdOf = /* @__PURE__ */ __name3((entryId) => `${entryId}.join`, "joinIdOf");
|
|
2858
3170
|
var containerIdOf = /* @__PURE__ */ __name3((type, entryIndex) => `${type}@${entryIndex}`, "containerIdOf");
|
|
2859
3171
|
function compilePlan(g) {
|
|
@@ -2869,10 +3181,10 @@ function compilePlan(g) {
|
|
|
2869
3181
|
let prevTails = [];
|
|
2870
3182
|
const foreachJoinToEntry = /* @__PURE__ */ new Map();
|
|
2871
3183
|
g.definition.graph.forEach((entry, entryIndex) => {
|
|
2872
|
-
if (
|
|
2873
|
-
const id =
|
|
3184
|
+
if (isArmStep(entry)) {
|
|
3185
|
+
const id = armStepId(entry);
|
|
2874
3186
|
addNode(id, {
|
|
2875
|
-
kind:
|
|
3187
|
+
kind: armStepKind(entry),
|
|
2876
3188
|
dependsOn: prevTails,
|
|
2877
3189
|
downstream: [],
|
|
2878
3190
|
unsatisfiedDeps: prevTails.length,
|
|
@@ -2897,9 +3209,8 @@ function compilePlan(g) {
|
|
|
2897
3209
|
];
|
|
2898
3210
|
return;
|
|
2899
3211
|
case "sleep":
|
|
2900
|
-
case "sleepUntil":
|
|
2901
|
-
|
|
2902
|
-
const kind = entry.type === "approval" ? "approval" : entry.type;
|
|
3212
|
+
case "sleepUntil": {
|
|
3213
|
+
const kind = entry.type;
|
|
2903
3214
|
addNode(entry.id, {
|
|
2904
3215
|
kind,
|
|
2905
3216
|
dependsOn: prevTails,
|
|
@@ -2912,24 +3223,12 @@ function compilePlan(g) {
|
|
|
2912
3223
|
];
|
|
2913
3224
|
return;
|
|
2914
3225
|
}
|
|
2915
|
-
case "waitForSignal":
|
|
2916
|
-
addNode(entry.id, {
|
|
2917
|
-
kind: "signal",
|
|
2918
|
-
dependsOn: prevTails,
|
|
2919
|
-
downstream: [],
|
|
2920
|
-
unsatisfiedDeps: prevTails.length,
|
|
2921
|
-
entry
|
|
2922
|
-
});
|
|
2923
|
-
prevTails = [
|
|
2924
|
-
entry.id
|
|
2925
|
-
];
|
|
2926
|
-
return;
|
|
2927
3226
|
case "parallel": {
|
|
2928
3227
|
const entryId = containerIdOf("parallel", entryIndex);
|
|
2929
3228
|
const childIds = entry.steps.map((arm) => {
|
|
2930
|
-
const childId =
|
|
3229
|
+
const childId = armStepId(arm);
|
|
2931
3230
|
addNode(childId, {
|
|
2932
|
-
kind:
|
|
3231
|
+
kind: armStepKind(arm),
|
|
2933
3232
|
dependsOn: prevTails,
|
|
2934
3233
|
downstream: [],
|
|
2935
3234
|
unsatisfiedDeps: prevTails.length,
|
|
@@ -2967,9 +3266,9 @@ function compilePlan(g) {
|
|
|
2967
3266
|
node.otherwise
|
|
2968
3267
|
] : node.steps;
|
|
2969
3268
|
const childIds = arms.map((arm) => {
|
|
2970
|
-
const childId = arm.type === "mapping" ? arm.id :
|
|
3269
|
+
const childId = arm.type === "mapping" ? arm.id : armStepId(arm);
|
|
2971
3270
|
addNode(childId, {
|
|
2972
|
-
kind: arm.type === "mapping" ? "map" :
|
|
3271
|
+
kind: arm.type === "mapping" ? "map" : armStepKind(arm),
|
|
2973
3272
|
dependsOn: [
|
|
2974
3273
|
entryId
|
|
2975
3274
|
],
|
|
@@ -3343,7 +3642,16 @@ var WorkflowTemplateError = class extends Error {
|
|
|
3343
3642
|
this.name = "WorkflowTemplateError";
|
|
3344
3643
|
}
|
|
3345
3644
|
};
|
|
3645
|
+
function isMapConfigObject(v) {
|
|
3646
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
3647
|
+
}
|
|
3648
|
+
__name(isMapConfigObject, "isMapConfigObject");
|
|
3649
|
+
__name3(isMapConfigObject, "isMapConfigObject");
|
|
3346
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
|
+
}
|
|
3347
3655
|
try {
|
|
3348
3656
|
return JSON.parse(raw);
|
|
3349
3657
|
} catch (e) {
|
|
@@ -3352,6 +3660,13 @@ function parseMapConfig(raw, stepId) {
|
|
|
3352
3660
|
}
|
|
3353
3661
|
__name(parseMapConfig, "parseMapConfig");
|
|
3354
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");
|
|
3355
3670
|
var TEMPLATE_PLACEHOLDER = /\$\{([^}]*)\}/g;
|
|
3356
3671
|
var TEMPLATE_NAMESPACES = [
|
|
3357
3672
|
"initData",
|
|
@@ -3622,6 +3937,7 @@ function isContinuedFailureValue(v) {
|
|
|
3622
3937
|
}
|
|
3623
3938
|
__name(isContinuedFailureValue, "isContinuedFailureValue");
|
|
3624
3939
|
__name3(isContinuedFailureValue, "isContinuedFailureValue");
|
|
3940
|
+
var isHitlNode2 = /* @__PURE__ */ __name3((n2) => isWorkflowHitlEntryType(n2.type), "isHitlNode");
|
|
3625
3941
|
function lowerContainerArm(mapping, step22) {
|
|
3626
3942
|
const stepId = nodeIdOf(step22);
|
|
3627
3943
|
return {
|
|
@@ -3707,7 +4023,28 @@ function resolvePlacements(calls) {
|
|
|
3707
4023
|
break;
|
|
3708
4024
|
}
|
|
3709
4025
|
});
|
|
3710
|
-
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) => {
|
|
3711
4048
|
if ("node" in ref) {
|
|
3712
4049
|
if (ref.node.type === "mapping" && !allowMapping) {
|
|
3713
4050
|
issues.push({
|
|
@@ -3724,7 +4061,7 @@ function resolvePlacements(calls) {
|
|
|
3724
4061
|
if (!d) {
|
|
3725
4062
|
issues.push({
|
|
3726
4063
|
code: "unknown-step-ref",
|
|
3727
|
-
message: `"${ref.ref}" is not declared anywhere in the chain \u2014 declare it with agentStep/specialistStep/toolStep/map(\u2026, { id })/workflow(\u2026)`,
|
|
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)`,
|
|
3728
4065
|
callIndex: i,
|
|
3729
4066
|
stepId: ref.ref
|
|
3730
4067
|
});
|
|
@@ -3739,6 +4076,11 @@ function resolvePlacements(calls) {
|
|
|
3739
4076
|
});
|
|
3740
4077
|
return void 0;
|
|
3741
4078
|
}
|
|
4079
|
+
const hitl = hitlPlacementIssue(d.node, ref, i, container);
|
|
4080
|
+
if (hitl) {
|
|
4081
|
+
issues.push(hitl);
|
|
4082
|
+
return void 0;
|
|
4083
|
+
}
|
|
3742
4084
|
const prior = placedBy.get(ref.ref);
|
|
3743
4085
|
if (prior !== void 0 && prior !== i) {
|
|
3744
4086
|
issues.push({
|
|
@@ -3752,23 +4094,31 @@ function resolvePlacements(calls) {
|
|
|
3752
4094
|
placedBy.set(ref.ref, i);
|
|
3753
4095
|
return d.node;
|
|
3754
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");
|
|
3755
4105
|
calls.forEach((call, i) => {
|
|
3756
4106
|
switch (call.kind) {
|
|
3757
4107
|
case "place":
|
|
3758
|
-
|
|
4108
|
+
claim({
|
|
3759
4109
|
ref: call.ref
|
|
3760
|
-
}, i, false);
|
|
4110
|
+
}, i, false, "place");
|
|
3761
4111
|
break;
|
|
3762
4112
|
case "parallel":
|
|
3763
|
-
for (const arm of call.arms)
|
|
4113
|
+
for (const arm of call.arms) claim(arm, i, false, "parallel");
|
|
3764
4114
|
break;
|
|
3765
4115
|
case "conditional":
|
|
3766
|
-
for (const a of call.arms)
|
|
3767
|
-
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");
|
|
3768
4118
|
break;
|
|
3769
4119
|
case "foreach":
|
|
3770
4120
|
case "loop":
|
|
3771
|
-
|
|
4121
|
+
claim(call.body, i, false, call.kind);
|
|
3772
4122
|
break;
|
|
3773
4123
|
default:
|
|
3774
4124
|
break;
|
|
@@ -3777,7 +4127,7 @@ function resolvePlacements(calls) {
|
|
|
3777
4127
|
const graph = [];
|
|
3778
4128
|
const lookup = /* @__PURE__ */ __name3((ref) => {
|
|
3779
4129
|
const n2 = "node" in ref ? ref.node : declared.get(ref.ref)?.node;
|
|
3780
|
-
if (!n2 || !ref.armMap || n2.type === "mapping") return n2;
|
|
4130
|
+
if (!n2 || !ref.armMap || n2.type === "mapping" || isHitlNode2(n2)) return n2;
|
|
3781
4131
|
return lowerContainerArm(ref.armMap, n2);
|
|
3782
4132
|
}, "lookup");
|
|
3783
4133
|
calls.forEach((call, i) => {
|
|
@@ -3859,6 +4209,75 @@ function resolvePlacements(calls) {
|
|
|
3859
4209
|
}
|
|
3860
4210
|
__name(resolvePlacements, "resolvePlacements");
|
|
3861
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");
|
|
3862
4281
|
function seedLedgerFromRun(run, steps, targetPlan, opts = {}) {
|
|
3863
4282
|
const byId = /* @__PURE__ */ new Map();
|
|
3864
4283
|
for (const s of steps) {
|
|
@@ -4027,6 +4446,15 @@ function replayLedger(g, ledger) {
|
|
|
4027
4446
|
local,
|
|
4028
4447
|
diverged: recordedCount !== local
|
|
4029
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
|
+
});
|
|
4030
4458
|
}
|
|
4031
4459
|
}
|
|
4032
4460
|
return {
|
|
@@ -4214,7 +4642,23 @@ function runCancelView(cancel) {
|
|
|
4214
4642
|
return {
|
|
4215
4643
|
requestedAt: cancel.requestedAt,
|
|
4216
4644
|
requestedBy: cancel.requestedBy ?? "",
|
|
4217
|
-
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
|
+
} : {}
|
|
4218
4662
|
};
|
|
4219
4663
|
}
|
|
4220
4664
|
__name(runCancelView, "runCancelView");
|
|
@@ -4237,7 +4681,6 @@ __name3(runWorkspaceView, "runWorkspaceView");
|
|
|
4237
4681
|
function toWorkflowRunSummary(run) {
|
|
4238
4682
|
const status = run.status;
|
|
4239
4683
|
const principal = run.principal?.principal;
|
|
4240
|
-
const gated = status === "gated" || status === "suspended";
|
|
4241
4684
|
return pruneUndefined({
|
|
4242
4685
|
runId: run.id,
|
|
4243
4686
|
workflowId: run.workflowId,
|
|
@@ -4249,7 +4692,14 @@ function toWorkflowRunSummary(run) {
|
|
|
4249
4692
|
orgId: run.orgId,
|
|
4250
4693
|
spaceAgentId: run.spaceAgentId,
|
|
4251
4694
|
status,
|
|
4252
|
-
|
|
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,
|
|
4253
4703
|
batchId: run.batchId,
|
|
4254
4704
|
goalId: run.goalId,
|
|
4255
4705
|
foreachOverflow: run.foreachOverflow,
|
|
@@ -4272,9 +4722,11 @@ function toWorkflowRunSummary(run) {
|
|
|
4272
4722
|
principalKind: run.principalKind ?? "user",
|
|
4273
4723
|
cancel: runCancelView(run.cancel),
|
|
4274
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.
|
|
4275
4727
|
error: run.error ? {
|
|
4276
4728
|
code: run.error.code ?? "error",
|
|
4277
|
-
message: run.error.message ?? "",
|
|
4729
|
+
message: scrubStepErrorMessage(run.error.message) ?? run.error.code ?? "error",
|
|
4278
4730
|
stepId: run.error.stepId
|
|
4279
4731
|
} : void 0,
|
|
4280
4732
|
kind: "run",
|
|
@@ -4295,6 +4747,86 @@ function toWorkflowRunSummary(run) {
|
|
|
4295
4747
|
}
|
|
4296
4748
|
__name(toWorkflowRunSummary, "toWorkflowRunSummary");
|
|
4297
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");
|
|
4298
4830
|
var MAX_HOLIDAYS = 366;
|
|
4299
4831
|
var MAX_WALK_DAYS = 400;
|
|
4300
4832
|
var HHMM = /^([01]\d|2[0-3]):([0-5]\d)$/;
|
|
@@ -4903,6 +5435,34 @@ var ApprovalOnTimeoutSchema = z22.union([
|
|
|
4903
5435
|
EscalationHopSchema
|
|
4904
5436
|
])).min(1).max(ESCALATION_MAX_HOPS + 1)
|
|
4905
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");
|
|
4906
5466
|
var BINDING_ROOTS = [
|
|
4907
5467
|
"initData",
|
|
4908
5468
|
"stepResults",
|
|
@@ -4937,7 +5497,19 @@ function validateApproverBlock(node, opts = {
|
|
|
4937
5497
|
if (!r.success) {
|
|
4938
5498
|
const users = spec?.users;
|
|
4939
5499
|
if (Array.isArray(users) && users.length > APPROVER_SPEC_MAX_USERS) push("cap-exceeded", path, `at most ${APPROVER_SPEC_MAX_USERS} users`);
|
|
4940
|
-
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
|
+
}
|
|
4941
5513
|
return;
|
|
4942
5514
|
}
|
|
4943
5515
|
const s = r.data;
|
|
@@ -5449,7 +6021,7 @@ var assertPredicate = /* @__PURE__ */ __name((p, where) => {
|
|
|
5449
6021
|
}, "assertPredicate");
|
|
5450
6022
|
var assertRetry = /* @__PURE__ */ __name((r, id) => {
|
|
5451
6023
|
if (!r) return;
|
|
5452
|
-
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(" | ")}`);
|
|
5453
6025
|
if (r.maxBackoffSeconds !== void 0) {
|
|
5454
6026
|
if (r.backoff !== "exponential") throw new LuaWorkflowBuildError("backoff-invalid", `"${id}": retry.maxBackoffSeconds is only meaningful with backoff:'exponential'`);
|
|
5455
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`);
|
|
@@ -5637,8 +6209,14 @@ function materializeEntry(entry, steps) {
|
|
|
5637
6209
|
}
|
|
5638
6210
|
}
|
|
5639
6211
|
__name(materializeEntry, "materializeEntry");
|
|
6212
|
+
var isHitlEntry = /* @__PURE__ */ __name((n2) => isWorkflowHitlEntryType(n2?.type), "isHitlEntry");
|
|
5640
6213
|
function graphHasHitl(graph, steps) {
|
|
5641
|
-
|
|
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
|
+
}
|
|
5642
6220
|
return Object.values(steps).some((s) => s.suspendSchema !== void 0);
|
|
5643
6221
|
}
|
|
5644
6222
|
__name(graphHasHitl, "graphHasHitl");
|
|
@@ -5693,7 +6271,6 @@ var WorkflowBuilderImpl = class WorkflowBuilderImpl2 {
|
|
|
5693
6271
|
const [mapping, target] = arm;
|
|
5694
6272
|
if (target === void 0 || arm.length < 2) throw new LuaWorkflowBuildError("container-arm-empty", `${where}: a bare mapping arm has nothing to run`);
|
|
5695
6273
|
if (!mapping || typeof mapping !== "object" || Array.isArray(mapping)) throw new LuaWorkflowBuildError("mapping-placement", `${where}: the arm head must be a map config object`);
|
|
5696
|
-
if (mapping.type === "approval" || target.type === "approval") throw new LuaWorkflowBuildError("approval-inside-container", `${where}: approval / waitForSignal are top-level only in v1`);
|
|
5697
6274
|
assertNoClosure(mapping, `${where} arm map`);
|
|
5698
6275
|
this.recordEnvRefs(mapping);
|
|
5699
6276
|
const inner = this.armRef(target, where);
|
|
@@ -5711,8 +6288,9 @@ var WorkflowBuilderImpl = class WorkflowBuilderImpl2 {
|
|
|
5711
6288
|
if (typeof arm === "string") return {
|
|
5712
6289
|
ref: arm
|
|
5713
6290
|
};
|
|
5714
|
-
if (arm && typeof arm === "object" && arm
|
|
5715
|
-
|
|
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`);
|
|
5716
6294
|
}
|
|
5717
6295
|
return {
|
|
5718
6296
|
node: this.registerStep(arm, where)
|
|
@@ -5913,17 +6491,13 @@ var WorkflowBuilderImpl = class WorkflowBuilderImpl2 {
|
|
|
5913
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'`);
|
|
5914
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`);
|
|
5915
6493
|
if (opts.toolScope?.jobTools && tier !== "job") throw new LuaWorkflowBuildError("cap-exceeded", `"${id}": toolScope.jobTools require tier:'job' (job-tools-require-job-tier)`);
|
|
5916
|
-
|
|
5917
|
-
if (
|
|
5918
|
-
if (
|
|
5919
|
-
|
|
5920
|
-
|
|
5921
|
-
|
|
5922
|
-
|
|
5923
|
-
}
|
|
5924
|
-
if (opts.maxInputTokens !== void 0) {
|
|
5925
|
-
if (tier !== "job") throw new LuaWorkflowBuildError("max-turns-requires-job-tier", `"${id}": maxInputTokens is only legal on a tier:'job' agent step`);
|
|
5926
|
-
if (!Number.isInteger(opts.maxInputTokens) || opts.maxInputTokens < 1e6 || opts.maxInputTokens > 5e8) throw new LuaWorkflowBuildError("max-turns-invalid", `"${id}": maxInputTokens must be an integer 1000000..500000000`);
|
|
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
|
+
}
|
|
5927
6501
|
}
|
|
5928
6502
|
assertTimeout({
|
|
5929
6503
|
id,
|
|
@@ -6075,8 +6649,8 @@ var WorkflowBuilderImpl = class WorkflowBuilderImpl2 {
|
|
|
6075
6649
|
itemTimeout: opts.itemTimeout
|
|
6076
6650
|
});
|
|
6077
6651
|
return this.push({
|
|
6078
|
-
kind: "
|
|
6079
|
-
|
|
6652
|
+
kind: "declare",
|
|
6653
|
+
node
|
|
6080
6654
|
});
|
|
6081
6655
|
}
|
|
6082
6656
|
waitForSignal(id, opts) {
|
|
@@ -6094,12 +6668,13 @@ var WorkflowBuilderImpl = class WorkflowBuilderImpl2 {
|
|
|
6094
6668
|
acceptedSources: opts.acceptedSources
|
|
6095
6669
|
});
|
|
6096
6670
|
return this.push({
|
|
6097
|
-
kind: "
|
|
6098
|
-
|
|
6671
|
+
kind: "declare",
|
|
6672
|
+
node
|
|
6099
6673
|
});
|
|
6100
6674
|
}
|
|
6101
6675
|
workflow(id, ref, input, opts) {
|
|
6102
6676
|
this.assertId(id, "workflow()");
|
|
6677
|
+
assertRetry(opts?.retry, id);
|
|
6103
6678
|
const name = typeof ref === "string" ? ref : ref instanceof LuaWorkflow ? ref.getName() : void 0;
|
|
6104
6679
|
if (!name) throw new LuaWorkflowBuildError("invalid-envelope", `workflow("${id}") needs a LuaWorkflow or a workflow name`);
|
|
6105
6680
|
if (opts?.workspace === "inherit") {
|
|
@@ -6121,7 +6696,8 @@ var WorkflowBuilderImpl = class WorkflowBuilderImpl2 {
|
|
|
6121
6696
|
id,
|
|
6122
6697
|
workflowId: name,
|
|
6123
6698
|
input,
|
|
6124
|
-
workspace: opts?.workspace
|
|
6699
|
+
workspace: opts?.workspace,
|
|
6700
|
+
retry: opts?.retry
|
|
6125
6701
|
});
|
|
6126
6702
|
return this.push({
|
|
6127
6703
|
kind: "declare",
|
|
@@ -6135,7 +6711,7 @@ var WorkflowBuilderImpl = class WorkflowBuilderImpl2 {
|
|
|
6135
6711
|
const { graph, issues } = resolvePlacements(this.calls);
|
|
6136
6712
|
const fatal = issues[0];
|
|
6137
6713
|
if (fatal) {
|
|
6138
|
-
const hint = fatal.code === "unknown-step-ref" ? "a string StepRef must name an entry declared by agentStep/specialistStep/toolStep/map(\u2026, { id })/workflow(\u2026) somewhere in the chain \u2014 before OR after the reference" : void 0;
|
|
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;
|
|
6139
6715
|
throw new LuaWorkflowBuildError(fatal.code, fatal.message, hint);
|
|
6140
6716
|
}
|
|
6141
6717
|
if (graph.length === 0) throw new LuaWorkflowBuildError("empty-graph", `workflow "${this.config.name}" has no entries`);
|