agent-inspect 6.31.31 → 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.
Files changed (47) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +1 -1
  3. package/docs/CLI.md +3 -1
  4. package/docs/TRACE-CONTRACTS.md +4 -0
  5. package/package.json +1 -1
  6. package/packages/cli/dist/{chunk-4GTOJ3G2.mjs → chunk-AILSHDEF.mjs} +191 -31
  7. package/packages/cli/dist/chunk-AILSHDEF.mjs.map +1 -0
  8. package/packages/cli/dist/index.cjs +209 -33
  9. package/packages/cli/dist/index.cjs.map +1 -1
  10. package/packages/cli/dist/index.mjs +23 -7
  11. package/packages/cli/dist/index.mjs.map +1 -1
  12. package/packages/cli/dist/{src-BF74ZVDF.mjs → src-SP36M4VB.mjs} +3 -3
  13. package/packages/cli/dist/{src-BF74ZVDF.mjs.map → src-SP36M4VB.mjs.map} +1 -1
  14. package/packages/core/dist/advanced.cjs +80 -18
  15. package/packages/core/dist/advanced.cjs.map +1 -1
  16. package/packages/core/dist/advanced.d.cts +2 -2
  17. package/packages/core/dist/advanced.d.ts +2 -2
  18. package/packages/core/dist/advanced.mjs +9 -9
  19. package/packages/core/dist/checks.cjs +185 -29
  20. package/packages/core/dist/checks.cjs.map +1 -1
  21. package/packages/core/dist/checks.d.cts +2 -2
  22. package/packages/core/dist/checks.d.ts +2 -2
  23. package/packages/core/dist/checks.mjs +3 -3
  24. package/packages/core/dist/{chunk-76QIWWJ5.mjs → chunk-6S666BF4.mjs} +3 -3
  25. package/packages/core/dist/{chunk-76QIWWJ5.mjs.map → chunk-6S666BF4.mjs.map} +1 -1
  26. package/packages/core/dist/{chunk-BYNAJPNH.mjs → chunk-EMDJLFRK.mjs} +188 -32
  27. package/packages/core/dist/chunk-EMDJLFRK.mjs.map +1 -0
  28. package/packages/core/dist/{chunk-QMO3ITYF.mjs → chunk-FBPDF7HO.mjs} +3 -3
  29. package/packages/core/dist/{chunk-QMO3ITYF.mjs.map → chunk-FBPDF7HO.mjs.map} +1 -1
  30. package/packages/core/dist/{chunk-XM27TZLD.mjs → chunk-RB6WANSW.mjs} +6 -2
  31. package/packages/core/dist/chunk-RB6WANSW.mjs.map +1 -0
  32. package/packages/core/dist/{chunk-JTJN4KBJ.mjs → chunk-VIEJJ5JB.mjs} +4 -4
  33. package/packages/core/dist/{chunk-JTJN4KBJ.mjs.map → chunk-VIEJJ5JB.mjs.map} +1 -1
  34. package/packages/core/dist/exporters.cjs +4 -0
  35. package/packages/core/dist/exporters.cjs.map +1 -1
  36. package/packages/core/dist/exporters.mjs +3 -3
  37. package/packages/core/dist/{index-DACU4Dy1.d.cts → index-Ceyw5x7b.d.cts} +4 -2
  38. package/packages/core/dist/{index-b4tIGrzq.d.ts → index-K5Cinf1r.d.ts} +4 -2
  39. package/packages/core/dist/index.cjs +4 -0
  40. package/packages/core/dist/index.cjs.map +1 -1
  41. package/packages/core/dist/index.mjs +4 -4
  42. package/packages/core/dist/writers.cjs +4 -0
  43. package/packages/core/dist/writers.cjs.map +1 -1
  44. package/packages/core/dist/writers.mjs +1 -1
  45. package/packages/cli/dist/chunk-4GTOJ3G2.mjs.map +0 -1
  46. package/packages/core/dist/chunk-BYNAJPNH.mjs.map +0 -1
  47. package/packages/core/dist/chunk-XM27TZLD.mjs.map +0 -1
@@ -1,4 +1,4 @@
1
- import { OBSERVED_OUTCOME_METHODS, extractOutcomesFromPersistedEvents, outcomesMatchingStatus } from './chunk-QMO3ITYF.mjs';
1
+ import { OBSERVED_OUTCOME_METHODS, extractOutcomesFromPersistedEvents, outcomesMatchingStatus } from './chunk-FBPDF7HO.mjs';
2
2
  import { DEFAULT_CREDENTIAL_SENSITIVE_KEYS, isCredentialSensitiveKey } from './chunk-VLVCLAPB.mjs';
3
3
  import { projectLogicalEvents, resolveCanonicalToolName } from './chunk-AHUZNHFI.mjs';
4
4
  import { formatProgrammaticDiagnostic } from './chunk-S6LQBLX5.mjs';
@@ -2345,10 +2345,17 @@ function contractToRules(contract) {
2345
2345
  (event) => event.status === "running"
2346
2346
  );
2347
2347
  if (running.length > 0) {
2348
+ const labels = running.map((event) => {
2349
+ if (event.kind === "TOOL") {
2350
+ return `tool ${resolveCanonicalToolName(event)}`;
2351
+ }
2352
+ return `${event.kind} ${event.name}`;
2353
+ });
2354
+ const message = labels.length === 1 ? `Run contains incomplete running events: ${labels[0]} never completed.` : `Run contains incomplete running events: ${labels.join("; ")}.`;
2348
2355
  findings.push(
2349
2356
  contractFailFinding(
2350
2357
  "contract.run.allowedStatuses",
2351
- "Run contains incomplete running events.",
2358
+ message,
2352
2359
  running.map((event) => ({
2353
2360
  runId: event.runId,
2354
2361
  eventId: event.eventId,
@@ -2357,7 +2364,10 @@ function contractToRules(contract) {
2357
2364
  status: event.status
2358
2365
  })),
2359
2366
  "no running events",
2360
- running.length
2367
+ {
2368
+ count: running.length,
2369
+ unfinished: labels
2370
+ }
2361
2371
  )
2362
2372
  );
2363
2373
  }
@@ -3307,6 +3317,12 @@ var SAFE_USAGE_LEAF_KEYS = /* @__PURE__ */ new Set([
3307
3317
  var DEFAULT_SECRET_PATTERNS = [
3308
3318
  { id: "bearer-token", pattern: /Bearer\s+[A-Za-z0-9._~+/-]{12,}=*/ },
3309
3319
  { id: "openai-key", pattern: /sk-[A-Za-z0-9_-]{16,}/ },
3320
+ // Stripe + Slack: keep in sync with packages/redact + credential-value-patterns (#520).
3321
+ {
3322
+ id: "stripe-key",
3323
+ pattern: /\b(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{16,}\b/
3324
+ },
3325
+ { id: "slack-token", pattern: /\bxox[abposr]-[A-Za-z0-9-]{10,}\b/ },
3310
3326
  { id: "aws-access-key", pattern: /AKIA[0-9A-Z]{16}/ },
3311
3327
  { id: "github-token", pattern: /gh[opsu]_[A-Za-z0-9_]{20,}/ },
3312
3328
  // Keep in sync with packages/redact/src/key-value-secret.ts (KEY_VALUE_SECRET_PATTERN_SOURCE).
@@ -3488,6 +3504,11 @@ function compareFindings(eventById) {
3488
3504
  if (STATUS_RANK[a.status] !== STATUS_RANK[b.status]) {
3489
3505
  return STATUS_RANK[a.status] - STATUS_RANK[b.status];
3490
3506
  }
3507
+ if (a.ruleId === "tool.usage") {
3508
+ const aHasEvent = a.evidence[0]?.eventId !== void 0;
3509
+ const bHasEvent = b.evidence[0]?.eventId !== void 0;
3510
+ if (aHasEvent !== bHasEvent) return aHasEvent ? -1 : 1;
3511
+ }
3491
3512
  const byRun = compareStrings(a.evidence[0]?.runId, b.evidence[0]?.runId);
3492
3513
  if (byRun !== 0) return byRun;
3493
3514
  const byTime = eventTimestamp(a, eventById).localeCompare(eventTimestamp(b, eventById));
@@ -3613,6 +3634,37 @@ function failFinding(ruleId, message, evidence, expected, actual, meta) {
3613
3634
  function toolName(event) {
3614
3635
  return resolveCanonicalToolName(event);
3615
3636
  }
3637
+ function formatToolNameForMessage(name) {
3638
+ const details = [];
3639
+ for (let index = 0; index < name.length; ) {
3640
+ const codePoint = name.codePointAt(index);
3641
+ const width = codePoint > 65535 ? 2 : 1;
3642
+ if (codePoint > 127) {
3643
+ details.push(
3644
+ `U+${codePoint.toString(16).toUpperCase().padStart(4, "0")} at index ${index}`
3645
+ );
3646
+ }
3647
+ index += width;
3648
+ }
3649
+ if (details.length === 0) return name;
3650
+ return `"${name}" (contains ${details.join("; ")})`;
3651
+ }
3652
+ function describeRunningEvent(event) {
3653
+ if (event.kind === "TOOL") {
3654
+ return `tool ${formatToolNameForMessage(resolveCanonicalToolName(event))}`;
3655
+ }
3656
+ if (event.kind === "LLM") {
3657
+ return `llm ${stepEndpointName(event, "LLM")}`;
3658
+ }
3659
+ return `${event.kind} ${event.name}`;
3660
+ }
3661
+ function incompleteRunningMessage(prefix, running) {
3662
+ const labels = running.map(describeRunningEvent);
3663
+ if (labels.length === 1) {
3664
+ return `${prefix}: ${labels[0]} never completed.`;
3665
+ }
3666
+ return `${prefix}: ${labels.join("; ")}.`;
3667
+ }
3616
3668
  function stepEndpointName(event, kind) {
3617
3669
  if (kind === "TOOL") return resolveCanonicalToolName(event);
3618
3670
  for (const prefix of ["llm:", "generation:", "transcription:", "speech:"]) {
@@ -3687,6 +3739,56 @@ function eventEndMs(event) {
3687
3739
  }
3688
3740
  return void 0;
3689
3741
  }
3742
+ function rowIndexByEventId(events) {
3743
+ const index = /* @__PURE__ */ new Map();
3744
+ for (const [position, event] of events.entries()) {
3745
+ if (!index.has(event.eventId)) index.set(event.eventId, position);
3746
+ }
3747
+ return index;
3748
+ }
3749
+ function recordedRowSpan(rowIndex, event) {
3750
+ const sourceIds = "sourceEventIds" in event && Array.isArray(event.sourceEventIds) ? event.sourceEventIds : [event.eventId];
3751
+ const rows = [];
3752
+ for (const id of sourceIds) {
3753
+ const row = typeof id === "string" ? rowIndex.get(id) : void 0;
3754
+ if (row === void 0) return void 0;
3755
+ rows.push(row);
3756
+ }
3757
+ if (rows.length === 0) return void 0;
3758
+ return { first: Math.min(...rows), last: Math.max(...rows) };
3759
+ }
3760
+ function recordedTieOrder(context, tiedBefore, tiedAfter) {
3761
+ const rowIndex = rowIndexByEventId(context.events);
3762
+ let lastFinished;
3763
+ let firstStarted;
3764
+ for (const event of tiedBefore) {
3765
+ const span = recordedRowSpan(rowIndex, event);
3766
+ if (span === void 0) return { inOrder: void 0 };
3767
+ if (lastFinished === void 0 || span.last > lastFinished.row) {
3768
+ lastFinished = { event, row: span.last };
3769
+ }
3770
+ }
3771
+ for (const event of tiedAfter) {
3772
+ const span = recordedRowSpan(rowIndex, event);
3773
+ if (span === void 0) return { inOrder: void 0 };
3774
+ if (firstStarted === void 0 || span.first < firstStarted.row) {
3775
+ firstStarted = { event, row: span.first };
3776
+ }
3777
+ }
3778
+ if (lastFinished === void 0 || firstStarted === void 0) return { inOrder: void 0 };
3779
+ return {
3780
+ inOrder: lastFinished.row < firstStarted.row,
3781
+ lastFinishedBefore: lastFinished.event,
3782
+ firstStartedAfter: firstStarted.event
3783
+ };
3784
+ }
3785
+ function tieResolution(order) {
3786
+ return order.inOrder === void 0 ? "recorded-order-unavailable" : "recorded-order";
3787
+ }
3788
+ function tieMessage(order, beforeLabel, afterLabel) {
3789
+ if (order === void 0 || order.inOrder === true) return "";
3790
+ return order.inOrder === void 0 ? " Their timestamps tie at the same millisecond and the recorded event order could not be resolved." : ` Their timestamps tie at the same millisecond; the recorded event order shows ${afterLabel} started before ${beforeLabel} finished.`;
3791
+ }
3690
3792
  function sortedStrings(values) {
3691
3793
  return [...values ?? []].sort((a, b) => a.localeCompare(b));
3692
3794
  }
@@ -3907,10 +4009,13 @@ function createRunStatusRule(options = {}) {
3907
4009
  findings.push(
3908
4010
  failFinding(
3909
4011
  "run.status",
3910
- "Run contains incomplete running events.",
4012
+ incompleteRunningMessage("Run contains incomplete running events", running),
3911
4013
  running.map((event) => eventEvidence3(event)),
3912
4014
  "no running events",
3913
- running.length
4015
+ {
4016
+ count: running.length,
4017
+ unfinished: running.map(describeRunningEvent)
4018
+ }
3914
4019
  )
3915
4020
  );
3916
4021
  }
@@ -4116,6 +4221,34 @@ function createToolUsageRule(options) {
4116
4221
  const names = tools.map(toolName);
4117
4222
  const nameSet = new Set(names);
4118
4223
  const findings = [];
4224
+ const forbidden = new Set(options.forbidden ?? []);
4225
+ const allowed = options.allowed?.length ? new Set(options.allowed) : void 0;
4226
+ for (const event of tools) {
4227
+ const name = toolName(event);
4228
+ const display = formatToolNameForMessage(name);
4229
+ if (forbidden.has(name)) {
4230
+ findings.push(
4231
+ failFinding(
4232
+ "tool.usage",
4233
+ `Forbidden tool ${display} appeared.`,
4234
+ [eventEvidence3(event)],
4235
+ "tool absent",
4236
+ name
4237
+ )
4238
+ );
4239
+ }
4240
+ if (allowed && !allowed.has(name)) {
4241
+ findings.push(
4242
+ failFinding(
4243
+ "tool.usage",
4244
+ `Tool ${display} is not in the allowed tool set.`,
4245
+ [eventEvidence3(event)],
4246
+ [...allowed].sort(),
4247
+ name
4248
+ )
4249
+ );
4250
+ }
4251
+ }
4119
4252
  for (const required of options.required ?? []) {
4120
4253
  if (!nameSet.has(required)) {
4121
4254
  const otherKinds = [
@@ -4133,7 +4266,7 @@ function createToolUsageRule(options) {
4133
4266
  findings.push(
4134
4267
  failFinding(
4135
4268
  "tool.usage",
4136
- `Required tool ${required} did not appear.${hint}`,
4269
+ `Required tool ${formatToolNameForMessage(required)} did not appear.${hint}`,
4137
4270
  runEvidence(context.selectedRun),
4138
4271
  required,
4139
4272
  names
@@ -4141,21 +4274,6 @@ function createToolUsageRule(options) {
4141
4274
  );
4142
4275
  }
4143
4276
  }
4144
- const forbidden = new Set(options.forbidden ?? []);
4145
- const allowed = options.allowed?.length ? new Set(options.allowed) : void 0;
4146
- for (const event of tools) {
4147
- const name = toolName(event);
4148
- if (forbidden.has(name)) {
4149
- findings.push(
4150
- failFinding("tool.usage", `Forbidden tool ${name} appeared.`, [eventEvidence3(event)], "tool absent", name)
4151
- );
4152
- }
4153
- if (allowed && !allowed.has(name)) {
4154
- findings.push(
4155
- failFinding("tool.usage", `Tool ${name} is not in the allowed tool set.`, [eventEvidence3(event)], [...allowed].sort(), name)
4156
- );
4157
- }
4158
- }
4159
4277
  if (options.minCount !== void 0 && tools.length < options.minCount) {
4160
4278
  findings.push(
4161
4279
  failFinding("tool.usage", `Tool count ${tools.length} was below minimum ${options.minCount}.`, runEvidence(context.selectedRun), { minCount: options.minCount }, tools.length)
@@ -4189,6 +4307,28 @@ function createStepOrderingRule(options) {
4189
4307
  const afterEvents = finishedEvents(context, options.after.kind).filter(
4190
4308
  (event) => stepEndpointName(event, options.after.kind) === options.after.name
4191
4309
  );
4310
+ const beforeRunning = semanticEvents(context).filter(
4311
+ (event) => event.kind === options.before.kind && event.status === "running" && stepEndpointName(event, options.before.kind) === options.before.name
4312
+ );
4313
+ if (beforeEvents.length === 0 && afterEvents.length > 0 && beforeRunning.length > 0) {
4314
+ const endpointExpected2 = bothTools ? { before: options.before.name, after: options.after.name } : { before: options.before, after: options.after };
4315
+ return [
4316
+ failFinding(
4317
+ ruleId,
4318
+ `${subject} ${beforeLabel} started but never finished before ${afterLabel} ran.`,
4319
+ [
4320
+ ...beforeRunning.map((event) => eventEvidence3(event)),
4321
+ eventEvidence3(afterEvents[0])
4322
+ ],
4323
+ { ...endpointExpected2, mode },
4324
+ {
4325
+ code: bothTools ? "tool.order.interval-unresolved" : "step.order.interval-unresolved",
4326
+ unfinishedBefore: true,
4327
+ beforeStatus: "running"
4328
+ }
4329
+ )
4330
+ ];
4331
+ }
4192
4332
  if (beforeEvents.length === 0 || afterEvents.length === 0) {
4193
4333
  if (!requireEndpoints) return [];
4194
4334
  const findings = [];
@@ -4305,16 +4445,18 @@ function createStepOrderingRule(options) {
4305
4445
  )
4306
4446
  ];
4307
4447
  }
4308
- if (beforeEnd > afterStart) {
4448
+ const tie = beforeEnd === afterStart ? recordedTieOrder(context, [beforeEvent], [afterEvent]) : void 0;
4449
+ if (beforeEnd > afterStart || tie !== void 0 && tie.inOrder !== true) {
4309
4450
  return [
4310
4451
  failFinding(
4311
4452
  ruleId,
4312
- `${subject} ${beforeLabel} must finish before ${afterLabel} starts.`,
4453
+ `${subject} ${beforeLabel} must finish before ${afterLabel} starts.${tieMessage(tie, beforeLabel, afterLabel)}`,
4313
4454
  [eventEvidence3(beforeEvent), eventEvidence3(afterEvent)],
4314
4455
  expected,
4315
4456
  {
4316
4457
  beforeEndedAt: new Date(beforeEnd).toISOString(),
4317
- afterStartedAt: new Date(afterStart).toISOString()
4458
+ afterStartedAt: new Date(afterStart).toISOString(),
4459
+ ...tie !== void 0 ? { resolvedBy: tieResolution(tie) } : {}
4318
4460
  }
4319
4461
  )
4320
4462
  ];
@@ -4377,16 +4519,27 @@ function createStepOrderingRule(options) {
4377
4519
  )
4378
4520
  ];
4379
4521
  }
4380
- if (latestBefore.end > earliestAfter.start) {
4522
+ const tiedEnd = latestBefore.end;
4523
+ const tiedStart = earliestAfter.start;
4524
+ const tie = tiedEnd === tiedStart ? recordedTieOrder(
4525
+ context,
4526
+ beforeEvents.filter((event) => eventEndMs(event) === tiedEnd),
4527
+ afterEvents.filter((event) => eventStartMs(event) === tiedStart)
4528
+ ) : void 0;
4529
+ if (latestBefore.end > earliestAfter.start || tie !== void 0 && tie.inOrder !== true) {
4381
4530
  return [
4382
4531
  failFinding(
4383
4532
  ruleId,
4384
- bothTools ? `Every ${options.before.name} tool call must finish before any ${options.after.name} tool call starts.` : `Every ${beforeLabel} must finish before any ${afterLabel} starts.`,
4385
- [eventEvidence3(latestBefore.event), eventEvidence3(earliestAfter.event)],
4533
+ (bothTools ? `Every ${options.before.name} tool call must finish before any ${options.after.name} tool call starts.` : `Every ${beforeLabel} must finish before any ${afterLabel} starts.`) + tieMessage(tie, beforeLabel, afterLabel),
4534
+ [
4535
+ eventEvidence3(tie?.lastFinishedBefore ?? latestBefore.event),
4536
+ eventEvidence3(tie?.firstStartedAfter ?? earliestAfter.event)
4537
+ ],
4386
4538
  expected,
4387
4539
  {
4388
4540
  latestBeforeEndedAt: new Date(latestBefore.end).toISOString(),
4389
- earliestAfterStartedAt: new Date(earliestAfter.start).toISOString()
4541
+ earliestAfterStartedAt: new Date(earliestAfter.start).toISOString(),
4542
+ ...tie !== void 0 ? { resolvedBy: tieResolution(tie) } : {}
4390
4543
  }
4391
4544
  )
4392
4545
  ];
@@ -4552,10 +4705,13 @@ function createStructureIncompleteRule(options = {}) {
4552
4705
  findings.push(
4553
4706
  failFinding(
4554
4707
  "structure.incomplete",
4555
- "Trace contains incomplete running events.",
4708
+ incompleteRunningMessage("Trace contains incomplete running events", running),
4556
4709
  running.map((event) => eventEvidence3(event, "status")),
4557
4710
  "no running events",
4558
- running.length
4711
+ {
4712
+ count: running.length,
4713
+ unfinished: running.map(describeRunningEvent)
4714
+ }
4559
4715
  )
4560
4716
  );
4561
4717
  }
@@ -5349,5 +5505,5 @@ function runTraceChecks(input, options = {}) {
5349
5505
  }
5350
5506
 
5351
5507
  export { buildTraceFacts, createBaselineRegressionRule, createDecisionRule, createGuardrailRule, createLlmUsageRule, createMaxStepDurationRule, createObservedOutcomeRule, createRequireCompletedRule, createRetrievalRule, createRunDepthRule, createRunDurationRule, createRunEventCountRule, createRunStatusRule, createSafetyOversizedAttributeRule, createSafetyRawContentRule, createSafetyRedactionRule, createSafetySecretPatternRule, createStallDetectionRule, createStepOrderingRule, createStructureCycleRule, createStructureIncompleteRule, createStructureOrphanRule, createStructureParallelWidthRule, createStructureRelationshipRule, createToolFailureRule, createToolOrderingRule, createToolUsageRule, defineTraceContract, deriveFailureFacts, deriveRelationshipFacts, evaluateToolArgumentValue, evaluateTraceContract, evaluateTraceContractRead, explainTraceContract, extractSessionWorkflowMetadata, extractToolArgumentPayload, lintTraceContract, resolveJsonPointer, resolveTraceContractScope, runTraceChecks, sessionKeyForRun, summarizeSemanticParity, workflowMetadataForRun };
5352
- //# sourceMappingURL=chunk-BYNAJPNH.mjs.map
5353
- //# sourceMappingURL=chunk-BYNAJPNH.mjs.map
5508
+ //# sourceMappingURL=chunk-EMDJLFRK.mjs.map
5509
+ //# sourceMappingURL=chunk-EMDJLFRK.mjs.map