agent-contracts 0.6.0 → 0.7.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 +128 -0
- package/dist/cli.js +266 -11
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +18 -0
- package/dist/index.js +209 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -244,6 +244,7 @@ Design regressions become testable.
|
|
|
244
244
|
* **Structured handoff definitions**
|
|
245
245
|
* **Artifact ownership and lifecycle modeling**
|
|
246
246
|
* **Config-driven prompt rendering**
|
|
247
|
+
* **Variable substitution** via `${vars.xxx}` in DSL values
|
|
247
248
|
* **Inheritance with merge operators via `extends`**
|
|
248
249
|
* **Flexible file splitting** via `$ref` (replacement) and `$refs` (import + deep-merge)
|
|
249
250
|
* **JSON Schema for editor support and external tooling**
|
|
@@ -533,6 +534,67 @@ Supported merge operators:
|
|
|
533
534
|
|
|
534
535
|
---
|
|
535
536
|
|
|
537
|
+
## Variable substitution
|
|
538
|
+
|
|
539
|
+
When using `extends` to share a base DSL across projects, base definitions often contain values that differ per project (project name, language, repository URL, etc.).
|
|
540
|
+
|
|
541
|
+
`vars` in `agent-contracts.config.yaml` lets you define project-specific values that are substituted into DSL string values using `${vars.xxx}` syntax.
|
|
542
|
+
|
|
543
|
+
### Defining vars
|
|
544
|
+
|
|
545
|
+
Add a `vars` section to your config file. Values must be flat string key-value pairs.
|
|
546
|
+
|
|
547
|
+
````yaml
|
|
548
|
+
# agent-contracts.config.yaml
|
|
549
|
+
vars:
|
|
550
|
+
project_name: "my-service"
|
|
551
|
+
language: "TypeScript"
|
|
552
|
+
repo_url: "https://github.com/org/my-service"
|
|
553
|
+
````
|
|
554
|
+
|
|
555
|
+
### Using placeholders in DSL
|
|
556
|
+
|
|
557
|
+
Use `${vars.<key>}` in any string value within the DSL YAML (base or project).
|
|
558
|
+
|
|
559
|
+
````yaml
|
|
560
|
+
# base/agent-contracts.yaml
|
|
561
|
+
agents:
|
|
562
|
+
implementer:
|
|
563
|
+
purpose: "Implements features for ${vars.project_name}"
|
|
564
|
+
constraints:
|
|
565
|
+
- "Use ${vars.language} for all implementations"
|
|
566
|
+
- "Repository: ${vars.repo_url}"
|
|
567
|
+
````
|
|
568
|
+
|
|
569
|
+
### Processing order
|
|
570
|
+
|
|
571
|
+
Variable substitution happens **after** DSL resolution (`extends` merge) and **before** schema validation:
|
|
572
|
+
|
|
573
|
+
1. Load config (including `vars`)
|
|
574
|
+
2. Resolve DSL (load + merge `extends`)
|
|
575
|
+
3. Substitute `${vars.xxx}` in all string values
|
|
576
|
+
4. Validate schema
|
|
577
|
+
5. Render / lint / check
|
|
578
|
+
|
|
579
|
+
This ensures that merged strings from both base and project are substituted, and the resulting values pass schema validation.
|
|
580
|
+
|
|
581
|
+
### Error handling
|
|
582
|
+
|
|
583
|
+
If a placeholder references an undefined variable, the command exits with an error:
|
|
584
|
+
|
|
585
|
+
````
|
|
586
|
+
VarsSubstitutionError: Undefined variable "repo_url" in value "Repository: ${vars.repo_url}"
|
|
587
|
+
Defined vars: project_name, language
|
|
588
|
+
````
|
|
589
|
+
|
|
590
|
+
### Notes
|
|
591
|
+
|
|
592
|
+
- Only string values are substituted; object keys are not affected.
|
|
593
|
+
- `vars` is optional. If omitted, no substitution occurs.
|
|
594
|
+
- Patterns that do not match `${vars.<key>}` (e.g. `${env.HOME}`, `$vars.xxx`, `{{vars.xxx}}`) are left unchanged.
|
|
595
|
+
|
|
596
|
+
---
|
|
597
|
+
|
|
536
598
|
## CLI
|
|
537
599
|
|
|
538
600
|
### Installation
|
|
@@ -576,6 +638,11 @@ Rendering is configured via `agent-contracts.config.yaml`.
|
|
|
576
638
|
````yaml
|
|
577
639
|
dsl: ./agent-contracts.yaml
|
|
578
640
|
|
|
641
|
+
vars:
|
|
642
|
+
project_name: "my-service"
|
|
643
|
+
language: "TypeScript"
|
|
644
|
+
repo_url: "https://github.com/org/my-service"
|
|
645
|
+
|
|
579
646
|
renders:
|
|
580
647
|
- template: ./templates/agent-prompt.md.hbs
|
|
581
648
|
context: agent
|
|
@@ -597,6 +664,67 @@ This lets you generate static outputs for:
|
|
|
597
664
|
|
|
598
665
|
all from the same resolved DSL.
|
|
599
666
|
|
|
667
|
+
### Available context types
|
|
668
|
+
|
|
669
|
+
Each `context` type provides a different rendering scope:
|
|
670
|
+
|
|
671
|
+
| Context | Scope | Output | Key variables |
|
|
672
|
+
|---------|-------|--------|---------------|
|
|
673
|
+
| `system` | Single file | `output` as-is | `system`, `dsl` |
|
|
674
|
+
| `agent` | Per agent | `{agent.id}` in output path | `agent`, `receivableTasks`, `delegatableTasks`, `relatedArtifacts`, `relatedTools`, `relatedHandoffTypes`, `mergedBehavior`, `dsl` |
|
|
675
|
+
| `task` | Per task | `{task.id}` in output path | `task`, `targetAgent`, `dsl` |
|
|
676
|
+
| `artifact` | Per artifact | `{artifact.id}` in output path | `artifact`, `relatedTools`, `relatedValidations`, `producerAgents`, `consumerAgents`, `editorAgents`, `createdInWorkflows`, `dsl` |
|
|
677
|
+
| `tool` | Per tool | `{tool.id}` in output path | `tool`, `invokableAgents`, `inputArtifactDetails`, `outputArtifactDetails`, `dsl` |
|
|
678
|
+
| `validation` | Per validation | `{validation.id}` in output path | `validation`, `dsl` |
|
|
679
|
+
| `handoff_type` | Per handoff type | `{handoff_type.id}` in output path | `handoff_type`, `relatedTasks`, `dsl` |
|
|
680
|
+
| `workflow` | Per workflow phase | `{workflow.id}` in output path | `workflow`, `relatedAgents`, `relatedTasks`, `relatedTools`, `relatedArtifacts`, `relatedValidations`, `dsl` |
|
|
681
|
+
| `policy` | Per policy | `{policy.id}` in output path | `policy`, `dsl` |
|
|
682
|
+
|
|
683
|
+
#### Enriched context details
|
|
684
|
+
|
|
685
|
+
**`workflow` context** collects all entities involved in a phase:
|
|
686
|
+
|
|
687
|
+
* `relatedTasks` — tasks where `task.workflow` matches this phase
|
|
688
|
+
* `relatedAgents` — agents from task `target_agent`, `allowed_from_agents`, step `from_agent`, and validation executors
|
|
689
|
+
* `relatedTools` — tools from `can_execute_tools` of all related agents
|
|
690
|
+
* `relatedArtifacts` — artifacts from `can_read_artifacts`, `can_write_artifacts`, and `input_artifacts`
|
|
691
|
+
* `relatedValidations` — validations referenced in workflow steps
|
|
692
|
+
|
|
693
|
+
**`artifact` context** provides ownership and cross-reference data:
|
|
694
|
+
|
|
695
|
+
* `relatedTools` — tools with this artifact in `input_artifacts` or `output_artifacts`
|
|
696
|
+
* `relatedValidations` — validations targeting this artifact
|
|
697
|
+
* `producerAgents` / `consumerAgents` / `editorAgents` — resolved agent records
|
|
698
|
+
* `createdInWorkflows` — workflow phases where this artifact is written
|
|
699
|
+
|
|
700
|
+
**`tool` context** resolves agent and artifact references:
|
|
701
|
+
|
|
702
|
+
* `invokableAgents` — agents listed in `invokable_by`
|
|
703
|
+
* `inputArtifactDetails` / `outputArtifactDetails` — resolved artifact records
|
|
704
|
+
|
|
705
|
+
### Handlebars helpers
|
|
706
|
+
|
|
707
|
+
Templates can use these built-in helpers:
|
|
708
|
+
|
|
709
|
+
| Helper | Usage | Description |
|
|
710
|
+
|--------|-------|-------------|
|
|
711
|
+
| `eq` | `{{#if (eq a b)}}` | Strict equality |
|
|
712
|
+
| `notEmpty` | `{{#if (notEmpty obj)}}` | True when object has at least one key |
|
|
713
|
+
| `inc` | `{{inc @index}}` | Increment number by 1 (for 1-based indexing) |
|
|
714
|
+
| `yamlBlock` | `{{{yamlBlock obj}}}` | Render value as YAML-formatted text |
|
|
715
|
+
| `lookupPayloadFields` | `{{#each (lookupPayloadFields payload)}}` | Extract payload field info (name, type, required, enum) |
|
|
716
|
+
| `join` | `{{join arr ", "}}` | Join array elements with separator |
|
|
717
|
+
| `contains` | `{{#if (contains arr "x")}}` | True when array includes value |
|
|
718
|
+
| `groupBy` | `{{#with (groupBy arr "key")}}` | Group array elements by field value |
|
|
719
|
+
| `filterByField` | `{{#each (filterByField arr "field" "val")}}` | Filter array by field match |
|
|
720
|
+
| `keys` | `{{#each (keys obj)}}` | Object keys as array |
|
|
721
|
+
| `values` | `{{#each (values obj)}}` | Object values as array |
|
|
722
|
+
| `size` | `{{size obj}}` | Array length or object key count |
|
|
723
|
+
| `not` | `{{#if (not x)}}` | Boolean negation |
|
|
724
|
+
| `or` | `{{#if (or a b)}}` | Boolean OR (variadic) |
|
|
725
|
+
| `and` | `{{#if (and a b)}}` | Boolean AND (variadic) |
|
|
726
|
+
| `gt` / `gte` / `lt` | `{{#if (gt a b)}}` | Numeric comparisons |
|
|
727
|
+
|
|
600
728
|
---
|
|
601
729
|
|
|
602
730
|
## Validation model
|
package/dist/cli.js
CHANGED
|
@@ -11927,7 +11927,7 @@ var require_getJsonPathForPosition = __commonJS({
|
|
|
11927
11927
|
return;
|
|
11928
11928
|
return path;
|
|
11929
11929
|
};
|
|
11930
|
-
function*
|
|
11930
|
+
function* walk2(node) {
|
|
11931
11931
|
switch (node.kind) {
|
|
11932
11932
|
case types_1.Kind.MAP:
|
|
11933
11933
|
if (node.mappings.length !== 0) {
|
|
@@ -11988,7 +11988,7 @@ var require_getJsonPathForPosition = __commonJS({
|
|
|
11988
11988
|
return node;
|
|
11989
11989
|
}
|
|
11990
11990
|
function findClosestScalar(container, offset, line, lineMap) {
|
|
11991
|
-
for (const node of
|
|
11991
|
+
for (const node of walk2(container)) {
|
|
11992
11992
|
if (node.startPosition <= offset && offset <= node.endPosition) {
|
|
11993
11993
|
return node.kind === types_1.Kind.SCALAR ? node : findClosestScalar(node, offset, line, lineMap);
|
|
11994
11994
|
}
|
|
@@ -58342,6 +58342,7 @@ var RenderTargetSchema = z.object({
|
|
|
58342
58342
|
);
|
|
58343
58343
|
var AgentContractsConfigSchema = z.object({
|
|
58344
58344
|
dsl: z.string(),
|
|
58345
|
+
vars: z.record(z.string(), z.string()).optional(),
|
|
58345
58346
|
renders: z.array(RenderTargetSchema).min(1)
|
|
58346
58347
|
});
|
|
58347
58348
|
|
|
@@ -58410,6 +58411,7 @@ ${issues}`,
|
|
|
58410
58411
|
const config = result.data;
|
|
58411
58412
|
return {
|
|
58412
58413
|
dsl: resolve(configDir, config.dsl),
|
|
58414
|
+
vars: config.vars,
|
|
58413
58415
|
renders: config.renders.map((r3) => ({
|
|
58414
58416
|
...r3,
|
|
58415
58417
|
template: resolve(configDir, r3.template),
|
|
@@ -58980,6 +58982,50 @@ async function resolve4(projectDirOrFile) {
|
|
|
58980
58982
|
};
|
|
58981
58983
|
}
|
|
58982
58984
|
|
|
58985
|
+
// src/resolver/substitute-vars.ts
|
|
58986
|
+
var VAR_PATTERN = /\$\{vars\.([a-zA-Z0-9_-]+)\}/g;
|
|
58987
|
+
var VarsSubstitutionError = class extends Error {
|
|
58988
|
+
constructor(varName, sourceValue, definedVars) {
|
|
58989
|
+
const defined = definedVars.length > 0 ? definedVars.join(", ") : "(none)";
|
|
58990
|
+
super(
|
|
58991
|
+
`Undefined variable "${varName}" in value "${sourceValue}"
|
|
58992
|
+
Defined vars: ${defined}`
|
|
58993
|
+
);
|
|
58994
|
+
this.varName = varName;
|
|
58995
|
+
this.sourceValue = sourceValue;
|
|
58996
|
+
this.definedVars = definedVars;
|
|
58997
|
+
this.name = "VarsSubstitutionError";
|
|
58998
|
+
}
|
|
58999
|
+
varName;
|
|
59000
|
+
sourceValue;
|
|
59001
|
+
definedVars;
|
|
59002
|
+
};
|
|
59003
|
+
function substituteString(value, vars) {
|
|
59004
|
+
return value.replace(VAR_PATTERN, (match, varName) => {
|
|
59005
|
+
if (!(varName in vars)) {
|
|
59006
|
+
throw new VarsSubstitutionError(varName, value, Object.keys(vars));
|
|
59007
|
+
}
|
|
59008
|
+
return vars[varName];
|
|
59009
|
+
});
|
|
59010
|
+
}
|
|
59011
|
+
function walk(data, vars) {
|
|
59012
|
+
if (typeof data === "string") return substituteString(data, vars);
|
|
59013
|
+
if (Array.isArray(data)) return data.map((item) => walk(item, vars));
|
|
59014
|
+
if (data !== null && typeof data === "object") {
|
|
59015
|
+
const result = {};
|
|
59016
|
+
for (const [key, value] of Object.entries(
|
|
59017
|
+
data
|
|
59018
|
+
)) {
|
|
59019
|
+
result[key] = walk(value, vars);
|
|
59020
|
+
}
|
|
59021
|
+
return result;
|
|
59022
|
+
}
|
|
59023
|
+
return data;
|
|
59024
|
+
}
|
|
59025
|
+
function substituteVars(data, vars) {
|
|
59026
|
+
return walk(data, vars);
|
|
59027
|
+
}
|
|
59028
|
+
|
|
58983
59029
|
// src/cli/commands/resolve.ts
|
|
58984
59030
|
import { stringify } from "yaml";
|
|
58985
59031
|
var DIR_DEFAULT = "agent-contracts.yaml";
|
|
@@ -58988,10 +59034,11 @@ var resolveCommand = new Command("resolve").description("Resolve DSL (load + mer
|
|
|
58988
59034
|
const config = await loadConfig(opts.config);
|
|
58989
59035
|
const dslPath = resolveDslPath(dir, DIR_DEFAULT, config);
|
|
58990
59036
|
const result = await resolve4(dslPath);
|
|
59037
|
+
const data = config?.vars ? substituteVars(result.data, config.vars) : result.data;
|
|
58991
59038
|
if (opts.format === "json") {
|
|
58992
|
-
process.stdout.write(JSON.stringify(
|
|
59039
|
+
process.stdout.write(JSON.stringify(data, null, 2) + "\n");
|
|
58993
59040
|
} else {
|
|
58994
|
-
process.stdout.write(stringify(
|
|
59041
|
+
process.stdout.write(stringify(data));
|
|
58995
59042
|
}
|
|
58996
59043
|
} catch (err) {
|
|
58997
59044
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -59512,7 +59559,8 @@ var validateCommand = new Command2("validate").description("Validate DSL against
|
|
|
59512
59559
|
const config = await loadConfig(opts.config);
|
|
59513
59560
|
const dslPath = resolveDslPath(dir, DIR_DEFAULT2, config);
|
|
59514
59561
|
const resolved = await resolve4(dslPath);
|
|
59515
|
-
const
|
|
59562
|
+
const data = config?.vars ? substituteVars(resolved.data, config.vars) : resolved.data;
|
|
59563
|
+
const schemaResult = validateSchema(data);
|
|
59516
59564
|
if (!schemaResult.success) {
|
|
59517
59565
|
const output = formatDiagnostics(schemaResult.diagnostics, opts);
|
|
59518
59566
|
if (output) process.stderr.write(output + "\n");
|
|
@@ -60376,7 +60424,8 @@ var lintCommand = new Command3("lint").description("Run semantic lint rules on r
|
|
|
60376
60424
|
const config = await loadConfig(opts.config);
|
|
60377
60425
|
const dslPath = resolveDslPath(dir, DIR_DEFAULT3, config);
|
|
60378
60426
|
const resolved = await resolve4(dslPath);
|
|
60379
|
-
const
|
|
60427
|
+
const data = config?.vars ? substituteVars(resolved.data, config.vars) : resolved.data;
|
|
60428
|
+
const schemaResult = validateSchema(data);
|
|
60380
60429
|
if (!schemaResult.success) {
|
|
60381
60430
|
const output = formatDiagnostics(schemaResult.diagnostics, {
|
|
60382
60431
|
format: opts.format,
|
|
@@ -60434,12 +60483,76 @@ function buildTaskContext(dsl, taskId) {
|
|
|
60434
60483
|
function buildArtifactContext(dsl, artifactId) {
|
|
60435
60484
|
const artifactDef = dsl.artifacts[artifactId];
|
|
60436
60485
|
const artifact = { ...artifactDef, id: artifactId };
|
|
60437
|
-
|
|
60486
|
+
const relatedTools = {};
|
|
60487
|
+
for (const [id, tool] of Object.entries(dsl.tools)) {
|
|
60488
|
+
if (tool.input_artifacts.includes(artifactId) || tool.output_artifacts.includes(artifactId)) {
|
|
60489
|
+
relatedTools[id] = tool;
|
|
60490
|
+
}
|
|
60491
|
+
}
|
|
60492
|
+
const relatedValidations = {};
|
|
60493
|
+
for (const [id, val] of Object.entries(dsl.validations)) {
|
|
60494
|
+
if (val.target_artifact === artifactId) {
|
|
60495
|
+
relatedValidations[id] = val;
|
|
60496
|
+
}
|
|
60497
|
+
}
|
|
60498
|
+
const pickAgents = (ids) => {
|
|
60499
|
+
const result = {};
|
|
60500
|
+
for (const id of ids) {
|
|
60501
|
+
if (dsl.agents[id]) result[id] = dsl.agents[id];
|
|
60502
|
+
}
|
|
60503
|
+
return result;
|
|
60504
|
+
};
|
|
60505
|
+
const producerAgents = pickAgents(artifactDef.producers);
|
|
60506
|
+
const consumerAgents = pickAgents(artifactDef.consumers);
|
|
60507
|
+
const editorAgents = pickAgents(artifactDef.editors);
|
|
60508
|
+
const createdInWorkflows = [];
|
|
60509
|
+
for (const [_taskId, task] of Object.entries(dsl.tasks)) {
|
|
60510
|
+
if (createdInWorkflows.includes(task.workflow)) continue;
|
|
60511
|
+
const targetAgent = dsl.agents[task.target_agent];
|
|
60512
|
+
if (targetAgent?.can_write_artifacts.includes(artifactId)) {
|
|
60513
|
+
createdInWorkflows.push(task.workflow);
|
|
60514
|
+
continue;
|
|
60515
|
+
}
|
|
60516
|
+
const steps = task.execution_steps ?? [];
|
|
60517
|
+
for (const step of steps) {
|
|
60518
|
+
if (step["produces_artifact"] === artifactId) {
|
|
60519
|
+
createdInWorkflows.push(task.workflow);
|
|
60520
|
+
break;
|
|
60521
|
+
}
|
|
60522
|
+
}
|
|
60523
|
+
}
|
|
60524
|
+
return {
|
|
60525
|
+
artifact,
|
|
60526
|
+
relatedTools,
|
|
60527
|
+
relatedValidations,
|
|
60528
|
+
producerAgents,
|
|
60529
|
+
consumerAgents,
|
|
60530
|
+
editorAgents,
|
|
60531
|
+
createdInWorkflows,
|
|
60532
|
+
dsl
|
|
60533
|
+
};
|
|
60438
60534
|
}
|
|
60439
60535
|
function buildToolContext(dsl, toolId) {
|
|
60440
60536
|
const toolDef = dsl.tools[toolId];
|
|
60441
60537
|
const tool = { ...toolDef, id: toolId };
|
|
60442
|
-
|
|
60538
|
+
const invokableAgents = {};
|
|
60539
|
+
for (const agentId of toolDef.invokable_by) {
|
|
60540
|
+
if (dsl.agents[agentId]) invokableAgents[agentId] = dsl.agents[agentId];
|
|
60541
|
+
}
|
|
60542
|
+
const pickArtifacts = (ids) => {
|
|
60543
|
+
const result = {};
|
|
60544
|
+
for (const id of ids) {
|
|
60545
|
+
if (dsl.artifacts[id]) result[id] = dsl.artifacts[id];
|
|
60546
|
+
}
|
|
60547
|
+
return result;
|
|
60548
|
+
};
|
|
60549
|
+
return {
|
|
60550
|
+
tool,
|
|
60551
|
+
invokableAgents,
|
|
60552
|
+
inputArtifactDetails: pickArtifacts(toolDef.input_artifacts),
|
|
60553
|
+
outputArtifactDetails: pickArtifacts(toolDef.output_artifacts),
|
|
60554
|
+
dsl
|
|
60555
|
+
};
|
|
60443
60556
|
}
|
|
60444
60557
|
function buildValidationContext(dsl, validationId) {
|
|
60445
60558
|
const validationDef = dsl.validations[validationId];
|
|
@@ -60457,7 +60570,76 @@ function buildHandoffTypeContext(dsl, handoffTypeId) {
|
|
|
60457
60570
|
function buildWorkflowContext(dsl, workflowId) {
|
|
60458
60571
|
const wfDef = dsl.workflow[workflowId];
|
|
60459
60572
|
const workflow = { ...wfDef, id: workflowId };
|
|
60460
|
-
|
|
60573
|
+
const relatedTasks = Object.entries(dsl.tasks).filter(([, t3]) => t3.workflow === workflowId).map(([id, t3]) => ({ ...t3, id }));
|
|
60574
|
+
const agentIds = /* @__PURE__ */ new Set();
|
|
60575
|
+
for (const task of relatedTasks) {
|
|
60576
|
+
agentIds.add(task.target_agent);
|
|
60577
|
+
for (const fromAgent of task.allowed_from_agents) {
|
|
60578
|
+
agentIds.add(fromAgent);
|
|
60579
|
+
}
|
|
60580
|
+
}
|
|
60581
|
+
for (const step of wfDef.steps) {
|
|
60582
|
+
if (step.type === "handoff" && step.from_agent) {
|
|
60583
|
+
agentIds.add(step.from_agent);
|
|
60584
|
+
}
|
|
60585
|
+
if (step.type === "validation") {
|
|
60586
|
+
const val = dsl.validations[step.validation];
|
|
60587
|
+
if (val?.executor_type === "agent" && val.executor) {
|
|
60588
|
+
agentIds.add(val.executor);
|
|
60589
|
+
}
|
|
60590
|
+
}
|
|
60591
|
+
}
|
|
60592
|
+
const relatedAgents = {};
|
|
60593
|
+
for (const id of agentIds) {
|
|
60594
|
+
if (dsl.agents[id]) relatedAgents[id] = dsl.agents[id];
|
|
60595
|
+
}
|
|
60596
|
+
const toolIds = /* @__PURE__ */ new Set();
|
|
60597
|
+
for (const id of agentIds) {
|
|
60598
|
+
const agent = dsl.agents[id];
|
|
60599
|
+
if (agent) {
|
|
60600
|
+
for (const toolId of agent.can_execute_tools) {
|
|
60601
|
+
toolIds.add(toolId);
|
|
60602
|
+
}
|
|
60603
|
+
}
|
|
60604
|
+
}
|
|
60605
|
+
const relatedTools = {};
|
|
60606
|
+
for (const id of toolIds) {
|
|
60607
|
+
if (dsl.tools[id]) relatedTools[id] = dsl.tools[id];
|
|
60608
|
+
}
|
|
60609
|
+
const artifactIds = /* @__PURE__ */ new Set();
|
|
60610
|
+
for (const task of relatedTasks) {
|
|
60611
|
+
for (const artId of task.input_artifacts) artifactIds.add(artId);
|
|
60612
|
+
}
|
|
60613
|
+
for (const id of agentIds) {
|
|
60614
|
+
const agent = dsl.agents[id];
|
|
60615
|
+
if (agent) {
|
|
60616
|
+
for (const artId of agent.can_read_artifacts) artifactIds.add(artId);
|
|
60617
|
+
for (const artId of agent.can_write_artifacts) artifactIds.add(artId);
|
|
60618
|
+
}
|
|
60619
|
+
}
|
|
60620
|
+
const relatedArtifacts = {};
|
|
60621
|
+
for (const id of artifactIds) {
|
|
60622
|
+
if (dsl.artifacts[id]) relatedArtifacts[id] = dsl.artifacts[id];
|
|
60623
|
+
}
|
|
60624
|
+
const validationIds = /* @__PURE__ */ new Set();
|
|
60625
|
+
for (const step of wfDef.steps) {
|
|
60626
|
+
if (step.type === "validation") {
|
|
60627
|
+
validationIds.add(step.validation);
|
|
60628
|
+
}
|
|
60629
|
+
}
|
|
60630
|
+
const relatedValidations = {};
|
|
60631
|
+
for (const id of validationIds) {
|
|
60632
|
+
if (dsl.validations[id]) relatedValidations[id] = dsl.validations[id];
|
|
60633
|
+
}
|
|
60634
|
+
return {
|
|
60635
|
+
workflow,
|
|
60636
|
+
relatedAgents,
|
|
60637
|
+
relatedTasks,
|
|
60638
|
+
relatedTools,
|
|
60639
|
+
relatedArtifacts,
|
|
60640
|
+
relatedValidations,
|
|
60641
|
+
dsl
|
|
60642
|
+
};
|
|
60461
60643
|
}
|
|
60462
60644
|
function buildPolicyContext(dsl, policyId) {
|
|
60463
60645
|
const policyDef = dsl.policies[policyId];
|
|
@@ -60572,6 +60754,11 @@ function buildPerAgentContext(dsl, agent) {
|
|
|
60572
60754
|
|
|
60573
60755
|
// src/renderer/renderer.ts
|
|
60574
60756
|
Handlebars.registerHelper("eq", (a3, b3) => a3 === b3);
|
|
60757
|
+
Handlebars.registerHelper("notEmpty", (obj) => {
|
|
60758
|
+
if (!obj || typeof obj !== "object") return false;
|
|
60759
|
+
return Object.keys(obj).length > 0;
|
|
60760
|
+
});
|
|
60761
|
+
Handlebars.registerHelper("inc", (val) => val + 1);
|
|
60575
60762
|
Handlebars.registerHelper(
|
|
60576
60763
|
"lookupPayloadFields",
|
|
60577
60764
|
(payload) => {
|
|
@@ -60660,6 +60847,72 @@ function toYamlLines(obj, indent) {
|
|
|
60660
60847
|
Handlebars.registerHelper("yamlBlock", (obj) => {
|
|
60661
60848
|
return toYamlLines(obj, 0).join("\n");
|
|
60662
60849
|
});
|
|
60850
|
+
Handlebars.registerHelper(
|
|
60851
|
+
"join",
|
|
60852
|
+
(arr, separator) => {
|
|
60853
|
+
if (!Array.isArray(arr)) return "";
|
|
60854
|
+
const sep = typeof separator === "string" ? separator : ", ";
|
|
60855
|
+
return arr.join(sep);
|
|
60856
|
+
}
|
|
60857
|
+
);
|
|
60858
|
+
Handlebars.registerHelper("contains", (arr, value) => {
|
|
60859
|
+
if (!Array.isArray(arr)) return false;
|
|
60860
|
+
return arr.includes(value);
|
|
60861
|
+
});
|
|
60862
|
+
Handlebars.registerHelper(
|
|
60863
|
+
"groupBy",
|
|
60864
|
+
(arr, key) => {
|
|
60865
|
+
if (!Array.isArray(arr)) return {};
|
|
60866
|
+
const result = {};
|
|
60867
|
+
for (const item of arr) {
|
|
60868
|
+
const k2 = String(
|
|
60869
|
+
item?.[key] ?? "undefined"
|
|
60870
|
+
);
|
|
60871
|
+
(result[k2] ??= []).push(item);
|
|
60872
|
+
}
|
|
60873
|
+
return result;
|
|
60874
|
+
}
|
|
60875
|
+
);
|
|
60876
|
+
Handlebars.registerHelper(
|
|
60877
|
+
"keys",
|
|
60878
|
+
(obj) => {
|
|
60879
|
+
if (!obj || typeof obj !== "object") return [];
|
|
60880
|
+
return Object.keys(obj);
|
|
60881
|
+
}
|
|
60882
|
+
);
|
|
60883
|
+
Handlebars.registerHelper(
|
|
60884
|
+
"values",
|
|
60885
|
+
(obj) => {
|
|
60886
|
+
if (!obj || typeof obj !== "object") return [];
|
|
60887
|
+
return Object.values(obj);
|
|
60888
|
+
}
|
|
60889
|
+
);
|
|
60890
|
+
Handlebars.registerHelper(
|
|
60891
|
+
"filterByField",
|
|
60892
|
+
(arr, field, value) => {
|
|
60893
|
+
if (!Array.isArray(arr)) return [];
|
|
60894
|
+
return arr.filter(
|
|
60895
|
+
(item) => item?.[field] === value
|
|
60896
|
+
);
|
|
60897
|
+
}
|
|
60898
|
+
);
|
|
60899
|
+
Handlebars.registerHelper("not", (value) => !value);
|
|
60900
|
+
Handlebars.registerHelper("or", (...args) => {
|
|
60901
|
+
const _options = args.pop();
|
|
60902
|
+
return args.some((a3) => !!a3);
|
|
60903
|
+
});
|
|
60904
|
+
Handlebars.registerHelper("and", (...args) => {
|
|
60905
|
+
const _options = args.pop();
|
|
60906
|
+
return args.every((a3) => !!a3);
|
|
60907
|
+
});
|
|
60908
|
+
Handlebars.registerHelper("gt", (a3, b3) => a3 > b3);
|
|
60909
|
+
Handlebars.registerHelper("gte", (a3, b3) => a3 >= b3);
|
|
60910
|
+
Handlebars.registerHelper("lt", (a3, b3) => a3 < b3);
|
|
60911
|
+
Handlebars.registerHelper("size", (obj) => {
|
|
60912
|
+
if (Array.isArray(obj)) return obj.length;
|
|
60913
|
+
if (obj && typeof obj === "object") return Object.keys(obj).length;
|
|
60914
|
+
return 0;
|
|
60915
|
+
});
|
|
60663
60916
|
function getDslSection(dsl, context) {
|
|
60664
60917
|
const sectionMap = {
|
|
60665
60918
|
agent: dsl.agents,
|
|
@@ -60782,7 +61035,8 @@ var renderCommand = new Command4("render").description("Render resolved DSL with
|
|
|
60782
61035
|
process.exit(1);
|
|
60783
61036
|
}
|
|
60784
61037
|
const resolved = await resolve4(config.dsl);
|
|
60785
|
-
const
|
|
61038
|
+
const data = config.vars ? substituteVars(resolved.data, config.vars) : resolved.data;
|
|
61039
|
+
const schemaResult = validateSchema(data);
|
|
60786
61040
|
if (!schemaResult.success) {
|
|
60787
61041
|
process.stderr.write(
|
|
60788
61042
|
"Schema validation failed. Run 'agent-contracts validate' for details.\n"
|
|
@@ -60847,7 +61101,8 @@ var checkCommand = new Command5("check").description("Run full pipeline: resolve
|
|
|
60847
61101
|
process.exit(1);
|
|
60848
61102
|
}
|
|
60849
61103
|
const resolved = await resolve4(config.dsl);
|
|
60850
|
-
const
|
|
61104
|
+
const data = config.vars ? substituteVars(resolved.data, config.vars) : resolved.data;
|
|
61105
|
+
const schemaResult = validateSchema(data);
|
|
60851
61106
|
if (!schemaResult.success) {
|
|
60852
61107
|
const output = formatDiagnostics(schemaResult.diagnostics, {
|
|
60853
61108
|
format: opts.format,
|