agent-inspect 6.31.32 → 6.31.33

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 6.31.33
4
+
5
+ ### Patch Changes
6
+
7
+ - b2adf03: Fail requiredOrder when a before tool starts but never finishes while after runs (#521). Escape mixed-script lookalike tool names in tool.usage findings and sort allowlist causes first (#522).
8
+
3
9
  ## 6.31.32
4
10
 
5
11
  ### Patch Changes
package/README.md CHANGED
@@ -213,7 +213,7 @@ The root package is enough for custom capture, the CLI, checks, and Evidence wor
213
213
 
214
214
  ## Status and documentation
215
215
 
216
- **Current published baseline:** **6.31.32** · persisted schema `1.0` · Node.js `>=20` · MIT.
216
+ **Current published baseline:** **6.31.33** · persisted schema `1.0` · Node.js `>=20` · MIT.
217
217
 
218
218
  Legacy v0.1 and v0.2 traces remain readable. Check the npm badge and [changelog](CHANGELOG.md) for the current published version.
219
219
 
@@ -45,6 +45,7 @@ Contracts compile to deterministic check rules for common cases:
45
45
  - `all-occurrences` requires every `before` occurrence to finish before every `after` occurrence starts (`max(before.end) <= min(after.start)`);
46
46
  - causal modes fail when a required interval boundary cannot be resolved instead of falling back to encounter order.
47
47
  - when the boundary timestamps tie at millisecond resolution (for example scripted or mocked tools that start and finish inside one millisecond), causal modes decide by recorded event order: the before event must have been recorded as finished before the after event was recorded as started. Such findings carry `resolvedBy: "recorded-order"`.
48
+ - a `before` tool that starts but never finishes (`status: "running"`) while a later `after` tool finishes fails the order rule in every mode with `tool.order.interval-unresolved` (unfinished before), naming the abandoned tool — not only the generic incomplete-running `run.status` finding.
48
49
 
49
50
  Examples for `requiredOrder: ["retrieve_policy", "send_email"]` (both TOOL-typed):
50
51
 
@@ -120,6 +121,7 @@ The evaluator expands each list into adjacent pairs and applies one `requiredOrd
120
121
  - `requiredOrderMode: "all-occurrences"` requires every before to end before every after starts; any cross-boundary overlap or later before fails.
121
122
  - Missing interval boundaries fail closed in the two causal modes.
122
123
  - Millisecond timestamp ties are decided by recorded event order in the two causal modes.
124
+ - A started-but-never-finished `before` tool (running, no completion) fails order in every mode when `after` has finished, with `tool.order.interval-unresolved`.
123
125
 
124
126
  ### `alternatives.anyOf` (shipped)
125
127
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-inspect",
3
- "version": "6.31.32",
3
+ "version": "6.31.33",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "description": "Local evidence debugger and trajectory-test toolkit for TypeScript AI agents — execution trees, TraceContract checks, Evidence v2, and read-only MCP",
@@ -7051,10 +7051,17 @@ function contractToRules(contract) {
7051
7051
  (event) => event.status === "running"
7052
7052
  );
7053
7053
  if (running.length > 0) {
7054
+ const labels = running.map((event) => {
7055
+ if (event.kind === "TOOL") {
7056
+ return `tool ${resolveCanonicalToolName(event)}`;
7057
+ }
7058
+ return `${event.kind} ${event.name}`;
7059
+ });
7060
+ const message = labels.length === 1 ? `Run contains incomplete running events: ${labels[0]} never completed.` : `Run contains incomplete running events: ${labels.join("; ")}.`;
7054
7061
  findings.push(
7055
7062
  contractFailFinding(
7056
7063
  "contract.run.allowedStatuses",
7057
- "Run contains incomplete running events.",
7064
+ message,
7058
7065
  running.map((event) => ({
7059
7066
  runId: event.runId,
7060
7067
  eventId: event.eventId,
@@ -7063,7 +7070,10 @@ function contractToRules(contract) {
7063
7070
  status: event.status
7064
7071
  })),
7065
7072
  "no running events",
7066
- running.length
7073
+ {
7074
+ count: running.length,
7075
+ unfinished: labels
7076
+ }
7067
7077
  )
7068
7078
  );
7069
7079
  }
@@ -8009,6 +8019,11 @@ function compareFindings(eventById) {
8009
8019
  if (STATUS_RANK[a.status] !== STATUS_RANK[b.status]) {
8010
8020
  return STATUS_RANK[a.status] - STATUS_RANK[b.status];
8011
8021
  }
8022
+ if (a.ruleId === "tool.usage") {
8023
+ const aHasEvent = a.evidence[0]?.eventId !== void 0;
8024
+ const bHasEvent = b.evidence[0]?.eventId !== void 0;
8025
+ if (aHasEvent !== bHasEvent) return aHasEvent ? -1 : 1;
8026
+ }
8012
8027
  const byRun = compareStrings(a.evidence[0]?.runId, b.evidence[0]?.runId);
8013
8028
  if (byRun !== 0) return byRun;
8014
8029
  const byTime = eventTimestamp(a, eventById).localeCompare(eventTimestamp(b, eventById));
@@ -8134,6 +8149,37 @@ function failFinding(ruleId, message, evidence, expected, actual, meta) {
8134
8149
  function toolName(event) {
8135
8150
  return resolveCanonicalToolName(event);
8136
8151
  }
8152
+ function formatToolNameForMessage(name) {
8153
+ const details = [];
8154
+ for (let index = 0; index < name.length; ) {
8155
+ const codePoint = name.codePointAt(index);
8156
+ const width = codePoint > 65535 ? 2 : 1;
8157
+ if (codePoint > 127) {
8158
+ details.push(
8159
+ `U+${codePoint.toString(16).toUpperCase().padStart(4, "0")} at index ${index}`
8160
+ );
8161
+ }
8162
+ index += width;
8163
+ }
8164
+ if (details.length === 0) return name;
8165
+ return `"${name}" (contains ${details.join("; ")})`;
8166
+ }
8167
+ function describeRunningEvent(event) {
8168
+ if (event.kind === "TOOL") {
8169
+ return `tool ${formatToolNameForMessage(resolveCanonicalToolName(event))}`;
8170
+ }
8171
+ if (event.kind === "LLM") {
8172
+ return `llm ${stepEndpointName(event, "LLM")}`;
8173
+ }
8174
+ return `${event.kind} ${event.name}`;
8175
+ }
8176
+ function incompleteRunningMessage(prefix, running) {
8177
+ const labels = running.map(describeRunningEvent);
8178
+ if (labels.length === 1) {
8179
+ return `${prefix}: ${labels[0]} never completed.`;
8180
+ }
8181
+ return `${prefix}: ${labels.join("; ")}.`;
8182
+ }
8137
8183
  function stepEndpointName(event, kind) {
8138
8184
  if (kind === "TOOL") return resolveCanonicalToolName(event);
8139
8185
  for (const prefix of ["llm:", "generation:", "transcription:", "speech:"]) {
@@ -8475,10 +8521,13 @@ function createRunStatusRule(options = {}) {
8475
8521
  findings.push(
8476
8522
  failFinding(
8477
8523
  "run.status",
8478
- "Run contains incomplete running events.",
8524
+ incompleteRunningMessage("Run contains incomplete running events", running),
8479
8525
  running.map((event) => eventEvidence3(event)),
8480
8526
  "no running events",
8481
- running.length
8527
+ {
8528
+ count: running.length,
8529
+ unfinished: running.map(describeRunningEvent)
8530
+ }
8482
8531
  )
8483
8532
  );
8484
8533
  }
@@ -8648,6 +8697,34 @@ function createToolUsageRule(options) {
8648
8697
  const names = tools.map(toolName);
8649
8698
  const nameSet = new Set(names);
8650
8699
  const findings = [];
8700
+ const forbidden = new Set(options.forbidden ?? []);
8701
+ const allowed = options.allowed?.length ? new Set(options.allowed) : void 0;
8702
+ for (const event of tools) {
8703
+ const name = toolName(event);
8704
+ const display = formatToolNameForMessage(name);
8705
+ if (forbidden.has(name)) {
8706
+ findings.push(
8707
+ failFinding(
8708
+ "tool.usage",
8709
+ `Forbidden tool ${display} appeared.`,
8710
+ [eventEvidence3(event)],
8711
+ "tool absent",
8712
+ name
8713
+ )
8714
+ );
8715
+ }
8716
+ if (allowed && !allowed.has(name)) {
8717
+ findings.push(
8718
+ failFinding(
8719
+ "tool.usage",
8720
+ `Tool ${display} is not in the allowed tool set.`,
8721
+ [eventEvidence3(event)],
8722
+ [...allowed].sort(),
8723
+ name
8724
+ )
8725
+ );
8726
+ }
8727
+ }
8651
8728
  for (const required of options.required ?? []) {
8652
8729
  if (!nameSet.has(required)) {
8653
8730
  const otherKinds = [
@@ -8665,7 +8742,7 @@ function createToolUsageRule(options) {
8665
8742
  findings.push(
8666
8743
  failFinding(
8667
8744
  "tool.usage",
8668
- `Required tool ${required} did not appear.${hint}`,
8745
+ `Required tool ${formatToolNameForMessage(required)} did not appear.${hint}`,
8669
8746
  runEvidence(context.selectedRun),
8670
8747
  required,
8671
8748
  names
@@ -8673,21 +8750,6 @@ function createToolUsageRule(options) {
8673
8750
  );
8674
8751
  }
8675
8752
  }
8676
- const forbidden = new Set(options.forbidden ?? []);
8677
- const allowed = options.allowed?.length ? new Set(options.allowed) : void 0;
8678
- for (const event of tools) {
8679
- const name = toolName(event);
8680
- if (forbidden.has(name)) {
8681
- findings.push(
8682
- failFinding("tool.usage", `Forbidden tool ${name} appeared.`, [eventEvidence3(event)], "tool absent", name)
8683
- );
8684
- }
8685
- if (allowed && !allowed.has(name)) {
8686
- findings.push(
8687
- failFinding("tool.usage", `Tool ${name} is not in the allowed tool set.`, [eventEvidence3(event)], [...allowed].sort(), name)
8688
- );
8689
- }
8690
- }
8691
8753
  if (options.minCount !== void 0 && tools.length < options.minCount) {
8692
8754
  findings.push(
8693
8755
  failFinding("tool.usage", `Tool count ${tools.length} was below minimum ${options.minCount}.`, runEvidence(context.selectedRun), { minCount: options.minCount }, tools.length)
@@ -8721,6 +8783,28 @@ function createStepOrderingRule(options) {
8721
8783
  const afterEvents = finishedEvents(context, options.after.kind).filter(
8722
8784
  (event) => stepEndpointName(event, options.after.kind) === options.after.name
8723
8785
  );
8786
+ const beforeRunning = semanticEvents(context).filter(
8787
+ (event) => event.kind === options.before.kind && event.status === "running" && stepEndpointName(event, options.before.kind) === options.before.name
8788
+ );
8789
+ if (beforeEvents.length === 0 && afterEvents.length > 0 && beforeRunning.length > 0) {
8790
+ const endpointExpected2 = bothTools ? { before: options.before.name, after: options.after.name } : { before: options.before, after: options.after };
8791
+ return [
8792
+ failFinding(
8793
+ ruleId,
8794
+ `${subject} ${beforeLabel} started but never finished before ${afterLabel} ran.`,
8795
+ [
8796
+ ...beforeRunning.map((event) => eventEvidence3(event)),
8797
+ eventEvidence3(afterEvents[0])
8798
+ ],
8799
+ { ...endpointExpected2, mode },
8800
+ {
8801
+ code: bothTools ? "tool.order.interval-unresolved" : "step.order.interval-unresolved",
8802
+ unfinishedBefore: true,
8803
+ beforeStatus: "running"
8804
+ }
8805
+ )
8806
+ ];
8807
+ }
8724
8808
  if (beforeEvents.length === 0 || afterEvents.length === 0) {
8725
8809
  if (!requireEndpoints) return [];
8726
8810
  const findings = [];
@@ -9097,10 +9181,13 @@ function createStructureIncompleteRule(options = {}) {
9097
9181
  findings.push(
9098
9182
  failFinding(
9099
9183
  "structure.incomplete",
9100
- "Trace contains incomplete running events.",
9184
+ incompleteRunningMessage("Trace contains incomplete running events", running),
9101
9185
  running.map((event) => eventEvidence3(event, "status")),
9102
9186
  "no running events",
9103
- running.length
9187
+ {
9188
+ count: running.length,
9189
+ unfinished: running.map(describeRunningEvent)
9190
+ }
9104
9191
  )
9105
9192
  );
9106
9193
  }
@@ -16084,5 +16171,5 @@ function renderGateReport(result, options = {}) {
16084
16171
  }
16085
16172
 
16086
16173
  export { ATTRIBUTION_CONFIDENCES, COHORT_METRIC_IDS, DEFAULT_REDACT_KEYS, DEFAULT_SUITE_ARTIFACTS_DIR, DELIVERY_CONFLICT_ATTR, DELIVERY_EVENT_BASE_ATTR, EVIDENCE_FORMAT_VERSION, EVIDENCE_HTML_FILENAME, EVIDENCE_MANIFEST_FILENAME, IDENTITY_CONFLICT_ATTR, Redactor, TraceDirectory, TraceReadError, TreeBuilder, aggregateBundleSafeStatus, aggregateSessionCheckResults, analyzeCohort, applyProfileMetadataCaps, assertBundlePathContained, assertEvidenceRelativePath, buildActivitySummary, buildBundleMetadata, buildBundleSummaryMarkdown, buildEvidenceCausalFailureViewHtml, buildEvidenceCiPackage, buildEvidenceCircuitViewHtml, buildEvidenceContractsViewHtml, buildEvidenceDiffViewHtml, buildEvidenceHtmlShell, buildEvidenceManifest, buildEvidenceOutcomesViewHtml, buildEvidenceProvenanceViewHtml, buildEvidenceSafetyViewHtml, buildEvidenceTimelineViewHtml, buildEvidenceToolsLlmViewHtml, buildEvidenceTreeViewHtml, buildLocalExplanation, buildPlaceholderArtifact, buildRunSummary, buildRunTimeline, buildRunWhatSummary, buildSessionIndex, buildTraceStats, buildZipArchive, bundleFailsOnSafety, bundleRunAssetRelativePath, collectTraceSchemaVersions, compactAttributes, createBaselineRegressionRule, createLlmUsageRule, createMaxStepDurationRule, createObservedOutcomeRule, createRequireCompletedRule, createRunDepthRule, createRunDurationRule, createRunStatusRule, createSafetyOversizedAttributeRule, createSafetyRawContentRule, createSafetyRedactionRule, createSafetySecretPatternRule, createStallDetectionRule, createStructureCycleRule, createStructureOrphanRule, createStructureParallelWidthRule, createStructureRelationshipRule, createToolUsageRule, defaultBundleOutputPath, defaultSuiteConfigTemplate, defineTraceContract, diffRuns, diffTraceEvents, enrichSessionRunRecord, escapeHtml, escapeMarkdown, evaluateTraceContract, extractMetadata, extractOutcomesFromTraceEvents, filterMetasBySessionScope, filterTraces, flattenTree, formatDuration2 as formatDuration, formatStepLabel, formatTimestamp, gateHasThresholds, getIndent, getTraceFilePath, inferEvidenceFileRole, isAgentInspectTrace, isAttributionConfidence, isCredentialSensitiveKey, isPersistedInspectEvent, loadSessionRunRecords, loadSuiteConfig, loadTraceMetadataList, manualTraceEventsToComparableRun, nanoid, normalizeBundleOutputPath, openTrace, parseCohortMetricList, parseDuration, parseDurationFilter, parseGateList, parseTraceJsonl, persistedInspectEventsToRunTrees, persistedInspectEventsToTraceEvents, projectLogicalEvents, renderActivitySummaryHuman, renderCohortReport, renderErrorLine, renderGateReport, renderObservedOutcomesHtml, renderObservedOutcomesMarkdown, renderRunDiff, renderRunWhat, renderStepLine, renderSuiteReport, renderTimeline, renderTraceStats, resolveBundleRunIds, resolveRedactionProfile, resolveSuiteTemplate, resolveTraceDir, runGate, runSuite, runTraceChecks, safeString, sanitizeBundleRunId, searchTraces, serializeEvidenceManifest, sha256Hex, stableJson, stringContainsHighConfidenceCredential, summarizeObservedOutcomes, summarizeSemanticParity, traceEventToPersistedInspectEvent, truncateName, truncateStringForProfile, validateEvent, validateSuiteConfig, verifyEvidenceDirectory, zeroKinds };
16087
- //# sourceMappingURL=chunk-PEGM6T2X.mjs.map
16088
- //# sourceMappingURL=chunk-PEGM6T2X.mjs.map
16174
+ //# sourceMappingURL=chunk-AILSHDEF.mjs.map
16175
+ //# sourceMappingURL=chunk-AILSHDEF.mjs.map