@sanity/ailf 2.9.0 → 3.0.0

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 (42) hide show
  1. package/dist/_vendor/ailf-core/artifact-registry.d.ts +1 -1
  2. package/dist/_vendor/ailf-core/artifact-registry.js +1 -18
  3. package/dist/_vendor/ailf-core/batch-signing.d.ts +64 -0
  4. package/dist/_vendor/ailf-core/batch-signing.js +23 -0
  5. package/dist/_vendor/ailf-core/index.d.ts +1 -1
  6. package/dist/_vendor/ailf-core/index.js +1 -1
  7. package/dist/_vendor/ailf-core/ports/context.d.ts +4 -20
  8. package/dist/_vendor/ailf-core/ports/index.d.ts +0 -2
  9. package/dist/adapters/config-sources/file-config-adapter.js +0 -4
  10. package/dist/artifact-capture/redact-artifact.d.ts +3 -5
  11. package/dist/artifact-capture/redact-artifact.js +3 -5
  12. package/dist/cli.js +56 -2
  13. package/dist/commands/explain-handler.js +1 -5
  14. package/dist/commands/pipeline-action.d.ts +0 -4
  15. package/dist/commands/pipeline-action.js +11 -45
  16. package/dist/commands/pipeline.d.ts +1 -5
  17. package/dist/commands/pipeline.js +1 -5
  18. package/dist/commands/runs.d.ts +18 -0
  19. package/dist/commands/runs.js +71 -0
  20. package/dist/composition-root.js +2 -28
  21. package/dist/orchestration/build-app-context.js +4 -7
  22. package/dist/orchestration/pipeline-orchestrator.js +3 -23
  23. package/dist/pipeline/map-request-to-config.js +0 -4
  24. package/package.json +1 -1
  25. package/dist/_vendor/ailf-core/artifact-capture/noop-collector.d.ts +0 -14
  26. package/dist/_vendor/ailf-core/artifact-capture/noop-collector.js +0 -25
  27. package/dist/_vendor/ailf-core/ports/artifact-collector.d.ts +0 -94
  28. package/dist/_vendor/ailf-core/ports/artifact-collector.js +0 -13
  29. package/dist/_vendor/ailf-core/ports/capture-comparator.d.ts +0 -138
  30. package/dist/_vendor/ailf-core/ports/capture-comparator.js +0 -10
  31. package/dist/artifact-capture/comparator.d.ts +0 -22
  32. package/dist/artifact-capture/comparator.js +0 -493
  33. package/dist/artifact-capture/filesystem-collector.d.ts +0 -60
  34. package/dist/artifact-capture/filesystem-collector.js +0 -262
  35. package/dist/artifact-capture/gcs-collector.d.ts +0 -55
  36. package/dist/artifact-capture/gcs-collector.js +0 -117
  37. package/dist/commands/capture-compare.d.ts +0 -15
  38. package/dist/commands/capture-compare.js +0 -253
  39. package/dist/commands/capture-list.d.ts +0 -12
  40. package/dist/commands/capture-list.js +0 -150
  41. package/dist/commands/capture.d.ts +0 -9
  42. package/dist/commands/capture.js +0 -16
@@ -15,13 +15,10 @@
15
15
  * @see packages/core/src/ports/context.ts — AppContext interface
16
16
  * @see docs/archive/exec-plans/ports-and-adapters/phase-7-composition-root.md
17
17
  */
18
- import { join } from "node:path";
19
- import { InMemoryPluginRegistry, NoOpArtifactCollector, NoOpArtifactWriter, generateRunId, isArtifactType, } from "./_vendor/ailf-core/index.js";
18
+ import { InMemoryPluginRegistry, NoOpArtifactWriter, generateRunId, isArtifactType, } from "./_vendor/ailf-core/index.js";
20
19
  import { AccumulatingArtifactWriter } from "./artifact-capture/accumulating-artifact-writer.js";
21
20
  import { ApiGatewayArtifactWriter } from "./artifact-capture/api-gateway-artifact-writer.js";
22
21
  import { FanoutArtifactWriter } from "./artifact-capture/fanout-artifact-writer.js";
23
- import { FilesystemArtifactCollector } from "./artifact-capture/filesystem-collector.js";
24
- import { GcsArtifactCollector } from "./artifact-capture/gcs-collector.js";
25
22
  import { GcsArtifactWriter } from "./artifact-capture/gcs-artifact-writer.js";
26
23
  import { LocalFilesystemArtifactWriter } from "./artifact-capture/local-fs-artifact-writer.js";
27
24
  import { ContentLakeCacheAdapter } from "./adapters/cache/content-lake-cache.js";
@@ -63,28 +60,6 @@ export function createAppContext(config) {
63
60
  const reportStore = createReportStore(config);
64
61
  // Sinks — loaded from config/sinks
65
62
  const sinks = loadSinks();
66
- // Artifact collector — no-op by default, filesystem when --capture is set,
67
- // GCS decorator when --capture-gcs-bucket is also provided (D0030/W0035)
68
- let collector = new NoOpArtifactCollector();
69
- if (config.captureEnabled) {
70
- const fsCollector = new FilesystemArtifactCollector({
71
- captureDir: config.captureDir ?? join(config.outputDir, "..", "captures"),
72
- mode: config.mode,
73
- compress: config.captureCompress ?? true,
74
- extras: config.captureExtras ?? true,
75
- pipeline: {
76
- variant: config.variant,
77
- source: config.source,
78
- areas: config.areas,
79
- },
80
- });
81
- collector = config.captureGcsBucket
82
- ? new GcsArtifactCollector(fsCollector, {
83
- bucket: config.captureGcsBucket,
84
- prefix: config.captureGcsPrefix,
85
- })
86
- : fsCollector;
87
- }
88
63
  // Artifact writer — writes run artifacts + manifest to GCS at known
89
64
  // `runs/{runId}/…` paths (D0032). Auto-detects the right adapter from
90
65
  // available credentials; defaults bucket to "ailf-artifacts". Set
@@ -97,7 +72,6 @@ export function createAppContext(config) {
97
72
  return {
98
73
  artifactWriter,
99
74
  cache,
100
- collector,
101
75
  config,
102
76
  docFetcher,
103
77
  evalRunner,
@@ -194,7 +168,7 @@ function resolveExcludeList(raw, logger) {
194
168
  valid.push(name);
195
169
  }
196
170
  else {
197
- logger.warn(`--capture-exclude: "${name}" is not a known artifact type — ignored`);
171
+ logger.warn(`--artifacts-exclude: "${name}" is not a known artifact type — ignored`);
198
172
  }
199
173
  }
200
174
  return valid;
@@ -8,7 +8,6 @@
8
8
  * Once all commands construct ResolvedConfig directly (or use --config),
9
9
  * this bridge can be deleted.
10
10
  */
11
- import { join } from "node:path";
12
11
  import { createAppContext } from "../composition-root.js";
13
12
  import { tryLoadConfigFile } from "../pipeline/compiler/config-loader.js";
14
13
  /**
@@ -78,12 +77,10 @@ export function mapToResolvedConfig(opts, rootDir) {
78
77
  remote: opts.remote ?? false,
79
78
  apiUrl: opts.apiUrl ?? "https://ailf-api.sanity.build",
80
79
  apiKey: opts.apiKey,
81
- captureEnabled: opts.captureEnabled ?? false,
82
- captureDir: opts.captureDir ?? join(opts.outputDir, "..", "captures"),
83
- captureCompress: opts.captureCompress ?? true,
84
- captureExtras: opts.captureExtras ?? true,
85
- captureGcsBucket: process.env.AILF_CAPTURE_GCS_BUCKET,
86
- captureGcsPrefix: process.env.AILF_CAPTURE_GCS_PREFIX,
80
+ artifactsDisabled: opts.artifactsDisabled,
81
+ artifactsDir: opts.artifactsDir,
82
+ artifactsDryRun: opts.artifactsDryRun,
83
+ artifactsExclude: opts.artifactsExclude,
87
84
  artifactGcsBucket: process.env.AILF_GCS_ARTIFACT_BUCKET,
88
85
  artifactUpload: parseArtifactUploadEnv(process.env.AILF_ARTIFACT_UPLOAD),
89
86
  };
@@ -111,21 +111,6 @@ async function capturePipelineContext(ctx, state, results) {
111
111
  ctx.logger.debug(`pipelineContext emit rejected: ${err instanceof Error ? err.message : String(err)}`);
112
112
  }
113
113
  }
114
- /**
115
- * Flush captured artifacts to disk. Non-blocking — failures are logged
116
- * but never affect the pipeline result.
117
- */
118
- async function flushArtifacts(ctx) {
119
- if (!ctx.collector.enabled)
120
- return;
121
- try {
122
- const result = await ctx.collector.flush();
123
- ctx.logger.info(`Captured ${result.artifactCount} artifacts → ${result.destination}`);
124
- }
125
- catch (err) {
126
- ctx.logger.warn(`Artifact capture flush failed: ${err instanceof Error ? err.message : err}`);
127
- }
128
- }
129
114
  // ---------------------------------------------------------------------------
130
115
  // Orchestrator
131
116
  // ---------------------------------------------------------------------------
@@ -182,13 +167,10 @@ export async function orchestratePipeline(ctx, steps) {
182
167
  step: step.name,
183
168
  }, jobUpdates);
184
169
  }
185
- // Capture pipeline context and job updates before flushing
170
+ // Capture pipeline context before exiting. `job-updates` was an
171
+ // observability-only capture not tied to a registered artifact type;
172
+ // dropped in W0050. Use the JobStore path for job telemetry.
186
173
  await capturePipelineContext(ctx, state, results);
187
- // W0050 — `job-updates` was an observability-only capture not tied
188
- // to a registered artifact type; dropped here. Use the JobStore
189
- // path if job telemetry is needed.
190
- // Flush captured artifacts even on failure (partial capture is useful)
191
- await flushArtifacts(ctx);
192
174
  return {
193
175
  belowCritical: state.belowCritical,
194
176
  durationMs: Date.now() - pipelineStart,
@@ -245,8 +227,6 @@ export async function orchestratePipeline(ctx, steps) {
245
227
  // Capture pipeline context. `job-updates` observability captures were
246
228
  // dropped in Slice 6.1 — JobStore is the supported telemetry path.
247
229
  await capturePipelineContext(ctx, state, results);
248
- // Flush captured artifacts (non-blocking — failures never affect pipeline result)
249
- await flushArtifacts(ctx);
250
230
  return {
251
231
  belowCritical: state.belowCritical,
252
232
  durationMs,
@@ -74,10 +74,6 @@ export function mapRequestToConfig(request, rootDir) {
74
74
  callerGit: request.callerGit,
75
75
  callback: request.callback,
76
76
  jobId: request.jobId,
77
- captureEnabled: false,
78
- captureDir: undefined,
79
- captureCompress: true,
80
- captureExtras: true,
81
77
  remote: false,
82
78
  apiUrl: "https://ailf-api.sanity.build",
83
79
  presets: request.presets,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanity/ailf",
3
- "version": "2.9.0",
3
+ "version": "3.0.0",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -1,14 +0,0 @@
1
- /**
2
- * No-op artifact collector — used when --capture is not set.
3
- *
4
- * All methods are constant-time stubs. Zero overhead on the
5
- * default pipeline path.
6
- */
7
- import type { ArtifactCollector, CaptureFlushResult } from "../ports/artifact-collector.js";
8
- export declare class NoOpArtifactCollector implements ArtifactCollector {
9
- readonly enabled = false;
10
- readonly extrasEnabled = false;
11
- capture(): void;
12
- captureFile(): void;
13
- flush(): Promise<CaptureFlushResult>;
14
- }
@@ -1,25 +0,0 @@
1
- /**
2
- * No-op artifact collector — used when --capture is not set.
3
- *
4
- * All methods are constant-time stubs. Zero overhead on the
5
- * default pipeline path.
6
- */
7
- const EMPTY_RESULT = Object.freeze({
8
- artifactCount: 0,
9
- compressed: false,
10
- destination: "",
11
- totalBytes: 0,
12
- });
13
- export class NoOpArtifactCollector {
14
- enabled = false;
15
- extrasEnabled = false;
16
- capture() {
17
- // no-op
18
- }
19
- captureFile() {
20
- // no-op
21
- }
22
- async flush() {
23
- return EMPTY_RESULT;
24
- }
25
- }
@@ -1,94 +0,0 @@
1
- /**
2
- * Port: ArtifactCollector — captures pipeline artifacts during execution.
3
- *
4
- * Injected into AppContext. When capture is disabled (default), the
5
- * composition root provides NoOpArtifactCollector. When --capture is
6
- * set, provides FilesystemArtifactCollector.
7
- *
8
- * Design principles:
9
- * - P1: Zero-cost when off (no-op stub)
10
- * - P2: Capture, don't intercept (steps call capture() explicitly)
11
- * - P5: Non-blocking (failures swallowed, never block the pipeline)
12
- */
13
- /**
14
- * The contract for artifact capture during pipeline execution.
15
- *
16
- * Steps call capture() for in-memory data and captureFile() for
17
- * artifacts already on disk. The orchestrator calls flush() once
18
- * at pipeline end to write everything to the configured destination.
19
- */
20
- export interface ArtifactCollector {
21
- /**
22
- * Record an in-memory artifact produced during pipeline execution.
23
- *
24
- * Callers need not check `enabled` before calling — the NoOp
25
- * implementation is zero-cost, so unconditional calls are safe.
26
- *
27
- * @param step - Pipeline step name (e.g., "run-eval")
28
- * @param type - Artifact type identifier (e.g., "eval-results")
29
- * @param data - Content to serialize (JSON or text)
30
- * @param meta - Optional metadata (variant, model, etc.)
31
- */
32
- capture(step: string, type: string, data: unknown, meta?: Record<string, unknown>): void;
33
- /**
34
- * Record a file reference for an artifact already on disk.
35
- * The file is copied into the capture directory on flush().
36
- *
37
- * @param step - Pipeline step name
38
- * @param type - Artifact type identifier
39
- * @param filePath - Absolute path to the existing file
40
- * @param meta - Optional metadata
41
- */
42
- captureFile(step: string, type: string, filePath: string, meta?: Record<string, unknown>): void;
43
- /**
44
- * Flush all captured artifacts to the configured destination.
45
- * Called once at pipeline end by the orchestrator.
46
- */
47
- flush(): Promise<CaptureFlushResult>;
48
- /** Whether capture is active */
49
- readonly enabled: boolean;
50
- /** Whether mode-specific extras are being captured */
51
- readonly extrasEnabled: boolean;
52
- }
53
- /** Result of flushing captured artifacts to the destination. */
54
- export interface CaptureFlushResult {
55
- /** Total number of artifacts captured */
56
- artifactCount: number;
57
- /** Output path (directory or .tar.gz) */
58
- destination: string;
59
- /** Total bytes written (uncompressed) */
60
- totalBytes: number;
61
- /** Whether output was compressed */
62
- compressed: boolean;
63
- }
64
- /** A single entry in the capture manifest. */
65
- export interface CaptureManifestEntry {
66
- /** Pipeline step that produced this artifact */
67
- step: string;
68
- /** Artifact type identifier */
69
- type: string;
70
- /** Relative path within the capture directory */
71
- path: string;
72
- /** ISO 8601 timestamp of when capture() was called */
73
- capturedAt: string;
74
- /** Byte size of the artifact */
75
- bytes: number;
76
- /** Content format */
77
- format: "json" | "markdown" | "text";
78
- /** Optional metadata */
79
- meta?: Record<string, unknown>;
80
- }
81
- /** The manifest.json written to each capture directory. */
82
- export interface CaptureManifest {
83
- version: 1;
84
- captureId: string;
85
- startedAt: string;
86
- completedAt: string;
87
- pipeline: {
88
- mode: string;
89
- variant?: string;
90
- source?: string;
91
- areas?: string[];
92
- };
93
- artifacts: CaptureManifestEntry[];
94
- }
@@ -1,13 +0,0 @@
1
- /**
2
- * Port: ArtifactCollector — captures pipeline artifacts during execution.
3
- *
4
- * Injected into AppContext. When capture is disabled (default), the
5
- * composition root provides NoOpArtifactCollector. When --capture is
6
- * set, provides FilesystemArtifactCollector.
7
- *
8
- * Design principles:
9
- * - P1: Zero-cost when off (no-op stub)
10
- * - P2: Capture, don't intercept (steps call capture() explicitly)
11
- * - P5: Non-blocking (failures swallowed, never block the pipeline)
12
- */
13
- export {};
@@ -1,138 +0,0 @@
1
- /**
2
- * Types for cross-run capture comparison.
3
- *
4
- * The CaptureComparator reads two capture directories (baseline + experiment)
5
- * and produces a CaptureDiffReport. Types are defined in core so external
6
- * tooling can consume diff reports without depending on the eval package.
7
- *
8
- * Implementation lives in packages/eval/src/artifact-capture/comparator.ts.
9
- */
10
- /** How deeply to compare artifacts. */
11
- export type ComparisonMode = "strict" | "structural" | "inventory";
12
- /** Configurable thresholds for comparison. */
13
- export interface ComparisonOptions {
14
- /** Comparison depth: inventory (existence), structural (shape), strict (content) */
15
- mode: ComparisonMode;
16
- /** Score regression thresholds */
17
- scoreThresholds?: {
18
- /** Maximum allowed aggregate score delta (percentage points, default 5) */
19
- aggregate: number;
20
- /** Maximum allowed per-task score drop (points, default 10) */
21
- perTask: number;
22
- };
23
- /** Timing regression thresholds */
24
- timingThresholds?: {
25
- /** Multiplier — flag steps exceeding this ratio (default 2.0) */
26
- multiplier: number;
27
- /** Per-step overrides (step name → custom multiplier) */
28
- perStep?: Record<string, number>;
29
- };
30
- /** JSON structural diff depth (default 3) */
31
- jsonDiffDepth?: number;
32
- /** Additional ephemeral fields to ignore (merged with defaults) */
33
- ephemeralFields?: string[];
34
- }
35
- /** Inventory diff — which artifacts exist in each capture. */
36
- export interface InventoryDiff {
37
- /** Artifact types in experiment but not in baseline */
38
- added: string[];
39
- /** Artifact types in baseline but not in experiment */
40
- removed: string[];
41
- /** Artifact types present in both */
42
- common: string[];
43
- }
44
- /** A single structural change in a JSON artifact. */
45
- export interface JsonDiffEntry {
46
- /** JSON pointer path (e.g., "config.mode") */
47
- path: string;
48
- /** Value in baseline (undefined if key is added) */
49
- baseline?: unknown;
50
- /** Value in experiment (undefined if key is removed) */
51
- experiment?: unknown;
52
- }
53
- /** Content diff for a single artifact. */
54
- export interface ArtifactContentDiff {
55
- /** Artifact type identifier (step/type) */
56
- artifactKey: string;
57
- /** Content format */
58
- format: "json" | "markdown" | "text";
59
- /** Structural changes (JSON) or line diff summary (text/markdown) */
60
- changes: JsonDiffEntry[] | {
61
- addedLines: number;
62
- removedLines: number;
63
- };
64
- }
65
- /** Score comparison between two captures. */
66
- export interface ScoreComparison {
67
- /** Baseline aggregate score */
68
- baselineMean: number;
69
- /** Experiment aggregate score */
70
- currentMean: number;
71
- /** Absolute delta (current - baseline) */
72
- delta: number;
73
- /** Per-task score deltas */
74
- perTask: {
75
- task: string;
76
- baseline: number;
77
- current: number;
78
- delta: number;
79
- }[];
80
- /** Tasks that breached configured thresholds */
81
- breaches: string[];
82
- }
83
- /** Timing comparison between two captures. */
84
- export interface TimingComparison {
85
- /** Total pipeline duration delta in ms */
86
- totalDeltaMs: number;
87
- /** Per-step timing */
88
- perStep: {
89
- step: string;
90
- baselineMs: number;
91
- currentMs: number;
92
- ratio: number;
93
- }[];
94
- /** Steps that breached the timing multiplier threshold */
95
- breaches: string[];
96
- }
97
- /** Metadata comparison between two captures. */
98
- export interface MetadataComparison {
99
- /** Whether pipeline modes match */
100
- modeMatch: boolean;
101
- /** Whether pipeline variants match */
102
- variantMatch: boolean;
103
- /** Config key differences */
104
- configDiffs: JsonDiffEntry[];
105
- }
106
- /** Security scan results. */
107
- export interface SecurityScan {
108
- /** Whether any potential secret leaks were found */
109
- leaksFound: boolean;
110
- /** Details of each violation */
111
- violations: {
112
- /** Relative artifact file path */
113
- file: string;
114
- /** Description of the finding */
115
- detail: string;
116
- }[];
117
- }
118
- /** The full diff report produced by CaptureComparator. */
119
- export interface CaptureDiffReport {
120
- /** Are the two captures semantically equivalent? */
121
- equivalent: boolean;
122
- /** Human-readable summary (1-3 sentences) */
123
- summary: string;
124
- /** Comparison mode used */
125
- mode: ComparisonMode;
126
- /** Artifact inventory diff */
127
- inventory: InventoryDiff;
128
- /** Content diffs for common artifacts (structural/strict modes only) */
129
- content?: ArtifactContentDiff[];
130
- /** Score comparison (if score-summary exists in both captures) */
131
- scores?: ScoreComparison;
132
- /** Timing comparison (if pipeline-context exists in both captures) */
133
- timing?: TimingComparison;
134
- /** Metadata comparison */
135
- metadata?: MetadataComparison;
136
- /** Security scan results */
137
- security: SecurityScan;
138
- }
@@ -1,10 +0,0 @@
1
- /**
2
- * Types for cross-run capture comparison.
3
- *
4
- * The CaptureComparator reads two capture directories (baseline + experiment)
5
- * and produces a CaptureDiffReport. Types are defined in core so external
6
- * tooling can consume diff reports without depending on the eval package.
7
- *
8
- * Implementation lives in packages/eval/src/artifact-capture/comparator.ts.
9
- */
10
- export {};
@@ -1,22 +0,0 @@
1
- /**
2
- * CaptureComparator — compares two capture directories and produces a diff report.
3
- *
4
- * Reads manifest.json from both directories and computes:
5
- * - Inventory diff (added/removed/common artifacts)
6
- * - Content diff (structural or strict, for common artifacts)
7
- * - Score comparison (from score-summary.json)
8
- * - Timing comparison (from pipeline-context.json)
9
- * - Metadata comparison (mode, variant, config keys)
10
- * - Security scan (regex for leaked secrets)
11
- *
12
- * Implementation for the types defined in @sanity/ailf-core.
13
- */
14
- import type { CaptureDiffReport, ComparisonOptions } from "../_vendor/ailf-core/index.d.ts";
15
- /**
16
- * Compare two capture directories and produce a structured diff report.
17
- *
18
- * @param baselineDir - Path to the baseline capture directory (contains manifest.json)
19
- * @param experimentDir - Path to the experiment capture directory
20
- * @param opts - Comparison options (mode, thresholds, etc.)
21
- */
22
- export declare function compareCaptures(baselineDir: string, experimentDir: string, opts?: Partial<ComparisonOptions>): CaptureDiffReport;