agent-inspect 6.19.0 → 6.20.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.
Files changed (57) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +3 -1
  3. package/SECURITY.md +2 -0
  4. package/docs/ARCHITECTURE.md +28 -16
  5. package/docs/KNOWN-ISSUES.md +3 -3
  6. package/docs/LIMITATIONS.md +5 -4
  7. package/docs/PERFORMANCE.md +1 -1
  8. package/docs/SUPPORT-LEVELS.md +2 -1
  9. package/docs/TRACE-CONTRACTS.md +64 -52
  10. package/package.json +3 -2
  11. package/packages/cli/dist/{chunk-R7Y5SGH5.mjs → chunk-QPRJUVLH.mjs} +57 -17
  12. package/packages/cli/dist/chunk-QPRJUVLH.mjs.map +1 -0
  13. package/packages/cli/dist/index.cjs +69 -18
  14. package/packages/cli/dist/index.cjs.map +1 -1
  15. package/packages/cli/dist/index.mjs +9 -6
  16. package/packages/cli/dist/index.mjs.map +1 -1
  17. package/packages/cli/dist/{src-T4EZERCX.mjs → src-6W5ZR3I7.mjs} +3 -3
  18. package/packages/cli/dist/{src-T4EZERCX.mjs.map → src-6W5ZR3I7.mjs.map} +1 -1
  19. package/packages/core/dist/advanced.cjs +118 -18
  20. package/packages/core/dist/advanced.cjs.map +1 -1
  21. package/packages/core/dist/advanced.d.cts +2 -2
  22. package/packages/core/dist/advanced.d.ts +2 -2
  23. package/packages/core/dist/advanced.mjs +8 -8
  24. package/packages/core/dist/checks.cjs +443 -9
  25. package/packages/core/dist/checks.cjs.map +1 -1
  26. package/packages/core/dist/checks.d.cts +78 -16
  27. package/packages/core/dist/checks.d.ts +78 -16
  28. package/packages/core/dist/checks.mjs +3 -3
  29. package/packages/core/dist/{chunk-VFO76UH3.mjs → chunk-3XS5O4JI.mjs} +3 -3
  30. package/packages/core/dist/{chunk-VFO76UH3.mjs.map → chunk-3XS5O4JI.mjs.map} +1 -1
  31. package/packages/core/dist/{chunk-ZWA5RWMV.mjs → chunk-5YFJJFXU.mjs} +4 -4
  32. package/packages/core/dist/{chunk-ZWA5RWMV.mjs.map → chunk-5YFJJFXU.mjs.map} +1 -1
  33. package/packages/core/dist/{chunk-NABMW5DX.mjs → chunk-G47KQB42.mjs} +445 -13
  34. package/packages/core/dist/chunk-G47KQB42.mjs.map +1 -0
  35. package/packages/core/dist/{chunk-SGL4JGCD.mjs → chunk-JBNRJLIV.mjs} +58 -18
  36. package/packages/core/dist/chunk-JBNRJLIV.mjs.map +1 -0
  37. package/packages/core/dist/{chunk-ULCGIRTC.mjs → chunk-U25RHRSU.mjs} +65 -5
  38. package/packages/core/dist/chunk-U25RHRSU.mjs.map +1 -0
  39. package/packages/core/dist/{context-Ca5yK8Cs.d.cts → context-C5ye_xcS.d.cts} +0 -4
  40. package/packages/core/dist/{context-D6cS4XIL.d.ts → context-CNsNSgE2.d.ts} +0 -4
  41. package/packages/core/dist/exporters.cjs +55 -15
  42. package/packages/core/dist/exporters.cjs.map +1 -1
  43. package/packages/core/dist/exporters.mjs +3 -3
  44. package/packages/core/dist/{index-DKwuGSe-.d.ts → index-Bg9ZdsdS.d.ts} +10 -0
  45. package/packages/core/dist/{index-DCR816Yt.d.cts → index-rVpX5PPh.d.cts} +10 -0
  46. package/packages/core/dist/index.cjs +63 -3
  47. package/packages/core/dist/index.cjs.map +1 -1
  48. package/packages/core/dist/index.d.cts +1 -1
  49. package/packages/core/dist/index.d.ts +1 -1
  50. package/packages/core/dist/index.mjs +4 -4
  51. package/packages/core/dist/writers.cjs +33 -2
  52. package/packages/core/dist/writers.cjs.map +1 -1
  53. package/packages/core/dist/writers.mjs +1 -1
  54. package/packages/cli/dist/chunk-R7Y5SGH5.mjs.map +0 -1
  55. package/packages/core/dist/chunk-NABMW5DX.mjs.map +0 -1
  56. package/packages/core/dist/chunk-SGL4JGCD.mjs.map +0 -1
  57. package/packages/core/dist/chunk-ULCGIRTC.mjs.map +0 -1
@@ -1,4 +1,4 @@
1
- import { extractOutcomesFromPersistedEvents, outcomesMatchingStatus } from './chunk-VFO76UH3.mjs';
1
+ import { extractOutcomesFromPersistedEvents, outcomesMatchingStatus } from './chunk-3XS5O4JI.mjs';
2
2
  import { formatProgrammaticDiagnostic } from './chunk-FWK3Q3OJ.mjs';
3
3
 
4
4
  // packages/core/src/checks/logical-events.ts
@@ -1676,6 +1676,7 @@ function createToolUsageRule(options) {
1676
1676
  }
1677
1677
  function createToolOrderingRule(options) {
1678
1678
  const ruleId = options.id ?? "tool.order";
1679
+ const mode = options.mode ?? "first-occurrence";
1679
1680
  return {
1680
1681
  id: ruleId,
1681
1682
  category: "tool",
@@ -1688,7 +1689,7 @@ function createToolOrderingRule(options) {
1688
1689
  if (beforeIndex === void 0 || afterIndex === void 0) {
1689
1690
  return [];
1690
1691
  }
1691
- if (beforeIndex >= afterIndex) {
1692
+ if (mode === "first-occurrence" && beforeIndex >= afterIndex) {
1692
1693
  return [
1693
1694
  failFinding(
1694
1695
  ruleId,
@@ -1703,6 +1704,130 @@ function createToolOrderingRule(options) {
1703
1704
  const afterEvent = tools[afterIndex];
1704
1705
  const beforeEnd = eventEndMs(beforeEvent);
1705
1706
  const afterStart = eventStartMs(afterEvent);
1707
+ if (mode === "happens-before") {
1708
+ const expected = {
1709
+ before: options.before,
1710
+ after: options.after,
1711
+ mode,
1712
+ relation: "before.end <= after.start"
1713
+ };
1714
+ if (options.before === options.after) {
1715
+ return [
1716
+ failFinding(
1717
+ ruleId,
1718
+ `Tool ${options.before} cannot causally happen before itself.`,
1719
+ [eventEvidence(beforeEvent)],
1720
+ expected,
1721
+ { code: "tool.order.same-tool" }
1722
+ )
1723
+ ];
1724
+ }
1725
+ if (beforeEnd === void 0 || afterStart === void 0) {
1726
+ return [
1727
+ failFinding(
1728
+ ruleId,
1729
+ `Tool order ${options.before} before ${options.after} could not establish causal timing.`,
1730
+ [eventEvidence(beforeEvent), eventEvidence(afterEvent)],
1731
+ expected,
1732
+ {
1733
+ code: "tool.order.interval-unresolved",
1734
+ beforeEndResolved: beforeEnd !== void 0,
1735
+ afterStartResolved: afterStart !== void 0
1736
+ }
1737
+ )
1738
+ ];
1739
+ }
1740
+ if (beforeEnd > afterStart) {
1741
+ return [
1742
+ failFinding(
1743
+ ruleId,
1744
+ `Tool ${options.before} must finish before ${options.after} starts.`,
1745
+ [eventEvidence(beforeEvent), eventEvidence(afterEvent)],
1746
+ expected,
1747
+ {
1748
+ beforeEndedAt: new Date(beforeEnd).toISOString(),
1749
+ afterStartedAt: new Date(afterStart).toISOString()
1750
+ }
1751
+ )
1752
+ ];
1753
+ }
1754
+ return [];
1755
+ }
1756
+ if (mode === "all-occurrences") {
1757
+ const beforeEvents = tools.filter((event) => toolName(event) === options.before);
1758
+ const afterEvents = tools.filter((event) => toolName(event) === options.after);
1759
+ const expected = {
1760
+ before: options.before,
1761
+ after: options.after,
1762
+ mode,
1763
+ relation: "max(before.end) <= min(after.start)"
1764
+ };
1765
+ if (options.before === options.after) {
1766
+ return [
1767
+ failFinding(
1768
+ ruleId,
1769
+ `Tool ${options.before} cannot causally happen before itself.`,
1770
+ [eventEvidence(beforeEvent)],
1771
+ expected,
1772
+ { code: "tool.order.same-tool" }
1773
+ )
1774
+ ];
1775
+ }
1776
+ let latestBefore;
1777
+ let earliestAfter;
1778
+ let missingBeforeEnds = 0;
1779
+ let missingAfterStarts = 0;
1780
+ let firstMissingBefore;
1781
+ let firstMissingAfter;
1782
+ for (const event of beforeEvents) {
1783
+ const end = eventEndMs(event);
1784
+ if (end === void 0) {
1785
+ missingBeforeEnds += 1;
1786
+ firstMissingBefore ??= event;
1787
+ } else if (latestBefore === void 0 || end > latestBefore.end) {
1788
+ latestBefore = { event, end };
1789
+ }
1790
+ }
1791
+ for (const event of afterEvents) {
1792
+ const start = eventStartMs(event);
1793
+ if (start === void 0) {
1794
+ missingAfterStarts += 1;
1795
+ firstMissingAfter ??= event;
1796
+ } else if (earliestAfter === void 0 || start < earliestAfter.start) {
1797
+ earliestAfter = { event, start };
1798
+ }
1799
+ }
1800
+ if (missingBeforeEnds > 0 || missingAfterStarts > 0 || latestBefore === void 0 || earliestAfter === void 0) {
1801
+ return [
1802
+ failFinding(
1803
+ ruleId,
1804
+ `Tool order ${options.before} before ${options.after} could not establish all causal intervals.`,
1805
+ [firstMissingBefore, firstMissingAfter].filter((event) => event !== void 0).map((event) => eventEvidence(event)),
1806
+ expected,
1807
+ {
1808
+ code: "tool.order.interval-unresolved",
1809
+ missingBeforeEnds,
1810
+ missingAfterStarts
1811
+ }
1812
+ )
1813
+ ];
1814
+ }
1815
+ if (latestBefore.end > earliestAfter.start) {
1816
+ return [
1817
+ failFinding(
1818
+ ruleId,
1819
+ `Every ${options.before} tool call must finish before any ${options.after} tool call starts.`,
1820
+ [eventEvidence(latestBefore.event), eventEvidence(earliestAfter.event)],
1821
+ expected,
1822
+ {
1823
+ latestBeforeEndedAt: new Date(latestBefore.end).toISOString(),
1824
+ earliestAfterStartedAt: new Date(earliestAfter.start).toISOString()
1825
+ }
1826
+ )
1827
+ ];
1828
+ }
1829
+ return [];
1830
+ }
1706
1831
  if (beforeEnd !== void 0 && afterStart !== void 0 && beforeEnd > afterStart) {
1707
1832
  return [
1708
1833
  {
@@ -2653,6 +2778,17 @@ function normalizeStatus(status) {
2653
2778
  if (status === "failed") return "error";
2654
2779
  return "error";
2655
2780
  }
2781
+ function cloneBody(body) {
2782
+ return {
2783
+ ...body.run ? { run: { ...body.run } } : {},
2784
+ ...body.tools ? { tools: { ...body.tools } } : {},
2785
+ ...body.llm ? { llm: { ...body.llm } } : {},
2786
+ ...body.observations ? { observations: { ...body.observations } } : {}
2787
+ };
2788
+ }
2789
+ function bodyHasRules(body) {
2790
+ return body.run !== void 0 || body.tools !== void 0 || body.llm !== void 0 || body.observations !== void 0;
2791
+ }
2656
2792
  function contractToRules(contract) {
2657
2793
  const rules = [];
2658
2794
  const allowedStatuses = contract.run?.allowedStatuses ?? [];
@@ -2727,6 +2863,7 @@ function contractToRules(contract) {
2727
2863
  }
2728
2864
  if (contract.tools) {
2729
2865
  const order = contract.tools.requiredOrder ?? [];
2866
+ const requiredOrderMode = contract.tools.requiredOrderMode ?? "first-occurrence";
2730
2867
  const required = [
2731
2868
  .../* @__PURE__ */ new Set([
2732
2869
  ...contract.tools.required ?? [],
@@ -2751,7 +2888,8 @@ function contractToRules(contract) {
2751
2888
  createToolOrderingRule({
2752
2889
  before: order[i],
2753
2890
  after: order[i + 1],
2754
- id: `contract.tool.order.${i}`
2891
+ id: `contract.tool.order.${i}`,
2892
+ mode: requiredOrderMode
2755
2893
  })
2756
2894
  );
2757
2895
  }
@@ -2801,25 +2939,319 @@ function contractToRules(contract) {
2801
2939
  }
2802
2940
  return rules;
2803
2941
  }
2804
- function defineTraceContract(input) {
2942
+ function validateAlternativesShape(alternatives) {
2943
+ const diagnostics = [];
2944
+ if (alternatives === void 0) return diagnostics;
2945
+ const branches = alternatives.anyOf ?? [];
2946
+ if (branches.length === 0) {
2947
+ diagnostics.push({
2948
+ code: "contract.alternatives.empty",
2949
+ severity: "error",
2950
+ message: "alternatives.anyOf must contain at least one branch.",
2951
+ path: "alternatives.anyOf"
2952
+ });
2953
+ return diagnostics;
2954
+ }
2955
+ const seen = /* @__PURE__ */ new Set();
2956
+ for (const [index, branch] of branches.entries()) {
2957
+ const path = `alternatives.anyOf[${index}]`;
2958
+ if (!branch?.id || branch.id.trim() === "") {
2959
+ diagnostics.push({
2960
+ code: "contract.alternatives.missing-id",
2961
+ severity: "error",
2962
+ message: "Each alternatives.anyOf branch requires a non-empty id.",
2963
+ path
2964
+ });
2965
+ continue;
2966
+ }
2967
+ if (seen.has(branch.id)) {
2968
+ diagnostics.push({
2969
+ code: "contract.alternatives.duplicate-id",
2970
+ severity: "error",
2971
+ message: `Duplicate alternatives.anyOf branch id: ${branch.id}`,
2972
+ path: `${path}.id`
2973
+ });
2974
+ }
2975
+ seen.add(branch.id);
2976
+ if (!bodyHasRules(branch.contract ?? {})) {
2977
+ diagnostics.push({
2978
+ code: "contract.alternatives.empty-branch",
2979
+ severity: "error",
2980
+ message: `Branch ${branch.id} must declare at least one run/tools/llm/observations rule.`,
2981
+ path: `${path}.contract`
2982
+ });
2983
+ }
2984
+ const nested = branch.contract?.alternatives;
2985
+ if (nested !== void 0) {
2986
+ diagnostics.push({
2987
+ code: "contract.alternatives.nested",
2988
+ severity: "error",
2989
+ message: "Nested alternatives.anyOf is not supported. Keep one alternatives level only.",
2990
+ path: `${path}.contract.alternatives`
2991
+ });
2992
+ }
2993
+ }
2994
+ return diagnostics;
2995
+ }
2996
+ function emptyPassResult(input, runId) {
2805
2997
  return {
2806
- ...input.run ? { run: { ...input.run } } : {},
2807
- ...input.tools ? { tools: { ...input.tools } } : {},
2808
- ...input.llm ? { llm: { ...input.llm } } : {},
2809
- ...input.observations ? { observations: { ...input.observations } } : {}
2998
+ ok: true,
2999
+ status: "pass",
3000
+ format: input.read.format,
3001
+ ...runId !== void 0 ? { runId } : {},
3002
+ summary: { passed: 0, failed: 0, warnings: 0, errors: 0, rulesEvaluated: 0 },
3003
+ findings: [],
3004
+ diagnostics: [],
3005
+ ruleExecutions: []
2810
3006
  };
2811
3007
  }
2812
- function evaluateTraceContract(input, contract, options = {}) {
2813
- const rules = contractToRules(contract);
3008
+ function mergeContractResults(input, parts, extraFindings = []) {
3009
+ const findings = [...parts.flatMap((part) => part.findings), ...extraFindings];
3010
+ const diagnostics = parts.flatMap((part) => part.diagnostics);
3011
+ const ruleExecutions = parts.flatMap((part) => part.ruleExecutions);
3012
+ const failed = findings.filter(
3013
+ (finding) => finding.status === "fail" && finding.severity === "error"
3014
+ ).length;
3015
+ const errors = diagnostics.filter((item) => item.severity === "error").length;
3016
+ const status = errors > 0 ? "error" : failed > 0 ? "fail" : "pass";
3017
+ const runId = parts.find((part) => part.runId !== void 0)?.runId;
3018
+ return {
3019
+ ok: status === "pass",
3020
+ status,
3021
+ format: input.read.format,
3022
+ ...runId !== void 0 ? { runId } : {},
3023
+ summary: {
3024
+ passed: findings.filter((finding) => finding.status === "pass").length,
3025
+ failed,
3026
+ warnings: findings.filter(
3027
+ (finding) => finding.status === "warning" || finding.severity === "warning"
3028
+ ).length,
3029
+ errors,
3030
+ rulesEvaluated: ruleExecutions.length
3031
+ },
3032
+ findings,
3033
+ diagnostics,
3034
+ ruleExecutions
3035
+ };
3036
+ }
3037
+ function evaluateBody(input, body, options = {}) {
3038
+ const rules = contractToRules(body);
3039
+ if (rules.length === 0) {
3040
+ return emptyPassResult(input, options.runId);
3041
+ }
2814
3042
  return runTraceChecks(input, {
2815
3043
  rules,
2816
3044
  ...options.runId !== void 0 ? { runId: options.runId } : {}
2817
3045
  });
2818
3046
  }
3047
+ function defineTraceContract(input) {
3048
+ const shapeErrors = validateAlternativesShape(input.alternatives).filter(
3049
+ (item) => item.severity === "error"
3050
+ );
3051
+ if (shapeErrors.length > 0) {
3052
+ throw new TypeError(shapeErrors.map((item) => item.message).join(" "));
3053
+ }
3054
+ const body = cloneBody(input);
3055
+ const alternatives = input.alternatives ? {
3056
+ anyOf: input.alternatives.anyOf.map((branch) => ({
3057
+ id: branch.id,
3058
+ ...branch.description !== void 0 ? { description: branch.description } : {},
3059
+ contract: cloneBody(branch.contract)
3060
+ }))
3061
+ } : void 0;
3062
+ return {
3063
+ ...body,
3064
+ ...alternatives ? { alternatives } : {}
3065
+ };
3066
+ }
3067
+ function evaluateTraceContract(input, contract, options = {}) {
3068
+ const shapeErrors = validateAlternativesShape(contract.alternatives).filter(
3069
+ (item) => item.severity === "error"
3070
+ );
3071
+ if (shapeErrors.length > 0) {
3072
+ return {
3073
+ ok: false,
3074
+ status: "error",
3075
+ format: input.read.format,
3076
+ ...options.runId !== void 0 ? { runId: options.runId } : {},
3077
+ summary: {
3078
+ passed: 0,
3079
+ failed: 0,
3080
+ warnings: 0,
3081
+ errors: shapeErrors.length,
3082
+ rulesEvaluated: 0
3083
+ },
3084
+ findings: [],
3085
+ diagnostics: shapeErrors.map((item) => ({
3086
+ code: "AI_CHECK_INVALID_CONFIG",
3087
+ message: item.message,
3088
+ severity: "error",
3089
+ ruleId: item.code
3090
+ })),
3091
+ ruleExecutions: []
3092
+ };
3093
+ }
3094
+ const base = evaluateBody(input, contract, options);
3095
+ const branches = contract.alternatives?.anyOf ?? [];
3096
+ if (branches.length === 0) {
3097
+ return base;
3098
+ }
3099
+ const branchEvaluations = branches.map((branch) => {
3100
+ const result = evaluateBody(input, branch.contract, options);
3101
+ return { branch, result };
3102
+ });
3103
+ const satisfied = branchEvaluations.filter((item) => item.result.ok && item.result.status === "pass").map((item) => item.branch.id);
3104
+ const failedBranches = branchEvaluations.filter(
3105
+ (item) => !(item.result.ok && item.result.status === "pass")
3106
+ );
3107
+ if (satisfied.length > 0) {
3108
+ const passFinding = {
3109
+ ruleId: "contract.alternatives.anyOf",
3110
+ severity: "info",
3111
+ status: "pass",
3112
+ message: `Satisfied alternative branch(es): ${satisfied.join(", ")}.`,
3113
+ expected: { anyOf: branches.map((branch) => branch.id) },
3114
+ actual: {
3115
+ satisfied,
3116
+ failed: failedBranches.map((item) => item.branch.id)
3117
+ },
3118
+ evidence: []
3119
+ };
3120
+ return mergeContractResults(input, [base], [passFinding]);
3121
+ }
3122
+ const noneFinding = {
3123
+ ruleId: "contract.alternatives.none-satisfied",
3124
+ severity: "error",
3125
+ status: "fail",
3126
+ message: "No alternatives.anyOf branch fully satisfied its contract.",
3127
+ expected: { anyOf: branches.map((branch) => branch.id) },
3128
+ actual: {
3129
+ branchFailures: Object.fromEntries(
3130
+ failedBranches.map((item) => [
3131
+ item.branch.id,
3132
+ item.result.findings.filter((finding) => finding.status === "fail").map((finding) => ({
3133
+ ruleId: finding.ruleId,
3134
+ message: finding.message
3135
+ }))
3136
+ ])
3137
+ )
3138
+ },
3139
+ evidence: []
3140
+ };
3141
+ const prefixedBranchFindings = failedBranches.flatMap(
3142
+ (item) => item.result.findings.filter((finding) => finding.status === "fail").map((finding) => ({
3143
+ ...finding,
3144
+ ruleId: `contract.alternatives.${item.branch.id}.${finding.ruleId}`
3145
+ }))
3146
+ );
3147
+ return mergeContractResults(input, [base], [noneFinding, ...prefixedBranchFindings]);
3148
+ }
2819
3149
  function evaluateTraceContractRead(read, contract, options = {}) {
2820
3150
  return evaluateTraceContract({ read }, contract, options);
2821
3151
  }
3152
+ function lintTraceContract(contract) {
3153
+ const diagnostics = validateAlternativesShape(contract.alternatives);
3154
+ const branches = contract.alternatives?.anyOf ?? [];
3155
+ if (branches.length === 1) {
3156
+ diagnostics.push({
3157
+ code: "contract.alternatives.single-branch",
3158
+ severity: "warning",
3159
+ message: "alternatives.anyOf has a single branch. Prefer base rules unless a second valid path exists.",
3160
+ path: "alternatives.anyOf"
3161
+ });
3162
+ }
3163
+ const required = [
3164
+ ...contract.tools?.required ?? [],
3165
+ ...contract.tools?.requiredTools ?? []
3166
+ ];
3167
+ if (required.length > 0 && branches.length === 0) {
3168
+ diagnostics.push({
3169
+ code: "contract.brittle.unconditional-required",
3170
+ severity: "info",
3171
+ message: "tools.required is unconditional. Use alternatives.anyOf when a legitimate shortcut may skip a tool.",
3172
+ path: "tools.required"
3173
+ });
3174
+ }
3175
+ const order = contract.tools?.requiredOrder ?? [];
3176
+ if (order.length >= 2 && contract.tools?.requiredOrderMode === void 0) {
3177
+ diagnostics.push({
3178
+ code: "contract.brittle.first-occurrence-default",
3179
+ severity: "info",
3180
+ message: "requiredOrder defaults to first-occurrence. Set requiredOrderMode when repeated calls must fail.",
3181
+ path: "tools.requiredOrderMode"
3182
+ });
3183
+ }
3184
+ return diagnostics;
3185
+ }
3186
+ function explainTraceContract(contract) {
3187
+ const lines = [];
3188
+ if (contract.run?.requireCompleted !== false) {
3189
+ lines.push("Base: run must complete (no running events).");
3190
+ }
3191
+ if (contract.run?.allowedStatuses?.length) {
3192
+ lines.push(`Base: run status in [${contract.run.allowedStatuses.join(", ")}].`);
3193
+ }
3194
+ if (contract.run?.maxDurationMs !== void 0) {
3195
+ lines.push(`Base: run duration <= ${contract.run.maxDurationMs}ms.`);
3196
+ }
3197
+ if (contract.tools) {
3198
+ const required = [
3199
+ .../* @__PURE__ */ new Set([
3200
+ ...contract.tools.required ?? [],
3201
+ ...contract.tools.requiredTools ?? [],
3202
+ ...contract.tools.requiredOrder ?? []
3203
+ ])
3204
+ ];
3205
+ if (required.length > 0) {
3206
+ lines.push(`Base: required tools [${required.join(", ")}].`);
3207
+ }
3208
+ const forbidden = [
3209
+ ...contract.tools.forbidden ?? [],
3210
+ ...contract.tools.forbiddenTools ?? []
3211
+ ];
3212
+ if (forbidden.length > 0) {
3213
+ lines.push(`Base: forbidden tools [${forbidden.join(", ")}].`);
3214
+ }
3215
+ if ((contract.tools.requiredOrder?.length ?? 0) >= 2) {
3216
+ const mode = contract.tools.requiredOrderMode ?? "first-occurrence";
3217
+ lines.push(
3218
+ `Base: requiredOrder [${contract.tools.requiredOrder.join(" \u2192 ")}] mode=${mode}.`
3219
+ );
3220
+ }
3221
+ }
3222
+ if (contract.llm) {
3223
+ if (contract.llm.maxCalls !== void 0) {
3224
+ lines.push(`Base: LLM maxCalls=${contract.llm.maxCalls}.`);
3225
+ }
3226
+ if (contract.llm.maxTotalTokens !== void 0) {
3227
+ lines.push(`Base: LLM maxTotalTokens=${contract.llm.maxTotalTokens}.`);
3228
+ }
3229
+ if (contract.llm.allowedModels?.length) {
3230
+ lines.push(`Base: LLM allowedModels [${contract.llm.allowedModels.join(", ")}].`);
3231
+ }
3232
+ }
3233
+ if (contract.observations?.required?.length) {
3234
+ lines.push(`Base: required observations [${contract.observations.required.join(", ")}].`);
3235
+ }
3236
+ if (contract.observations?.failOn?.length) {
3237
+ lines.push(`Base: fail on observations [${contract.observations.failOn.join(", ")}].`);
3238
+ }
3239
+ const branches = contract.alternatives?.anyOf ?? [];
3240
+ if (branches.length > 0) {
3241
+ lines.push(
3242
+ `Alternatives: at least one of [${branches.map((branch) => branch.id).join(", ")}] must fully pass.`
3243
+ );
3244
+ for (const branch of branches) {
3245
+ const detail = branch.description ? ` \u2014 ${branch.description}` : "";
3246
+ lines.push(` branch ${branch.id}${detail}`);
3247
+ }
3248
+ }
3249
+ if (lines.length === 0) {
3250
+ lines.push("Empty contract: no base rules and no alternatives.");
3251
+ }
3252
+ return lines;
3253
+ }
2822
3254
 
2823
- export { buildTraceFacts, createBaselineRegressionRule, createDecisionRule, createGuardrailRule, createLlmUsageRule, createMaxStepDurationRule, createObservedOutcomeRule, createRequireCompletedRule, createRetrievalRule, createRunDepthRule, createRunDurationRule, createRunEventCountRule, createRunStatusRule, createSafetyOversizedAttributeRule, createSafetyRawContentRule, createSafetyRedactionRule, createSafetySecretPatternRule, createStallDetectionRule, createStructureCycleRule, createStructureIncompleteRule, createStructureOrphanRule, createStructureParallelWidthRule, createStructureRelationshipRule, createToolFailureRule, createToolOrderingRule, createToolUsageRule, defineTraceContract, deriveFailureFacts, evaluateTraceContract, evaluateTraceContractRead, projectLogicalEvents, resolveCanonicalToolName, runTraceChecks, summarizeSemanticParity };
2824
- //# sourceMappingURL=chunk-NABMW5DX.mjs.map
2825
- //# sourceMappingURL=chunk-NABMW5DX.mjs.map
3255
+ export { buildTraceFacts, createBaselineRegressionRule, createDecisionRule, createGuardrailRule, createLlmUsageRule, createMaxStepDurationRule, createObservedOutcomeRule, createRequireCompletedRule, createRetrievalRule, createRunDepthRule, createRunDurationRule, createRunEventCountRule, createRunStatusRule, createSafetyOversizedAttributeRule, createSafetyRawContentRule, createSafetyRedactionRule, createSafetySecretPatternRule, createStallDetectionRule, createStructureCycleRule, createStructureIncompleteRule, createStructureOrphanRule, createStructureParallelWidthRule, createStructureRelationshipRule, createToolFailureRule, createToolOrderingRule, createToolUsageRule, defineTraceContract, deriveFailureFacts, evaluateTraceContract, evaluateTraceContractRead, explainTraceContract, lintTraceContract, projectLogicalEvents, resolveCanonicalToolName, runTraceChecks, summarizeSemanticParity };
3256
+ //# sourceMappingURL=chunk-G47KQB42.mjs.map
3257
+ //# sourceMappingURL=chunk-G47KQB42.mjs.map