agent-contracts 0.11.8 → 0.12.1
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 +13 -1
- package/dist/cli.js +242 -33
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +58 -9
- package/dist/index.js +230 -31
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -998,7 +998,7 @@ Each `context` type provides a different rendering scope:
|
|
|
998
998
|
|
|
999
999
|
| Context | Scope | Output | Key variables |
|
|
1000
1000
|
|---------|-------|--------|---------------|
|
|
1001
|
-
| `system` | Single file | `output` as-is | `system`, `dsl` |
|
|
1001
|
+
| `system` | Single file | `output` as-is | `system`, `dsl`, `guardrailEnforcement`\*, `bindings`\* |
|
|
1002
1002
|
| `agent` | Per agent | `{agent.id}` in output path | `agent`, `receivableTasks`, `delegatableTasks`, `relatedArtifacts`, `relatedTools`, `relatedHandoffTypes`, `mergedBehavior`, `dsl` |
|
|
1003
1003
|
| `task` | Per task | `{task.id}` in output path | `task`, `targetAgent`, `dsl` |
|
|
1004
1004
|
| `artifact` | Per artifact | `{artifact.id}` in output path | `artifact`, `relatedTools`, `relatedValidations`, `producerAgents`, `consumerAgents`, `editorAgents`, `createdInWorkflows`, `dsl` |
|
|
@@ -1027,6 +1027,18 @@ Each `context` type provides a different rendering scope:
|
|
|
1027
1027
|
* `producerAgents` / `consumerAgents` / `editorAgents` — resolved agent records
|
|
1028
1028
|
* `createdInWorkflows` — workflow phases where this artifact is written
|
|
1029
1029
|
|
|
1030
|
+
**`system` context** includes binding-aware guardrail enforcement data when `bindings` and `active_guardrail_policy` are configured:
|
|
1031
|
+
|
|
1032
|
+
* `guardrailEnforcement` — array of enforcement entries, each with `guardrail_id`, `description`, `severity`, `action`, scoped entities (`scoped_agents`, `scoped_tasks`, `scoped_workflows`, `scoped_tools`, `scoped_artifacts`), `allow_override`, `override_requires`, `trigger` (from binding matcher type), and `escalation`
|
|
1033
|
+
* `bindings` — array of loaded `SoftwareBinding` objects
|
|
1034
|
+
|
|
1035
|
+
These fields are only populated when the config specifies `bindings` and `active_guardrail_policy`. Existing templates that do not reference these fields are unaffected.
|
|
1036
|
+
|
|
1037
|
+
**Matrix helpers** are available in `context: system` templates:
|
|
1038
|
+
|
|
1039
|
+
* `guardrailCoverageMatrix` — generates a Guardrail Coverage Matrix table (guardrail × severity × action × scoped entities × trigger × override × escalation)
|
|
1040
|
+
* `taskGuardrailMatrix` — generates a Task × Guardrail cross-reference table showing which action applies to each task
|
|
1041
|
+
|
|
1030
1042
|
**`tool` context** resolves agent and artifact references:
|
|
1031
1043
|
|
|
1032
1044
|
* `invokableAgents` — agents listed in `invokable_by`
|
package/dist/cli.js
CHANGED
|
@@ -2243,7 +2243,7 @@ var require_lodash = __commonJS({
|
|
|
2243
2243
|
} else {
|
|
2244
2244
|
newValue = [];
|
|
2245
2245
|
}
|
|
2246
|
-
} else if (
|
|
2246
|
+
} else if (isPlainObject2(srcValue) || isArguments(srcValue)) {
|
|
2247
2247
|
newValue = objValue;
|
|
2248
2248
|
if (isArguments(objValue)) {
|
|
2249
2249
|
newValue = toPlainObject(objValue);
|
|
@@ -3207,7 +3207,7 @@ var require_lodash = __commonJS({
|
|
|
3207
3207
|
return objValue;
|
|
3208
3208
|
}
|
|
3209
3209
|
function customOmitClone(value) {
|
|
3210
|
-
return
|
|
3210
|
+
return isPlainObject2(value) ? undefined2 : value;
|
|
3211
3211
|
}
|
|
3212
3212
|
function equalArrays(array, other, bitmask, customizer, equalFunc, stack) {
|
|
3213
3213
|
var isPartial = bitmask & COMPARE_PARTIAL_FLAG, arrLength = array.length, othLength = other.length;
|
|
@@ -4708,7 +4708,7 @@ var require_lodash = __commonJS({
|
|
|
4708
4708
|
var isBuffer = nativeIsBuffer || stubFalse;
|
|
4709
4709
|
var isDate = nodeIsDate ? baseUnary(nodeIsDate) : baseIsDate;
|
|
4710
4710
|
function isElement(value) {
|
|
4711
|
-
return isObjectLike(value) && value.nodeType === 1 && !
|
|
4711
|
+
return isObjectLike(value) && value.nodeType === 1 && !isPlainObject2(value);
|
|
4712
4712
|
}
|
|
4713
4713
|
function isEmpty(value) {
|
|
4714
4714
|
if (value == null) {
|
|
@@ -4744,7 +4744,7 @@ var require_lodash = __commonJS({
|
|
|
4744
4744
|
return false;
|
|
4745
4745
|
}
|
|
4746
4746
|
var tag = baseGetTag(value);
|
|
4747
|
-
return tag == errorTag || tag == domExcTag || typeof value.message == "string" && typeof value.name == "string" && !
|
|
4747
|
+
return tag == errorTag || tag == domExcTag || typeof value.message == "string" && typeof value.name == "string" && !isPlainObject2(value);
|
|
4748
4748
|
}
|
|
4749
4749
|
function isFinite2(value) {
|
|
4750
4750
|
return typeof value == "number" && nativeIsFinite(value);
|
|
@@ -4795,7 +4795,7 @@ var require_lodash = __commonJS({
|
|
|
4795
4795
|
function isNumber(value) {
|
|
4796
4796
|
return typeof value == "number" || isObjectLike(value) && baseGetTag(value) == numberTag;
|
|
4797
4797
|
}
|
|
4798
|
-
function
|
|
4798
|
+
function isPlainObject2(value) {
|
|
4799
4799
|
if (!isObjectLike(value) || baseGetTag(value) != objectTag) {
|
|
4800
4800
|
return false;
|
|
4801
4801
|
}
|
|
@@ -5866,7 +5866,7 @@ var require_lodash = __commonJS({
|
|
|
5866
5866
|
lodash.isNumber = isNumber;
|
|
5867
5867
|
lodash.isObject = isObject2;
|
|
5868
5868
|
lodash.isObjectLike = isObjectLike;
|
|
5869
|
-
lodash.isPlainObject =
|
|
5869
|
+
lodash.isPlainObject = isPlainObject2;
|
|
5870
5870
|
lodash.isRegExp = isRegExp;
|
|
5871
5871
|
lodash.isSafeInteger = isSafeInteger;
|
|
5872
5872
|
lodash.isSet = isSet;
|
|
@@ -17506,10 +17506,10 @@ var require_immer_cjs_development = __commonJS({
|
|
|
17506
17506
|
function isDraftable(value) {
|
|
17507
17507
|
var _value$constructor;
|
|
17508
17508
|
if (!value) return false;
|
|
17509
|
-
return
|
|
17509
|
+
return isPlainObject2(value) || Array.isArray(value) || !!value[DRAFTABLE] || !!((_value$constructor = value.constructor) === null || _value$constructor === void 0 ? void 0 : _value$constructor[DRAFTABLE]) || isMap(value) || isSet(value);
|
|
17510
17510
|
}
|
|
17511
17511
|
var objectCtorString = /* @__PURE__ */ Object.prototype.constructor.toString();
|
|
17512
|
-
function
|
|
17512
|
+
function isPlainObject2(value) {
|
|
17513
17513
|
if (!value || typeof value !== "object") return false;
|
|
17514
17514
|
var proto = Object.getPrototypeOf(value);
|
|
17515
17515
|
if (proto === null) {
|
|
@@ -40192,10 +40192,10 @@ var require_object_inspect = __commonJS({
|
|
|
40192
40192
|
}
|
|
40193
40193
|
if (!isDate(obj) && !isRegExp(obj)) {
|
|
40194
40194
|
var ys = arrObjKeys(obj, inspect);
|
|
40195
|
-
var
|
|
40195
|
+
var isPlainObject2 = gPO ? gPO(obj) === Object.prototype : obj instanceof Object || obj.constructor === Object;
|
|
40196
40196
|
var protoTag = obj instanceof Object ? "" : "null prototype";
|
|
40197
|
-
var stringTag = !
|
|
40198
|
-
var constructorTag =
|
|
40197
|
+
var stringTag = !isPlainObject2 && toStringTag2 && Object(obj) === obj && toStringTag2 in obj ? $slice.call(toStr(obj), 8, -1) : protoTag ? "Object" : "";
|
|
40198
|
+
var constructorTag = isPlainObject2 || typeof obj.constructor !== "function" ? "" : obj.constructor.name ? obj.constructor.name + " " : "";
|
|
40199
40199
|
var tag = constructorTag + (stringTag || protoTag ? "[" + $join.call($concat.call([], stringTag || [], protoTag || []), ": ") + "] " : "");
|
|
40200
40200
|
if (ys.length === 0) {
|
|
40201
40201
|
return tag + "{}";
|
|
@@ -44530,7 +44530,7 @@ var require_lodash3 = __commonJS({
|
|
|
44530
44530
|
} else {
|
|
44531
44531
|
newValue = [];
|
|
44532
44532
|
}
|
|
44533
|
-
} else if (
|
|
44533
|
+
} else if (isPlainObject2(srcValue) || isArguments(srcValue)) {
|
|
44534
44534
|
newValue = objValue;
|
|
44535
44535
|
if (isArguments(objValue)) {
|
|
44536
44536
|
newValue = toPlainObject(objValue);
|
|
@@ -45501,7 +45501,7 @@ var require_lodash3 = __commonJS({
|
|
|
45501
45501
|
return objValue;
|
|
45502
45502
|
}
|
|
45503
45503
|
function customOmitClone(value) {
|
|
45504
|
-
return
|
|
45504
|
+
return isPlainObject2(value) ? undefined2 : value;
|
|
45505
45505
|
}
|
|
45506
45506
|
function equalArrays(array, other, bitmask, customizer, equalFunc, stack) {
|
|
45507
45507
|
var isPartial = bitmask & COMPARE_PARTIAL_FLAG, arrLength = array.length, othLength = other.length;
|
|
@@ -47002,7 +47002,7 @@ var require_lodash3 = __commonJS({
|
|
|
47002
47002
|
var isBuffer = nativeIsBuffer || stubFalse;
|
|
47003
47003
|
var isDate = nodeIsDate ? baseUnary(nodeIsDate) : baseIsDate;
|
|
47004
47004
|
function isElement(value) {
|
|
47005
|
-
return isObjectLike(value) && value.nodeType === 1 && !
|
|
47005
|
+
return isObjectLike(value) && value.nodeType === 1 && !isPlainObject2(value);
|
|
47006
47006
|
}
|
|
47007
47007
|
function isEmpty(value) {
|
|
47008
47008
|
if (value == null) {
|
|
@@ -47038,7 +47038,7 @@ var require_lodash3 = __commonJS({
|
|
|
47038
47038
|
return false;
|
|
47039
47039
|
}
|
|
47040
47040
|
var tag = baseGetTag(value);
|
|
47041
|
-
return tag == errorTag || tag == domExcTag || typeof value.message == "string" && typeof value.name == "string" && !
|
|
47041
|
+
return tag == errorTag || tag == domExcTag || typeof value.message == "string" && typeof value.name == "string" && !isPlainObject2(value);
|
|
47042
47042
|
}
|
|
47043
47043
|
function isFinite2(value) {
|
|
47044
47044
|
return typeof value == "number" && nativeIsFinite(value);
|
|
@@ -47089,7 +47089,7 @@ var require_lodash3 = __commonJS({
|
|
|
47089
47089
|
function isNumber(value) {
|
|
47090
47090
|
return typeof value == "number" || isObjectLike(value) && baseGetTag(value) == numberTag;
|
|
47091
47091
|
}
|
|
47092
|
-
function
|
|
47092
|
+
function isPlainObject2(value) {
|
|
47093
47093
|
if (!isObjectLike(value) || baseGetTag(value) != objectTag) {
|
|
47094
47094
|
return false;
|
|
47095
47095
|
}
|
|
@@ -48155,7 +48155,7 @@ var require_lodash3 = __commonJS({
|
|
|
48155
48155
|
lodash.isNumber = isNumber;
|
|
48156
48156
|
lodash.isObject = isObject2;
|
|
48157
48157
|
lodash.isObjectLike = isObjectLike;
|
|
48158
|
-
lodash.isPlainObject =
|
|
48158
|
+
lodash.isPlainObject = isPlainObject2;
|
|
48159
48159
|
lodash.isRegExp = isRegExp;
|
|
48160
48160
|
lodash.isSafeInteger = isSafeInteger;
|
|
48161
48161
|
lodash.isSet = isSet;
|
|
@@ -50527,13 +50527,20 @@ var BindingOutputSchema = z3.object({
|
|
|
50527
50527
|
target: z3.string(),
|
|
50528
50528
|
template: z3.string().optional(),
|
|
50529
50529
|
inline_template: z3.string().optional(),
|
|
50530
|
+
source: z3.string().optional(),
|
|
50530
50531
|
mode: z3.enum(["write", "patch"]).default("write"),
|
|
50532
|
+
format: z3.enum(["json", "yaml", "text"]).optional(),
|
|
50533
|
+
patch_strategy: z3.enum(["deep_merge", "append"]).optional(),
|
|
50534
|
+
array_merge_key: z3.string().optional(),
|
|
50531
50535
|
group_by: z3.string().optional(),
|
|
50532
50536
|
executable: z3.boolean().optional(),
|
|
50533
50537
|
skip_empty: z3.boolean().optional()
|
|
50534
50538
|
}).passthrough().refine(
|
|
50535
|
-
(data) =>
|
|
50536
|
-
|
|
50539
|
+
(data) => {
|
|
50540
|
+
const count = [data.template, data.inline_template, data.source].filter(Boolean).length;
|
|
50541
|
+
return count === 1 || count === 0;
|
|
50542
|
+
},
|
|
50543
|
+
{ message: "Only one of template, inline_template, or source may be specified" }
|
|
50537
50544
|
);
|
|
50538
50545
|
var ReportingSchema = z3.object({
|
|
50539
50546
|
commands: z3.record(z3.string(), z3.string()),
|
|
@@ -53574,8 +53581,52 @@ import { dirname as dirname5 } from "path";
|
|
|
53574
53581
|
import Handlebars from "handlebars";
|
|
53575
53582
|
|
|
53576
53583
|
// src/renderer/context.ts
|
|
53577
|
-
function buildSystemContext(dsl) {
|
|
53578
|
-
|
|
53584
|
+
function buildSystemContext(dsl, options) {
|
|
53585
|
+
const ctx = { system: dsl.system, dsl };
|
|
53586
|
+
if (options?.loadedBindings && options.loadedBindings.length > 0) {
|
|
53587
|
+
ctx.bindings = options.loadedBindings.map((lb) => lb.binding);
|
|
53588
|
+
const policyName = options.activeGuardrailPolicy;
|
|
53589
|
+
const policy = policyName ? dsl.guardrail_policies[policyName] : void 0;
|
|
53590
|
+
if (policy) {
|
|
53591
|
+
ctx.guardrailEnforcement = buildGuardrailEnforcement(dsl, policy, options.loadedBindings);
|
|
53592
|
+
}
|
|
53593
|
+
}
|
|
53594
|
+
return ctx;
|
|
53595
|
+
}
|
|
53596
|
+
function buildGuardrailEnforcement(dsl, policy, loadedBindings) {
|
|
53597
|
+
const entries = [];
|
|
53598
|
+
const bindingTriggers = /* @__PURE__ */ new Map();
|
|
53599
|
+
for (const lb of loadedBindings) {
|
|
53600
|
+
const impl = lb.binding.guardrail_impl ?? {};
|
|
53601
|
+
for (const [guardrailId, gi] of Object.entries(impl)) {
|
|
53602
|
+
for (const check of gi.checks) {
|
|
53603
|
+
if (check.matcher) {
|
|
53604
|
+
bindingTriggers.set(guardrailId, check.matcher.type);
|
|
53605
|
+
}
|
|
53606
|
+
}
|
|
53607
|
+
}
|
|
53608
|
+
}
|
|
53609
|
+
for (const rule of policy.rules) {
|
|
53610
|
+
const guardrail = dsl.guardrails[rule.guardrail];
|
|
53611
|
+
if (!guardrail) continue;
|
|
53612
|
+
const scope = guardrail.scope ?? {};
|
|
53613
|
+
entries.push({
|
|
53614
|
+
guardrail_id: rule.guardrail,
|
|
53615
|
+
description: guardrail.description,
|
|
53616
|
+
severity: rule.severity,
|
|
53617
|
+
action: rule.action,
|
|
53618
|
+
scoped_agents: scope.agents ?? [],
|
|
53619
|
+
scoped_tasks: scope.tasks ?? [],
|
|
53620
|
+
scoped_workflows: scope.workflows ?? [],
|
|
53621
|
+
scoped_tools: scope.tools ?? [],
|
|
53622
|
+
scoped_artifacts: scope.artifacts ?? [],
|
|
53623
|
+
allow_override: rule.allow_override,
|
|
53624
|
+
override_requires: rule.override_requires ?? [],
|
|
53625
|
+
trigger: bindingTriggers.get(rule.guardrail) ?? null,
|
|
53626
|
+
escalation: rule.escalation ? { target: rule.escalation.target, condition: rule.escalation.condition } : null
|
|
53627
|
+
});
|
|
53628
|
+
}
|
|
53629
|
+
return entries;
|
|
53579
53630
|
}
|
|
53580
53631
|
function buildTaskContext(dsl, taskId) {
|
|
53581
53632
|
const taskDef = dsl.tasks[taskId];
|
|
@@ -54630,6 +54681,50 @@ Handlebars.registerHelper(
|
|
|
54630
54681
|
return generateOverviewFlowchart(dsl);
|
|
54631
54682
|
}
|
|
54632
54683
|
);
|
|
54684
|
+
Handlebars.registerHelper(
|
|
54685
|
+
"guardrailCoverageMatrix",
|
|
54686
|
+
function() {
|
|
54687
|
+
const enforcement = this["guardrailEnforcement"];
|
|
54688
|
+
if (!enforcement || enforcement.length === 0) return "";
|
|
54689
|
+
const header = "| Guardrail | Severity | Action | Agents | Tasks | Workflows | Tools | Artifacts | Trigger | Override | Escalation |";
|
|
54690
|
+
const divider = "|-----------|----------|--------|--------|-------|-----------|-------|-----------|---------|----------|------------|";
|
|
54691
|
+
const rows = enforcement.map((e3) => {
|
|
54692
|
+
const agents = e3["scoped_agents"]?.join(", ") || "\u2014";
|
|
54693
|
+
const tasks = e3["scoped_tasks"]?.join(", ") || "\u2014";
|
|
54694
|
+
const workflows = e3["scoped_workflows"]?.join(", ") || "\u2014";
|
|
54695
|
+
const tools = e3["scoped_tools"]?.join(", ") || "\u2014";
|
|
54696
|
+
const artifacts = e3["scoped_artifacts"]?.join(", ") || "\u2014";
|
|
54697
|
+
const trigger = e3["trigger"] || "\u2014";
|
|
54698
|
+
const override = e3["allow_override"] ? "yes" : "no";
|
|
54699
|
+
const esc = e3["escalation"];
|
|
54700
|
+
const escalation = esc ? esc["target"] : "\u2014";
|
|
54701
|
+
return `| ${e3["guardrail_id"]} | ${e3["severity"]} | ${e3["action"]} | ${agents} | ${tasks} | ${workflows} | ${tools} | ${artifacts} | ${trigger} | ${override} | ${escalation} |`;
|
|
54702
|
+
});
|
|
54703
|
+
return [header, divider, ...rows].join("\n");
|
|
54704
|
+
}
|
|
54705
|
+
);
|
|
54706
|
+
Handlebars.registerHelper(
|
|
54707
|
+
"taskGuardrailMatrix",
|
|
54708
|
+
function() {
|
|
54709
|
+
const dsl = this["dsl"];
|
|
54710
|
+
const enforcement = this["guardrailEnforcement"];
|
|
54711
|
+
if (!dsl || !enforcement || enforcement.length === 0) return "";
|
|
54712
|
+
const taskIds = Object.keys(dsl.tasks);
|
|
54713
|
+
if (taskIds.length === 0) return "";
|
|
54714
|
+
const guardrailIds = enforcement.map((e3) => e3["guardrail_id"]);
|
|
54715
|
+
const header = `| Task | ${guardrailIds.join(" | ")} |`;
|
|
54716
|
+
const divider = `|------|${guardrailIds.map(() => "------").join("|")}|`;
|
|
54717
|
+
const rows = taskIds.map((taskId) => {
|
|
54718
|
+
const cells = enforcement.map((e3) => {
|
|
54719
|
+
const scopedTasks = e3["scoped_tasks"];
|
|
54720
|
+
if (scopedTasks.length > 0 && !scopedTasks.includes(taskId)) return "n/a";
|
|
54721
|
+
return e3["action"];
|
|
54722
|
+
});
|
|
54723
|
+
return `| ${taskId} | ${cells.join(" | ")} |`;
|
|
54724
|
+
});
|
|
54725
|
+
return [header, divider, ...rows].join("\n");
|
|
54726
|
+
}
|
|
54727
|
+
);
|
|
54633
54728
|
function getDslSection(dsl, context) {
|
|
54634
54729
|
const sectionMap = {
|
|
54635
54730
|
agent: dsl.agents,
|
|
@@ -54694,13 +54789,13 @@ async function removeIfExists(filePath) {
|
|
|
54694
54789
|
} catch {
|
|
54695
54790
|
}
|
|
54696
54791
|
}
|
|
54697
|
-
async function renderFromConfig(dsl, renderTargets) {
|
|
54792
|
+
async function renderFromConfig(dsl, renderTargets, options) {
|
|
54698
54793
|
const outputFiles = [];
|
|
54699
54794
|
for (const target of renderTargets) {
|
|
54700
54795
|
const templateContent = await loadTemplate(target.template);
|
|
54701
54796
|
const compiled = Handlebars.compile(templateContent, { noEscape: false });
|
|
54702
54797
|
if (target.context === "system") {
|
|
54703
|
-
const ctx = buildSystemContext(dsl);
|
|
54798
|
+
const ctx = buildSystemContext(dsl, options);
|
|
54704
54799
|
const output = compiled(ctx);
|
|
54705
54800
|
if (target.skip_empty && isEffectivelyEmpty(output)) {
|
|
54706
54801
|
await removeIfExists(target.output);
|
|
@@ -54749,13 +54844,13 @@ function checkExpectedVsExisting(expected, outputPath, skipEmpty, diffs) {
|
|
|
54749
54844
|
diffs.push(outputPath);
|
|
54750
54845
|
});
|
|
54751
54846
|
}
|
|
54752
|
-
async function checkDriftFromConfig(dsl, renderTargets) {
|
|
54847
|
+
async function checkDriftFromConfig(dsl, renderTargets, options) {
|
|
54753
54848
|
const diffs = [];
|
|
54754
54849
|
for (const target of renderTargets) {
|
|
54755
54850
|
const templateContent = await loadTemplate(target.template);
|
|
54756
54851
|
const compiled = Handlebars.compile(templateContent, { noEscape: false });
|
|
54757
54852
|
if (target.context === "system") {
|
|
54758
|
-
const ctx = buildSystemContext(dsl);
|
|
54853
|
+
const ctx = buildSystemContext(dsl, options);
|
|
54759
54854
|
const expected = compiled(ctx);
|
|
54760
54855
|
await checkExpectedVsExisting(expected, target.output, target.skip_empty, diffs);
|
|
54761
54856
|
} else {
|
|
@@ -54793,10 +54888,19 @@ var renderCommand = new Command4("render").description("Render resolved DSL with
|
|
|
54793
54888
|
);
|
|
54794
54889
|
process.exit(1);
|
|
54795
54890
|
}
|
|
54891
|
+
let renderOptions;
|
|
54892
|
+
if (config.bindings.length > 0) {
|
|
54893
|
+
const loadedBindings = await loadBindings(config.bindings);
|
|
54894
|
+
renderOptions = {
|
|
54895
|
+
loadedBindings,
|
|
54896
|
+
activeGuardrailPolicy: config.activeGuardrailPolicy
|
|
54897
|
+
};
|
|
54898
|
+
}
|
|
54796
54899
|
if (opts.check) {
|
|
54797
54900
|
const drift = await checkDriftFromConfig(
|
|
54798
54901
|
schemaResult.data,
|
|
54799
|
-
config.renders
|
|
54902
|
+
config.renders,
|
|
54903
|
+
renderOptions
|
|
54800
54904
|
);
|
|
54801
54905
|
if (drift.hasDrift) {
|
|
54802
54906
|
process.stderr.write("Drift detected in the following files:\n");
|
|
@@ -54812,7 +54916,8 @@ var renderCommand = new Command4("render").description("Render resolved DSL with
|
|
|
54812
54916
|
} else {
|
|
54813
54917
|
const files = await renderFromConfig(
|
|
54814
54918
|
schemaResult.data,
|
|
54815
|
-
config.renders
|
|
54919
|
+
config.renders,
|
|
54920
|
+
renderOptions
|
|
54816
54921
|
);
|
|
54817
54922
|
if (!opts.quiet) {
|
|
54818
54923
|
process.stdout.write(`Rendered ${files.length} file(s):
|
|
@@ -54924,9 +55029,10 @@ var checkCommand = new Command5("check").description("Run full pipeline: resolve
|
|
|
54924
55029
|
import { Command as Command6 } from "commander";
|
|
54925
55030
|
|
|
54926
55031
|
// src/guardrail-generator/generator.ts
|
|
54927
|
-
import { readFile as readFile5, writeFile as writeFile2, mkdir as mkdir2, chmod, unlink as unlink2 } from "fs/promises";
|
|
55032
|
+
import { readFile as readFile5, writeFile as writeFile2, mkdir as mkdir2, chmod, unlink as unlink2, copyFile } from "fs/promises";
|
|
54928
55033
|
import { resolve as resolve6, dirname as dirname6 } from "path";
|
|
54929
55034
|
import Handlebars2 from "handlebars";
|
|
55035
|
+
import YAML from "yaml";
|
|
54930
55036
|
|
|
54931
55037
|
// src/guardrail-generator/resolve-checks.ts
|
|
54932
55038
|
function resolveChecks(dsl, binding, policy) {
|
|
@@ -54998,6 +55104,81 @@ Handlebars2.registerHelper(
|
|
|
54998
55104
|
return new Handlebars2.SafeString(result);
|
|
54999
55105
|
}
|
|
55000
55106
|
);
|
|
55107
|
+
function isPlainObject(val) {
|
|
55108
|
+
return typeof val === "object" && val !== null && !Array.isArray(val);
|
|
55109
|
+
}
|
|
55110
|
+
function deepMergeArrays(existing, incoming, mergeKey) {
|
|
55111
|
+
if (!mergeKey) return [...existing, ...incoming];
|
|
55112
|
+
const merged = [...existing];
|
|
55113
|
+
for (const item of incoming) {
|
|
55114
|
+
if (isPlainObject(item) && mergeKey in item) {
|
|
55115
|
+
const idx = merged.findIndex(
|
|
55116
|
+
(e3) => isPlainObject(e3) && e3[mergeKey] === item[mergeKey]
|
|
55117
|
+
);
|
|
55118
|
+
if (idx >= 0) {
|
|
55119
|
+
merged[idx] = item;
|
|
55120
|
+
} else {
|
|
55121
|
+
merged.push(item);
|
|
55122
|
+
}
|
|
55123
|
+
} else {
|
|
55124
|
+
merged.push(item);
|
|
55125
|
+
}
|
|
55126
|
+
}
|
|
55127
|
+
return merged;
|
|
55128
|
+
}
|
|
55129
|
+
function deepMerge(existing, incoming, arrayMergeKey) {
|
|
55130
|
+
if (Array.isArray(existing) && Array.isArray(incoming)) {
|
|
55131
|
+
return deepMergeArrays(existing, incoming, arrayMergeKey);
|
|
55132
|
+
}
|
|
55133
|
+
if (isPlainObject(existing) && isPlainObject(incoming)) {
|
|
55134
|
+
const result = { ...existing };
|
|
55135
|
+
for (const [key, val] of Object.entries(incoming)) {
|
|
55136
|
+
result[key] = key in result ? deepMerge(result[key], val, arrayMergeKey) : val;
|
|
55137
|
+
}
|
|
55138
|
+
return result;
|
|
55139
|
+
}
|
|
55140
|
+
return incoming;
|
|
55141
|
+
}
|
|
55142
|
+
function parseContent(raw, format2) {
|
|
55143
|
+
if (format2 === "json") return JSON.parse(raw);
|
|
55144
|
+
if (format2 === "yaml") return YAML.parse(raw);
|
|
55145
|
+
throw new Error(`Unsupported format for patch parsing: ${format2}`);
|
|
55146
|
+
}
|
|
55147
|
+
function serializeContent(data, format2) {
|
|
55148
|
+
if (format2 === "json") return JSON.stringify(data, null, 2) + "\n";
|
|
55149
|
+
if (format2 === "yaml") return YAML.stringify(data);
|
|
55150
|
+
throw new Error(`Unsupported format for patch serialization: ${format2}`);
|
|
55151
|
+
}
|
|
55152
|
+
async function applyPatch(targetPath, patchContent, outputDef) {
|
|
55153
|
+
const format2 = outputDef.format ?? "json";
|
|
55154
|
+
if (format2 === "text") {
|
|
55155
|
+
let existing = "";
|
|
55156
|
+
try {
|
|
55157
|
+
existing = await readFile5(targetPath, "utf8");
|
|
55158
|
+
} catch {
|
|
55159
|
+
}
|
|
55160
|
+
return existing + patchContent;
|
|
55161
|
+
}
|
|
55162
|
+
const patchData = parseContent(patchContent, format2);
|
|
55163
|
+
let existingData;
|
|
55164
|
+
try {
|
|
55165
|
+
const existingRaw = await readFile5(targetPath, "utf8");
|
|
55166
|
+
existingData = parseContent(existingRaw, format2);
|
|
55167
|
+
} catch {
|
|
55168
|
+
return serializeContent(patchData, format2);
|
|
55169
|
+
}
|
|
55170
|
+
const strategy = outputDef.patch_strategy ?? "deep_merge";
|
|
55171
|
+
if (strategy === "append" && Array.isArray(existingData)) {
|
|
55172
|
+
const merged2 = deepMergeArrays(
|
|
55173
|
+
existingData,
|
|
55174
|
+
Array.isArray(patchData) ? patchData : [patchData],
|
|
55175
|
+
outputDef.array_merge_key
|
|
55176
|
+
);
|
|
55177
|
+
return serializeContent(merged2, format2);
|
|
55178
|
+
}
|
|
55179
|
+
const merged = deepMerge(existingData, patchData, outputDef.array_merge_key);
|
|
55180
|
+
return serializeContent(merged, format2);
|
|
55181
|
+
}
|
|
55001
55182
|
async function generateGuardrails(options) {
|
|
55002
55183
|
const { dsl, config, loadedBindings, filterBindings, dryRun } = options;
|
|
55003
55184
|
const outputFiles = [];
|
|
@@ -55067,6 +55248,31 @@ async function generateGuardrails(options) {
|
|
|
55067
55248
|
continue;
|
|
55068
55249
|
}
|
|
55069
55250
|
const targetPath = resolve6(config.configDir, pathResult.resolved);
|
|
55251
|
+
if (outputDef.source) {
|
|
55252
|
+
const sourcePath = resolve6(config.configDir, outputDef.source);
|
|
55253
|
+
if (!dryRun) {
|
|
55254
|
+
try {
|
|
55255
|
+
await mkdir2(dirname6(targetPath), { recursive: true });
|
|
55256
|
+
await copyFile(sourcePath, targetPath);
|
|
55257
|
+
if (outputDef.executable) {
|
|
55258
|
+
await chmod(targetPath, 493);
|
|
55259
|
+
}
|
|
55260
|
+
} catch (err) {
|
|
55261
|
+
const code = err.code;
|
|
55262
|
+
if (code === "ENOENT") {
|
|
55263
|
+
diagnostics.push({
|
|
55264
|
+
path: `binding.${binding.software}.outputs.${outputId}`,
|
|
55265
|
+
message: `Source file not found: ${sourcePath}`,
|
|
55266
|
+
severity: "error"
|
|
55267
|
+
});
|
|
55268
|
+
continue;
|
|
55269
|
+
}
|
|
55270
|
+
throw err;
|
|
55271
|
+
}
|
|
55272
|
+
}
|
|
55273
|
+
outputFiles.push(targetPath);
|
|
55274
|
+
continue;
|
|
55275
|
+
}
|
|
55070
55276
|
let templateContent;
|
|
55071
55277
|
if (outputDef.inline_template) {
|
|
55072
55278
|
templateContent = outputDef.inline_template;
|
|
@@ -55093,12 +55299,13 @@ async function generateGuardrails(options) {
|
|
|
55093
55299
|
} else {
|
|
55094
55300
|
diagnostics.push({
|
|
55095
55301
|
path: `binding.${binding.software}.outputs.${outputId}`,
|
|
55096
|
-
message: "Output has neither template nor
|
|
55302
|
+
message: "Output has neither template, inline_template, nor source",
|
|
55097
55303
|
severity: "error"
|
|
55098
55304
|
});
|
|
55099
55305
|
continue;
|
|
55100
55306
|
}
|
|
55101
55307
|
const shouldSkipEmpty = outputDef.skip_empty === true;
|
|
55308
|
+
const isPatch = outputDef.mode === "patch";
|
|
55102
55309
|
if (outputDef.group_by) {
|
|
55103
55310
|
const groupField = outputDef.group_by;
|
|
55104
55311
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -55116,9 +55323,9 @@ async function generateGuardrails(options) {
|
|
|
55116
55323
|
current_group: groupKey
|
|
55117
55324
|
};
|
|
55118
55325
|
const compiled = Handlebars2.compile(templateContent, { noEscape: true });
|
|
55119
|
-
const
|
|
55326
|
+
const rendered = compiled(groupCtx);
|
|
55120
55327
|
const groupTarget = resolve6(targetPath, groupKey);
|
|
55121
|
-
if (shouldSkipEmpty &&
|
|
55328
|
+
if (shouldSkipEmpty && rendered.trim().length === 0) {
|
|
55122
55329
|
if (!dryRun) {
|
|
55123
55330
|
try {
|
|
55124
55331
|
await unlink2(groupTarget);
|
|
@@ -55127,6 +55334,7 @@ async function generateGuardrails(options) {
|
|
|
55127
55334
|
}
|
|
55128
55335
|
continue;
|
|
55129
55336
|
}
|
|
55337
|
+
const output = isPatch && !dryRun ? await applyPatch(groupTarget, rendered, outputDef) : rendered;
|
|
55130
55338
|
if (!dryRun) {
|
|
55131
55339
|
await mkdir2(dirname6(groupTarget), { recursive: true });
|
|
55132
55340
|
await writeFile2(groupTarget, output, "utf8");
|
|
@@ -55138,8 +55346,8 @@ async function generateGuardrails(options) {
|
|
|
55138
55346
|
}
|
|
55139
55347
|
} else {
|
|
55140
55348
|
const compiled = Handlebars2.compile(templateContent, { noEscape: true });
|
|
55141
|
-
const
|
|
55142
|
-
if (shouldSkipEmpty &&
|
|
55349
|
+
const rendered = compiled(ctx);
|
|
55350
|
+
if (shouldSkipEmpty && rendered.trim().length === 0) {
|
|
55143
55351
|
if (!dryRun) {
|
|
55144
55352
|
try {
|
|
55145
55353
|
await unlink2(targetPath);
|
|
@@ -55147,6 +55355,7 @@ async function generateGuardrails(options) {
|
|
|
55147
55355
|
}
|
|
55148
55356
|
}
|
|
55149
55357
|
} else {
|
|
55358
|
+
const output = isPatch && !dryRun ? await applyPatch(targetPath, rendered, outputDef) : rendered;
|
|
55150
55359
|
if (!dryRun) {
|
|
55151
55360
|
await mkdir2(dirname6(targetPath), { recursive: true });
|
|
55152
55361
|
await writeFile2(targetPath, output, "utf8");
|