agent-contracts 0.9.0 → 0.11.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 +266 -18
- package/dist/cli.js +10854 -18000
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +336 -7
- package/dist/index.js +10761 -17976
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -228,10 +228,39 @@ Workflow steps support additional properties:
|
|
|
228
228
|
* `group` — consecutive steps with the same group are rendered as `par` (parallel) blocks in sequence diagrams
|
|
229
229
|
* `retry` (handoff steps only) — defines a conditional retry loop with `condition`, `fix_task`, and optional `revalidate_task`
|
|
230
230
|
|
|
231
|
-
###
|
|
231
|
+
### Guardrail
|
|
232
232
|
|
|
233
|
-
A **
|
|
234
|
-
|
|
233
|
+
A **Guardrail** declares a cross-cutting constraint:
|
|
234
|
+
|
|
235
|
+
* description — what is protected
|
|
236
|
+
* scope — which DSL entities it applies to (agents, tasks, tools, artifacts, workflows)
|
|
237
|
+
* rationale — why the constraint exists
|
|
238
|
+
* tags — classification for filtering
|
|
239
|
+
* exemptions — glob patterns or entity IDs exempt from the guardrail
|
|
240
|
+
|
|
241
|
+
### Guardrail Policy
|
|
242
|
+
|
|
243
|
+
A **Guardrail Policy** defines enforcement strategy for guardrails:
|
|
244
|
+
|
|
245
|
+
* rules — array of enforcement rules mapping guardrails to actions
|
|
246
|
+
* Each rule specifies: severity (`critical`/`mandatory`/`warning`/`info`), action (`block`/`warn`/`shadow`/`info`), override permissions
|
|
247
|
+
|
|
248
|
+
### Handoff Type
|
|
249
|
+
|
|
250
|
+
A **Handoff Type** defines the schema for inter-agent messages:
|
|
251
|
+
|
|
252
|
+
* `schema` — a JSON Schema object describing the full message structure
|
|
253
|
+
* `description`
|
|
254
|
+
* `example`
|
|
255
|
+
* `version`
|
|
256
|
+
|
|
257
|
+
Schemas can use `allOf` with `$ref: "#/components/schemas/..."` to compose shared fields (e.g., common envelope) with type-specific properties.
|
|
258
|
+
|
|
259
|
+
### Components
|
|
260
|
+
|
|
261
|
+
**Components** provide reusable definitions, following the OpenAPI pattern:
|
|
262
|
+
|
|
263
|
+
* `components.schemas` — named JSON Schema fragments that can be referenced from anywhere via `$ref: "#/components/schemas/<name>"`
|
|
235
264
|
|
|
236
265
|
---
|
|
237
266
|
|
|
@@ -265,12 +294,17 @@ Design regressions become testable.
|
|
|
265
294
|
* **Static schema validation**
|
|
266
295
|
* **Reference integrity checks**
|
|
267
296
|
* **Semantic linting**
|
|
268
|
-
* **Structured handoff definitions**
|
|
297
|
+
* **Structured handoff definitions** with formal JSON Schema and `allOf` composition
|
|
298
|
+
* **Reusable schema components** via `components.schemas` and JSON Pointer `$ref`
|
|
269
299
|
* **Artifact ownership and lifecycle modeling**
|
|
270
300
|
* **Config-driven prompt rendering**
|
|
271
301
|
* **Variable substitution** via `${vars.xxx}` in DSL values
|
|
272
302
|
* **Inheritance with merge operators via `extends`**
|
|
273
|
-
* **
|
|
303
|
+
* **Guardrail definitions** for cross-cutting process constraints
|
|
304
|
+
* **Guardrail policies** with configurable enforcement (block/warn/shadow/info)
|
|
305
|
+
* **Software bindings** (DI) for tool-specific guardrail implementation (Cursor, Git, GitHub)
|
|
306
|
+
* **Guardrail generation** from DSL + policy + bindings via `generate guardrails`
|
|
307
|
+
* **Flexible file splitting** via `$ref` (replacement), `$refs` (import + deep-merge), and JSON Pointer `$ref` (in-document)
|
|
274
308
|
* **JSON Schema for editor support and external tooling**
|
|
275
309
|
* **CI-friendly workflow checks**
|
|
276
310
|
|
|
@@ -304,6 +338,10 @@ validations: {}
|
|
|
304
338
|
handoff_types: {}
|
|
305
339
|
workflow: {}
|
|
306
340
|
policies: {}
|
|
341
|
+
guardrails: {}
|
|
342
|
+
guardrail_policies: {}
|
|
343
|
+
components:
|
|
344
|
+
schemas: {}
|
|
307
345
|
````
|
|
308
346
|
|
|
309
347
|
This makes definitions easy to merge, extend, and reference by stable identifiers.
|
|
@@ -421,8 +459,44 @@ Overlapping map keys are deep-merged recursively. Conflicting leaf values (scala
|
|
|
421
459
|
| Directive | Type | Behavior |
|
|
422
460
|
| --------- | ------ | -------------------------------------------------------- |
|
|
423
461
|
| `$ref` | string | Replace the object at that position with file contents |
|
|
462
|
+
| `$ref` (`#/...`) | string | Replace with the value at the given JSON Pointer path within the document |
|
|
424
463
|
| `$refs` | array | Import files and deep-merge into the containing map |
|
|
425
464
|
|
|
465
|
+
### JSON Pointer `$ref`
|
|
466
|
+
|
|
467
|
+
`$ref` also supports **in-document references** using JSON Pointer syntax (RFC 6901).
|
|
468
|
+
When the value starts with `#/`, it resolves against the root document instead of the file system.
|
|
469
|
+
|
|
470
|
+
````yaml
|
|
471
|
+
components:
|
|
472
|
+
schemas:
|
|
473
|
+
handoff-common:
|
|
474
|
+
type: object
|
|
475
|
+
required: [from_agent, to_agent]
|
|
476
|
+
properties:
|
|
477
|
+
from_agent: { type: string }
|
|
478
|
+
to_agent: { type: string }
|
|
479
|
+
|
|
480
|
+
handoff_types:
|
|
481
|
+
task-delegation:
|
|
482
|
+
version: 1
|
|
483
|
+
schema:
|
|
484
|
+
allOf:
|
|
485
|
+
- $ref: "#/components/schemas/handoff-common"
|
|
486
|
+
- type: object
|
|
487
|
+
required: [payload]
|
|
488
|
+
properties:
|
|
489
|
+
payload:
|
|
490
|
+
type: object
|
|
491
|
+
required: [objective]
|
|
492
|
+
properties:
|
|
493
|
+
objective: { type: string }
|
|
494
|
+
````
|
|
495
|
+
|
|
496
|
+
This is particularly useful for sharing common schema fragments across multiple `handoff_types` entries via `components.schemas`.
|
|
497
|
+
|
|
498
|
+
JSON Pointer references are resolved in the same processing phase as file `$ref` — before Zod validation. They can be used anywhere in the document, not just within `handoff_types`.
|
|
499
|
+
|
|
426
500
|
---
|
|
427
501
|
|
|
428
502
|
## Example: Agent definition
|
|
@@ -545,6 +619,80 @@ workflow:
|
|
|
545
619
|
|
|
546
620
|
---
|
|
547
621
|
|
|
622
|
+
## Example: Handoff type definition
|
|
623
|
+
|
|
624
|
+
Handoff types define the schema for inter-agent messages using JSON Schema.
|
|
625
|
+
|
|
626
|
+
````yaml
|
|
627
|
+
handoff_types:
|
|
628
|
+
task-delegation:
|
|
629
|
+
version: 1
|
|
630
|
+
description: "Delegate a task to a sub-agent"
|
|
631
|
+
schema:
|
|
632
|
+
type: object
|
|
633
|
+
required: [task, objective]
|
|
634
|
+
properties:
|
|
635
|
+
task: { type: string }
|
|
636
|
+
objective: { type: string }
|
|
637
|
+
constraints:
|
|
638
|
+
type: array
|
|
639
|
+
items: { type: string }
|
|
640
|
+
````
|
|
641
|
+
|
|
642
|
+
### Using `components.schemas` with `allOf`
|
|
643
|
+
|
|
644
|
+
Common fields (e.g., `from_agent`, `to_agent`, `run_id`) can be shared across handoff types by placing them in `components.schemas` and composing via `allOf`:
|
|
645
|
+
|
|
646
|
+
````yaml
|
|
647
|
+
components:
|
|
648
|
+
schemas:
|
|
649
|
+
handoff-common:
|
|
650
|
+
type: object
|
|
651
|
+
required: [from_agent, to_agent]
|
|
652
|
+
properties:
|
|
653
|
+
from_agent: { type: string }
|
|
654
|
+
to_agent: { type: string }
|
|
655
|
+
run_id: { type: string }
|
|
656
|
+
|
|
657
|
+
handoff_types:
|
|
658
|
+
task-delegation:
|
|
659
|
+
version: 1
|
|
660
|
+
description: "Delegate a task"
|
|
661
|
+
schema:
|
|
662
|
+
allOf:
|
|
663
|
+
- $ref: "#/components/schemas/handoff-common"
|
|
664
|
+
- type: object
|
|
665
|
+
required: [payload]
|
|
666
|
+
properties:
|
|
667
|
+
payload:
|
|
668
|
+
type: object
|
|
669
|
+
required: [objective]
|
|
670
|
+
properties:
|
|
671
|
+
objective: { type: string }
|
|
672
|
+
|
|
673
|
+
implementation-result:
|
|
674
|
+
version: 1
|
|
675
|
+
description: "Return implementation results"
|
|
676
|
+
schema:
|
|
677
|
+
allOf:
|
|
678
|
+
- $ref: "#/components/schemas/handoff-common"
|
|
679
|
+
- type: object
|
|
680
|
+
required: [payload]
|
|
681
|
+
properties:
|
|
682
|
+
payload:
|
|
683
|
+
type: object
|
|
684
|
+
required: [result]
|
|
685
|
+
properties:
|
|
686
|
+
result: { type: string }
|
|
687
|
+
evidence:
|
|
688
|
+
type: array
|
|
689
|
+
items: { type: string }
|
|
690
|
+
````
|
|
691
|
+
|
|
692
|
+
The `$ref: "#/..."` references are resolved during loading, before validation. The resulting merged schema is then meta-validated as valid JSON Schema.
|
|
693
|
+
|
|
694
|
+
---
|
|
695
|
+
|
|
548
696
|
## Inheritance and merge operators
|
|
549
697
|
|
|
550
698
|
`agent-contracts` supports shared base definitions with project-level overrides through `extends`.
|
|
@@ -665,6 +813,7 @@ npx agent-contracts
|
|
|
665
813
|
| `agent-contracts validate [path]` | Validate schema and references |
|
|
666
814
|
| `agent-contracts lint [path]` | Run semantic lint |
|
|
667
815
|
| `agent-contracts render` | Render outputs from config |
|
|
816
|
+
| `agent-contracts generate guardrails` | Generate guardrail artifacts from bindings |
|
|
668
817
|
| `agent-contracts check` | Run resolve → validate → lint → render --check |
|
|
669
818
|
|
|
670
819
|
The `[path]` argument defaults to `agent-contracts.yaml` in the current directory.
|
|
@@ -731,6 +880,8 @@ Each `context` type provides a different rendering scope:
|
|
|
731
880
|
| `handoff_type` | Per handoff type | `{handoff_type.id}` in output path | `handoff_type`, `relatedTasks`, `dsl` |
|
|
732
881
|
| `workflow` | Per workflow phase | `{workflow.id}` in output path | `workflow`, `relatedAgents`, `relatedTasks`, `relatedTools`, `relatedArtifacts`, `relatedValidations`, `dsl` |
|
|
733
882
|
| `policy` | Per policy | `{policy.id}` in output path | `policy`, `dsl` |
|
|
883
|
+
| `guardrail` | Per guardrail | `{guardrail.id}` in output path | `guardrail`, `dsl` |
|
|
884
|
+
| `guardrail_policy` | Per guardrail policy | `{guardrail_policy.id}` in output path | `guardrail_policy`, `dsl` |
|
|
734
885
|
|
|
735
886
|
#### Enriched context details
|
|
736
887
|
|
|
@@ -764,7 +915,7 @@ Templates can use these built-in helpers:
|
|
|
764
915
|
| `notEmpty` | `{{#if (notEmpty obj)}}` | True when object has at least one key |
|
|
765
916
|
| `inc` | `{{inc @index}}` | Increment number by 1 (for 1-based indexing) |
|
|
766
917
|
| `yamlBlock` | `{{{yamlBlock obj}}}` | Render value as YAML-formatted text |
|
|
767
|
-
| `lookupPayloadFields` | `{{#each (lookupPayloadFields
|
|
918
|
+
| `lookupPayloadFields` | `{{#each (lookupPayloadFields schema)}}` | Extract schema field info (name, type, required, enum); resolves `allOf` internally |
|
|
768
919
|
| `join` | `{{join arr ", "}}` | Join array elements with separator |
|
|
769
920
|
| `contains` | `{{#if (contains arr "x")}}` | True when array includes value |
|
|
770
921
|
| `groupBy` | `{{#with (groupBy arr "key")}}` | Group array elements by field value |
|
|
@@ -781,6 +932,102 @@ Templates can use these built-in helpers:
|
|
|
781
932
|
|
|
782
933
|
---
|
|
783
934
|
|
|
935
|
+
## Guardrail DI system
|
|
936
|
+
|
|
937
|
+
`agent-contracts` includes a dependency injection system for guardrails that separates **what to protect** from **how to enforce** and **where to output**.
|
|
938
|
+
|
|
939
|
+
### Architecture
|
|
940
|
+
|
|
941
|
+
```text
|
|
942
|
+
agent-contracts.yaml (DSL) agent-contracts.config.yaml
|
|
943
|
+
├─ guardrails: (what + why) ├─ bindings: [cursor.yaml, git.yaml, ...]
|
|
944
|
+
├─ guardrail_policies: (how) ├─ active_guardrail_policy: default
|
|
945
|
+
└─ agents, tasks, ... ├─ paths: {cursor_root: .cursor, ...}
|
|
946
|
+
└─ vars, renders (existing)
|
|
947
|
+
```
|
|
948
|
+
|
|
949
|
+
### Guardrail definition
|
|
950
|
+
|
|
951
|
+
Guardrails declare constraints in the DSL without any implementation details:
|
|
952
|
+
|
|
953
|
+
````yaml
|
|
954
|
+
guardrails:
|
|
955
|
+
no-force-push:
|
|
956
|
+
description: "Force push to protected branches is forbidden"
|
|
957
|
+
scope:
|
|
958
|
+
tools: [git]
|
|
959
|
+
rationale: "Force push destroys commit history"
|
|
960
|
+
tags: [branch-protection, safety]
|
|
961
|
+
````
|
|
962
|
+
|
|
963
|
+
### Guardrail policy
|
|
964
|
+
|
|
965
|
+
Policies define enforcement strategies:
|
|
966
|
+
|
|
967
|
+
````yaml
|
|
968
|
+
guardrail_policies:
|
|
969
|
+
default-enforcement:
|
|
970
|
+
rules:
|
|
971
|
+
- guardrail: no-force-push
|
|
972
|
+
severity: critical
|
|
973
|
+
action: block
|
|
974
|
+
- guardrail: english-only-code
|
|
975
|
+
severity: warning
|
|
976
|
+
action: warn
|
|
977
|
+
allow_override: true
|
|
978
|
+
````
|
|
979
|
+
|
|
980
|
+
### Software bindings
|
|
981
|
+
|
|
982
|
+
Bindings define software-specific check implementations:
|
|
983
|
+
|
|
984
|
+
````yaml
|
|
985
|
+
# bindings/cursor.yaml
|
|
986
|
+
software: cursor
|
|
987
|
+
version: 1
|
|
988
|
+
|
|
989
|
+
guardrail_impl:
|
|
990
|
+
no-force-push:
|
|
991
|
+
checks:
|
|
992
|
+
- hook_event: beforeShellExecution
|
|
993
|
+
matcher:
|
|
994
|
+
type: command_regex
|
|
995
|
+
pattern: "git\\s+push\\s+.*--force"
|
|
996
|
+
message: "Force push is forbidden"
|
|
997
|
+
|
|
998
|
+
outputs:
|
|
999
|
+
hook-script:
|
|
1000
|
+
target: "{cursor_root}/hooks/evaluate-hook.sh"
|
|
1001
|
+
mode: write
|
|
1002
|
+
executable: true
|
|
1003
|
+
template: ./templates/cursor-hook-wrapper.sh.hbs
|
|
1004
|
+
````
|
|
1005
|
+
|
|
1006
|
+
### Config
|
|
1007
|
+
|
|
1008
|
+
````yaml
|
|
1009
|
+
# agent-contracts.config.yaml
|
|
1010
|
+
bindings:
|
|
1011
|
+
- ./bindings/cursor.yaml
|
|
1012
|
+
- ./bindings/git.yaml
|
|
1013
|
+
|
|
1014
|
+
active_guardrail_policy: default-enforcement
|
|
1015
|
+
|
|
1016
|
+
paths:
|
|
1017
|
+
cursor_root: .cursor
|
|
1018
|
+
git_hooks_root: scripts/git-hooks
|
|
1019
|
+
````
|
|
1020
|
+
|
|
1021
|
+
### Generate command
|
|
1022
|
+
|
|
1023
|
+
````bash
|
|
1024
|
+
agent-contracts generate guardrails -c agent-contracts.config.yaml
|
|
1025
|
+
agent-contracts generate guardrails -c agent-contracts.config.yaml --binding cursor
|
|
1026
|
+
agent-contracts generate guardrails -c agent-contracts.config.yaml --dry-run
|
|
1027
|
+
````
|
|
1028
|
+
|
|
1029
|
+
---
|
|
1030
|
+
|
|
784
1031
|
## Validation model
|
|
785
1032
|
|
|
786
1033
|
`agent-contracts` validates your system in multiple layers.
|
|
@@ -792,7 +1039,8 @@ Checks:
|
|
|
792
1039
|
* required fields
|
|
793
1040
|
* types
|
|
794
1041
|
* enums
|
|
795
|
-
* handoff
|
|
1042
|
+
* handoff schema shape (meta-validated as valid JSON Schema via ajv)
|
|
1043
|
+
* `allOf` composition in handoff schemas
|
|
796
1044
|
* invalid custom properties without `x-` prefix (checked at all nesting levels)
|
|
797
1045
|
|
|
798
1046
|
Custom properties with `x-` prefix are allowed on any object in the DSL — top-level entities (agents, tasks, artifacts, …), nested objects (rules, execution steps, workflow steps, …), and the root DSL itself.
|
|
@@ -803,7 +1051,7 @@ Checks:
|
|
|
803
1051
|
|
|
804
1052
|
* cross-entity references
|
|
805
1053
|
* owner / producer / editor / consumer validity
|
|
806
|
-
* handoff
|
|
1054
|
+
* handoff schema consistency (`required` vs. `properties` alignment)
|
|
807
1055
|
* permission alignment between agents and artifacts
|
|
808
1056
|
|
|
809
1057
|
### Semantic lint
|
|
@@ -839,16 +1087,16 @@ That separation keeps runtime concerns and architecture concerns from being mixe
|
|
|
839
1087
|
|
|
840
1088
|
## Tech stack
|
|
841
1089
|
|
|
842
|
-
| Category
|
|
843
|
-
|
|
|
844
|
-
| Language
|
|
845
|
-
| Schema
|
|
846
|
-
| YAML parsing
|
|
847
|
-
| Lint
|
|
848
|
-
| Templates
|
|
849
|
-
| CLI
|
|
850
|
-
| Testing
|
|
851
|
-
| Build
|
|
1090
|
+
| Category | Choice |
|
|
1091
|
+
| -------------- | ---------------------------------- |
|
|
1092
|
+
| Language | TypeScript (ESM, strict mode) |
|
|
1093
|
+
| Schema | Zod + ajv (JSON Schema meta-validation) |
|
|
1094
|
+
| YAML parsing | yaml |
|
|
1095
|
+
| Lint | TypeScript custom rules + Spectral |
|
|
1096
|
+
| Templates | Handlebars |
|
|
1097
|
+
| CLI | commander |
|
|
1098
|
+
| Testing | Vitest |
|
|
1099
|
+
| Build | tsup |
|
|
852
1100
|
|
|
853
1101
|
---
|
|
854
1102
|
|