agent-contracts 0.22.3 → 0.22.5

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/dist/cli.js CHANGED
@@ -49436,7 +49436,7 @@ function createProgram(handlers2, version) {
49436
49436
  }
49437
49437
  await handlers2.score(dir, opts, globalOpts);
49438
49438
  });
49439
- program2.command("audit").description("Run LLM-based semantic audit on DSL definitions and generated outputs.").argument("[type]", "Audit type to run: render (semantic diff of generated outputs vs DSL), dsl (design coherence review), prompt (generated prompt fidelity check), or all (run all three).").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").option("--scope <entities>", "Limit audit scope to specified entities (e.g. agents:architect,implementer).").option("--dry-run", "Output the audit prompt without calling LLM.", false).option("--adapter <name>", "SDK adapter to use for LLM calls (overrides config audit.adapter).").option("--model <name>", "LLM model override (overrides config audit.model).").option("--fail-on <level>", "Minimum finding 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. When both are specified, --report-format takes precedence.", "text").action(async (type, opts, cmd) => {
49439
+ program2.command("audit").description("Run LLM-based semantic audit on DSL definitions and generated outputs.").argument("[type]", "Audit type to run: render (semantic diff of generated outputs vs DSL), dsl (design coherence review), prompt (generated prompt fidelity check), extensions (unconsumed x-* property detection), or all (run all four).").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").option("--scope <entities>", "Limit audit scope to specified entities (e.g. agents:architect,implementer).").option("--dry-run", "Output the audit prompt without calling LLM.", false).option("--adapter <name>", "SDK adapter to use for LLM calls (overrides config audit.adapter).").option("--model <name>", "LLM model override (overrides config audit.model).").option("--fail-on <level>", "Minimum finding 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. When both are specified, --report-format takes precedence.", "text").action(async (type, opts, cmd) => {
49440
49440
  const globalOpts = cmd.optsWithGlobals();
49441
49441
  if (globalOpts.introspect) {
49442
49442
  const policy = deriveCommandPolicy("audit", opts);
@@ -49533,6 +49533,7 @@ var ArtifactSchema = z2.object({
49533
49533
  states: z2.array(z2.string()),
49534
49534
  required_validations: z2.array(z2.string()).default([]),
49535
49535
  visibility: z2.string().optional(),
49536
+ classification: z2.string().optional(),
49536
49537
  guardrails: z2.array(z2.string()).optional()
49537
49538
  }).passthrough();
49538
49539
 
@@ -49737,7 +49738,7 @@ var ExecutionStepSchema = z9.object({
49737
49738
  var TaskSchema = z9.object({
49738
49739
  description: z9.string(),
49739
49740
  target_agent: z9.string(),
49740
- allowed_from_agents: z9.array(z9.string()),
49741
+ allowed_from_agents: z9.array(z9.string()).default([]),
49741
49742
  workflow: z9.string(),
49742
49743
  input_artifacts: z9.array(z9.string()),
49743
49744
  invocation_handoff: z9.string(),
@@ -49810,7 +49811,8 @@ var ValidationSchema = z13.object({
49810
49811
  executor_type: z13.enum(["tool", "agent"]),
49811
49812
  executor: z13.string(),
49812
49813
  blocking: z13.boolean(),
49813
- produces_evidence: z13.string().optional()
49814
+ produces_evidence: z13.string().optional(),
49815
+ description: z13.string().optional()
49814
49816
  }).passthrough();
49815
49817
 
49816
49818
  // src/schema/workflow.ts
@@ -49955,13 +49957,13 @@ var DslSchema = z15.object({
49955
49957
  // src/schema/json-schema-utils.ts
49956
49958
  function resolveAllOf(schema) {
49957
49959
  const allOf = schema["allOf"];
49958
- if (!Array.isArray(allOf)) return schema;
49960
+ if (!Array.isArray(allOf)) return resolveNestedProperties(schema);
49959
49961
  let mergedProperties = {};
49960
49962
  let mergedRequired = [];
49961
49963
  const mergedTop = {};
49962
49964
  for (const sub of allOf) {
49963
49965
  if (typeof sub !== "object" || sub === null || Array.isArray(sub)) continue;
49964
- const subSchema = sub;
49966
+ const subSchema = resolveAllOf(sub);
49965
49967
  if (subSchema["properties"] && typeof subSchema["properties"] === "object") {
49966
49968
  mergedProperties = {
49967
49969
  ...mergedProperties,
@@ -49992,13 +49994,30 @@ function resolveAllOf(schema) {
49992
49994
  }
49993
49995
  const result = { ...mergedTop };
49994
49996
  if (Object.keys(mergedProperties).length > 0) {
49995
- result["properties"] = mergedProperties;
49997
+ result["properties"] = resolvePropertySchemas(mergedProperties);
49996
49998
  }
49997
49999
  if (mergedRequired.length > 0) {
49998
50000
  result["required"] = [...new Set(mergedRequired)];
49999
50001
  }
50000
50002
  return result;
50001
50003
  }
50004
+ function resolveNestedProperties(schema) {
50005
+ const props = schema["properties"];
50006
+ if (!props || typeof props !== "object") return schema;
50007
+ return { ...schema, properties: resolvePropertySchemas(props) };
50008
+ }
50009
+ function resolvePropertySchemas(properties) {
50010
+ const result = {};
50011
+ for (const [key, value] of Object.entries(properties)) {
50012
+ if (value && typeof value === "object" && !Array.isArray(value)) {
50013
+ const propSchema = value;
50014
+ result[key] = propSchema["allOf"] ? resolveAllOf(propSchema) : propSchema;
50015
+ } else {
50016
+ result[key] = value;
50017
+ }
50018
+ }
50019
+ return result;
50020
+ }
50002
50021
 
50003
50022
  // src/schema/merge-operators.ts
50004
50023
  import { z as z16 } from "zod";
@@ -50194,7 +50213,8 @@ ${issues}`,
50194
50213
  bindings: [],
50195
50214
  activeGuardrailPolicy: void 0,
50196
50215
  paths: void 0,
50197
- teams: resolveTeamConfigs(config.teams, configDir)
50216
+ teams: resolveTeamConfigs(config.teams, configDir),
50217
+ audit: config.audit ?? void 0
50198
50218
  };
50199
50219
  }
50200
50220
  return {
@@ -50204,7 +50224,8 @@ ${issues}`,
50204
50224
  configDir,
50205
50225
  bindings: (config.bindings ?? []).map((b3) => resolve(configDir, b3)),
50206
50226
  activeGuardrailPolicy: config.active_guardrail_policy,
50207
- paths: config.paths
50227
+ paths: config.paths,
50228
+ audit: config.audit ?? void 0
50208
50229
  };
50209
50230
  }
50210
50231
  function resolveDslPath(dirArg, dirArgDefault, config) {
@@ -50248,7 +50269,7 @@ function deepClone(value) {
50248
50269
  }
50249
50270
  return result;
50250
50271
  }
50251
- function resolveJsonPointer(root, pointer) {
50272
+ function tryResolveJsonPointer(root, pointer) {
50252
50273
  const path = pointer.slice(2);
50253
50274
  const segments = path.split("/").map(
50254
50275
  (s2) => s2.replace(/~1/g, "/").replace(/~0/g, "~")
@@ -50262,12 +50283,19 @@ function resolveJsonPointer(root, pointer) {
50262
50283
  }
50263
50284
  current = current[segment];
50264
50285
  if (current === void 0) {
50265
- throw new DslLoadError(
50266
- `Cannot resolve JSON Pointer "${pointer}": path segment "${segment}" not found`
50267
- );
50286
+ return { found: false };
50268
50287
  }
50269
50288
  }
50270
- return current;
50289
+ return { found: true, value: current };
50290
+ }
50291
+ function resolveJsonPointer(root, pointer) {
50292
+ const result = tryResolveJsonPointer(root, pointer);
50293
+ if (!result.found) {
50294
+ throw new DslLoadError(
50295
+ `Cannot resolve JSON Pointer "${pointer}": target not found`
50296
+ );
50297
+ }
50298
+ return result.value;
50271
50299
  }
50272
50300
  function hasRefs(value) {
50273
50301
  if (!("$refs" in value)) return false;
@@ -50311,7 +50339,7 @@ function deepMergeRefs(a3, b3, sourcePath) {
50311
50339
  }
50312
50340
  return result;
50313
50341
  }
50314
- async function loadRefsSource(refPath, baseDir, resolving, rootDoc) {
50342
+ async function loadRefsSource(refPath, baseDir, resolving) {
50315
50343
  const target = resolve2(baseDir, refPath);
50316
50344
  const s2 = await fsStat(target).catch(() => null);
50317
50345
  if (s2?.isDirectory()) {
@@ -50319,7 +50347,7 @@ async function loadRefsSource(refPath, baseDir, resolving, rootDoc) {
50319
50347
  throw new DslLoadError(`Circular $refs detected: ${target}`, target);
50320
50348
  }
50321
50349
  resolving.add(target);
50322
- const result = await loadDirectoryAsMap(target, resolving, rootDoc);
50350
+ const result = await loadDirectoryAsMap(target, resolving);
50323
50351
  resolving.delete(target);
50324
50352
  return result;
50325
50353
  }
@@ -50337,16 +50365,16 @@ async function loadRefsSource(refPath, baseDir, resolving, rootDoc) {
50337
50365
  target
50338
50366
  );
50339
50367
  }
50340
- const resolved = await resolveRefsDeep(
50368
+ const resolved = await assembleRefs(
50341
50369
  content,
50342
50370
  dirname2(target),
50343
50371
  resolving,
50344
- rootDoc
50372
+ content
50345
50373
  );
50346
50374
  resolving.delete(target);
50347
50375
  return resolved;
50348
50376
  }
50349
- async function loadDirectoryAsMap(dirPath, resolving, rootDoc) {
50377
+ async function loadDirectoryAsMap(dirPath, resolving) {
50350
50378
  let entries;
50351
50379
  try {
50352
50380
  entries = await readdir(dirPath);
@@ -50373,17 +50401,17 @@ async function loadDirectoryAsMap(dirPath, resolving, rootDoc) {
50373
50401
  filePath
50374
50402
  );
50375
50403
  }
50376
- const resolved = await resolveRefsDeep(
50404
+ const resolved = await assembleRefs(
50377
50405
  content,
50378
50406
  dirPath,
50379
50407
  resolving,
50380
- rootDoc
50408
+ content
50381
50409
  );
50382
50410
  merged = deepMergeRefs(merged, resolved, filePath);
50383
50411
  }
50384
50412
  return merged;
50385
50413
  }
50386
- async function processRefs(obj, baseDir, resolving, rootDoc) {
50414
+ async function processRefs(obj, baseDir, resolving) {
50387
50415
  const refPaths = obj["$refs"];
50388
50416
  const inline = {};
50389
50417
  for (const [key, value] of Object.entries(obj)) {
@@ -50393,17 +50421,17 @@ async function processRefs(obj, baseDir, resolving, rootDoc) {
50393
50421
  }
50394
50422
  let merged = {};
50395
50423
  for (const refPath of refPaths) {
50396
- const loaded = await loadRefsSource(refPath, baseDir, resolving, rootDoc);
50424
+ const loaded = await loadRefsSource(refPath, baseDir, resolving);
50397
50425
  merged = deepMergeRefs(merged, loaded, refPath);
50398
50426
  }
50399
50427
  merged = deepMergeRefs(merged, inline, "(inline)");
50400
50428
  return merged;
50401
50429
  }
50402
- async function resolveRefsDeep(data, baseDir, resolving, rootDoc) {
50430
+ async function assembleRefs(data, baseDir, resolving, fileRoot) {
50403
50431
  if (typeof data !== "object" || data === null) return data;
50404
50432
  if (Array.isArray(data)) {
50405
50433
  return Promise.all(
50406
- data.map((item) => resolveRefsDeep(item, baseDir, resolving, rootDoc))
50434
+ data.map((item) => assembleRefs(item, baseDir, resolving, fileRoot))
50407
50435
  );
50408
50436
  }
50409
50437
  if (isRef(data)) {
@@ -50412,13 +50440,16 @@ async function resolveRefsDeep(data, baseDir, resolving, rootDoc) {
50412
50440
  if (resolving.has(refValue)) {
50413
50441
  throw new DslLoadError(`Circular $ref detected: ${refValue}`);
50414
50442
  }
50443
+ const result2 = tryResolveJsonPointer(fileRoot, refValue);
50444
+ if (!result2.found) {
50445
+ return data;
50446
+ }
50415
50447
  resolving.add(refValue);
50416
- const target = resolveJsonPointer(rootDoc, refValue);
50417
- const resolved = await resolveRefsDeep(
50418
- deepClone(target),
50448
+ const resolved = await assembleRefs(
50449
+ deepClone(result2.value),
50419
50450
  baseDir,
50420
50451
  resolving,
50421
- rootDoc
50452
+ fileRoot
50422
50453
  );
50423
50454
  resolving.delete(refValue);
50424
50455
  return resolved;
@@ -50442,7 +50473,7 @@ async function resolveRefsDeep(data, baseDir, resolving, rootDoc) {
50442
50473
  );
50443
50474
  }
50444
50475
  resolving.add(refTarget);
50445
- const result2 = await loadDirectoryAsMap(refTarget, resolving, rootDoc);
50476
+ const result2 = await loadDirectoryAsMap(refTarget, resolving);
50446
50477
  resolving.delete(refTarget);
50447
50478
  return result2;
50448
50479
  }
@@ -50457,12 +50488,12 @@ async function resolveRefsDeep(data, baseDir, resolving, rootDoc) {
50457
50488
  }
50458
50489
  resolving.add(refTarget);
50459
50490
  const content = await readYaml(refTarget);
50460
- const fileRoot = fragment ? content : rootDoc;
50461
- let fileData = await resolveRefsDeep(
50491
+ const newFileRoot = isRecord(content) ? content : fileRoot;
50492
+ let fileData = await assembleRefs(
50462
50493
  content,
50463
50494
  dirname2(refTarget),
50464
50495
  resolving,
50465
- fileRoot
50496
+ newFileRoot
50466
50497
  );
50467
50498
  resolving.delete(refTarget);
50468
50499
  if (fragment && fragment.startsWith("#/")) {
@@ -50478,11 +50509,31 @@ async function resolveRefsDeep(data, baseDir, resolving, rootDoc) {
50478
50509
  }
50479
50510
  let obj = data;
50480
50511
  if (hasRefs(obj)) {
50481
- obj = await processRefs(obj, baseDir, resolving, rootDoc);
50512
+ obj = await processRefs(obj, baseDir, resolving);
50513
+ }
50514
+ const result = {};
50515
+ for (const [key, value] of Object.entries(obj)) {
50516
+ result[key] = await assembleRefs(value, baseDir, resolving, fileRoot);
50517
+ }
50518
+ return result;
50519
+ }
50520
+ function linkDocPointers(data, rootDoc) {
50521
+ if (typeof data !== "object" || data === null) return data;
50522
+ if (Array.isArray(data)) {
50523
+ return data.map((item) => linkDocPointers(item, rootDoc));
50482
50524
  }
50525
+ if (isRef(data)) {
50526
+ const refValue = data.$ref;
50527
+ if (refValue.startsWith("#/")) {
50528
+ const target = resolveJsonPointer(rootDoc, refValue);
50529
+ return linkDocPointers(deepClone(target), rootDoc);
50530
+ }
50531
+ return data;
50532
+ }
50533
+ const obj = data;
50483
50534
  const result = {};
50484
50535
  for (const [key, value] of Object.entries(obj)) {
50485
- result[key] = await resolveRefsDeep(value, baseDir, resolving, rootDoc);
50536
+ result[key] = linkDocPointers(value, rootDoc);
50486
50537
  }
50487
50538
  return result;
50488
50539
  }
@@ -50513,13 +50564,16 @@ async function loadDsl(entryPath) {
50513
50564
  const data = raw;
50514
50565
  checkVersion(data, absPath);
50515
50566
  const baseDir = dirname2(absPath);
50516
- const resolving = /* @__PURE__ */ new Set([absPath]);
50517
- const resolved = await resolveRefsDeep(
50567
+ const assembled = await assembleRefs(
50518
50568
  data,
50519
50569
  baseDir,
50520
- resolving,
50570
+ /* @__PURE__ */ new Set([absPath]),
50521
50571
  data
50522
50572
  );
50573
+ const resolved = linkDocPointers(
50574
+ assembled,
50575
+ assembled
50576
+ );
50523
50577
  return { data: resolved, filePath: absPath };
50524
50578
  }
50525
50579
 
@@ -52316,6 +52370,7 @@ var taskAgentBindingRule = {
52316
52370
  }
52317
52371
  for (const [taskId, task] of Object.entries(dsl.tasks)) {
52318
52372
  for (const fromAgentId of task.allowed_from_agents) {
52373
+ if (fromAgentId === task.target_agent) continue;
52319
52374
  const fromAgent = dsl.agents[fromAgentId];
52320
52375
  if (fromAgent && !fromAgent.can_invoke_agents.includes(task.target_agent)) {
52321
52376
  diagnostics.push({
@@ -52890,6 +52945,176 @@ var validationExecutorNoContextRule = {
52890
52945
  }
52891
52946
  };
52892
52947
 
52948
+ // src/linter/rules/extension-consumption.ts
52949
+ function isRecord5(v2) {
52950
+ return typeof v2 === "object" && v2 !== null && !Array.isArray(v2);
52951
+ }
52952
+ function collectXUsages(dsl) {
52953
+ const usages = /* @__PURE__ */ new Map();
52954
+ function record(key, path, nodeType) {
52955
+ let list = usages.get(key);
52956
+ if (!list) {
52957
+ list = [];
52958
+ usages.set(key, list);
52959
+ }
52960
+ list.push({ path, nodeType });
52961
+ }
52962
+ function walkObj(obj, path, nodeType) {
52963
+ for (const key of Object.keys(obj)) {
52964
+ if (key.startsWith("x-") && key !== "x-extensions" && key !== "x-extensions-strict") {
52965
+ record(key, path ? `${path}.${key}` : key, nodeType);
52966
+ }
52967
+ }
52968
+ }
52969
+ walkObj(dsl, "", "root");
52970
+ if (isRecord5(dsl.system)) {
52971
+ walkObj(dsl.system, "system", "system");
52972
+ }
52973
+ for (const [id, agent] of Object.entries(dsl.agents)) {
52974
+ const agentObj = agent;
52975
+ walkObj(agentObj, `agents.${id}`, "agent");
52976
+ if (Array.isArray(agentObj["rules"])) {
52977
+ for (let i3 = 0; i3 < agentObj["rules"].length; i3++) {
52978
+ const r3 = agentObj["rules"][i3];
52979
+ if (isRecord5(r3)) walkObj(r3, `agents.${id}.rules[${i3}]`, "rule");
52980
+ }
52981
+ }
52982
+ if (Array.isArray(agentObj["escalation_criteria"])) {
52983
+ for (let i3 = 0; i3 < agentObj["escalation_criteria"].length; i3++) {
52984
+ const e3 = agentObj["escalation_criteria"][i3];
52985
+ if (isRecord5(e3)) walkObj(e3, `agents.${id}.escalation_criteria[${i3}]`, "escalation_criterion");
52986
+ }
52987
+ }
52988
+ if (Array.isArray(agentObj["prerequisites"])) {
52989
+ for (let i3 = 0; i3 < agentObj["prerequisites"].length; i3++) {
52990
+ const p2 = agentObj["prerequisites"][i3];
52991
+ if (isRecord5(p2)) walkObj(p2, `agents.${id}.prerequisites[${i3}]`, "prerequisite");
52992
+ }
52993
+ }
52994
+ }
52995
+ for (const [id, task] of Object.entries(dsl.tasks)) {
52996
+ const taskObj = task;
52997
+ walkObj(taskObj, `tasks.${id}`, "task");
52998
+ if (Array.isArray(taskObj["execution_steps"])) {
52999
+ for (let i3 = 0; i3 < taskObj["execution_steps"].length; i3++) {
53000
+ const s2 = taskObj["execution_steps"][i3];
53001
+ if (isRecord5(s2)) walkObj(s2, `tasks.${id}.execution_steps[${i3}]`, "execution_step");
53002
+ }
53003
+ }
53004
+ }
53005
+ for (const [id, art] of Object.entries(dsl.artifacts)) {
53006
+ walkObj(art, `artifacts.${id}`, "artifact");
53007
+ }
53008
+ for (const [id, tool] of Object.entries(dsl.tools)) {
53009
+ const toolObj = tool;
53010
+ walkObj(toolObj, `tools.${id}`, "tool");
53011
+ if (Array.isArray(toolObj["commands"])) {
53012
+ for (let i3 = 0; i3 < toolObj["commands"].length; i3++) {
53013
+ const c2 = toolObj["commands"][i3];
53014
+ if (isRecord5(c2)) walkObj(c2, `tools.${id}.commands[${i3}]`, "tool_command");
53015
+ }
53016
+ }
53017
+ }
53018
+ for (const [id, val] of Object.entries(dsl.validations)) {
53019
+ walkObj(val, `validations.${id}`, "validation");
53020
+ }
53021
+ for (const [id, ht] of Object.entries(dsl.handoff_types)) {
53022
+ walkObj(ht, `handoff_types.${id}`, "handoff_type");
53023
+ }
53024
+ for (const [id, wf] of Object.entries(dsl.workflow)) {
53025
+ const wfObj = wf;
53026
+ walkObj(wfObj, `workflow.${id}`, "workflow");
53027
+ if (Array.isArray(wfObj["steps"])) {
53028
+ for (let i3 = 0; i3 < wfObj["steps"].length; i3++) {
53029
+ const s2 = wfObj["steps"][i3];
53030
+ if (isRecord5(s2)) walkObj(s2, `workflow.${id}.steps[${i3}]`, "workflow_step");
53031
+ }
53032
+ }
53033
+ }
53034
+ for (const [id, pol] of Object.entries(dsl.policies)) {
53035
+ walkObj(pol, `policies.${id}`, "policy");
53036
+ }
53037
+ for (const [id, gr] of Object.entries(dsl.guardrails)) {
53038
+ walkObj(gr, `guardrails.${id}`, "guardrail");
53039
+ }
53040
+ for (const [id, gp] of Object.entries(dsl.guardrail_policies)) {
53041
+ walkObj(gp, `guardrail_policies.${id}`, "guardrail_policy");
53042
+ }
53043
+ return usages;
53044
+ }
53045
+ var extensionDeclaredButUnusedRule = {
53046
+ id: "extension-declared-unused",
53047
+ description: "Declared extension in `extensions` is never used on any entity",
53048
+ run(dsl) {
53049
+ const diagnostics = [];
53050
+ const declaredKeys = Object.keys(dsl.extensions);
53051
+ if (declaredKeys.length === 0) return diagnostics;
53052
+ const usages = collectXUsages(dsl);
53053
+ for (const key of declaredKeys) {
53054
+ if (!usages.has(key)) {
53055
+ diagnostics.push({
53056
+ ruleId: "extension-declared-unused",
53057
+ severity: "warning",
53058
+ path: `extensions.${key}`,
53059
+ message: `Extension "${key}" is declared but never used on any entity`
53060
+ });
53061
+ }
53062
+ }
53063
+ return diagnostics;
53064
+ }
53065
+ };
53066
+ var extensionScopeMismatchRule = {
53067
+ id: "extension-scope-mismatch",
53068
+ description: "Extension used on a node type outside its declared scope",
53069
+ run(dsl) {
53070
+ const diagnostics = [];
53071
+ const extensions = dsl.extensions;
53072
+ if (Object.keys(extensions).length === 0) return diagnostics;
53073
+ const usages = collectXUsages(dsl);
53074
+ for (const [key, decl] of Object.entries(extensions)) {
53075
+ const scope = decl.scope;
53076
+ if (!scope || scope.length === 0) continue;
53077
+ const scopeSet = new Set(scope);
53078
+ const keyUsages = usages.get(key);
53079
+ if (!keyUsages) continue;
53080
+ for (const usage of keyUsages) {
53081
+ if (!scopeSet.has(usage.nodeType)) {
53082
+ diagnostics.push({
53083
+ ruleId: "extension-scope-mismatch",
53084
+ severity: "warning",
53085
+ path: usage.path,
53086
+ message: `Extension "${key}" is used on ${usage.nodeType} but declared scope is [${scope.join(", ")}]`
53087
+ });
53088
+ }
53089
+ }
53090
+ }
53091
+ return diagnostics;
53092
+ }
53093
+ };
53094
+ var extensionUndeclaredUsageRule = {
53095
+ id: "extension-undeclared-usage",
53096
+ description: "Entity uses x-* property that is not declared in extensions (when extensions section exists)",
53097
+ run(dsl) {
53098
+ const diagnostics = [];
53099
+ const extensions = dsl.extensions;
53100
+ if (Object.keys(extensions).length === 0) return diagnostics;
53101
+ const declaredKeys = new Set(Object.keys(extensions));
53102
+ const usages = collectXUsages(dsl);
53103
+ for (const [key, keyUsages] of usages) {
53104
+ if (declaredKeys.has(key)) continue;
53105
+ for (const usage of keyUsages) {
53106
+ diagnostics.push({
53107
+ ruleId: "extension-undeclared-usage",
53108
+ severity: "info",
53109
+ path: usage.path,
53110
+ message: `Extension "${key}" is used but not declared in extensions \u2014 consider adding a declaration`
53111
+ });
53112
+ }
53113
+ }
53114
+ return diagnostics;
53115
+ }
53116
+ };
53117
+
52893
53118
  // src/linter/linter.ts
52894
53119
  var builtinRules = [
52895
53120
  validationCoverageRule,
@@ -52906,7 +53131,10 @@ var builtinRules = [
52906
53131
  entityGuardrailUndefinedRule,
52907
53132
  entityNoGuardrailsRule,
52908
53133
  guardrailOrphanedRule,
52909
- validationExecutorNoContextRule
53134
+ validationExecutorNoContextRule,
53135
+ extensionDeclaredButUnusedRule,
53136
+ extensionScopeMismatchRule,
53137
+ extensionUndeclaredUsageRule
52910
53138
  ];
52911
53139
  function lint(dsl, rules = builtinRules) {
52912
53140
  const diagnostics = [];
@@ -55839,6 +56067,121 @@ function formatDslOverview(dsl) {
55839
56067
  `- Validations: ${Object.keys(dsl.validations).join(", ")}`
55840
56068
  ].join("\n");
55841
56069
  }
56070
+ function isRecord6(v2) {
56071
+ return typeof v2 === "object" && v2 !== null && !Array.isArray(v2);
56072
+ }
56073
+ function collectAllXUsages(dsl) {
56074
+ const entries = [];
56075
+ function walk2(obj, path, nodeType) {
56076
+ for (const key of Object.keys(obj)) {
56077
+ if (key.startsWith("x-") && key !== "x-extensions" && key !== "x-extensions-strict") {
56078
+ entries.push({ path: path ? `${path}.${key}` : key, nodeType, key });
56079
+ }
56080
+ }
56081
+ }
56082
+ walk2(dsl, "", "root");
56083
+ if (isRecord6(dsl.system)) walk2(dsl.system, "system", "system");
56084
+ for (const [id, a3] of Object.entries(dsl.agents))
56085
+ walk2(a3, `agents.${id}`, "agent");
56086
+ for (const [id, t3] of Object.entries(dsl.tasks))
56087
+ walk2(t3, `tasks.${id}`, "task");
56088
+ for (const [id, a3] of Object.entries(dsl.artifacts))
56089
+ walk2(a3, `artifacts.${id}`, "artifact");
56090
+ for (const [id, t3] of Object.entries(dsl.tools))
56091
+ walk2(t3, `tools.${id}`, "tool");
56092
+ for (const [id, v2] of Object.entries(dsl.validations))
56093
+ walk2(v2, `validations.${id}`, "validation");
56094
+ for (const [id, h2] of Object.entries(dsl.handoff_types))
56095
+ walk2(h2, `handoff_types.${id}`, "handoff_type");
56096
+ for (const [id, w2] of Object.entries(dsl.workflow))
56097
+ walk2(w2, `workflow.${id}`, "workflow");
56098
+ for (const [id, p2] of Object.entries(dsl.policies))
56099
+ walk2(p2, `policies.${id}`, "policy");
56100
+ for (const [id, g2] of Object.entries(dsl.guardrails))
56101
+ walk2(g2, `guardrails.${id}`, "guardrail");
56102
+ for (const [id, gp] of Object.entries(dsl.guardrail_policies))
56103
+ walk2(gp, `guardrail_policies.${id}`, "guardrail_policy");
56104
+ return entries;
56105
+ }
56106
+ function extractTemplateXReferences(config) {
56107
+ const refs = [];
56108
+ try {
56109
+ const { readFileSync: readFileSync3 } = __require("fs");
56110
+ for (const target of config.renders) {
56111
+ try {
56112
+ const content = readFileSync3(target.template, "utf8");
56113
+ const matches = content.matchAll(/\{\{[^}]*?(x-[\w-]+)[^}]*?\}\}/g);
56114
+ for (const m2 of matches) refs.push(m2[1]);
56115
+ } catch {
56116
+ }
56117
+ }
56118
+ } catch {
56119
+ }
56120
+ return [...new Set(refs)];
56121
+ }
56122
+ function buildExtensionsContext(dsl, config) {
56123
+ const parts = [];
56124
+ parts.push("## Extension Declarations");
56125
+ const declaredKeys = Object.keys(dsl.extensions);
56126
+ if (declaredKeys.length === 0) {
56127
+ parts.push("(No extensions declared in `extensions` section)");
56128
+ } else {
56129
+ parts.push("```yaml\n" + yaml.stringify({ extensions: dsl.extensions }) + "```");
56130
+ }
56131
+ parts.push("## x-* Usage Map");
56132
+ const usages = collectAllXUsages(dsl);
56133
+ if (usages.length === 0) {
56134
+ parts.push("(No x-* properties found on any entity)");
56135
+ } else {
56136
+ const byKey = /* @__PURE__ */ new Map();
56137
+ for (const u2 of usages) {
56138
+ let list = byKey.get(u2.key);
56139
+ if (!list) {
56140
+ list = [];
56141
+ byKey.set(u2.key, list);
56142
+ }
56143
+ list.push(u2);
56144
+ }
56145
+ const lines = ["| Extension | Node Type | Path |", "|-----------|-----------|------|"];
56146
+ for (const [key, entries] of byKey) {
56147
+ for (const e3 of entries) {
56148
+ lines.push(`| ${key} | ${e3.nodeType} | ${e3.path} |`);
56149
+ }
56150
+ }
56151
+ parts.push(lines.join("\n"));
56152
+ }
56153
+ parts.push("## Template x-* References");
56154
+ const templateRefs = extractTemplateXReferences(config);
56155
+ if (templateRefs.length === 0) {
56156
+ parts.push("(No x-* references found in render templates, or no templates configured)");
56157
+ } else {
56158
+ parts.push(templateRefs.map((r3) => `- ${r3}`).join("\n"));
56159
+ }
56160
+ parts.push("## Runtime Codegen Fixed Fields");
56161
+ parts.push(
56162
+ "The agent-contracts-runtime codegen templates emit only these fixed fields (x-* properties are not included in generated TypeScript contracts):\n- **AgentContract**: id, role_name, purpose, mode, dispatch_only, can_read_artifacts, can_write_artifacts, can_execute_tools, can_invoke_agents, can_return_handoffs, responsibilities, constraints, rules, escalation_criteria\n- **TaskContract**: id, description, target_agent, allowed_from_agents, workflow, invocation_handoff, result_handoff, input_artifacts, responsibilities, completion_criteria, optional\n- **WorkflowContract**: id, description, trigger, entry_conditions, steps"
56163
+ );
56164
+ const declaredSet = new Set(declaredKeys);
56165
+ const usedKeys = new Set(usages.map((u2) => u2.key));
56166
+ const templateRefSet = new Set(templateRefs);
56167
+ parts.push("## Gap Summary");
56168
+ const gaps = [];
56169
+ for (const key of declaredKeys) {
56170
+ if (!usedKeys.has(key)) gaps.push(`- **${key}**: declared but never populated on any entity`);
56171
+ }
56172
+ for (const key of usedKeys) {
56173
+ if (declaredKeys.length > 0 && !declaredSet.has(key))
56174
+ gaps.push(`- **${key}**: used on entities but not declared in extensions`);
56175
+ if (!templateRefSet.has(key))
56176
+ gaps.push(`- **${key}**: populated on entities but not referenced in any render template`);
56177
+ }
56178
+ if (gaps.length === 0) {
56179
+ parts.push("(No obvious gaps detected by static analysis)");
56180
+ } else {
56181
+ parts.push(gaps.join("\n"));
56182
+ }
56183
+ return parts.join("\n\n");
56184
+ }
55842
56185
  async function buildAuditContext(auditType, dsl, config) {
55843
56186
  const sections = [];
55844
56187
  sections.push(formatDslOverview(dsl));
@@ -55859,6 +56202,9 @@ ${entry.content}`);
55859
56202
  sections.push("(No rendered agent prompt files found. Run `agent-contracts render` first.)");
55860
56203
  }
55861
56204
  }
56205
+ if (auditType === "extensions") {
56206
+ sections.push(buildExtensionsContext(dsl, config));
56207
+ }
55862
56208
  if (auditType === "dsl") {
55863
56209
  sections.push("## Agent Definitions");
55864
56210
  for (const [agentId, agentDef] of Object.entries(dsl.agents)) {
@@ -55925,7 +56271,8 @@ var dslAuditor = {
55925
56271
  "Prioritize improvement recommendations (P0/P1/P2) with concrete fix proposals",
55926
56272
  "Report score-based improvement areas as audit recommendations (read-only; consumes dsl-score-report produced by dsl-designer)",
55927
56273
  "Review DSL design for semantic coherence (role overlap, scope breadth, gate placement)",
55928
- "Verify generated prompts faithfully represent DSL intent (no hallucinated permissions)"
56274
+ "Verify generated prompts faithfully represent DSL intent (no hallucinated permissions)",
56275
+ "Audit x-* extension consumption across render templates and runtime codegen paths"
55929
56276
  ],
55930
56277
  constraints: [
55931
56278
  "Do not directly modify DSL definitions (read-only analysis)",
@@ -56186,12 +56533,14 @@ var auditSemanticDesign = {
56186
56533
  "Verify handoff schemas carry sufficient fields for task completion_criteria",
56187
56534
  "Check workflow gates are placed effectively",
56188
56535
  "Detect guardrails declared but absent from execution path",
56189
- "Check semantic validations are distributed across phases"
56536
+ "Check semantic validations are distributed across phases",
56537
+ "Detect custom x- properties that replicate standard DSL control-flow features (gate, decision, entry_conditions)"
56190
56538
  ],
56191
56539
  completion_criteria: [
56192
56540
  "All agents reviewed for scope and overlap",
56193
56541
  "Workflow gate placement analyzed",
56194
56542
  "Guardrail enforcement path verified",
56543
+ "Custom x- property misuse flagged",
56195
56544
  "Findings classified with severity and category"
56196
56545
  ],
56197
56546
  optional: false
@@ -56224,6 +56573,35 @@ var auditGeneratedPrompts = {
56224
56573
  ],
56225
56574
  optional: false
56226
56575
  };
56576
+ var auditExtensionConsumption = {
56577
+ id: "audit-extension-consumption",
56578
+ description: "Audit declared x-* extensions for consumption gaps across render and runtime paths",
56579
+ target_agent: "dsl-auditor",
56580
+ allowed_from_agents: [
56581
+ "dsl-auditor"
56582
+ ],
56583
+ workflow: "dsl-audit",
56584
+ invocation_handoff: "dsl-task-request",
56585
+ result_handoff: "dsl-audit-result",
56586
+ input_artifacts: [
56587
+ "dsl-source",
56588
+ "dsl-generated-output"
56589
+ ],
56590
+ responsibilities: [
56591
+ "Cross-check extensions declarations against entity x-* usage",
56592
+ "Identify x-* properties populated in DSL but not consumed by any render template",
56593
+ "Detect semantic overlap between x-* extensions and standard DSL features",
56594
+ "Report runtime codegen reachability for each extension",
56595
+ "Distinguish intentional metadata-only extensions from consumption gaps"
56596
+ ],
56597
+ completion_criteria: [
56598
+ "All declared extensions checked for template and runtime consumption",
56599
+ "Unused extensions flagged with suggested action (remove, migrate to standard, or add template support)",
56600
+ "Runtime-unreachable extensions documented with explanation",
56601
+ "Findings classified with severity and category"
56602
+ ],
56603
+ optional: false
56604
+ };
56227
56605
  var taskRegistry = {
56228
56606
  "update-dsl-definitions": updateDslDefinitions,
56229
56607
  "update-dsl-binding": updateDslBinding,
@@ -56231,7 +56609,8 @@ var taskRegistry = {
56231
56609
  "check-dsl-score": checkDslScore,
56232
56610
  "audit-dsl-completeness": auditDslCompleteness,
56233
56611
  "audit-semantic-design": auditSemanticDesign,
56234
- "audit-generated-prompts": auditGeneratedPrompts
56612
+ "audit-generated-prompts": auditGeneratedPrompts,
56613
+ "audit-extension-consumption": auditExtensionConsumption
56235
56614
  };
56236
56615
 
56237
56616
  // src/generated/dsl-base/handoffs.ts
@@ -56253,8 +56632,44 @@ var DslTaskResultSchema = z20.object({
56253
56632
  score_dimensions: z20.record(z20.string(), z20.unknown()).optional(),
56254
56633
  notes: z20.string().optional()
56255
56634
  });
56635
+ var AuditResultSchema = z20.object({
56636
+ summary: z20.string(),
56637
+ riskLevel: z20.enum(["low", "medium", "high", "critical"]),
56638
+ findings: z20.array(z20.object({
56639
+ id: z20.string().optional(),
56640
+ severity: z20.enum(["info", "warning", "error", "critical"]),
56641
+ category: z20.string(),
56642
+ target: z20.string().optional(),
56643
+ location: z20.string().optional(),
56644
+ message: z20.string(),
56645
+ recommendation: z20.string().optional(),
56646
+ confidence: z20.number().optional(),
56647
+ evidence: z20.array(z20.object({
56648
+ kind: z20.enum(["file", "command", "schema", "diff", "stdout", "stderr", "text"]),
56649
+ target: z20.string().optional(),
56650
+ location: z20.string().optional(),
56651
+ excerpt: z20.string().optional()
56652
+ })).optional(),
56653
+ details: z20.record(z20.string(), z20.unknown()).optional()
56654
+ })),
56655
+ recommendedActions: z20.array(z20.object({
56656
+ kind: z20.enum(["run_command", "edit_file", "review", "confirm", "block", "ignore"]),
56657
+ title: z20.string(),
56658
+ command: z20.string().optional(),
56659
+ target: z20.string().optional(),
56660
+ rationale: z20.string().optional()
56661
+ })).optional(),
56662
+ metadata: z20.object({
56663
+ tool: z20.string().optional(),
56664
+ command: z20.string().optional(),
56665
+ version: z20.string().optional(),
56666
+ generatedAt: z20.string().optional(),
56667
+ adapter: z20.string().optional(),
56668
+ model: z20.string().optional()
56669
+ }).optional()
56670
+ });
56256
56671
  var DslAuditResultSchema = z20.object({
56257
- audit_type: z20.enum(["completeness", "semantic", "prompt"]),
56672
+ audit_type: z20.enum(["completeness", "semantic", "prompt", "extensions"]),
56258
56673
  total_dimensions: z20.number(),
56259
56674
  pass_count: z20.number(),
56260
56675
  miss_count: z20.number(),
@@ -56278,6 +56693,7 @@ var DslAuditResultSchema = z20.object({
56278
56693
  var handoffSchemas = {
56279
56694
  "dsl-task-request": DslTaskRequestSchema,
56280
56695
  "dsl-task-result": DslTaskResultSchema,
56696
+ "audit-result": AuditResultSchema,
56281
56697
  "dsl-audit-result": DslAuditResultSchema
56282
56698
  };
56283
56699
 
@@ -56330,7 +56746,8 @@ async function createAdapter(runtimePkg, name, config) {
56330
56746
  var AUDIT_TYPE_TO_TASK = {
56331
56747
  render: "audit-dsl-completeness",
56332
56748
  dsl: "audit-semantic-design",
56333
- prompt: "audit-generated-prompts"
56749
+ prompt: "audit-generated-prompts",
56750
+ extensions: "audit-extension-consumption"
56334
56751
  };
56335
56752
  async function runAudit(dsl, config, auditConfig, options) {
56336
56753
  const taskId = AUDIT_TYPE_TO_TASK[options.auditType];
@@ -56384,7 +56801,7 @@ async function runAudit(dsl, config, auditConfig, options) {
56384
56801
  };
56385
56802
  }
56386
56803
  async function runAllAudits(dsl, config, auditConfig, options) {
56387
- const types = ["render", "dsl", "prompt"];
56804
+ const types = ["render", "dsl", "prompt", "extensions"];
56388
56805
  const results = [];
56389
56806
  for (const auditType of types) {
56390
56807
  results.push(await runAudit(dsl, config, auditConfig, { ...options, auditType }));
@@ -57092,7 +57509,7 @@ ${issues}
57092
57509
  process.exit(1);
57093
57510
  }
57094
57511
  };
57095
- var AUDIT_TYPES = ["render", "dsl", "prompt", "all"];
57512
+ var AUDIT_TYPES = ["render", "dsl", "prompt", "extensions", "all"];
57096
57513
  function parseAuditConfig(configObj) {
57097
57514
  if (!configObj) return {};
57098
57515
  const audit = configObj;