agent-contracts 0.19.2 → 0.19.4

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
@@ -229,6 +229,7 @@ A **Workflow** defines a phase-level execution sequence:
229
229
  Workflow steps support additional properties:
230
230
 
231
231
  * `group` — consecutive steps with the same group are rendered as `par` (parallel) blocks in sequence diagrams
232
+ * `depends_on` — list of step task IDs that must complete before this step starts. When specified, the runtime can execute independent steps in parallel. When omitted, the step implicitly depends on all preceding steps (sequential execution)
232
233
  * `max_retries` (delegate steps) — maximum number of full task re-executions (new sessions) allowed per step. Defaults to `0` (no retries), or `1` when a `retry` block is present
233
234
  * `max_follow_ups` (delegate steps) — maximum number of lightweight same-session follow-up messages for output format corrections
234
235
  * `retry` (delegate steps) — defines a conditional retry loop with `condition`, `fix_task`, and optional `revalidate_task`. These are rendered as recovery instructions in the LLM prompt
package/cli-contract.yaml CHANGED
@@ -3,7 +3,7 @@ cliContracts: 0.1.0
3
3
 
4
4
  info:
5
5
  title: agent-contracts CLI
6
- version: 0.19.2
6
+ version: 0.19.4
7
7
  description: >-
8
8
  Declarative YAML DSL toolkit for defining, validating, and rendering
9
9
  multi-agent development workflows. Provides static validation,
@@ -625,44 +625,81 @@ commandSets:
625
625
  schema:
626
626
  type: string
627
627
 
628
+ - name: fail-on
629
+ description: >-
630
+ Minimum finding severity that causes exit 10
631
+ (info|warning|error|critical).
632
+ valueName: level
633
+ schema:
634
+ type: string
635
+ enum: [info, warning, error, critical]
636
+ default: critical
637
+
638
+ - name: output
639
+ aliases: [o]
640
+ description: Write result to a file instead of stdout.
641
+ valueName: file
642
+ schema:
643
+ type: string
644
+ file:
645
+ mode: write
646
+ encoding: utf-8
647
+
648
+ - name: report-format
649
+ description: >-
650
+ Alias for --format. When both are specified, --report-format
651
+ takes precedence.
652
+ valueName: fmt
653
+ schema:
654
+ type: string
655
+ enum: [text, json, markdown]
656
+ default: text
657
+
628
658
  exits:
629
659
  '0':
630
- description: No critical findings detected.
660
+ description: Audit succeeded. No findings at or above --fail-on threshold.
631
661
  stdout:
632
662
  format: '{options.format}'
633
- schemaNote: >-
634
- Audit findings are always emitted on stdout as the primary
635
- output. Format depends on --format option (text, json, or
636
- markdown).
663
+ schema:
664
+ $ref: './dsl_base/components.yaml#/schemas/agent-audit-result'
637
665
 
638
666
  '1':
639
- description: Critical findings detected.
667
+ description: Unexpected error (invalid input, config error, or internal failure).
640
668
  stderr:
641
669
  format: text
642
- schemaNote: >-
643
- Summary diagnostic on stderr. Structured findings are
644
- emitted on stdout in the requested --format.
670
+
671
+ '10':
672
+ description: Findings at or above --fail-on severity threshold detected.
645
673
  stdout:
646
674
  format: '{options.format}'
675
+ schema:
676
+ $ref: './dsl_base/components.yaml#/schemas/agent-audit-result'
677
+ stderr:
678
+ format: text
647
679
  schemaNote: >-
648
- Full findings output. Exit 1 indicates at least one
649
- critical-severity finding exists; the findings themselves
650
- are valid structured data, not error messages.
680
+ Summary diagnostic on stderr. Structured findings are
681
+ emitted on stdout in the requested format.
651
682
 
652
- '2':
653
- description: Invalid input or configuration error.
683
+ '11':
684
+ description: >-
685
+ Runtime dependency missing (agent-contracts-runtime not installed).
654
686
  stderr:
655
687
  format: text
656
688
 
657
- '3':
658
- description: LLM adapter error (API failure, runtime not installed).
689
+ '12':
690
+ description: LLM provider or adapter error (API failure, auth error).
659
691
  stderr:
660
692
  format: text
661
693
 
662
694
  x-agent:
663
695
  riskLevel: medium
664
696
  requiresConfirmation: false
665
- idempotent: true
697
+ idempotent: false
698
+ idempotentNote: >-
699
+ Safe to repeat (no persistent side effects beyond network calls),
700
+ but LLM inference is non-deterministic — identical inputs may
701
+ produce different findings, severity assignments, and
702
+ recommendation text across runs.
666
703
  sideEffects:
667
704
  - network
668
705
  sideEffectNote: >-
@@ -671,7 +708,7 @@ commandSets:
671
708
  cost and sends DSL content to the LLM provider.
672
709
  safeDryRunOption: dry-run
673
710
  expectedDurationMs: 120000
674
- retryableExitCodes: [3]
711
+ retryableExitCodes: [12]
675
712
  recommendedBeforeUse:
676
713
  - Ensure agent-contracts.config.yaml exists with render targets.
677
714
  - Run validate first to confirm DSL is valid.
package/dist/cli.js CHANGED
@@ -48983,6 +48983,7 @@ var WorkflowDelegateStepSchema = z14.object({
48983
48983
  task: z14.string(),
48984
48984
  from_agent: z14.string(),
48985
48985
  group: z14.string().optional(),
48986
+ depends_on: z14.array(z14.string()).optional(),
48986
48987
  max_retries: z14.number().int().min(0).optional(),
48987
48988
  max_follow_ups: z14.number().int().min(0).optional(),
48988
48989
  retry: RetrySchema.optional()
@@ -48991,7 +48992,8 @@ var WorkflowGateStepSchema = z14.object({
48991
48992
  type: z14.literal("gate"),
48992
48993
  description: z14.string().optional(),
48993
48994
  gate_kind: z14.string(),
48994
- group: z14.string().optional()
48995
+ group: z14.string().optional(),
48996
+ depends_on: z14.array(z14.string()).optional()
48995
48997
  }).passthrough();
48996
48998
  var WorkflowHandoffStepSchema = z14.object({
48997
48999
  type: z14.literal("handoff"),
@@ -49000,13 +49002,15 @@ var WorkflowHandoffStepSchema = z14.object({
49000
49002
  task: z14.string().optional(),
49001
49003
  from_agent: z14.string().optional(),
49002
49004
  group: z14.string().optional(),
49005
+ depends_on: z14.array(z14.string()).optional(),
49003
49006
  retry: RetrySchema.optional()
49004
49007
  }).passthrough();
49005
49008
  var WorkflowValidationStepSchema = z14.object({
49006
49009
  type: z14.literal("validation"),
49007
49010
  description: z14.string().optional(),
49008
49011
  validation: z14.string(),
49009
- group: z14.string().optional()
49012
+ group: z14.string().optional(),
49013
+ depends_on: z14.array(z14.string()).optional()
49010
49014
  }).passthrough();
49011
49015
  var WorkflowDecisionStepSchema = z14.object({
49012
49016
  type: z14.literal("decision"),
@@ -49015,7 +49019,8 @@ var WorkflowDecisionStepSchema = z14.object({
49015
49019
  on: z14.string().optional(),
49016
49020
  routing_key: z14.string().optional(),
49017
49021
  branches: z14.record(z14.string(), z14.array(z14.string())),
49018
- group: z14.string().optional()
49022
+ group: z14.string().optional(),
49023
+ depends_on: z14.array(z14.string()).optional()
49019
49024
  }).passthrough();
49020
49025
  var WorkflowTeamTaskStepSchema = z14.object({
49021
49026
  type: z14.literal("team_task"),
@@ -49024,7 +49029,8 @@ var WorkflowTeamTaskStepSchema = z14.object({
49024
49029
  workflow: z14.string(),
49025
49030
  handoff: z14.string(),
49026
49031
  expects: z14.string(),
49027
- group: z14.string().optional()
49032
+ group: z14.string().optional(),
49033
+ depends_on: z14.array(z14.string()).optional()
49028
49034
  }).passthrough();
49029
49035
  var WorkflowStepSchema = z14.discriminatedUnion("type", [
49030
49036
  WorkflowDelegateStepSchema,
@@ -55892,6 +55898,7 @@ ${issues}
55892
55898
  );
55893
55899
 
55894
55900
  // src/cli/commands/audit.ts
55901
+ import { writeFileSync as writeFileSync2 } from "fs";
55895
55902
  import { Command as Command8 } from "commander";
55896
55903
 
55897
55904
  // src/auditor/context-builder.ts
@@ -56626,16 +56633,22 @@ function formatAuditResults(results, format2) {
56626
56633
  }
56627
56634
  return results.map((r3) => formatAuditResult(r3, format2)).join("\n\n");
56628
56635
  }
56629
- function computeExitCode(results) {
56636
+ function computeExitCode(results, failOn) {
56630
56637
  for (const r3 of results) {
56631
56638
  if (r3.status === "error") {
56632
- if (r3.errorMessage?.includes("not installed") || r3.errorMessage?.includes("adapter")) return 3;
56633
- return 2;
56639
+ if (r3.errorMessage?.includes("not installed")) return 11;
56640
+ if (r3.errorMessage?.includes("adapter") || r3.errorMessage?.includes("API")) return 12;
56641
+ return 1;
56634
56642
  }
56635
56643
  }
56644
+ const severityRank = { info: 0, warning: 1, error: 2, critical: 3 };
56645
+ const threshold = severityRank[failOn ?? "critical"] ?? 3;
56636
56646
  for (const r3 of results) {
56637
- if (r3.data?.critical_gaps && r3.data.critical_gaps.some((g2) => g2.severity === "critical")) {
56638
- return 1;
56647
+ if (r3.data?.critical_gaps) {
56648
+ for (const gap of r3.data.critical_gaps) {
56649
+ const rank = severityRank[gap.severity ?? ""] ?? 0;
56650
+ if (rank >= threshold) return 10;
56651
+ }
56639
56652
  }
56640
56653
  }
56641
56654
  return 0;
@@ -56653,13 +56666,13 @@ function parseAuditConfig(config) {
56653
56666
  cache_dir: audit.cache_dir
56654
56667
  };
56655
56668
  }
56656
- async function runAuditForDsl(dslPath, vars, configObj, auditType, opts) {
56669
+ async function runAuditForDsl(dslPath, vars, configObj, auditType, opts, failOn) {
56657
56670
  const resolved = await resolve4(dslPath);
56658
56671
  const data = vars ? substituteVars(resolved.data, vars) : resolved.data;
56659
56672
  const schemaResult = validateSchema(data);
56660
56673
  if (!schemaResult.success) {
56661
56674
  process.stderr.write("Schema validation failed. Run 'agent-contracts validate' first.\n");
56662
- return 2;
56675
+ return { exitCode: 1, output: "" };
56663
56676
  }
56664
56677
  const auditConfig = parseAuditConfig(
56665
56678
  configObj.audit
@@ -56672,17 +56685,18 @@ async function runAuditForDsl(dslPath, vars, configObj, auditType, opts) {
56672
56685
  scope: opts.scope,
56673
56686
  dryRun: opts.dryRun
56674
56687
  });
56688
+ let output2;
56675
56689
  if (opts.dryRun) {
56676
- for (const r3 of results) {
56677
- process.stdout.write(`
56690
+ output2 = results.map(
56691
+ (r3) => `
56678
56692
  --- Audit prompt: ${r3.auditType} ---
56679
- `);
56680
- process.stdout.write(r3.prompt + "\n");
56681
- }
56693
+ ${r3.prompt}
56694
+ `
56695
+ ).join("");
56682
56696
  } else {
56683
- process.stdout.write(formatAuditResults(results, opts.format) + "\n");
56697
+ output2 = formatAuditResults(results, opts.format) + "\n";
56684
56698
  }
56685
- return computeExitCode(results);
56699
+ return { exitCode: computeExitCode(results, failOn), output: output2 };
56686
56700
  }
56687
56701
  const result = await runAudit(schemaResult.data, configObj, auditConfig, {
56688
56702
  auditType,
@@ -56690,67 +56704,78 @@ async function runAuditForDsl(dslPath, vars, configObj, auditType, opts) {
56690
56704
  scope: opts.scope,
56691
56705
  dryRun: opts.dryRun
56692
56706
  });
56707
+ let output;
56693
56708
  if (opts.dryRun) {
56694
- process.stdout.write(result.prompt + "\n");
56709
+ output = result.prompt + "\n";
56695
56710
  } else {
56696
- process.stdout.write(formatAuditResult(result, opts.format) + "\n");
56711
+ output = formatAuditResult(result, opts.format) + "\n";
56697
56712
  }
56698
- return computeExitCode([result]);
56713
+ return { exitCode: computeExitCode([result], failOn), output };
56699
56714
  }
56700
- var auditCommand = new Command8("audit").description("Run LLM-based semantic audit on DSL definitions and generated outputs").argument("[type]", `Audit type: ${AUDIT_TYPES.join(", ")}`, "all").option("-c, --config <path>", "Path to agent-contracts.config.yaml").option("--team <id>", "Limit to one team (multi-team config only)").option("--format <format>", "Output format (text|json|markdown)", "text").option("--scope <entities>", "Limit audit scope (e.g. agents:architect,implementer)").option("--dry-run", "Output the audit prompt without calling LLM", false).option("--adapter <name>", "SDK adapter to use (default from config)").option("--model <name>", "LLM model override").action(
56715
+ var auditCommand = new Command8("audit").description("Run LLM-based semantic audit on DSL definitions and generated outputs").argument("[type]", `Audit type: ${AUDIT_TYPES.join(", ")}`, "all").option("-c, --config <path>", "Path to agent-contracts.config.yaml").option("--team <id>", "Limit to one team (multi-team config only)").option("--format <format>", "Output format (text|json|markdown)", "text").option("--scope <entities>", "Limit audit scope (e.g. agents:architect,implementer)").option("--dry-run", "Output the audit prompt without calling LLM", false).option("--adapter <name>", "SDK adapter to use (default from config)").option("--model <name>", "LLM model override").option("--fail-on <level>", "Minimum severity that causes exit 10 (info|warning|error|critical)", "critical").option("-o, --output <file>", "Write result to a file instead of stdout").option("--report-format <fmt>", "Alias for --format (for cli-contracts standard conformance)").action(
56701
56716
  async (type, opts) => {
56702
56717
  if (!AUDIT_TYPES.includes(type)) {
56703
56718
  process.stderr.write(
56704
56719
  `Unknown audit type: "${type}". Use one of: ${AUDIT_TYPES.join(", ")}
56705
56720
  `
56706
56721
  );
56707
- process.exit(2);
56722
+ process.exit(1);
56708
56723
  }
56709
- const format2 = opts.format;
56724
+ const format2 = opts.reportFormat ?? opts.format;
56710
56725
  try {
56711
56726
  const config = await loadConfig(opts.config);
56712
56727
  if (!config) {
56713
56728
  process.stderr.write(
56714
56729
  "Error: agent-contracts.config.yaml not found. Use --config to specify path.\n"
56715
56730
  );
56716
- process.exit(2);
56731
+ process.exit(1);
56717
56732
  }
56718
56733
  if (isMultiTeamConfig(config)) {
56719
56734
  const teamEntries = getTeamEntries(config, opts.team);
56720
56735
  let maxExit = 0;
56736
+ const allOutput = [];
56721
56737
  for (const [teamId, teamConfig] of teamEntries) {
56722
56738
  process.stderr.write(`
56723
56739
  --- Team: ${teamId} ---
56724
56740
  `);
56725
- const exitCode2 = await runAuditForDsl(
56741
+ const result2 = await runAuditForDsl(
56726
56742
  teamConfig.dsl,
56727
56743
  teamConfig.vars,
56728
56744
  config,
56729
56745
  type,
56730
- { format: format2, scope: opts.scope, dryRun: opts.dryRun, adapter: opts.adapter, model: opts.model }
56746
+ { format: format2, scope: opts.scope, dryRun: opts.dryRun, adapter: opts.adapter, model: opts.model },
56747
+ opts.failOn
56731
56748
  );
56732
- if (exitCode2 > maxExit) maxExit = exitCode2;
56749
+ process.stdout.write(result2.output);
56750
+ allOutput.push(result2.output);
56751
+ if (result2.exitCode > maxExit) maxExit = result2.exitCode;
56733
56752
  }
56753
+ if (opts.output) writeFileSync2(opts.output, allOutput.join(""), "utf8");
56734
56754
  process.exit(maxExit);
56735
56755
  }
56736
- const exitCode = await runAuditForDsl(
56756
+ const result = await runAuditForDsl(
56737
56757
  config.dsl,
56738
56758
  config.vars,
56739
56759
  config,
56740
56760
  type,
56741
- { format: format2, scope: opts.scope, dryRun: opts.dryRun, adapter: opts.adapter, model: opts.model }
56761
+ { format: format2, scope: opts.scope, dryRun: opts.dryRun, adapter: opts.adapter, model: opts.model },
56762
+ opts.failOn
56742
56763
  );
56743
- process.exit(exitCode);
56764
+ process.stdout.write(result.output);
56765
+ if (opts.output) writeFileSync2(opts.output, result.output, "utf8");
56766
+ process.exit(result.exitCode);
56744
56767
  } catch (err) {
56745
56768
  if (err instanceof ConfigLoadError) {
56746
56769
  process.stderr.write(`Config error: ${err.message}
56747
56770
  `);
56748
- process.exit(2);
56771
+ process.exit(1);
56749
56772
  }
56750
56773
  const msg = err instanceof Error ? err.message : String(err);
56751
56774
  process.stderr.write(`Error: ${msg}
56752
56775
  `);
56753
- process.exit(err instanceof Error && msg.includes("not installed") ? 3 : 2);
56776
+ if (msg.includes("not installed")) process.exit(11);
56777
+ if (msg.includes("adapter") || msg.includes("API")) process.exit(12);
56778
+ process.exit(1);
56754
56779
  }
56755
56780
  }
56756
56781
  );