agent-inspect 6.30.0 → 6.31.1

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 (30) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +1 -1
  3. package/docs/COMPARE.md +1 -1
  4. package/docs/FIRST-TRACE-IN-5-MINUTES.md +21 -17
  5. package/docs/GETTING-STARTED.md +1 -1
  6. package/docs/TRACE-CONTRACTS.md +25 -0
  7. package/package.json +4 -2
  8. package/packages/cli/dist/{chunk-BDRJENLM.mjs → chunk-SJ6FRNOE.mjs} +201 -49
  9. package/packages/cli/dist/chunk-SJ6FRNOE.mjs.map +1 -0
  10. package/packages/cli/dist/index.cjs +288 -50
  11. package/packages/cli/dist/index.cjs.map +1 -1
  12. package/packages/cli/dist/index.mjs +91 -5
  13. package/packages/cli/dist/index.mjs.map +1 -1
  14. package/packages/cli/dist/{src-ZUGAUXY6.mjs → src-YNEJOPRA.mjs} +3 -3
  15. package/packages/cli/dist/{src-ZUGAUXY6.mjs.map → src-YNEJOPRA.mjs.map} +1 -1
  16. package/packages/core/dist/advanced.cjs +39 -1
  17. package/packages/core/dist/advanced.cjs.map +1 -1
  18. package/packages/core/dist/advanced.d.cts +2 -2
  19. package/packages/core/dist/advanced.d.ts +2 -2
  20. package/packages/core/dist/advanced.mjs +2 -2
  21. package/packages/core/dist/checks.cjs +207 -47
  22. package/packages/core/dist/checks.cjs.map +1 -1
  23. package/packages/core/dist/checks.d.cts +2 -2
  24. package/packages/core/dist/checks.d.ts +2 -2
  25. package/packages/core/dist/checks.mjs +1 -1
  26. package/packages/core/dist/{chunk-OOX25GTH.mjs → chunk-CBPWLHNQ.mjs} +209 -50
  27. package/packages/core/dist/{chunk-OOX25GTH.mjs.map → chunk-CBPWLHNQ.mjs.map} +1 -1
  28. package/packages/core/dist/{index-7hG_qggp.d.ts → index-CgOAGuM5.d.ts} +111 -1
  29. package/packages/core/dist/{index-CQJUNPPe.d.cts → index-EQihlEei.d.cts} +111 -1
  30. package/packages/cli/dist/chunk-BDRJENLM.mjs.map +0 -1
@@ -5700,6 +5700,31 @@ function failFinding(ruleId, message, evidence, expected, actual, meta2) {
5700
5700
  function toolName(event) {
5701
5701
  return resolveCanonicalToolName(event);
5702
5702
  }
5703
+ function stepEndpointName(event, kind) {
5704
+ if (kind === "TOOL") return resolveCanonicalToolName(event);
5705
+ for (const prefix of ["llm:", "generation:", "transcription:", "speech:"]) {
5706
+ if (event.name.startsWith(prefix)) return event.name.slice(prefix.length);
5707
+ }
5708
+ return event.name;
5709
+ }
5710
+ function formatStepEndpoint(endpoint) {
5711
+ return `${endpoint.kind} ${endpoint.name}`;
5712
+ }
5713
+ function otherKindsForStepName(context, expectedKind, name) {
5714
+ return [
5715
+ ...new Set(
5716
+ semanticEvents(context).filter((event) => {
5717
+ const kind = typeof event.kind === "string" ? event.kind.toUpperCase() : "";
5718
+ if (kind === expectedKind) return false;
5719
+ if (kind === "TOOL") return resolveCanonicalToolName(event) === name;
5720
+ if (kind === "LLM") return stepEndpointName(event, "LLM") === name;
5721
+ return event.name === name || event.name === `tool:${name}` || event.name === `llm:${name}` || event.name.endsWith(`:${name}`);
5722
+ }).map(
5723
+ (event) => typeof event.kind === "string" ? event.kind.toUpperCase() : "UNKNOWN"
5724
+ )
5725
+ )
5726
+ ].sort();
5727
+ }
5703
5728
  function semanticEvents(context) {
5704
5729
  return context.logicalEvents ?? context.events;
5705
5730
  }
@@ -5726,14 +5751,6 @@ function finishedEvents(context, kind) {
5726
5751
  function toolInvocationEvents(context) {
5727
5752
  return semanticEvents(context).filter((event) => event.kind === "TOOL");
5728
5753
  }
5729
- function firstIndexByName(events) {
5730
- const index = /* @__PURE__ */ new Map();
5731
- for (const [position, event] of events.entries()) {
5732
- const name = toolName(event);
5733
- if (!index.has(name)) index.set(name, position);
5734
- }
5735
- return index;
5736
- }
5737
5754
  function isRecord9(value) {
5738
5755
  return typeof value === "object" && value !== null && !Array.isArray(value);
5739
5756
  }
@@ -6204,18 +6221,85 @@ function createToolUsageRule(options) {
6204
6221
  }
6205
6222
  };
6206
6223
  }
6207
- function createToolOrderingRule(options) {
6208
- const ruleId = options.id ?? "tool.order";
6224
+ function createStepOrderingRule(options) {
6225
+ const bothTools = options.before.kind === "TOOL" && options.after.kind === "TOOL";
6226
+ const ruleId = options.id ?? (bothTools ? "tool.order" : "step.order");
6209
6227
  const mode = options.mode ?? "first-occurrence";
6228
+ const requireEndpoints = options.requireEndpoints === true;
6229
+ const beforeLabel = bothTools ? options.before.name : formatStepEndpoint(options.before);
6230
+ const afterLabel = bothTools ? options.after.name : formatStepEndpoint(options.after);
6231
+ const subject = bothTools ? "Tool" : "Step";
6210
6232
  return {
6211
6233
  id: ruleId,
6212
- category: "tool",
6234
+ category: bothTools ? "tool" : "structure",
6213
6235
  defaultSeverity: "error",
6214
6236
  evaluate(context) {
6215
- const tools = finishedEvents(context, "TOOL");
6216
- const index = firstIndexByName(tools);
6217
- const beforeIndex = index.get(options.before);
6218
- const afterIndex = index.get(options.after);
6237
+ const beforeEvents = finishedEvents(context, options.before.kind).filter(
6238
+ (event) => stepEndpointName(event, options.before.kind) === options.before.name
6239
+ );
6240
+ const afterEvents = finishedEvents(context, options.after.kind).filter(
6241
+ (event) => stepEndpointName(event, options.after.kind) === options.after.name
6242
+ );
6243
+ if (beforeEvents.length === 0 || afterEvents.length === 0) {
6244
+ if (!requireEndpoints) return [];
6245
+ const findings = [];
6246
+ if (beforeEvents.length === 0) {
6247
+ const otherKinds = otherKindsForStepName(
6248
+ context,
6249
+ options.before.kind,
6250
+ options.before.name
6251
+ );
6252
+ const hint = otherKinds.length > 0 ? ` Required name appears under non-${options.before.kind} kind(s): ${otherKinds.join(", ")}.` : "";
6253
+ findings.push(
6254
+ failFinding(
6255
+ ruleId,
6256
+ `Required ${formatStepEndpoint(options.before)} did not appear.${hint}`,
6257
+ runEvidence(context.selectedRun),
6258
+ options.before,
6259
+ {
6260
+ code: bothTools ? "tool.order.missing-endpoint" : "step.order.missing-endpoint",
6261
+ endpoint: "before",
6262
+ otherKinds
6263
+ }
6264
+ )
6265
+ );
6266
+ }
6267
+ if (afterEvents.length === 0) {
6268
+ const otherKinds = otherKindsForStepName(
6269
+ context,
6270
+ options.after.kind,
6271
+ options.after.name
6272
+ );
6273
+ const hint = otherKinds.length > 0 ? ` Required name appears under non-${options.after.kind} kind(s): ${otherKinds.join(", ")}.` : "";
6274
+ findings.push(
6275
+ failFinding(
6276
+ ruleId,
6277
+ `Required ${formatStepEndpoint(options.after)} did not appear.${hint}`,
6278
+ runEvidence(context.selectedRun),
6279
+ options.after,
6280
+ {
6281
+ code: bothTools ? "tool.order.missing-endpoint" : "step.order.missing-endpoint",
6282
+ endpoint: "after",
6283
+ otherKinds
6284
+ }
6285
+ )
6286
+ );
6287
+ }
6288
+ return findings;
6289
+ }
6290
+ const encounter = finishedEvents(context).filter(
6291
+ (event) => event.kind === options.before.kind && stepEndpointName(event, options.before.kind) === options.before.name || event.kind === options.after.kind && stepEndpointName(event, options.after.kind) === options.after.name
6292
+ );
6293
+ let beforeIndex;
6294
+ let afterIndex;
6295
+ for (const [position, event] of encounter.entries()) {
6296
+ if (beforeIndex === void 0 && event.kind === options.before.kind && stepEndpointName(event, options.before.kind) === options.before.name) {
6297
+ beforeIndex = position;
6298
+ }
6299
+ if (afterIndex === void 0 && event.kind === options.after.kind && stepEndpointName(event, options.after.kind) === options.after.name) {
6300
+ afterIndex = position;
6301
+ }
6302
+ }
6219
6303
  if (beforeIndex === void 0 || afterIndex === void 0) {
6220
6304
  return [];
6221
6305
  }
@@ -6223,32 +6307,37 @@ function createToolOrderingRule(options) {
6223
6307
  return [
6224
6308
  failFinding(
6225
6309
  ruleId,
6226
- `Tool ${options.before} must appear before ${options.after}.`,
6227
- [eventEvidence(tools[beforeIndex]), eventEvidence(tools[afterIndex])],
6228
- { before: options.before, after: options.after },
6229
- tools.map(toolName)
6310
+ `${subject} ${beforeLabel} must appear before ${afterLabel}.`,
6311
+ [
6312
+ eventEvidence(encounter[beforeIndex]),
6313
+ eventEvidence(encounter[afterIndex])
6314
+ ],
6315
+ bothTools ? { before: options.before.name, after: options.after.name } : { before: options.before, after: options.after },
6316
+ bothTools ? finishedEvents(context, "TOOL").map(toolName) : encounter.map(
6317
+ (event) => event.kind === "TOOL" || event.kind === "LLM" ? `${event.kind}:${stepEndpointName(event, event.kind)}` : event.name
6318
+ )
6230
6319
  )
6231
6320
  ];
6232
6321
  }
6233
- const beforeEvent = tools[beforeIndex];
6234
- const afterEvent = tools[afterIndex];
6322
+ const beforeEvent = beforeEvents[0];
6323
+ const afterEvent = afterEvents[0];
6235
6324
  const beforeEnd = eventEndMs(beforeEvent);
6236
6325
  const afterStart = eventStartMs(afterEvent);
6326
+ const endpointExpected = bothTools ? { before: options.before.name, after: options.after.name } : { before: options.before, after: options.after };
6237
6327
  if (mode === "happens-before") {
6238
6328
  const expected = {
6239
- before: options.before,
6240
- after: options.after,
6329
+ ...endpointExpected,
6241
6330
  mode,
6242
6331
  relation: "before.end <= after.start"
6243
6332
  };
6244
- if (options.before === options.after) {
6333
+ if (options.before.kind === options.after.kind && options.before.name === options.after.name) {
6245
6334
  return [
6246
6335
  failFinding(
6247
6336
  ruleId,
6248
- `Tool ${options.before} cannot causally happen before itself.`,
6337
+ `${subject} ${beforeLabel} cannot causally happen before itself.`,
6249
6338
  [eventEvidence(beforeEvent)],
6250
6339
  expected,
6251
- { code: "tool.order.same-tool" }
6340
+ { code: bothTools ? "tool.order.same-tool" : "step.order.same-step" }
6252
6341
  )
6253
6342
  ];
6254
6343
  }
@@ -6256,11 +6345,11 @@ function createToolOrderingRule(options) {
6256
6345
  return [
6257
6346
  failFinding(
6258
6347
  ruleId,
6259
- `Tool order ${options.before} before ${options.after} could not establish causal timing.`,
6348
+ `${subject} order ${beforeLabel} before ${afterLabel} could not establish causal timing.`,
6260
6349
  [eventEvidence(beforeEvent), eventEvidence(afterEvent)],
6261
6350
  expected,
6262
6351
  {
6263
- code: "tool.order.interval-unresolved",
6352
+ code: bothTools ? "tool.order.interval-unresolved" : "step.order.interval-unresolved",
6264
6353
  beforeEndResolved: beforeEnd !== void 0,
6265
6354
  afterStartResolved: afterStart !== void 0
6266
6355
  }
@@ -6271,7 +6360,7 @@ function createToolOrderingRule(options) {
6271
6360
  return [
6272
6361
  failFinding(
6273
6362
  ruleId,
6274
- `Tool ${options.before} must finish before ${options.after} starts.`,
6363
+ `${subject} ${beforeLabel} must finish before ${afterLabel} starts.`,
6275
6364
  [eventEvidence(beforeEvent), eventEvidence(afterEvent)],
6276
6365
  expected,
6277
6366
  {
@@ -6284,22 +6373,19 @@ function createToolOrderingRule(options) {
6284
6373
  return [];
6285
6374
  }
6286
6375
  if (mode === "all-occurrences") {
6287
- const beforeEvents = tools.filter((event) => toolName(event) === options.before);
6288
- const afterEvents = tools.filter((event) => toolName(event) === options.after);
6289
6376
  const expected = {
6290
- before: options.before,
6291
- after: options.after,
6377
+ ...endpointExpected,
6292
6378
  mode,
6293
6379
  relation: "max(before.end) <= min(after.start)"
6294
6380
  };
6295
- if (options.before === options.after) {
6381
+ if (options.before.kind === options.after.kind && options.before.name === options.after.name) {
6296
6382
  return [
6297
6383
  failFinding(
6298
6384
  ruleId,
6299
- `Tool ${options.before} cannot causally happen before itself.`,
6385
+ `${subject} ${beforeLabel} cannot causally happen before itself.`,
6300
6386
  [eventEvidence(beforeEvent)],
6301
6387
  expected,
6302
- { code: "tool.order.same-tool" }
6388
+ { code: bothTools ? "tool.order.same-tool" : "step.order.same-step" }
6303
6389
  )
6304
6390
  ];
6305
6391
  }
@@ -6331,11 +6417,11 @@ function createToolOrderingRule(options) {
6331
6417
  return [
6332
6418
  failFinding(
6333
6419
  ruleId,
6334
- `Tool order ${options.before} before ${options.after} could not establish all causal intervals.`,
6420
+ `${subject} order ${beforeLabel} before ${afterLabel} could not establish all causal intervals.`,
6335
6421
  [firstMissingBefore, firstMissingAfter].filter((event) => event !== void 0).map((event) => eventEvidence(event)),
6336
6422
  expected,
6337
6423
  {
6338
- code: "tool.order.interval-unresolved",
6424
+ code: bothTools ? "tool.order.interval-unresolved" : "step.order.interval-unresolved",
6339
6425
  missingBeforeEnds,
6340
6426
  missingAfterStarts
6341
6427
  }
@@ -6346,7 +6432,7 @@ function createToolOrderingRule(options) {
6346
6432
  return [
6347
6433
  failFinding(
6348
6434
  ruleId,
6349
- `Every ${options.before} tool call must finish before any ${options.after} tool call starts.`,
6435
+ 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.`,
6350
6436
  [eventEvidence(latestBefore.event), eventEvidence(earliestAfter.event)],
6351
6437
  expected,
6352
6438
  {
@@ -6364,10 +6450,13 @@ function createToolOrderingRule(options) {
6364
6450
  ruleId,
6365
6451
  severity: "warning",
6366
6452
  status: "warning",
6367
- message: `Tool order ${options.before} before ${options.after} satisfied start/encounter order, but intervals overlap so causal completion before the next tool was not established.`,
6368
- expected: { before: options.before, after: options.after, mode: "first-occurrence" },
6453
+ message: `${subject} order ${beforeLabel} before ${afterLabel} satisfied start/encounter order, but intervals overlap so causal completion before the next ${bothTools ? "tool" : "step"} was not established.`,
6454
+ expected: {
6455
+ ...endpointExpected,
6456
+ mode: "first-occurrence"
6457
+ },
6369
6458
  actual: {
6370
- code: "tool.order.overlap",
6459
+ code: bothTools ? "tool.order.overlap" : "step.order.overlap",
6371
6460
  beforeEndedAt: beforeEvent.endedAt,
6372
6461
  afterStartedAt: afterEvent.startedAt ?? afterEvent.timestamp
6373
6462
  },
@@ -6379,6 +6468,15 @@ function createToolOrderingRule(options) {
6379
6468
  }
6380
6469
  };
6381
6470
  }
6471
+ function createToolOrderingRule(options) {
6472
+ return createStepOrderingRule({
6473
+ before: { kind: "TOOL", name: options.before },
6474
+ after: { kind: "TOOL", name: options.after },
6475
+ ...options.id !== void 0 ? { id: options.id } : {},
6476
+ ...options.mode !== void 0 ? { mode: options.mode } : {},
6477
+ requireEndpoints: false
6478
+ });
6479
+ }
6382
6480
  function createLlmUsageRule(options) {
6383
6481
  return {
6384
6482
  id: "llm.usage",
@@ -8640,11 +8738,33 @@ function contractFailFinding(ruleId, message, evidence, expected, actual) {
8640
8738
  evidence: [...evidence]
8641
8739
  };
8642
8740
  }
8741
+ function parseAllowedStatus(status) {
8742
+ return ALLOWED_STATUS_ALIASES[status];
8743
+ }
8643
8744
  function normalizeStatus(status) {
8644
- if (status === "ok" || status === "error" || status === "running") return status;
8645
- if (status === "success") return "ok";
8646
- if (status === "failed") return "error";
8647
- return "error";
8745
+ const parsed = parseAllowedStatus(status);
8746
+ if (parsed === void 0) {
8747
+ throw new TypeError(
8748
+ `Unknown run status ${JSON.stringify(status)}. Allowed: ok, error, running (aliases: success, failed).`
8749
+ );
8750
+ }
8751
+ return parsed;
8752
+ }
8753
+ function validateAllowedStatusesShape(allowedStatuses, pathPrefix = "run.allowedStatuses") {
8754
+ if (!allowedStatuses?.length) return [];
8755
+ const out = [];
8756
+ for (let i = 0; i < allowedStatuses.length; i++) {
8757
+ const status = allowedStatuses[i];
8758
+ if (parseAllowedStatus(status) === void 0) {
8759
+ out.push({
8760
+ code: "contract.run.allowedStatuses.unknown",
8761
+ severity: "error",
8762
+ message: `${pathPrefix}[${i}] has unknown status ${JSON.stringify(status)}. Allowed: ok, error, running (aliases: success, failed).`,
8763
+ path: `${pathPrefix}[${i}]`
8764
+ });
8765
+ }
8766
+ }
8767
+ return out;
8648
8768
  }
8649
8769
  function cloneBody(body) {
8650
8770
  return {
@@ -8657,6 +8777,17 @@ function cloneBody(body) {
8657
8777
  }
8658
8778
  } : {},
8659
8779
  ...body.llm ? { llm: { ...body.llm } } : {},
8780
+ ...body.steps ? {
8781
+ steps: {
8782
+ ...body.steps.orderRelations ? {
8783
+ orderRelations: body.steps.orderRelations.map((item) => ({
8784
+ ...item,
8785
+ before: { ...item.before },
8786
+ after: { ...item.after }
8787
+ }))
8788
+ } : {}
8789
+ }
8790
+ } : {},
8660
8791
  ...body.observations ? {
8661
8792
  observations: {
8662
8793
  ...body.observations,
@@ -8696,7 +8827,7 @@ function cloneBody(body) {
8696
8827
  };
8697
8828
  }
8698
8829
  function bodyHasRules(body) {
8699
- return body.run !== void 0 || body.tools !== void 0 || body.llm !== void 0 || body.observations !== void 0 || body.controls !== void 0 || body.retry !== void 0;
8830
+ return body.run !== void 0 || body.tools !== void 0 || body.llm !== void 0 || body.steps !== void 0 || body.observations !== void 0 || body.controls !== void 0 || body.retry !== void 0;
8700
8831
  }
8701
8832
  function parseEvidenceEventIds(evidence) {
8702
8833
  if (evidence === void 0 || evidence === null) {
@@ -9037,6 +9168,18 @@ function contractToRules(contract) {
9037
9168
  })
9038
9169
  );
9039
9170
  }
9171
+ const orderRelations = contract.steps?.orderRelations ?? [];
9172
+ for (const [index, relation] of orderRelations.entries()) {
9173
+ rules.push(
9174
+ createStepOrderingRule({
9175
+ before: { kind: relation.before.kind, name: relation.before.name },
9176
+ after: { kind: relation.after.kind, name: relation.after.name },
9177
+ id: `contract.step.orderRelation.${index}`,
9178
+ mode: relation.mode ?? "first-occurrence",
9179
+ requireEndpoints: relation.requireEndpoints !== false
9180
+ })
9181
+ );
9182
+ }
9040
9183
  if (contract.observations) {
9041
9184
  const required = contract.observations.required ?? [];
9042
9185
  if (required.length > 0) {
@@ -9286,6 +9429,7 @@ function evaluateBody(input3, body, options = {}) {
9286
9429
  }
9287
9430
  function defineTraceContract(input3) {
9288
9431
  const shapeErrors = [
9432
+ ...validateAllowedStatusesShape(input3.run?.allowedStatuses),
9289
9433
  ...validateAlternativesShape(input3.alternatives),
9290
9434
  ...validateScopeShape(input3.scope),
9291
9435
  ...validateProvenanceShape(input3.observations)
@@ -9316,6 +9460,7 @@ function defineTraceContract(input3) {
9316
9460
  }
9317
9461
  function evaluateTraceContract(input3, contract, options = {}) {
9318
9462
  const shapeErrors = [
9463
+ ...validateAllowedStatusesShape(contract.run?.allowedStatuses),
9319
9464
  ...validateAlternativesShape(contract.alternatives),
9320
9465
  ...validateScopeShape(contract.scope),
9321
9466
  ...validateProvenanceShape(contract.observations)
@@ -9451,7 +9596,7 @@ function evaluateTraceContract(input3, contract, options = {}) {
9451
9596
  [...scopeFinding ? [scopeFinding] : [], noneFinding, ...prefixedBranchFindings]
9452
9597
  );
9453
9598
  }
9454
- var MAX_EVIDENCE_EVENT_IDS, METHOD_VOCABULARY;
9599
+ var ALLOWED_STATUS_ALIASES, MAX_EVIDENCE_EVENT_IDS, METHOD_VOCABULARY;
9455
9600
  var init_contract = __esm({
9456
9601
  "packages/core/src/checks/contract.ts"() {
9457
9602
  init_checks2();
@@ -9464,6 +9609,13 @@ var init_contract = __esm({
9464
9609
  init_outcomes();
9465
9610
  init_contract_scope();
9466
9611
  init_tool_arguments();
9612
+ ALLOWED_STATUS_ALIASES = {
9613
+ ok: "ok",
9614
+ error: "error",
9615
+ running: "running",
9616
+ success: "ok",
9617
+ failed: "error"
9618
+ };
9467
9619
  MAX_EVIDENCE_EVENT_IDS = 16;
9468
9620
  METHOD_VOCABULARY = new Set(OBSERVED_OUTCOME_METHODS);
9469
9621
  }
@@ -16001,7 +16153,7 @@ var init_src = __esm({
16001
16153
  });
16002
16154
 
16003
16155
  // package.json
16004
- var version = "6.30.0";
16156
+ var version = "6.31.1";
16005
16157
 
16006
16158
  // packages/cli/src/list.ts
16007
16159
  init_advanced();
@@ -22925,6 +23077,7 @@ var CONTRACT_KEYS = /* @__PURE__ */ new Set([
22925
23077
  "run",
22926
23078
  "tools",
22927
23079
  "llm",
23080
+ "steps",
22928
23081
  "observations",
22929
23082
  "scope",
22930
23083
  "alternatives",
@@ -22935,6 +23088,7 @@ var CONTRACT_BODY_KEYS = /* @__PURE__ */ new Set([
22935
23088
  "run",
22936
23089
  "tools",
22937
23090
  "llm",
23091
+ "steps",
22938
23092
  "observations",
22939
23093
  "controls",
22940
23094
  "retry"
@@ -23012,6 +23166,15 @@ var CONTRACT_ORDER_RULE_KEYS = /* @__PURE__ */ new Set([
23012
23166
  "occurrenceMode",
23013
23167
  "requireEndpoints"
23014
23168
  ]);
23169
+ var CONTRACT_STEPS_KEYS = /* @__PURE__ */ new Set(["orderRelations"]);
23170
+ var CONTRACT_STEP_ORDER_RELATION_KEYS = /* @__PURE__ */ new Set([
23171
+ "before",
23172
+ "after",
23173
+ "mode",
23174
+ "requireEndpoints"
23175
+ ]);
23176
+ var CONTRACT_STEP_REF_KEYS = /* @__PURE__ */ new Set(["kind", "name"]);
23177
+ var CONTRACT_STEP_KINDS = /* @__PURE__ */ new Set(["TOOL", "LLM"]);
23015
23178
  var CONTRACT_ARGUMENT_KEYS = /* @__PURE__ */ new Set([
23016
23179
  "tool",
23017
23180
  "path",
@@ -23342,6 +23505,7 @@ function contractConfigHasEffect(contract) {
23342
23505
  if (sectionHasEffect(contract.run)) return true;
23343
23506
  if (sectionHasEffect(contract.tools)) return true;
23344
23507
  if (sectionHasEffect(contract.llm)) return true;
23508
+ if (sectionHasEffect(contract.steps)) return true;
23345
23509
  if (sectionHasEffect(contract.observations)) {
23346
23510
  return true;
23347
23511
  }
@@ -23361,10 +23525,21 @@ function parseContractRunSection(value, pathPrefix = "contract.run") {
23361
23525
  );
23362
23526
  }
23363
23527
  if (record.allowedStatuses !== void 0) {
23364
- out.allowedStatuses = requireBoundedStringArray(
23528
+ const statuses = requireBoundedStringArray(
23365
23529
  record.allowedStatuses,
23366
23530
  `${pathPrefix}.allowedStatuses`
23367
23531
  );
23532
+ const allowed = /* @__PURE__ */ new Set(["ok", "error", "running", "success", "failed"]);
23533
+ for (let i = 0; i < statuses.length; i++) {
23534
+ const status = statuses[i];
23535
+ if (!allowed.has(status)) {
23536
+ throw new CheckConfigError(
23537
+ "AI_CHECK_CONFIG_INVALID_VALUE",
23538
+ `${pathPrefix}.allowedStatuses[${i}] must be one of: ok, error, running (aliases: success, failed); got ${JSON.stringify(status)}.`
23539
+ );
23540
+ }
23541
+ }
23542
+ out.allowedStatuses = statuses;
23368
23543
  }
23369
23544
  if (record.maxDurationMs !== void 0) {
23370
23545
  out.maxDurationMs = requireNonNegativeNumber(
@@ -23526,6 +23701,66 @@ function parseContractLlmSection(value, pathPrefix = "contract.llm") {
23526
23701
  }
23527
23702
  return out;
23528
23703
  }
23704
+ function parseStepRef(value, pathPrefix) {
23705
+ const record = requireObject(value, pathPrefix);
23706
+ rejectUnknownKeys(record, CONTRACT_STEP_REF_KEYS, pathPrefix);
23707
+ if (typeof record.kind !== "string" || !CONTRACT_STEP_KINDS.has(record.kind)) {
23708
+ throw new CheckConfigError(
23709
+ "AI_CHECK_CONFIG_INVALID_VALUE",
23710
+ `${pathPrefix}.kind must be "TOOL" or "LLM".`
23711
+ );
23712
+ }
23713
+ return {
23714
+ kind: record.kind,
23715
+ name: requireNonEmptyString(record.name, `${pathPrefix}.name`)
23716
+ };
23717
+ }
23718
+ function parseStepOrderRelation(value, pathPrefix) {
23719
+ const record = requireObject(value, pathPrefix);
23720
+ rejectUnknownKeys(record, CONTRACT_STEP_ORDER_RELATION_KEYS, pathPrefix);
23721
+ if (record.before === void 0) {
23722
+ throw new CheckConfigError(
23723
+ "AI_CHECK_CONFIG_INVALID_VALUE",
23724
+ `${pathPrefix}.before is required.`
23725
+ );
23726
+ }
23727
+ if (record.after === void 0) {
23728
+ throw new CheckConfigError(
23729
+ "AI_CHECK_CONFIG_INVALID_VALUE",
23730
+ `${pathPrefix}.after is required.`
23731
+ );
23732
+ }
23733
+ const out = {
23734
+ before: parseStepRef(record.before, `${pathPrefix}.before`),
23735
+ after: parseStepRef(record.after, `${pathPrefix}.after`)
23736
+ };
23737
+ if (record.mode !== void 0) {
23738
+ out.mode = requireOrderMode(record.mode, `${pathPrefix}.mode`);
23739
+ }
23740
+ if (record.requireEndpoints !== void 0) {
23741
+ out.requireEndpoints = requireBoolean(
23742
+ record.requireEndpoints,
23743
+ `${pathPrefix}.requireEndpoints`
23744
+ );
23745
+ }
23746
+ return out;
23747
+ }
23748
+ function parseContractStepsSection(value, pathPrefix = "contract.steps") {
23749
+ const record = requireObject(value, pathPrefix);
23750
+ rejectUnknownKeys(record, CONTRACT_STEPS_KEYS, pathPrefix);
23751
+ const out = {};
23752
+ if (record.orderRelations !== void 0) {
23753
+ const items = requireBoundedArray(
23754
+ record.orderRelations,
23755
+ `${pathPrefix}.orderRelations`,
23756
+ CONTRACT_MAX_ORDER_RULES
23757
+ );
23758
+ out.orderRelations = items.map(
23759
+ (item, index) => parseStepOrderRelation(item, `${pathPrefix}.orderRelations[${index}]`)
23760
+ );
23761
+ }
23762
+ return out;
23763
+ }
23529
23764
  function parseRequireProvenance(value, pathPrefix) {
23530
23765
  const record = requireObject(value, pathPrefix);
23531
23766
  rejectUnknownKeys(record, CONTRACT_PROVENANCE_KEYS, pathPrefix);
@@ -23811,6 +24046,9 @@ function parseContractBodySection(value, pathPrefix) {
23811
24046
  out.tools = parseContractToolsSection(record.tools, `${pathPrefix}.tools`);
23812
24047
  }
23813
24048
  if (record.llm !== void 0) out.llm = parseContractLlmSection(record.llm, `${pathPrefix}.llm`);
24049
+ if (record.steps !== void 0) {
24050
+ out.steps = parseContractStepsSection(record.steps, `${pathPrefix}.steps`);
24051
+ }
23814
24052
  if (record.observations !== void 0) {
23815
24053
  out.observations = parseContractObservationsSection(
23816
24054
  record.observations,