@sanity/ailf 3.4.1 → 3.5.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 (34) hide show
  1. package/config/airbyte/ai_literacy_framework.connector.yaml +114 -0
  2. package/config/bigquery/README.md +11 -4
  3. package/config/bigquery/views/official_area_scores.sql +20 -0
  4. package/config/bigquery/views/official_runs.sql +31 -0
  5. package/config/bigquery/views/reports.sql +19 -0
  6. package/config/bigquery/views/team_runs_template.sql +17 -0
  7. package/dist/_vendor/ailf-core/examples/index.d.ts +1 -1
  8. package/dist/_vendor/ailf-core/examples/index.js +1 -1
  9. package/dist/_vendor/ailf-core/ports/context.d.ts +25 -0
  10. package/dist/_vendor/ailf-core/schemas/pipeline-request.d.ts +23 -0
  11. package/dist/_vendor/ailf-core/schemas/pipeline-request.js +59 -1
  12. package/dist/_vendor/ailf-shared/index.d.ts +2 -0
  13. package/dist/_vendor/ailf-shared/index.js +2 -0
  14. package/dist/_vendor/ailf-shared/owner-teams.d.ts +26 -0
  15. package/dist/_vendor/ailf-shared/owner-teams.js +52 -0
  16. package/dist/_vendor/ailf-shared/run-classification.d.ts +100 -0
  17. package/dist/_vendor/ailf-shared/run-classification.js +28 -0
  18. package/dist/_vendor/ailf-shared/run-context.d.ts +23 -0
  19. package/dist/adapters/api-client/build-request.d.ts +31 -0
  20. package/dist/adapters/api-client/build-request.js +82 -1
  21. package/dist/adapters/api-client/index.d.ts +1 -1
  22. package/dist/adapters/api-client/index.js +1 -1
  23. package/dist/commands/explain-handler.js +5 -0
  24. package/dist/commands/pipeline-action.d.ts +6 -0
  25. package/dist/commands/pipeline-action.js +5 -0
  26. package/dist/commands/pipeline.d.ts +5 -0
  27. package/dist/commands/pipeline.js +15 -0
  28. package/dist/commands/remote-pipeline.js +7 -0
  29. package/dist/orchestration/steps/finalize-run-step.js +1 -0
  30. package/dist/orchestration/steps/publish-report-step.js +1 -0
  31. package/dist/pipeline/map-request-to-config.js +18 -0
  32. package/dist/pipeline/run-context.d.ts +63 -0
  33. package/dist/pipeline/run-context.js +166 -0
  34. package/package.json +1 -1
@@ -12,8 +12,13 @@
12
12
  *
13
13
  * @see docs/decisions/D0032-run-anchored-artifact-store.md (§ Move 5 — Drift Prevention)
14
14
  */
15
+ import { execSync } from "node:child_process";
16
+ import { createRequire } from "node:module";
17
+ import * as os from "node:os";
18
+ import { isRunClassification, } from "../_vendor/ailf-shared/index.js";
15
19
  import { ConsoleLogger } from "../adapters/loggers/index.js";
16
20
  import { tryLoadConfigFile } from "./compiler/config-loader.js";
21
+ const requireFromHere = createRequire(import.meta.url);
17
22
  /**
18
23
  * Derive `RunContext` from pipeline inputs. The only construction path.
19
24
  *
@@ -35,6 +40,21 @@ export function buildRunContext(input) {
35
40
  }
36
41
  : detectGitMetadata();
37
42
  const trigger = detectTrigger();
43
+ // D0037: caller envelope (from PipelineRequest) takes precedence over
44
+ // server-env detection — same pattern as `callerGit` for identity
45
+ // preservation across the --remote boundary.
46
+ const envelope = input.callerEnvelope;
47
+ const classification = envelope?.classification ?? detectClassification(log);
48
+ const owner = envelope?.owner ?? detectOwner();
49
+ const executor = envelope?.executor ?? detectExecutor();
50
+ // `tool` and `host` are server-environment facts — they always reflect
51
+ // where this pipeline is actually running, never what a caller claimed.
52
+ // Callers cannot override these via the envelope and the wire schema
53
+ // explicitly doesn't carry them (see pipeline-request.ts).
54
+ const tool = detectTool(log);
55
+ const host = detectHost();
56
+ const labels = envelope?.labels ?? detectLabels();
57
+ const purpose = envelope?.purpose ?? (process.env.AILF_PURPOSE?.trim() || undefined);
38
58
  // Non-literacy modes (agent-harness, mcp-server, etc.) don't use the
39
59
  // config/models.ts model matrix — listing those models would be
40
60
  // misleading. Only include them for literacy mode where they're the
@@ -44,11 +64,18 @@ export function buildRunContext(input) {
44
64
  : [];
45
65
  return {
46
66
  areas: input.areas,
67
+ classification,
47
68
  evalFingerprint: input.evalFingerprint,
69
+ executor,
48
70
  git,
49
71
  graderModel: models.grader.id,
72
+ host,
73
+ labels,
74
+ lineage: input.lineage,
50
75
  mode: input.mode,
51
76
  models: evaluatedModels,
77
+ owner,
78
+ purpose,
52
79
  source: {
53
80
  baseUrl: input.source.baseUrl,
54
81
  dataset: input.source.dataset,
@@ -57,6 +84,7 @@ export function buildRunContext(input) {
57
84
  projectId: input.source.projectId,
58
85
  },
59
86
  taskIds: input.taskIds,
87
+ tool,
60
88
  trigger,
61
89
  };
62
90
  }
@@ -137,6 +165,144 @@ function detectTrigger() {
137
165
  return { type: "manual" };
138
166
  }
139
167
  // ---------------------------------------------------------------------------
168
+ // Classification, owner, executor, labels, tool, host detection (D0037)
169
+ // ---------------------------------------------------------------------------
170
+ /**
171
+ * Resolve `classification` from `AILF_CLASSIFICATION`, validated against
172
+ * the closed enum. Defaults to `"ad-hoc"` so unannotated runs never leak
173
+ * into the canonical `"official"` series.
174
+ */
175
+ export function detectClassification(log) {
176
+ const raw = process.env.AILF_CLASSIFICATION?.trim();
177
+ if (!raw)
178
+ return "ad-hoc";
179
+ if (isRunClassification(raw))
180
+ return raw;
181
+ log.warn(`AILF_CLASSIFICATION="${raw}" is not a recognized value; defaulting to "ad-hoc"`);
182
+ return "ad-hoc";
183
+ }
184
+ /**
185
+ * Resolve `owner` from `AILF_OWNER_TEAM` (+ optional
186
+ * `AILF_OWNER_INDIVIDUAL`). `team` is free-form; default is `"unknown"`.
187
+ */
188
+ export function detectOwner() {
189
+ const team = process.env.AILF_OWNER_TEAM?.trim() || "unknown";
190
+ const individual = process.env.AILF_OWNER_INDIVIDUAL?.trim() || undefined;
191
+ return individual ? { individual, team } : { team };
192
+ }
193
+ /**
194
+ * Detect who/what invoked the run.
195
+ *
196
+ * Priority:
197
+ * 1. GitHub Actions context → `{ type: "system", name: "github-actions", ... }`
198
+ * 2. CLI context → `{ type: "user", surface: "cli", ... }` with git-config
199
+ * or OS username fallback. Email capture gated by
200
+ * `AILF_CAPTURE_EMAIL` (default on; set `0` to opt out).
201
+ *
202
+ * Every identity field is optional — missing git, containers, or masked
203
+ * env vars must never block a run.
204
+ */
205
+ export function detectExecutor() {
206
+ if (process.env.GITHUB_ACTIONS === "true") {
207
+ return {
208
+ name: "github-actions",
209
+ runId: process.env.GITHUB_RUN_ID?.trim() || undefined,
210
+ type: "system",
211
+ workflow: process.env.GITHUB_WORKFLOW?.trim() || undefined,
212
+ };
213
+ }
214
+ const surface = resolveExecutorSurface();
215
+ const githubActor = process.env.GITHUB_ACTOR?.trim() || undefined;
216
+ const name = detectGitConfig("user.name") ?? githubActor ?? osUsername() ?? undefined;
217
+ const email = shouldCaptureEmail() ? detectGitConfig("user.email") : undefined;
218
+ const exec = { surface, type: "user" };
219
+ if (name)
220
+ exec.name = name;
221
+ if (email)
222
+ exec.email = email;
223
+ if (githubActor)
224
+ exec.githubActor = githubActor;
225
+ return exec;
226
+ }
227
+ function resolveExecutorSurface() {
228
+ const explicit = process.env.AILF_EXECUTOR_SURFACE?.trim();
229
+ if (explicit === "cli" || explicit === "studio" || explicit === "api") {
230
+ return explicit;
231
+ }
232
+ return "cli";
233
+ }
234
+ function shouldCaptureEmail() {
235
+ const raw = process.env.AILF_CAPTURE_EMAIL?.trim().toLowerCase();
236
+ if (raw === "0" || raw === "false" || raw === "no")
237
+ return false;
238
+ return true;
239
+ }
240
+ function detectGitConfig(key) {
241
+ try {
242
+ const value = execSync(`git config --get ${key}`, {
243
+ encoding: "utf8",
244
+ stdio: ["ignore", "pipe", "ignore"],
245
+ timeout: 500,
246
+ }).trim();
247
+ return value || undefined;
248
+ }
249
+ catch {
250
+ return undefined;
251
+ }
252
+ }
253
+ function osUsername() {
254
+ try {
255
+ return os.userInfo().username || undefined;
256
+ }
257
+ catch {
258
+ return undefined;
259
+ }
260
+ }
261
+ /**
262
+ * Parse `AILF_LABELS` (comma-separated) into a trimmed unique list.
263
+ * Returns undefined when empty so absent labels stay absent.
264
+ */
265
+ function detectLabels() {
266
+ const raw = process.env.AILF_LABELS;
267
+ if (!raw)
268
+ return undefined;
269
+ const labels = raw
270
+ .split(",")
271
+ .map((label) => label.trim())
272
+ .filter(Boolean);
273
+ if (labels.length === 0)
274
+ return undefined;
275
+ return Array.from(new Set(labels));
276
+ }
277
+ /**
278
+ * Resolve `tool` — which AILF/Node ran the eval. Captured on every new
279
+ * run so cross-version trend comparisons can isolate framework changes
280
+ * from doc changes.
281
+ */
282
+ export function detectTool(log) {
283
+ const nodeVersion = process.version;
284
+ let ailfVersion;
285
+ try {
286
+ const pkg = requireFromHere("../../package.json");
287
+ ailfVersion = pkg.version;
288
+ }
289
+ catch (err) {
290
+ log.warn(`Could not read @sanity/ailf package.json for tool.ailfVersion: ${err instanceof Error ? err.message : String(err)}`);
291
+ }
292
+ return { ailfVersion: ailfVersion ?? "unknown", nodeVersion };
293
+ }
294
+ /**
295
+ * Resolve `host` — platform + arch + CI provider. Hostname is
296
+ * intentionally excluded (leaks identity without filtering benefit).
297
+ */
298
+ export function detectHost() {
299
+ const ci = process.env.GITHUB_ACTIONS === "true" ? "github-actions" : undefined;
300
+ const host = { arch: os.arch(), platform: os.platform() };
301
+ if (ci)
302
+ host.ci = ci;
303
+ return host;
304
+ }
305
+ // ---------------------------------------------------------------------------
140
306
  // Model config loading
141
307
  // ---------------------------------------------------------------------------
142
308
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanity/ailf",
3
- "version": "3.4.1",
3
+ "version": "3.5.0",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"