veryfront 0.1.1127 → 0.1.1132

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 (118) hide show
  1. package/esm/cli/commands/eval/command.d.ts +2 -70
  2. package/esm/cli/commands/eval/command.d.ts.map +1 -1
  3. package/esm/cli/commands/eval/command.js +207 -550
  4. package/esm/cli/templates/manifest.d.ts +1 -0
  5. package/esm/cli/templates/manifest.js +3 -2
  6. package/esm/deno.js +1 -1
  7. package/esm/src/agent/hosted/chat-execution-runtime.d.ts.map +1 -1
  8. package/esm/src/agent/hosted/chat-execution-runtime.js +16 -33
  9. package/esm/src/agent/hosted/child-fork-tool-sources.d.ts.map +1 -1
  10. package/esm/src/agent/hosted/child-fork-tool-sources.js +6 -38
  11. package/esm/src/agent/hosted/hosted-chat-finalization.d.ts +30 -0
  12. package/esm/src/agent/hosted/hosted-chat-finalization.d.ts.map +1 -0
  13. package/esm/src/agent/hosted/hosted-chat-finalization.js +249 -0
  14. package/esm/src/agent/hosted/project-remote-tool-source.d.ts.map +1 -1
  15. package/esm/src/agent/hosted/project-remote-tool-source.js +4 -23
  16. package/esm/src/agent/mcp-tool-policy.d.ts +19 -0
  17. package/esm/src/agent/mcp-tool-policy.d.ts.map +1 -0
  18. package/esm/src/agent/mcp-tool-policy.js +66 -0
  19. package/esm/src/agent/runtime/mcp-server-tool-sources.d.ts.map +1 -1
  20. package/esm/src/agent/runtime/mcp-server-tool-sources.js +8 -44
  21. package/esm/src/discovery/import-rewriter.d.ts.map +1 -1
  22. package/esm/src/discovery/import-rewriter.js +7 -90
  23. package/esm/src/eval/run-report.d.ts +189 -0
  24. package/esm/src/eval/run-report.d.ts.map +1 -0
  25. package/esm/src/eval/run-report.js +603 -0
  26. package/esm/src/html/hydration-script-builder/dev-client-renderer.js +1 -1
  27. package/esm/src/html/hydration-script-builder/prod-scripts.js +1 -1
  28. package/esm/src/html/hydration-script-builder/templates/router.d.ts.map +1 -1
  29. package/esm/src/html/hydration-script-builder/templates/router.js +35 -11
  30. package/esm/src/html/utils.d.ts.map +1 -1
  31. package/esm/src/html/utils.js +19 -45
  32. package/esm/src/index.client.d.ts +6 -21
  33. package/esm/src/index.client.d.ts.map +1 -1
  34. package/esm/src/index.client.js +6 -21
  35. package/esm/src/modules/server/ssr-import-rewriter.d.ts +2 -26
  36. package/esm/src/modules/server/ssr-import-rewriter.d.ts.map +1 -1
  37. package/esm/src/modules/server/ssr-import-rewriter.js +1 -206
  38. package/esm/src/react/runtime/core.d.ts +22 -0
  39. package/esm/src/react/runtime/core.d.ts.map +1 -1
  40. package/esm/src/react/runtime/core.js +1 -1
  41. package/esm/src/rendering/orchestrator/module-collection.d.ts +8 -0
  42. package/esm/src/rendering/orchestrator/module-collection.d.ts.map +1 -1
  43. package/esm/src/rendering/orchestrator/module-collection.js +10 -0
  44. package/esm/src/rendering/orchestrator/pipeline.d.ts.map +1 -1
  45. package/esm/src/rendering/orchestrator/pipeline.js +2 -2
  46. package/esm/src/routing/api/context-builder.d.ts +7 -0
  47. package/esm/src/routing/api/context-builder.d.ts.map +1 -1
  48. package/esm/src/routing/api/context-builder.js +20 -2
  49. package/esm/src/routing/api/module-loader/external-import-rewriter.d.ts +2 -7
  50. package/esm/src/routing/api/module-loader/external-import-rewriter.d.ts.map +1 -1
  51. package/esm/src/routing/api/module-loader/external-import-rewriter.js +10 -209
  52. package/esm/src/server/dev-server/file-watch-setup.d.ts.map +1 -1
  53. package/esm/src/server/dev-server/file-watch-setup.js +11 -1
  54. package/esm/src/server/dev-server/middleware.d.ts.map +1 -1
  55. package/esm/src/server/dev-server/middleware.js +45 -11
  56. package/esm/src/server/handlers/dev/files/esbuild-plugins.d.ts.map +1 -1
  57. package/esm/src/server/handlers/dev/files/esbuild-plugins.js +22 -0
  58. package/esm/src/server/handlers/request/module/page-data-endpoint-handler.js +24 -1
  59. package/esm/src/server/handlers/response/not-found.d.ts +0 -1
  60. package/esm/src/server/handlers/response/not-found.d.ts.map +1 -1
  61. package/esm/src/server/handlers/response/not-found.js +9 -110
  62. package/esm/src/server/runtime-handler/index.d.ts.map +1 -1
  63. package/esm/src/server/runtime-handler/index.js +66 -145
  64. package/esm/src/server/runtime-handler/project-runtime-context.d.ts +118 -0
  65. package/esm/src/server/runtime-handler/project-runtime-context.d.ts.map +1 -0
  66. package/esm/src/server/runtime-handler/project-runtime-context.js +181 -0
  67. package/esm/src/server/services/rsc/orchestrators/handler.d.ts +3 -1
  68. package/esm/src/server/services/rsc/orchestrators/handler.d.ts.map +1 -1
  69. package/esm/src/server/services/rsc/orchestrators/handler.js +13 -8
  70. package/esm/src/server/services/rsc/orchestrators/render-handler.d.ts +1 -1
  71. package/esm/src/server/services/rsc/orchestrators/render-handler.d.ts.map +1 -1
  72. package/esm/src/server/services/rsc/orchestrators/render-handler.js +2 -2
  73. package/esm/src/transforms/esm/http-cache.d.ts.map +1 -1
  74. package/esm/src/transforms/esm/http-cache.js +132 -46
  75. package/esm/src/transforms/esm/import-attributes.d.ts.map +1 -1
  76. package/esm/src/transforms/esm/import-attributes.js +2 -9
  77. package/esm/src/transforms/esm/in-flight-manager.d.ts +3 -2
  78. package/esm/src/transforms/esm/in-flight-manager.d.ts.map +1 -1
  79. package/esm/src/transforms/esm/in-flight-manager.js +6 -5
  80. package/esm/src/transforms/import-rewriter/core.d.ts +8 -0
  81. package/esm/src/transforms/import-rewriter/core.d.ts.map +1 -0
  82. package/esm/src/transforms/import-rewriter/core.js +27 -0
  83. package/esm/src/transforms/import-rewriter/import-edit.d.ts +57 -0
  84. package/esm/src/transforms/import-rewriter/import-edit.d.ts.map +1 -0
  85. package/esm/src/transforms/import-rewriter/import-edit.js +151 -0
  86. package/esm/src/transforms/import-rewriter/package-resolution.d.ts +12 -0
  87. package/esm/src/transforms/import-rewriter/package-resolution.d.ts.map +1 -0
  88. package/esm/src/transforms/import-rewriter/package-resolution.js +79 -0
  89. package/esm/src/transforms/import-rewriter/parse-cache.d.ts +4 -42
  90. package/esm/src/transforms/import-rewriter/parse-cache.d.ts.map +1 -1
  91. package/esm/src/transforms/import-rewriter/parse-cache.js +3 -134
  92. package/esm/src/transforms/import-rewriter/route-adapter.d.ts +24 -0
  93. package/esm/src/transforms/import-rewriter/route-adapter.d.ts.map +1 -0
  94. package/esm/src/transforms/import-rewriter/route-adapter.js +235 -0
  95. package/esm/src/transforms/import-rewriter/ssr-adapter.d.ts +27 -0
  96. package/esm/src/transforms/import-rewriter/ssr-adapter.d.ts.map +1 -0
  97. package/esm/src/transforms/import-rewriter/ssr-adapter.js +206 -0
  98. package/esm/src/transforms/import-rewriter/strategies/bare-strategy.d.ts.map +1 -1
  99. package/esm/src/transforms/import-rewriter/strategies/bare-strategy.js +16 -2
  100. package/esm/src/transforms/import-rewriter/unified-rewriter.d.ts +0 -4
  101. package/esm/src/transforms/import-rewriter/unified-rewriter.d.ts.map +1 -1
  102. package/esm/src/transforms/import-rewriter/unified-rewriter.js +2 -39
  103. package/esm/src/utils/version-constant.d.ts +1 -1
  104. package/esm/src/utils/version-constant.js +1 -1
  105. package/esm/src/workflow/executor/workflow-executor.d.ts +0 -20
  106. package/esm/src/workflow/executor/workflow-executor.d.ts.map +1 -1
  107. package/esm/src/workflow/executor/workflow-executor.js +41 -320
  108. package/esm/src/workflow/runtime/approval-manager.d.ts.map +1 -1
  109. package/esm/src/workflow/runtime/approval-manager.js +20 -61
  110. package/esm/src/workflow/runtime/workflow-run-control.d.ts +95 -0
  111. package/esm/src/workflow/runtime/workflow-run-control.d.ts.map +1 -0
  112. package/esm/src/workflow/runtime/workflow-run-control.js +529 -0
  113. package/esm/src/workflow/worker/run-manager.d.ts.map +1 -1
  114. package/esm/src/workflow/worker/run-manager.js +18 -92
  115. package/esm/src/workflow/worker/shared.d.ts +1 -1
  116. package/esm/src/workflow/worker/shared.d.ts.map +1 -1
  117. package/esm/src/workflow/worker/shared.js +17 -15
  118. package/package.json +5 -5
@@ -0,0 +1,603 @@
1
+ import { join, relative } from "../../deps/jsr.io/@std/path/1.1.4/mod.js";
2
+ import { compareEvalReports } from "./baseline.js";
3
+ import { compareEvalModelReports, createEvalModelComparisonMarkdown } from "./model-comparison.js";
4
+ import { EVAL_REPORT_SCHEMA_VERSION } from "./report.js";
5
+ import { createEvalRunId } from "./run-id.js";
6
+ function createEvalReportDirTimestamp(runId) {
7
+ return runId.startsWith("evalrun_") ? runId.slice("evalrun_".length) : runId;
8
+ }
9
+ function sanitizeEvalReportDirLabel(label) {
10
+ const normalized = label.startsWith("eval:") ? label.slice("eval:".length) : label;
11
+ return normalized.trim().replace(/[^A-Za-z0-9._-]+/g, "-").replace(/-+/g, "-").replace(/^[-._]+|[-._]+$/g, "");
12
+ }
13
+ function createDefaultEvalReportDir(runId, label) {
14
+ const timestamp = createEvalReportDirTimestamp(runId);
15
+ const suffix = label ? sanitizeEvalReportDirLabel(label) : "";
16
+ return join(".veryfront", "evals", suffix ? `${timestamp}-${suffix}` : timestamp);
17
+ }
18
+ function createEvalArtifactPaths(reportDir) {
19
+ return {
20
+ directory: reportDir,
21
+ summary: join(reportDir, "summary.json"),
22
+ results: join(reportDir, "results.jsonl"),
23
+ reportMarkdown: join(reportDir, "report.md"),
24
+ };
25
+ }
26
+ function createEvalSuiteArtifactPaths(reportDir) {
27
+ return {
28
+ directory: reportDir,
29
+ summary: join(reportDir, "summary.json"),
30
+ results: join(reportDir, "results.jsonl"),
31
+ reportMarkdown: join(reportDir, "report.md"),
32
+ };
33
+ }
34
+ function createEvalModelArtifactPaths(reportDir, model) {
35
+ const directory = join(reportDir, "models", sanitizeModelIdForPath(model));
36
+ return {
37
+ directory,
38
+ summary: join(directory, "summary.json"),
39
+ results: join(directory, "results.jsonl"),
40
+ reportMarkdown: join(directory, "report.md"),
41
+ junit: join(directory, "junit.xml"),
42
+ };
43
+ }
44
+ function createEvalModelComparisonArtifactPaths(reportDir, models) {
45
+ return {
46
+ directory: reportDir,
47
+ comparisonJson: join(reportDir, "comparison.json"),
48
+ comparisonMarkdown: join(reportDir, "comparison.md"),
49
+ models: Object.fromEntries(models.map((model) => [model, createEvalModelArtifactPaths(reportDir, model)])),
50
+ };
51
+ }
52
+ function createEvalSuiteChildDirectory(suiteDirectory, index, evalId) {
53
+ return join(suiteDirectory, `${String(index + 1).padStart(3, "0")}-${sanitizeEvalReportDirLabel(evalId)}`);
54
+ }
55
+ function sanitizeModelIdForPath(model) {
56
+ return model.trim().replace(/[^A-Za-z0-9._-]+/g, "__").replace(/^_+|_+$/g, "") || "model";
57
+ }
58
+ function uniqueTrimmedValues(values) {
59
+ return Array.from(new Set(values.map((value) => value.trim()).filter(Boolean)));
60
+ }
61
+ function summarizeReportForCli(report) {
62
+ return {
63
+ runId: report.runId,
64
+ evalId: report.definitionId,
65
+ target: report.target,
66
+ records: report.summary.records,
67
+ passed: report.summary.passed,
68
+ failed: report.summary.failed,
69
+ passRate: report.summary.passRate,
70
+ metrics: report.summary.metrics,
71
+ };
72
+ }
73
+ function sortEvals(evals) {
74
+ return [...evals].sort((left, right) => left.id.localeCompare(right.id) || left.filePath.localeCompare(right.filePath));
75
+ }
76
+ function createSummaryArtifact(report, baseline) {
77
+ return {
78
+ kind: "eval-summary",
79
+ schemaVersion: report.schemaVersion ?? EVAL_REPORT_SCHEMA_VERSION,
80
+ runId: report.runId,
81
+ definitionId: report.definitionId,
82
+ targetKind: report.targetKind,
83
+ target: report.target,
84
+ ...(report.dataset ? { dataset: report.dataset } : {}),
85
+ startedAt: report.startedAt,
86
+ endedAt: report.endedAt,
87
+ summary: report.summary,
88
+ ...(report.metadata ? { metadata: report.metadata } : {}),
89
+ ...(report.exports ? { exports: report.exports } : {}),
90
+ ...(baseline ? { baseline } : {}),
91
+ };
92
+ }
93
+ function createResultsJsonl(report) {
94
+ if (report.records.length === 0)
95
+ return "";
96
+ return `${report.records.map((record) => JSON.stringify(record)).join("\n")}\n`;
97
+ }
98
+ function markdownCell(value) {
99
+ return value.replaceAll("|", "\\|").replaceAll("\n", " ");
100
+ }
101
+ function numberCell(value) {
102
+ return value === undefined ? "-" : String(Math.round(value));
103
+ }
104
+ function decimalCell(value) {
105
+ if (value === undefined)
106
+ return "-";
107
+ if (Number.isInteger(value))
108
+ return String(value);
109
+ return value.toFixed(4).replace(/0+$/, "").replace(/\.$/, "");
110
+ }
111
+ function percentCell(value) {
112
+ return `${Math.round(value * 100)}%`;
113
+ }
114
+ function durationCell(valueMs) {
115
+ return valueMs === undefined ? "-" : `${(valueMs / 1000).toFixed(3)}s`;
116
+ }
117
+ function usdCell(value) {
118
+ if (value === undefined)
119
+ return "-";
120
+ const absolute = Math.abs(value);
121
+ if (absolute >= 0.01)
122
+ return `$${value.toFixed(2)}`;
123
+ return `$${value.toFixed(6).replace(/0+$/, "").replace(/\.$/, "")}`;
124
+ }
125
+ function usageRows(usage) {
126
+ if (!usage)
127
+ return [];
128
+ const rows = [
129
+ ["Input tokens", numberCell(usage.inputTokens)],
130
+ ["Output tokens", numberCell(usage.outputTokens)],
131
+ ["Total tokens", numberCell(usage.totalTokens)],
132
+ ["Billable input tokens", numberCell(usage.billableInputTokens)],
133
+ ["Billable output tokens", numberCell(usage.billableOutputTokens)],
134
+ ["Provider input cost USD", usdCell(usage.providerInputCostUsd)],
135
+ ["Provider output cost USD", usdCell(usage.providerOutputCostUsd)],
136
+ ["Provider cost USD", usdCell(usage.providerCostUsd ?? usage.costUsd)],
137
+ ["Veryfront input charge USD", usdCell(usage.veryfrontInputChargeUsd)],
138
+ ["Veryfront output charge USD", usdCell(usage.veryfrontOutputChargeUsd)],
139
+ ["Veryfront charge USD", usdCell(usage.veryfrontChargeUsd)],
140
+ ["Veryfront billed USD", usdCell(usage.veryfrontBilledUsd)],
141
+ ["Cost credits", decimalCell(usage.costCredits)],
142
+ ["Cost source", usage.costSource ?? "-"],
143
+ ["Billing mode", usage.billingMode ?? "-"],
144
+ ["Usage capture status", usage.usageCaptureStatus ?? "-"],
145
+ ];
146
+ return rows.filter(([, value]) => value !== "-");
147
+ }
148
+ function isBlockingEvalResultFailure(result) {
149
+ return !result.skipped && result.pass === false &&
150
+ (result.severity === "gate" || result.severity === "budget");
151
+ }
152
+ function examplePassed(record) {
153
+ if (!record.completed || record.error)
154
+ return false;
155
+ return [...(record.metrics ?? []), ...(record.checks ?? [])].every((result) => !isBlockingEvalResultFailure(result));
156
+ }
157
+ function createEvalMarkdownReport(report, baseline) {
158
+ const lines = [
159
+ `# Eval report: ${markdownCell(report.definitionId)}`,
160
+ "",
161
+ `Run: \`${markdownCell(report.runId)}\``,
162
+ `Target: \`${markdownCell(report.target)}\``,
163
+ ...(report.metadata?.model ? [`Model: \`${markdownCell(report.metadata.model)}\``] : []),
164
+ `Result: \`${report.summary.passed}/${report.summary.records} passed (${percentCell(report.summary.passRate)})\``,
165
+ "",
166
+ "## Metrics",
167
+ "",
168
+ "| Metric | Severity | Passed | Failed | Pass rate |",
169
+ "| --- | --- | ---: | ---: | ---: |",
170
+ ];
171
+ for (const metric of report.summary.metrics) {
172
+ lines.push(`| \`${markdownCell(metric.name)}\` | ${metric.severity} | ${metric.passed} | ${metric.failed} | ${percentCell(metric.passRate)} |`);
173
+ }
174
+ const rows = usageRows(report.summary.usage);
175
+ if (rows.length > 0) {
176
+ lines.push("", "## Usage", "", "| Usage | Value |", "| --- | ---: |");
177
+ for (const [label, value] of rows) {
178
+ lines.push(`| ${label} | ${value.startsWith("$") ? `\`${value}\`` : value} |`);
179
+ }
180
+ }
181
+ lines.push("", "## Examples", "", "| Example | Result | Duration | Tokens | Billed USD | Credits |", "| --- | ---: | ---: | ---: | ---: | ---: |");
182
+ for (const record of report.records) {
183
+ lines.push(`| \`${markdownCell(record.id)}\` | ${examplePassed(record) ? "PASS" : "FAIL"} | ${durationCell(record.durationMs)} | ${numberCell(record.usage.totalTokens)} | ${record.usage.veryfrontBilledUsd === undefined
184
+ ? "-"
185
+ : `\`${usdCell(record.usage.veryfrontBilledUsd)}\``} | ${decimalCell(record.usage.costCredits)} |`);
186
+ }
187
+ if (baseline) {
188
+ const direction = baseline.passRateDelta >= 0 ? "+" : "";
189
+ lines.push("", "## Baseline", "", `Status: \`${baseline.regressed ? "regressed" : "ok"}\``, `Pass rate delta: \`${direction}${Math.round(baseline.passRateDelta * 100)} pp\``);
190
+ if (baseline.newFailedExamples.length > 0) {
191
+ lines.push(`New failed examples: ${baseline.newFailedExamples.map(markdownCell).join(", ")}`);
192
+ }
193
+ }
194
+ if (report.exports?.length) {
195
+ lines.push("", "## Exports", "");
196
+ for (const result of report.exports) {
197
+ lines.push(`- \`${markdownCell(result.exporterId)}\`: ${result.ok ? "ok" : `failed, ${markdownCell(result.error)}`}`);
198
+ }
199
+ }
200
+ lines.push("");
201
+ return lines.join("\n");
202
+ }
203
+ function xmlEscape(value) {
204
+ return value
205
+ .replaceAll("&", "&")
206
+ .replaceAll("<", "&lt;")
207
+ .replaceAll(">", "&gt;")
208
+ .replaceAll('"', "&quot;")
209
+ .replaceAll("'", "&apos;");
210
+ }
211
+ function blockingResults(record) {
212
+ return [...(record.metrics ?? []), ...(record.checks ?? [])].filter((result) => !result.skipped && result.pass === false &&
213
+ (result.severity === "gate" || result.severity === "budget"));
214
+ }
215
+ function skippedResults(record) {
216
+ return [...(record.metrics ?? []), ...(record.checks ?? [])].filter((result) => result.skipped);
217
+ }
218
+ function testcaseName(record) {
219
+ return `${record.exampleId}#${record.repetition}`;
220
+ }
221
+ function failureMessage(result) {
222
+ return `${result.name} failed`;
223
+ }
224
+ function failureBody(result) {
225
+ if (result.explanation)
226
+ return result.explanation;
227
+ if (result.evidence)
228
+ return JSON.stringify(result.evidence);
229
+ return failureMessage(result);
230
+ }
231
+ function createJunitXml(report) {
232
+ const skipped = report.records.reduce((count, record) => count + skippedResults(record).length, 0);
233
+ const lines = [
234
+ '<?xml version="1.0" encoding="UTF-8"?>',
235
+ `<testsuite name="${xmlEscape(report.definitionId)}" tests="${report.summary.records}" failures="${report.summary.failed}" skipped="${skipped}">`,
236
+ ];
237
+ for (const record of report.records) {
238
+ const failures = blockingResults(record);
239
+ const skips = skippedResults(record);
240
+ const attrs = `classname="${xmlEscape(report.definitionId)}" name="${xmlEscape(testcaseName(record))}" time="${(record.durationMs / 1000).toFixed(3)}"`;
241
+ if (failures.length === 0 && skips.length === 0) {
242
+ lines.push(` <testcase ${attrs} />`);
243
+ continue;
244
+ }
245
+ lines.push(` <testcase ${attrs}>`);
246
+ for (const failure of failures) {
247
+ lines.push(` <failure message="${xmlEscape(failureMessage(failure))}">${xmlEscape(failureBody(failure))}</failure>`);
248
+ }
249
+ for (const skip of skips) {
250
+ lines.push(` <skipped message="${xmlEscape(skip.explanation ?? `${skip.name} skipped`)}" />`);
251
+ }
252
+ lines.push(" </testcase>");
253
+ }
254
+ lines.push("</testsuite>");
255
+ return `${lines.join("\n")}\n`;
256
+ }
257
+ async function writeEvalArtifacts(report, paths, artifacts, baseline) {
258
+ await artifacts.writeTextFileEnsuringDir(paths.summary, JSON.stringify(createSummaryArtifact(report, baseline), null, 2));
259
+ await artifacts.writeTextFileEnsuringDir(paths.results, createResultsJsonl(report));
260
+ await artifacts.writeTextFileEnsuringDir(paths.reportMarkdown, createEvalMarkdownReport(report, baseline));
261
+ }
262
+ function createEvalSuiteResultsJsonl(summary) {
263
+ if (summary.results.length === 0)
264
+ return "";
265
+ return `${summary.results.map((result) => JSON.stringify(result)).join("\n")}\n`;
266
+ }
267
+ function createEvalSuiteMarkdown(summary) {
268
+ const rows = summary.results.map((result) => `| ${markdownCell(result.id)} | ${result.status} | ${result.summary ? `${result.summary.passed}/${result.summary.records}` : "n/a"} | ${result.error ? markdownCell(result.error) : ""} |`);
269
+ return [
270
+ "# Eval suite report",
271
+ "",
272
+ `Run: \`${markdownCell(summary.runId)}\``,
273
+ `Result: \`${summary.passed}/${summary.total} passed\``,
274
+ "",
275
+ "| Eval | Status | Records | Error |",
276
+ "| --- | --- | --- | --- |",
277
+ ...rows,
278
+ "",
279
+ ].join("\n");
280
+ }
281
+ function createEvalSuiteJunitXml(summary) {
282
+ const lines = [
283
+ '<?xml version="1.0" encoding="UTF-8"?>',
284
+ `<testsuites tests="${summary.total}" failures="${summary.failed}" skipped="0">`,
285
+ ` <testsuite name="veryfront eval suite" tests="${summary.total}" failures="${summary.failed}" skipped="0">`,
286
+ ];
287
+ for (const result of summary.results) {
288
+ const attrs = `classname="eval" name="${xmlEscape(result.id)}"`;
289
+ if (result.status === "passed") {
290
+ lines.push(` <testcase ${attrs} />`);
291
+ continue;
292
+ }
293
+ const message = result.error ??
294
+ (result.summary?.failed
295
+ ? `${result.summary.failed} record(s) failed`
296
+ : "A required eval export failed.");
297
+ lines.push(` <testcase ${attrs}>`);
298
+ lines.push(` <failure message="${xmlEscape(message)}">${xmlEscape(message)}</failure>`);
299
+ lines.push(" </testcase>");
300
+ }
301
+ lines.push(" </testsuite>");
302
+ lines.push("</testsuites>");
303
+ return `${lines.join("\n")}\n`;
304
+ }
305
+ async function writeEvalSuiteArtifacts(summary, paths, artifacts) {
306
+ await artifacts.writeTextFileEnsuringDir(paths.summary, JSON.stringify(summary, null, 2));
307
+ await artifacts.writeTextFileEnsuringDir(paths.results, createEvalSuiteResultsJsonl(summary));
308
+ await artifacts.writeTextFileEnsuringDir(paths.reportMarkdown, createEvalSuiteMarkdown(summary));
309
+ }
310
+ function createEvalExitCode(report, baseline, exportIsRequired = false) {
311
+ const exportFailed = exportIsRequired &&
312
+ (!(report.exports?.length) || report.exports.some((result) => !result.ok));
313
+ return report.summary.failed === 0 && baseline?.regressed !== true && !exportFailed ? 0 : 1;
314
+ }
315
+ function createOutputHints(paths, input) {
316
+ return {
317
+ reportDirectory: paths.directory,
318
+ reportMarkdown: paths.reportMarkdown,
319
+ ...(input.report ? { report: input.report } : {}),
320
+ ...(input.junit ? { junit: input.junit } : {}),
321
+ ...(input.writeBaseline ? { baselineWritten: input.writeBaseline } : {}),
322
+ };
323
+ }
324
+ function createSuiteOutputHints(paths, input, children) {
325
+ return {
326
+ reportDirectory: paths.directory,
327
+ reportMarkdown: paths.reportMarkdown,
328
+ ...(input.junit ? { junit: input.junit } : {}),
329
+ children,
330
+ };
331
+ }
332
+ function createRunId(clock) {
333
+ return createEvalRunId(clock?.now?.() ?? new Date(), clock?.createSuffix);
334
+ }
335
+ function stripFileProtocol(path) {
336
+ if (!path.startsWith("file://"))
337
+ return path;
338
+ return decodeURIComponent(new URL(path).pathname);
339
+ }
340
+ function displaySourcePath(filePath, projectDir) {
341
+ const normalized = stripFileProtocol(filePath);
342
+ if (normalized.startsWith(projectDir)) {
343
+ return relative(projectDir, normalized);
344
+ }
345
+ return normalized;
346
+ }
347
+ function createMetadata(input, model) {
348
+ return {
349
+ ...(input.provenance ? { provenance: input.provenance } : {}),
350
+ ...(input.kind === "single" && input.selectedModel ? { model: input.selectedModel } : {}),
351
+ ...(model ? { model } : {}),
352
+ };
353
+ }
354
+ async function readBaselineComparison(report, input, artifacts) {
355
+ if (!input.baseline)
356
+ return undefined;
357
+ const baseline = JSON.parse(await artifacts.readTextFile(input.baseline));
358
+ return compareEvalReports(report, baseline, input.baselinePolicy);
359
+ }
360
+ function createEvalReportExportConfig(input, artifactPaths) {
361
+ if (!input.exportConfig)
362
+ return undefined;
363
+ const tags = input.evalItem.definition.tags ?? [];
364
+ const metadata = input.evalItem.definition.metadata ?? {};
365
+ const reportPath = input.report ?? artifactPaths.summary;
366
+ return {
367
+ ...input.exportConfig,
368
+ context: {
369
+ evalId: input.evalItem.id,
370
+ sourcePath: displaySourcePath(input.evalItem.filePath, input.projectDir),
371
+ reportPath,
372
+ ...(tags.length > 0 ? { tags } : {}),
373
+ ...(Object.keys(metadata).length > 0 ? { metadata } : {}),
374
+ ...input.exportConfig.context,
375
+ },
376
+ };
377
+ }
378
+ function createModelComparisonOutputHints(paths, input, reports, comparison) {
379
+ return {
380
+ reportDirectory: paths.directory,
381
+ reportMarkdown: paths.comparisonMarkdown,
382
+ ...(input.report ? { report: input.report } : {}),
383
+ models: reports.map((report) => ({
384
+ model: report.metadata?.model ?? report.runId,
385
+ report,
386
+ })),
387
+ comparisonJson: paths.comparisonJson,
388
+ comparisonMarkdown: paths.comparisonMarkdown,
389
+ recommendation: comparison.recommendation,
390
+ };
391
+ }
392
+ function createEvalModelComparisonExitCode(reports, exportIsRequired = false) {
393
+ return reports.some((report) => createEvalExitCode(report, undefined, exportIsRequired) !== 0)
394
+ ? 1
395
+ : 0;
396
+ }
397
+ async function writeEvalModelComparisonArtifacts(comparison, paths, artifacts) {
398
+ await artifacts.writeTextFileEnsuringDir(paths.comparisonJson, JSON.stringify(comparison, null, 2));
399
+ await artifacts.writeTextFileEnsuringDir(paths.comparisonMarkdown, createEvalModelComparisonMarkdown(comparison));
400
+ }
401
+ function createEvalSuiteSummary(runId, startedAt, endedAt, results) {
402
+ const passed = results.filter((result) => result.status === "passed").length;
403
+ return {
404
+ kind: "eval-suite-summary",
405
+ runId,
406
+ startedAt: startedAt.toISOString(),
407
+ endedAt: endedAt.toISOString(),
408
+ total: results.length,
409
+ passed,
410
+ failed: results.length - passed,
411
+ results,
412
+ };
413
+ }
414
+ function errorMessage(error) {
415
+ return error instanceof Error ? error.message : String(error);
416
+ }
417
+ async function runEvalReportSuite(input, adapters) {
418
+ const startedAt = adapters.clock?.now?.() ?? new Date();
419
+ const runId = createEvalRunId(startedAt, adapters.clock?.createSuffix);
420
+ const artifacts = createEvalSuiteArtifactPaths(input.reportDir ?? createDefaultEvalReportDir(runId));
421
+ const metadata = createMetadata(input);
422
+ const results = [];
423
+ const children = [];
424
+ for (const [index, evalItem] of sortEvals(input.evalItems).entries()) {
425
+ const childRunId = `${runId}_${String(index + 1).padStart(3, "0")}`;
426
+ const childArtifacts = createEvalArtifactPaths(createEvalSuiteChildDirectory(artifacts.directory, index, evalItem.id));
427
+ try {
428
+ if (!adapters.targets.resolveTarget) {
429
+ throw new Error("Suite eval target resolution is not configured.");
430
+ }
431
+ const target = await adapters.targets.resolveTarget(evalItem);
432
+ const finalizedReport = await adapters.billing.runWithGatewayBillingGroup(childRunId, () => adapters.targets.runEval(evalItem, {
433
+ baseDir: input.datasetBase ?? input.projectDir,
434
+ runId: childRunId,
435
+ frameworkVersion: input.frameworkVersion,
436
+ targetKind: target.targetKind,
437
+ target: target.target,
438
+ targetAdapter: target.targetAdapter,
439
+ metadata,
440
+ }));
441
+ const exportConfig = createEvalReportExportConfig({
442
+ projectDir: input.projectDir,
443
+ evalItem,
444
+ exportConfig: input.exportConfig,
445
+ }, childArtifacts);
446
+ const report = await adapters.exporters.exportReport(finalizedReport, exportConfig);
447
+ await writeEvalArtifacts(report, childArtifacts, adapters.artifacts);
448
+ const summary = summarizeReportForCli(report);
449
+ const status = createEvalExitCode(report, undefined, exportConfig?.required) === 0
450
+ ? "passed"
451
+ : "failed";
452
+ results.push({
453
+ id: evalItem.id,
454
+ name: evalItem.name,
455
+ target: evalItem.definition.target,
456
+ status,
457
+ artifacts: childArtifacts,
458
+ summary,
459
+ });
460
+ children.push({
461
+ kind: "report",
462
+ evalId: evalItem.id,
463
+ reportDirectory: childArtifacts.directory,
464
+ report,
465
+ });
466
+ }
467
+ catch (error) {
468
+ const message = errorMessage(error);
469
+ results.push({
470
+ id: evalItem.id,
471
+ name: evalItem.name,
472
+ target: evalItem.definition.target,
473
+ status: "error",
474
+ artifacts: childArtifacts,
475
+ error: message,
476
+ });
477
+ children.push({
478
+ kind: "error",
479
+ evalId: evalItem.id,
480
+ error: message,
481
+ });
482
+ }
483
+ }
484
+ const endedAt = adapters.clock?.now?.() ?? new Date();
485
+ const suite = createEvalSuiteSummary(runId, startedAt, endedAt, results);
486
+ await writeEvalSuiteArtifacts(suite, artifacts, adapters.artifacts);
487
+ if (input.junit) {
488
+ await adapters.artifacts.writeTextFileEnsuringDir(input.junit, createEvalSuiteJunitXml(suite));
489
+ }
490
+ return {
491
+ kind: "suite",
492
+ suite,
493
+ artifacts,
494
+ exitCode: suite.failed === 0 ? 0 : 1,
495
+ outputHints: createSuiteOutputHints(artifacts, input, children),
496
+ };
497
+ }
498
+ async function runEvalReportModelComparison(input, adapters) {
499
+ const runId = createRunId(adapters.clock);
500
+ const reportDir = input.reportDir ?? createDefaultEvalReportDir(runId, input.evalItem.id);
501
+ const baselineModel = input.baselineModel.trim();
502
+ const models = uniqueTrimmedValues([baselineModel, ...input.candidateModels]);
503
+ const paths = createEvalModelComparisonArtifactPaths(reportDir, models);
504
+ const reports = [];
505
+ for (const model of models) {
506
+ if (!adapters.targets.createModelTargetAdapter) {
507
+ throw new Error("Model comparison target adapter creation is not configured.");
508
+ }
509
+ const modelPaths = paths.models[model];
510
+ const modelRunId = `${runId}_${sanitizeModelIdForPath(model)}`;
511
+ const targetAdapter = await adapters.targets.createModelTargetAdapter(model);
512
+ const metadata = createMetadata(input, model);
513
+ const finalizedReport = await adapters.billing.runWithGatewayBillingGroup(modelRunId, () => adapters.targets.runEval(input.evalItem, {
514
+ baseDir: input.datasetBase ?? input.projectDir,
515
+ runId: modelRunId,
516
+ frameworkVersion: input.frameworkVersion,
517
+ targetKind: "agent",
518
+ target: input.target,
519
+ targetAdapter,
520
+ selectedModel: model,
521
+ ...(input.maxOutputTokens !== undefined
522
+ ? { maxOutputTokens: input.maxOutputTokens }
523
+ : {}),
524
+ metadata,
525
+ }));
526
+ const exportConfig = createEvalReportExportConfig({
527
+ projectDir: input.projectDir,
528
+ evalItem: input.evalItem,
529
+ exportConfig: input.exportConfig,
530
+ }, modelPaths);
531
+ const report = await adapters.exporters.exportReport(finalizedReport, exportConfig);
532
+ reports.push(report);
533
+ await writeEvalArtifacts(report, modelPaths, adapters.artifacts);
534
+ await adapters.artifacts.writeTextFileEnsuringDir(modelPaths.junit, createJunitXml(report));
535
+ }
536
+ const comparison = compareEvalModelReports(reports, {
537
+ ...input.comparisonPolicy,
538
+ baselineModel,
539
+ });
540
+ await writeEvalModelComparisonArtifacts(comparison, paths, adapters.artifacts);
541
+ if (input.report) {
542
+ await adapters.artifacts.writeTextFileEnsuringDir(input.report, JSON.stringify(comparison, null, 2));
543
+ }
544
+ return {
545
+ kind: "model-comparison",
546
+ reports,
547
+ comparison,
548
+ artifacts: paths,
549
+ exitCode: createEvalModelComparisonExitCode(reports, input.exportConfig?.required),
550
+ outputHints: createModelComparisonOutputHints(paths, input, reports, comparison),
551
+ };
552
+ }
553
+ export async function runEvalReport(input, adapters) {
554
+ switch (input.kind) {
555
+ case "suite":
556
+ return await runEvalReportSuite(input, adapters);
557
+ case "model-comparison":
558
+ return await runEvalReportModelComparison(input, adapters);
559
+ case "single":
560
+ break;
561
+ default:
562
+ throw new Error(`Unsupported eval report kind: ${input.kind}`);
563
+ }
564
+ const runId = createRunId(adapters.clock);
565
+ const artifactPaths = createEvalArtifactPaths(input.reportDir ?? createDefaultEvalReportDir(runId, input.evalItem.id));
566
+ const billingGroupId = input.selectedModel
567
+ ? `${runId}_${sanitizeModelIdForPath(input.selectedModel)}`
568
+ : runId;
569
+ const metadata = createMetadata(input);
570
+ const finalizedReport = await adapters.billing.runWithGatewayBillingGroup(billingGroupId, () => adapters.targets.runEval(input.evalItem, {
571
+ baseDir: input.datasetBase ?? input.projectDir,
572
+ runId,
573
+ frameworkVersion: input.frameworkVersion,
574
+ targetKind: input.targetKind,
575
+ target: input.target,
576
+ targetAdapter: input.targetAdapter,
577
+ ...(input.selectedModel ? { selectedModel: input.selectedModel } : {}),
578
+ ...(input.maxOutputTokens !== undefined ? { maxOutputTokens: input.maxOutputTokens } : {}),
579
+ metadata,
580
+ }));
581
+ const exportConfig = createEvalReportExportConfig(input, artifactPaths);
582
+ const report = await adapters.exporters.exportReport(finalizedReport, exportConfig);
583
+ const baseline = await readBaselineComparison(report, input, adapters.artifacts);
584
+ await writeEvalArtifacts(report, artifactPaths, adapters.artifacts, baseline);
585
+ if (input.report) {
586
+ await adapters.artifacts.writeTextFileEnsuringDir(input.report, JSON.stringify(report, null, 2));
587
+ }
588
+ if (input.junit) {
589
+ await adapters.artifacts.writeTextFileEnsuringDir(input.junit, createJunitXml(report));
590
+ }
591
+ if (input.writeBaseline) {
592
+ await adapters.artifacts.writeTextFileEnsuringDir(input.writeBaseline, JSON.stringify(report, null, 2));
593
+ }
594
+ return {
595
+ kind: "single",
596
+ report,
597
+ summary: summarizeReportForCli(report),
598
+ ...(baseline ? { baseline } : {}),
599
+ artifacts: artifactPaths,
600
+ exitCode: createEvalExitCode(report, baseline, exportConfig?.required),
601
+ outputHints: createOutputHints(artifactPaths, input),
602
+ };
603
+ }
@@ -6,7 +6,7 @@ export function generateDevClientRendererScript(nonce) {
6
6
  <script type="module"${nonceAttr}>
7
7
  import * as React from 'react';
8
8
  import { createRoot } from 'react-dom/client';
9
- import { RouterProvider, useRouter as useRouterFromModule } from 'veryfront/router';
9
+ import { RouterProvider, useRouter as useRouterFromModule, getNavigationStore } from 'veryfront/router';
10
10
  import { PageContextProvider } from 'veryfront/context';
11
11
 
12
12
  ${getRouterScript()}
@@ -8,7 +8,7 @@ export function generateProdHydrationModule() {
8
8
  return [
9
9
  `import * as React from 'react';`,
10
10
  `import { createRoot } from 'react-dom/client';`,
11
- `import { RouterProvider, useRouter as useRouterFromModule } from 'veryfront/router';`,
11
+ `import { RouterProvider, useRouter as useRouterFromModule, getNavigationStore } from 'veryfront/router';`,
12
12
  `import { PageContextProvider } from 'veryfront/context';`,
13
13
  getRouterScript().trim(),
14
14
  getLoaderScript().trim(),
@@ -1 +1 @@
1
- {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../../../../src/src/html/hydration-script-builder/templates/router.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,eAAe,cAstC3B,CAAC"}
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../../../../../src/src/html/hydration-script-builder/templates/router.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,eAAe,cA8uC3B,CAAC"}