@wix/eval-assertions 0.17.0 → 0.18.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.
- package/README.md +3 -2
- package/build/index.js +124 -95
- package/build/index.js.map +4 -4
- package/build/index.mjs +117 -94
- package/build/index.mjs.map +4 -4
- package/build/types/evaluators/assertion-evaluator.d.ts +4 -17
- package/build/types/evaluators/index.d.ts +2 -2
- package/build/types/evaluators/llm-judge-evaluator.d.ts +11 -0
- package/build/types/index.d.ts +2 -1
- package/build/types/tools/index.d.ts +1 -0
- package/build/types/tools/read-file-tool.d.ts +10 -0
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -149,11 +149,12 @@ Optional context for assertions:
|
|
|
149
149
|
```typescript
|
|
150
150
|
interface AssertionContext {
|
|
151
151
|
workDir?: string; // For build_passed
|
|
152
|
-
llmConfig?: { // For llm_judge
|
|
152
|
+
llmConfig?: { // For llm_judge
|
|
153
153
|
baseUrl: string;
|
|
154
154
|
headers: Record<string, string>;
|
|
155
155
|
};
|
|
156
|
-
|
|
156
|
+
defaultJudgeModel?: string; // Default model for llm_judge
|
|
157
|
+
model?: LanguageModel; // Override model
|
|
157
158
|
}
|
|
158
159
|
```
|
|
159
160
|
|
package/build/index.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/index.ts
|
|
@@ -28,6 +38,7 @@ __export(index_exports, {
|
|
|
28
38
|
BuildPassedEvaluator: () => BuildPassedEvaluator,
|
|
29
39
|
CostAssertionSchema: () => CostAssertionSchema,
|
|
30
40
|
CostEvaluator: () => CostEvaluator,
|
|
41
|
+
JudgeResultSchema: () => JudgeResultSchema,
|
|
31
42
|
LLMBreakdownStatsSchema: () => LLMBreakdownStatsSchema,
|
|
32
43
|
LLMStepType: () => LLMStepType,
|
|
33
44
|
LLMTraceSchema: () => LLMTraceSchema,
|
|
@@ -40,6 +51,7 @@ __export(index_exports, {
|
|
|
40
51
|
TimeAssertionSchema: () => TimeAssertionSchema,
|
|
41
52
|
TimeEvaluator: () => TimeEvaluator,
|
|
42
53
|
TokenUsageSchema: () => TokenUsageSchema,
|
|
54
|
+
createReadFileTool: () => createReadFileTool,
|
|
43
55
|
evaluateAssertions: () => evaluateAssertions,
|
|
44
56
|
formatTraceForJudge: () => formatTraceForJudge,
|
|
45
57
|
getEvaluator: () => getEvaluator,
|
|
@@ -401,10 +413,48 @@ var CostEvaluator = class extends AssertionEvaluator {
|
|
|
401
413
|
}
|
|
402
414
|
};
|
|
403
415
|
|
|
416
|
+
// src/tools/read-file-tool.ts
|
|
417
|
+
var import_ai = require("ai");
|
|
418
|
+
var import_zod4 = require("zod");
|
|
419
|
+
var import_promises = require("fs/promises");
|
|
420
|
+
var import_path = __toESM(require("path"));
|
|
421
|
+
function createReadFileTool(workDir) {
|
|
422
|
+
const resolvedWorkDir = import_path.default.resolve(workDir);
|
|
423
|
+
return (0, import_ai.tool)({
|
|
424
|
+
description: "Read the content of any file in the workspace by its relative path. Use this to inspect file contents when evaluating code changes.",
|
|
425
|
+
inputSchema: import_zod4.z.object({
|
|
426
|
+
path: import_zod4.z.string().describe("Relative file path in the workspace")
|
|
427
|
+
}),
|
|
428
|
+
execute: async ({
|
|
429
|
+
path: filePath
|
|
430
|
+
}) => {
|
|
431
|
+
const resolved = import_path.default.resolve(resolvedWorkDir, filePath);
|
|
432
|
+
if (!resolved.startsWith(resolvedWorkDir + import_path.default.sep)) {
|
|
433
|
+
return { error: `Access denied: path escapes workspace directory` };
|
|
434
|
+
}
|
|
435
|
+
try {
|
|
436
|
+
const content = await (0, import_promises.readFile)(resolved, "utf-8");
|
|
437
|
+
return { path: filePath, content };
|
|
438
|
+
} catch {
|
|
439
|
+
return { error: `File not found: ${filePath}` };
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
|
|
404
445
|
// src/evaluators/llm-judge-evaluator.ts
|
|
405
446
|
var import_crypto5 = require("crypto");
|
|
406
447
|
var import_anthropic = require("@ai-sdk/anthropic");
|
|
407
|
-
var
|
|
448
|
+
var import_ai2 = require("ai");
|
|
449
|
+
var import_zod5 = require("zod");
|
|
450
|
+
var JudgeResultSchema = import_zod5.z.object({
|
|
451
|
+
text: import_zod5.z.string().describe("A brief textual verdict of the test result"),
|
|
452
|
+
score: import_zod5.z.number().min(0).max(100).describe(
|
|
453
|
+
"A number from 0 to 100 reflecting how well the answer meets the acceptance criteria"
|
|
454
|
+
),
|
|
455
|
+
scoreReasoning: import_zod5.z.string().describe("A concise explanation justifying the assigned score")
|
|
456
|
+
});
|
|
457
|
+
var MAX_JUDGE_STEPS = 20;
|
|
408
458
|
function formatTraceForJudge(llmTrace) {
|
|
409
459
|
if (!llmTrace?.steps?.length) {
|
|
410
460
|
return "No trace available.";
|
|
@@ -475,40 +525,22 @@ var DEFAULT_JUDGE_CONTEXT = `You are judging a scenario run. The ACTUAL run data
|
|
|
475
525
|
- {{newFiles}}: list of new files that were created (or "No new files were created")
|
|
476
526
|
- {{trace}}: step-by-step trace (tool calls, completions) so you can check e.g. which tools were called and how many times
|
|
477
527
|
|
|
478
|
-
|
|
479
|
-
var JSON_OUTPUT_FORMAT_INSTRUCTIONS = `You must respond only with a valid JSON object that conforms exactly to the following structure:
|
|
528
|
+
You have access to a read_file tool that lets you read the content of ANY file in the workspace (not just changed files). Use it to inspect file contents whenever you need to verify claims about code, check imports, review implementations, or validate that specific code patterns exist. Always read files before making judgments about their content \u2014 do not guess.
|
|
480
529
|
|
|
481
|
-
|
|
482
|
-
"text": string,
|
|
483
|
-
"score": number (0-100),
|
|
484
|
-
"scoreReasoning": string
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
- text: A brief textual verdict of the test result.
|
|
488
|
-
- score: A number from 0 to 100 that reflects how well the answer meets the acceptance criteria.
|
|
489
|
-
- scoreReasoning: A concise explanation justifying the assigned score.
|
|
490
|
-
|
|
491
|
-
Your response must:
|
|
492
|
-
- Contain only the JSON object above \u2014 no introductory text, no code formatting (e.g., no triple backticks), and no trailing comments.
|
|
493
|
-
- Be valid and parseable by \`JSON.parse\`.
|
|
494
|
-
- Use only double quotes for all keys and strings, as required by JSON.
|
|
495
|
-
|
|
496
|
-
Any response that includes extra content or deviates from the specified format will cause parsing to fail. Follow these instructions exactly.`;
|
|
530
|
+
CRITICAL: When the user asks you to verify a specific fact, compare it strictly against the actual data above and the actual file contents (use the read_file tool). If the expected outcome does NOT match the actual outcome, you MUST give a score of 0 or near 0. Do not be lenient \u2014 factual mismatches are failures.`;
|
|
497
531
|
var LlmJudgeEvaluator = class extends AssertionEvaluator {
|
|
498
532
|
type = "llm_judge";
|
|
499
533
|
async evaluate(assertion, input, context) {
|
|
500
534
|
const assertionId = (0, import_crypto5.randomUUID)();
|
|
501
|
-
const llmConfig = context?.llmConfig;
|
|
502
535
|
const workDir = context?.workDir ?? "";
|
|
503
|
-
const generateTextStub = context?.generateTextForLlmJudge;
|
|
504
536
|
const output = input.outputText ?? "";
|
|
505
537
|
const fileDiffs = input.fileDiffs ?? [];
|
|
506
538
|
const changedPaths = fileDiffs.map((d) => d.path);
|
|
507
539
|
const modifiedPaths = fileDiffs.filter((d) => d.status === "modified").map((d) => d.path);
|
|
508
540
|
const newPaths = fileDiffs.filter((d) => d.status === "new").map((d) => d.path);
|
|
509
|
-
const changedFiles = changedPaths.length > 0 ? changedPaths.map((
|
|
510
|
-
const modifiedFiles = modifiedPaths.length > 0 ? modifiedPaths.map((
|
|
511
|
-
const newFiles = newPaths.length > 0 ? newPaths.map((
|
|
541
|
+
const changedFiles = changedPaths.length > 0 ? changedPaths.map((p) => `- ${p}`).join("\n") : "No files were changed";
|
|
542
|
+
const modifiedFiles = modifiedPaths.length > 0 ? modifiedPaths.map((p) => `- ${p}`).join("\n") : "No files were modified";
|
|
543
|
+
const newFiles = newPaths.length > 0 ? newPaths.map((p) => `- ${p}`).join("\n") : "No new files were created";
|
|
512
544
|
const trace = formatTraceForJudge(input.llmTrace);
|
|
513
545
|
const ctx = {
|
|
514
546
|
output,
|
|
@@ -520,94 +552,70 @@ var LlmJudgeEvaluator = class extends AssertionEvaluator {
|
|
|
520
552
|
};
|
|
521
553
|
const replace = (s) => replacePlaceholders(s, ctx);
|
|
522
554
|
const finalPrompt = replace(assertion.prompt);
|
|
523
|
-
const systemPrompt = assertion.systemPrompt != null && assertion.systemPrompt !== "" ? replace(assertion.systemPrompt) + "\n\n" + JSON_OUTPUT_FORMAT_INSTRUCTIONS : replace(DEFAULT_JUDGE_CONTEXT) + "\n\n" + JSON_OUTPUT_FORMAT_INSTRUCTIONS;
|
|
524
555
|
const minScore = assertion.minScore ?? DEFAULT_MIN_SCORE;
|
|
525
556
|
const maxOutputTokens = assertion.maxTokens ?? 1024;
|
|
526
557
|
const temperature = assertion.temperature ?? 0;
|
|
527
|
-
const
|
|
528
|
-
|
|
558
|
+
const modelId = assertion.model ?? context?.defaultJudgeModel;
|
|
559
|
+
const model = this.resolveModel(context, modelId);
|
|
560
|
+
if (!model) {
|
|
561
|
+
const reason = !modelId && !context?.model ? "No model configured for llm_judge assertion (set model on assertion or provide defaultJudgeModel/model in context)" : "No llmConfig for llm_judge assertion (AI gateway required)";
|
|
529
562
|
return {
|
|
530
563
|
id: (0, import_crypto5.randomUUID)(),
|
|
531
564
|
assertionId,
|
|
532
565
|
assertionType: "llm_judge",
|
|
533
566
|
assertionName: "LLM judge",
|
|
534
567
|
status: "failed" /* FAILED */,
|
|
535
|
-
message:
|
|
568
|
+
message: reason,
|
|
536
569
|
expected: String(minScore)
|
|
537
570
|
};
|
|
538
571
|
}
|
|
539
|
-
|
|
540
|
-
return {
|
|
541
|
-
id: (0, import_crypto5.randomUUID)(),
|
|
542
|
-
assertionId,
|
|
543
|
-
assertionType: "llm_judge",
|
|
544
|
-
assertionName: "LLM judge",
|
|
545
|
-
status: "failed" /* FAILED */,
|
|
546
|
-
message: "No llmConfig for llm_judge assertion (AI gateway required)",
|
|
547
|
-
expected: String(minScore)
|
|
548
|
-
};
|
|
549
|
-
}
|
|
550
|
-
const maxParseAttempts = 3;
|
|
551
|
-
let lastParseError;
|
|
552
|
-
let lastRawText;
|
|
572
|
+
const systemPrompt = assertion.systemPrompt != null && assertion.systemPrompt !== "" ? replace(assertion.systemPrompt) : replace(DEFAULT_JUDGE_CONTEXT);
|
|
553
573
|
try {
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
finalPrompt,
|
|
564
|
-
systemPrompt,
|
|
565
|
-
maxOutputTokens,
|
|
566
|
-
temperature
|
|
567
|
-
);
|
|
568
|
-
lastRawText = result.text;
|
|
569
|
-
try {
|
|
570
|
-
const cleaned = stripMarkdownCodeBlock(result.text);
|
|
571
|
-
const parsed = JSON.parse(cleaned);
|
|
572
|
-
const judgeResult = validateJudgeResult(parsed);
|
|
573
|
-
const passed = judgeResult.score >= minScore;
|
|
574
|
-
return {
|
|
575
|
-
id: (0, import_crypto5.randomUUID)(),
|
|
576
|
-
assertionId,
|
|
577
|
-
assertionType: "llm_judge",
|
|
578
|
-
assertionName: "LLM judge",
|
|
579
|
-
status: passed ? "passed" /* PASSED */ : "failed" /* FAILED */,
|
|
580
|
-
message: passed ? `Judge score ${judgeResult.score} >= ${minScore}: ${judgeResult.text}` : `Judge score ${judgeResult.score} < ${minScore}: ${judgeResult.text}`,
|
|
581
|
-
expected: String(minScore),
|
|
582
|
-
actual: String(judgeResult.score),
|
|
583
|
-
details: {
|
|
584
|
-
score: judgeResult.score,
|
|
585
|
-
scoreReasoning: judgeResult.scoreReasoning,
|
|
586
|
-
text: judgeResult.text
|
|
587
|
-
}
|
|
588
|
-
};
|
|
589
|
-
} catch (parseErr) {
|
|
590
|
-
lastParseError = parseErr instanceof Error ? parseErr : new Error(String(parseErr));
|
|
591
|
-
}
|
|
592
|
-
}
|
|
574
|
+
const judgeResult = await this.callGenerateText(
|
|
575
|
+
model,
|
|
576
|
+
finalPrompt,
|
|
577
|
+
systemPrompt,
|
|
578
|
+
maxOutputTokens,
|
|
579
|
+
temperature,
|
|
580
|
+
workDir || void 0
|
|
581
|
+
);
|
|
582
|
+
const passed = judgeResult.score >= minScore;
|
|
593
583
|
return {
|
|
594
584
|
id: (0, import_crypto5.randomUUID)(),
|
|
595
585
|
assertionId,
|
|
596
586
|
assertionType: "llm_judge",
|
|
597
587
|
assertionName: "LLM judge",
|
|
598
|
-
status: "failed" /* FAILED */,
|
|
599
|
-
message:
|
|
588
|
+
status: passed ? "passed" /* PASSED */ : "failed" /* FAILED */,
|
|
589
|
+
message: passed ? `Judge score ${judgeResult.score} >= ${minScore}: ${judgeResult.text}` : `Judge score ${judgeResult.score} < ${minScore}: ${judgeResult.text}`,
|
|
600
590
|
expected: String(minScore),
|
|
601
|
-
actual:
|
|
602
|
-
details: {
|
|
591
|
+
actual: String(judgeResult.score),
|
|
592
|
+
details: {
|
|
593
|
+
score: judgeResult.score,
|
|
594
|
+
scoreReasoning: judgeResult.scoreReasoning,
|
|
595
|
+
text: judgeResult.text
|
|
596
|
+
}
|
|
603
597
|
};
|
|
604
598
|
} catch (err) {
|
|
599
|
+
if (import_ai2.NoObjectGeneratedError.isInstance(err)) {
|
|
600
|
+
return {
|
|
601
|
+
id: (0, import_crypto5.randomUUID)(),
|
|
602
|
+
assertionId,
|
|
603
|
+
assertionType: "llm_judge",
|
|
604
|
+
assertionName: "LLM judge",
|
|
605
|
+
status: "failed" /* FAILED */,
|
|
606
|
+
message: "LLM judge failed to produce valid structured output",
|
|
607
|
+
expected: String(minScore),
|
|
608
|
+
details: {
|
|
609
|
+
rawText: typeof err.text === "string" ? err.text.slice(0, 500) : void 0
|
|
610
|
+
}
|
|
611
|
+
};
|
|
612
|
+
}
|
|
605
613
|
const message = err instanceof Error ? err.message : String(err);
|
|
606
614
|
const details = {
|
|
607
615
|
error: message,
|
|
608
|
-
model:
|
|
616
|
+
model: modelId
|
|
609
617
|
};
|
|
610
|
-
if (
|
|
618
|
+
if (import_ai2.APICallError.isInstance(err)) {
|
|
611
619
|
details.statusCode = err.statusCode;
|
|
612
620
|
details.url = err.url;
|
|
613
621
|
details.isRetryable = err.isRetryable;
|
|
@@ -625,20 +633,39 @@ var LlmJudgeEvaluator = class extends AssertionEvaluator {
|
|
|
625
633
|
};
|
|
626
634
|
}
|
|
627
635
|
}
|
|
628
|
-
|
|
636
|
+
/**
|
|
637
|
+
* Resolve the LanguageModel to use: context.model (injected mock/override)
|
|
638
|
+
* takes precedence, otherwise create from llmConfig + modelId.
|
|
639
|
+
*/
|
|
640
|
+
resolveModel(context, modelId) {
|
|
641
|
+
if (context?.model) {
|
|
642
|
+
return context.model;
|
|
643
|
+
}
|
|
644
|
+
if (!modelId || !context?.llmConfig) {
|
|
645
|
+
return null;
|
|
646
|
+
}
|
|
629
647
|
const anthropic = (0, import_anthropic.createAnthropic)({
|
|
630
|
-
baseURL: llmConfig.baseUrl,
|
|
648
|
+
baseURL: context.llmConfig.baseUrl,
|
|
631
649
|
apiKey: "dummy",
|
|
632
|
-
headers: llmConfig.headers
|
|
650
|
+
headers: context.llmConfig.headers
|
|
633
651
|
});
|
|
634
|
-
|
|
635
|
-
|
|
652
|
+
return anthropic(modelId);
|
|
653
|
+
}
|
|
654
|
+
async callGenerateText(model, prompt, system, maxOutputTokens, temperature, workDir) {
|
|
655
|
+
const baseOptions = {
|
|
656
|
+
model,
|
|
636
657
|
prompt,
|
|
637
658
|
system,
|
|
638
659
|
maxOutputTokens,
|
|
639
|
-
temperature
|
|
640
|
-
|
|
641
|
-
|
|
660
|
+
temperature,
|
|
661
|
+
output: import_ai2.Output.object({ schema: JudgeResultSchema }),
|
|
662
|
+
stopWhen: (0, import_ai2.stepCountIs)(MAX_JUDGE_STEPS)
|
|
663
|
+
};
|
|
664
|
+
const { output } = workDir ? await (0, import_ai2.generateText)({
|
|
665
|
+
...baseOptions,
|
|
666
|
+
tools: { read_file: createReadFileTool(workDir) }
|
|
667
|
+
}) : await (0, import_ai2.generateText)(baseOptions);
|
|
668
|
+
return output;
|
|
642
669
|
}
|
|
643
670
|
};
|
|
644
671
|
|
|
@@ -694,6 +721,7 @@ async function evaluateAssertions(input, assertions, context) {
|
|
|
694
721
|
BuildPassedEvaluator,
|
|
695
722
|
CostAssertionSchema,
|
|
696
723
|
CostEvaluator,
|
|
724
|
+
JudgeResultSchema,
|
|
697
725
|
LLMBreakdownStatsSchema,
|
|
698
726
|
LLMStepType,
|
|
699
727
|
LLMTraceSchema,
|
|
@@ -706,6 +734,7 @@ async function evaluateAssertions(input, assertions, context) {
|
|
|
706
734
|
TimeAssertionSchema,
|
|
707
735
|
TimeEvaluator,
|
|
708
736
|
TokenUsageSchema,
|
|
737
|
+
createReadFileTool,
|
|
709
738
|
evaluateAssertions,
|
|
710
739
|
formatTraceForJudge,
|
|
711
740
|
getEvaluator,
|
package/build/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/index.ts", "../src/types/assertions.ts", "../src/types/trace.ts", "../src/types/result.ts", "../src/evaluators/index.ts", "../src/evaluators/skill-was-called-evaluator.ts", "../src/evaluators/assertion-evaluator.ts", "../src/evaluators/build-passed-evaluator.ts", "../src/evaluators/time-evaluator.ts", "../src/evaluators/cost-evaluator.ts", "../src/evaluators/llm-judge-evaluator.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * @wix/eval-assertions\n *\n * Assertion framework for AI agent evaluations.\n * Supports skill invocation checks, build validation, and LLM-based judging.\n */\n\n// Types\nexport {\n // Assertion schemas and types\n AssertionSchema,\n SkillWasCalledAssertionSchema,\n BuildPassedAssertionSchema,\n TimeAssertionSchema,\n CostAssertionSchema,\n LlmJudgeAssertionSchema,\n type Assertion,\n type SkillWasCalledAssertion,\n type BuildPassedAssertion,\n type TimeAssertion,\n type CostAssertion,\n type LlmJudgeAssertion,\n // Trace types\n LLMTraceSchema,\n LLMTraceStepSchema,\n LLMTraceSummarySchema,\n LLMBreakdownStatsSchema,\n TokenUsageSchema,\n LLMStepType,\n type LLMTrace,\n type LLMTraceStep,\n type LLMTraceSummary,\n type LLMBreakdownStats,\n type TokenUsage,\n // Result types\n AssertionResultSchema,\n AssertionResultStatus,\n type AssertionResult,\n // Input types\n type EvaluationInput,\n type FileDiff,\n} from \"./types/index.js\";\n\n// Evaluators\nexport {\n evaluateAssertions,\n registerEvaluator,\n getEvaluator,\n AssertionEvaluator,\n SkillWasCalledEvaluator,\n BuildPassedEvaluator,\n TimeEvaluator,\n CostEvaluator,\n LlmJudgeEvaluator,\n formatTraceForJudge,\n replacePlaceholders,\n stripMarkdownCodeBlock,\n validateJudgeResult,\n type AssertionContext,\n type LlmConfig,\n type LlmJudgeGenerateTextOptions,\n type JudgeResult,\n} from \"./evaluators/index.js\";\n", "import { z } from \"zod\";\n\n/**\n * Assertion: the agent must have invoked one or more skills during the run.\n * Checked by inspecting the LLM trace for \"Skill\" tool uses with the given skills.\n * When multiple skills are in one assertion, they are treated as a group (1 assertion).\n * Each skill in the group must have been called for the assertion to pass.\n */\nexport const SkillWasCalledAssertionSchema = z.object({\n type: z.literal(\"skill_was_called\"),\n /** Names of the skills that must have been called (matched against trace Skill tool args) */\n skillNames: z.array(z.string()).min(1),\n});\n\nexport type SkillWasCalledAssertion = z.infer<\n typeof SkillWasCalledAssertionSchema\n>;\n\n/**\n * Assertion: a build command must exit with the expected code (default 0).\n * Runs the command in the scenario working directory.\n */\nexport const BuildPassedAssertionSchema = z.object({\n type: z.literal(\"build_passed\"),\n /** Command to run (default: \"yarn build\") */\n command: z.string().optional(),\n /** Expected exit code (default: 0) */\n expectedExitCode: z.number().int().optional(),\n});\n\nexport type BuildPassedAssertion = z.infer<typeof BuildPassedAssertionSchema>;\n\n/**\n * Assertion: the scenario LLM execution cost must stay within a USD threshold.\n * Checked by reading llmTrace.summary.totalCostUsd.\n */\nexport const CostAssertionSchema = z.object({\n type: z.literal(\"cost\"),\n /** Maximum allowed cost in USD */\n maxCostUsd: z.number().positive(),\n});\n\nexport type CostAssertion = z.infer<typeof CostAssertionSchema>;\n\n/**\n * Assertion: an LLM judges the scenario output (score 0-100).\n * Prompt can use {{output}}, {{cwd}}, {{changedFiles}}, {{modifiedFiles}}, {{newFiles}}, {{trace}}.\n * Passes if judge score >= minScore.\n */\nexport const LlmJudgeAssertionSchema = z.object({\n type: z.literal(\"llm_judge\"),\n /** Prompt template; placeholders: {{output}}, {{cwd}}, {{changedFiles}}, {{modifiedFiles}}, {{newFiles}}, {{trace}} */\n prompt: z.string(),\n /** Optional system prompt for the judge (default asks for JSON with score) */\n systemPrompt: z.string().optional(),\n /** Minimum score to pass (0-100, default 70) */\n minScore: z.number().int().min(0).max(100).optional(),\n /** Model for the judge (e.g. claude-3-5-haiku) */\n model: z.string().optional(),\n maxTokens: z.number().int().optional(),\n temperature: z.number().min(0).max(1).optional(),\n});\n\nexport type LlmJudgeAssertion = z.infer<typeof LlmJudgeAssertionSchema>;\n\n/**\n * Assertion: scenario must complete within a maximum duration.\n * Deterministic check against the scenario execution time.\n */\nexport const TimeAssertionSchema = z.object({\n type: z.literal(\"time_limit\"),\n /** Maximum allowed duration in milliseconds */\n maxDurationMs: z.number().int().positive(),\n});\n\nexport type TimeAssertion = z.infer<typeof TimeAssertionSchema>;\n\n/**\n * Union of all assertion types.\n * Each assertion has a type and type-specific data.\n * Uses z.union (not z.discriminatedUnion) for Zod v4 compatibility when used as array element.\n */\nexport const AssertionSchema = z.union([\n SkillWasCalledAssertionSchema,\n BuildPassedAssertionSchema,\n TimeAssertionSchema,\n CostAssertionSchema,\n LlmJudgeAssertionSchema,\n]);\n\nexport type Assertion = z.infer<typeof AssertionSchema>;\n", "import { z } from \"zod\";\n\n/**\n * Token usage schema.\n */\nexport const TokenUsageSchema = z.object({\n prompt: z.number(),\n completion: z.number(),\n total: z.number(),\n});\n\nexport type TokenUsage = z.infer<typeof TokenUsageSchema>;\n\n/**\n * LLM step type enum.\n */\nexport enum LLMStepType {\n COMPLETION = \"completion\",\n TOOL_USE = \"tool_use\",\n TOOL_RESULT = \"tool_result\",\n THINKING = \"thinking\",\n}\n\n/**\n * LLM trace step schema.\n */\nexport const LLMTraceStepSchema = z.object({\n id: z.string(),\n stepNumber: z.number(),\n type: z.enum(LLMStepType),\n model: z.string(),\n provider: z.string(),\n startedAt: z.string(),\n durationMs: z.number(),\n tokenUsage: TokenUsageSchema,\n costUsd: z.number(),\n toolName: z.string().optional(),\n toolArguments: z.string().optional(),\n inputPreview: z.string().optional(),\n outputPreview: z.string().optional(),\n success: z.boolean(),\n error: z.string().optional(),\n});\n\nexport type LLMTraceStep = z.infer<typeof LLMTraceStepSchema>;\n\n/**\n * LLM breakdown stats schema.\n */\nexport const LLMBreakdownStatsSchema = z.object({\n count: z.number(),\n durationMs: z.number(),\n tokens: z.number(),\n costUsd: z.number(),\n});\n\nexport type LLMBreakdownStats = z.infer<typeof LLMBreakdownStatsSchema>;\n\n/**\n * LLM trace summary schema.\n */\nexport const LLMTraceSummarySchema = z.object({\n totalSteps: z.number(),\n totalDurationMs: z.number(),\n totalTokens: TokenUsageSchema,\n totalCostUsd: z.number(),\n stepTypeBreakdown: z.record(z.string(), LLMBreakdownStatsSchema).optional(),\n modelBreakdown: z.record(z.string(), LLMBreakdownStatsSchema),\n modelsUsed: z.array(z.string()),\n});\n\nexport type LLMTraceSummary = z.infer<typeof LLMTraceSummarySchema>;\n\n/**\n * LLM trace schema.\n */\nexport const LLMTraceSchema = z.object({\n id: z.string(),\n steps: z.array(LLMTraceStepSchema),\n summary: LLMTraceSummarySchema,\n});\n\nexport type LLMTrace = z.infer<typeof LLMTraceSchema>;\n", "import { z } from \"zod\";\nimport { LLMTraceStepSchema } from \"./trace.js\";\n\n/**\n * Assertion result status enum.\n */\nexport enum AssertionResultStatus {\n PASSED = \"passed\",\n FAILED = \"failed\",\n SKIPPED = \"skipped\",\n ERROR = \"error\",\n}\n\n/**\n * Assertion result schema.\n */\nexport const AssertionResultSchema = z.object({\n id: z.string(),\n assertionId: z.string(),\n assertionType: z.string(),\n assertionName: z.string(),\n status: z.enum(AssertionResultStatus),\n message: z.string().optional(),\n expected: z.string().optional(),\n actual: z.string().optional(),\n duration: z.number().optional(),\n details: z.record(z.string(), z.unknown()).optional(),\n llmTraceSteps: z.array(LLMTraceStepSchema).optional(),\n});\n\nexport type AssertionResult = z.infer<typeof AssertionResultSchema>;\n", "import type { Assertion, AssertionResult } from \"../types/index.js\";\nimport { AssertionResultStatus } from \"../types/index.js\";\nimport { randomUUID } from \"crypto\";\nimport type { AssertionContext } from \"./assertion-evaluator.js\";\nimport { AssertionEvaluator } from \"./assertion-evaluator.js\";\nimport { SkillWasCalledEvaluator } from \"./skill-was-called-evaluator.js\";\nimport { BuildPassedEvaluator } from \"./build-passed-evaluator.js\";\nimport { TimeEvaluator } from \"./time-evaluator.js\";\nimport { CostEvaluator } from \"./cost-evaluator.js\";\nimport { LlmJudgeEvaluator } from \"./llm-judge-evaluator.js\";\nimport type { EvaluationInput } from \"../types/index.js\";\n\nconst llmJudgeEvaluator = new LlmJudgeEvaluator();\n\nconst evaluators: Record<string, AssertionEvaluator> = {\n skill_was_called: new SkillWasCalledEvaluator(),\n build_passed: new BuildPassedEvaluator(),\n time_limit: new TimeEvaluator(),\n cost: new CostEvaluator(),\n llm_judge: llmJudgeEvaluator,\n // Custom assertions use the same LLM-based evaluation as llm_judge\n custom: llmJudgeEvaluator,\n};\n\n/**\n * Register a custom assertion evaluator.\n *\n * @param type - The assertion type identifier\n * @param evaluator - The evaluator instance\n */\nexport function registerEvaluator(\n type: string,\n evaluator: AssertionEvaluator,\n): void {\n evaluators[type] = evaluator;\n}\n\n/**\n * Get a registered evaluator by type.\n *\n * @param type - The assertion type identifier\n * @returns The evaluator or undefined if not found\n */\nexport function getEvaluator(type: string): AssertionEvaluator | undefined {\n return evaluators[type];\n}\n\n/**\n * Evaluate all assertions against the input.\n *\n * @param input - Evaluation input (includes outputText, llmTrace, fileDiffs)\n * @param assertions - List of assertions to evaluate\n * @param context - Optional context (e.g. workDir for build_passed, llmConfig for llm_judge)\n * @returns Array of assertion results; empty if no assertions\n */\nexport async function evaluateAssertions(\n input: EvaluationInput,\n assertions: Assertion[],\n context?: AssertionContext,\n): Promise<AssertionResult[]> {\n if (assertions.length === 0) {\n return [];\n }\n return Promise.all(\n assertions.map(async (assertion) => {\n const evaluator = evaluators[assertion.type];\n if (!evaluator) {\n return {\n id: randomUUID(),\n assertionId: randomUUID(),\n assertionType: assertion.type,\n assertionName: \"Unknown assertion\",\n status: AssertionResultStatus.ERROR,\n message: `Unsupported assertion type: ${assertion.type}`,\n duration: 0,\n };\n }\n const startMs = Date.now();\n const result = await evaluator.evaluate(assertion, input, context);\n const durationMs = Date.now() - startMs;\n return { ...result, duration: durationMs };\n }),\n );\n}\n\n// Re-export evaluator classes and types\nexport { AssertionEvaluator } from \"./assertion-evaluator.js\";\nexport type {\n AssertionContext,\n LlmConfig,\n LlmJudgeGenerateTextOptions,\n} from \"./assertion-evaluator.js\";\nexport { SkillWasCalledEvaluator } from \"./skill-was-called-evaluator.js\";\nexport { BuildPassedEvaluator } from \"./build-passed-evaluator.js\";\nexport { TimeEvaluator } from \"./time-evaluator.js\";\nexport { CostEvaluator } from \"./cost-evaluator.js\";\nexport {\n LlmJudgeEvaluator,\n formatTraceForJudge,\n replacePlaceholders,\n stripMarkdownCodeBlock,\n validateJudgeResult,\n type JudgeResult,\n} from \"./llm-judge-evaluator.js\";\n", "import type {\n SkillWasCalledAssertion,\n AssertionResult,\n LLMTrace,\n EvaluationInput,\n} from \"../types/index.js\";\nimport { AssertionResultStatus } from \"../types/index.js\";\nimport { randomUUID } from \"crypto\";\nimport type { AssertionContext } from \"./assertion-evaluator.js\";\nimport { AssertionEvaluator } from \"./assertion-evaluator.js\";\n\n/**\n * Collect all skill names that were called in the LLM trace.\n */\nfunction collectCalledSkillNames(llmTrace: LLMTrace): Set<string> {\n const calledSkills = new Set<string>();\n for (const step of llmTrace.steps) {\n if (step.toolName !== \"Skill\") {\n continue;\n }\n let args: unknown;\n try {\n args = step.toolArguments\n ? (JSON.parse(step.toolArguments) as unknown)\n : undefined;\n } catch {\n continue;\n }\n if (args !== null && typeof args === \"object\") {\n const obj = args as Record<string, unknown>;\n if (typeof obj.skill === \"string\") {\n calledSkills.add(obj.skill);\n }\n }\n }\n return calledSkills;\n}\n\n/**\n * Evaluator for \"skill_was_called\" assertion: the LLM trace must contain steps\n * where the \"Skill\" tool was used with ALL expected skills (by name).\n *\n * Multiple skills in one assertion are treated as a group \u2014 all must be called\n * for the assertion to pass. To check skills independently, add separate assertions.\n */\nexport class SkillWasCalledEvaluator extends AssertionEvaluator<SkillWasCalledAssertion> {\n readonly type = \"skill_was_called\" as const;\n\n evaluate(\n assertion: SkillWasCalledAssertion,\n input: EvaluationInput,\n // eslint-disable-next-line @typescript-eslint/no-unused-vars -- context not used for skill_was_called\n _context?: AssertionContext,\n ): AssertionResult {\n const assertionId = randomUUID();\n const expectedSkills = assertion.skillNames;\n const expectedLabel = expectedSkills.join(\", \");\n\n const llmTrace: LLMTrace | undefined = input.llmTrace;\n if (!llmTrace?.steps?.length) {\n return {\n id: randomUUID(),\n assertionId,\n assertionType: \"skill_was_called\",\n assertionName: \"Skill was called\",\n status: AssertionResultStatus.FAILED,\n message: \"No LLM trace steps to check for skill invocation\",\n expected: expectedLabel,\n };\n }\n\n const calledSkills = collectCalledSkillNames(llmTrace);\n const missingSkills = expectedSkills.filter(\n (name) => !calledSkills.has(name),\n );\n\n if (missingSkills.length === 0) {\n const message =\n expectedSkills.length === 1\n ? `Skill \"${expectedSkills[0]}\" was called`\n : `All skills were called: ${expectedLabel}`;\n return {\n id: randomUUID(),\n assertionId,\n assertionType: \"skill_was_called\",\n assertionName: \"Skill was called\",\n status: AssertionResultStatus.PASSED,\n message,\n expected: expectedLabel,\n };\n }\n\n const missingLabel = missingSkills.join(\", \");\n const message =\n expectedSkills.length === 1\n ? `Skill \"${missingSkills[0]}\" was not called`\n : `Missing skills: ${missingLabel} (expected all of: ${expectedLabel})`;\n return {\n id: randomUUID(),\n assertionId,\n assertionType: \"skill_was_called\",\n assertionName: \"Skill was called\",\n status: AssertionResultStatus.FAILED,\n message,\n expected: expectedLabel,\n };\n }\n}\n", "import type {\n Assertion,\n AssertionResult,\n EvaluationInput,\n} from \"../types/index.js\";\n\n/**\n * Options passed to the LLM for llm_judge. Used by the optional stub for testing.\n */\nexport interface LlmJudgeGenerateTextOptions {\n prompt: string;\n system: string;\n maxOutputTokens: number;\n temperature: number;\n}\n\n/**\n * Configuration for LLM calls (used by llm_judge assertion).\n */\nexport interface LlmConfig {\n /** Base URL for the AI API (e.g., 'https://api.anthropic.com') */\n baseUrl: string;\n /** Headers to include in API requests (e.g., API key) */\n headers: Record<string, string>;\n}\n\n/**\n * Optional context passed when evaluating assertions.\n */\nexport interface AssertionContext {\n /** Working directory for the scenario (used by build_passed) */\n workDir?: string;\n /** LLM configuration (used by llm_judge) */\n llmConfig?: LlmConfig;\n /** Default model for llm_judge when assertion.model is not set. Caller provides this. */\n defaultJudgeModel?: string;\n /**\n * Optional stub for llm_judge: when set, the evaluator uses this instead of the real AI call.\n * Used only in tests to avoid hitting the API.\n */\n generateTextForLlmJudge?: (\n options: LlmJudgeGenerateTextOptions,\n ) => Promise<{ text: string }>;\n}\n\n/**\n * Abstract base for assertion evaluators.\n * Each assertion type has a concrete class that implements evaluate().\n * evaluate() may return a Promise for async assertions (e.g. llm_judge).\n */\nexport abstract class AssertionEvaluator<T extends Assertion = Assertion> {\n abstract readonly type: T[\"type\"];\n\n abstract evaluate(\n assertion: T,\n input: EvaluationInput,\n context?: AssertionContext,\n ): AssertionResult | Promise<AssertionResult>;\n}\n", "import type {\n BuildPassedAssertion,\n AssertionResult,\n EvaluationInput,\n} from \"../types/index.js\";\nimport { AssertionResultStatus } from \"../types/index.js\";\nimport { randomUUID } from \"crypto\";\nimport { execSync } from \"child_process\";\nimport type { AssertionContext } from \"./assertion-evaluator.js\";\nimport { AssertionEvaluator } from \"./assertion-evaluator.js\";\n\nconst DEFAULT_COMMAND = \"yarn build\";\nconst DEFAULT_EXIT_CODE = 0;\n\n/**\n * Evaluator for \"build_passed\" assertion: runs a build command in the scenario\n * working directory and passes if the command exits with the expected code (default 0).\n */\nexport class BuildPassedEvaluator extends AssertionEvaluator<BuildPassedAssertion> {\n readonly type = \"build_passed\" as const;\n\n evaluate(\n assertion: BuildPassedAssertion,\n _input: EvaluationInput,\n context?: AssertionContext,\n ): AssertionResult {\n const assertionId = randomUUID();\n const workDir = context?.workDir;\n const command = assertion.command ?? DEFAULT_COMMAND;\n const expectedExitCode = assertion.expectedExitCode ?? DEFAULT_EXIT_CODE;\n\n if (!workDir) {\n return this.createResult(assertionId, {\n status: AssertionResultStatus.FAILED,\n message: \"No working directory provided for build_passed assertion\",\n expected: String(expectedExitCode),\n });\n }\n\n let exitCode: number | null = null;\n let errorMessage: string | null = null;\n let stdout: string | undefined;\n let stderr: string | undefined;\n\n console.log(`[build_passed] Running \"${command}\" in: ${workDir}`);\n\n try {\n execSync(command, {\n cwd: workDir,\n encoding: \"utf-8\",\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n exitCode = 0;\n } catch (err) {\n const error = err as Error & {\n status?: number;\n code?: number;\n stdout?: string | Buffer;\n stderr?: string | Buffer;\n };\n exitCode =\n typeof error.status === \"number\"\n ? error.status\n : typeof error.code === \"number\"\n ? error.code\n : null;\n errorMessage = error.message;\n stdout = this.bufferToString(error.stdout);\n stderr = this.bufferToString(error.stderr);\n }\n\n const passed = exitCode !== null && exitCode === expectedExitCode;\n\n const details: Record<string, unknown> = { workDir, command };\n if (stdout !== undefined && stdout !== \"\") {\n details.stdout = stdout;\n }\n if (stderr !== undefined && stderr !== \"\") {\n details.stderr = stderr;\n }\n\n return this.createResult(assertionId, {\n status: passed\n ? AssertionResultStatus.PASSED\n : AssertionResultStatus.FAILED,\n message: this.formatMessage(exitCode, expectedExitCode, errorMessage),\n expected: String(expectedExitCode),\n actual: exitCode !== null ? String(exitCode) : undefined,\n details,\n });\n }\n\n private createResult(\n assertionId: string,\n fields: Partial<AssertionResult>,\n ): AssertionResult {\n return {\n id: randomUUID(),\n assertionId,\n assertionType: \"build_passed\",\n assertionName: \"Build passed\",\n status: AssertionResultStatus.FAILED,\n ...fields,\n };\n }\n\n private bufferToString(\n value: string | Buffer | undefined,\n ): string | undefined {\n if (value === undefined || value === null) return undefined;\n if (typeof value === \"string\") return value;\n return value.toString(\"utf-8\");\n }\n\n private formatMessage(\n exitCode: number | null,\n expectedExitCode: number,\n errorMessage: string | null,\n ): string {\n if (exitCode === null) {\n return `Build failed: ${errorMessage}`;\n }\n if (exitCode === expectedExitCode) {\n return `Build passed (exit code ${exitCode})`;\n }\n return `Build exited with ${exitCode}, expected ${expectedExitCode}`;\n }\n}\n", "import type {\n TimeAssertion,\n AssertionResult,\n EvaluationInput,\n} from \"../types/index.js\";\nimport { AssertionResultStatus } from \"../types/index.js\";\nimport { randomUUID } from \"crypto\";\nimport { AssertionEvaluator } from \"./assertion-evaluator.js\";\n\n/**\n * Evaluator for \"time_limit\" assertion: passes if the scenario completed\n * within the configured maximum duration (maxDurationMs).\n */\nexport class TimeEvaluator extends AssertionEvaluator<TimeAssertion> {\n readonly type = \"time_limit\" as const;\n\n evaluate(assertion: TimeAssertion, input: EvaluationInput): AssertionResult {\n const maxDurationMs = assertion.maxDurationMs;\n\n if (input.durationMs == null) {\n return this.createResult({\n status: AssertionResultStatus.FAILED,\n message: \"No duration data available for time assertion\",\n expected: `<= ${maxDurationMs}ms`,\n });\n }\n\n const passed = input.durationMs <= maxDurationMs;\n\n return this.createResult({\n status: passed\n ? AssertionResultStatus.PASSED\n : AssertionResultStatus.FAILED,\n message: passed\n ? `Completed in ${input.durationMs}ms (limit: ${maxDurationMs}ms)`\n : `Exceeded time limit: ${input.durationMs}ms > ${maxDurationMs}ms`,\n expected: `<= ${maxDurationMs}ms`,\n actual: `${input.durationMs}ms`,\n });\n }\n\n private createResult(fields: Partial<AssertionResult>): AssertionResult {\n return {\n id: randomUUID(),\n assertionId: randomUUID(),\n assertionType: \"time_limit\",\n assertionName: \"Time limit\",\n status: AssertionResultStatus.FAILED,\n ...fields,\n };\n }\n}\n", "import type {\n CostAssertion,\n AssertionResult,\n EvaluationInput,\n} from \"../types/index.js\";\nimport { AssertionResultStatus } from \"../types/index.js\";\nimport { randomUUID } from \"crypto\";\nimport { AssertionEvaluator } from \"./assertion-evaluator.js\";\n\n/**\n * Evaluator for \"cost\" assertion: checks that the scenario's LLM execution cost\n * stays within a configured USD threshold by reading llmTrace.summary.totalCostUsd.\n */\nexport class CostEvaluator extends AssertionEvaluator<CostAssertion> {\n readonly type = \"cost\" as const;\n\n evaluate(assertion: CostAssertion, input: EvaluationInput): AssertionResult {\n const assertionId = randomUUID();\n const id = randomUUID();\n const assertionName = \"Cost\";\n const assertionType = \"cost\";\n const maxCostUsd = assertion.maxCostUsd;\n\n if (!input.llmTrace) {\n return {\n id,\n assertionId,\n assertionType,\n assertionName,\n status: AssertionResultStatus.SKIPPED,\n message: \"No LLM trace available to check cost\",\n };\n }\n\n const actualCostUsd = input.llmTrace.summary.totalCostUsd;\n const formattedActual = actualCostUsd.toFixed(6);\n const formattedMax = maxCostUsd.toFixed(6);\n const passed = Number(formattedActual) <= Number(formattedMax);\n\n return {\n id,\n assertionId,\n assertionType,\n assertionName,\n status: passed\n ? AssertionResultStatus.PASSED\n : AssertionResultStatus.FAILED,\n message: passed\n ? `Cost $${formattedActual} is within limit of $${formattedMax}`\n : `Cost $${formattedActual} exceeds limit of $${formattedMax}`,\n expected: `<= $${formattedMax}`,\n actual: `$${formattedActual}`,\n details: { actualCostUsd, maxCostUsd },\n };\n }\n}\n", "import type {\n LlmJudgeAssertion,\n AssertionResult,\n LLMTrace,\n EvaluationInput,\n} from \"../types/index.js\";\nimport { AssertionResultStatus } from \"../types/index.js\";\nimport { randomUUID } from \"crypto\";\nimport { createAnthropic } from \"@ai-sdk/anthropic\";\nimport { generateText, APICallError } from \"ai\";\nimport type { AssertionContext, LlmConfig } from \"./assertion-evaluator.js\";\nimport { AssertionEvaluator } from \"./assertion-evaluator.js\";\n\nexport interface JudgeResult {\n text: string;\n score: number;\n scoreReasoning: string;\n}\n\n/**\n * Format LLM trace as readable text for the judge (step number, type, tool name/args, output preview).\n */\nexport function formatTraceForJudge(llmTrace: LLMTrace | undefined): string {\n if (!llmTrace?.steps?.length) {\n return \"No trace available.\";\n }\n const lines: string[] = [];\n for (const step of llmTrace.steps) {\n const parts: string[] = [\n `Step ${step.stepNumber}`,\n `type: ${step.type}`,\n `duration: ${step.durationMs}ms`,\n ];\n if (step.toolName) {\n parts.push(`tool: ${step.toolName}`);\n if (step.toolArguments) {\n parts.push(`args: ${step.toolArguments}`);\n }\n }\n if (step.outputPreview) {\n parts.push(`output: ${step.outputPreview}`);\n }\n if (step.error) {\n parts.push(`error: ${step.error}`);\n }\n lines.push(parts.join(\", \"));\n }\n return lines.join(\"\\n\");\n}\n\n/**\n * Context object for placeholder replacement.\n */\nexport interface PlaceholderContext {\n output: string;\n cwd: string;\n changedFiles: string;\n modifiedFiles: string;\n newFiles: string;\n trace: string;\n}\n\nexport function replacePlaceholders(\n str: string,\n ctx: PlaceholderContext,\n): string {\n return str\n .replace(/\\{\\{output\\}\\}/g, ctx.output)\n .replace(/\\{\\{cwd\\}\\}/g, ctx.cwd)\n .replace(/\\{\\{changedFiles\\}\\}/g, ctx.changedFiles)\n .replace(/\\{\\{modifiedFiles\\}\\}/g, ctx.modifiedFiles)\n .replace(/\\{\\{newFiles\\}\\}/g, ctx.newFiles)\n .replace(/\\{\\{trace\\}\\}/g, ctx.trace);\n}\n\n/**\n * Strip markdown code fences (e.g. ```json ... ```) from LLM output,\n * returning only the inner content for JSON parsing.\n */\nexport function stripMarkdownCodeBlock(text: string): string {\n const trimmed = text.trim();\n const match = trimmed.match(/^```(?:\\w+)?\\s*\\n?([\\s\\S]*?)\\n?\\s*```$/);\n return match ? match[1].trim() : trimmed;\n}\n\nexport function validateJudgeResult(parsed: unknown): JudgeResult {\n if (parsed === null || typeof parsed !== \"object\") {\n throw new Error(\"Judge result is not an object\");\n }\n const obj = parsed as Record<string, unknown>;\n if (typeof obj.text !== \"string\") {\n throw new Error(\"Judge result does not contain a valid text field\");\n }\n if (typeof obj.score !== \"number\") {\n throw new Error(\"Judge result does not contain a valid score field\");\n }\n if (obj.score < 0 || obj.score > 100) {\n throw new Error(\"Judge result score is not between 0 and 100\");\n }\n if (typeof obj.scoreReasoning !== \"string\") {\n throw new Error(\n \"Judge result does not contain a valid scoreReasoning field\",\n );\n }\n return {\n text: obj.text,\n score: obj.score,\n scoreReasoning: obj.scoreReasoning,\n };\n}\n\nconst DEFAULT_MIN_SCORE = 70;\n\n/** Default judge context (run data + placeholders); used when assertion.systemPrompt is empty. */\nconst DEFAULT_JUDGE_CONTEXT = `You are judging a scenario run. The ACTUAL run data is provided below \u2014 use it to verify facts:\n\n- {{output}}: the agent's final output\n- {{cwd}}: working directory\n- {{changedFiles}}: list of all files changed (or \"No files were changed\")\n- {{modifiedFiles}}: list of existing files that were modified (or \"No files were modified\")\n- {{newFiles}}: list of new files that were created (or \"No new files were created\")\n- {{trace}}: step-by-step trace (tool calls, completions) so you can check e.g. which tools were called and how many times\n\nCRITICAL: When the user asks you to verify a specific fact, compare it strictly against the actual data above. If the expected outcome does NOT match the actual outcome, you MUST give a score of 0 or near 0. Do not be lenient \u2014 factual mismatches are failures.`;\n\nconst JSON_OUTPUT_FORMAT_INSTRUCTIONS = `You must respond only with a valid JSON object that conforms exactly to the following structure:\n\n{\n \"text\": string,\n \"score\": number (0-100),\n \"scoreReasoning\": string\n}\n\n- text: A brief textual verdict of the test result.\n- score: A number from 0 to 100 that reflects how well the answer meets the acceptance criteria.\n- scoreReasoning: A concise explanation justifying the assigned score.\n\nYour response must:\n- Contain only the JSON object above \u2014 no introductory text, no code formatting (e.g., no triple backticks), and no trailing comments.\n- Be valid and parseable by \\`JSON.parse\\`.\n- Use only double quotes for all keys and strings, as required by JSON.\n\nAny response that includes extra content or deviates from the specified format will cause parsing to fail. Follow these instructions exactly.`;\n\n/**\n * Evaluator for \"llm_judge\" assertion: an LLM judges the scenario output\n * (prompt with {{output}}, {{cwd}}, {{changedFiles}}, {{trace}}) and returns a score 0-100.\n * Passes if score >= minScore.\n */\nexport class LlmJudgeEvaluator extends AssertionEvaluator<LlmJudgeAssertion> {\n readonly type = \"llm_judge\" as const;\n\n async evaluate(\n assertion: LlmJudgeAssertion,\n input: EvaluationInput,\n context?: AssertionContext,\n ): Promise<AssertionResult> {\n const assertionId = randomUUID();\n const llmConfig = context?.llmConfig;\n const workDir = context?.workDir ?? \"\";\n const generateTextStub = context?.generateTextForLlmJudge;\n\n const output = input.outputText ?? \"\";\n const fileDiffs = input.fileDiffs ?? [];\n\n // Compute file lists by status\n const changedPaths = fileDiffs.map((d) => d.path);\n const modifiedPaths = fileDiffs\n .filter((d) => d.status === \"modified\")\n .map((d) => d.path);\n const newPaths = fileDiffs\n .filter((d) => d.status === \"new\")\n .map((d) => d.path);\n\n const changedFiles =\n changedPaths.length > 0\n ? changedPaths.map((path: string) => `- ${path}`).join(\"\\n\")\n : \"No files were changed\";\n const modifiedFiles =\n modifiedPaths.length > 0\n ? modifiedPaths.map((path: string) => `- ${path}`).join(\"\\n\")\n : \"No files were modified\";\n const newFiles =\n newPaths.length > 0\n ? newPaths.map((path: string) => `- ${path}`).join(\"\\n\")\n : \"No new files were created\";\n\n const trace = formatTraceForJudge(input.llmTrace);\n const ctx: PlaceholderContext = {\n output,\n cwd: workDir,\n changedFiles,\n modifiedFiles,\n newFiles,\n trace,\n };\n const replace = (s: string) => replacePlaceholders(s, ctx);\n\n const finalPrompt = replace(assertion.prompt);\n const systemPrompt =\n assertion.systemPrompt != null && assertion.systemPrompt !== \"\"\n ? replace(assertion.systemPrompt) +\n \"\\n\\n\" +\n JSON_OUTPUT_FORMAT_INSTRUCTIONS\n : replace(DEFAULT_JUDGE_CONTEXT) +\n \"\\n\\n\" +\n JSON_OUTPUT_FORMAT_INSTRUCTIONS;\n\n const minScore = assertion.minScore ?? DEFAULT_MIN_SCORE;\n const maxOutputTokens = assertion.maxTokens ?? 1024;\n const temperature = assertion.temperature ?? 0;\n const modelUsed = assertion.model ?? context?.defaultJudgeModel;\n\n if (!modelUsed && !generateTextStub) {\n return {\n id: randomUUID(),\n assertionId,\n assertionType: \"llm_judge\",\n assertionName: \"LLM judge\",\n status: AssertionResultStatus.FAILED,\n message:\n \"No model configured for llm_judge assertion (set model on assertion or provide defaultJudgeModel in context)\",\n expected: String(minScore),\n };\n }\n\n if (!generateTextStub && !llmConfig) {\n return {\n id: randomUUID(),\n assertionId,\n assertionType: \"llm_judge\",\n assertionName: \"LLM judge\",\n status: AssertionResultStatus.FAILED,\n message: \"No llmConfig for llm_judge assertion (AI gateway required)\",\n expected: String(minScore),\n };\n }\n\n const maxParseAttempts = 3;\n let lastParseError: Error | undefined;\n let lastRawText: string | undefined;\n\n try {\n for (let attempt = 1; attempt <= maxParseAttempts; attempt++) {\n const result = generateTextStub\n ? await generateTextStub({\n prompt: finalPrompt,\n system: systemPrompt,\n maxOutputTokens,\n temperature,\n })\n : await this.callGenerateText(\n llmConfig!,\n modelUsed!,\n finalPrompt,\n systemPrompt,\n maxOutputTokens,\n temperature,\n );\n\n lastRawText = result.text;\n try {\n const cleaned = stripMarkdownCodeBlock(result.text);\n const parsed = JSON.parse(cleaned);\n const judgeResult = validateJudgeResult(parsed);\n const passed = judgeResult.score >= minScore;\n return {\n id: randomUUID(),\n assertionId,\n assertionType: \"llm_judge\",\n assertionName: \"LLM judge\",\n status: passed\n ? AssertionResultStatus.PASSED\n : AssertionResultStatus.FAILED,\n message: passed\n ? `Judge score ${judgeResult.score} >= ${minScore}: ${judgeResult.text}`\n : `Judge score ${judgeResult.score} < ${minScore}: ${judgeResult.text}`,\n expected: String(minScore),\n actual: String(judgeResult.score),\n details: {\n score: judgeResult.score,\n scoreReasoning: judgeResult.scoreReasoning,\n text: judgeResult.text,\n },\n };\n } catch (parseErr) {\n lastParseError =\n parseErr instanceof Error ? parseErr : new Error(String(parseErr));\n }\n }\n\n return {\n id: randomUUID(),\n assertionId,\n assertionType: \"llm_judge\",\n assertionName: \"LLM judge\",\n status: AssertionResultStatus.FAILED,\n message: `Failed to parse judge response after ${maxParseAttempts} attempts: ${lastParseError?.message ?? \"unknown\"}`,\n expected: String(minScore),\n actual: undefined,\n details: { rawText: lastRawText?.slice(0, 500) },\n };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n const details: Record<string, unknown> = {\n error: message,\n model: modelUsed,\n };\n\n if (APICallError.isInstance(err)) {\n details.statusCode = err.statusCode;\n details.url = err.url;\n details.isRetryable = err.isRetryable;\n details.responseBody =\n typeof err.responseBody === \"string\"\n ? err.responseBody.slice(0, 2000)\n : err.responseBody;\n }\n\n return {\n id: randomUUID(),\n assertionId,\n assertionType: \"llm_judge\",\n assertionName: \"LLM judge\",\n status: AssertionResultStatus.FAILED,\n message: `LLM judge call failed: ${message}`,\n expected: String(minScore),\n details,\n };\n }\n }\n\n private async callGenerateText(\n llmConfig: LlmConfig,\n modelId: string,\n prompt: string,\n system: string,\n maxOutputTokens: number,\n temperature: number,\n ): Promise<{ text: string }> {\n const anthropic = createAnthropic({\n baseURL: llmConfig.baseUrl,\n apiKey: \"dummy\",\n headers: llmConfig.headers,\n });\n const result = await generateText({\n model: anthropic(modelId),\n prompt,\n system,\n maxOutputTokens,\n temperature,\n });\n return { text: result.text };\n }\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["import_zod", "LLMStepType", "import_zod", "AssertionResultStatus", "import_crypto", "message", "import_crypto", "import_crypto", "import_crypto", "import_crypto"]
|
|
3
|
+
"sources": ["../src/index.ts", "../src/types/assertions.ts", "../src/types/trace.ts", "../src/types/result.ts", "../src/evaluators/index.ts", "../src/evaluators/skill-was-called-evaluator.ts", "../src/evaluators/assertion-evaluator.ts", "../src/evaluators/build-passed-evaluator.ts", "../src/evaluators/time-evaluator.ts", "../src/evaluators/cost-evaluator.ts", "../src/tools/read-file-tool.ts", "../src/evaluators/llm-judge-evaluator.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * @wix/eval-assertions\n *\n * Assertion framework for AI agent evaluations.\n * Supports skill invocation checks, build validation, and LLM-based judging.\n */\n\n// Types\nexport {\n // Assertion schemas and types\n AssertionSchema,\n SkillWasCalledAssertionSchema,\n BuildPassedAssertionSchema,\n TimeAssertionSchema,\n CostAssertionSchema,\n LlmJudgeAssertionSchema,\n type Assertion,\n type SkillWasCalledAssertion,\n type BuildPassedAssertion,\n type TimeAssertion,\n type CostAssertion,\n type LlmJudgeAssertion,\n // Trace types\n LLMTraceSchema,\n LLMTraceStepSchema,\n LLMTraceSummarySchema,\n LLMBreakdownStatsSchema,\n TokenUsageSchema,\n LLMStepType,\n type LLMTrace,\n type LLMTraceStep,\n type LLMTraceSummary,\n type LLMBreakdownStats,\n type TokenUsage,\n // Result types\n AssertionResultSchema,\n AssertionResultStatus,\n type AssertionResult,\n // Input types\n type EvaluationInput,\n type FileDiff,\n} from \"./types/index.js\";\n\n// Evaluators\nexport {\n evaluateAssertions,\n registerEvaluator,\n getEvaluator,\n AssertionEvaluator,\n SkillWasCalledEvaluator,\n BuildPassedEvaluator,\n TimeEvaluator,\n CostEvaluator,\n LlmJudgeEvaluator,\n JudgeResultSchema,\n formatTraceForJudge,\n replacePlaceholders,\n stripMarkdownCodeBlock,\n validateJudgeResult,\n type AssertionContext,\n type LlmConfig,\n type JudgeResult,\n} from \"./evaluators/index.js\";\n\n// Tools\nexport { createReadFileTool } from \"./tools/index.js\";\n", "import { z } from \"zod\";\n\n/**\n * Assertion: the agent must have invoked one or more skills during the run.\n * Checked by inspecting the LLM trace for \"Skill\" tool uses with the given skills.\n * When multiple skills are in one assertion, they are treated as a group (1 assertion).\n * Each skill in the group must have been called for the assertion to pass.\n */\nexport const SkillWasCalledAssertionSchema = z.object({\n type: z.literal(\"skill_was_called\"),\n /** Names of the skills that must have been called (matched against trace Skill tool args) */\n skillNames: z.array(z.string()).min(1),\n});\n\nexport type SkillWasCalledAssertion = z.infer<\n typeof SkillWasCalledAssertionSchema\n>;\n\n/**\n * Assertion: a build command must exit with the expected code (default 0).\n * Runs the command in the scenario working directory.\n */\nexport const BuildPassedAssertionSchema = z.object({\n type: z.literal(\"build_passed\"),\n /** Command to run (default: \"yarn build\") */\n command: z.string().optional(),\n /** Expected exit code (default: 0) */\n expectedExitCode: z.number().int().optional(),\n});\n\nexport type BuildPassedAssertion = z.infer<typeof BuildPassedAssertionSchema>;\n\n/**\n * Assertion: the scenario LLM execution cost must stay within a USD threshold.\n * Checked by reading llmTrace.summary.totalCostUsd.\n */\nexport const CostAssertionSchema = z.object({\n type: z.literal(\"cost\"),\n /** Maximum allowed cost in USD */\n maxCostUsd: z.number().positive(),\n});\n\nexport type CostAssertion = z.infer<typeof CostAssertionSchema>;\n\n/**\n * Assertion: an LLM judges the scenario output (score 0-100).\n * Prompt can use {{output}}, {{cwd}}, {{changedFiles}}, {{modifiedFiles}}, {{newFiles}}, {{trace}}.\n * Passes if judge score >= minScore.\n */\nexport const LlmJudgeAssertionSchema = z.object({\n type: z.literal(\"llm_judge\"),\n /** Prompt template; placeholders: {{output}}, {{cwd}}, {{changedFiles}}, {{modifiedFiles}}, {{newFiles}}, {{trace}} */\n prompt: z.string(),\n /** Optional system prompt for the judge (default asks for JSON with score) */\n systemPrompt: z.string().optional(),\n /** Minimum score to pass (0-100, default 70) */\n minScore: z.number().int().min(0).max(100).optional(),\n /** Model for the judge (e.g. claude-3-5-haiku) */\n model: z.string().optional(),\n maxTokens: z.number().int().optional(),\n temperature: z.number().min(0).max(1).optional(),\n});\n\nexport type LlmJudgeAssertion = z.infer<typeof LlmJudgeAssertionSchema>;\n\n/**\n * Assertion: scenario must complete within a maximum duration.\n * Deterministic check against the scenario execution time.\n */\nexport const TimeAssertionSchema = z.object({\n type: z.literal(\"time_limit\"),\n /** Maximum allowed duration in milliseconds */\n maxDurationMs: z.number().int().positive(),\n});\n\nexport type TimeAssertion = z.infer<typeof TimeAssertionSchema>;\n\n/**\n * Union of all assertion types.\n * Each assertion has a type and type-specific data.\n * Uses z.union (not z.discriminatedUnion) for Zod v4 compatibility when used as array element.\n */\nexport const AssertionSchema = z.union([\n SkillWasCalledAssertionSchema,\n BuildPassedAssertionSchema,\n TimeAssertionSchema,\n CostAssertionSchema,\n LlmJudgeAssertionSchema,\n]);\n\nexport type Assertion = z.infer<typeof AssertionSchema>;\n", "import { z } from \"zod\";\n\n/**\n * Token usage schema.\n */\nexport const TokenUsageSchema = z.object({\n prompt: z.number(),\n completion: z.number(),\n total: z.number(),\n});\n\nexport type TokenUsage = z.infer<typeof TokenUsageSchema>;\n\n/**\n * LLM step type enum.\n */\nexport enum LLMStepType {\n COMPLETION = \"completion\",\n TOOL_USE = \"tool_use\",\n TOOL_RESULT = \"tool_result\",\n THINKING = \"thinking\",\n}\n\n/**\n * LLM trace step schema.\n */\nexport const LLMTraceStepSchema = z.object({\n id: z.string(),\n stepNumber: z.number(),\n type: z.enum(LLMStepType),\n model: z.string(),\n provider: z.string(),\n startedAt: z.string(),\n durationMs: z.number(),\n tokenUsage: TokenUsageSchema,\n costUsd: z.number(),\n toolName: z.string().optional(),\n toolArguments: z.string().optional(),\n inputPreview: z.string().optional(),\n outputPreview: z.string().optional(),\n success: z.boolean(),\n error: z.string().optional(),\n});\n\nexport type LLMTraceStep = z.infer<typeof LLMTraceStepSchema>;\n\n/**\n * LLM breakdown stats schema.\n */\nexport const LLMBreakdownStatsSchema = z.object({\n count: z.number(),\n durationMs: z.number(),\n tokens: z.number(),\n costUsd: z.number(),\n});\n\nexport type LLMBreakdownStats = z.infer<typeof LLMBreakdownStatsSchema>;\n\n/**\n * LLM trace summary schema.\n */\nexport const LLMTraceSummarySchema = z.object({\n totalSteps: z.number(),\n totalDurationMs: z.number(),\n totalTokens: TokenUsageSchema,\n totalCostUsd: z.number(),\n stepTypeBreakdown: z.record(z.string(), LLMBreakdownStatsSchema).optional(),\n modelBreakdown: z.record(z.string(), LLMBreakdownStatsSchema),\n modelsUsed: z.array(z.string()),\n});\n\nexport type LLMTraceSummary = z.infer<typeof LLMTraceSummarySchema>;\n\n/**\n * LLM trace schema.\n */\nexport const LLMTraceSchema = z.object({\n id: z.string(),\n steps: z.array(LLMTraceStepSchema),\n summary: LLMTraceSummarySchema,\n});\n\nexport type LLMTrace = z.infer<typeof LLMTraceSchema>;\n", "import { z } from \"zod\";\nimport { LLMTraceStepSchema } from \"./trace.js\";\n\n/**\n * Assertion result status enum.\n */\nexport enum AssertionResultStatus {\n PASSED = \"passed\",\n FAILED = \"failed\",\n SKIPPED = \"skipped\",\n ERROR = \"error\",\n}\n\n/**\n * Assertion result schema.\n */\nexport const AssertionResultSchema = z.object({\n id: z.string(),\n assertionId: z.string(),\n assertionType: z.string(),\n assertionName: z.string(),\n status: z.enum(AssertionResultStatus),\n message: z.string().optional(),\n expected: z.string().optional(),\n actual: z.string().optional(),\n duration: z.number().optional(),\n details: z.record(z.string(), z.unknown()).optional(),\n llmTraceSteps: z.array(LLMTraceStepSchema).optional(),\n});\n\nexport type AssertionResult = z.infer<typeof AssertionResultSchema>;\n", "import type { Assertion, AssertionResult } from \"../types/index.js\";\nimport { AssertionResultStatus } from \"../types/index.js\";\nimport { randomUUID } from \"crypto\";\nimport type { AssertionContext } from \"./assertion-evaluator.js\";\nimport { AssertionEvaluator } from \"./assertion-evaluator.js\";\nimport { SkillWasCalledEvaluator } from \"./skill-was-called-evaluator.js\";\nimport { BuildPassedEvaluator } from \"./build-passed-evaluator.js\";\nimport { TimeEvaluator } from \"./time-evaluator.js\";\nimport { CostEvaluator } from \"./cost-evaluator.js\";\nimport { LlmJudgeEvaluator } from \"./llm-judge-evaluator.js\";\nimport type { EvaluationInput } from \"../types/index.js\";\n\nconst llmJudgeEvaluator = new LlmJudgeEvaluator();\n\nconst evaluators: Record<string, AssertionEvaluator> = {\n skill_was_called: new SkillWasCalledEvaluator(),\n build_passed: new BuildPassedEvaluator(),\n time_limit: new TimeEvaluator(),\n cost: new CostEvaluator(),\n llm_judge: llmJudgeEvaluator,\n // Custom assertions use the same LLM-based evaluation as llm_judge\n custom: llmJudgeEvaluator,\n};\n\n/**\n * Register a custom assertion evaluator.\n *\n * @param type - The assertion type identifier\n * @param evaluator - The evaluator instance\n */\nexport function registerEvaluator(\n type: string,\n evaluator: AssertionEvaluator,\n): void {\n evaluators[type] = evaluator;\n}\n\n/**\n * Get a registered evaluator by type.\n *\n * @param type - The assertion type identifier\n * @returns The evaluator or undefined if not found\n */\nexport function getEvaluator(type: string): AssertionEvaluator | undefined {\n return evaluators[type];\n}\n\n/**\n * Evaluate all assertions against the input.\n *\n * @param input - Evaluation input (includes outputText, llmTrace, fileDiffs)\n * @param assertions - List of assertions to evaluate\n * @param context - Optional context (e.g. workDir for build_passed, llmConfig for llm_judge)\n * @returns Array of assertion results; empty if no assertions\n */\nexport async function evaluateAssertions(\n input: EvaluationInput,\n assertions: Assertion[],\n context?: AssertionContext,\n): Promise<AssertionResult[]> {\n if (assertions.length === 0) {\n return [];\n }\n return Promise.all(\n assertions.map(async (assertion) => {\n const evaluator = evaluators[assertion.type];\n if (!evaluator) {\n return {\n id: randomUUID(),\n assertionId: randomUUID(),\n assertionType: assertion.type,\n assertionName: \"Unknown assertion\",\n status: AssertionResultStatus.ERROR,\n message: `Unsupported assertion type: ${assertion.type}`,\n duration: 0,\n };\n }\n const startMs = Date.now();\n const result = await evaluator.evaluate(assertion, input, context);\n const durationMs = Date.now() - startMs;\n return { ...result, duration: durationMs };\n }),\n );\n}\n\n// Re-export evaluator classes and types\nexport { AssertionEvaluator } from \"./assertion-evaluator.js\";\nexport type { AssertionContext, LlmConfig } from \"./assertion-evaluator.js\";\nexport { SkillWasCalledEvaluator } from \"./skill-was-called-evaluator.js\";\nexport { BuildPassedEvaluator } from \"./build-passed-evaluator.js\";\nexport { TimeEvaluator } from \"./time-evaluator.js\";\nexport { CostEvaluator } from \"./cost-evaluator.js\";\nexport {\n LlmJudgeEvaluator,\n JudgeResultSchema,\n formatTraceForJudge,\n replacePlaceholders,\n stripMarkdownCodeBlock,\n validateJudgeResult,\n type JudgeResult,\n} from \"./llm-judge-evaluator.js\";\n", "import type {\n SkillWasCalledAssertion,\n AssertionResult,\n LLMTrace,\n EvaluationInput,\n} from \"../types/index.js\";\nimport { AssertionResultStatus } from \"../types/index.js\";\nimport { randomUUID } from \"crypto\";\nimport type { AssertionContext } from \"./assertion-evaluator.js\";\nimport { AssertionEvaluator } from \"./assertion-evaluator.js\";\n\n/**\n * Collect all skill names that were called in the LLM trace.\n */\nfunction collectCalledSkillNames(llmTrace: LLMTrace): Set<string> {\n const calledSkills = new Set<string>();\n for (const step of llmTrace.steps) {\n if (step.toolName !== \"Skill\") {\n continue;\n }\n let args: unknown;\n try {\n args = step.toolArguments\n ? (JSON.parse(step.toolArguments) as unknown)\n : undefined;\n } catch {\n continue;\n }\n if (args !== null && typeof args === \"object\") {\n const obj = args as Record<string, unknown>;\n if (typeof obj.skill === \"string\") {\n calledSkills.add(obj.skill);\n }\n }\n }\n return calledSkills;\n}\n\n/**\n * Evaluator for \"skill_was_called\" assertion: the LLM trace must contain steps\n * where the \"Skill\" tool was used with ALL expected skills (by name).\n *\n * Multiple skills in one assertion are treated as a group \u2014 all must be called\n * for the assertion to pass. To check skills independently, add separate assertions.\n */\nexport class SkillWasCalledEvaluator extends AssertionEvaluator<SkillWasCalledAssertion> {\n readonly type = \"skill_was_called\" as const;\n\n evaluate(\n assertion: SkillWasCalledAssertion,\n input: EvaluationInput,\n // eslint-disable-next-line @typescript-eslint/no-unused-vars -- context not used for skill_was_called\n _context?: AssertionContext,\n ): AssertionResult {\n const assertionId = randomUUID();\n const expectedSkills = assertion.skillNames;\n const expectedLabel = expectedSkills.join(\", \");\n\n const llmTrace: LLMTrace | undefined = input.llmTrace;\n if (!llmTrace?.steps?.length) {\n return {\n id: randomUUID(),\n assertionId,\n assertionType: \"skill_was_called\",\n assertionName: \"Skill was called\",\n status: AssertionResultStatus.FAILED,\n message: \"No LLM trace steps to check for skill invocation\",\n expected: expectedLabel,\n };\n }\n\n const calledSkills = collectCalledSkillNames(llmTrace);\n const missingSkills = expectedSkills.filter(\n (name) => !calledSkills.has(name),\n );\n\n if (missingSkills.length === 0) {\n const message =\n expectedSkills.length === 1\n ? `Skill \"${expectedSkills[0]}\" was called`\n : `All skills were called: ${expectedLabel}`;\n return {\n id: randomUUID(),\n assertionId,\n assertionType: \"skill_was_called\",\n assertionName: \"Skill was called\",\n status: AssertionResultStatus.PASSED,\n message,\n expected: expectedLabel,\n };\n }\n\n const missingLabel = missingSkills.join(\", \");\n const message =\n expectedSkills.length === 1\n ? `Skill \"${missingSkills[0]}\" was not called`\n : `Missing skills: ${missingLabel} (expected all of: ${expectedLabel})`;\n return {\n id: randomUUID(),\n assertionId,\n assertionType: \"skill_was_called\",\n assertionName: \"Skill was called\",\n status: AssertionResultStatus.FAILED,\n message,\n expected: expectedLabel,\n };\n }\n}\n", "import type {\n Assertion,\n AssertionResult,\n EvaluationInput,\n} from \"../types/index.js\";\nimport type { LanguageModel } from \"ai\";\n\n/**\n * Configuration for LLM calls (used by llm_judge assertion).\n */\nexport interface LlmConfig {\n /** Base URL for the AI API (e.g., 'https://api.anthropic.com') */\n baseUrl: string;\n /** Headers to include in API requests (e.g., API key) */\n headers: Record<string, string>;\n}\n\n/**\n * Optional context passed when evaluating assertions.\n */\nexport interface AssertionContext {\n /** Working directory for the scenario (used by build_passed) */\n workDir?: string;\n /** LLM configuration (used by llm_judge) */\n llmConfig?: LlmConfig;\n /** Default model for llm_judge when assertion.model is not set */\n defaultJudgeModel?: string;\n /** Optional model override \u2014 when provided, used instead of creating from llmConfig + modelId */\n model?: LanguageModel;\n}\n\n/**\n * Abstract base for assertion evaluators.\n * Each assertion type has a concrete class that implements evaluate().\n * evaluate() may return a Promise for async assertions (e.g. llm_judge).\n */\nexport abstract class AssertionEvaluator<T extends Assertion = Assertion> {\n abstract readonly type: T[\"type\"];\n\n abstract evaluate(\n assertion: T,\n input: EvaluationInput,\n context?: AssertionContext,\n ): AssertionResult | Promise<AssertionResult>;\n}\n", "import type {\n BuildPassedAssertion,\n AssertionResult,\n EvaluationInput,\n} from \"../types/index.js\";\nimport { AssertionResultStatus } from \"../types/index.js\";\nimport { randomUUID } from \"crypto\";\nimport { execSync } from \"child_process\";\nimport type { AssertionContext } from \"./assertion-evaluator.js\";\nimport { AssertionEvaluator } from \"./assertion-evaluator.js\";\n\nconst DEFAULT_COMMAND = \"yarn build\";\nconst DEFAULT_EXIT_CODE = 0;\n\n/**\n * Evaluator for \"build_passed\" assertion: runs a build command in the scenario\n * working directory and passes if the command exits with the expected code (default 0).\n */\nexport class BuildPassedEvaluator extends AssertionEvaluator<BuildPassedAssertion> {\n readonly type = \"build_passed\" as const;\n\n evaluate(\n assertion: BuildPassedAssertion,\n _input: EvaluationInput,\n context?: AssertionContext,\n ): AssertionResult {\n const assertionId = randomUUID();\n const workDir = context?.workDir;\n const command = assertion.command ?? DEFAULT_COMMAND;\n const expectedExitCode = assertion.expectedExitCode ?? DEFAULT_EXIT_CODE;\n\n if (!workDir) {\n return this.createResult(assertionId, {\n status: AssertionResultStatus.FAILED,\n message: \"No working directory provided for build_passed assertion\",\n expected: String(expectedExitCode),\n });\n }\n\n let exitCode: number | null = null;\n let errorMessage: string | null = null;\n let stdout: string | undefined;\n let stderr: string | undefined;\n\n console.log(`[build_passed] Running \"${command}\" in: ${workDir}`);\n\n try {\n execSync(command, {\n cwd: workDir,\n encoding: \"utf-8\",\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n exitCode = 0;\n } catch (err) {\n const error = err as Error & {\n status?: number;\n code?: number;\n stdout?: string | Buffer;\n stderr?: string | Buffer;\n };\n exitCode =\n typeof error.status === \"number\"\n ? error.status\n : typeof error.code === \"number\"\n ? error.code\n : null;\n errorMessage = error.message;\n stdout = this.bufferToString(error.stdout);\n stderr = this.bufferToString(error.stderr);\n }\n\n const passed = exitCode !== null && exitCode === expectedExitCode;\n\n const details: Record<string, unknown> = { workDir, command };\n if (stdout !== undefined && stdout !== \"\") {\n details.stdout = stdout;\n }\n if (stderr !== undefined && stderr !== \"\") {\n details.stderr = stderr;\n }\n\n return this.createResult(assertionId, {\n status: passed\n ? AssertionResultStatus.PASSED\n : AssertionResultStatus.FAILED,\n message: this.formatMessage(exitCode, expectedExitCode, errorMessage),\n expected: String(expectedExitCode),\n actual: exitCode !== null ? String(exitCode) : undefined,\n details,\n });\n }\n\n private createResult(\n assertionId: string,\n fields: Partial<AssertionResult>,\n ): AssertionResult {\n return {\n id: randomUUID(),\n assertionId,\n assertionType: \"build_passed\",\n assertionName: \"Build passed\",\n status: AssertionResultStatus.FAILED,\n ...fields,\n };\n }\n\n private bufferToString(\n value: string | Buffer | undefined,\n ): string | undefined {\n if (value === undefined || value === null) return undefined;\n if (typeof value === \"string\") return value;\n return value.toString(\"utf-8\");\n }\n\n private formatMessage(\n exitCode: number | null,\n expectedExitCode: number,\n errorMessage: string | null,\n ): string {\n if (exitCode === null) {\n return `Build failed: ${errorMessage}`;\n }\n if (exitCode === expectedExitCode) {\n return `Build passed (exit code ${exitCode})`;\n }\n return `Build exited with ${exitCode}, expected ${expectedExitCode}`;\n }\n}\n", "import type {\n TimeAssertion,\n AssertionResult,\n EvaluationInput,\n} from \"../types/index.js\";\nimport { AssertionResultStatus } from \"../types/index.js\";\nimport { randomUUID } from \"crypto\";\nimport { AssertionEvaluator } from \"./assertion-evaluator.js\";\n\n/**\n * Evaluator for \"time_limit\" assertion: passes if the scenario completed\n * within the configured maximum duration (maxDurationMs).\n */\nexport class TimeEvaluator extends AssertionEvaluator<TimeAssertion> {\n readonly type = \"time_limit\" as const;\n\n evaluate(assertion: TimeAssertion, input: EvaluationInput): AssertionResult {\n const maxDurationMs = assertion.maxDurationMs;\n\n if (input.durationMs == null) {\n return this.createResult({\n status: AssertionResultStatus.FAILED,\n message: \"No duration data available for time assertion\",\n expected: `<= ${maxDurationMs}ms`,\n });\n }\n\n const passed = input.durationMs <= maxDurationMs;\n\n return this.createResult({\n status: passed\n ? AssertionResultStatus.PASSED\n : AssertionResultStatus.FAILED,\n message: passed\n ? `Completed in ${input.durationMs}ms (limit: ${maxDurationMs}ms)`\n : `Exceeded time limit: ${input.durationMs}ms > ${maxDurationMs}ms`,\n expected: `<= ${maxDurationMs}ms`,\n actual: `${input.durationMs}ms`,\n });\n }\n\n private createResult(fields: Partial<AssertionResult>): AssertionResult {\n return {\n id: randomUUID(),\n assertionId: randomUUID(),\n assertionType: \"time_limit\",\n assertionName: \"Time limit\",\n status: AssertionResultStatus.FAILED,\n ...fields,\n };\n }\n}\n", "import type {\n CostAssertion,\n AssertionResult,\n EvaluationInput,\n} from \"../types/index.js\";\nimport { AssertionResultStatus } from \"../types/index.js\";\nimport { randomUUID } from \"crypto\";\nimport { AssertionEvaluator } from \"./assertion-evaluator.js\";\n\n/**\n * Evaluator for \"cost\" assertion: checks that the scenario's LLM execution cost\n * stays within a configured USD threshold by reading llmTrace.summary.totalCostUsd.\n */\nexport class CostEvaluator extends AssertionEvaluator<CostAssertion> {\n readonly type = \"cost\" as const;\n\n evaluate(assertion: CostAssertion, input: EvaluationInput): AssertionResult {\n const assertionId = randomUUID();\n const id = randomUUID();\n const assertionName = \"Cost\";\n const assertionType = \"cost\";\n const maxCostUsd = assertion.maxCostUsd;\n\n if (!input.llmTrace) {\n return {\n id,\n assertionId,\n assertionType,\n assertionName,\n status: AssertionResultStatus.SKIPPED,\n message: \"No LLM trace available to check cost\",\n };\n }\n\n const actualCostUsd = input.llmTrace.summary.totalCostUsd;\n const formattedActual = actualCostUsd.toFixed(6);\n const formattedMax = maxCostUsd.toFixed(6);\n const passed = Number(formattedActual) <= Number(formattedMax);\n\n return {\n id,\n assertionId,\n assertionType,\n assertionName,\n status: passed\n ? AssertionResultStatus.PASSED\n : AssertionResultStatus.FAILED,\n message: passed\n ? `Cost $${formattedActual} is within limit of $${formattedMax}`\n : `Cost $${formattedActual} exceeds limit of $${formattedMax}`,\n expected: `<= $${formattedMax}`,\n actual: `$${formattedActual}`,\n details: { actualCostUsd, maxCostUsd },\n };\n }\n}\n", "import { tool, type Tool } from \"ai\";\nimport { z } from \"zod\";\nimport { readFile } from \"fs/promises\";\nimport path from \"path\";\n\nexport type ReadFileResult =\n | { path: string; content: string }\n | { error: string };\n\nexport function createReadFileTool(\n workDir: string,\n): Tool<{ path: string }, ReadFileResult> {\n const resolvedWorkDir = path.resolve(workDir);\n return tool({\n description:\n \"Read the content of any file in the workspace by its relative path. Use this to inspect file contents when evaluating code changes.\",\n inputSchema: z.object({\n path: z.string().describe(\"Relative file path in the workspace\"),\n }),\n execute: async ({\n path: filePath,\n }: {\n path: string;\n }): Promise<{ path: string; content: string } | { error: string }> => {\n const resolved = path.resolve(resolvedWorkDir, filePath);\n if (!resolved.startsWith(resolvedWorkDir + path.sep)) {\n return { error: `Access denied: path escapes workspace directory` };\n }\n try {\n const content = await readFile(resolved, \"utf-8\");\n return { path: filePath, content };\n } catch {\n return { error: `File not found: ${filePath}` };\n }\n },\n });\n}\n", "import type {\n LlmJudgeAssertion,\n AssertionResult,\n LLMTrace,\n EvaluationInput,\n} from \"../types/index.js\";\nimport { AssertionResultStatus } from \"../types/index.js\";\nimport { createReadFileTool } from \"../tools/index.js\";\nimport { randomUUID } from \"crypto\";\nimport { createAnthropic } from \"@ai-sdk/anthropic\";\nimport {\n generateText,\n Output,\n APICallError,\n NoObjectGeneratedError,\n stepCountIs,\n type LanguageModel,\n} from \"ai\";\nimport { z } from \"zod\";\nimport type { AssertionContext } from \"./assertion-evaluator.js\";\nimport { AssertionEvaluator } from \"./assertion-evaluator.js\";\n\nexport interface JudgeResult {\n text: string;\n score: number;\n scoreReasoning: string;\n}\n\nexport const JudgeResultSchema = z.object({\n text: z.string().describe(\"A brief textual verdict of the test result\"),\n score: z\n .number()\n .min(0)\n .max(100)\n .describe(\n \"A number from 0 to 100 reflecting how well the answer meets the acceptance criteria\",\n ),\n scoreReasoning: z\n .string()\n .describe(\"A concise explanation justifying the assigned score\"),\n});\n\nconst MAX_JUDGE_STEPS = 20;\n\n/**\n * Format LLM trace as readable text for the judge (step number, type, tool name/args, output preview).\n */\nexport function formatTraceForJudge(llmTrace: LLMTrace | undefined): string {\n if (!llmTrace?.steps?.length) {\n return \"No trace available.\";\n }\n const lines: string[] = [];\n for (const step of llmTrace.steps) {\n const parts: string[] = [\n `Step ${step.stepNumber}`,\n `type: ${step.type}`,\n `duration: ${step.durationMs}ms`,\n ];\n if (step.toolName) {\n parts.push(`tool: ${step.toolName}`);\n if (step.toolArguments) {\n parts.push(`args: ${step.toolArguments}`);\n }\n }\n if (step.outputPreview) {\n parts.push(`output: ${step.outputPreview}`);\n }\n if (step.error) {\n parts.push(`error: ${step.error}`);\n }\n lines.push(parts.join(\", \"));\n }\n return lines.join(\"\\n\");\n}\n\n/**\n * Context object for placeholder replacement.\n */\nexport interface PlaceholderContext {\n output: string;\n cwd: string;\n changedFiles: string;\n modifiedFiles: string;\n newFiles: string;\n trace: string;\n}\n\nexport function replacePlaceholders(\n str: string,\n ctx: PlaceholderContext,\n): string {\n return str\n .replace(/\\{\\{output\\}\\}/g, ctx.output)\n .replace(/\\{\\{cwd\\}\\}/g, ctx.cwd)\n .replace(/\\{\\{changedFiles\\}\\}/g, ctx.changedFiles)\n .replace(/\\{\\{modifiedFiles\\}\\}/g, ctx.modifiedFiles)\n .replace(/\\{\\{newFiles\\}\\}/g, ctx.newFiles)\n .replace(/\\{\\{trace\\}\\}/g, ctx.trace);\n}\n\n/**\n * Strip markdown code fences (e.g. ```json ... ```) from LLM output,\n * returning only the inner content for JSON parsing.\n */\nexport function stripMarkdownCodeBlock(text: string): string {\n const trimmed = text.trim();\n const match = trimmed.match(/^```(?:\\w+)?\\s*\\n?([\\s\\S]*?)\\n?\\s*```$/);\n return match ? match[1].trim() : trimmed;\n}\n\nexport function validateJudgeResult(parsed: unknown): JudgeResult {\n if (parsed === null || typeof parsed !== \"object\") {\n throw new Error(\"Judge result is not an object\");\n }\n const obj = parsed as Record<string, unknown>;\n if (typeof obj.text !== \"string\") {\n throw new Error(\"Judge result does not contain a valid text field\");\n }\n if (typeof obj.score !== \"number\") {\n throw new Error(\"Judge result does not contain a valid score field\");\n }\n if (obj.score < 0 || obj.score > 100) {\n throw new Error(\"Judge result score is not between 0 and 100\");\n }\n if (typeof obj.scoreReasoning !== \"string\") {\n throw new Error(\n \"Judge result does not contain a valid scoreReasoning field\",\n );\n }\n return {\n text: obj.text,\n score: obj.score,\n scoreReasoning: obj.scoreReasoning,\n };\n}\n\nconst DEFAULT_MIN_SCORE = 70;\n\n/** Default judge context (run data + placeholders); used when assertion.systemPrompt is empty. */\nconst DEFAULT_JUDGE_CONTEXT = `You are judging a scenario run. The ACTUAL run data is provided below \u2014 use it to verify facts:\n\n- {{output}}: the agent's final output\n- {{cwd}}: working directory\n- {{changedFiles}}: list of all files changed (or \"No files were changed\")\n- {{modifiedFiles}}: list of existing files that were modified (or \"No files were modified\")\n- {{newFiles}}: list of new files that were created (or \"No new files were created\")\n- {{trace}}: step-by-step trace (tool calls, completions) so you can check e.g. which tools were called and how many times\n\nYou have access to a read_file tool that lets you read the content of ANY file in the workspace (not just changed files). Use it to inspect file contents whenever you need to verify claims about code, check imports, review implementations, or validate that specific code patterns exist. Always read files before making judgments about their content \u2014 do not guess.\n\nCRITICAL: When the user asks you to verify a specific fact, compare it strictly against the actual data above and the actual file contents (use the read_file tool). If the expected outcome does NOT match the actual outcome, you MUST give a score of 0 or near 0. Do not be lenient \u2014 factual mismatches are failures.`;\n\n/**\n * Evaluator for \"llm_judge\" assertion: an LLM judges the scenario output\n * (prompt with {{output}}, {{cwd}}, {{changedFiles}}, {{trace}}) and returns a score 0-100.\n * Passes if score >= minScore.\n */\nexport class LlmJudgeEvaluator extends AssertionEvaluator<LlmJudgeAssertion> {\n readonly type = \"llm_judge\" as const;\n\n async evaluate(\n assertion: LlmJudgeAssertion,\n input: EvaluationInput,\n context?: AssertionContext,\n ): Promise<AssertionResult> {\n const assertionId = randomUUID();\n const workDir = context?.workDir ?? \"\";\n\n const output = input.outputText ?? \"\";\n const fileDiffs = input.fileDiffs ?? [];\n\n const changedPaths = fileDiffs.map((d) => d.path);\n const modifiedPaths = fileDiffs\n .filter((d) => d.status === \"modified\")\n .map((d) => d.path);\n const newPaths = fileDiffs\n .filter((d) => d.status === \"new\")\n .map((d) => d.path);\n\n const changedFiles =\n changedPaths.length > 0\n ? changedPaths.map((p: string) => `- ${p}`).join(\"\\n\")\n : \"No files were changed\";\n const modifiedFiles =\n modifiedPaths.length > 0\n ? modifiedPaths.map((p: string) => `- ${p}`).join(\"\\n\")\n : \"No files were modified\";\n const newFiles =\n newPaths.length > 0\n ? newPaths.map((p: string) => `- ${p}`).join(\"\\n\")\n : \"No new files were created\";\n\n const trace = formatTraceForJudge(input.llmTrace);\n const ctx: PlaceholderContext = {\n output,\n cwd: workDir,\n changedFiles,\n modifiedFiles,\n newFiles,\n trace,\n };\n const replace = (s: string) => replacePlaceholders(s, ctx);\n\n const finalPrompt = replace(assertion.prompt);\n\n const minScore = assertion.minScore ?? DEFAULT_MIN_SCORE;\n const maxOutputTokens = assertion.maxTokens ?? 1024;\n const temperature = assertion.temperature ?? 0;\n const modelId = assertion.model ?? context?.defaultJudgeModel;\n\n const model = this.resolveModel(context, modelId);\n if (!model) {\n const reason =\n !modelId && !context?.model\n ? \"No model configured for llm_judge assertion (set model on assertion or provide defaultJudgeModel/model in context)\"\n : \"No llmConfig for llm_judge assertion (AI gateway required)\";\n return {\n id: randomUUID(),\n assertionId,\n assertionType: \"llm_judge\",\n assertionName: \"LLM judge\",\n status: AssertionResultStatus.FAILED,\n message: reason,\n expected: String(minScore),\n };\n }\n\n const systemPrompt =\n assertion.systemPrompt != null && assertion.systemPrompt !== \"\"\n ? replace(assertion.systemPrompt)\n : replace(DEFAULT_JUDGE_CONTEXT);\n\n try {\n const judgeResult = await this.callGenerateText(\n model,\n finalPrompt,\n systemPrompt,\n maxOutputTokens,\n temperature,\n workDir || undefined,\n );\n\n const passed = judgeResult.score >= minScore;\n return {\n id: randomUUID(),\n assertionId,\n assertionType: \"llm_judge\",\n assertionName: \"LLM judge\",\n status: passed\n ? AssertionResultStatus.PASSED\n : AssertionResultStatus.FAILED,\n message: passed\n ? `Judge score ${judgeResult.score} >= ${minScore}: ${judgeResult.text}`\n : `Judge score ${judgeResult.score} < ${minScore}: ${judgeResult.text}`,\n expected: String(minScore),\n actual: String(judgeResult.score),\n details: {\n score: judgeResult.score,\n scoreReasoning: judgeResult.scoreReasoning,\n text: judgeResult.text,\n },\n };\n } catch (err) {\n if (NoObjectGeneratedError.isInstance(err)) {\n return {\n id: randomUUID(),\n assertionId,\n assertionType: \"llm_judge\",\n assertionName: \"LLM judge\",\n status: AssertionResultStatus.FAILED,\n message: \"LLM judge failed to produce valid structured output\",\n expected: String(minScore),\n details: {\n rawText:\n typeof err.text === \"string\" ? err.text.slice(0, 500) : undefined,\n },\n };\n }\n\n const message = err instanceof Error ? err.message : String(err);\n const details: Record<string, unknown> = {\n error: message,\n model: modelId,\n };\n\n if (APICallError.isInstance(err)) {\n details.statusCode = err.statusCode;\n details.url = err.url;\n details.isRetryable = err.isRetryable;\n details.responseBody =\n typeof err.responseBody === \"string\"\n ? err.responseBody.slice(0, 2000)\n : err.responseBody;\n }\n\n return {\n id: randomUUID(),\n assertionId,\n assertionType: \"llm_judge\",\n assertionName: \"LLM judge\",\n status: AssertionResultStatus.FAILED,\n message: `LLM judge call failed: ${message}`,\n expected: String(minScore),\n details,\n };\n }\n }\n\n /**\n * Resolve the LanguageModel to use: context.model (injected mock/override)\n * takes precedence, otherwise create from llmConfig + modelId.\n */\n private resolveModel(\n context: AssertionContext | undefined,\n modelId: string | undefined,\n ): LanguageModel | null {\n if (context?.model) {\n return context.model;\n }\n if (!modelId || !context?.llmConfig) {\n return null;\n }\n const anthropic = createAnthropic({\n baseURL: context.llmConfig.baseUrl,\n apiKey: \"dummy\",\n headers: context.llmConfig.headers,\n });\n return anthropic(modelId);\n }\n\n private async callGenerateText(\n model: LanguageModel,\n prompt: string,\n system: string,\n maxOutputTokens: number,\n temperature: number,\n workDir?: string,\n ): Promise<JudgeResult> {\n const baseOptions = {\n model,\n prompt,\n system,\n maxOutputTokens,\n temperature,\n output: Output.object({ schema: JudgeResultSchema }),\n stopWhen: stepCountIs(MAX_JUDGE_STEPS),\n } as const;\n\n const { output } = workDir\n ? await generateText({\n ...baseOptions,\n tools: { read_file: createReadFileTool(workDir) },\n })\n : await generateText(baseOptions);\n\n return output;\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["import_zod", "LLMStepType", "import_zod", "AssertionResultStatus", "import_crypto", "message", "import_crypto", "import_crypto", "import_crypto", "import_zod", "path", "import_crypto", "import_ai", "import_zod"]
|
|
7
7
|
}
|