@tangle-network/agent-eval 0.193.0 → 0.193.2

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 (67) hide show
  1. package/CHANGELOG.md +22 -2
  2. package/dist/analyst/index.d.ts +2 -2
  3. package/dist/analyst/index.js +4 -4
  4. package/dist/{benchmark-command-DVhUPU7H.js → benchmark-command-N5r3GXMP.js} +8 -8
  5. package/dist/{benchmark-command-DVhUPU7H.js.map → benchmark-command-N5r3GXMP.js.map} +1 -1
  6. package/dist/benchmarks/index.js +3 -3
  7. package/dist/campaign/index.js +4 -4
  8. package/dist/{campaign-DdE6S80e.js → campaign-DnL0S4XL.js} +6 -6
  9. package/dist/{campaign-DdE6S80e.js.map → campaign-DnL0S4XL.js.map} +1 -1
  10. package/dist/{chat-client-B_ItsSBw.js → chat-client-CJORSG_J.js} +2 -2
  11. package/dist/{chat-client-B_ItsSBw.js.map → chat-client-CJORSG_J.js.map} +1 -1
  12. package/dist/{chat-json-call-CXHBvUz8.js → chat-json-call-DnlDjpco.js} +2 -2
  13. package/dist/{chat-json-call-CXHBvUz8.js.map → chat-json-call-DnlDjpco.js.map} +1 -1
  14. package/dist/cli.js +3 -3
  15. package/dist/contract/index.js +6 -6
  16. package/dist/{define-agent-eval-DkPbnJEY.js → define-agent-eval-qxAPjMSL.js} +3 -3
  17. package/dist/{define-agent-eval-DkPbnJEY.js.map → define-agent-eval-qxAPjMSL.js.map} +1 -1
  18. package/dist/diagnosis/index.d.ts +1 -1
  19. package/dist/diagnosis/index.d.ts.map +1 -1
  20. package/dist/diagnosis/index.js +11 -5
  21. package/dist/diagnosis/index.js.map +1 -1
  22. package/dist/{dspy-rlm-engine-BX34vPVY.js → dspy-rlm-engine-k_QdEBZo.js} +2 -2
  23. package/dist/{dspy-rlm-engine-BX34vPVY.js.map → dspy-rlm-engine-k_QdEBZo.js.map} +1 -1
  24. package/dist/{eval-campaign-CwsBn0Gm.js → eval-campaign-CXT2WYUe.js} +2 -2
  25. package/dist/{eval-campaign-CwsBn0Gm.js.map → eval-campaign-CXT2WYUe.js.map} +1 -1
  26. package/dist/{external-optimizer-process-D-Sab4v3.js → external-optimizer-process-_YIECYJ7.js} +2 -2
  27. package/dist/{external-optimizer-process-D-Sab4v3.js.map → external-optimizer-process-_YIECYJ7.js.map} +1 -1
  28. package/dist/{external-optimizer-subprocess-kbqC-iWf.js → external-optimizer-subprocess-BrghTvEp.js} +2 -2
  29. package/dist/{external-optimizer-subprocess-kbqC-iWf.js.map → external-optimizer-subprocess-BrghTvEp.js.map} +1 -1
  30. package/dist/index.d.ts +1 -1
  31. package/dist/index.js +56 -21
  32. package/dist/index.js.map +1 -1
  33. package/dist/{llm-client-KYQBh9C8.js → llm-client-c_bKHTXz.js} +2 -2
  34. package/dist/{llm-client-KYQBh9C8.js.map → llm-client-c_bKHTXz.js.map} +1 -1
  35. package/dist/{llm-judge-ChbWfeWl.js → llm-judge-Cififl_V.js} +4 -4
  36. package/dist/{llm-judge-ChbWfeWl.js.map → llm-judge-Cififl_V.js.map} +1 -1
  37. package/dist/openapi.json +1 -1
  38. package/dist/{otel-spans-DS7t1t1J.js → otel-spans-CQmSxtcS.js} +2 -2
  39. package/dist/{otel-spans-DS7t1t1J.js.map → otel-spans-CQmSxtcS.js.map} +1 -1
  40. package/dist/pipelines/index.d.ts +1 -1
  41. package/dist/{produced-state-BqeIaIp2.js → produced-state-B2AhDRnq.js} +3 -3
  42. package/dist/{produced-state-BqeIaIp2.js.map → produced-state-B2AhDRnq.js.map} +1 -1
  43. package/dist/{raw-provider-sink-DYh4Bszw.js → raw-provider-sink-CXuOhLvT.js} +2 -2
  44. package/dist/{raw-provider-sink-DYh4Bszw.js.map → raw-provider-sink-CXuOhLvT.js.map} +1 -1
  45. package/dist/{redact-CopuMxll.d.ts → redact-Cveoq6fJ.d.ts} +2 -2
  46. package/dist/{redact-CopuMxll.d.ts.map → redact-Cveoq6fJ.d.ts.map} +1 -1
  47. package/dist/{redact-B7rt9Zot.js → redact-Dk1ZdJEm.js} +59 -26
  48. package/dist/redact-Dk1ZdJEm.js.map +1 -0
  49. package/dist/rl.js +1 -1
  50. package/dist/{semantic-concept-judge-Bg-560H0.js → semantic-concept-judge-Bh5lvKGg.js} +4 -4
  51. package/dist/{semantic-concept-judge-Bg-560H0.js.map → semantic-concept-judge-Bh5lvKGg.js.map} +1 -1
  52. package/dist/{server-DtetGGoA.js → server-D56r1lhp.js} +2 -2
  53. package/dist/{server-DtetGGoA.js.map → server-D56r1lhp.js.map} +1 -1
  54. package/dist/{skillopt-optimization-method-DXQdaBjx.js → skillopt-optimization-method-C6DbFKnw.js} +5 -5
  55. package/dist/{skillopt-optimization-method-DXQdaBjx.js.map → skillopt-optimization-method-C6DbFKnw.js.map} +1 -1
  56. package/dist/{spans-BWtjk_9c.d.ts → spans-D9_ehFuk.d.ts} +2 -2
  57. package/dist/{spans-BWtjk_9c.d.ts.map → spans-D9_ehFuk.d.ts.map} +1 -1
  58. package/dist/{store-tool-spans-BcC_5ef-.js → store-tool-spans-BaGakFc2.js} +2 -2
  59. package/dist/{store-tool-spans-BcC_5ef-.js.map → store-tool-spans-BaGakFc2.js.map} +1 -1
  60. package/dist/{task-failure-attributes-CEqEfuIY.js → task-failure-attributes-DngaLdE4.js} +4 -3
  61. package/dist/task-failure-attributes-DngaLdE4.js.map +1 -0
  62. package/dist/traces.d.ts +1 -1
  63. package/dist/traces.js +4 -4
  64. package/dist/wire/index.js +1 -1
  65. package/package.json +2 -2
  66. package/dist/redact-B7rt9Zot.js.map +0 -1
  67. package/dist/task-failure-attributes-CEqEfuIY.js.map +0 -1
package/dist/index.d.ts CHANGED
@@ -34,7 +34,7 @@ import { A as SemanticConceptJudgeInput, C as DspyRlmTraceEngineOptions, D as Co
34
34
  import { i as BenchmarkEvaluation } from "./types-qoxUfKQg.js";
35
35
  import { a as SpanHandle, n as CaptureWrite, o as TraceEmitter, s as TraceEmitterOptions, t as CaptureErrorHandler } from "./emitter-HcCmFQl_.js";
36
36
  import { n as SandboxDriver } from "./sandbox-harness-BlSOu4LX.js";
37
- import { S as shareAllowed, b as redactForShare, d as ShareSafetyVerdict, f as assessShareSafety, i as RedactOptions, o as RedactionProfile, r as REDACTION_VERSION, s as RedactionReport, u as ShareSafetyStatus, x as redactText, y as redact } from "./redact-CopuMxll.js";
37
+ import { S as shareAllowed, b as redactForShare, d as ShareSafetyVerdict, f as assessShareSafety, i as RedactOptions, o as RedactionProfile, r as REDACTION_VERSION, s as RedactionReport, u as ShareSafetyStatus, x as redactText, y as redact } from "./redact-Cveoq6fJ.js";
38
38
  import { a as PairedEvalueStep, c as SequentialDecision, i as PairedEvalueSequence, l as evaluateInterimReleaseConfidence, n as InterimReleaseConfidenceInput, r as PairedEvalueOptions, t as InterimReleaseConfidence, u as pairedEvalueSequence } from "./sequential-BhsrMupG.js";
39
39
  import { n as TrajectoryStep, r as buildTrajectory, t as Trajectory } from "./trajectory-Y1fmdlw7.js";
40
40
  import { a as runCounterfactual, i as CounterfactualRunner, n as CounterfactualMutation, r as CounterfactualResult, t as CounterfactualContext } from "./counterfactual-CPaWN99-.js";
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-8H4AJuhK.js";
2
- import { i as JudgeError, o as NotFoundError, r as ConfigError, s as ValidationError, t as AgentEvalError } from "./errors-Dngq5h35.js";
2
+ import { i as JudgeError, n as CaptureIntegrityError, o as NotFoundError, r as ConfigError, s as ValidationError, t as AgentEvalError } from "./errors-Dngq5h35.js";
3
3
  import { a as hashCanonical, r as canonicalString } from "./canonical-BsZVgBhW.js";
4
- import { a as CODING_HARNESSES, c as agentProfileId, d as harnessAxisOf, i as verifyCompletion, l as agentProfileModelId, n as completionVerdict, o as HARNESS_NATIVE_MODEL, r as createLlmCorrectnessChecker, s as agentProfileHash, t as extractProducedState, u as expandProfileAxes } from "./produced-state-BqeIaIp2.js";
4
+ import { a as CODING_HARNESSES, c as agentProfileId, d as harnessAxisOf, i as verifyCompletion, l as agentProfileModelId, n as completionVerdict, o as HARNESS_NATIVE_MODEL, r as createLlmCorrectnessChecker, s as agentProfileHash, t as extractProducedState, u as expandProfileAxes } from "./produced-state-B2AhDRnq.js";
5
5
  import { c as groupRunsByAgentProfileCell, d as validateAgentProfileCell, f as verifyAgentProfileCell, h as manifestContentDigest, i as agentProfileCellKey, m as hashJson, r as agentProfileCellHashMaterial, s as buildAgentProfileCell, t as AGENT_PROFILE_KINDS, u as toAgentProfileJson } from "./agent-profile-cell-DcFW4vOc.js";
6
6
  import { t as mulberry32 } from "./random-Dn5fPWkt.js";
7
7
  import { a as spearmanR, c as weightedMean, i as ranks, n as partialCredit, o as summarizeNumberSeries, r as pearsonR, s as weightedComposite, t as confidenceInterval } from "./descriptive-1V17A-qa.js";
@@ -22,12 +22,12 @@ import { r as observedSplitScore, t as isRealnessGated } from "./reward-nw2xZGZG
22
22
  import { a as parseRunRecordSafe, c as runTaskScore, i as modelHasSnapshot, l as searchCellRunId, n as UNKNOWN_MODEL, o as roundTripRunRecord, r as isRunRecord, s as runCostFloorUsd, t as RunRecordValidationError, u as validateRunRecord } from "./run-record-BRlM99rX.js";
23
23
  import { a as summaryTable, n as gainHistogram, r as paretoChart } from "./summary-report-BMvou2oZ.js";
24
24
  import { n as contentHash, r as fileVerdictCache, t as canonicalJson } from "./verdict-cache-BQEsVy9n.js";
25
- import { $ as redTeamDataset, K as dominates, Q as DEFAULT_RED_TEAM_CORPUS, U as pairedPromotionPower, W as requiredPairsForPairedPromotion, a as transientDispatchFailure, ct as runCampaign, et as redTeamReport, i as quotaExhaustedUntil, nt as runCanaries, o as aggregateRunScore, q as paretoFrontier, s as clamp01, t as llmJudge, tt as scoreRedTeamOutput } from "./llm-judge-ChbWfeWl.js";
25
+ import { $ as redTeamDataset, K as dominates, Q as DEFAULT_RED_TEAM_CORPUS, U as pairedPromotionPower, W as requiredPairsForPairedPromotion, a as transientDispatchFailure, ct as runCampaign, et as redTeamReport, i as quotaExhaustedUntil, nt as runCanaries, o as aggregateRunScore, q as paretoFrontier, s as clamp01, t as llmJudge, tt as scoreRedTeamOutput } from "./llm-judge-Cififl_V.js";
26
26
  import { a as resolveModelPricing, i as isModelPriced, n as estimateCost, r as estimateTokens, t as MODEL_PRICING } from "./metrics-DOMpkoeb.js";
27
27
  import { a as CostLedgerPersistenceError, c as costForTokenPricing, i as CostLedger, l as costForUsage, n as CostCallConflictError, o as CostReceiptCaptureError, r as CostCeilingReachedError, s as CostReservationExceededError, t as CostAccountingIncompleteError, u as modelPriceKey } from "./cost-ledger-_IbkejCF.js";
28
28
  import { B as assertRealBackend, F as surfaceContentHash, T as parseReflectionResponse, V as summarizeBackendIntegrity, c as ATTESTATION_ALGORITHM, l as attest, u as verifyAttestation, w as buildReflectionPrompt, z as BackendIntegrityError } from "./campaign-evidence-DJc60F9C.js";
29
- import { d as redactForShare, f as redactText, n as REDACTION_VERSION, p as shareAllowed, r as assessShareSafety, u as redact } from "./redact-B7rt9Zot.js";
30
- import { r as selfImprove, t as defineAgentEval } from "./define-agent-eval-DkPbnJEY.js";
29
+ import { d as redactForShare, f as redactText, n as REDACTION_VERSION, p as shareAllowed, r as assessShareSafety, u as redact } from "./redact-Dk1ZdJEm.js";
30
+ import { r as selfImprove, t as defineAgentEval } from "./define-agent-eval-qxAPjMSL.js";
31
31
  import { n as equivalenceVerdict, t as certificationEvidenceDigest } from "./verdict-DhPoXApk.js";
32
32
  import { t as TraceEmitter } from "./emitter-DbMXUIVP.js";
33
33
  import { t as buildTrajectory } from "./trajectory-D_7rLrvE.js";
@@ -36,26 +36,26 @@ import { G as DEFAULT_TRACE_ANALYST_OUTPUT_TOKENS, U as parseFindingSubject, h a
36
36
  import { _ as completedAnalystReviewQuality, b as validateAnalystReviewDecisions, c as FAILURE_MODE_KIND_SPEC, g as assertUniqueFindingIds, h as analystRunDigest, i as DEFAULT_TRACE_ANALYST_KINDS, m as analystFindingDigest, n as AnalystRegistry, t as buildDefaultAnalystRegistry, v as readAnalystReview, y as snapshotAnalystRun } from "./default-registry-xMLX160E.js";
37
37
  import { OUTPUT_VALUE } from "./trace-attributes.js";
38
38
  import { r as extractUsageFromSse, t as extractUsage } from "./extract-usage-DFATEuWW.js";
39
- import { n as InMemoryRawProviderSink, r as NoopRawProviderSink, t as FileSystemRawProviderSink } from "./raw-provider-sink-DYh4Bszw.js";
40
- import { S as exportRunAsOtlp, T as captureFetchToRawSink, _ as tokenizeDomainWords, d as inferDomainKeywords, g as scoreTraceInsightReadiness, l as describeTraceInsightScope, n as toolSpansToTraceAnalysisStore, o as buildTraceInsightContext, s as buildTraceInsightPrompt, u as domainEvidencePattern, v as analyzeTraces } from "./store-tool-spans-BcC_5ef-.js";
39
+ import { n as InMemoryRawProviderSink, r as NoopRawProviderSink, t as FileSystemRawProviderSink } from "./raw-provider-sink-CXuOhLvT.js";
40
+ import { S as exportRunAsOtlp, T as captureFetchToRawSink, _ as tokenizeDomainWords, d as inferDomainKeywords, g as scoreTraceInsightReadiness, l as describeTraceInsightScope, n as toolSpansToTraceAnalysisStore, o as buildTraceInsightContext, s as buildTraceInsightPrompt, u as domainEvidencePattern, v as analyzeTraces } from "./store-tool-spans-BaGakFc2.js";
41
41
  import { n as assertRunCaptured, t as RunIntegrityError } from "./integrity-BFP4kjGj.js";
42
42
  import { n as InMemoryTraceStore, t as FileSystemTraceStore } from "./store-DNe_Uv1Q.js";
43
43
  import { t as packageVersion$1 } from "./package-version-D7lQHt_-.js";
44
44
  import { a as classifyFailure, i as INFRA_FAILURE_BLAMES, o as classifyFailureReason, r as FAILURE_BLAME, s as failureBlame, t as DEFAULT_FAILURE_REASON_RULES } from "./failure-taxonomy-CjabTATj.js";
45
45
  import { n as runBoundedProcess, t as DEFAULT_BOUNDED_PROCESS_TIMEOUT_MS } from "./bounded-process-BBZob7vl.js";
46
- import { t as runEvalCampaign } from "./eval-campaign-CwsBn0Gm.js";
46
+ import { t as runEvalCampaign } from "./eval-campaign-CXT2WYUe.js";
47
47
  import "./rollout-B-XBReTC.js";
48
48
  import { t as mintRolloutRows } from "./mint-iCD-W43s.js";
49
49
  import { n as pairedEvalueSequence, t as evaluateInterimReleaseConfidence } from "./sequential-CzK5DarL.js";
50
- import { S as judgeFamily, _ as assertServedModels, b as CrossFamilyError, d as stripFencedJson, f as ModelSubstitutionError, h as assertServedModel, i as backoffMs, l as isTransientLlmError, m as assertCrossFamilyServed, o as costReceiptFromLlm, p as ServedCrossFamilyError, r as LlmResponseError, s as costReceiptFromLlmError, t as LlmCallError, u as maximumChargeForLlmRequest, v as checkServedModel, x as assertCrossFamily, y as servedModelAcceptable } from "./llm-client-KYQBh9C8.js";
51
- import { n as paidJsonChat } from "./chat-json-call-CXHBvUz8.js";
50
+ import { S as judgeFamily, _ as assertServedModels, b as CrossFamilyError, d as stripFencedJson, f as ModelSubstitutionError, h as assertServedModel, i as backoffMs, l as isTransientLlmError, m as assertCrossFamilyServed, o as costReceiptFromLlm, p as ServedCrossFamilyError, r as LlmResponseError, s as costReceiptFromLlmError, t as LlmCallError, u as maximumChargeForLlmRequest, v as checkServedModel, x as assertCrossFamily, y as servedModelAcceptable } from "./llm-client-c_bKHTXz.js";
51
+ import { n as paidJsonChat } from "./chat-json-call-DnlDjpco.js";
52
52
  import { i as makeProposalFinding, n as computeFindingId, r as makeFinding } from "./types-CW5b90JH.js";
53
- import { t as createDspyRlmTraceEngine } from "./dspy-rlm-engine-BX34vPVY.js";
54
- import { a as diffFindings, n as runSemanticConceptJudge, o as createChatTraceEngine, r as FindingsStore, t as SEMANTIC_CONCEPT_JUDGE_VERSION } from "./semantic-concept-judge-Bg-560H0.js";
53
+ import { t as createDspyRlmTraceEngine } from "./dspy-rlm-engine-k_QdEBZo.js";
54
+ import { a as diffFindings, n as runSemanticConceptJudge, o as createChatTraceEngine, r as FindingsStore, t as SEMANTIC_CONCEPT_JUDGE_VERSION } from "./semantic-concept-judge-Bh5lvKGg.js";
55
55
  import { t as analyzeSeries } from "./series-convergence-CjO2QdRW.js";
56
56
  import { i as otlpTextToTraceAnalysisStore, n as OtlpFileTraceStore } from "./store-otlp-BPowgPm6.js";
57
57
  import { n as evaluateReleaseConfidence, r as bootstrapCi } from "./release-confidence-XQm_xjhR.js";
58
- import { t as createChatClient } from "./chat-client-B_ItsSBw.js";
58
+ import { t as createChatClient } from "./chat-client-CJORSG_J.js";
59
59
  import { createHash } from "node:crypto";
60
60
  import { accessSync, appendFileSync, constants, cpSync, existsSync, mkdirSync, promises, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
61
61
  import { basename, delimiter, dirname, extname, isAbsolute, join, relative, resolve } from "node:path";
@@ -1358,7 +1358,7 @@ function assertPredicate(value, where) {
1358
1358
  ]) if (p[field] !== void 0) assertMatcher(p[field], `${where} "${field}"`);
1359
1359
  if (p.kind !== void 0 && !SPAN_KINDS.includes(p.kind)) throw new ValidationError(`${where}: "kind" must be one of ${SPAN_KINDS.join(", ")}`);
1360
1360
  if (p.attr !== void 0 && (p.attr === null || typeof p.attr !== "object" || Array.isArray(p.attr))) throw new ValidationError(`${where}: "attr" must be a plain object`);
1361
- for (const [key, expected] of Object.entries(p.attr ?? {})) if (isSerializedRegex(expected)) assertMatcher(expected, `${where} attr "${key}"`);
1361
+ for (const [key, expected] of Object.entries(p.attr ?? {})) if (expected !== null && typeof expected === "object") assertMatcher(expected, `${where} attr "${key}"`);
1362
1362
  if (p.not !== void 0) assertPredicate(p.not, `${where} "not"`);
1363
1363
  if (p.requiresCustom && typeof p.custom !== "function") throw new ValidationError(`${where}: rule was built with a custom predicate function, which does not survive JSON serialization — re-attach \`custom\` after deserializing or drop the rule`);
1364
1364
  const hasAttr = p.attr !== void 0 && Object.keys(p.attr).length > 0;
@@ -1371,7 +1371,7 @@ function predicateMatches(span, p) {
1371
1371
  if (p.model !== void 0 && !matchText(spanModel(span), p.model)) return false;
1372
1372
  if (p.attr !== void 0) for (const [key, expected] of Object.entries(p.attr)) {
1373
1373
  const actual = span.attributes?.[key];
1374
- if (isRegexValue(expected)) {
1374
+ if (isRegexValue(expected) || isOneOfMatcher(expected)) {
1375
1375
  if (!matchText(typeof actual === "string" ? actual : void 0, expected)) return false;
1376
1376
  } else if (actual !== expected) return false;
1377
1377
  }
@@ -1813,6 +1813,33 @@ function traceContract(name) {
1813
1813
  function spanRef(span, index) {
1814
1814
  return span.spanId ?? `#${index}`;
1815
1815
  }
1816
+ /**
1817
+ * Whether this trace shows any sign that spans of `kind` are actually
1818
+ * captured, independent of whether any span happens to match a given rule's
1819
+ * predicate. A `never`, `atMost`, `tokensAtMost` or `retrySafe` rule that
1820
+ * targets TOOL or LLM spans and finds zero matches cannot otherwise tell "no
1821
+ * calls happened" from "calls were not recorded": an exporter that silently
1822
+ * drops a span kind makes `forbidden`, `allowed`, `maxCalls` and `retries`
1823
+ * checks on that kind pass vacuously. Evidence is: a span of that kind
1824
+ * anywhere in the trace, or — for TOOL — a recorded offered-tool set
1825
+ * (`gen_ai.tool.definitions`), which proves the capture pipeline ran even on
1826
+ * a turn that called no tool.
1827
+ */
1828
+ function hasCaptureEvidence(spans, kind) {
1829
+ if (spans.some((span) => contractSpanKind(span) === kind)) return true;
1830
+ if (kind === "TOOL") return spans.some((span) => spanOfferedTools(span).recorded);
1831
+ return false;
1832
+ }
1833
+ /** Only `TOOL`/`LLM`-scoped predicates have capture evidence to check. */
1834
+ function capturedKindOf(p) {
1835
+ return p.kind === "TOOL" || p.kind === "LLM" ? p.kind : void 0;
1836
+ }
1837
+ function requireCaptureEvidence(spans, p, rule, situation) {
1838
+ const kind = capturedKindOf(p);
1839
+ if (kind === void 0) return;
1840
+ if (hasCaptureEvidence(spans, kind)) return;
1841
+ throw new CaptureIntegrityError(`rule '${rule}': ${situation} and the trace has no ${kind} capture evidence (no ${kind} span` + (kind === "TOOL" ? `, no ${TOOL_DEFINITIONS_ATTR}` : "") + `) — cannot tell whether ${describePredicate(p)} never happened or was never recorded`);
1842
+ }
1816
1843
  function indexed(spans, p) {
1817
1844
  const out = [];
1818
1845
  spans.forEach((span, i) => {
@@ -2066,7 +2093,9 @@ function checkRetrySafe(rule, spans) {
2066
2093
  const out = [];
2067
2094
  const groups = /* @__PURE__ */ new Map();
2068
2095
  const unseen = /* @__PURE__ */ new Map();
2069
- for (const call of toolSpans(spans)) {
2096
+ const calls = toolSpans(spans);
2097
+ if (calls.length === 0 && !hasCaptureEvidence(spans, "TOOL")) throw new CaptureIntegrityError(`rule '${rule.label}': retrySafe found 0 TOOL spans and the trace has no TOOL capture evidence (no TOOL span, no ${TOOL_DEFINITIONS_ATTR}) — cannot tell whether no reads or writes happened or the trace lost its TOOL spans`);
2098
+ for (const call of calls) {
2070
2099
  const name = contractSpanToolName(call.span);
2071
2100
  if (name === void 0) {
2072
2101
  out.push({
@@ -2182,11 +2211,15 @@ function checkRule(rule, ctx) {
2182
2211
  spanId: span.spanId,
2183
2212
  detail: `span ${spanRef(span, i)} ("${span.name ?? ""}") fails always(${describePredicate(rule.p)})`
2184
2213
  }]);
2185
- case "never": return indexed(spans, rule.p).map(({ span, ref }) => ({
2186
- rule: rule.label,
2187
- spanId: span.spanId,
2188
- detail: `span ${ref} ("${span.name ?? ""}") matches never(${describePredicate(rule.p)})`
2189
- }));
2214
+ case "never": {
2215
+ const matches = indexed(spans, rule.p);
2216
+ if (matches.length === 0) requireCaptureEvidence(spans, rule.p, rule.label, `never(${describePredicate(rule.p)}) matched 0 spans`);
2217
+ return matches.map(({ span, ref }) => ({
2218
+ rule: rule.label,
2219
+ spanId: span.spanId,
2220
+ detail: `span ${ref} ("${span.name ?? ""}") matches never(${describePredicate(rule.p)})`
2221
+ }));
2222
+ }
2190
2223
  case "eventually": return spans.some((span) => predicateMatches(span, rule.p)) ? [] : [{
2191
2224
  rule: rule.label,
2192
2225
  detail: `no span matches eventually(${describePredicate(rule.p)}) over ${spans.length} span(s)`
@@ -2209,6 +2242,7 @@ function checkRule(rule, ctx) {
2209
2242
  case "neverUnless": return orderViolations(rule.label, indexed(spans, rule.prior), indexed(spans, rule.p), rule.order ?? "start-order", `${describePredicate(rule.p)} without an earlier ${describePredicate(rule.prior)}`);
2210
2243
  case "atMost": {
2211
2244
  const matches = indexed(spans, rule.p);
2245
+ if (matches.length === 0) requireCaptureEvidence(spans, rule.p, rule.label, `atMost(${describePredicate(rule.p)}) matched 0 spans`);
2212
2246
  return matches.length <= rule.max ? [] : [{
2213
2247
  rule: rule.label,
2214
2248
  spanId: matches[rule.max]?.span.spanId,
@@ -2217,6 +2251,7 @@ function checkRule(rule, ctx) {
2217
2251
  }
2218
2252
  case "tokensAtMost": {
2219
2253
  const matches = indexed(spans, rule.p);
2254
+ if (matches.length === 0) requireCaptureEvidence(spans, rule.p, rule.label, `tokensAtMost(${describePredicate(rule.p)}) matched 0 spans`);
2220
2255
  const unknown = matches.find((m) => spanTotalTokens(m.span) === void 0);
2221
2256
  if (unknown) return [{
2222
2257
  rule: rule.label,