@sanity/ailf 3.7.0 → 3.8.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.
Files changed (104) hide show
  1. package/config/airbyte/ai_literacy_framework.connector.yaml +1 -1
  2. package/config/thresholds.ts +3 -3
  3. package/dist/_vendor/ailf-core/examples/index.d.ts +2 -2
  4. package/dist/_vendor/ailf-core/examples/index.js +2 -2
  5. package/dist/_vendor/ailf-core/ports/context.d.ts +0 -4
  6. package/dist/_vendor/ailf-core/schemas/eval-config.d.ts +38 -12
  7. package/dist/_vendor/ailf-core/schemas/eval-config.js +102 -22
  8. package/dist/_vendor/ailf-core/schemas/pipeline-request.d.ts +4 -6
  9. package/dist/_vendor/ailf-core/schemas/pipeline-request.js +1 -3
  10. package/dist/_vendor/ailf-core/schemas/schedules.d.ts +2 -2
  11. package/dist/_vendor/ailf-shared/run-classification.d.ts +2 -2
  12. package/dist/_vendor/ailf-shared/run-classification.js +1 -1
  13. package/dist/_vendor/ailf-shared/run-context.d.ts +1 -1
  14. package/dist/adapters/api-client/build-request.d.ts +0 -2
  15. package/dist/adapters/api-client/build-request.js +2 -6
  16. package/dist/adapters/config-sources/cli-config-adapter.d.ts +1 -1
  17. package/dist/adapters/config-sources/file-config-adapter.d.ts +1 -1
  18. package/dist/adapters/config-sources/file-config-adapter.js +42 -17
  19. package/dist/adapters/task-sources/repo-schemas.d.ts +41 -3
  20. package/dist/adapters/task-sources/repo-schemas.js +127 -0
  21. package/dist/cli-program.d.ts +39 -0
  22. package/dist/cli-program.js +137 -0
  23. package/dist/cli.d.ts +8 -2
  24. package/dist/cli.js +128 -142
  25. package/dist/commands/agent-report.js +1 -1
  26. package/dist/commands/calculate-scores.js +0 -2
  27. package/dist/commands/check-staleness.js +1 -1
  28. package/dist/commands/chronic-failures.js +4 -4
  29. package/dist/commands/coverage-audit.js +6 -7
  30. package/dist/commands/discovery-report.js +16 -4
  31. package/dist/commands/eval.d.ts +1 -1
  32. package/dist/commands/eval.js +1 -1
  33. package/dist/commands/explain-handler.d.ts +1 -1
  34. package/dist/commands/explain-handler.js +13 -44
  35. package/dist/commands/fetch-docs.js +0 -2
  36. package/dist/commands/generate-configs.js +0 -2
  37. package/dist/commands/grader/index.js +3 -3
  38. package/dist/commands/init.d.ts +2 -2
  39. package/dist/commands/init.js +10 -9
  40. package/dist/commands/interactive.d.ts +1 -1
  41. package/dist/commands/interactive.js +8 -8
  42. package/dist/commands/pipeline-action.d.ts +1 -3
  43. package/dist/commands/pipeline-action.js +174 -140
  44. package/dist/commands/pr-comment.js +1 -3
  45. package/dist/commands/publish.d.ts +1 -1
  46. package/dist/commands/publish.js +2 -4
  47. package/dist/commands/readiness-report.js +17 -8
  48. package/dist/commands/remote-pipeline.d.ts +1 -1
  49. package/dist/commands/remote-pipeline.js +1 -3
  50. package/dist/commands/run.d.ts +64 -0
  51. package/dist/commands/{pipeline.js → run.js} +19 -30
  52. package/dist/commands/shared/help.js +4 -4
  53. package/dist/commands/shared/options.d.ts +29 -3
  54. package/dist/commands/shared/options.js +37 -13
  55. package/dist/commands/validate-tasks.js +1 -1
  56. package/dist/commands/validate.d.ts +1 -1
  57. package/dist/commands/validate.js +2 -2
  58. package/dist/commands/weekly-digest.js +3 -3
  59. package/dist/config/thresholds.ts +3 -3
  60. package/dist/orchestration/build-app-context.js +0 -2
  61. package/dist/orchestration/build-step-sequence.js +1 -11
  62. package/dist/orchestration/steps/fetch-docs-step.js +1 -1
  63. package/dist/orchestration/steps/index.d.ts +0 -2
  64. package/dist/orchestration/steps/index.js +0 -2
  65. package/dist/orchestration/steps/run-eval-step.js +1 -1
  66. package/dist/pipeline/cache.d.ts +1 -1
  67. package/dist/pipeline/map-request-to-config.js +0 -2
  68. package/dist/pipeline/mirror-repo-tasks.d.ts +1 -1
  69. package/dist/pipeline/plan.d.ts +2 -4
  70. package/dist/pipeline/plan.js +4 -32
  71. package/dist/pipeline/run-context.d.ts +1 -1
  72. package/dist/pipeline/run-context.js +4 -4
  73. package/dist/pipeline/validate.d.ts +1 -1
  74. package/dist/pipeline/validate.js +1 -1
  75. package/package.json +11 -9
  76. package/dist/commands/pipeline.d.ts +0 -77
  77. package/dist/orchestration/steps/discovery-report-step.d.ts +0 -13
  78. package/dist/orchestration/steps/discovery-report-step.js +0 -62
  79. package/dist/orchestration/steps/readiness-step.d.ts +0 -13
  80. package/dist/orchestration/steps/readiness-step.js +0 -98
  81. package/dist/pipeline/compiler/__tests__/agent-harness-handler.test.d.ts +0 -10
  82. package/dist/pipeline/compiler/__tests__/agent-harness-handler.test.js +0 -366
  83. package/dist/pipeline/compiler/__tests__/assertion-mapper.test.d.ts +0 -9
  84. package/dist/pipeline/compiler/__tests__/assertion-mapper.test.js +0 -145
  85. package/dist/pipeline/compiler/__tests__/knowledge-probe-handler.test.d.ts +0 -10
  86. package/dist/pipeline/compiler/__tests__/knowledge-probe-handler.test.js +0 -314
  87. package/dist/pipeline/compiler/__tests__/literacy-handler.test.d.ts +0 -10
  88. package/dist/pipeline/compiler/__tests__/literacy-handler.test.js +0 -486
  89. package/dist/pipeline/compiler/__tests__/mcp-server-handler.test.d.ts +0 -10
  90. package/dist/pipeline/compiler/__tests__/mcp-server-handler.test.js +0 -425
  91. package/dist/pipeline/compiler/__tests__/promptfoo-compiler.test.d.ts +0 -9
  92. package/dist/pipeline/compiler/__tests__/promptfoo-compiler.test.js +0 -332
  93. package/dist/pipeline/compiler/__tests__/sandbox-and-fixtures.test.d.ts +0 -12
  94. package/dist/pipeline/compiler/__tests__/sandbox-and-fixtures.test.js +0 -210
  95. package/dist/pipeline/compiler/__tests__/scoring-and-presets.test.d.ts +0 -7
  96. package/dist/pipeline/compiler/__tests__/scoring-and-presets.test.js +0 -404
  97. package/dist/pipeline/compiler/__tests__/scoring-bridge.test.d.ts +0 -10
  98. package/dist/pipeline/compiler/__tests__/scoring-bridge.test.js +0 -184
  99. package/dist/pipeline/compiler/__tests__/task-graph-builder.test.d.ts +0 -8
  100. package/dist/pipeline/compiler/__tests__/task-graph-builder.test.js +0 -301
  101. package/dist/pipeline/compiler/__tests__/telemetry.test.d.ts +0 -9
  102. package/dist/pipeline/compiler/__tests__/telemetry.test.js +0 -503
  103. package/dist/pipeline/compiler/__tests__/tool-loop-openai.test.d.ts +0 -10
  104. package/dist/pipeline/compiler/__tests__/tool-loop-openai.test.js +0 -509
@@ -1,332 +0,0 @@
1
- /**
2
- * promptfoo-compiler.test.ts — Unit tests for the PromptfooCompiler.
3
- *
4
- * Tests compilation of a TaskGraph into Promptfoo configuration,
5
- * including provider assembly, prompt generation, and test case creation.
6
- *
7
- * Run: npx tsx --test src/pipeline/compiler/__tests__/promptfoo-compiler.test.ts
8
- */
9
- import assert from "node:assert/strict";
10
- import { describe, it } from "node:test";
11
- import { tmpdir } from "os";
12
- import { compileToPromptfoo } from "../promptfoo-compiler.js";
13
- // ---------------------------------------------------------------------------
14
- // Helpers
15
- // ---------------------------------------------------------------------------
16
- function makeModels(models = []) {
17
- return {
18
- defaults: { temperature: 0.2 },
19
- grader: { id: "openai:chat:gpt-5", label: "GPT-5" },
20
- models: models.map((m) => ({
21
- config: {},
22
- id: m.id,
23
- label: m.label,
24
- modes: m.modes,
25
- })),
26
- };
27
- }
28
- function makeNode(id, prompt) {
29
- return {
30
- dependsOn: [],
31
- priority: 0,
32
- resolvedPrompt: prompt ?? `Implement ${id}`,
33
- resolvedVariables: {
34
- declarations: [],
35
- provenance: {},
36
- values: {
37
- task: prompt ?? `Implement ${id}`,
38
- docs: `Documentation for ${id}`,
39
- },
40
- },
41
- taskId: id,
42
- };
43
- }
44
- function makeGraph(nodes, edges = []) {
45
- const nodeMap = new Map();
46
- for (const n of nodes)
47
- nodeMap.set(n.taskId, n);
48
- return {
49
- compilationTarget: "promptfoo",
50
- edges,
51
- fixtures: new Map(),
52
- nodes: nodeMap,
53
- };
54
- }
55
- // ---------------------------------------------------------------------------
56
- // compileToPromptfoo — basic compilation
57
- // ---------------------------------------------------------------------------
58
- describe("compileToPromptfoo", () => {
59
- it("compiles a single-task graph into a Promptfoo config", () => {
60
- const graph = makeGraph([makeNode("groq-basics")]);
61
- const result = compileToPromptfoo(graph, {
62
- mode: "literacy",
63
- models: makeModels([{ id: "openai:chat:gpt-4o", label: "GPT-4o" }]),
64
- rootDir: tmpdir(),
65
- });
66
- assert.ok(result.config);
67
- assert.equal(result.taskCount, 1);
68
- // Literacy mode produces gold + baseline test cases
69
- assert.equal(result.testCaseCount, 2);
70
- assert.equal(result.config.tests.length, 2);
71
- });
72
- it("generates correct test case descriptions", () => {
73
- const graph = makeGraph([makeNode("groq-basics")]);
74
- const result = compileToPromptfoo(graph, {
75
- mode: "literacy",
76
- models: makeModels(),
77
- rootDir: tmpdir(),
78
- });
79
- assert.equal(result.config.tests[0].description, "groq-basics");
80
- assert.equal(result.config.tests[1].description, "groq-basics [baseline]");
81
- });
82
- it("includes task variables in test case vars", () => {
83
- const graph = makeGraph([makeNode("task-1", "Do the thing")]);
84
- const result = compileToPromptfoo(graph, {
85
- mode: "literacy",
86
- models: makeModels(),
87
- rootDir: tmpdir(),
88
- });
89
- const vars = result.config.tests[0].vars;
90
- assert.equal(vars.task, "Do the thing");
91
- assert.equal(vars.docs, "Documentation for task-1");
92
- });
93
- it("baseline test case has empty docs", () => {
94
- const graph = makeGraph([makeNode("task-1")]);
95
- const result = compileToPromptfoo(graph, {
96
- mode: "literacy",
97
- models: makeModels(),
98
- rootDir: tmpdir(),
99
- });
100
- // Second test case is the baseline
101
- assert.equal(result.config.tests[1].vars.docs, "");
102
- });
103
- it("builds providers from model registry", () => {
104
- const graph = makeGraph([makeNode("task-1")]);
105
- const result = compileToPromptfoo(graph, {
106
- mode: "literacy",
107
- models: makeModels([
108
- { id: "openai:chat:gpt-4o", label: "GPT-4o" },
109
- {
110
- id: "anthropic:messages:claude-sonnet-4-6",
111
- label: "Claude Sonnet",
112
- },
113
- ]),
114
- rootDir: tmpdir(),
115
- });
116
- assert.equal(result.config.providers.length, 2);
117
- assert.equal(result.config.providers[0].id, "openai:chat:gpt-4o");
118
- assert.equal(result.config.providers[1].id, "anthropic:messages:claude-sonnet-4-6");
119
- });
120
- it("filters models by mode", () => {
121
- const graph = makeGraph([makeNode("task-1")]);
122
- const result = compileToPromptfoo(graph, {
123
- mode: "literacy",
124
- models: makeModels([
125
- { id: "model-a", label: "A", modes: ["literacy"] },
126
- { id: "model-b", label: "B", modes: ["mcp-server"] },
127
- ]),
128
- rootDir: tmpdir(),
129
- });
130
- assert.equal(result.config.providers.length, 1);
131
- assert.equal(result.config.providers[0].id, "model-a");
132
- });
133
- it("generates default prompts for literacy mode", () => {
134
- const graph = makeGraph([makeNode("task-1")]);
135
- const result = compileToPromptfoo(graph, {
136
- mode: "literacy",
137
- models: makeModels(),
138
- rootDir: tmpdir(),
139
- });
140
- assert.equal(result.config.prompts.length, 2);
141
- assert.equal(result.config.prompts[0].id, "with-docs");
142
- assert.equal(result.config.prompts[1].id, "without-docs");
143
- });
144
- it("generates default prompts for non-literacy modes", () => {
145
- const graph = makeGraph([makeNode("task-1")]);
146
- const result = compileToPromptfoo(graph, {
147
- mode: "mcp-server",
148
- models: makeModels(),
149
- rootDir: tmpdir(),
150
- });
151
- assert.equal(result.config.prompts.length, 1);
152
- assert.equal(result.config.prompts[0].id, "default");
153
- // Non-literacy modes don't create baseline variants
154
- assert.equal(result.config.tests.length, 1);
155
- });
156
- it("sets grader provider in defaultTest options", () => {
157
- const graph = makeGraph([makeNode("task-1")]);
158
- const result = compileToPromptfoo(graph, {
159
- mode: "literacy",
160
- models: makeModels(),
161
- rootDir: tmpdir(),
162
- graderProvider: "openai:chat:gpt-5",
163
- });
164
- assert.ok(result.config.defaultTest);
165
- assert.equal(result.config.defaultTest.options?.provider, "openai:chat:gpt-5");
166
- });
167
- it("compiles multiple tasks in priority order", () => {
168
- const nodeA = makeNode("task-a");
169
- nodeA.priority = 0;
170
- const nodeB = makeNode("task-b");
171
- nodeB.priority = 1;
172
- const graph = makeGraph([nodeB, nodeA]); // Intentionally reversed
173
- const result = compileToPromptfoo(graph, {
174
- mode: "mcp-server",
175
- models: makeModels(),
176
- rootDir: tmpdir(),
177
- });
178
- // Should be sorted by priority: task-a first, then task-b
179
- assert.equal(result.config.tests[0].description, "task-a");
180
- assert.equal(result.config.tests[1].description, "task-b");
181
- });
182
- it("sets output path when provided", () => {
183
- const graph = makeGraph([makeNode("task-1")]);
184
- const result = compileToPromptfoo(graph, {
185
- mode: "literacy",
186
- models: makeModels(),
187
- rootDir: tmpdir(),
188
- outputPath: "results/latest/eval-results.json",
189
- });
190
- assert.equal(result.config.outputPath, "results/latest/eval-results.json");
191
- });
192
- it("merges model defaults with per-model config", () => {
193
- const graph = makeGraph([makeNode("task-1")]);
194
- const result = compileToPromptfoo(graph, {
195
- mode: "literacy",
196
- models: {
197
- defaults: { temperature: 0.2, max_tokens: 4096 },
198
- grader: { id: "gpt-5" },
199
- models: [
200
- {
201
- id: "model-a",
202
- label: "A",
203
- config: { temperature: 0.5 },
204
- },
205
- ],
206
- },
207
- rootDir: tmpdir(),
208
- });
209
- assert.equal(result.config.providers.length, 1);
210
- const config = result.config.providers[0].config;
211
- // Per-model config overrides defaults
212
- assert.equal(config.temperature, 0.5);
213
- assert.equal(config.max_tokens, 4096);
214
- });
215
- });
216
- // ---------------------------------------------------------------------------
217
- // Handler-owned prompt resolution
218
- // ---------------------------------------------------------------------------
219
- describe("compileToPromptfoo — handler prompt resolution", () => {
220
- /** Build a minimal handler that returns prompts from getPrompts() */
221
- function makeHandlerWithPrompts(prompts) {
222
- return {
223
- compileTask() {
224
- return { providers: [], tests: [], prompts: [], warnings: [] };
225
- },
226
- getPrompts() {
227
- return prompts;
228
- },
229
- };
230
- }
231
- /** Build a handler that does NOT implement getPrompts */
232
- function makeHandlerWithoutPrompts() {
233
- return {
234
- compileTask() {
235
- return { providers: [], tests: [], prompts: [], warnings: [] };
236
- },
237
- };
238
- }
239
- it("uses handler prompts when handler.getPrompts() returns templates", () => {
240
- const graph = makeGraph([makeNode("task-1")]);
241
- const handler = makeHandlerWithPrompts({
242
- "custom-prompt": {
243
- id: "custom-prompt",
244
- label: "Custom Mode Prompt",
245
- template: "Do the thing: {{task}}",
246
- variables: ["task"],
247
- },
248
- });
249
- const result = compileToPromptfoo(graph, {
250
- mode: "literacy",
251
- models: makeModels(),
252
- rootDir: tmpdir(),
253
- handler,
254
- });
255
- assert.equal(result.config.prompts.length, 1);
256
- assert.equal(result.config.prompts[0].id, "custom-prompt");
257
- assert.equal(result.config.prompts[0].label, "Custom Mode Prompt");
258
- assert.equal(result.config.prompts[0].raw, "Do the thing: {{task}}");
259
- });
260
- it("falls back to default prompts when handler has no getPrompts()", () => {
261
- const graph = makeGraph([makeNode("task-1")]);
262
- const handler = makeHandlerWithoutPrompts();
263
- const result = compileToPromptfoo(graph, {
264
- mode: "literacy",
265
- models: makeModels(),
266
- rootDir: tmpdir(),
267
- handler,
268
- });
269
- // Should fall back to built-in literacy defaults
270
- assert.equal(result.config.prompts.length, 2);
271
- assert.equal(result.config.prompts[0].id, "with-docs");
272
- assert.equal(result.config.prompts[1].id, "without-docs");
273
- });
274
- it("falls back to default prompts when no handler is provided", () => {
275
- const graph = makeGraph([makeNode("task-1")]);
276
- const result = compileToPromptfoo(graph, {
277
- mode: "mcp-server",
278
- models: makeModels(),
279
- rootDir: tmpdir(),
280
- // No handler
281
- });
282
- // Non-literacy modes get generic default prompt
283
- assert.equal(result.config.prompts.length, 1);
284
- assert.equal(result.config.prompts[0].id, "default");
285
- });
286
- it("prefers handler prompts over explicit options.prompts", () => {
287
- const graph = makeGraph([makeNode("task-1")]);
288
- const handler = makeHandlerWithPrompts({
289
- "handler-owned": {
290
- id: "handler-owned",
291
- label: "Handler Prompt",
292
- template: "From handler: {{task}}",
293
- },
294
- });
295
- const result = compileToPromptfoo(graph, {
296
- mode: "literacy",
297
- models: makeModels(),
298
- rootDir: tmpdir(),
299
- handler,
300
- // Explicit prompts should be ignored when handler provides them
301
- prompts: [
302
- { id: "explicit", label: "Explicit", raw: "explicit {{task}}" },
303
- ],
304
- });
305
- assert.equal(result.config.prompts.length, 1);
306
- assert.equal(result.config.prompts[0].id, "handler-owned");
307
- });
308
- it("falls back to explicit prompts when handler.getPrompts() returns undefined", () => {
309
- const graph = makeGraph([makeNode("task-1")]);
310
- // Handler with getPrompts defined but returning undefined
311
- const handler = {
312
- compileTask() {
313
- return { providers: [], tests: [], prompts: [], warnings: [] };
314
- },
315
- getPrompts() {
316
- return undefined;
317
- },
318
- };
319
- const explicitPrompts = [
320
- { id: "explicit-p", label: "Explicit Prompt", raw: "explicit: {{task}}" },
321
- ];
322
- const result = compileToPromptfoo(graph, {
323
- mode: "literacy",
324
- models: makeModels(),
325
- rootDir: tmpdir(),
326
- handler,
327
- prompts: explicitPrompts,
328
- });
329
- assert.equal(result.config.prompts.length, 1);
330
- assert.equal(result.config.prompts[0].id, "explicit-p");
331
- });
332
- });
@@ -1,12 +0,0 @@
1
- /**
2
- * sandbox-and-fixtures.test.ts — Tests for sandbox strategy + fixture provisioning.
3
- *
4
- * Tests TempDir sandbox (the universal fallback), fixture provisioning
5
- * pipeline stages, ignoreFields stripping, and sandbox selection logic.
6
- *
7
- * Docker and GitWorktree sandboxes are not tested here (they require
8
- * external dependencies). CI tests for Docker are in a separate workflow.
9
- *
10
- * Run: npx tsx --test src/pipeline/compiler/__tests__/sandbox-and-fixtures.test.ts
11
- */
12
- export {};
@@ -1,210 +0,0 @@
1
- /**
2
- * sandbox-and-fixtures.test.ts — Tests for sandbox strategy + fixture provisioning.
3
- *
4
- * Tests TempDir sandbox (the universal fallback), fixture provisioning
5
- * pipeline stages, ignoreFields stripping, and sandbox selection logic.
6
- *
7
- * Docker and GitWorktree sandboxes are not tested here (they require
8
- * external dependencies). CI tests for Docker are in a separate workflow.
9
- *
10
- * Run: npx tsx --test src/pipeline/compiler/__tests__/sandbox-and-fixtures.test.ts
11
- */
12
- import assert from "node:assert/strict";
13
- import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync, } from "node:fs";
14
- import { afterEach, beforeEach, describe, it } from "node:test";
15
- import { tmpdir } from "os";
16
- import { resolve } from "path";
17
- import { TempDirSandboxStrategy } from "../sandbox/tempdir-sandbox.js";
18
- import { selectSandboxStrategy } from "../sandbox/sandbox-selector.js";
19
- import { provisionFixtures } from "../sandbox/fixture-provisioner.js";
20
- import { stripFields, compareWithIgnoredFields, buildIgnoreFieldsWrapper, } from "../ignore-fields.js";
21
- // ---------------------------------------------------------------------------
22
- // TempDirSandboxStrategy
23
- // ---------------------------------------------------------------------------
24
- describe("TempDirSandboxStrategy", () => {
25
- const strategy = new TempDirSandboxStrategy();
26
- it("is always available", async () => {
27
- assert.equal(await strategy.isAvailable(), true);
28
- });
29
- it("provisions a sandbox with a working directory", async () => {
30
- const sandbox = await strategy.provision({
31
- type: "tempdir",
32
- taskId: "test-task",
33
- });
34
- assert.ok(sandbox.id.startsWith("ailf-sandbox-"));
35
- assert.ok(existsSync(sandbox.workingDir));
36
- assert.equal(sandbox.strategy, "tempdir");
37
- // Clean up
38
- await strategy.teardown(sandbox);
39
- assert.ok(!existsSync(sandbox.workingDir));
40
- });
41
- it("collects artifacts from sandbox", async () => {
42
- const sandbox = await strategy.provision({
43
- type: "tempdir",
44
- taskId: "artifact-test",
45
- });
46
- // Create some files
47
- writeFileSync(resolve(sandbox.workingDir, "file1.ts"), "export const x = 1");
48
- mkdirSync(resolve(sandbox.workingDir, "subdir"), { recursive: true });
49
- writeFileSync(resolve(sandbox.workingDir, "subdir", "file2.ts"), "export const y = 2");
50
- const artifacts = await strategy.collectArtifacts(sandbox);
51
- assert.ok(artifacts.modifiedFiles.includes("file1.ts"));
52
- assert.ok(artifacts.modifiedFiles.includes("subdir/file2.ts"));
53
- assert.ok(artifacts.durationMs >= 0);
54
- await strategy.teardown(sandbox);
55
- });
56
- });
57
- // ---------------------------------------------------------------------------
58
- // selectSandboxStrategy
59
- // ---------------------------------------------------------------------------
60
- describe("selectSandboxStrategy", () => {
61
- it("returns TempDir as universal fallback", async () => {
62
- // Force fallback by requesting an unlikely-available strategy
63
- const result = await selectSandboxStrategy("tempdir");
64
- assert.equal(result.strategy.type, "tempdir");
65
- assert.equal(result.isFallback, false);
66
- });
67
- it("reports fallback when preferred strategy is unavailable", async () => {
68
- // git-worktree is available when git is installed, but docker may not be
69
- // Use tempdir as a reliable test
70
- const result = await selectSandboxStrategy("tempdir");
71
- assert.equal(result.isFallback, false);
72
- });
73
- });
74
- // ---------------------------------------------------------------------------
75
- // Fixture provisioning
76
- // ---------------------------------------------------------------------------
77
- const FIXTURE_DIR = resolve(tmpdir(), `ailf-fixture-test-${process.pid}`);
78
- beforeEach(() => {
79
- mkdirSync(FIXTURE_DIR, { recursive: true });
80
- });
81
- afterEach(() => {
82
- rmSync(FIXTURE_DIR, { recursive: true, force: true });
83
- });
84
- describe("provisionFixtures", () => {
85
- it("resolves file:// fixtures", async () => {
86
- writeFileSync(resolve(FIXTURE_DIR, "test.md"), "# Hello World");
87
- const result = await provisionFixtures([{ uri: "file://test.md", inject: "vars", key: "docs" }], { rootDir: FIXTURE_DIR });
88
- assert.equal(result.fixtures.length, 1);
89
- assert.equal(result.fixtures[0].content, "# Hello World");
90
- assert.equal(result.vars.docs, "# Hello World");
91
- assert.ok(result.manifest["file://test.md"]);
92
- });
93
- it("injects fixtures into sandbox working directory", async () => {
94
- writeFileSync(resolve(FIXTURE_DIR, "schema.ts"), "export default {}");
95
- const sandbox = await new TempDirSandboxStrategy().provision({
96
- type: "tempdir",
97
- taskId: "inject-test",
98
- });
99
- const result = await provisionFixtures([
100
- {
101
- uri: "file://schema.ts",
102
- inject: "working_dir",
103
- key: "src/schema.ts",
104
- },
105
- ], { rootDir: FIXTURE_DIR, sandbox });
106
- assert.equal(result.fixtures.length, 1);
107
- const injectedPath = resolve(sandbox.workingDir, "src", "schema.ts");
108
- assert.ok(existsSync(injectedPath));
109
- assert.equal(readFileSync(injectedPath, "utf-8"), "export default {}");
110
- rmSync(sandbox.workingDir, { recursive: true, force: true });
111
- });
112
- it("warns on missing file:// fixture", async () => {
113
- const result = await provisionFixtures([{ uri: "file://nonexistent.md", inject: "vars", key: "docs" }], { rootDir: FIXTURE_DIR });
114
- assert.equal(result.fixtures.length, 0);
115
- assert.ok(result.warnings.some((w) => w.includes("not found")));
116
- });
117
- it("applies strip-html transform", async () => {
118
- writeFileSync(resolve(FIXTURE_DIR, "page.html"), "<h1>Title</h1><p>Body text</p>");
119
- const result = await provisionFixtures([
120
- {
121
- uri: "file://page.html",
122
- inject: "vars",
123
- key: "content",
124
- transform: "strip-html",
125
- },
126
- ], { rootDir: FIXTURE_DIR });
127
- assert.equal(result.vars.content, "TitleBody text");
128
- });
129
- it("applies truncate transform", async () => {
130
- const longContent = "x".repeat(20_000);
131
- writeFileSync(resolve(FIXTURE_DIR, "long.txt"), longContent);
132
- const result = await provisionFixtures([
133
- {
134
- uri: "file://long.txt",
135
- inject: "vars",
136
- key: "docs",
137
- transform: "truncate",
138
- },
139
- ], { rootDir: FIXTURE_DIR });
140
- const content = result.vars.docs;
141
- assert.ok(content.length < 20_000);
142
- assert.ok(content.includes("truncated"));
143
- });
144
- it("produces content hash manifest", async () => {
145
- writeFileSync(resolve(FIXTURE_DIR, "a.md"), "alpha");
146
- writeFileSync(resolve(FIXTURE_DIR, "b.md"), "beta");
147
- const result = await provisionFixtures([
148
- { uri: "file://a.md", inject: "vars", key: "a" },
149
- { uri: "file://b.md", inject: "vars", key: "b" },
150
- ], { rootDir: FIXTURE_DIR });
151
- assert.equal(Object.keys(result.manifest).length, 2);
152
- assert.ok(result.manifest["file://a.md"]);
153
- assert.ok(result.manifest["file://b.md"]);
154
- // Different content → different hashes
155
- assert.notEqual(result.manifest["file://a.md"], result.manifest["file://b.md"]);
156
- });
157
- });
158
- // ---------------------------------------------------------------------------
159
- // stripFields / ignoreFields
160
- // ---------------------------------------------------------------------------
161
- describe("stripFields", () => {
162
- it("removes a top-level field", () => {
163
- const result = stripFields({ a: 1, b: 2, c: 3 }, ["b"]);
164
- assert.deepEqual(result, { a: 1, c: 3 });
165
- });
166
- it("removes a nested field", () => {
167
- const result = stripFields({ meta: { createdAt: "2024-01-01", author: "Alice" }, title: "Hello" }, ["meta.createdAt"]);
168
- assert.deepEqual(result, { meta: { author: "Alice" }, title: "Hello" });
169
- });
170
- it("removes multiple fields", () => {
171
- const result = stripFields({ a: 1, b: 2, c: { d: 3, e: 4 } }, ["b", "c.d"]);
172
- assert.deepEqual(result, { a: 1, c: { e: 4 } });
173
- });
174
- it("handles non-existent fields gracefully", () => {
175
- const result = stripFields({ a: 1 }, ["b", "c.d"]);
176
- assert.deepEqual(result, { a: 1 });
177
- });
178
- it("handles null/undefined input", () => {
179
- assert.equal(stripFields(null, ["a"]), null);
180
- assert.equal(stripFields(undefined, ["a"]), undefined);
181
- });
182
- it("does not mutate the original object", () => {
183
- const original = { a: 1, b: 2 };
184
- stripFields(original, ["b"]);
185
- assert.deepEqual(original, { a: 1, b: 2 });
186
- });
187
- });
188
- describe("compareWithIgnoredFields", () => {
189
- it("returns true when objects match after stripping", () => {
190
- const result = compareWithIgnoredFields({ title: "Hello", _rev: "abc123", _updatedAt: "2024-01-01" }, { title: "Hello", _rev: "def456", _updatedAt: "2024-06-01" }, ["_rev", "_updatedAt"]);
191
- assert.equal(result, true);
192
- });
193
- it("returns false when objects differ in non-ignored fields", () => {
194
- const result = compareWithIgnoredFields({ title: "Hello", _rev: "abc123" }, { title: "World", _rev: "def456" }, ["_rev"]);
195
- assert.equal(result, false);
196
- });
197
- });
198
- describe("buildIgnoreFieldsWrapper", () => {
199
- it("returns original code when no fields to ignore", () => {
200
- const code = "return true";
201
- assert.equal(buildIgnoreFieldsWrapper(code, []), code);
202
- });
203
- it("wraps code with stripFields function", () => {
204
- const wrapped = buildIgnoreFieldsWrapper("// check", ["_rev", "meta.ts"]);
205
- assert.ok(wrapped.includes("function stripFields"));
206
- assert.ok(wrapped.includes("__ignoreFields"));
207
- assert.ok(wrapped.includes('"_rev"'));
208
- assert.ok(wrapped.includes('"meta.ts"'));
209
- });
210
- });
@@ -1,7 +0,0 @@
1
- /**
2
- * scoring-and-presets.test.ts — Tests for 4-tier scoring engine,
3
- * storage schema, and plugin registry / presets.
4
- *
5
- * Run: npx tsx --test src/pipeline/compiler/__tests__/scoring-and-presets.test.ts
6
- */
7
- export {};