@sanity/ailf-studio 1.14.0 → 1.15.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.
package/dist/index.d.ts CHANGED
@@ -556,13 +556,13 @@ interface ScoreItem {
556
556
  ceilingScore?: number;
557
557
  }
558
558
  /**
559
- * A single row in `ArtifactRef.entries[]`. W0051 adds optional `preview` +
559
+ * A single row in `StudioArtifactRef.entries[]`. W0051 adds optional `preview` +
560
560
  * `association` + `truncated` so list-view renderers can consume the
561
561
  * descriptor-extracted preview without fetching the external payload.
562
562
  * Older manifests (pre-W0051) carry only `{ key, bytes }`; readers treat
563
563
  * missing fields as absent data, not as errors.
564
564
  */
565
- interface ArtifactRefEntry {
565
+ interface StudioArtifactRefEntry {
566
566
  key: string;
567
567
  bytes: number;
568
568
  association?: Record<string, string | number>;
@@ -570,7 +570,7 @@ interface ArtifactRefEntry {
570
570
  preview?: unknown;
571
571
  }
572
572
  /** Reference to an artifact stored in an external object store. */
573
- interface ArtifactRef {
573
+ interface StudioArtifactRef {
574
574
  store: "gcs" | "local";
575
575
  bucket: string;
576
576
  path: string;
@@ -582,7 +582,7 @@ interface ArtifactRef {
582
582
  */
583
583
  layout?: "bulk" | "per-entry";
584
584
  /** Per-entry index (populated for `layout: "per-entry"` refs only). */
585
- entries?: ArtifactRefEntry[];
585
+ entries?: StudioArtifactRefEntry[];
586
586
  truncated?: boolean;
587
587
  preview?: unknown;
588
588
  /**
@@ -720,12 +720,12 @@ interface SummaryData {
720
720
  agentBehavior?: FeatureAgentBehaviorData[] | null;
721
721
  /** External artifact references — present when pipeline uploads to GCS (D0032) */
722
722
  artifactManifest?: {
723
- testOutputs?: ArtifactRef;
724
- renderedPrompts?: ArtifactRef;
725
- rawResults?: ArtifactRef;
726
- graderPrompts?: ArtifactRef;
727
- traces?: ArtifactRef;
728
- pipelineContext?: ArtifactRef;
723
+ testOutputs?: StudioArtifactRef;
724
+ renderedPrompts?: StudioArtifactRef;
725
+ rawResults?: StudioArtifactRef;
726
+ graderPrompts?: StudioArtifactRef;
727
+ traces?: StudioArtifactRef;
728
+ pipelineContext?: StudioArtifactRef;
729
729
  };
730
730
  belowCritical: string[];
731
731
  /** All Sanity documents used across the entire evaluation */
package/dist/index.js CHANGED
@@ -11,6 +11,9 @@ import { useClient } from "sanity";
11
11
  var API_VERSION = "2026-03-11";
12
12
  var isProduction = process.env.NODE_ENV === "production";
13
13
  var ARTIFACT_API_BASE_URL = isProduction ? "https://ailf-api.sanity.build/v1" : "http://localhost:3000/v1";
14
+ var ENV = globalThis.process?.env ?? {};
15
+ var REFERENCE_DATASET = ENV.SANITY_STUDIO_AILF_REF_DATASET ?? "next";
16
+ var REFERENCE_WORKSPACE = ENV.SANITY_STUDIO_AILF_REF_WORKSPACE ?? "editorial";
14
17
 
15
18
  // src/actions/ArchiveTaskAction.tsx
16
19
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -2323,8 +2326,17 @@ import {
2323
2326
  } from "sanity";
2324
2327
  import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
2325
2328
  function CanonicalDocPreview(props) {
2326
- const { perspective, perspectiveTitle, reason, refType, slug, path } = props;
2329
+ const {
2330
+ docType,
2331
+ perspective,
2332
+ perspectiveTitle,
2333
+ reason,
2334
+ refType,
2335
+ slug,
2336
+ path
2337
+ } = props;
2327
2338
  const isPerspective = refType === "perspective";
2339
+ const isNonArticleDoc = !isPerspective && typeof docType === "string" && docType !== "article";
2328
2340
  const { data: activeReleases } = useActiveReleases();
2329
2341
  const { data: archivedReleases } = useArchivedReleases();
2330
2342
  const resolvedTitle = useMemo(() => {
@@ -2371,7 +2383,14 @@ function CanonicalDocPreview(props) {
2371
2383
  return /* @__PURE__ */ jsxs5(Flex3, { align: "center", gap: 2, padding: 2, children: [
2372
2384
  /* @__PURE__ */ jsx5(Text5, { size: 2, children: icon }),
2373
2385
  /* @__PURE__ */ jsxs5(Flex3, { direction: "column", gap: 1, flex: 1, children: [
2374
- /* @__PURE__ */ jsx5(Text5, { size: 2, weight: "semibold", children: resolvedTitle }),
2386
+ /* @__PURE__ */ jsxs5(Flex3, { align: "center", gap: 2, children: [
2387
+ /* @__PURE__ */ jsx5(Text5, { size: 2, weight: "semibold", children: resolvedTitle }),
2388
+ isNonArticleDoc && /* @__PURE__ */ jsxs5(Text5, { muted: true, size: 0, children: [
2389
+ "(",
2390
+ docType,
2391
+ ")"
2392
+ ] })
2393
+ ] }),
2375
2394
  subtitle && /* @__PURE__ */ jsx5(Text5, { muted: true, size: 1, children: subtitle })
2376
2395
  ] })
2377
2396
  ] });
@@ -2634,6 +2653,11 @@ function ReleasePicker(props) {
2634
2653
  }
2635
2654
 
2636
2655
  // src/schema/task.ts
2656
+ function articleStudioUrl(document2) {
2657
+ const origin = typeof window !== "undefined" && window.location?.origin ? window.location.origin : "";
2658
+ const typePart = document2.type ? `;type=${document2.type}` : "";
2659
+ return `${origin}/${REFERENCE_WORKSPACE}/intent/edit/id=${document2.id}${typePart}/`;
2660
+ }
2637
2661
  var ASSERTION_TYPES = [
2638
2662
  { title: "LLM Rubric", value: "llm-rubric" },
2639
2663
  { title: "Contains", value: "contains" },
@@ -2811,13 +2835,41 @@ var taskSchema = defineType5({
2811
2835
  validation: (rule) => rule.required()
2812
2836
  }),
2813
2837
  // --- Article reference (Document mode) ---
2838
+ // Cross-dataset reference — `ailf.task` lives in the AILF private
2839
+ // dataset; `article` lives in the editorial dataset (REFERENCE_DATASET,
2840
+ // default `next`). See D0043.
2841
+ //
2842
+ // `weak: true` keeps AILF tasks publishable when the referenced
2843
+ // editorial article is retired or deleted — AILF's lifecycle is
2844
+ // independent of editorial content (D0043, spike Finding 16).
2814
2845
  defineField5({
2846
+ dataset: REFERENCE_DATASET,
2815
2847
  description: "The article this canonical doc entry points to. The pipeline resolves the slug, path, and _id from this reference.",
2816
2848
  hidden: ({ parent }) => parent?.refType === "perspective",
2817
2849
  name: "doc",
2850
+ studioUrl: articleStudioUrl,
2818
2851
  title: "Document",
2819
- to: [{ type: "article" }],
2820
- type: "reference"
2852
+ to: [
2853
+ {
2854
+ preview: {
2855
+ select: { subtitle: "slug.current", title: "title" }
2856
+ },
2857
+ type: "article"
2858
+ },
2859
+ // typesReference — SDK / API type-definition documents.
2860
+ // The pipeline resolves these via the renderer registry
2861
+ // (see packages/eval/src/sanity/document-renderers.ts);
2862
+ // adding more curated types here is a one-line schema
2863
+ // change with no other code path required (W0195).
2864
+ {
2865
+ preview: {
2866
+ select: { subtitle: "slug.current", title: "title" }
2867
+ },
2868
+ type: "typesReference"
2869
+ }
2870
+ ],
2871
+ type: "crossDatasetReference",
2872
+ weak: true
2821
2873
  }),
2822
2874
  // --- Path (hidden — only populated by mirrored YAML tasks) ---
2823
2875
  defineField5({
@@ -2872,6 +2924,7 @@ var taskSchema = defineType5({
2872
2924
  name: "canonicalDocRef",
2873
2925
  preview: {
2874
2926
  select: {
2927
+ docType: "doc._type",
2875
2928
  path: "path",
2876
2929
  perspective: "perspective",
2877
2930
  perspectiveTitle: "perspectiveTitle",
@@ -4063,6 +4116,20 @@ function getDimensionValue(score, key) {
4063
4116
  }
4064
4117
 
4065
4118
  // src/lib/comparison.ts
4119
+ function normalizePerArea(perArea) {
4120
+ if (perArea == null) return [];
4121
+ if (Array.isArray(perArea)) {
4122
+ return perArea.filter(
4123
+ (e) => typeof e === "object" && e !== null && typeof e.area === "string" && typeof e.delta === "number"
4124
+ );
4125
+ }
4126
+ if (typeof perArea === "object") {
4127
+ return Object.entries(perArea).filter(
4128
+ (entry) => typeof entry[1] === "number"
4129
+ ).map(([area, delta]) => ({ area, delta }));
4130
+ }
4131
+ return [];
4132
+ }
4066
4133
  function scoreMap(summary) {
4067
4134
  return new Map(summary.scores.map((s) => [s.feature, s]));
4068
4135
  }
@@ -12033,7 +12100,7 @@ function StrengthsList({
12033
12100
  AreaScoresGrid,
12034
12101
  {
12035
12102
  mode,
12036
- perArea: comparison?.deltas?.perArea,
12103
+ perArea: normalizePerArea(comparison?.deltas?.perArea),
12037
12104
  perModel: expandedPerModel,
12038
12105
  scores: displayedScores
12039
12106
  }
@@ -12120,7 +12187,7 @@ function WeaknessesList({
12120
12187
  );
12121
12188
  const dimWeaknesses = scores.map((s) => ({ area: s, dims: getDimensionWeaknesses(s) })).filter(({ dims }) => dims.length > 0);
12122
12189
  const regressed = comparison?.regressed ?? [];
12123
- const perArea = comparison?.deltas?.perArea;
12190
+ const perArea = normalizePerArea(comparison?.deltas?.perArea);
12124
12191
  const efficiencyAnomalies = scores.filter(
12125
12192
  (s) => s.infrastructureEfficiency != null && s.infrastructureEfficiency > EFFICIENCY_ANOMALY
12126
12193
  );
@@ -12333,7 +12400,7 @@ function WeaknessesList({
12333
12400
  ),
12334
12401
  /* @__PURE__ */ jsx56(Stack31, { children: regressed.map((featureName, i) => {
12335
12402
  const area = scores.find((s) => s.feature === featureName);
12336
- const areaDelta = perArea?.find(
12403
+ const areaDelta = perArea.find(
12337
12404
  (p) => p.area === featureName
12338
12405
  )?.delta;
12339
12406
  return /* @__PURE__ */ jsxs40(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanity/ailf-studio",
3
- "version": "1.14.0",
3
+ "version": "1.15.1",
4
4
  "description": "AI Literacy Framework — Sanity Studio dashboard plugin",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -39,6 +39,7 @@
39
39
  "README.md"
40
40
  ],
41
41
  "devDependencies": {
42
+ "@sanity/client": "^7.3.0",
42
43
  "@sanity/icons": "^3.7.4",
43
44
  "@sanity/ui": "^3.1.13",
44
45
  "@testing-library/dom": "^10.4.1",