@edictum/core 0.1.0 → 0.2.0
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/README.md +46 -0
- package/dist/{chunk-IXMXZGJG.mjs → chunk-23XIQZR5.mjs} +1 -1
- package/dist/chunk-23XIQZR5.mjs.map +1 -0
- package/dist/{chunk-CRPQFRYJ.mjs → chunk-2YSBMUK5.mjs} +2 -10
- package/dist/chunk-2YSBMUK5.mjs.map +1 -0
- package/dist/{chunk-X5E2YY35.mjs → chunk-JOBPRXVE.mjs} +44 -110
- package/dist/chunk-JOBPRXVE.mjs.map +1 -0
- package/dist/{dry-run-54PYIM6T.mjs → dry-run-JTRNTZA5.mjs} +3 -3
- package/dist/dry-run-JTRNTZA5.mjs.map +1 -0
- package/dist/index.cjs +413 -224
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +28 -27
- package/dist/index.d.ts +28 -27
- package/dist/index.mjs +370 -118
- package/dist/index.mjs.map +1 -1
- package/dist/runner-JCAQMF6O.mjs +10 -0
- package/package.json +14 -13
- package/dist/chunk-CRPQFRYJ.mjs.map +0 -1
- package/dist/chunk-IXMXZGJG.mjs.map +0 -1
- package/dist/chunk-X5E2YY35.mjs.map +0 -1
- package/dist/dry-run-54PYIM6T.mjs.map +0 -1
- package/dist/runner-ASI4JIW2.mjs +0 -10
- /package/dist/{runner-ASI4JIW2.mjs.map → runner-JCAQMF6O.mjs.map} +0 -0
package/dist/index.mjs
CHANGED
|
@@ -18,11 +18,11 @@ import {
|
|
|
18
18
|
createPreDecision,
|
|
19
19
|
defaultSuccessCheck,
|
|
20
20
|
run
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-JOBPRXVE.mjs";
|
|
22
22
|
import {
|
|
23
23
|
createContractResult,
|
|
24
24
|
createEvaluationResult
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-23XIQZR5.mjs";
|
|
26
26
|
import {
|
|
27
27
|
BashClassifier,
|
|
28
28
|
EdictumConfigError,
|
|
@@ -30,12 +30,11 @@ import {
|
|
|
30
30
|
EdictumToolError,
|
|
31
31
|
SideEffect,
|
|
32
32
|
ToolRegistry,
|
|
33
|
-
__require,
|
|
34
33
|
_validateToolName,
|
|
35
34
|
createEnvelope,
|
|
36
35
|
createPrincipal,
|
|
37
36
|
deepFreeze
|
|
38
|
-
} from "./chunk-
|
|
37
|
+
} from "./chunk-2YSBMUK5.mjs";
|
|
39
38
|
|
|
40
39
|
// src/limits.ts
|
|
41
40
|
var DEFAULT_LIMITS = Object.freeze({
|
|
@@ -108,21 +107,15 @@ function classifyFinding(contractId, verdictMessage) {
|
|
|
108
107
|
const contractLower = contractId.toLowerCase();
|
|
109
108
|
const messageLower = (verdictMessage || "").toLowerCase();
|
|
110
109
|
const piiTerms = ["pii", "ssn", "patient", "name", "dob"];
|
|
111
|
-
if (piiTerms.some(
|
|
112
|
-
(term) => contractLower.includes(term) || messageLower.includes(term)
|
|
113
|
-
)) {
|
|
110
|
+
if (piiTerms.some((term) => contractLower.includes(term) || messageLower.includes(term))) {
|
|
114
111
|
return "pii_detected";
|
|
115
112
|
}
|
|
116
113
|
const secretTerms = ["secret", "token", "key", "credential", "password"];
|
|
117
|
-
if (secretTerms.some(
|
|
118
|
-
(term) => contractLower.includes(term) || messageLower.includes(term)
|
|
119
|
-
)) {
|
|
114
|
+
if (secretTerms.some((term) => contractLower.includes(term) || messageLower.includes(term))) {
|
|
120
115
|
return "secret_detected";
|
|
121
116
|
}
|
|
122
117
|
const limitTerms = ["session", "limit", "max_calls", "budget"];
|
|
123
|
-
if (limitTerms.some(
|
|
124
|
-
(term) => contractLower.includes(term) || messageLower.includes(term)
|
|
125
|
-
)) {
|
|
118
|
+
if (limitTerms.some((term) => contractLower.includes(term) || messageLower.includes(term))) {
|
|
126
119
|
return "limit_exceeded";
|
|
127
120
|
}
|
|
128
121
|
return "policy_violation";
|
|
@@ -263,9 +256,7 @@ function mergeObserveAlongside(merged, layer, label, contractSources, observes)
|
|
|
263
256
|
for (const contract of layer.contracts ?? []) {
|
|
264
257
|
const cid = contract.id;
|
|
265
258
|
const observeId = `${cid}:candidate`;
|
|
266
|
-
const existingIds = new Set(
|
|
267
|
-
mc.map((c) => c.id)
|
|
268
|
-
);
|
|
259
|
+
const existingIds = new Set(mc.map((c) => c.id));
|
|
269
260
|
if (existingIds.has(observeId)) {
|
|
270
261
|
throw new EdictumConfigError(
|
|
271
262
|
`observe_alongside collision: generated ID "${observeId}" already exists in the bundle. Rename the conflicting contract or use a different ID for "${cid}".`
|
|
@@ -405,10 +396,7 @@ function resolveSelector(selector, envelope, outputText, customSelectors) {
|
|
|
405
396
|
if (rest === "role") return envelope.principal.role;
|
|
406
397
|
if (rest === "ticket_ref") return envelope.principal.ticketRef;
|
|
407
398
|
if (rest.startsWith("claims.")) {
|
|
408
|
-
return resolveNested(
|
|
409
|
-
rest.slice(7),
|
|
410
|
-
envelope.principal.claims
|
|
411
|
-
);
|
|
399
|
+
return resolveNested(rest.slice(7), envelope.principal.claims);
|
|
412
400
|
}
|
|
413
401
|
return _MISSING;
|
|
414
402
|
}
|
|
@@ -422,10 +410,7 @@ function resolveSelector(selector, envelope, outputText, customSelectors) {
|
|
|
422
410
|
return coerceEnvValue(raw);
|
|
423
411
|
}
|
|
424
412
|
if (selector.startsWith("metadata.")) {
|
|
425
|
-
return resolveNested(
|
|
426
|
-
selector.slice(9),
|
|
427
|
-
envelope.metadata
|
|
428
|
-
);
|
|
413
|
+
return resolveNested(selector.slice(9), envelope.metadata);
|
|
429
414
|
}
|
|
430
415
|
if (customSelectors) {
|
|
431
416
|
const dotPos = selector.indexOf(".");
|
|
@@ -474,13 +459,31 @@ function evaluateExpression(expr, envelope, outputText, options) {
|
|
|
474
459
|
const customOps = options?.customOperators ?? null;
|
|
475
460
|
const customSels = options?.customSelectors ?? null;
|
|
476
461
|
if ("all" in expr) {
|
|
477
|
-
return _evalAll(
|
|
462
|
+
return _evalAll(
|
|
463
|
+
expr.all,
|
|
464
|
+
envelope,
|
|
465
|
+
outputText,
|
|
466
|
+
customOps,
|
|
467
|
+
customSels
|
|
468
|
+
);
|
|
478
469
|
}
|
|
479
470
|
if ("any" in expr) {
|
|
480
|
-
return _evalAny(
|
|
471
|
+
return _evalAny(
|
|
472
|
+
expr.any,
|
|
473
|
+
envelope,
|
|
474
|
+
outputText,
|
|
475
|
+
customOps,
|
|
476
|
+
customSels
|
|
477
|
+
);
|
|
481
478
|
}
|
|
482
479
|
if ("not" in expr) {
|
|
483
|
-
return _evalNot(
|
|
480
|
+
return _evalNot(
|
|
481
|
+
expr.not,
|
|
482
|
+
envelope,
|
|
483
|
+
outputText,
|
|
484
|
+
customOps,
|
|
485
|
+
customSels
|
|
486
|
+
);
|
|
484
487
|
}
|
|
485
488
|
const leafKeys = Object.keys(expr);
|
|
486
489
|
if (leafKeys.length !== 1) {
|
|
@@ -535,7 +538,8 @@ function _applyOperator(op, fieldValue, opValue, selector, customOperators) {
|
|
|
535
538
|
}
|
|
536
539
|
if (fieldValue === _MISSING || fieldValue == null) return false;
|
|
537
540
|
try {
|
|
538
|
-
if (Object.hasOwn(OPERATORS, op))
|
|
541
|
+
if (Object.hasOwn(OPERATORS, op))
|
|
542
|
+
return OPERATORS[op](fieldValue, opValue);
|
|
539
543
|
if (customOperators && Object.hasOwn(customOperators, op)) {
|
|
540
544
|
return Boolean(customOperators[op](fieldValue, opValue));
|
|
541
545
|
}
|
|
@@ -562,10 +566,7 @@ function expandMessage(template, envelope, outputText, customSelectors) {
|
|
|
562
566
|
});
|
|
563
567
|
}
|
|
564
568
|
function validateOperators(bundle, customOperators) {
|
|
565
|
-
const known = /* @__PURE__ */ new Set([
|
|
566
|
-
...BUILTIN_OPERATOR_NAMES,
|
|
567
|
-
...Object.keys(customOperators ?? {})
|
|
568
|
-
]);
|
|
569
|
+
const known = /* @__PURE__ */ new Set([...BUILTIN_OPERATOR_NAMES, ...Object.keys(customOperators ?? {})]);
|
|
569
570
|
const contracts = bundle.contracts ?? [];
|
|
570
571
|
for (const contract of contracts) {
|
|
571
572
|
const when = contract.when;
|
|
@@ -597,9 +598,7 @@ function _validateExpressionOperators(expr, known, contractId) {
|
|
|
597
598
|
if (operator != null && typeof operator === "object") {
|
|
598
599
|
for (const opName of Object.keys(operator)) {
|
|
599
600
|
if (!known.has(opName)) {
|
|
600
|
-
throw new EdictumConfigError(
|
|
601
|
-
`Contract '${contractId}': unknown operator '${opName}'`
|
|
602
|
-
);
|
|
601
|
+
throw new EdictumConfigError(`Contract '${contractId}': unknown operator '${opName}'`);
|
|
603
602
|
}
|
|
604
603
|
}
|
|
605
604
|
}
|
|
@@ -734,7 +733,16 @@ function compilePost(contract, mode, customOps, customSels) {
|
|
|
734
733
|
const meta = then.metadata ?? {};
|
|
735
734
|
const check = (envelope, response) => {
|
|
736
735
|
const outputText = response != null ? String(response) : void 0;
|
|
737
|
-
return _evalAndVerdict(
|
|
736
|
+
return _evalAndVerdict(
|
|
737
|
+
whenExpr,
|
|
738
|
+
envelope,
|
|
739
|
+
outputText,
|
|
740
|
+
msgTpl,
|
|
741
|
+
tags,
|
|
742
|
+
meta,
|
|
743
|
+
customOps,
|
|
744
|
+
customSels
|
|
745
|
+
);
|
|
738
746
|
};
|
|
739
747
|
const effectValue = then.effect ?? "warn";
|
|
740
748
|
const result = {
|
|
@@ -799,14 +807,18 @@ function mergeSessionLimits(contract, existing) {
|
|
|
799
807
|
if ("max_tool_calls" in sessionLimits) {
|
|
800
808
|
const raw = sessionLimits.max_tool_calls;
|
|
801
809
|
if (typeof raw !== "number" || !Number.isFinite(raw)) {
|
|
802
|
-
throw new EdictumConfigError(
|
|
810
|
+
throw new EdictumConfigError(
|
|
811
|
+
`Session limit max_tool_calls must be a finite number, got: ${String(raw)}`
|
|
812
|
+
);
|
|
803
813
|
}
|
|
804
814
|
maxToolCalls = Math.min(maxToolCalls, raw);
|
|
805
815
|
}
|
|
806
816
|
if ("max_attempts" in sessionLimits) {
|
|
807
817
|
const raw = sessionLimits.max_attempts;
|
|
808
818
|
if (typeof raw !== "number" || !Number.isFinite(raw)) {
|
|
809
|
-
throw new EdictumConfigError(
|
|
819
|
+
throw new EdictumConfigError(
|
|
820
|
+
`Session limit max_attempts must be a finite number, got: ${String(raw)}`
|
|
821
|
+
);
|
|
810
822
|
}
|
|
811
823
|
maxAttempts = Math.min(maxAttempts, raw);
|
|
812
824
|
}
|
|
@@ -828,10 +840,6 @@ function mergeSessionLimits(contract, existing) {
|
|
|
828
840
|
return { maxAttempts, maxToolCalls, maxCallsPerTool };
|
|
829
841
|
}
|
|
830
842
|
|
|
831
|
-
// src/yaml-engine/sandbox-compiler.ts
|
|
832
|
-
import { realpathSync } from "fs";
|
|
833
|
-
import { resolve as pathResolve } from "path";
|
|
834
|
-
|
|
835
843
|
// src/fnmatch.ts
|
|
836
844
|
function fnmatch(name, pattern) {
|
|
837
845
|
if (pattern === "*") return true;
|
|
@@ -856,6 +864,36 @@ function fnmatch(name, pattern) {
|
|
|
856
864
|
return new RegExp("^" + regex + "$").test(safeName);
|
|
857
865
|
}
|
|
858
866
|
|
|
867
|
+
// src/yaml-engine/resolve-path.ts
|
|
868
|
+
import { realpathSync } from "fs";
|
|
869
|
+
import { resolve as pathResolve, sep as pathSep, join as pathJoin } from "path";
|
|
870
|
+
function resolvePath(p) {
|
|
871
|
+
const cleaned = p.replace(/\0/g, "");
|
|
872
|
+
const resolved = pathResolve(cleaned);
|
|
873
|
+
try {
|
|
874
|
+
return realpathSync(resolved);
|
|
875
|
+
} catch (err) {
|
|
876
|
+
if (err.code !== "ENOENT") {
|
|
877
|
+
return resolved;
|
|
878
|
+
}
|
|
879
|
+
const parts = resolved.split(pathSep);
|
|
880
|
+
for (let i = parts.length - 1; i > 0; i--) {
|
|
881
|
+
const prefix = parts.slice(0, i).join(pathSep) || "/";
|
|
882
|
+
try {
|
|
883
|
+
const realPrefix = realpathSync(prefix);
|
|
884
|
+
const rest = parts.slice(i).join(pathSep);
|
|
885
|
+
return pathJoin(realPrefix, rest);
|
|
886
|
+
} catch (innerErr) {
|
|
887
|
+
if (innerErr.code !== "ENOENT") {
|
|
888
|
+
return resolved;
|
|
889
|
+
}
|
|
890
|
+
continue;
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
return resolved;
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
|
|
859
897
|
// src/yaml-engine/sandbox-compiler.ts
|
|
860
898
|
var _REDIRECT_PREFIX_RE = /^(?:\d*>>|>>|\d*>|>|<<|<)/;
|
|
861
899
|
var _SHELL_SEPARATOR_RE = /[;|&\n\r`]|\$\(|\$\{|<\(/;
|
|
@@ -931,21 +969,25 @@ var _PATH_ARG_KEYS = /* @__PURE__ */ new Set([
|
|
|
931
969
|
"destination",
|
|
932
970
|
"source",
|
|
933
971
|
"src",
|
|
934
|
-
"dst"
|
|
972
|
+
"dst",
|
|
973
|
+
// Common path-like arg names from AI framework tool calls.
|
|
974
|
+
// IMPORTANT: Only include keys that are unambiguously path-related.
|
|
975
|
+
// Generic keys like 'name', 'input', 'output', 'from', 'to' are excluded
|
|
976
|
+
// because they frequently hold non-path values (e.g., { from: "English" }),
|
|
977
|
+
// and resolvePath() would produce false positives. The heuristic loop below
|
|
978
|
+
// catches path-like VALUES in any key (containing '..', '~', or '/').
|
|
979
|
+
"filename",
|
|
980
|
+
"file",
|
|
981
|
+
"filepath",
|
|
982
|
+
"read_path",
|
|
983
|
+
"write_path"
|
|
935
984
|
]);
|
|
936
|
-
function _realpath(p) {
|
|
937
|
-
try {
|
|
938
|
-
return realpathSync(p);
|
|
939
|
-
} catch {
|
|
940
|
-
return pathResolve(p);
|
|
941
|
-
}
|
|
942
|
-
}
|
|
943
985
|
function extractPaths(envelope) {
|
|
944
986
|
const paths = [];
|
|
945
987
|
const seen = /* @__PURE__ */ new Set();
|
|
946
988
|
function add(p) {
|
|
947
989
|
if (!p) return;
|
|
948
|
-
const resolved =
|
|
990
|
+
const resolved = resolvePath(p);
|
|
949
991
|
if (!seen.has(resolved)) {
|
|
950
992
|
seen.add(resolved);
|
|
951
993
|
paths.push(resolved);
|
|
@@ -959,6 +1001,17 @@ function extractPaths(envelope) {
|
|
|
959
1001
|
for (const [key, value] of Object.entries(args)) {
|
|
960
1002
|
if (typeof value === "string" && value.startsWith("/") && !_PATH_ARG_KEYS.has(key)) add(value);
|
|
961
1003
|
}
|
|
1004
|
+
for (const [key, value] of Object.entries(args)) {
|
|
1005
|
+
if (typeof value === "string" && !_PATH_ARG_KEYS.has(key)) {
|
|
1006
|
+
const isUrl = value.includes("://");
|
|
1007
|
+
if (!isUrl && value.includes("../") || // embedded traversal: foo/../etc/passwd
|
|
1008
|
+
value === ".." || // bare parent reference
|
|
1009
|
+
value.startsWith("../") || // parent traversal prefix: ../../etc/passwd
|
|
1010
|
+
value.startsWith("~") || value.startsWith("./") && !isUrl) {
|
|
1011
|
+
add(value);
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
962
1015
|
const cmd = envelope.bashCommand ?? args.command ?? "";
|
|
963
1016
|
if (cmd) {
|
|
964
1017
|
for (const token of tokenizeCommand(cmd)) {
|
|
@@ -1011,23 +1064,14 @@ function domainMatches(hostname, patterns) {
|
|
|
1011
1064
|
}
|
|
1012
1065
|
|
|
1013
1066
|
// src/yaml-engine/sandbox-compile-fn.ts
|
|
1014
|
-
import { realpathSync as realpathSync2 } from "fs";
|
|
1015
|
-
import { resolve as pathResolve2 } from "path";
|
|
1016
|
-
function _realpath2(p) {
|
|
1017
|
-
try {
|
|
1018
|
-
return realpathSync2(p);
|
|
1019
|
-
} catch {
|
|
1020
|
-
return pathResolve2(p);
|
|
1021
|
-
}
|
|
1022
|
-
}
|
|
1023
1067
|
function _pathWithin(filePath, prefix) {
|
|
1024
1068
|
return filePath === prefix || filePath.startsWith(prefix.replace(/\/+$/, "") + "/");
|
|
1025
1069
|
}
|
|
1026
1070
|
function compileSandbox(contract, mode) {
|
|
1027
1071
|
const contractId = contract.id;
|
|
1028
1072
|
const toolPatterns = "tools" in contract ? contract.tools : [contract.tool];
|
|
1029
|
-
const within = (contract.within ?? []).map(
|
|
1030
|
-
const notWithin = (contract.not_within ?? []).map(
|
|
1073
|
+
const within = (contract.within ?? []).map(resolvePath);
|
|
1074
|
+
const notWithin = (contract.not_within ?? []).map(resolvePath);
|
|
1031
1075
|
const allows = contract.allows ?? {};
|
|
1032
1076
|
const notAllows = contract.not_allows ?? {};
|
|
1033
1077
|
const allowedCommands = allows.commands ?? [];
|
|
@@ -1040,6 +1084,11 @@ function compileSandbox(contract, mode) {
|
|
|
1040
1084
|
const check = (envelope) => {
|
|
1041
1085
|
if (within.length > 0 || notWithin.length > 0) {
|
|
1042
1086
|
const paths = extractPaths(envelope);
|
|
1087
|
+
if (paths.length === 0 && within.length > 0) {
|
|
1088
|
+
return Verdict.fail(
|
|
1089
|
+
expandMessage(messageTemplate, envelope) + " (no extractable paths \u2014 sandbox cannot verify boundary compliance)"
|
|
1090
|
+
);
|
|
1091
|
+
}
|
|
1043
1092
|
if (paths.length > 0) {
|
|
1044
1093
|
for (const p of paths) {
|
|
1045
1094
|
for (const excluded of notWithin) {
|
|
@@ -1106,9 +1155,7 @@ function compileContracts(bundle, options = {}) {
|
|
|
1106
1155
|
const customSels = options?.customSelectors ?? null;
|
|
1107
1156
|
validateOperators(bundle, customOps);
|
|
1108
1157
|
if (bundle.defaults == null || typeof bundle.defaults !== "object") {
|
|
1109
|
-
throw new EdictumConfigError(
|
|
1110
|
-
"Bundle missing required 'defaults' section with 'mode' field"
|
|
1111
|
-
);
|
|
1158
|
+
throw new EdictumConfigError("Bundle missing required 'defaults' section with 'mode' field");
|
|
1112
1159
|
}
|
|
1113
1160
|
const defaults = bundle.defaults;
|
|
1114
1161
|
const defaultMode = defaults.mode;
|
|
@@ -1154,7 +1201,8 @@ function compileContracts(bundle, options = {}) {
|
|
|
1154
1201
|
|
|
1155
1202
|
// src/yaml-engine/loader.ts
|
|
1156
1203
|
import { createHash } from "crypto";
|
|
1157
|
-
import { readFileSync, realpathSync as
|
|
1204
|
+
import { readFileSync, realpathSync as realpathSync2, statSync } from "fs";
|
|
1205
|
+
import yaml from "js-yaml";
|
|
1158
1206
|
|
|
1159
1207
|
// src/yaml-engine/loader-validators.ts
|
|
1160
1208
|
function validateSchema(data) {
|
|
@@ -1175,13 +1223,22 @@ function validateSchema(data) {
|
|
|
1175
1223
|
throw new EdictumConfigError("Schema validation failed: contracts must be an array");
|
|
1176
1224
|
}
|
|
1177
1225
|
}
|
|
1178
|
-
var CONTROL_CHAR_RE = /[\x00-\x1f\x7f-\x9f]/;
|
|
1226
|
+
var CONTROL_CHAR_RE = /[\x00-\x1f\x7f-\x9f\u2028\u2029]/;
|
|
1227
|
+
var CONTRACT_ID_RE = /^[a-z0-9][a-z0-9_-]*$/;
|
|
1179
1228
|
function validateContractId(contractId) {
|
|
1229
|
+
if (contractId.length > 1e4) {
|
|
1230
|
+
throw new EdictumConfigError("Contract id exceeds maximum length");
|
|
1231
|
+
}
|
|
1180
1232
|
if (CONTROL_CHAR_RE.test(contractId)) {
|
|
1181
1233
|
throw new EdictumConfigError(
|
|
1182
1234
|
`Contract id contains control characters: '${contractId.replace(CONTROL_CHAR_RE, "\\x??")}'`
|
|
1183
1235
|
);
|
|
1184
1236
|
}
|
|
1237
|
+
if (!CONTRACT_ID_RE.test(contractId)) {
|
|
1238
|
+
throw new EdictumConfigError(
|
|
1239
|
+
`Contract id '${contractId}' must match pattern ^[a-z0-9][a-z0-9_-]*$`
|
|
1240
|
+
);
|
|
1241
|
+
}
|
|
1185
1242
|
}
|
|
1186
1243
|
function validateUniqueIds(data) {
|
|
1187
1244
|
const ids = /* @__PURE__ */ new Set();
|
|
@@ -1282,26 +1339,229 @@ function validateSandboxContracts(data) {
|
|
|
1282
1339
|
}
|
|
1283
1340
|
}
|
|
1284
1341
|
|
|
1342
|
+
// src/yaml-engine/loader-field-validators.ts
|
|
1343
|
+
var VALID_CONTRACT_TYPES = /* @__PURE__ */ new Set(["pre", "post", "session", "sandbox"]);
|
|
1344
|
+
var PRE_EFFECTS = /* @__PURE__ */ new Set(["deny", "approve"]);
|
|
1345
|
+
var POST_EFFECTS = /* @__PURE__ */ new Set(["warn", "redact", "deny"]);
|
|
1346
|
+
var VALID_MODES = /* @__PURE__ */ new Set(["enforce", "observe"]);
|
|
1347
|
+
var VALID_SIDE_EFFECTS = /* @__PURE__ */ new Set(["pure", "read", "write", "irreversible"]);
|
|
1348
|
+
var KNOWN_TOP_LEVEL = /* @__PURE__ */ new Set([
|
|
1349
|
+
"apiVersion",
|
|
1350
|
+
"kind",
|
|
1351
|
+
"metadata",
|
|
1352
|
+
"defaults",
|
|
1353
|
+
"contracts",
|
|
1354
|
+
"tools",
|
|
1355
|
+
"observability",
|
|
1356
|
+
"observe_alongside"
|
|
1357
|
+
]);
|
|
1358
|
+
var METADATA_NAME_RE = /^[a-z0-9][a-z0-9._-]*$/;
|
|
1359
|
+
var MAX_MESSAGE_LENGTH = 500;
|
|
1360
|
+
function fail(msg) {
|
|
1361
|
+
throw new EdictumConfigError(`Schema validation failed: ${msg}`);
|
|
1362
|
+
}
|
|
1363
|
+
function validateContractFields(data) {
|
|
1364
|
+
for (const key of Object.keys(data)) {
|
|
1365
|
+
if (!KNOWN_TOP_LEVEL.has(key)) fail(`unknown top-level field '${key}'`);
|
|
1366
|
+
}
|
|
1367
|
+
if (data.metadata == null || typeof data.metadata !== "object" || Array.isArray(data.metadata)) {
|
|
1368
|
+
fail("'metadata' is required and must be an object");
|
|
1369
|
+
}
|
|
1370
|
+
const meta = data.metadata;
|
|
1371
|
+
if (meta.name == null || typeof meta.name !== "string") fail("metadata.name is required");
|
|
1372
|
+
const metaName = meta.name;
|
|
1373
|
+
if (metaName.length > 1e4) fail("metadata.name exceeds maximum length");
|
|
1374
|
+
if (!METADATA_NAME_RE.test(metaName)) {
|
|
1375
|
+
fail(
|
|
1376
|
+
`metadata.name must be a lowercase slug (^[a-z0-9][a-z0-9._-]*$), got '${metaName.slice(0, 100)}'`
|
|
1377
|
+
);
|
|
1378
|
+
}
|
|
1379
|
+
if (data.defaults == null || typeof data.defaults !== "object" || Array.isArray(data.defaults)) {
|
|
1380
|
+
fail("'defaults' is required and must be an object");
|
|
1381
|
+
}
|
|
1382
|
+
const mode = data.defaults.mode;
|
|
1383
|
+
if (!VALID_MODES.has(mode)) {
|
|
1384
|
+
fail(`defaults.mode must be 'enforce' or 'observe', got '${String(mode)}'`);
|
|
1385
|
+
}
|
|
1386
|
+
const contracts = data.contracts;
|
|
1387
|
+
if (contracts.length === 0) fail("contracts must contain at least 1 item");
|
|
1388
|
+
if (data.tools != null) {
|
|
1389
|
+
if (typeof data.tools !== "object" || Array.isArray(data.tools)) {
|
|
1390
|
+
fail("'tools' must be a mapping of tool names to descriptors, not an array");
|
|
1391
|
+
}
|
|
1392
|
+
for (const [tn, td] of Object.entries(data.tools)) {
|
|
1393
|
+
if (td == null || typeof td !== "object" || Array.isArray(td)) {
|
|
1394
|
+
fail(`tools.${tn} must be an object with a 'side_effect' field`);
|
|
1395
|
+
}
|
|
1396
|
+
const se = td.side_effect;
|
|
1397
|
+
if (!VALID_SIDE_EFFECTS.has(se)) {
|
|
1398
|
+
fail(
|
|
1399
|
+
`tools.${tn}.side_effect must be 'pure', 'read', 'write', or 'irreversible', got '${String(se)}'`
|
|
1400
|
+
);
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
for (const c of contracts) {
|
|
1405
|
+
if (c == null || typeof c !== "object" || Array.isArray(c)) {
|
|
1406
|
+
fail("every contract must be an object (got null or non-object array element)");
|
|
1407
|
+
}
|
|
1408
|
+
if (c.id == null || typeof c.id !== "string" || c.id.length === 0) {
|
|
1409
|
+
fail("every contract requires a non-empty 'id' string");
|
|
1410
|
+
}
|
|
1411
|
+
const cid = c.id;
|
|
1412
|
+
if (!VALID_CONTRACT_TYPES.has(c.type)) {
|
|
1413
|
+
fail(`contract '${cid}': invalid type '${String(c.type)}'`);
|
|
1414
|
+
}
|
|
1415
|
+
const t = c.type;
|
|
1416
|
+
if (t === "pre" || t === "post") validatePrePost(c, t, cid);
|
|
1417
|
+
else if (t === "session") validateSession(c, cid);
|
|
1418
|
+
else if (t === "sandbox") validateSandboxStructure(c, cid);
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
function validatePrePost(c, t, cid) {
|
|
1422
|
+
if (c.tool == null || typeof c.tool !== "string") {
|
|
1423
|
+
fail(`${t} contract '${cid}' requires 'tool' to be a string`);
|
|
1424
|
+
}
|
|
1425
|
+
if (c.when == null || typeof c.when !== "object" || Array.isArray(c.when)) {
|
|
1426
|
+
fail(
|
|
1427
|
+
`${t} contract '${cid}' requires 'when' to be a mapping (got ${Array.isArray(c.when) ? "array" : typeof c.when})`
|
|
1428
|
+
);
|
|
1429
|
+
}
|
|
1430
|
+
if (c.then == null || typeof c.then !== "object" || Array.isArray(c.then)) {
|
|
1431
|
+
fail(`${t} contract '${cid}' requires 'then' to be a mapping`);
|
|
1432
|
+
}
|
|
1433
|
+
const then = c.then;
|
|
1434
|
+
if (then.effect == null) fail(`${t} contract '${cid}' requires 'then.effect'`);
|
|
1435
|
+
if (then.message == null) fail(`${t} contract '${cid}' requires 'then.message'`);
|
|
1436
|
+
validateMessageLength(then.message, `${t} contract '${cid}'`);
|
|
1437
|
+
const effect = then.effect;
|
|
1438
|
+
if (t === "pre" && !PRE_EFFECTS.has(effect)) {
|
|
1439
|
+
fail(`pre contract '${cid}': effect must be 'deny' or 'approve', got '${effect}'`);
|
|
1440
|
+
}
|
|
1441
|
+
if (t === "post" && !POST_EFFECTS.has(effect)) {
|
|
1442
|
+
fail(`post contract '${cid}': effect must be 'warn', 'redact', or 'deny', got '${effect}'`);
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
function validateSession(c, cid) {
|
|
1446
|
+
if (c.limits == null || typeof c.limits !== "object" || Array.isArray(c.limits)) {
|
|
1447
|
+
fail(`session contract '${cid}' requires 'limits' to be a mapping`);
|
|
1448
|
+
}
|
|
1449
|
+
const lim = c.limits;
|
|
1450
|
+
if (!("max_tool_calls" in lim) && !("max_attempts" in lim) && !("max_calls_per_tool" in lim)) {
|
|
1451
|
+
fail(
|
|
1452
|
+
`session contract '${cid}': limits must have max_tool_calls, max_attempts, or max_calls_per_tool`
|
|
1453
|
+
);
|
|
1454
|
+
}
|
|
1455
|
+
if (c.then == null || typeof c.then !== "object" || Array.isArray(c.then)) {
|
|
1456
|
+
fail(`session contract '${cid}' requires 'then' to be a mapping`);
|
|
1457
|
+
}
|
|
1458
|
+
const then = c.then;
|
|
1459
|
+
if (then.effect !== "deny") {
|
|
1460
|
+
fail(`session contract '${cid}': effect must be 'deny', got '${String(then.effect)}'`);
|
|
1461
|
+
}
|
|
1462
|
+
if (then.message == null) fail(`session contract '${cid}' requires 'then.message'`);
|
|
1463
|
+
validateMessageLength(then.message, `session contract '${cid}'`);
|
|
1464
|
+
}
|
|
1465
|
+
function validateSandboxStructure(c, cid) {
|
|
1466
|
+
if (c.tool == null && c.tools == null) {
|
|
1467
|
+
fail(`sandbox contract '${cid}' requires either 'tool' or 'tools'`);
|
|
1468
|
+
}
|
|
1469
|
+
if (c.tool != null && typeof c.tool !== "string") {
|
|
1470
|
+
fail(`sandbox contract '${cid}': 'tool' must be a string`);
|
|
1471
|
+
}
|
|
1472
|
+
if (c.tools != null && (!Array.isArray(c.tools) || c.tools.length === 0)) {
|
|
1473
|
+
fail(`sandbox contract '${cid}': 'tools' must be a non-empty array`);
|
|
1474
|
+
}
|
|
1475
|
+
if (c.within == null && c.allows == null) {
|
|
1476
|
+
fail(`sandbox contract '${cid}' requires either 'within' or 'allows'`);
|
|
1477
|
+
}
|
|
1478
|
+
if (c.within != null && (!Array.isArray(c.within) || c.within.length === 0)) {
|
|
1479
|
+
fail(`sandbox contract '${cid}': 'within' must be a non-empty array`);
|
|
1480
|
+
}
|
|
1481
|
+
if (c.message == null) fail(`sandbox contract '${cid}' requires 'message'`);
|
|
1482
|
+
validateMessageLength(c.message, `sandbox contract '${cid}'`);
|
|
1483
|
+
}
|
|
1484
|
+
function validateMessageLength(msg, context) {
|
|
1485
|
+
if (typeof msg !== "string") {
|
|
1486
|
+
fail(`${context}: message must be a string`);
|
|
1487
|
+
}
|
|
1488
|
+
if (msg.length > MAX_MESSAGE_LENGTH) {
|
|
1489
|
+
fail(`${context}: message exceeds ${MAX_MESSAGE_LENGTH} characters`);
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
// src/yaml-engine/loader-expression-validators.ts
|
|
1494
|
+
var NUMERIC_OPS = /* @__PURE__ */ new Set(["gt", "gte", "lt", "lte"]);
|
|
1495
|
+
var STRING_OPS = /* @__PURE__ */ new Set(["contains", "starts_with", "ends_with"]);
|
|
1496
|
+
var ARRAY_MIN1_OPS = /* @__PURE__ */ new Set(["in", "not_in", "contains_any", "matches_any"]);
|
|
1497
|
+
function fail2(msg) {
|
|
1498
|
+
throw new EdictumConfigError(`Schema validation failed: ${msg}`);
|
|
1499
|
+
}
|
|
1500
|
+
function validateExpressionShapes(data) {
|
|
1501
|
+
for (const c of data.contracts ?? []) {
|
|
1502
|
+
if (c == null || typeof c !== "object") continue;
|
|
1503
|
+
const contract = c;
|
|
1504
|
+
if (contract.when != null) checkExprShape(contract.when, contract.id ?? "?");
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
var MAX_EXPR_DEPTH = 50;
|
|
1508
|
+
function checkExprShape(expr, cid, depth = 0) {
|
|
1509
|
+
if (depth > MAX_EXPR_DEPTH)
|
|
1510
|
+
fail2(`contract '${cid}': expression nesting exceeds maximum depth (${MAX_EXPR_DEPTH})`);
|
|
1511
|
+
if (expr == null || typeof expr !== "object") return;
|
|
1512
|
+
const e = expr;
|
|
1513
|
+
if ("all" in e) {
|
|
1514
|
+
const a = e.all;
|
|
1515
|
+
if (!Array.isArray(a) || a.length === 0)
|
|
1516
|
+
fail2(`contract '${cid}': 'all' requires a non-empty array`);
|
|
1517
|
+
for (const s of a) checkExprShape(s, cid, depth + 1);
|
|
1518
|
+
return;
|
|
1519
|
+
}
|
|
1520
|
+
if ("any" in e) {
|
|
1521
|
+
const a = e.any;
|
|
1522
|
+
if (!Array.isArray(a) || a.length === 0)
|
|
1523
|
+
fail2(`contract '${cid}': 'any' requires a non-empty array`);
|
|
1524
|
+
for (const s of a) checkExprShape(s, cid, depth + 1);
|
|
1525
|
+
return;
|
|
1526
|
+
}
|
|
1527
|
+
if ("not" in e) {
|
|
1528
|
+
if (e.not == null || typeof e.not !== "object" || Array.isArray(e.not)) {
|
|
1529
|
+
fail2(
|
|
1530
|
+
`contract '${cid}': 'not' requires an expression mapping (got ${e.not === null ? "null" : Array.isArray(e.not) ? "array" : typeof e.not})`
|
|
1531
|
+
);
|
|
1532
|
+
}
|
|
1533
|
+
checkExprShape(e.not, cid, depth + 1);
|
|
1534
|
+
return;
|
|
1535
|
+
}
|
|
1536
|
+
for (const v of Object.values(e)) {
|
|
1537
|
+
if (v == null || typeof v !== "object") continue;
|
|
1538
|
+
if (Array.isArray(v)) {
|
|
1539
|
+
fail2(`contract '${cid}': selector value must be an operator mapping, not an array`);
|
|
1540
|
+
}
|
|
1541
|
+
const op = v;
|
|
1542
|
+
for (const [name, val] of Object.entries(op)) {
|
|
1543
|
+
if (NUMERIC_OPS.has(name) && typeof val !== "number") {
|
|
1544
|
+
fail2(`contract '${cid}': operator '${name}' requires a number, got ${typeof val}`);
|
|
1545
|
+
}
|
|
1546
|
+
if (STRING_OPS.has(name) && typeof val !== "string") {
|
|
1547
|
+
fail2(`contract '${cid}': operator '${name}' requires a string, got ${typeof val}`);
|
|
1548
|
+
}
|
|
1549
|
+
if (ARRAY_MIN1_OPS.has(name) && (!Array.isArray(val) || val.length === 0)) {
|
|
1550
|
+
fail2(`contract '${cid}': operator '${name}' requires a non-empty array`);
|
|
1551
|
+
}
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1285
1556
|
// src/yaml-engine/loader.ts
|
|
1286
1557
|
var MAX_BUNDLE_SIZE = 1048576;
|
|
1287
1558
|
function computeHash(rawBytes) {
|
|
1288
1559
|
return { hex: createHash("sha256").update(rawBytes).digest("hex") };
|
|
1289
1560
|
}
|
|
1290
|
-
function requireYaml() {
|
|
1291
|
-
try {
|
|
1292
|
-
const yaml = __require("js-yaml");
|
|
1293
|
-
return yaml;
|
|
1294
|
-
} catch {
|
|
1295
|
-
throw new EdictumConfigError(
|
|
1296
|
-
"The YAML engine requires js-yaml. Install it with: npm install js-yaml"
|
|
1297
|
-
);
|
|
1298
|
-
}
|
|
1299
|
-
}
|
|
1300
1561
|
function parseYaml(content) {
|
|
1301
|
-
const yaml = requireYaml();
|
|
1302
1562
|
let data;
|
|
1303
1563
|
try {
|
|
1304
|
-
data = yaml.load(content);
|
|
1564
|
+
data = yaml.load(content, { schema: yaml.CORE_SCHEMA });
|
|
1305
1565
|
} catch (e) {
|
|
1306
1566
|
throw new EdictumConfigError(`YAML parse error: ${String(e)}`);
|
|
1307
1567
|
}
|
|
@@ -1312,13 +1572,15 @@ function parseYaml(content) {
|
|
|
1312
1572
|
}
|
|
1313
1573
|
function validateBundle(data) {
|
|
1314
1574
|
validateSchema(data);
|
|
1575
|
+
validateContractFields(data);
|
|
1315
1576
|
validateUniqueIds(data);
|
|
1577
|
+
validateExpressionShapes(data);
|
|
1316
1578
|
validateRegexes(data);
|
|
1317
1579
|
validatePreSelectors(data);
|
|
1318
1580
|
validateSandboxContracts(data);
|
|
1319
1581
|
}
|
|
1320
1582
|
function loadBundle(source) {
|
|
1321
|
-
const resolved =
|
|
1583
|
+
const resolved = realpathSync2(source);
|
|
1322
1584
|
const fileSize = statSync(resolved).size;
|
|
1323
1585
|
if (fileSize > MAX_BUNDLE_SIZE) {
|
|
1324
1586
|
throw new EdictumConfigError(
|
|
@@ -1373,9 +1635,10 @@ function fromYaml(...args) {
|
|
|
1373
1635
|
policyVersion = entry[1].hex;
|
|
1374
1636
|
report = { overriddenContracts: [], observeContracts: [] };
|
|
1375
1637
|
} else {
|
|
1376
|
-
const bundleTuples = loaded.map(
|
|
1377
|
-
|
|
1378
|
-
|
|
1638
|
+
const bundleTuples = loaded.map(([data], i) => [
|
|
1639
|
+
data,
|
|
1640
|
+
paths[i]
|
|
1641
|
+
]);
|
|
1379
1642
|
const composed = composeBundles(...bundleTuples);
|
|
1380
1643
|
bundleData = composed.bundle;
|
|
1381
1644
|
report = composed.report;
|
|
@@ -1496,15 +1759,9 @@ var Edictum = class _Edictum {
|
|
|
1496
1759
|
this._approvalBackend = options.approvalBackend ?? null;
|
|
1497
1760
|
this._localSink = new CollectingAuditSink();
|
|
1498
1761
|
if (Array.isArray(options.auditSink)) {
|
|
1499
|
-
this.auditSink = new CompositeSink([
|
|
1500
|
-
this._localSink,
|
|
1501
|
-
...options.auditSink
|
|
1502
|
-
]);
|
|
1762
|
+
this.auditSink = new CompositeSink([this._localSink, ...options.auditSink]);
|
|
1503
1763
|
} else if (options.auditSink != null) {
|
|
1504
|
-
this.auditSink = new CompositeSink([
|
|
1505
|
-
this._localSink,
|
|
1506
|
-
options.auditSink
|
|
1507
|
-
]);
|
|
1764
|
+
this.auditSink = new CompositeSink([this._localSink, options.auditSink]);
|
|
1508
1765
|
} else {
|
|
1509
1766
|
this.auditSink = this._localSink;
|
|
1510
1767
|
}
|
|
@@ -1602,24 +1859,16 @@ var Edictum = class _Edictum {
|
|
|
1602
1859
|
}
|
|
1603
1860
|
getHooks(phase, envelope) {
|
|
1604
1861
|
const hooks = phase === "before" ? this._beforeHooks : this._afterHooks;
|
|
1605
|
-
return hooks.filter(
|
|
1606
|
-
(h) => h.tool === "*" || fnmatch(envelope.toolName, h.tool)
|
|
1607
|
-
);
|
|
1862
|
+
return hooks.filter((h) => h.tool === "*" || fnmatch(envelope.toolName, h.tool));
|
|
1608
1863
|
}
|
|
1609
1864
|
// -----------------------------------------------------------------------
|
|
1610
1865
|
// Contract accessors -- enforce mode
|
|
1611
1866
|
// -----------------------------------------------------------------------
|
|
1612
1867
|
getPreconditions(envelope) {
|
|
1613
|
-
return _Edictum._filterByTool(
|
|
1614
|
-
this._state.preconditions,
|
|
1615
|
-
envelope
|
|
1616
|
-
);
|
|
1868
|
+
return _Edictum._filterByTool(this._state.preconditions, envelope);
|
|
1617
1869
|
}
|
|
1618
1870
|
getPostconditions(envelope) {
|
|
1619
|
-
return _Edictum._filterByTool(
|
|
1620
|
-
this._state.postconditions,
|
|
1621
|
-
envelope
|
|
1622
|
-
);
|
|
1871
|
+
return _Edictum._filterByTool(this._state.postconditions, envelope);
|
|
1623
1872
|
}
|
|
1624
1873
|
getSessionContracts() {
|
|
1625
1874
|
return [...this._state.sessionContracts];
|
|
@@ -1679,12 +1928,16 @@ var Edictum = class _Edictum {
|
|
|
1679
1928
|
const edictumType = raw._edictum_type;
|
|
1680
1929
|
const isObserve = raw._edictum_observe ?? raw._edictum_shadow ?? false;
|
|
1681
1930
|
if (edictumType != null) {
|
|
1682
|
-
_Edictum._classifyInternal(
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1931
|
+
_Edictum._classifyInternal(raw, edictumType, isObserve, {
|
|
1932
|
+
pre,
|
|
1933
|
+
post,
|
|
1934
|
+
session,
|
|
1935
|
+
sandbox,
|
|
1936
|
+
oPre,
|
|
1937
|
+
oPost,
|
|
1938
|
+
oSession,
|
|
1939
|
+
oSandbox
|
|
1940
|
+
});
|
|
1688
1941
|
} else if (isSessionContract(item)) {
|
|
1689
1942
|
const name = raw.name ?? "anonymous";
|
|
1690
1943
|
session.push({
|
|
@@ -1742,9 +1995,12 @@ var Edictum = class _Edictum {
|
|
|
1742
1995
|
static _classifyInternal(raw, edictumType, isObserve, lists) {
|
|
1743
1996
|
const target = isObserve ? { pre: lists.oPre, post: lists.oPost, session: lists.oSession, sandbox: lists.oSandbox } : { pre: lists.pre, post: lists.post, session: lists.session, sandbox: lists.sandbox };
|
|
1744
1997
|
if (edictumType === "precondition") target.pre.push(raw);
|
|
1745
|
-
else if (edictumType === "postcondition")
|
|
1746
|
-
|
|
1747
|
-
else if (edictumType === "
|
|
1998
|
+
else if (edictumType === "postcondition")
|
|
1999
|
+
target.post.push(raw);
|
|
2000
|
+
else if (edictumType === "session_contract")
|
|
2001
|
+
target.session.push(raw);
|
|
2002
|
+
else if (edictumType === "sandbox")
|
|
2003
|
+
target.sandbox.push(raw);
|
|
1748
2004
|
else {
|
|
1749
2005
|
throw new EdictumConfigError(
|
|
1750
2006
|
`Unknown _edictum_type "${edictumType}". Expected "precondition", "postcondition", "session_contract", or "sandbox".`
|
|
@@ -1786,7 +2042,7 @@ var Edictum = class _Edictum {
|
|
|
1786
2042
|
// -----------------------------------------------------------------------
|
|
1787
2043
|
/** Execute a tool call with full governance pipeline. */
|
|
1788
2044
|
async run(toolName, args, toolCallable, options) {
|
|
1789
|
-
const { run: run2 } = await import("./runner-
|
|
2045
|
+
const { run: run2 } = await import("./runner-JCAQMF6O.mjs");
|
|
1790
2046
|
return run2(this, toolName, args, toolCallable, options);
|
|
1791
2047
|
}
|
|
1792
2048
|
/**
|
|
@@ -1796,15 +2052,11 @@ var Edictum = class _Edictum {
|
|
|
1796
2052
|
* Session contracts are skipped.
|
|
1797
2053
|
*/
|
|
1798
2054
|
evaluate(toolName, args, options) {
|
|
1799
|
-
return import("./dry-run-
|
|
1800
|
-
({ evaluate }) => evaluate(this, toolName, args, options)
|
|
1801
|
-
);
|
|
2055
|
+
return import("./dry-run-JTRNTZA5.mjs").then(({ evaluate }) => evaluate(this, toolName, args, options));
|
|
1802
2056
|
}
|
|
1803
2057
|
/** Evaluate a batch of tool calls. Thin wrapper over evaluate(). */
|
|
1804
2058
|
evaluateBatch(calls) {
|
|
1805
|
-
return import("./dry-run-
|
|
1806
|
-
({ evaluateBatch }) => evaluateBatch(this, calls)
|
|
1807
|
-
);
|
|
2059
|
+
return import("./dry-run-JTRNTZA5.mjs").then(({ evaluateBatch }) => evaluateBatch(this, calls));
|
|
1808
2060
|
}
|
|
1809
2061
|
static fromYaml(...args) {
|
|
1810
2062
|
return fromYaml(...args);
|