agent-contracts 0.9.0 → 0.10.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 CHANGED
@@ -228,10 +228,22 @@ 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
- ### Handoff
231
+ ### Handoff Type
232
232
 
233
- A **Handoff** is a runtime delegation instance.
234
- The YAML defines the allowed handoff types and constraints; concrete handoffs are created at runtime.
233
+ A **Handoff Type** defines the schema for inter-agent messages:
234
+
235
+ * `schema` — a JSON Schema object describing the full message structure
236
+ * `description`
237
+ * `example`
238
+ * `version`
239
+
240
+ Schemas can use `allOf` with `$ref: "#/components/schemas/..."` to compose shared fields (e.g., common envelope) with type-specific properties.
241
+
242
+ ### Components
243
+
244
+ **Components** provide reusable definitions, following the OpenAPI pattern:
245
+
246
+ * `components.schemas` — named JSON Schema fragments that can be referenced from anywhere via `$ref: "#/components/schemas/<name>"`
235
247
 
236
248
  ---
237
249
 
@@ -265,12 +277,13 @@ Design regressions become testable.
265
277
  * **Static schema validation**
266
278
  * **Reference integrity checks**
267
279
  * **Semantic linting**
268
- * **Structured handoff definitions**
280
+ * **Structured handoff definitions** with formal JSON Schema and `allOf` composition
281
+ * **Reusable schema components** via `components.schemas` and JSON Pointer `$ref`
269
282
  * **Artifact ownership and lifecycle modeling**
270
283
  * **Config-driven prompt rendering**
271
284
  * **Variable substitution** via `${vars.xxx}` in DSL values
272
285
  * **Inheritance with merge operators via `extends`**
273
- * **Flexible file splitting** via `$ref` (replacement) and `$refs` (import + deep-merge)
286
+ * **Flexible file splitting** via `$ref` (replacement), `$refs` (import + deep-merge), and JSON Pointer `$ref` (in-document)
274
287
  * **JSON Schema for editor support and external tooling**
275
288
  * **CI-friendly workflow checks**
276
289
 
@@ -304,6 +317,8 @@ validations: {}
304
317
  handoff_types: {}
305
318
  workflow: {}
306
319
  policies: {}
320
+ components:
321
+ schemas: {}
307
322
  ````
308
323
 
309
324
  This makes definitions easy to merge, extend, and reference by stable identifiers.
@@ -421,8 +436,44 @@ Overlapping map keys are deep-merged recursively. Conflicting leaf values (scala
421
436
  | Directive | Type | Behavior |
422
437
  | --------- | ------ | -------------------------------------------------------- |
423
438
  | `$ref` | string | Replace the object at that position with file contents |
439
+ | `$ref` (`#/...`) | string | Replace with the value at the given JSON Pointer path within the document |
424
440
  | `$refs` | array | Import files and deep-merge into the containing map |
425
441
 
442
+ ### JSON Pointer `$ref`
443
+
444
+ `$ref` also supports **in-document references** using JSON Pointer syntax (RFC 6901).
445
+ When the value starts with `#/`, it resolves against the root document instead of the file system.
446
+
447
+ ````yaml
448
+ components:
449
+ schemas:
450
+ handoff-common:
451
+ type: object
452
+ required: [from_agent, to_agent]
453
+ properties:
454
+ from_agent: { type: string }
455
+ to_agent: { type: string }
456
+
457
+ handoff_types:
458
+ task-delegation:
459
+ version: 1
460
+ schema:
461
+ allOf:
462
+ - $ref: "#/components/schemas/handoff-common"
463
+ - type: object
464
+ required: [payload]
465
+ properties:
466
+ payload:
467
+ type: object
468
+ required: [objective]
469
+ properties:
470
+ objective: { type: string }
471
+ ````
472
+
473
+ This is particularly useful for sharing common schema fragments across multiple `handoff_types` entries via `components.schemas`.
474
+
475
+ 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`.
476
+
426
477
  ---
427
478
 
428
479
  ## Example: Agent definition
@@ -545,6 +596,80 @@ workflow:
545
596
 
546
597
  ---
547
598
 
599
+ ## Example: Handoff type definition
600
+
601
+ Handoff types define the schema for inter-agent messages using JSON Schema.
602
+
603
+ ````yaml
604
+ handoff_types:
605
+ task-delegation:
606
+ version: 1
607
+ description: "Delegate a task to a sub-agent"
608
+ schema:
609
+ type: object
610
+ required: [task, objective]
611
+ properties:
612
+ task: { type: string }
613
+ objective: { type: string }
614
+ constraints:
615
+ type: array
616
+ items: { type: string }
617
+ ````
618
+
619
+ ### Using `components.schemas` with `allOf`
620
+
621
+ 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`:
622
+
623
+ ````yaml
624
+ components:
625
+ schemas:
626
+ handoff-common:
627
+ type: object
628
+ required: [from_agent, to_agent]
629
+ properties:
630
+ from_agent: { type: string }
631
+ to_agent: { type: string }
632
+ run_id: { type: string }
633
+
634
+ handoff_types:
635
+ task-delegation:
636
+ version: 1
637
+ description: "Delegate a task"
638
+ schema:
639
+ allOf:
640
+ - $ref: "#/components/schemas/handoff-common"
641
+ - type: object
642
+ required: [payload]
643
+ properties:
644
+ payload:
645
+ type: object
646
+ required: [objective]
647
+ properties:
648
+ objective: { type: string }
649
+
650
+ implementation-result:
651
+ version: 1
652
+ description: "Return implementation results"
653
+ schema:
654
+ allOf:
655
+ - $ref: "#/components/schemas/handoff-common"
656
+ - type: object
657
+ required: [payload]
658
+ properties:
659
+ payload:
660
+ type: object
661
+ required: [result]
662
+ properties:
663
+ result: { type: string }
664
+ evidence:
665
+ type: array
666
+ items: { type: string }
667
+ ````
668
+
669
+ The `$ref: "#/..."` references are resolved during loading, before validation. The resulting merged schema is then meta-validated as valid JSON Schema.
670
+
671
+ ---
672
+
548
673
  ## Inheritance and merge operators
549
674
 
550
675
  `agent-contracts` supports shared base definitions with project-level overrides through `extends`.
@@ -764,7 +889,7 @@ Templates can use these built-in helpers:
764
889
  | `notEmpty` | `{{#if (notEmpty obj)}}` | True when object has at least one key |
765
890
  | `inc` | `{{inc @index}}` | Increment number by 1 (for 1-based indexing) |
766
891
  | `yamlBlock` | `{{{yamlBlock obj}}}` | Render value as YAML-formatted text |
767
- | `lookupPayloadFields` | `{{#each (lookupPayloadFields payload)}}` | Extract payload field info (name, type, required, enum) |
892
+ | `lookupPayloadFields` | `{{#each (lookupPayloadFields schema)}}` | Extract schema field info (name, type, required, enum); resolves `allOf` internally |
768
893
  | `join` | `{{join arr ", "}}` | Join array elements with separator |
769
894
  | `contains` | `{{#if (contains arr "x")}}` | True when array includes value |
770
895
  | `groupBy` | `{{#with (groupBy arr "key")}}` | Group array elements by field value |
@@ -792,7 +917,8 @@ Checks:
792
917
  * required fields
793
918
  * types
794
919
  * enums
795
- * handoff payload shape
920
+ * handoff schema shape (meta-validated as valid JSON Schema via ajv)
921
+ * `allOf` composition in handoff schemas
796
922
  * invalid custom properties without `x-` prefix (checked at all nesting levels)
797
923
 
798
924
  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 +929,7 @@ Checks:
803
929
 
804
930
  * cross-entity references
805
931
  * owner / producer / editor / consumer validity
806
- * handoff and payload consistency
932
+ * handoff schema consistency (`required` vs. `properties` alignment)
807
933
  * permission alignment between agents and artifacts
808
934
 
809
935
  ### Semantic lint
@@ -839,16 +965,16 @@ That separation keeps runtime concerns and architecture concerns from being mixe
839
965
 
840
966
  ## Tech stack
841
967
 
842
- | Category | Choice |
843
- | ------------ | ---------------------------------- |
844
- | Language | TypeScript (ESM, strict mode) |
845
- | Schema | Zod |
846
- | YAML parsing | yaml |
847
- | Lint | TypeScript custom rules + Spectral |
848
- | Templates | Handlebars |
849
- | CLI | commander |
850
- | Testing | Vitest |
851
- | Build | tsup |
968
+ | Category | Choice |
969
+ | -------------- | ---------------------------------- |
970
+ | Language | TypeScript (ESM, strict mode) |
971
+ | Schema | Zod + ajv (JSON Schema meta-validation) |
972
+ | YAML parsing | yaml |
973
+ | Lint | TypeScript custom rules + Spectral |
974
+ | Templates | Handlebars |
975
+ | CLI | commander |
976
+ | Testing | Vitest |
977
+ | Build | tsup |
852
978
 
853
979
  ---
854
980