@wix/pathgrade 1.0.38 → 1.0.40

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 (53) hide show
  1. package/README.md +5 -5
  2. package/dist/adapters/jest/results.d.ts +1 -0
  3. package/dist/adapters/jest/results.js +36 -14
  4. package/dist/adapters/jest/runner-adapter.d.ts +1 -0
  5. package/dist/adapters/jest/runner-adapter.js +5 -1
  6. package/dist/adapters/node-test/runner-adapter.js +6 -5
  7. package/dist/affected/meta.d.ts +4 -1
  8. package/dist/affected/meta.js +9 -5
  9. package/dist/affected/select.js +1 -1
  10. package/dist/agents/claude/sdk-message-projector.js +4 -5
  11. package/dist/agents/claude/tool-permission-bridge.js +2 -1
  12. package/dist/agents/codex-app-server/item-projection.js +2 -2
  13. package/dist/agents/codex-app-server/mcp-approval-correlator.js +3 -2
  14. package/dist/agents/opencode.js +4 -5
  15. package/dist/commands/report.js +5 -26
  16. package/dist/internal/direct-mcp-v2/acp-author-projector.js +20 -7
  17. package/dist/reporters/cli.js +20 -1
  18. package/dist/reporters/github-comment.js +18 -7
  19. package/dist/reporters/loader.d.ts +4 -0
  20. package/dist/reporters/loader.js +26 -9
  21. package/dist/reporting/comparison-contract.d.ts +3 -2
  22. package/dist/reporting/comparison-contract.js +66 -33
  23. package/dist/reporting/core.js +35 -8
  24. package/dist/reporting/reliability-contract.d.ts +1 -0
  25. package/dist/reporting/report-parser.js +125 -5
  26. package/dist/reporting/source-metadata.d.ts +3 -0
  27. package/dist/reporting/source-metadata.js +67 -2
  28. package/dist/reporting/types.d.ts +5 -1
  29. package/dist/runners/model-builders.d.ts +1 -0
  30. package/dist/runners/model-builders.js +36 -12
  31. package/dist/runners/model.d.ts +2 -0
  32. package/dist/runners/orchestrator.js +3 -4
  33. package/dist/runners/repeated-invocation.js +2 -5
  34. package/dist/runners/report-projection.js +84 -0
  35. package/dist/runners/vitest-adapter.js +6 -2
  36. package/dist/sdk/agent.js +10 -1
  37. package/dist/sdk/evaluate.js +58 -3
  38. package/dist/sdk/judge-prompt-builder.js +11 -7
  39. package/dist/sdk/mcp-event-input.d.ts +1 -0
  40. package/dist/sdk/mcp-event-input.js +3 -0
  41. package/dist/sdk/mcp-evidence.js +16 -3
  42. package/dist/sdk/mcp-safety.js +2 -2
  43. package/dist/sdk/scorers.d.ts +5 -0
  44. package/dist/sdk/scorers.js +4 -0
  45. package/dist/sdk/scripted-mcp-events.js +3 -2
  46. package/dist/sdk/tool-event-log.js +18 -3
  47. package/dist/sdk/tool-event-secrets.d.ts +4 -0
  48. package/dist/sdk/tool-event-secrets.js +33 -2
  49. package/dist/sdk/types.d.ts +2 -0
  50. package/dist/tool-event-results.d.ts +3 -0
  51. package/dist/tool-event-results.js +153 -23
  52. package/dist/types.d.ts +14 -7
  53. package/package.json +2 -2
@@ -1,12 +1,43 @@
1
- import { attachOriginalMcpInput, getOriginalMcpInput } from './mcp-event-input.js';
1
+ import { collectStructuredSensitiveValues, isSensitiveValueScanLimitError } from '../tool-event-results.js';
2
+ import { attachOriginalMcpInput, clearOriginalMcpInput, getOriginalMcpInput, } from './mcp-event-input.js';
2
3
  const sensitiveValuesByEvent = new WeakMap();
3
4
  export function attachToolEventSensitiveValues(event, sensitiveValues) {
4
- sensitiveValuesByEvent.set(event, [...sensitiveValues]);
5
+ sensitiveValuesByEvent.set(event, [...new Set([
6
+ ...getToolEventSensitiveValues(event),
7
+ ...sensitiveValues,
8
+ ])]);
5
9
  return event;
6
10
  }
7
11
  export function getToolEventSensitiveValues(event) {
8
12
  return sensitiveValuesByEvent.get(event) ?? [];
9
13
  }
14
+ export function attachLiveMcpInput(event, input) {
15
+ let sensitiveValues;
16
+ try {
17
+ sensitiveValues = collectStructuredSensitiveValues(input);
18
+ }
19
+ catch (error) {
20
+ if (!isSensitiveValueScanLimitError(error))
21
+ throw error;
22
+ return redactToolEventPayload(event);
23
+ }
24
+ return attachToolEventSensitiveValues(attachOriginalMcpInput(event, input), sensitiveValues);
25
+ }
26
+ export function redactToolEventPayload(event) {
27
+ return {
28
+ ...event,
29
+ arguments: { redacted: true },
30
+ summary: `${event.action} via ${event.providerToolName}`,
31
+ rawSnippet: '<redacted>',
32
+ };
33
+ }
34
+ export function collectToolEventSensitiveValues(events) {
35
+ return [...new Set(events.flatMap((event) => getToolEventSensitiveValues(event)))];
36
+ }
37
+ export function clearToolEventRuntimeMetadata(event) {
38
+ sensitiveValuesByEvent.delete(event);
39
+ clearOriginalMcpInput(event);
40
+ }
10
41
  export function cloneToolEventWithRuntimeMetadata(source, overrides) {
11
42
  const clone = attachToolEventSensitiveValues({ ...source, ...overrides }, getToolEventSensitiveValues(source));
12
43
  const originalInput = getOriginalMcpInput(source);
@@ -360,6 +360,8 @@ export interface EvaluateOptions {
360
360
  failFast?: boolean;
361
361
  llm?: LLMPort;
362
362
  onScorerError?: 'skip' | 'zero' | 'fail';
363
+ /** Evidence visible to deterministic scorers. Live mode is unavailable for snapshot replay. */
364
+ deterministicToolEvidence?: 'persisted' | 'live';
363
365
  /** Stable identity for this evaluation definition across separate runs. */
364
366
  evaluationDefinitionKey?: string;
365
367
  }
@@ -7,4 +7,7 @@ export declare function collectSensitiveEnvValues(env?: Readonly<Record<string,
7
7
  * is important for summaries, snippets, traces, and provider error text.
8
8
  */
9
9
  export declare function sanitizePersistenceValue<T>(source: T, explicitSensitiveValues?: readonly string[]): T;
10
+ export declare function sanitizeUntrustedPersistenceValue<T>(source: T, explicitSensitiveValues?: readonly string[]): T;
11
+ export declare function isSensitiveValueScanLimitError(error: unknown): boolean;
10
12
  export declare function sanitizeToolEventResult(source: Readonly<ToolEventResult>, sensitiveValues: readonly string[]): ToolEventResult;
13
+ export declare function collectStructuredSensitiveValues(source: unknown): string[];
@@ -24,6 +24,10 @@ const SECRET_KEY_NAMES = [
24
24
  'connectionstring',
25
25
  'credentials',
26
26
  'credential',
27
+ 'grant',
28
+ 'capability',
29
+ 'capabilityurl',
30
+ 'cursor',
27
31
  'svsession',
28
32
  'smsession',
29
33
  'wixsession',
@@ -32,6 +36,10 @@ const SECRET_KEY_NAMES = [
32
36
  const EXACT_ONLY_SECRET_KEY_NAMES = new Set([
33
37
  'auth',
34
38
  'token',
39
+ 'grant',
40
+ 'capability',
41
+ 'capabilityurl',
42
+ 'cursor',
35
43
  'svsession',
36
44
  'smsession',
37
45
  'wixsession',
@@ -39,6 +47,9 @@ const EXACT_ONLY_SECRET_KEY_NAMES = new Set([
39
47
  ]);
40
48
  const NON_SECRET_ENVIRONMENT_KEY_NAMES = new Set(['tokencount', 'tokenizersparallelism', 'tokenusage']);
41
49
  const BOUNDARY_ONLY_SENSITIVE_VALUE_MAX_LENGTH = 1;
50
+ const MAX_SANITIZE_DEPTH = 64;
51
+ const MAX_SENSITIVE_SCAN_NODES = 100_000;
52
+ const MAX_SENSITIVE_JSON_CHARS = 1024 * 1024;
42
53
  export function collectSensitiveEnvValues(env) {
43
54
  return [...new Set(Object.entries(env ?? {})
44
55
  .filter((entry) => typeof entry[1] === 'string')
@@ -60,15 +71,36 @@ export function sanitizePersistenceValue(source, explicitSensitiveValues = []) {
60
71
  ])].sort((a, b) => b.length - a.length);
61
72
  return sanitizeValue(source, '', sensitiveValues, new Set(explicitValues));
62
73
  }
74
+ export function sanitizeUntrustedPersistenceValue(source, explicitSensitiveValues = []) {
75
+ try {
76
+ return sanitizePersistenceValue(source, explicitSensitiveValues);
77
+ }
78
+ catch (error) {
79
+ if (!isSensitiveValueScanLimitError(error))
80
+ throw error;
81
+ if (Array.isArray(source))
82
+ return [];
83
+ if (isRecord(source))
84
+ return { redacted: true };
85
+ return '<redacted>';
86
+ }
87
+ }
88
+ export function isSensitiveValueScanLimitError(error) {
89
+ return error instanceof Error && error.message.startsWith('Sensitive-value scan exceeded');
90
+ }
63
91
  export function sanitizeToolEventResult(source, sensitiveValues) {
64
92
  const result = {};
65
93
  let truncated = source.truncated === true;
66
94
  const addBounded = (key, value) => {
67
95
  if (typeof value !== 'string')
68
96
  return;
69
- const bounded = boundText(sanitizePersistenceValue(value, sensitiveValues));
70
- result[key] = bounded.value;
71
- truncated ||= bounded.truncated;
97
+ const explicitValues = [...new Set(sensitiveValues.filter((entry) => entry.length > 0))];
98
+ const overlap = explicitValues.reduce((max, entry) => Math.max(max, entry.length), 0);
99
+ const prefix = value.slice(0, TOOL_RESULT_MAX_CHARS + Math.max(overlap, 1));
100
+ const bounded = boundText(redactSensitiveValues(prefix, explicitValues, new Set(explicitValues)));
101
+ const sanitized = boundText(sanitizePersistenceValue(bounded.value, sensitiveValues));
102
+ result[key] = sanitized.value;
103
+ truncated ||= value.length > prefix.length || bounded.truncated || sanitized.truncated;
72
104
  };
73
105
  addBounded('content', source.content);
74
106
  addBounded('stdout', source.stdout);
@@ -96,56 +128,93 @@ function redactSensitiveValues(value, sensitiveValues, explicitSensitiveValues)
96
128
  }
97
129
  return redacted;
98
130
  }
99
- function collectStructuredSensitiveValues(source) {
131
+ export function collectStructuredSensitiveValues(source) {
100
132
  const values = new Set();
101
- const visit = (value, key) => {
102
- if (typeof value === 'string' && !isNumericTokenTelemetry(key, value)
103
- && isSecretKey(key) && value.length > 0) {
104
- values.add(value);
105
- return;
133
+ let scannedNodes = 0;
134
+ let parsedJsonChars = 0;
135
+ const pending = [
136
+ { value: source, key: '' },
137
+ ];
138
+ while (pending.length > 0) {
139
+ if (++scannedNodes > MAX_SENSITIVE_SCAN_NODES) {
140
+ throw new Error('Sensitive-value scan exceeded the input node limit');
141
+ }
142
+ const { value, key } = pending.pop();
143
+ if (typeof value === 'string') {
144
+ if (value.length > 0 && !isNumericTokenTelemetry(key, value) && isSecretKey(key)) {
145
+ values.add(value);
146
+ }
147
+ for (const match of value.matchAll(/[?&](?:api[_-]?key|access[_-]?token|refresh[_-]?token|client[_-]?secret|token|secret|auth|password|passwd|session(?:id)?|cookie)=([^&#\s]+)/gi)) {
148
+ if (match[1])
149
+ values.add(match[1]);
150
+ }
151
+ for (const sensitiveValue of collectCredentialShapeSensitiveValues(value)) {
152
+ values.add(sensitiveValue);
153
+ }
154
+ const trimmed = value.trim();
155
+ const looksStructured = (trimmed.startsWith('{') && trimmed.endsWith('}'))
156
+ || (trimmed.startsWith('[') && trimmed.endsWith(']'));
157
+ if (looksStructured && (parsedJsonChars += value.length) > MAX_SENSITIVE_JSON_CHARS) {
158
+ throw new Error('Sensitive-value scan exceeded the nested JSON limit');
159
+ }
160
+ const structured = looksStructured ? parseStructuredJson(value) : undefined;
161
+ if (structured !== undefined)
162
+ pending.push({ value: structured, key });
163
+ continue;
164
+ }
165
+ if ((typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint')
166
+ && !isNumericTokenTelemetry(key, value) && isSecretKey(key)) {
167
+ values.add(String(value));
168
+ continue;
106
169
  }
107
170
  if (Array.isArray(value)) {
108
171
  for (const entry of value)
109
- visit(entry, key);
110
- return;
172
+ pending.push({ value: entry, key });
173
+ continue;
111
174
  }
112
175
  if (isRecord(value)) {
113
- for (const [entryKey, entryValue] of Object.entries(value))
114
- visit(entryValue, entryKey);
176
+ for (const [entryKey, entryValue] of Object.entries(value)) {
177
+ pending.push({ value: entryValue, key: entryKey });
178
+ }
115
179
  }
116
- };
117
- visit(source, '');
180
+ }
118
181
  return [...values];
119
182
  }
120
- function sanitizeValue(value, key, sensitiveValues, explicitSensitiveValues) {
183
+ function sanitizeValue(value, key, sensitiveValues, explicitSensitiveValues, depth = 0) {
121
184
  if (!isNumericTokenTelemetry(key, value) && isSecretKey(key))
122
185
  return '<redacted>';
123
186
  if (typeof value === 'string') {
124
- return redactCredentialShapes(redactSensitiveValues(value, sensitiveValues, explicitSensitiveValues), sensitiveValues, explicitSensitiveValues);
187
+ return redactCredentialShapes(redactSensitiveValues(value, sensitiveValues, explicitSensitiveValues), sensitiveValues, explicitSensitiveValues, depth);
125
188
  }
189
+ if (depth >= MAX_SANITIZE_DEPTH && (Array.isArray(value) || isRecord(value)))
190
+ return '<redacted>';
126
191
  if (Array.isArray(value)) {
127
- return value.map((entry) => sanitizeValue(entry, key, sensitiveValues, explicitSensitiveValues));
192
+ return value.map((entry) => sanitizeValue(entry, key, sensitiveValues, explicitSensitiveValues, depth + 1));
128
193
  }
129
194
  if (isRecord(value)) {
130
195
  return Object.fromEntries(Object.entries(value).map(([entryKey, entryValue]) => [
131
196
  entryKey,
132
- sanitizeValue(entryValue, entryKey, sensitiveValues, explicitSensitiveValues),
197
+ sanitizeValue(entryValue, entryKey, sensitiveValues, explicitSensitiveValues, depth + 1),
133
198
  ]));
134
199
  }
135
200
  return value;
136
201
  }
137
- function redactCredentialShapes(value, sensitiveValues, explicitSensitiveValues) {
202
+ function redactCredentialShapes(value, sensitiveValues, explicitSensitiveValues, depth) {
138
203
  let redacted = value;
204
+ const credentialShapeValues = collectCredentialShapeSensitiveValues(redacted);
205
+ redacted = redactSensitiveValues(redacted, credentialShapeValues, new Set(credentialShapeValues));
139
206
  const structured = parseStructuredJson(redacted);
140
207
  if (structured !== undefined) {
141
208
  const nestedSensitiveValues = [...new Set([
142
209
  ...sensitiveValues,
143
210
  ...collectStructuredSensitiveValues(structured),
144
211
  ])].sort((a, b) => b.length - a.length);
145
- redacted = JSON.stringify(sanitizeValue(structured, '', nestedSensitiveValues, explicitSensitiveValues));
212
+ redacted = JSON.stringify(sanitizeValue(structured, '', nestedSensitiveValues, explicitSensitiveValues, depth + 1));
146
213
  }
147
214
  if (redacted.includes('PRIVATE KEY-----')) {
148
- redacted = redacted.replace(/-----BEGIN [^-\n]*PRIVATE KEY-----[\s\S]*?-----END [^-\n]*PRIVATE KEY-----/g, '<redacted>');
215
+ for (const block of findPrivateKeyBlocks(redacted).reverse()) {
216
+ redacted = `${redacted.slice(0, block.start)}<redacted>${redacted.slice(block.end)}`;
217
+ }
149
218
  }
150
219
  if (/\b(?:bearer|basic)\s/i.test(redacted)) {
151
220
  redacted = redacted.replace(/(\b(?:bearer|basic)\s+)[^\s,;"']+/gi, '$1<redacted>');
@@ -154,10 +223,71 @@ function redactCredentialShapes(value, sensitiveValues, explicitSensitiveValues)
154
223
  redacted = redacted.replace(/([a-z][a-z0-9+.-]*:\/\/[^\s:/@]+:)[^\s/@]+(@)/gi, '$1<redacted>$2');
155
224
  }
156
225
  if (redacted.includes('=') || redacted.includes(':')) {
157
- redacted = redacted.replace(/(\b(?:api[_-]?key|access[_-]?token|refresh[_-]?token|client[_-]?secret|token|secret|auth|password|passwd|session(?:id)?|cookie)\s*[=:]\s*)[^\s,;"'}]+/gi, '$1<redacted>');
226
+ redacted = redacted.replace(/((?:^|[\s{[(,;])["']?(?:api[_-]?key|access[_-]?token|refresh[_-]?token|client[_-]?secret|token|secret|auth|password|passwd|session(?:id)?|cookie)["']?\s*[=:]\s*["']?)[^\s,;"'}]+/gi, '$1<redacted>');
227
+ redacted = redacted.replace(/((?:^|[\s{[(,;])["'](?:grant|capability|capabilityurl|cursor)["']\s*:\s*["']?)[^\s,;"'}]+/gi, '$1<redacted>');
158
228
  }
159
229
  return redacted;
160
230
  }
231
+ function collectCredentialShapeSensitiveValues(value) {
232
+ const values = new Set();
233
+ for (const match of value.matchAll(/https?:\/\/[^\s,;"']+\/(?:cap|capability)\/[^\s,;"']+/gi)) {
234
+ values.add(match[0]);
235
+ }
236
+ if (value.includes('PRIVATE KEY-----')) {
237
+ for (const block of findPrivateKeyBlocks(value)) {
238
+ values.add(value.slice(block.start, block.end));
239
+ if (block.body)
240
+ values.add(block.body);
241
+ }
242
+ }
243
+ if (/\b(?:bearer|basic)\s/i.test(value)) {
244
+ for (const match of value.matchAll(/\b(?:bearer|basic)\s+([^\s,;"']+)/gi)) {
245
+ if (match[1])
246
+ values.add(match[1]);
247
+ }
248
+ }
249
+ if (value.includes('://')) {
250
+ for (const match of value.matchAll(/[a-z][a-z0-9+.-]*:\/\/[^\s:/@]+:([^\s/@]+)@/gi)) {
251
+ if (match[1])
252
+ values.add(match[1]);
253
+ }
254
+ }
255
+ if (value.includes('=') || value.includes(':')) {
256
+ for (const match of value.matchAll(/(?:^|[\s{[(,;])["']?(?:api[_-]?key|access[_-]?token|refresh[_-]?token|client[_-]?secret|token|secret|auth|password|passwd|session(?:id)?|cookie)["']?\s*[=:]\s*["']?([^\s,;"'}]+)/gi)) {
257
+ if (match[1])
258
+ values.add(match[1]);
259
+ }
260
+ for (const match of value.matchAll(/(?:^|[\s{[(,;])["'](?:grant|capability|capabilityurl|cursor)["']\s*:\s*["']?([^\s,;"'}]+)/gi)) {
261
+ if (match[1])
262
+ values.add(match[1]);
263
+ }
264
+ }
265
+ return [...values].sort((a, b) => b.length - a.length);
266
+ }
267
+ function findPrivateKeyBlocks(value) {
268
+ const blocks = [];
269
+ let open;
270
+ let cursor = 0;
271
+ while (cursor < value.length) {
272
+ const start = value.indexOf('-----', cursor);
273
+ if (start < 0)
274
+ break;
275
+ const end = value.indexOf('-----', start + 5);
276
+ if (end < 0)
277
+ break;
278
+ const markerEnd = end + 5;
279
+ const marker = value.slice(start, markerEnd);
280
+ if (!open && marker.startsWith('-----BEGIN ') && marker.endsWith('PRIVATE KEY-----')) {
281
+ open = { start, bodyStart: markerEnd };
282
+ }
283
+ else if (open && marker.startsWith('-----END ') && marker.endsWith('PRIVATE KEY-----')) {
284
+ blocks.push({ start: open.start, end: markerEnd, body: value.slice(open.bodyStart, start).trim() });
285
+ open = undefined;
286
+ }
287
+ cursor = markerEnd;
288
+ }
289
+ return blocks;
290
+ }
161
291
  function isSecretKey(key) {
162
292
  const normalized = key.replace(/[^a-z0-9]/gi, '').toLowerCase();
163
293
  if (normalized === 'tokens')
package/dist/types.d.ts CHANGED
@@ -185,6 +185,10 @@ export interface EvalReport {
185
185
  task: string;
186
186
  /** Canonical status for this report or consolidated group when available. */
187
187
  status?: 'pass' | 'fail';
188
+ /** Whether every reportable runner attempt passed. */
189
+ runner_status?: 'pass' | 'fail';
190
+ /** Whether mean reward meets the configured threshold. */
191
+ threshold_status?: 'pass' | 'fail' | 'not_configured';
188
192
  /** Schema-v2 canonical average score. */
189
193
  mean_reward?: number;
190
194
  /** Binary success frequency, absent when attempts are ineligible. */
@@ -203,7 +207,7 @@ export interface EvalReport {
203
207
  skills_used: string[];
204
208
  }
205
209
  export type ComparisonUnavailableReason = 'definition_metadata_missing' | 'scorer_metadata_missing' | 'runtime_metadata_missing' | 'sampling_incomplete';
206
- export interface ComparisonContract {
210
+ export interface ComparisonContractV1 {
207
211
  version: 1;
208
212
  definition_revision?: string;
209
213
  scorer_revision?: string;
@@ -212,6 +216,7 @@ export interface ComparisonContract {
212
216
  report_schema_revision: 'pathgrade-results-v2';
213
217
  unavailable_reasons?: ComparisonUnavailableReason[];
214
218
  }
219
+ export type ComparisonContract = ComparisonContractV1 | import('./reporting/reliability-contract.js').ComparisonContractV2;
215
220
  /**
216
221
  * TrialResult with large trace fields stripped. These fields
217
222
  * live only in the per-group trace files; the consolidated results.json keeps
@@ -256,7 +261,7 @@ export interface PathgradeSelectionReport {
256
261
  * external consumers can gate on schema revisions.
257
262
  */
258
263
  export interface PathgradeReport {
259
- version: 1 | 2;
264
+ version: 1 | 2 | 3;
260
265
  timestamp: string;
261
266
  /** `ci.threshold` from Pathgrade config or legacy plugin config, if configured. */
262
267
  threshold?: number;
@@ -266,15 +271,17 @@ export interface PathgradeReport {
266
271
  overall_mean_reward?: number;
267
272
  attempts_requested?: number;
268
273
  attempts_completed?: number;
269
- /**
270
- * Threshold check result. When `threshold` is set: `'pass'` iff
271
- * `overall_pass_rate >= threshold`. Otherwise: `'pass'` iff every trial
272
- * in every group passed its vitest test.
273
- */
274
+ /** Whether every reportable runner attempt passed. */
275
+ runner_status?: 'pass' | 'fail';
276
+ /** Whether overall mean reward meets the configured threshold. */
277
+ threshold_status?: 'pass' | 'fail' | 'not_configured';
278
+ /** Conjunction of runner assertions and the optional aggregate threshold. */
274
279
  status: 'pass' | 'fail';
275
280
  /** Agent Evals compatibility manifest when changed selection finds no runnable evals. */
276
281
  run_kind?: 'evaluation' | 'no-affected';
277
282
  groups: PathgradeGroupReport[];
283
+ /** Current selected-task accounting, required for schema-v3 reports. */
284
+ task_inventory?: import('./reporting/reliability-contract.js').TaskInventory;
278
285
  /**
279
286
  * Present when `pathgrade run --changed` produced the run. Absent on
280
287
  * plain `pathgrade run`. Backward compatible — older consumers ignore
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/pathgrade",
3
- "version": "1.0.38",
3
+ "version": "1.0.40",
4
4
  "packageManager": "yarn@4.12.0",
5
5
  "description": "Evaluate whether AI agents discover and use your skills correctly",
6
6
  "exports": {
@@ -142,5 +142,5 @@
142
142
  "typescript": "^5.9.3",
143
143
  "zod": "4.3.6"
144
144
  },
145
- "falconPackageHash": "199b36204420a97fad83bac6e53d8b614d2b6592cb701e7db41d3bad"
145
+ "falconPackageHash": "4c4d43a7c8d439f02756404accbc72b44db02c52db4d0c2b2d18452f"
146
146
  }