agent-contracts 0.5.0 → 0.5.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/README.md +90 -1
- package/dist/cli.js +197 -51
- package/dist/cli.js.map +1 -1
- package/dist/index.js +197 -51
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
|
|
2
2
|
# agent-contracts
|
|
3
3
|
|
|
4
|
+
[](https://www.npmjs.com/package/agent-contracts)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
|
|
4
7
|
**Design multi-agent systems as contracts.**
|
|
5
8
|
|
|
6
9
|
`agent-contracts` is a toolkit for declaratively defining multi-agent development workflows in **YAML DSL**, with **static validation, semantic linting, and prompt rendering**.
|
|
@@ -242,6 +245,7 @@ Design regressions become testable.
|
|
|
242
245
|
* **Artifact ownership and lifecycle modeling**
|
|
243
246
|
* **Config-driven prompt rendering**
|
|
244
247
|
* **Inheritance with merge operators via `extends`**
|
|
248
|
+
* **Flexible file splitting** via `$ref` (replacement) and `$refs` (import + deep-merge)
|
|
245
249
|
* **JSON Schema for editor support and external tooling**
|
|
246
250
|
* **CI-friendly workflow checks**
|
|
247
251
|
|
|
@@ -289,7 +293,7 @@ tasks: { ... }
|
|
|
289
293
|
artifacts: { ... }
|
|
290
294
|
````
|
|
291
295
|
|
|
292
|
-
### Multi-file format
|
|
296
|
+
### Multi-file format (section-level `$ref`)
|
|
293
297
|
|
|
294
298
|
````yaml
|
|
295
299
|
version: 1
|
|
@@ -309,6 +313,91 @@ workflow: { $ref: "./workflow.yaml" }
|
|
|
309
313
|
policies: { $ref: "./policies.yaml" }
|
|
310
314
|
````
|
|
311
315
|
|
|
316
|
+
### Per-entry `$ref`
|
|
317
|
+
|
|
318
|
+
`$ref` can be used at any object position. This allows splitting individual entries into separate files:
|
|
319
|
+
|
|
320
|
+
````yaml
|
|
321
|
+
agents:
|
|
322
|
+
architect: { $ref: "./agents/architect.yaml" }
|
|
323
|
+
implementer: { $ref: "./agents/implementer.yaml" }
|
|
324
|
+
test-writer: { $ref: "./agents/test-writer.yaml" }
|
|
325
|
+
````
|
|
326
|
+
|
|
327
|
+
Each referenced file contains the agent definition directly (without the key):
|
|
328
|
+
|
|
329
|
+
````yaml
|
|
330
|
+
# agents/architect.yaml
|
|
331
|
+
role_name: "Architect"
|
|
332
|
+
purpose: "Drive phases and delegate work"
|
|
333
|
+
can_invoke_agents: [implementer]
|
|
334
|
+
````
|
|
335
|
+
|
|
336
|
+
### Directory `$ref`
|
|
337
|
+
|
|
338
|
+
When `$ref` points to a directory, all `*.yaml` / `*.yml` files in the directory are loaded and merged:
|
|
339
|
+
|
|
340
|
+
````yaml
|
|
341
|
+
agents: { $ref: "./agents/" }
|
|
342
|
+
````
|
|
343
|
+
|
|
344
|
+
Each file in the directory contains one or more keyed entries:
|
|
345
|
+
|
|
346
|
+
````yaml
|
|
347
|
+
# agents/architect.yaml
|
|
348
|
+
architect:
|
|
349
|
+
role_name: "Architect"
|
|
350
|
+
purpose: "Drive phases and delegate work"
|
|
351
|
+
````
|
|
352
|
+
|
|
353
|
+
Files are loaded in alphabetical order. Conflicting leaf values across files result in an error.
|
|
354
|
+
|
|
355
|
+
### `$refs` (import and merge)
|
|
356
|
+
|
|
357
|
+
`$refs` imports multiple files and **deep-merges** them into the containing map.
|
|
358
|
+
Unlike `$ref` (which replaces an object entirely), `$refs` allows mixing inline definitions with external files.
|
|
359
|
+
|
|
360
|
+
````yaml
|
|
361
|
+
agents:
|
|
362
|
+
inline-agent:
|
|
363
|
+
role_name: "Inline Agent"
|
|
364
|
+
purpose: "Defined right here"
|
|
365
|
+
$refs:
|
|
366
|
+
- "./agents/architect.yaml"
|
|
367
|
+
- "./agents/implementer.yaml"
|
|
368
|
+
- "./more-agents/" # directories are also supported
|
|
369
|
+
````
|
|
370
|
+
|
|
371
|
+
Each referenced file uses the same keyed format:
|
|
372
|
+
|
|
373
|
+
````yaml
|
|
374
|
+
# agents/architect.yaml
|
|
375
|
+
architect:
|
|
376
|
+
role_name: "Architect"
|
|
377
|
+
purpose: "Drive phases and delegate work"
|
|
378
|
+
````
|
|
379
|
+
|
|
380
|
+
`$refs` can also be used at the root level to compose a DSL from multiple aspect-oriented files:
|
|
381
|
+
|
|
382
|
+
````yaml
|
|
383
|
+
version: 1
|
|
384
|
+
system:
|
|
385
|
+
id: my-project
|
|
386
|
+
name: My Agent Workflow
|
|
387
|
+
default_phase_order: [analyze, implement]
|
|
388
|
+
$refs:
|
|
389
|
+
- "./agents-core.yaml" # agents + artifacts definitions
|
|
390
|
+
- "./agents-constraints.yaml" # constraints for the same agents
|
|
391
|
+
- "./tasks.yaml"
|
|
392
|
+
````
|
|
393
|
+
|
|
394
|
+
Overlapping map keys are deep-merged recursively. Conflicting leaf values (scalar or array) result in an error.
|
|
395
|
+
|
|
396
|
+
| Directive | Type | Behavior |
|
|
397
|
+
| --------- | ------ | -------------------------------------------------------- |
|
|
398
|
+
| `$ref` | string | Replace the object at that position with file contents |
|
|
399
|
+
| `$refs` | array | Import files and deep-merge into the containing map |
|
|
400
|
+
|
|
312
401
|
---
|
|
313
402
|
|
|
314
403
|
## Example: Agent definition
|
package/dist/cli.js
CHANGED
|
@@ -3952,7 +3952,7 @@ var require_lodash = __commonJS({
|
|
|
3952
3952
|
}
|
|
3953
3953
|
return mapped.length && mapped[0] === arrays[0] ? baseIntersection(mapped, undefined2, comparator) : [];
|
|
3954
3954
|
});
|
|
3955
|
-
function
|
|
3955
|
+
function join3(array, separator) {
|
|
3956
3956
|
return array == null ? "" : nativeJoin.call(array, separator);
|
|
3957
3957
|
}
|
|
3958
3958
|
function last(array) {
|
|
@@ -5876,7 +5876,7 @@ var require_lodash = __commonJS({
|
|
|
5876
5876
|
lodash.isUndefined = isUndefined;
|
|
5877
5877
|
lodash.isWeakMap = isWeakMap;
|
|
5878
5878
|
lodash.isWeakSet = isWeakSet;
|
|
5879
|
-
lodash.join =
|
|
5879
|
+
lodash.join = join3;
|
|
5880
5880
|
lodash.kebabCase = kebabCase;
|
|
5881
5881
|
lodash.last = last;
|
|
5882
5882
|
lodash.lastIndexOf = lastIndexOf;
|
|
@@ -7497,7 +7497,7 @@ var require_stable = __commonJS({
|
|
|
7497
7497
|
return result;
|
|
7498
7498
|
}
|
|
7499
7499
|
function stringifyFullFn(key, parent, stack, replacer, indent) {
|
|
7500
|
-
var i3, res,
|
|
7500
|
+
var i3, res, join3;
|
|
7501
7501
|
const originalIndentation = indentation;
|
|
7502
7502
|
var value = parent[key];
|
|
7503
7503
|
if (typeof value === "object" && value !== null && typeof value.toJSON === "function") {
|
|
@@ -7523,12 +7523,12 @@ var require_stable = __commonJS({
|
|
|
7523
7523
|
indentation += indent;
|
|
7524
7524
|
res += `
|
|
7525
7525
|
${indentation}`;
|
|
7526
|
-
|
|
7526
|
+
join3 = `,
|
|
7527
7527
|
${indentation}`;
|
|
7528
7528
|
for (i3 = 0; i3 < value.length - 1; i3++) {
|
|
7529
7529
|
const tmp2 = stringifyFullFn(i3, value, stack, replacer, indent);
|
|
7530
7530
|
res += tmp2 !== void 0 ? tmp2 : "null";
|
|
7531
|
-
res +=
|
|
7531
|
+
res += join3;
|
|
7532
7532
|
}
|
|
7533
7533
|
const tmp = stringifyFullFn(i3, value, stack, replacer, indent);
|
|
7534
7534
|
res += tmp !== void 0 ? tmp : "null";
|
|
@@ -7550,7 +7550,7 @@ ${originalIndentation}`;
|
|
|
7550
7550
|
indentation += indent;
|
|
7551
7551
|
res += `
|
|
7552
7552
|
${indentation}`;
|
|
7553
|
-
|
|
7553
|
+
join3 = `,
|
|
7554
7554
|
${indentation}`;
|
|
7555
7555
|
var separator = "";
|
|
7556
7556
|
for (i3 = 0; i3 < keys.length; i3++) {
|
|
@@ -7558,7 +7558,7 @@ ${indentation}`;
|
|
|
7558
7558
|
const tmp = stringifyFullFn(key, value, stack, replacer, indent);
|
|
7559
7559
|
if (tmp !== void 0) {
|
|
7560
7560
|
res += `${separator}"${strEscape(key)}": ${tmp}`;
|
|
7561
|
-
separator =
|
|
7561
|
+
separator = join3;
|
|
7562
7562
|
}
|
|
7563
7563
|
}
|
|
7564
7564
|
if (separator !== "") {
|
|
@@ -7580,7 +7580,7 @@ ${originalIndentation}`;
|
|
|
7580
7580
|
}
|
|
7581
7581
|
}
|
|
7582
7582
|
function stringifyFullArr(key, value, stack, replacer, indent) {
|
|
7583
|
-
var i3, res,
|
|
7583
|
+
var i3, res, join3;
|
|
7584
7584
|
const originalIndentation = indentation;
|
|
7585
7585
|
if (typeof value === "object" && value !== null && typeof value.toJSON === "function") {
|
|
7586
7586
|
value = value.toJSON(key);
|
|
@@ -7604,12 +7604,12 @@ ${originalIndentation}`;
|
|
|
7604
7604
|
indentation += indent;
|
|
7605
7605
|
res += `
|
|
7606
7606
|
${indentation}`;
|
|
7607
|
-
|
|
7607
|
+
join3 = `,
|
|
7608
7608
|
${indentation}`;
|
|
7609
7609
|
for (i3 = 0; i3 < value.length - 1; i3++) {
|
|
7610
7610
|
const tmp2 = stringifyFullArr(i3, value[i3], stack, replacer, indent);
|
|
7611
7611
|
res += tmp2 !== void 0 ? tmp2 : "null";
|
|
7612
|
-
res +=
|
|
7612
|
+
res += join3;
|
|
7613
7613
|
}
|
|
7614
7614
|
const tmp = stringifyFullArr(i3, value[i3], stack, replacer, indent);
|
|
7615
7615
|
res += tmp !== void 0 ? tmp : "null";
|
|
@@ -7630,7 +7630,7 @@ ${originalIndentation}`;
|
|
|
7630
7630
|
indentation += indent;
|
|
7631
7631
|
res += `
|
|
7632
7632
|
${indentation}`;
|
|
7633
|
-
|
|
7633
|
+
join3 = `,
|
|
7634
7634
|
${indentation}`;
|
|
7635
7635
|
var separator = "";
|
|
7636
7636
|
for (i3 = 0; i3 < replacer.length; i3++) {
|
|
@@ -7639,7 +7639,7 @@ ${indentation}`;
|
|
|
7639
7639
|
const tmp = stringifyFullArr(key, value[key], stack, replacer, indent);
|
|
7640
7640
|
if (tmp !== void 0) {
|
|
7641
7641
|
res += `${separator}"${strEscape(key)}": ${tmp}`;
|
|
7642
|
-
separator =
|
|
7642
|
+
separator = join3;
|
|
7643
7643
|
}
|
|
7644
7644
|
}
|
|
7645
7645
|
}
|
|
@@ -7662,7 +7662,7 @@ ${originalIndentation}`;
|
|
|
7662
7662
|
}
|
|
7663
7663
|
}
|
|
7664
7664
|
function stringifyIndent(key, value, stack, indent) {
|
|
7665
|
-
var i3, res,
|
|
7665
|
+
var i3, res, join3;
|
|
7666
7666
|
const originalIndentation = indentation;
|
|
7667
7667
|
switch (typeof value) {
|
|
7668
7668
|
case "object":
|
|
@@ -7692,12 +7692,12 @@ ${originalIndentation}`;
|
|
|
7692
7692
|
indentation += indent;
|
|
7693
7693
|
res += `
|
|
7694
7694
|
${indentation}`;
|
|
7695
|
-
|
|
7695
|
+
join3 = `,
|
|
7696
7696
|
${indentation}`;
|
|
7697
7697
|
for (i3 = 0; i3 < value.length - 1; i3++) {
|
|
7698
7698
|
const tmp2 = stringifyIndent(i3, value[i3], stack, indent);
|
|
7699
7699
|
res += tmp2 !== void 0 ? tmp2 : "null";
|
|
7700
|
-
res +=
|
|
7700
|
+
res += join3;
|
|
7701
7701
|
}
|
|
7702
7702
|
const tmp = stringifyIndent(i3, value[i3], stack, indent);
|
|
7703
7703
|
res += tmp !== void 0 ? tmp : "null";
|
|
@@ -7719,7 +7719,7 @@ ${originalIndentation}`;
|
|
|
7719
7719
|
indentation += indent;
|
|
7720
7720
|
res += `
|
|
7721
7721
|
${indentation}`;
|
|
7722
|
-
|
|
7722
|
+
join3 = `,
|
|
7723
7723
|
${indentation}`;
|
|
7724
7724
|
var separator = "";
|
|
7725
7725
|
for (i3 = 0; i3 < keys.length; i3++) {
|
|
@@ -7727,7 +7727,7 @@ ${indentation}`;
|
|
|
7727
7727
|
const tmp = stringifyIndent(key, value[key], stack, indent);
|
|
7728
7728
|
if (tmp !== void 0) {
|
|
7729
7729
|
res += `${separator}"${strEscape(key)}": ${tmp}`;
|
|
7730
|
-
separator =
|
|
7730
|
+
separator = join3;
|
|
7731
7731
|
}
|
|
7732
7732
|
}
|
|
7733
7733
|
if (separator !== "") {
|
|
@@ -52698,7 +52698,7 @@ var require_lodash3 = __commonJS({
|
|
|
52698
52698
|
}
|
|
52699
52699
|
return mapped.length && mapped[0] === arrays[0] ? baseIntersection(mapped, undefined2, comparator) : [];
|
|
52700
52700
|
});
|
|
52701
|
-
function
|
|
52701
|
+
function join3(array, separator) {
|
|
52702
52702
|
return array == null ? "" : nativeJoin.call(array, separator);
|
|
52703
52703
|
}
|
|
52704
52704
|
function last(array) {
|
|
@@ -54617,7 +54617,7 @@ var require_lodash3 = __commonJS({
|
|
|
54617
54617
|
lodash.isUndefined = isUndefined;
|
|
54618
54618
|
lodash.isWeakMap = isWeakMap;
|
|
54619
54619
|
lodash.isWeakSet = isWeakSet;
|
|
54620
|
-
lodash.join =
|
|
54620
|
+
lodash.join = join3;
|
|
54621
54621
|
lodash.kebabCase = kebabCase;
|
|
54622
54622
|
lodash.last = last;
|
|
54623
54623
|
lodash.lastIndexOf = lastIndexOf;
|
|
@@ -58429,12 +58429,12 @@ function resolveDslPath(dirArg, dirArgDefault, config) {
|
|
|
58429
58429
|
}
|
|
58430
58430
|
|
|
58431
58431
|
// src/resolver/base-resolver.ts
|
|
58432
|
-
import { resolve as resolve3, join } from "path";
|
|
58432
|
+
import { resolve as resolve3, join as join2 } from "path";
|
|
58433
58433
|
import { stat } from "fs/promises";
|
|
58434
58434
|
|
|
58435
58435
|
// src/loader/loader.ts
|
|
58436
|
-
import { readFile as readFile2 } from "fs/promises";
|
|
58437
|
-
import { dirname as dirname2, resolve as resolve2 } from "path";
|
|
58436
|
+
import { readFile as readFile2, readdir, stat as fsStat } from "fs/promises";
|
|
58437
|
+
import { dirname as dirname2, resolve as resolve2, join, extname } from "path";
|
|
58438
58438
|
import { parse as parseYaml2 } from "yaml";
|
|
58439
58439
|
var DslLoadError = class extends Error {
|
|
58440
58440
|
constructor(message, filePath) {
|
|
@@ -58444,18 +58444,16 @@ var DslLoadError = class extends Error {
|
|
|
58444
58444
|
}
|
|
58445
58445
|
filePath;
|
|
58446
58446
|
};
|
|
58447
|
-
|
|
58448
|
-
"
|
|
58449
|
-
|
|
58450
|
-
"artifacts",
|
|
58451
|
-
"tools",
|
|
58452
|
-
"validations",
|
|
58453
|
-
"handoff_types",
|
|
58454
|
-
"workflow",
|
|
58455
|
-
"policies"
|
|
58456
|
-
];
|
|
58447
|
+
function isRecord(v2) {
|
|
58448
|
+
return typeof v2 === "object" && v2 !== null && !Array.isArray(v2);
|
|
58449
|
+
}
|
|
58457
58450
|
function isRef(value) {
|
|
58458
|
-
return
|
|
58451
|
+
return isRecord(value) && "$ref" in value && typeof value["$ref"] === "string";
|
|
58452
|
+
}
|
|
58453
|
+
function hasRefs(value) {
|
|
58454
|
+
if (!("$refs" in value)) return false;
|
|
58455
|
+
const refs = value["$refs"];
|
|
58456
|
+
return Array.isArray(refs) && refs.every((r3) => typeof r3 === "string");
|
|
58459
58457
|
}
|
|
58460
58458
|
async function readYaml(filePath) {
|
|
58461
58459
|
let content;
|
|
@@ -58477,17 +58475,160 @@ async function readYaml(filePath) {
|
|
|
58477
58475
|
);
|
|
58478
58476
|
}
|
|
58479
58477
|
}
|
|
58480
|
-
|
|
58481
|
-
const
|
|
58482
|
-
for (const
|
|
58483
|
-
const
|
|
58484
|
-
if (
|
|
58485
|
-
|
|
58486
|
-
|
|
58478
|
+
function deepMergeRefs(a3, b3, sourcePath) {
|
|
58479
|
+
const result = { ...a3 };
|
|
58480
|
+
for (const [key, bVal] of Object.entries(b3)) {
|
|
58481
|
+
const aVal = result[key];
|
|
58482
|
+
if (aVal === void 0) {
|
|
58483
|
+
result[key] = bVal;
|
|
58484
|
+
} else if (isRecord(aVal) && isRecord(bVal)) {
|
|
58485
|
+
result[key] = deepMergeRefs(aVal, bVal, sourcePath);
|
|
58486
|
+
} else {
|
|
58487
|
+
throw new DslLoadError(
|
|
58488
|
+
`Conflicting value for key "${key}" while merging $refs from ${sourcePath}`,
|
|
58489
|
+
sourcePath
|
|
58490
|
+
);
|
|
58487
58491
|
}
|
|
58488
58492
|
}
|
|
58493
|
+
return result;
|
|
58494
|
+
}
|
|
58495
|
+
async function loadRefsSource(refPath, baseDir, resolving) {
|
|
58496
|
+
const target = resolve2(baseDir, refPath);
|
|
58497
|
+
const s2 = await fsStat(target).catch(() => null);
|
|
58498
|
+
if (s2?.isDirectory()) {
|
|
58499
|
+
if (resolving.has(target)) {
|
|
58500
|
+
throw new DslLoadError(`Circular $refs detected: ${target}`, target);
|
|
58501
|
+
}
|
|
58502
|
+
resolving.add(target);
|
|
58503
|
+
const result = await loadDirectoryAsMap(target, resolving);
|
|
58504
|
+
resolving.delete(target);
|
|
58505
|
+
return result;
|
|
58506
|
+
}
|
|
58507
|
+
if (!s2?.isFile()) {
|
|
58508
|
+
throw new DslLoadError(`File not found: ${target}`, target);
|
|
58509
|
+
}
|
|
58510
|
+
if (resolving.has(target)) {
|
|
58511
|
+
throw new DslLoadError(`Circular $refs detected: ${target}`, target);
|
|
58512
|
+
}
|
|
58513
|
+
resolving.add(target);
|
|
58514
|
+
const content = await readYaml(target);
|
|
58515
|
+
if (!isRecord(content)) {
|
|
58516
|
+
throw new DslLoadError(
|
|
58517
|
+
`Expected YAML object in ${target}, got ${Array.isArray(content) ? "array" : typeof content}`,
|
|
58518
|
+
target
|
|
58519
|
+
);
|
|
58520
|
+
}
|
|
58521
|
+
const resolved = await resolveRefsDeep(
|
|
58522
|
+
content,
|
|
58523
|
+
dirname2(target),
|
|
58524
|
+
resolving
|
|
58525
|
+
);
|
|
58526
|
+
resolving.delete(target);
|
|
58489
58527
|
return resolved;
|
|
58490
58528
|
}
|
|
58529
|
+
async function loadDirectoryAsMap(dirPath, resolving) {
|
|
58530
|
+
let entries;
|
|
58531
|
+
try {
|
|
58532
|
+
entries = await readdir(dirPath);
|
|
58533
|
+
} catch {
|
|
58534
|
+
throw new DslLoadError(
|
|
58535
|
+
`Cannot read directory: ${dirPath}`,
|
|
58536
|
+
dirPath
|
|
58537
|
+
);
|
|
58538
|
+
}
|
|
58539
|
+
const yamlFiles = entries.filter((f2) => [".yaml", ".yml"].includes(extname(f2))).sort();
|
|
58540
|
+
if (yamlFiles.length === 0) {
|
|
58541
|
+
throw new DslLoadError(
|
|
58542
|
+
`No YAML files found in directory: ${dirPath}`,
|
|
58543
|
+
dirPath
|
|
58544
|
+
);
|
|
58545
|
+
}
|
|
58546
|
+
let merged = {};
|
|
58547
|
+
for (const file of yamlFiles) {
|
|
58548
|
+
const filePath = join(dirPath, file);
|
|
58549
|
+
const content = await readYaml(filePath);
|
|
58550
|
+
if (!isRecord(content)) {
|
|
58551
|
+
throw new DslLoadError(
|
|
58552
|
+
`Expected YAML object in ${filePath}, got ${Array.isArray(content) ? "array" : typeof content}`,
|
|
58553
|
+
filePath
|
|
58554
|
+
);
|
|
58555
|
+
}
|
|
58556
|
+
const resolved = await resolveRefsDeep(
|
|
58557
|
+
content,
|
|
58558
|
+
dirPath,
|
|
58559
|
+
resolving
|
|
58560
|
+
);
|
|
58561
|
+
merged = deepMergeRefs(merged, resolved, filePath);
|
|
58562
|
+
}
|
|
58563
|
+
return merged;
|
|
58564
|
+
}
|
|
58565
|
+
async function processRefs(obj, baseDir, resolving) {
|
|
58566
|
+
const refPaths = obj["$refs"];
|
|
58567
|
+
const inline = {};
|
|
58568
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
58569
|
+
if (key !== "$refs") {
|
|
58570
|
+
inline[key] = value;
|
|
58571
|
+
}
|
|
58572
|
+
}
|
|
58573
|
+
let merged = {};
|
|
58574
|
+
for (const refPath of refPaths) {
|
|
58575
|
+
const loaded = await loadRefsSource(refPath, baseDir, resolving);
|
|
58576
|
+
merged = deepMergeRefs(merged, loaded, refPath);
|
|
58577
|
+
}
|
|
58578
|
+
merged = deepMergeRefs(merged, inline, "(inline)");
|
|
58579
|
+
return merged;
|
|
58580
|
+
}
|
|
58581
|
+
async function resolveRefsDeep(data, baseDir, resolving) {
|
|
58582
|
+
if (typeof data !== "object" || data === null) return data;
|
|
58583
|
+
if (Array.isArray(data)) {
|
|
58584
|
+
return Promise.all(
|
|
58585
|
+
data.map((item) => resolveRefsDeep(item, baseDir, resolving))
|
|
58586
|
+
);
|
|
58587
|
+
}
|
|
58588
|
+
if (isRef(data)) {
|
|
58589
|
+
const refTarget = resolve2(baseDir, data.$ref);
|
|
58590
|
+
const s2 = await fsStat(refTarget).catch(() => null);
|
|
58591
|
+
if (s2?.isDirectory()) {
|
|
58592
|
+
if (resolving.has(refTarget)) {
|
|
58593
|
+
throw new DslLoadError(
|
|
58594
|
+
`Circular $ref detected: ${refTarget}`,
|
|
58595
|
+
refTarget
|
|
58596
|
+
);
|
|
58597
|
+
}
|
|
58598
|
+
resolving.add(refTarget);
|
|
58599
|
+
const result2 = await loadDirectoryAsMap(refTarget, resolving);
|
|
58600
|
+
resolving.delete(refTarget);
|
|
58601
|
+
return result2;
|
|
58602
|
+
}
|
|
58603
|
+
if (!s2?.isFile()) {
|
|
58604
|
+
throw new DslLoadError(`File not found: ${refTarget}`, refTarget);
|
|
58605
|
+
}
|
|
58606
|
+
if (resolving.has(refTarget)) {
|
|
58607
|
+
throw new DslLoadError(
|
|
58608
|
+
`Circular $ref detected: ${refTarget}`,
|
|
58609
|
+
refTarget
|
|
58610
|
+
);
|
|
58611
|
+
}
|
|
58612
|
+
resolving.add(refTarget);
|
|
58613
|
+
const content = await readYaml(refTarget);
|
|
58614
|
+
const resolved = await resolveRefsDeep(
|
|
58615
|
+
content,
|
|
58616
|
+
dirname2(refTarget),
|
|
58617
|
+
resolving
|
|
58618
|
+
);
|
|
58619
|
+
resolving.delete(refTarget);
|
|
58620
|
+
return resolved;
|
|
58621
|
+
}
|
|
58622
|
+
let obj = data;
|
|
58623
|
+
if (hasRefs(obj)) {
|
|
58624
|
+
obj = await processRefs(obj, baseDir, resolving);
|
|
58625
|
+
}
|
|
58626
|
+
const result = {};
|
|
58627
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
58628
|
+
result[key] = await resolveRefsDeep(value, baseDir, resolving);
|
|
58629
|
+
}
|
|
58630
|
+
return result;
|
|
58631
|
+
}
|
|
58491
58632
|
function checkVersion(data, filePath) {
|
|
58492
58633
|
const version = data["version"];
|
|
58493
58634
|
if (version === void 0) {
|
|
@@ -58515,7 +58656,12 @@ async function loadDsl(entryPath) {
|
|
|
58515
58656
|
const data = raw;
|
|
58516
58657
|
checkVersion(data, absPath);
|
|
58517
58658
|
const baseDir = dirname2(absPath);
|
|
58518
|
-
const
|
|
58659
|
+
const resolving = /* @__PURE__ */ new Set([absPath]);
|
|
58660
|
+
const resolved = await resolveRefsDeep(
|
|
58661
|
+
data,
|
|
58662
|
+
baseDir,
|
|
58663
|
+
resolving
|
|
58664
|
+
);
|
|
58519
58665
|
return { data: resolved, filePath: absPath };
|
|
58520
58666
|
}
|
|
58521
58667
|
|
|
@@ -58529,7 +58675,7 @@ var BaseResolveError = class extends Error {
|
|
|
58529
58675
|
async function findEntryFile(dir) {
|
|
58530
58676
|
const candidates = ["agent-contracts.yaml", "agent-contracts.yml"];
|
|
58531
58677
|
for (const name of candidates) {
|
|
58532
|
-
const p2 =
|
|
58678
|
+
const p2 = join2(dir, name);
|
|
58533
58679
|
try {
|
|
58534
58680
|
const s2 = await stat(p2);
|
|
58535
58681
|
if (s2.isFile()) return p2;
|
|
@@ -58581,7 +58727,7 @@ var MergeError = class extends Error {
|
|
|
58581
58727
|
this.name = "MergeError";
|
|
58582
58728
|
}
|
|
58583
58729
|
};
|
|
58584
|
-
function
|
|
58730
|
+
function isRecord2(v2) {
|
|
58585
58731
|
return typeof v2 === "object" && v2 !== null && !Array.isArray(v2);
|
|
58586
58732
|
}
|
|
58587
58733
|
function hasOperator(obj) {
|
|
@@ -58593,7 +58739,7 @@ function hasOperator(obj) {
|
|
|
58593
58739
|
}
|
|
58594
58740
|
function findIndexById(arr, id) {
|
|
58595
58741
|
return arr.findIndex(
|
|
58596
|
-
(item) =>
|
|
58742
|
+
(item) => isRecord2(item) && item["id"] === id
|
|
58597
58743
|
);
|
|
58598
58744
|
}
|
|
58599
58745
|
function applyArrayMergeOperator(baseArray, operatorObj, path) {
|
|
@@ -58629,7 +58775,7 @@ function applyArrayMergeOperator(baseArray, operatorObj, path) {
|
|
|
58629
58775
|
const removeList = operatorObj["$remove"];
|
|
58630
58776
|
const idsToRemove = new Set(removeList.map((r3) => r3["id"]));
|
|
58631
58777
|
const result = baseArray.filter((item) => {
|
|
58632
|
-
if (
|
|
58778
|
+
if (isRecord2(item) && typeof item["id"] === "string") {
|
|
58633
58779
|
const itemId = item["id"];
|
|
58634
58780
|
if (idsToRemove.has(itemId)) {
|
|
58635
58781
|
idsToRemove.delete(itemId);
|
|
@@ -58718,7 +58864,7 @@ function deepMergeEntities(base, project, path, hasExtends) {
|
|
|
58718
58864
|
for (const key of Object.keys(project)) {
|
|
58719
58865
|
const baseVal = result[key];
|
|
58720
58866
|
const projVal = project[key];
|
|
58721
|
-
if (
|
|
58867
|
+
if (isRecord2(projVal) && hasOperator(projVal)) {
|
|
58722
58868
|
if (!hasExtends) {
|
|
58723
58869
|
throw new MergeError(
|
|
58724
58870
|
`Merge operator used without extends at ${path}.${key}`
|
|
@@ -58726,12 +58872,12 @@ function deepMergeEntities(base, project, path, hasExtends) {
|
|
|
58726
58872
|
}
|
|
58727
58873
|
if (Array.isArray(baseVal)) {
|
|
58728
58874
|
result[key] = applyArrayMergeOperator(baseVal, projVal, `${path}.${key}`);
|
|
58729
|
-
} else if (
|
|
58875
|
+
} else if (isRecord2(baseVal)) {
|
|
58730
58876
|
result[key] = applyMapMergeOperator(baseVal, projVal, `${path}.${key}`);
|
|
58731
58877
|
} else {
|
|
58732
58878
|
result[key] = applyArrayMergeOperator([], projVal, `${path}.${key}`);
|
|
58733
58879
|
}
|
|
58734
|
-
} else if (
|
|
58880
|
+
} else if (isRecord2(projVal) && isRecord2(baseVal) && !Array.isArray(projVal) && !Array.isArray(baseVal)) {
|
|
58735
58881
|
result[key] = deepMergeEntities(
|
|
58736
58882
|
baseVal,
|
|
58737
58883
|
projVal,
|
|
@@ -58745,7 +58891,7 @@ function deepMergeEntities(base, project, path, hasExtends) {
|
|
|
58745
58891
|
return result;
|
|
58746
58892
|
}
|
|
58747
58893
|
function mergeEntityMaps(baseMap, projectMap, path, hasExtends) {
|
|
58748
|
-
if (
|
|
58894
|
+
if (isRecord2(projectMap) && !Array.isArray(projectMap)) {
|
|
58749
58895
|
const op = hasOperator(projectMap);
|
|
58750
58896
|
if (op) {
|
|
58751
58897
|
if (!hasExtends) {
|
|
@@ -58759,7 +58905,7 @@ function mergeEntityMaps(baseMap, projectMap, path, hasExtends) {
|
|
|
58759
58905
|
const result = { ...baseMap };
|
|
58760
58906
|
for (const [key, projVal] of Object.entries(projectMap)) {
|
|
58761
58907
|
const baseVal = result[key];
|
|
58762
|
-
if (baseVal !== void 0 &&
|
|
58908
|
+
if (baseVal !== void 0 && isRecord2(baseVal) && isRecord2(projVal)) {
|
|
58763
58909
|
result[key] = deepMergeEntities(
|
|
58764
58910
|
baseVal,
|
|
58765
58911
|
projVal,
|
|
@@ -58799,7 +58945,7 @@ function mergeDsl(base, project) {
|
|
|
58799
58945
|
if (projVal === void 0) {
|
|
58800
58946
|
continue;
|
|
58801
58947
|
}
|
|
58802
|
-
const baseMap =
|
|
58948
|
+
const baseMap = isRecord2(baseVal) ? baseVal : {};
|
|
58803
58949
|
result[section] = mergeEntityMaps(
|
|
58804
58950
|
baseMap,
|
|
58805
58951
|
projVal,
|