@geotechcli/core 0.4.89 → 0.4.91

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 (58) hide show
  1. package/dist/config/index.d.ts.map +1 -1
  2. package/dist/config/index.js +4 -4
  3. package/dist/config/index.js.map +1 -1
  4. package/dist/fem/ground-model-draft.d.ts +7 -0
  5. package/dist/fem/ground-model-draft.d.ts.map +1 -1
  6. package/dist/fem/ground-model-draft.js +213 -6
  7. package/dist/fem/ground-model-draft.js.map +1 -1
  8. package/dist/fem/index.d.ts +1 -1
  9. package/dist/fem/index.d.ts.map +1 -1
  10. package/dist/fem/index.js +1 -1
  11. package/dist/fem/index.js.map +1 -1
  12. package/dist/index.d.ts +1 -1
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +1 -1
  15. package/dist/index.js.map +1 -1
  16. package/dist/ingest/document-evidence-packet.d.ts +92 -92
  17. package/dist/ingest/geotech-benchmark-corpus.d.ts +124 -2
  18. package/dist/ingest/geotech-benchmark-corpus.d.ts.map +1 -1
  19. package/dist/ingest/geotech-benchmark-corpus.js +420 -55
  20. package/dist/ingest/geotech-benchmark-corpus.js.map +1 -1
  21. package/dist/ingest/geotech-document-benchmark.d.ts +4 -0
  22. package/dist/ingest/geotech-document-benchmark.d.ts.map +1 -1
  23. package/dist/ingest/geotech-document-benchmark.js +196 -41
  24. package/dist/ingest/geotech-document-benchmark.js.map +1 -1
  25. package/dist/ingest/index.d.ts +2 -1
  26. package/dist/ingest/index.d.ts.map +1 -1
  27. package/dist/ingest/index.js +2 -1
  28. package/dist/ingest/index.js.map +1 -1
  29. package/dist/ingest/preprocessing-fixture-benchmark.d.ts +175 -0
  30. package/dist/ingest/preprocessing-fixture-benchmark.d.ts.map +1 -0
  31. package/dist/ingest/preprocessing-fixture-benchmark.js +598 -0
  32. package/dist/ingest/preprocessing-fixture-benchmark.js.map +1 -0
  33. package/dist/llm/byok-benchmark.d.ts +125 -0
  34. package/dist/llm/byok-benchmark.d.ts.map +1 -0
  35. package/dist/llm/byok-benchmark.js +529 -0
  36. package/dist/llm/byok-benchmark.js.map +1 -0
  37. package/dist/llm/index.d.ts +1 -0
  38. package/dist/llm/index.d.ts.map +1 -1
  39. package/dist/llm/index.js +1 -0
  40. package/dist/llm/index.js.map +1 -1
  41. package/dist/meta/metadata.json +1 -1
  42. package/dist/signal/index.d.ts +112 -0
  43. package/dist/signal/index.d.ts.map +1 -1
  44. package/dist/signal/index.js +648 -1
  45. package/dist/signal/index.js.map +1 -1
  46. package/dist/standards/index.d.ts +6 -0
  47. package/dist/standards/index.d.ts.map +1 -1
  48. package/dist/standards/index.js +243 -0
  49. package/dist/standards/index.js.map +1 -1
  50. package/dist/verifier/findings.d.ts +6 -0
  51. package/dist/verifier/findings.d.ts.map +1 -1
  52. package/dist/verifier/findings.js +192 -1
  53. package/dist/verifier/findings.js.map +1 -1
  54. package/dist/verifier/index.d.ts +1 -1
  55. package/dist/verifier/index.d.ts.map +1 -1
  56. package/dist/verifier/index.js +1 -1
  57. package/dist/verifier/index.js.map +1 -1
  58. package/package.json +1 -1
@@ -1,10 +1,36 @@
1
1
  import { extname, basename } from 'node:path';
2
2
  import { parseDelimitedFile, parseXlsxFile } from '../tabular/index.js';
3
+ export const SIGNAL_ANALYSIS_BENCHMARK_REQUIRED_TYPES = [
4
+ 'settlement',
5
+ 'piezometer',
6
+ 'inclinometer',
7
+ 'vibration',
8
+ 'load-test',
9
+ ];
3
10
  const DEFAULT_MAX_ROWS = 5000;
4
11
  const EPSILON = 1e-9;
5
12
  const EXCEL_SERIAL_MIN = 20_000;
6
13
  const EXCEL_SERIAL_MAX = 80_000;
7
14
  const EXCEL_SERIAL_EPOCH_MS = Date.UTC(1899, 11, 30);
15
+ const SIGNAL_ANALYSIS_TYPES = [
16
+ 'settlement',
17
+ 'piezometer',
18
+ 'inclinometer',
19
+ 'vibration',
20
+ 'load-test',
21
+ 'unknown',
22
+ ];
23
+ const SIGNAL_OUTPUT_PROHIBITED_KEYS = new Set([
24
+ 'apiKey',
25
+ 'llm',
26
+ 'model',
27
+ 'modelCall',
28
+ 'modelCalls',
29
+ 'prompt',
30
+ 'rawPrompt',
31
+ 'solver',
32
+ 'token',
33
+ ]);
8
34
  export const SIGNAL_THRESHOLD_PROFILES = [
9
35
  {
10
36
  id: 'settlement-review-mm',
@@ -63,6 +89,289 @@ export const SIGNAL_THRESHOLD_PROFILES = [
63
89
  },
64
90
  ];
65
91
  export const SIGNAL_THRESHOLD_PROFILE_IDS = SIGNAL_THRESHOLD_PROFILES.map((profile) => profile.id);
92
+ export function validateSignalAnalysisBenchmarkComparison(report, options = {}) {
93
+ const failures = [];
94
+ const warnings = [];
95
+ const requiredTypes = [...(options.requiredTypes ?? SIGNAL_ANALYSIS_BENCHMARK_REQUIRED_TYPES)];
96
+ const minSources = options.minSources ?? requiredTypes.length;
97
+ const minRowsAnalyzed = options.minRowsAnalyzed ?? 15;
98
+ const minDirectRowsAnalyzed = options.minDirectRowsAnalyzed ?? 3;
99
+ const minDirectThresholdFlags = options.minDirectThresholdFlags ?? 1;
100
+ const minDirectRateThresholdFlags = options.minDirectRateThresholdFlags ?? 1;
101
+ const minDirectMissingIntervals = options.minDirectMissingIntervals ?? 1;
102
+ const minDirectPlotHtmlBytes = options.minDirectPlotHtmlBytes ?? 1_000;
103
+ const minDirectPlotChartCount = options.minDirectPlotChartCount ?? 1;
104
+ const requireZeroModelCalls = options.requireZeroModelCalls ?? true;
105
+ if (!isRecord(report)) {
106
+ return {
107
+ schemaVersion: 'signal-analysis-benchmark-contract.v1',
108
+ ok: false,
109
+ failures: ['benchmark_comparison_must_be_object'],
110
+ warnings,
111
+ };
112
+ }
113
+ if (report.kind !== 'signal-analysis-local-benchmark-comparison') {
114
+ failures.push('wrong_benchmark_kind');
115
+ }
116
+ if (report.schemaVersion !== 1) {
117
+ failures.push('wrong_benchmark_schema_version');
118
+ }
119
+ if (!isNonEmptyString(report.generatedAt)) {
120
+ failures.push('missing_generated_at');
121
+ }
122
+ if (report.passed !== true) {
123
+ failures.push('benchmark_not_passed');
124
+ }
125
+ if (!Array.isArray(report.regressions)) {
126
+ failures.push('regressions_must_be_array');
127
+ }
128
+ else if (report.regressions.length > 0) {
129
+ failures.push('benchmark_regressions_present');
130
+ }
131
+ validateSignalBenchmarkArtifacts(report.artifacts, failures);
132
+ validateSignalBenchmarkWorkflow(report.workflow, failures, requireZeroModelCalls);
133
+ validateSignalBenchmarkSignalArtifacts(report.signalArtifacts, failures, {
134
+ requiredTypes,
135
+ minSources,
136
+ minRowsAnalyzed,
137
+ });
138
+ validateSignalBenchmarkDirectSignal(report.directSignal, failures, {
139
+ minDirectRowsAnalyzed,
140
+ minDirectThresholdFlags,
141
+ minDirectRateThresholdFlags,
142
+ minDirectMissingIntervals,
143
+ });
144
+ validateSignalBenchmarkDirectSignalPlot(report.directSignalPlot, failures, {
145
+ minDirectPlotHtmlBytes,
146
+ minDirectPlotChartCount,
147
+ });
148
+ validateSignalBenchmarkPathSafety(report, report.pathSafety, failures, 'comparison');
149
+ return buildSignalBenchmarkContractValidation(failures, warnings);
150
+ }
151
+ export function validateSignalAnalysisBenchmarkTrend(report, options = {}) {
152
+ const failures = [];
153
+ const warnings = [];
154
+ const requiredTypes = [...(options.requiredTypes ?? SIGNAL_ANALYSIS_BENCHMARK_REQUIRED_TYPES)];
155
+ const minSources = options.minSources ?? requiredTypes.length;
156
+ const minRowsAnalyzed = options.minRowsAnalyzed ?? 15;
157
+ const minDirectThresholdFlags = options.minDirectThresholdFlags ?? 1;
158
+ const minDirectRateThresholdFlags = options.minDirectRateThresholdFlags ?? 1;
159
+ const minDirectMissingIntervals = options.minDirectMissingIntervals ?? 1;
160
+ const minDirectPlotHtmlBytes = options.minDirectPlotHtmlBytes ?? 1_000;
161
+ const minDirectPlotChartCount = options.minDirectPlotChartCount ?? 1;
162
+ const requireZeroModelCalls = options.requireZeroModelCalls ?? true;
163
+ if (!isRecord(report)) {
164
+ return {
165
+ schemaVersion: 'signal-analysis-benchmark-contract.v1',
166
+ ok: false,
167
+ failures: ['benchmark_trend_must_be_object'],
168
+ warnings,
169
+ };
170
+ }
171
+ if (report.kind !== 'signal-analysis-benchmark-trend') {
172
+ failures.push('wrong_trend_kind');
173
+ }
174
+ if (report.schemaVersion !== 1) {
175
+ failures.push('wrong_trend_schema_version');
176
+ }
177
+ if (!isNonEmptyString(report.generatedAt)) {
178
+ failures.push('trend_missing_generated_at');
179
+ }
180
+ if (!isNonNegativeInteger(report.historyCount) || report.historyCount < 1) {
181
+ failures.push('trend_history_count_invalid');
182
+ }
183
+ if (!isNonEmptyString(report.note) || !/private paths|raw monitoring files/i.test(report.note)) {
184
+ warnings.push('trend_note_should_state_raw_files_and_private_paths_are_excluded');
185
+ }
186
+ validateSignalBenchmarkHistoryEntry(report.current, failures, 'current', {
187
+ requiredTypes,
188
+ minSources,
189
+ minRowsAnalyzed,
190
+ minDirectThresholdFlags,
191
+ minDirectRateThresholdFlags,
192
+ minDirectMissingIntervals,
193
+ minDirectPlotHtmlBytes,
194
+ minDirectPlotChartCount,
195
+ requireZeroModelCalls,
196
+ requirePassed: true,
197
+ });
198
+ if (report.previous !== null) {
199
+ validateSignalBenchmarkHistoryEntry(report.previous, failures, 'previous', {
200
+ requiredTypes,
201
+ minSources,
202
+ minRowsAnalyzed,
203
+ minDirectThresholdFlags,
204
+ minDirectRateThresholdFlags,
205
+ minDirectMissingIntervals,
206
+ minDirectPlotHtmlBytes,
207
+ minDirectPlotChartCount,
208
+ requireZeroModelCalls: false,
209
+ requirePassed: false,
210
+ });
211
+ }
212
+ if (report.previous && !isRecord(report.delta)) {
213
+ failures.push('trend_delta_required_when_previous_exists');
214
+ }
215
+ validateSignalBenchmarkPathSafety(report, null, failures, 'trend');
216
+ return buildSignalBenchmarkContractValidation(failures, warnings);
217
+ }
218
+ export function inspectSignalAnalysisBenchmarkPathSafety(value) {
219
+ return {
220
+ checked: true,
221
+ leaks: [...new Set(collectSignalBenchmarkPathLeaks(value))],
222
+ };
223
+ }
224
+ export function validateSignalAnalysisResultContract(value) {
225
+ const failures = [];
226
+ if (!isRecord(value)) {
227
+ return {
228
+ schemaVersion: 'signal-analysis-result-contract.v1',
229
+ ok: false,
230
+ failures: ['signal analysis result contract must be an object'],
231
+ };
232
+ }
233
+ if (value.schemaVersion !== 'signal-analysis.v0') {
234
+ failures.push('signal analysis result schemaVersion must be signal-analysis.v0');
235
+ }
236
+ if (!SIGNAL_ANALYSIS_TYPES.includes(value.signalType)) {
237
+ failures.push('signal analysis result must include a supported signalType');
238
+ }
239
+ const source = isRecord(value.source) ? value.source : null;
240
+ if (!source) {
241
+ failures.push('signal analysis result must include source metadata');
242
+ }
243
+ else {
244
+ if (!isNonEmptyString(source.path)) {
245
+ failures.push('signal source path must be a non-empty sanitized label');
246
+ }
247
+ else if (hasPrivateOrSecretText(source.path) || /[\\/]/.test(source.path)) {
248
+ failures.push('signal source path must not include local directories, separators, or token-shaped values');
249
+ }
250
+ if (source.format !== 'csv' && source.format !== 'xlsx') {
251
+ failures.push('signal source format must be csv or xlsx');
252
+ }
253
+ if (!isNonNegativeInteger(source.rowsAnalyzed)) {
254
+ failures.push('signal source rowsAnalyzed must be a nonnegative integer');
255
+ }
256
+ if (!isNonNegativeInteger(source.rowsRejected)) {
257
+ failures.push('signal source rowsRejected must be a nonnegative integer');
258
+ }
259
+ }
260
+ const columns = isRecord(value.columns) ? value.columns : null;
261
+ if (!columns || !isNonEmptyString(columns.value)) {
262
+ failures.push('signal analysis result must include a value column');
263
+ }
264
+ if (columns && !isNonEmptyString(columns.timestamp) && !isNonEmptyString(columns.depth)) {
265
+ failures.push('signal analysis result must include a timestamp or depth column');
266
+ }
267
+ if (!Array.isArray(value.trendSummary) || value.trendSummary.length === 0) {
268
+ failures.push('signal analysis result must include trendSummary entries');
269
+ }
270
+ if (!Array.isArray(value.rateOfChange) || value.rateOfChange.length === 0) {
271
+ failures.push('signal analysis result must include rateOfChange entries');
272
+ }
273
+ if (!Array.isArray(value.series) || value.series.length === 0) {
274
+ failures.push('signal analysis result must include chart-ready series');
275
+ }
276
+ if (!Array.isArray(value.thresholdFlags)) {
277
+ failures.push('signal analysis result must include thresholdFlags array');
278
+ }
279
+ if (!Array.isArray(value.missingIntervals)) {
280
+ failures.push('signal analysis result must include missingIntervals array');
281
+ }
282
+ if (!Array.isArray(value.warnings)) {
283
+ failures.push('signal analysis result must include warnings array');
284
+ }
285
+ for (const [index, trend] of (Array.isArray(value.trendSummary) ? value.trendSummary : []).entries()) {
286
+ if (!isRecord(trend)) {
287
+ failures.push(`signal trendSummary[${index}] must be an object`);
288
+ continue;
289
+ }
290
+ if (!isNonEmptyString(trend.seriesId)) {
291
+ failures.push(`signal trendSummary[${index}] must include seriesId`);
292
+ }
293
+ if (!isNonNegativeInteger(trend.count)) {
294
+ failures.push(`signal trendSummary[${index}] must include count`);
295
+ }
296
+ if (!['increasing', 'decreasing', 'stable', 'insufficient-data'].includes(String(trend.direction))) {
297
+ failures.push(`signal trendSummary[${index}] has invalid direction`);
298
+ }
299
+ if (!['per-day', 'per-meter', 'per-sample'].includes(String(trend.slopeUnit))) {
300
+ failures.push(`signal trendSummary[${index}] has invalid slopeUnit`);
301
+ }
302
+ }
303
+ for (const [index, rate] of (Array.isArray(value.rateOfChange) ? value.rateOfChange : []).entries()) {
304
+ if (!isRecord(rate)) {
305
+ failures.push(`signal rateOfChange[${index}] must be an object`);
306
+ continue;
307
+ }
308
+ if (!isNonEmptyString(rate.seriesId)) {
309
+ failures.push(`signal rateOfChange[${index}] must include seriesId`);
310
+ }
311
+ if (!['per-day', 'per-meter', 'per-sample'].includes(String(rate.unit))) {
312
+ failures.push(`signal rateOfChange[${index}] has invalid unit`);
313
+ }
314
+ }
315
+ const profile = isRecord(value.thresholdProfile) ? value.thresholdProfile : null;
316
+ if (profile) {
317
+ if (!SIGNAL_THRESHOLD_PROFILE_IDS.includes(profile.id)) {
318
+ failures.push('signal threshold profile id must be supported');
319
+ }
320
+ if (profile.signalType !== value.signalType) {
321
+ failures.push('signal threshold profile type must match result signalType');
322
+ }
323
+ if (profile.source !== 'explicit-profile' && profile.source !== 'auto-profile') {
324
+ failures.push('signal threshold profile source must be explicit-profile or auto-profile');
325
+ }
326
+ if (!Array.isArray(profile.reviewGates) || profile.reviewGates.length === 0) {
327
+ failures.push('signal threshold profile must include review gates');
328
+ }
329
+ if (!isRecord(profile.explicitOverrides)) {
330
+ failures.push('signal threshold profile must expose explicit override flags');
331
+ }
332
+ if (!isNonEmptyString(profile.basis) || !/project-specific|generic|review/i.test(profile.basis)) {
333
+ failures.push('signal threshold profile basis must clearly mark generic review usage');
334
+ }
335
+ }
336
+ for (const [index, flag] of (Array.isArray(value.thresholdFlags) ? value.thresholdFlags : []).entries()) {
337
+ if (!isRecord(flag)) {
338
+ failures.push(`signal thresholdFlags[${index}] must be an object`);
339
+ continue;
340
+ }
341
+ if (!['value-threshold', 'rate-threshold'].includes(String(flag.kind))) {
342
+ failures.push(`signal thresholdFlags[${index}] has invalid kind`);
343
+ }
344
+ if (flag.source === 'threshold-profile' && !SIGNAL_THRESHOLD_PROFILE_IDS.includes(flag.profileId)) {
345
+ failures.push(`signal thresholdFlags[${index}] profile flag must include supported profileId`);
346
+ }
347
+ }
348
+ for (const [index, interval] of (Array.isArray(value.missingIntervals) ? value.missingIntervals : []).entries()) {
349
+ if (!isRecord(interval)) {
350
+ failures.push(`signal missingIntervals[${index}] must be an object`);
351
+ continue;
352
+ }
353
+ if (!isNonEmptyString(interval.seriesId)) {
354
+ failures.push(`signal missingIntervals[${index}] must include seriesId`);
355
+ }
356
+ if (typeof interval.gapHours !== 'number' || interval.gapHours <= 0) {
357
+ failures.push(`signal missingIntervals[${index}] must include positive gapHours`);
358
+ }
359
+ if (!isNonNegativeInteger(interval.missingIntervals) || interval.missingIntervals < 1) {
360
+ failures.push(`signal missingIntervals[${index}] must include at least one missing interval`);
361
+ }
362
+ }
363
+ for (const keyPath of collectSignalOutputProhibitedKeys(value)) {
364
+ failures.push(`signal output must remain deterministic; prohibited model/secret key found at ${keyPath}`);
365
+ }
366
+ for (const leak of collectSignalOutputPrivateLeaks(value)) {
367
+ failures.push(`signal output must not leak private paths or tokens at ${leak}`);
368
+ }
369
+ return {
370
+ schemaVersion: 'signal-analysis-result-contract.v1',
371
+ ok: failures.length === 0,
372
+ failures: [...new Set(failures)],
373
+ };
374
+ }
66
375
  function normalizeHeader(value) {
67
376
  return value.toLowerCase().replace(/[^a-z0-9]+/g, '');
68
377
  }
@@ -183,6 +492,12 @@ function toTimestamp(value) {
183
492
  return undefined;
184
493
  return date.toISOString();
185
494
  }
495
+ function sanitizeSignalSourcePath(value) {
496
+ const normalized = value.trim().replace(/\\/g, '/');
497
+ const lastSegment = basename(normalized);
498
+ const safe = lastSegment.replace(/[^A-Za-z0-9._-]+/g, '-').replace(/^-+|-+$/g, '');
499
+ return safe || 'signal-input';
500
+ }
186
501
  function round(value) {
187
502
  return Number(value.toFixed(6));
188
503
  }
@@ -471,7 +786,7 @@ export async function analyzeSignalFile(filePath, options = {}) {
471
786
  return {
472
787
  schemaVersion: 'signal-analysis.v0',
473
788
  source: {
474
- path: options.sourcePath ?? filePath,
789
+ path: sanitizeSignalSourcePath(options.sourcePath ?? filePath),
475
790
  format: table.format,
476
791
  sheetName: table.sheetName,
477
792
  rowsAnalyzed: built.points.length,
@@ -488,4 +803,336 @@ export async function analyzeSignalFile(filePath, options = {}) {
488
803
  warnings: [...table.warnings, ...built.warnings, ...profileResult.warnings],
489
804
  };
490
805
  }
806
+ function validateSignalBenchmarkArtifacts(artifacts, failures) {
807
+ if (!isRecord(artifacts)) {
808
+ failures.push('artifacts_missing');
809
+ return;
810
+ }
811
+ for (const key of ['comparison', 'summarySvg', 'directSignal', 'directSignalPlotHtml', 'history', 'trend', 'trendHtml', 'signalIndex']) {
812
+ if (!isNonEmptyString(artifacts[key])) {
813
+ failures.push(`artifact_${key}_missing`);
814
+ }
815
+ }
816
+ }
817
+ function validateSignalBenchmarkWorkflow(workflow, failures, requireZeroModelCalls) {
818
+ if (!isRecord(workflow)) {
819
+ failures.push('workflow_missing');
820
+ return;
821
+ }
822
+ if (workflow.task !== 'signal-analysis') {
823
+ failures.push('workflow_task_not_signal_analysis');
824
+ }
825
+ if (!isNonEmptyString(workflow.status)) {
826
+ failures.push('workflow_status_missing');
827
+ }
828
+ if (workflow.llmRole !== 'none') {
829
+ failures.push('workflow_llm_role_not_none');
830
+ }
831
+ if (!isNonNegativeInteger(workflow.modelCallsBytes)) {
832
+ failures.push('workflow_model_calls_bytes_invalid');
833
+ }
834
+ else if (requireZeroModelCalls && workflow.modelCallsBytes !== 0) {
835
+ failures.push('workflow_model_calls_not_empty');
836
+ }
837
+ if (!isNonNegativeInteger(workflow.toolCallCount) || workflow.toolCallCount < 1) {
838
+ failures.push('workflow_tool_calls_missing');
839
+ }
840
+ }
841
+ function validateSignalBenchmarkSignalArtifacts(signalArtifacts, failures, options) {
842
+ if (!isRecord(signalArtifacts)) {
843
+ failures.push('signal_artifacts_missing');
844
+ return;
845
+ }
846
+ if (!isNonNegativeInteger(signalArtifacts.sources) || signalArtifacts.sources < options.minSources) {
847
+ failures.push('signal_sources_below_minimum');
848
+ }
849
+ if (!isNonNegativeInteger(signalArtifacts.analyzedSources)) {
850
+ failures.push('signal_analyzed_sources_invalid');
851
+ }
852
+ else if (isNonNegativeInteger(signalArtifacts.sources) && signalArtifacts.analyzedSources !== signalArtifacts.sources) {
853
+ failures.push('signal_analyzed_sources_mismatch');
854
+ }
855
+ if (signalArtifacts.blockedSources !== 0) {
856
+ failures.push('signal_blocked_sources_present');
857
+ }
858
+ if (!isNonNegativeInteger(signalArtifacts.rowsAnalyzed) || signalArtifacts.rowsAnalyzed < options.minRowsAnalyzed) {
859
+ failures.push('signal_rows_analyzed_below_minimum');
860
+ }
861
+ if (!isNonNegativeInteger(signalArtifacts.series) || signalArtifacts.series < options.minSources) {
862
+ failures.push('signal_series_below_minimum');
863
+ }
864
+ if (!isNonNegativeInteger(signalArtifacts.thresholdFlags)) {
865
+ failures.push('signal_threshold_flags_invalid');
866
+ }
867
+ if (!isNonNegativeInteger(signalArtifacts.missingIntervals)) {
868
+ failures.push('signal_missing_intervals_invalid');
869
+ }
870
+ const sourceTypes = isRecord(signalArtifacts.sourceTypes) ? signalArtifacts.sourceTypes : null;
871
+ if (!sourceTypes) {
872
+ failures.push('signal_source_types_missing');
873
+ return;
874
+ }
875
+ for (const type of options.requiredTypes) {
876
+ if (!isNonNegativeInteger(sourceTypes[type]) || sourceTypes[type] < 1) {
877
+ failures.push(`signal_source_type_${type}_missing`);
878
+ }
879
+ }
880
+ if (isNonNegativeInteger(sourceTypes.unknown) && sourceTypes.unknown > 0) {
881
+ failures.push('signal_source_type_unknown_present');
882
+ }
883
+ }
884
+ function validateSignalBenchmarkDirectSignal(directSignal, failures, options) {
885
+ if (!isRecord(directSignal)) {
886
+ failures.push('direct_signal_missing');
887
+ return;
888
+ }
889
+ if (!isNonNegativeInteger(directSignal.rowsAnalyzed) || directSignal.rowsAnalyzed < options.minDirectRowsAnalyzed) {
890
+ failures.push('direct_signal_rows_below_minimum');
891
+ }
892
+ if (directSignal.thresholdProfile !== 'settlement-review-mm') {
893
+ failures.push('direct_signal_threshold_profile_not_settlement_review');
894
+ }
895
+ if (!isNonNegativeInteger(directSignal.thresholdFlags) || directSignal.thresholdFlags < options.minDirectThresholdFlags) {
896
+ failures.push('direct_signal_threshold_flags_below_minimum');
897
+ }
898
+ if (!isNonNegativeInteger(directSignal.rateThresholdFlags) || directSignal.rateThresholdFlags < options.minDirectRateThresholdFlags) {
899
+ failures.push('direct_signal_rate_threshold_flags_below_minimum');
900
+ }
901
+ if (!isNonNegativeInteger(directSignal.missingIntervals) || directSignal.missingIntervals < options.minDirectMissingIntervals) {
902
+ failures.push('direct_signal_missing_intervals_below_minimum');
903
+ }
904
+ if (!isNonNegativeInteger(directSignal.series) || directSignal.series < 1) {
905
+ failures.push('direct_signal_series_missing');
906
+ }
907
+ }
908
+ function validateSignalBenchmarkDirectSignalPlot(directSignalPlot, failures, options) {
909
+ if (!isRecord(directSignalPlot)) {
910
+ failures.push('direct_signal_plot_missing');
911
+ return;
912
+ }
913
+ if (!isNonNegativeInteger(directSignalPlot.htmlBytes) || directSignalPlot.htmlBytes < options.minDirectPlotHtmlBytes) {
914
+ failures.push('direct_signal_plot_html_bytes_below_minimum');
915
+ }
916
+ if (!isNonNegativeInteger(directSignalPlot.chartCount) || directSignalPlot.chartCount < options.minDirectPlotChartCount) {
917
+ failures.push('direct_signal_plot_chart_count_below_minimum');
918
+ }
919
+ if (!isNonNegativeInteger(directSignalPlot.pointCount) || directSignalPlot.pointCount < 1) {
920
+ failures.push('direct_signal_plot_points_missing');
921
+ }
922
+ if (!Array.isArray(directSignalPlot.chartIds) || !directSignalPlot.chartIds.some((id) => id === 'signal-values' || id === 'signal-depth-profile')) {
923
+ failures.push('direct_signal_plot_signal_chart_missing');
924
+ }
925
+ if (!Array.isArray(directSignalPlot.seriesLabels) || directSignalPlot.seriesLabels.length === 0) {
926
+ failures.push('direct_signal_plot_series_labels_missing');
927
+ }
928
+ const pathSafety = isRecord(directSignalPlot.pathSafety) ? directSignalPlot.pathSafety : null;
929
+ if (!pathSafety) {
930
+ failures.push('direct_signal_plot_path_safety_missing');
931
+ return;
932
+ }
933
+ if (pathSafety.checked !== true) {
934
+ failures.push('direct_signal_plot_path_safety_not_checked');
935
+ }
936
+ if (!Array.isArray(pathSafety.leaks)) {
937
+ failures.push('direct_signal_plot_path_safety_leaks_missing');
938
+ }
939
+ else if (pathSafety.leaks.length > 0) {
940
+ failures.push('direct_signal_plot_path_safety_leaks_present');
941
+ }
942
+ }
943
+ function validateSignalBenchmarkHistoryEntry(entry, failures, prefix, options) {
944
+ if (!isRecord(entry)) {
945
+ failures.push(`${prefix}_history_entry_missing`);
946
+ return;
947
+ }
948
+ if (entry.kind !== 'signal-analysis-benchmark-history-entry') {
949
+ failures.push(`${prefix}_history_wrong_kind`);
950
+ }
951
+ if (entry.schemaVersion !== 1) {
952
+ failures.push(`${prefix}_history_wrong_schema_version`);
953
+ }
954
+ if (!isNonEmptyString(entry.generatedAt)) {
955
+ failures.push(`${prefix}_history_missing_generated_at`);
956
+ }
957
+ if (options.requirePassed && entry.passed !== true) {
958
+ failures.push(`${prefix}_history_not_passed`);
959
+ }
960
+ const summary = isRecord(entry.summary) ? entry.summary : null;
961
+ if (!summary) {
962
+ failures.push(`${prefix}_history_summary_missing`);
963
+ return;
964
+ }
965
+ for (const key of [
966
+ 'sources',
967
+ 'analyzedSources',
968
+ 'blockedSources',
969
+ 'rowsAnalyzed',
970
+ 'series',
971
+ 'directThresholdFlags',
972
+ 'directRateThresholdFlags',
973
+ 'directMissingIntervals',
974
+ 'modelCallsBytes',
975
+ 'pathLeakCount',
976
+ ]) {
977
+ if (!isNonNegativeInteger(summary[key])) {
978
+ failures.push(`${prefix}_history_${key}_invalid`);
979
+ }
980
+ }
981
+ if (!options.requirePassed) {
982
+ return;
983
+ }
984
+ if (summary.sources < options.minSources) {
985
+ failures.push(`${prefix}_history_sources_below_minimum`);
986
+ }
987
+ if (summary.analyzedSources !== summary.sources) {
988
+ failures.push(`${prefix}_history_analyzed_sources_mismatch`);
989
+ }
990
+ if (summary.blockedSources !== 0) {
991
+ failures.push(`${prefix}_history_blocked_sources_present`);
992
+ }
993
+ if (summary.rowsAnalyzed < options.minRowsAnalyzed) {
994
+ failures.push(`${prefix}_history_rows_below_minimum`);
995
+ }
996
+ if (summary.series < options.minSources) {
997
+ failures.push(`${prefix}_history_series_below_minimum`);
998
+ }
999
+ if (summary.directThresholdFlags < options.minDirectThresholdFlags) {
1000
+ failures.push(`${prefix}_history_direct_threshold_flags_below_minimum`);
1001
+ }
1002
+ if (summary.directRateThresholdFlags < options.minDirectRateThresholdFlags) {
1003
+ failures.push(`${prefix}_history_direct_rate_threshold_flags_below_minimum`);
1004
+ }
1005
+ if (summary.directMissingIntervals < options.minDirectMissingIntervals) {
1006
+ failures.push(`${prefix}_history_direct_missing_intervals_below_minimum`);
1007
+ }
1008
+ if (!isNonNegativeInteger(summary.directPlotHtmlBytes) || summary.directPlotHtmlBytes < options.minDirectPlotHtmlBytes) {
1009
+ failures.push(`${prefix}_history_direct_plot_html_bytes_below_minimum`);
1010
+ }
1011
+ if (!isNonNegativeInteger(summary.directPlotChartCount) || summary.directPlotChartCount < options.minDirectPlotChartCount) {
1012
+ failures.push(`${prefix}_history_direct_plot_chart_count_below_minimum`);
1013
+ }
1014
+ if (options.requireZeroModelCalls && summary.modelCallsBytes !== 0) {
1015
+ failures.push(`${prefix}_history_model_calls_not_empty`);
1016
+ }
1017
+ if (summary.pathLeakCount !== 0) {
1018
+ failures.push(`${prefix}_history_path_leaks_present`);
1019
+ }
1020
+ const sourceTypes = isRecord(summary.sourceTypes) ? summary.sourceTypes : null;
1021
+ if (!sourceTypes) {
1022
+ failures.push(`${prefix}_history_source_types_missing`);
1023
+ return;
1024
+ }
1025
+ for (const type of options.requiredTypes) {
1026
+ if (!isNonNegativeInteger(sourceTypes[type]) || sourceTypes[type] < 1) {
1027
+ failures.push(`${prefix}_history_source_type_${type}_missing`);
1028
+ }
1029
+ }
1030
+ }
1031
+ function validateSignalBenchmarkPathSafety(value, declaredPathSafety, failures, prefix) {
1032
+ const inspected = inspectSignalAnalysisBenchmarkPathSafety(value);
1033
+ if (inspected.leaks.length > 0) {
1034
+ failures.push(...inspected.leaks.map((leak) => `${prefix}_sensitive_value_leak_${sanitizeSignalBenchmarkFailureToken(leak)}`));
1035
+ }
1036
+ if (declaredPathSafety === null) {
1037
+ return;
1038
+ }
1039
+ if (!isRecord(declaredPathSafety)) {
1040
+ failures.push(`${prefix}_path_safety_missing`);
1041
+ return;
1042
+ }
1043
+ if (declaredPathSafety.checked !== true) {
1044
+ failures.push(`${prefix}_path_safety_not_checked`);
1045
+ }
1046
+ if (!Array.isArray(declaredPathSafety.leaks)) {
1047
+ failures.push(`${prefix}_path_safety_leaks_missing`);
1048
+ }
1049
+ else if (declaredPathSafety.leaks.length > 0) {
1050
+ failures.push(`${prefix}_path_safety_leaks_present`);
1051
+ }
1052
+ }
1053
+ function buildSignalBenchmarkContractValidation(failures, warnings) {
1054
+ return {
1055
+ schemaVersion: 'signal-analysis-benchmark-contract.v1',
1056
+ ok: failures.length === 0,
1057
+ failures: [...new Set(failures)],
1058
+ warnings: [...new Set(warnings)],
1059
+ };
1060
+ }
1061
+ function collectSignalBenchmarkPathLeaks(value, location = 'report') {
1062
+ if (typeof value === 'string') {
1063
+ const leaks = [];
1064
+ if (looksLikeSignalBenchmarkAbsolutePath(value)) {
1065
+ leaks.push(`${location}:absolute-path`);
1066
+ }
1067
+ if (looksLikeSignalBenchmarkSecret(value)) {
1068
+ leaks.push(`${location}:secret-like-value`);
1069
+ }
1070
+ return leaks;
1071
+ }
1072
+ if (Array.isArray(value)) {
1073
+ return value.flatMap((item, index) => collectSignalBenchmarkPathLeaks(item, `${location}_${index}`));
1074
+ }
1075
+ if (!isRecord(value)) {
1076
+ return [];
1077
+ }
1078
+ return Object.entries(value).flatMap(([key, child]) => collectSignalBenchmarkPathLeaks(child, `${location}_${sanitizeSignalBenchmarkFailureToken(key)}`));
1079
+ }
1080
+ function looksLikeSignalBenchmarkAbsolutePath(value) {
1081
+ return /\b[A-Za-z]:[\\/][^\s"',}<\]]+/.test(value)
1082
+ || /(^|[\s"'([{])\/(?:Users|home|tmp|var|private|mnt|Volumes|etc)\/[^\s"',}<\]]+/i.test(value);
1083
+ }
1084
+ function looksLikeSignalBenchmarkSecret(value) {
1085
+ return /(sk-or-v1-[A-Za-z0-9_-]{8,}|sk-ant-[A-Za-z0-9_-]{8,}|sk-[A-Za-z0-9_-]{8,}|hf_[A-Za-z0-9_-]{8,}|Bearer\s+[A-Za-z0-9._-]{16,}|(?:api[_-]?key|token|secret)\s*[:=]\s*[A-Za-z0-9._-]{8,})/i.test(value);
1086
+ }
1087
+ function sanitizeSignalBenchmarkFailureToken(value) {
1088
+ return value.replace(/[^A-Za-z0-9]+/g, '_').replace(/^_+|_+$/g, '').slice(0, 96) || 'value';
1089
+ }
1090
+ function isRecord(value) {
1091
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
1092
+ }
1093
+ function isNonEmptyString(value) {
1094
+ return typeof value === 'string' && value.trim().length > 0;
1095
+ }
1096
+ function isNonNegativeInteger(value) {
1097
+ return typeof value === 'number' && Number.isInteger(value) && value >= 0;
1098
+ }
1099
+ function hasPrivateOrSecretText(value) {
1100
+ return /(?:[A-Za-z]:[\\/](?:Users|home|tmp|var|mnt)[\\/]|\/(?:home|Users|tmp|var|mnt)\/|sk-(?:or-)?[A-Za-z0-9_-]{12,}|api[_-]?key\s*[:=]\s*[A-Za-z0-9_-]{12,})/i.test(value);
1101
+ }
1102
+ function collectSignalOutputProhibitedKeys(value) {
1103
+ const paths = [];
1104
+ walkUnknown(value, (entry, path) => {
1105
+ if (!isRecord(entry)) {
1106
+ return;
1107
+ }
1108
+ for (const key of Object.keys(entry)) {
1109
+ if (SIGNAL_OUTPUT_PROHIBITED_KEYS.has(key)) {
1110
+ paths.push(path ? `${path}.${key}` : key);
1111
+ }
1112
+ }
1113
+ });
1114
+ return paths;
1115
+ }
1116
+ function collectSignalOutputPrivateLeaks(value) {
1117
+ const paths = [];
1118
+ walkUnknown(value, (entry, path) => {
1119
+ if (typeof entry === 'string' && hasPrivateOrSecretText(entry)) {
1120
+ paths.push(path || '<root>');
1121
+ }
1122
+ });
1123
+ return paths;
1124
+ }
1125
+ function walkUnknown(value, visit, path = '') {
1126
+ visit(value, path);
1127
+ if (Array.isArray(value)) {
1128
+ value.forEach((item, index) => walkUnknown(item, visit, `${path}[${index}]`));
1129
+ return;
1130
+ }
1131
+ if (!isRecord(value)) {
1132
+ return;
1133
+ }
1134
+ for (const [key, entry] of Object.entries(value)) {
1135
+ walkUnknown(entry, visit, path ? `${path}.${key}` : key);
1136
+ }
1137
+ }
491
1138
  //# sourceMappingURL=index.js.map