@dailephd/my-dev-kit-lab 0.9.2 → 0.9.4

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 (44) hide show
  1. package/CHANGELOG.md +17 -1
  2. package/README.md +9 -5
  3. package/dist/src/agents/adapters/codexAdapter.js +4 -1
  4. package/dist/src/cli/help.js +38 -2
  5. package/dist/src/cli/runLabCli.js +9 -1
  6. package/dist/src/commands/buildGalleryCommand.js +16 -3
  7. package/dist/src/commands/runComparePromptsCommand.js +109 -0
  8. package/dist/src/commands/runExperimentRunCommand.js +47 -2
  9. package/dist/src/core/processTree.js +41 -17
  10. package/dist/src/core/runMeasuredCommand.js +14 -8
  11. package/dist/src/evaluation/affectedNeighborhood.js +140 -32
  12. package/dist/src/experiments/plugins/agentSuccessRate/campaignCheckpointStore.js +23 -1
  13. package/dist/src/experiments/plugins/agentSuccessRate/campaignExecution.js +7 -2
  14. package/dist/src/experiments/plugins/agentSuccessRate/campaignIdentity.js +29 -4
  15. package/dist/src/experiments/plugins/agentSuccessRate/campaignResumableExecution.js +1 -1
  16. package/dist/src/experiments/plugins/agentSuccessRate/config.js +47 -2
  17. package/dist/src/experiments/plugins/agentSuccessRate/plugin.js +4 -2
  18. package/dist/src/experiments/plugins/agentSuccessRate/promptContract.js +121 -0
  19. package/dist/src/experiments/plugins/agentSuccessRate/realAgentExecution.js +32 -2
  20. package/dist/src/experiments/plugins/agentSuccessRate/realAgentPrompt.js +105 -11
  21. package/dist/src/experiments/plugins/agentSuccessRate/strictDiffResponse.js +312 -0
  22. package/dist/src/gallery/galleryPathSafety.js +111 -0
  23. package/dist/src/gallery/readGallerySources.js +542 -0
  24. package/dist/src/gallery/writeGalleryManifest.js +57 -3
  25. package/dist/src/report/experiments/buildPluginExperimentReport.js +4 -16
  26. package/dist/src/report/experiments/htmlSafety.js +32 -0
  27. package/dist/src/report/experiments/metricEvidence.js +389 -0
  28. package/dist/src/report/experiments/metricGlossary.js +227 -0
  29. package/dist/src/report/experiments/metricGlossaryCatalog.js +199 -0
  30. package/dist/src/report/experiments/promptTemplateComparisonEvidence.js +377 -0
  31. package/dist/src/report/experiments/promptTemplateComparisonModel.js +436 -0
  32. package/dist/src/report/experiments/renderPluginExperimentReportHtml.js +158 -64
  33. package/dist/src/report/experiments/renderPluginExperimentReportText.js +22 -677
  34. package/dist/src/report/experiments/renderPromptTemplateComparison.js +192 -0
  35. package/dist/src/report/experiments/renderSpecializedTextSections.js +633 -0
  36. package/dist/src/report/experiments/reportSectionRegistry.js +79 -0
  37. package/dist/src/report/experiments/specializedReportSections.js +142 -0
  38. package/dist/src/report/experiments/writePluginExperimentReports.js +4 -2
  39. package/dist/src/securityValidation/attackScenarios/scenarios/secretLeakageScenario.js +146 -133
  40. package/dist/src/securityValidation/attackScenarios/scenarios/subprocessInjectionScenario.js +54 -36
  41. package/dist/src/securityValidation/commandRunner.js +13 -6
  42. package/dist/src/securityValidation/packageChecks/parseNpmPackDryRun.js +46 -21
  43. package/docs/METRICS.md +11 -1
  44. package/package.json +3 -3
@@ -1,6 +1,8 @@
1
1
  import { existsSync } from "node:fs";
2
- import { mkdir, readdir, writeFile } from "node:fs/promises";
2
+ import { mkdir, readdir, realpath, writeFile } from "node:fs/promises";
3
3
  import path from "node:path";
4
+ import { GALLERY_MEDIA_EXTENSIONS, safeGalleryHref } from "./galleryPathSafety.js";
5
+ import { readGallerySources } from "./readGallerySources.js";
4
6
  function toRelativePath(outDir, targetPath) {
5
7
  if (!targetPath) {
6
8
  return undefined;
@@ -154,6 +156,10 @@ export async function writeExperimentGalleryManifest(options) {
154
156
  warnings: []
155
157
  });
156
158
  }
159
+ if (options.sources && options.sources.length > 0) {
160
+ const sourceResult = await readGallerySources({ sources: options.sources, realOutDir: await realpath(outDir) });
161
+ items.push(...sourceResult.items);
162
+ }
157
163
  items.push({
158
164
  id: "final-demo",
159
165
  title: "Final demo artifact index",
@@ -203,11 +209,59 @@ async function listFiles(dir) {
203
209
  export function renderGalleryIndex(manifest) {
204
210
  return `<!DOCTYPE html>
205
211
  <html lang="en">
206
- <head><meta charset="utf-8" /><title>${escapeHtml(manifest.title)}</title><style>body{font-family:Arial,Helvetica,sans-serif;margin:32px;color:#17212b}section{border:1px solid #d9e1ea;border-radius:8px;padding:16px;margin:12px 0}code{background:#f2f4f7;padding:2px 4px}</style></head>
212
+ <head><meta charset="utf-8" /><title>${escapeHtml(manifest.title)}</title><style>body{font-family:Arial,Helvetica,sans-serif;margin:32px;color:#17212b}section{border:1px solid #d9e1ea;border-radius:8px;padding:16px;margin:12px 0}code{background:#f2f4f7;padding:2px 4px}video,img{max-width:100%;height:auto}a:focus-visible{outline:3px solid #1a5fb4}.unavailable{color:#8a1f1f}</style></head>
207
213
  <body><h1>${escapeHtml(manifest.title)}</h1><p>${escapeHtml(manifest.description)}</p>${manifest.items
208
- .map((item) => `<section><h2>${escapeHtml(item.title)}</h2><p>${escapeHtml(item.description)}</p><p>Status: <strong>${escapeHtml(item.status)}</strong></p><p>Kind: ${escapeHtml(item.kind)}</p>${item.htmlPath ? `<p>HTML: <code>${escapeHtml(item.htmlPath)}</code></p>` : ""}${item.summaryPath ? `<p>Summary: <code>${escapeHtml(item.summaryPath)}</code></p>` : ""}${item.screenshotPath ? `<p>Screenshot: <code>${escapeHtml(item.screenshotPath)}</code></p>` : ""}${item.artifactPaths?.length ? `<ul>${item.artifactPaths.map((artifact) => `<li><code>${escapeHtml(artifact)}</code></li>`).join("")}</ul>` : ""}</section>`)
214
+ .map((item) => `<section><h2>${escapeHtml(item.title)}</h2><p>${escapeHtml(item.description)}</p><p>Status: <strong>${escapeHtml(item.status)}</strong></p><p>Kind: ${escapeHtml(item.kind)}</p>${item.htmlPath ? `<p>HTML: <code>${escapeHtml(item.htmlPath)}</code></p>` : ""}${item.summaryPath ? `<p>Summary: <code>${escapeHtml(item.summaryPath)}</code></p>` : ""}${item.screenshotPath ? `<p>Screenshot: <code>${escapeHtml(item.screenshotPath)}</code></p>` : ""}${item.artifactPaths?.length ? `<ul>${item.artifactPaths.map((artifact) => `<li><code>${escapeHtml(artifact)}</code></li>`).join("")}</ul>` : ""}${renderSourceItemExtras(item)}</section>`)
209
215
  .join("")}</body></html>`;
210
216
  }
217
+ /**
218
+ * Additive presentation for items produced by `gallery build --source`. Every href/src is validated by
219
+ * safeGalleryHref independently of text escaping; a value that fails validation is shown as unavailable text,
220
+ * never as a link or media element.
221
+ */
222
+ function renderSourceItemExtras(item) {
223
+ if (!item.sourceFamily)
224
+ return "";
225
+ const parts = [];
226
+ parts.push(`<p>Source family: <code>${escapeHtml(item.sourceFamily)}</code>${item.sourceId ? ` · Identity: <code>${escapeHtml(item.sourceId)}</code>` : ""}</p>`);
227
+ const metricLine = item.metrics
228
+ .map((metric) => `${escapeHtml(metric.label)}: <strong>${escapeHtml(metric.value)}</strong>${metric.unit ? ` ${escapeHtml(metric.unit)}` : ""}`)
229
+ .join(" · ");
230
+ if (metricLine)
231
+ parts.push(`<p>${metricLine}</p>`);
232
+ const link = (value, label, extensions) => {
233
+ if (!value)
234
+ return "";
235
+ const href = safeGalleryHref(value, extensions);
236
+ return href === null
237
+ ? `<li class="unavailable">${escapeHtml(label)} unavailable: unsafe or unsupported path</li>`
238
+ : `<li><a href="${escapeHtml(href)}">${escapeHtml(label)}</a></li>`;
239
+ };
240
+ const links = [
241
+ link(item.htmlPath || undefined, "Open HTML report", [".html"]),
242
+ link(item.summaryPath, "Open summary JSON", [".json"]),
243
+ link(item.markdownPath, "Open generated Markdown", GALLERY_MEDIA_EXTENSIONS.markdown),
244
+ ...(item.artifactPaths ?? []).map((artifact) => link(artifact, `Artifact ${artifact}`, [...GALLERY_MEDIA_EXTENSIONS.text, ...GALLERY_MEDIA_EXTENSIONS.json]))
245
+ ].join("");
246
+ if (links)
247
+ parts.push(`<ul>${links}</ul>`);
248
+ if (item.videoPath) {
249
+ const src = safeGalleryHref(item.videoPath, GALLERY_MEDIA_EXTENSIONS.video);
250
+ parts.push(src === null
251
+ ? `<p class="unavailable">Video unavailable: unsafe or unsupported path</p>`
252
+ : `<video controls preload="metadata" src="${escapeHtml(src)}">Your browser does not support embedded video. <a href="${escapeHtml(src)}">Open the WebM video</a>.</video>`);
253
+ }
254
+ for (const [index, screenshot] of (item.screenshotPaths ?? []).entries()) {
255
+ const src = safeGalleryHref(screenshot, GALLERY_MEDIA_EXTENSIONS.screenshot);
256
+ parts.push(src === null
257
+ ? `<p class="unavailable">Screenshot ${index + 1} unavailable: unsafe or unsupported path</p>`
258
+ : `<figure><img src="${escapeHtml(src)}" alt="${escapeHtml(item.title)} screenshot ${index + 1}" loading="lazy" /></figure>`);
259
+ }
260
+ if (item.warnings.length > 0) {
261
+ parts.push(`<div><strong>Warnings</strong><ul>${item.warnings.map((warning) => `<li>${escapeHtml(warning)}</li>`).join("")}</ul></div>`);
262
+ }
263
+ return parts.join("");
264
+ }
211
265
  function escapeHtml(value) {
212
266
  return String(value ?? "")
213
267
  .replaceAll("&", "&amp;")
@@ -1,12 +1,6 @@
1
1
  import path from "node:path";
2
- import { buildContextStrategyComparisonV043Report } from "./buildContextStrategyComparisonV043Report.js";
3
- import { buildWarmIndexReuseReport } from "./buildWarmIndexReuseReport.js";
4
- import { buildIncrementalChangeStalenessPluginReport } from "./buildIncrementalChangeStalenessPluginReport.js";
5
- import { buildContextWindowScalingReport } from "./buildContextWindowScalingReport.js";
6
- import { buildRetrievalPrecisionRecallReport } from "./buildRetrievalPrecisionRecallReport.js";
7
- import { buildRetrievalQueryStrategyComparisonReport } from "./buildRetrievalQueryStrategyComparisonReport.js";
8
- import { buildContextPackGenerationReport } from "./buildContextPackGenerationReport.js";
9
- import { AGENT_SUCCESS_RATE_DETERMINISTIC_STATEMENT, AGENT_SUCCESS_RATE_REAL_AGENT_STATEMENT, buildAgentSuccessRateReport } from "./buildAgentSuccessRateReport.js";
2
+ import { AGENT_SUCCESS_RATE_DETERMINISTIC_STATEMENT, AGENT_SUCCESS_RATE_REAL_AGENT_STATEMENT } from "./buildAgentSuccessRateReport.js";
3
+ import { buildSpecializedReportProjections } from "./specializedReportSections.js";
10
4
  // Bulk context-window-scaling evidence is presented by the typed report section; the complete
11
5
  // evidence stays in context-window-scaling-execution.json.
12
6
  const CONTEXT_WINDOW_SCALING_BULK_KEYS = ["executionEvidence", "aggregate"];
@@ -26,14 +20,8 @@ const V043_BULK_ARRAY_KEYS = [
26
20
  export function buildPluginExperimentReport(args) {
27
21
  const outputRoot = args.outputRoot ?? readString(args.run.metadata?.outputRoot) ?? null;
28
22
  const allOutcomes = args.run.cases.flatMap((experimentCase) => experimentCase.outcomes);
29
- const contextStrategyComparisonV043 = buildContextStrategyComparisonV043Report(args.run);
30
- const warmIndexReuse = buildWarmIndexReuseReport(args.run);
31
- const incrementalChangeStaleness = buildIncrementalChangeStalenessPluginReport(args.run);
32
- const contextWindowScaling = buildContextWindowScalingReport(args.run);
33
- const retrievalPrecisionRecall = buildRetrievalPrecisionRecallReport(args.run);
34
- const retrievalQueryStrategyComparison = buildRetrievalQueryStrategyComparisonReport(args.run);
35
- const contextPackGeneration = buildContextPackGenerationReport(args.run, args.contextPacks);
36
- const agentSuccessRate = buildAgentSuccessRateReport(args.run);
23
+ // One shared registry owns specialized-family projection selection; each family builder runs once.
24
+ const { contextStrategyComparisonV043, warmIndexReuse, incrementalChangeStaleness, contextWindowScaling, retrievalPrecisionRecall, retrievalQueryStrategyComparison, contextPackGeneration, agentSuccessRate, } = buildSpecializedReportProjections({ run: args.run, contextPacks: args.contextPacks });
37
25
  const rawRun = { ...args.run, artifacts: relativizeArtifacts(args.run.artifacts, outputRoot) };
38
26
  for (const key of V043_BULK_ARRAY_KEYS) {
39
27
  delete rawRun[key];
@@ -0,0 +1,32 @@
1
+ // Context-correct HTML helpers for the generic static report. Text escaping and link validation are separate
2
+ // concerns: escaping alone never makes a URL safe, so every emitted href is validated here independently.
3
+ export function escapeHtml(value) {
4
+ return String(value ?? "")
5
+ .replaceAll("&", "&amp;")
6
+ .replaceAll("<", "&lt;")
7
+ .replaceAll(">", "&gt;")
8
+ .replaceAll('"', "&quot;")
9
+ .replaceAll("'", "&#39;");
10
+ }
11
+ const SAFE_ANCHOR_ID = /^[a-z][a-z0-9-]{0,127}$/;
12
+ /** True only for a plain lowercase in-document anchor id that this renderer generated. */
13
+ export function isSafeAnchorId(value) {
14
+ return typeof value === "string" && SAFE_ANCHOR_ID.test(value);
15
+ }
16
+ /**
17
+ * Returns an href that is safe to emit, or null. Only in-document fragment links to a safe anchor id are
18
+ * permitted: no scheme-bearing URL (javascript:, data:, file:, http:, ...) and no filesystem path is ever linked.
19
+ */
20
+ export function safeInternalHref(value) {
21
+ if (typeof value !== "string" || !value.startsWith("#"))
22
+ return null;
23
+ return isSafeAnchorId(value.slice(1)) ? value : null;
24
+ }
25
+ /** Derives a safe, lowercase section anchor id from a registry/descriptor identifier. */
26
+ export function sectionAnchorId(identifier) {
27
+ const slug = identifier
28
+ .toLowerCase()
29
+ .replace(/[^a-z0-9]+/g, "-")
30
+ .replace(/^-+|-+$/g, "");
31
+ return `section-${slug || "unnamed"}`;
32
+ }
@@ -0,0 +1,389 @@
1
+ // Report-presentation projection of metric evidence. It reads an already built PluginExperimentReport and
2
+ // produces display views and deterministic caveats. It never recalculates a scientific value, never converts
3
+ // unavailable or not-applicable evidence to zero, and never mutates its input. Provenance and reliability are
4
+ // taken only from evidence the report carries; anything else stays explicitly `unknown`.
5
+ import { RAW_FULL_FILE_VARIANT_ID, WARM_INDEX_REUSE_VARIANT_ID } from "../../experiments/plugins/warmIndexReuse/plugin.js";
6
+ import { attemptEvidenceOf } from "../../experiments/plugins/agentSuccessRate/attemptEvidence.js";
7
+ import { pluginLocalMetricId, resolveMetricDescriptor, } from "./metricGlossary.js";
8
+ const KIND_ORDER = [
9
+ "metric-unavailable",
10
+ "metric-not-applicable",
11
+ "telemetry-partial",
12
+ "telemetry-provenance-unknown",
13
+ "telemetry-reliability-low",
14
+ "telemetry-reliability-unknown",
15
+ "estimated-not-provider-usage",
16
+ "glossary-missing",
17
+ ];
18
+ /** Collects per-attempt telemetry already recorded in agent-success execution evidence. Pure; no I/O. */
19
+ export function collectMetricReportEvidence(run) {
20
+ // Only the agent-success-rate plugin records per-attempt token telemetry in this shape.
21
+ if (run.pluginId !== "agent-success-rate")
22
+ return {};
23
+ const cases = run.caseExecutionEvidence;
24
+ if (!Array.isArray(cases))
25
+ return {};
26
+ const attempts = [];
27
+ for (const entry of cases) {
28
+ if (!entry || !Array.isArray(entry.treatments))
29
+ continue;
30
+ for (const treatment of entry.treatments) {
31
+ attemptEvidenceOf(treatment).forEach((evidence, index) => {
32
+ const usage = evidence.agentTokenUsage;
33
+ attempts.push({
34
+ caseId: entry.caseId,
35
+ treatmentId: treatment.treatmentId,
36
+ attemptNumber: index + 1,
37
+ providerInvoked: evidence.realAgent?.providerInvoked === true,
38
+ tokenSource: usage ? usage.source : null,
39
+ tokenReliability: usage ? usage.reliability : null,
40
+ totalTokensAvailable: usage !== null && typeof usage.totalTokens === "number"
41
+ });
42
+ });
43
+ }
44
+ }
45
+ return { agentSuccessAttempts: attempts };
46
+ }
47
+ const MAX_LISTED = 10;
48
+ const MAX_REASONS = 3;
49
+ const AGENT_SUCCESS_PREFIX = "agent-success-rate.";
50
+ const DESCRIPTION_PATTERN = /^(available|unavailable|not-applicable)(?::\s*([\s\S]*))?$/;
51
+ function provenanceOfTokenSource(source) {
52
+ switch (source) {
53
+ case "provider-reported":
54
+ return "provider-reported";
55
+ case "agent-reported":
56
+ case "cli-json":
57
+ return "agent-reported";
58
+ case "estimated-from-text":
59
+ return "estimated";
60
+ default:
61
+ // "otel" is unattributed telemetry and "unavailable" carries no source: neither is assumed.
62
+ return "unknown";
63
+ }
64
+ }
65
+ function reliabilityOf(value) {
66
+ return value === "high" || value === "medium" || value === "low" || value === "unavailable" ? value : "unknown";
67
+ }
68
+ function basisProvenance(descriptor) {
69
+ switch (descriptor?.measurementBasis) {
70
+ case "measured":
71
+ return "measured";
72
+ case "estimated":
73
+ return "estimated";
74
+ case "derived":
75
+ case "verification-verdict":
76
+ case "answer-key-set-measure":
77
+ return "derived";
78
+ default:
79
+ return "unknown";
80
+ }
81
+ }
82
+ function isTokenTelemetry(descriptor) {
83
+ return descriptor?.measurementBasis === "agent-token-telemetry";
84
+ }
85
+ function telemetryKey(variantId, caseId) {
86
+ return `${variantId ?? ""}\u0000${caseId ?? ""}`;
87
+ }
88
+ /** Per variant/case token telemetry that the warm-index report already carries. */
89
+ function warmIndexTelemetry(report) {
90
+ const result = new Map();
91
+ for (const project of report.warmIndexReuse?.projects ?? []) {
92
+ for (const task of project.tasks) {
93
+ for (const [variantId, agent] of [
94
+ [RAW_FULL_FILE_VARIANT_ID, task.rawAgent],
95
+ [WARM_INDEX_REUSE_VARIANT_ID, task.warmAgent],
96
+ ]) {
97
+ if (!agent)
98
+ continue;
99
+ result.set(telemetryKey(variantId, task.caseId), {
100
+ provenance: provenanceOfTokenSource(agent.tokenUsageSource),
101
+ reliability: reliabilityOf(agent.tokenUsageReliability),
102
+ });
103
+ }
104
+ }
105
+ }
106
+ return result;
107
+ }
108
+ function commonValue(values, unknownValue) {
109
+ if (values.length === 0)
110
+ return unknownValue;
111
+ const first = values[0];
112
+ return values.every((value) => value === first) ? first : unknownValue;
113
+ }
114
+ /**
115
+ * Aggregate telemetry across contributing measurements. Provenance and reliability are attributed only when every
116
+ * contributor is known and identical; any missing or differing contributor leaves the aggregate unknown.
117
+ */
118
+ function aggregateTelemetry(parts) {
119
+ if (parts.length === 0 || parts.some((part) => part === null))
120
+ return { provenance: "unknown", reliability: "unknown" };
121
+ const known = parts;
122
+ const provenance = commonValue(known.map((part) => part.provenance), "unknown");
123
+ const reliability = commonValue(known.map((part) => part.reliability), "unknown");
124
+ return { provenance, reliability };
125
+ }
126
+ /** Cumulative warm-index token telemetry per variant/case: the strict prefix of tasks 1..N in the project. */
127
+ function warmIndexCumulativeTelemetry(report) {
128
+ const result = new Map();
129
+ for (const project of report.warmIndexReuse?.projects ?? []) {
130
+ const tasks = [...project.tasks].sort((a, b) => a.taskOrdinal - b.taskOrdinal);
131
+ for (const [variantId, pick] of [
132
+ [RAW_FULL_FILE_VARIANT_ID, (task) => task.rawAgent],
133
+ [WARM_INDEX_REUSE_VARIANT_ID, (task) => task.warmAgent]
134
+ ]) {
135
+ const parts = [];
136
+ for (const task of tasks) {
137
+ const agent = pick(task);
138
+ parts.push(agent ? { provenance: provenanceOfTokenSource(agent.tokenUsageSource), reliability: reliabilityOf(agent.tokenUsageReliability) } : null);
139
+ result.set(telemetryKey(variantId, task.caseId), aggregateTelemetry(parts));
140
+ }
141
+ }
142
+ }
143
+ return result;
144
+ }
145
+ function agentSuccessTelemetry(localId, caseId, variantId, evidence) {
146
+ if (!evidence?.agentSuccessAttempts || caseId === null || variantId === null)
147
+ return null;
148
+ const attempts = evidence.agentSuccessAttempts
149
+ .filter((attempt) => attempt.caseId === caseId && attempt.treatmentId === variantId)
150
+ .sort((a, b) => a.attemptNumber - b.attemptNumber);
151
+ if (attempts.length === 0)
152
+ return null;
153
+ const one = (attempt) => attempt && attempt.providerInvoked && attempt.totalTokensAvailable && attempt.tokenSource !== null
154
+ ? { provenance: provenanceOfTokenSource(attempt.tokenSource), reliability: reliabilityOf(attempt.tokenReliability) }
155
+ : null;
156
+ switch (localId) {
157
+ case "firstAttemptProviderTokens":
158
+ return one(attempts[0]);
159
+ case "agentTotalTokens":
160
+ case "finalAttemptProviderTokens":
161
+ return one(attempts[attempts.length - 1]);
162
+ case "totalProviderTokens":
163
+ // Cumulative over the attempts that actually ran provider processes.
164
+ return aggregateTelemetry(attempts.filter((attempt) => attempt.providerInvoked).map(one));
165
+ default:
166
+ return null;
167
+ }
168
+ }
169
+ function warmUnavailableAgentTokenViews(report, telemetry) {
170
+ const pluginId = report.plugin.id;
171
+ const descriptor = resolveMetricDescriptor(pluginId, "agent-total-tokens");
172
+ const views = [];
173
+ for (const project of report.warmIndexReuse?.projects ?? []) {
174
+ for (const task of project.tasks) {
175
+ for (const [variantId, metric] of [
176
+ [RAW_FULL_FILE_VARIANT_ID, task.raw?.agentTotalTokens],
177
+ [WARM_INDEX_REUSE_VARIANT_ID, task.warm?.agentTotalTokens],
178
+ ]) {
179
+ const measurement = metric;
180
+ if (!measurement || measurement.availability === "available")
181
+ continue;
182
+ const known = telemetry.get(telemetryKey(variantId, task.caseId));
183
+ views.push({
184
+ metricId: "agent-total-tokens",
185
+ pluginId,
186
+ displayName: descriptor?.displayName ?? "Agent total tokens",
187
+ value: null,
188
+ unit: measurement.unit ?? null,
189
+ availability: measurement.availability,
190
+ reason: measurement.reason,
191
+ provenance: known?.provenance ?? "unknown",
192
+ reliability: known?.reliability ?? "unknown",
193
+ variantId,
194
+ caseId: task.caseId,
195
+ glossaryAnchor: descriptor?.anchor ?? null,
196
+ });
197
+ }
198
+ }
199
+ }
200
+ return views;
201
+ }
202
+ /** Builds one evidence view per rendered metric, preserving report order. Pure; never mutates the report. */
203
+ export function buildMetricEvidenceViews(report, evidence) {
204
+ const pluginId = report.plugin.id;
205
+ const telemetry = warmIndexTelemetry(report);
206
+ const cumulative = warmIndexCumulativeTelemetry(report);
207
+ const views = report.metrics.map((metric) => {
208
+ const descriptor = resolveMetricDescriptor(pluginId, metric.id);
209
+ const parsed = metric.id.startsWith(AGENT_SUCCESS_PREFIX) && metric.description ? DESCRIPTION_PATTERN.exec(metric.description) : null;
210
+ let availability = metric.value === null ? "unavailable" : "available";
211
+ let reason = null;
212
+ if (parsed) {
213
+ availability = parsed[1];
214
+ reason = parsed[2] !== undefined && parsed[2].trim() !== "" ? parsed[2].trim() : null;
215
+ if (availability === "available")
216
+ reason = null;
217
+ }
218
+ const localId = pluginLocalMetricId(pluginId, metric.id);
219
+ const key = telemetryKey(metric.variantId ?? null, metric.caseId ?? null);
220
+ const tokenMetric = isTokenTelemetry(descriptor);
221
+ const known = !tokenMetric
222
+ ? null
223
+ : pluginId === "agent-success-rate"
224
+ ? agentSuccessTelemetry(localId, metric.caseId ?? null, metric.variantId ?? null, evidence)
225
+ : localId.startsWith("cumulative-")
226
+ ? cumulative.get(key)
227
+ : telemetry.get(key);
228
+ return {
229
+ metricId: metric.id,
230
+ pluginId,
231
+ displayName: descriptor?.displayName ?? metric.name,
232
+ value: metric.value,
233
+ unit: metric.unit ?? descriptor?.unit ?? null,
234
+ availability,
235
+ reason,
236
+ provenance: tokenMetric ? (known ? known.provenance : "unknown") : basisProvenance(descriptor),
237
+ reliability: tokenMetric && known ? known.reliability : "unknown",
238
+ variantId: metric.variantId ?? null,
239
+ caseId: metric.caseId ?? null,
240
+ glossaryAnchor: descriptor?.anchor ?? null,
241
+ };
242
+ });
243
+ return [...views, ...warmUnavailableAgentTokenViews(report, telemetry)];
244
+ }
245
+ function groupByMetric(views) {
246
+ const groups = new Map();
247
+ for (const view of views) {
248
+ const key = `${view.pluginId}\u0000${view.metricId}`;
249
+ const group = groups.get(key) ?? { pluginId: view.pluginId, metricId: view.metricId, displayName: view.displayName, views: [] };
250
+ group.views.push(view);
251
+ groups.set(key, group);
252
+ }
253
+ return [...groups.values()];
254
+ }
255
+ function boundedList(values, limit) {
256
+ const sorted = values.map((value) => value.replace(/[.s]+$/, "")).sort();
257
+ const shown = sorted.slice(0, limit).join("; ");
258
+ return sorted.length > limit ? `${shown}; and ${sorted.length - limit} more` : shown;
259
+ }
260
+ function label(group) {
261
+ return `"${group.displayName}" (${group.metricId})`;
262
+ }
263
+ /**
264
+ * Deterministic, de-duplicated caveats derived from the views and from evidence the report already carries.
265
+ * Caveats supplement the report's own warnings and never change a scientific conclusion.
266
+ */
267
+ export function buildMetricEvidenceCaveats(report, views) {
268
+ const byId = new Map();
269
+ const add = (caveat) => {
270
+ if (!byId.has(caveat.id))
271
+ byId.set(caveat.id, caveat);
272
+ };
273
+ for (const group of groupByMetric(views)) {
274
+ const total = group.views.length;
275
+ const descriptor = resolveMetricDescriptor(group.pluginId, group.metricId);
276
+ const base = { pluginId: group.pluginId, metricId: group.metricId };
277
+ const unavailable = group.views.filter((view) => view.availability === "unavailable");
278
+ if (unavailable.length > 0) {
279
+ const reasons = [...new Set(unavailable.map((view) => view.reason).filter((reason) => reason !== null))];
280
+ add({
281
+ ...base,
282
+ id: `metric-unavailable:${group.pluginId}:${group.metricId}`,
283
+ kind: "metric-unavailable",
284
+ severity: "warning",
285
+ text: `${label(group)} is unavailable for ${unavailable.length} of ${total} measurement(s); unavailable is not zero.` +
286
+ (reasons.length > 0 ? ` Recorded reason(s): ${boundedList(reasons, MAX_REASONS)}.` : " No reason was recorded."),
287
+ });
288
+ }
289
+ const notApplicable = group.views.filter((view) => view.availability === "not-applicable");
290
+ if (notApplicable.length > 0) {
291
+ const reasons = [...new Set(notApplicable.map((view) => view.reason).filter((reason) => reason !== null))];
292
+ add({
293
+ ...base,
294
+ id: `metric-not-applicable:${group.pluginId}:${group.metricId}`,
295
+ kind: "metric-not-applicable",
296
+ severity: "info",
297
+ text: `${label(group)} is not applicable for ${notApplicable.length} of ${total} measurement(s); this is not a zero or a failure.` +
298
+ (reasons.length > 0 ? ` Recorded reason(s): ${boundedList(reasons, MAX_REASONS)}.` : " No reason was recorded."),
299
+ });
300
+ }
301
+ const estimated = group.views.filter((view) => view.provenance === "estimated" && view.availability === "available");
302
+ if (estimated.length > 0) {
303
+ add({
304
+ ...base,
305
+ id: `estimated-not-provider-usage:${group.pluginId}:${group.metricId}`,
306
+ kind: "estimated-not-provider-usage",
307
+ severity: "info",
308
+ text: `${label(group)} is an estimate; it is not provider-reported usage and must not be compared to provider token totals.`,
309
+ });
310
+ }
311
+ if (isTokenTelemetry(descriptor)) {
312
+ const available = group.views.filter((view) => view.availability === "available");
313
+ if (available.some((view) => view.provenance === "unknown")) {
314
+ add({
315
+ ...base,
316
+ id: `telemetry-provenance-unknown:${group.pluginId}:${group.metricId}`,
317
+ kind: "telemetry-provenance-unknown",
318
+ severity: "warning",
319
+ text: `${label(group)}: the token telemetry source and reliability are not recorded in this report; the value is neither assumed provider-reported nor estimated.`,
320
+ });
321
+ }
322
+ else if (available.some((view) => view.reliability === "unknown")) {
323
+ add({
324
+ ...base,
325
+ id: `telemetry-reliability-unknown:${group.pluginId}:${group.metricId}`,
326
+ kind: "telemetry-reliability-unknown",
327
+ severity: "warning",
328
+ text: `${label(group)}: the token telemetry reliability is not recorded in this report.`,
329
+ });
330
+ }
331
+ const weak = group.views.filter((view) => view.reliability === "low" || view.reliability === "unavailable");
332
+ if (weak.length > 0) {
333
+ const levels = [...new Set(weak.map((view) => view.reliability))].sort().join(", ");
334
+ add({
335
+ ...base,
336
+ id: `telemetry-reliability-low:${group.pluginId}:${group.metricId}`,
337
+ kind: "telemetry-reliability-low",
338
+ severity: "warning",
339
+ text: `${label(group)}: ${weak.length} of ${total} measurement(s) carry ${levels} telemetry reliability; interpret token comparisons cautiously.`,
340
+ });
341
+ }
342
+ }
343
+ }
344
+ const tokenStatus = report.warmIndexReuse?.agentCampaign?.tokenEvidenceStatus;
345
+ if (tokenStatus === "partial" || tokenStatus === "unavailable") {
346
+ add({
347
+ pluginId: report.plugin.id,
348
+ metricId: null,
349
+ id: `telemetry-partial:${report.plugin.id}`,
350
+ kind: "telemetry-partial",
351
+ severity: "warning",
352
+ text: tokenStatus === "partial"
353
+ ? "Token evidence for the agent campaign is partial: some agent sides reported no token total, and missing totals are not zero."
354
+ : "Token evidence for the agent campaign is unavailable: no agent side reported a token total, and missing totals are not zero.",
355
+ });
356
+ }
357
+ const pluginId = report.plugin.id;
358
+ const missing = new Set();
359
+ for (const view of views) {
360
+ if (view.glossaryAnchor === null)
361
+ missing.add(pluginLocalMetricId(pluginId, view.metricId));
362
+ }
363
+ if (missing.size > 0) {
364
+ const ids = [...missing].sort();
365
+ const shown = ids.slice(0, MAX_LISTED).join(", ");
366
+ add({
367
+ pluginId,
368
+ metricId: null,
369
+ id: `glossary-missing:${pluginId}`,
370
+ kind: "glossary-missing",
371
+ severity: "info",
372
+ text: `Glossary metadata is missing for ${ids.length} rendered metric(s): ${shown}` +
373
+ (ids.length > MAX_LISTED ? `, and ${ids.length - MAX_LISTED} more` : "") +
374
+ ". They are shown without a definition or glossary link.",
375
+ });
376
+ }
377
+ return [...byId.values()].sort((a, b) => {
378
+ const rank = KIND_ORDER.indexOf(a.kind) - KIND_ORDER.indexOf(b.kind);
379
+ if (rank !== 0)
380
+ return rank;
381
+ if (a.pluginId !== b.pluginId)
382
+ return a.pluginId < b.pluginId ? -1 : 1;
383
+ const am = a.metricId ?? "";
384
+ const bm = b.metricId ?? "";
385
+ if (am !== bm)
386
+ return am < bm ? -1 : 1;
387
+ return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
388
+ });
389
+ }