tarsk 0.5.64 → 0.5.66

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 (135) hide show
  1. package/dist/bundled-skills/skill-creator/eval-viewer/generate_review.js +41 -40
  2. package/dist/bundled-skills/skill-creator/scripts/aggregate_benchmark.js +106 -98
  3. package/dist/bundled-skills/skill-creator/scripts/generate_report.js +78 -67
  4. package/dist/bundled-skills/skill-creator/scripts/improve_description.js +119 -72
  5. package/dist/bundled-skills/skill-creator/scripts/run_eval.js +76 -54
  6. package/dist/bundled-skills/skill-creator/scripts/run_loop.js +221 -142
  7. package/dist/index.js +51644 -47172
  8. package/dist/public/assets/{account-view-Dp0PfqVq.js → account-view-BlK6g38q.js} +1 -1
  9. package/dist/public/assets/{alert-dialog-Ge-FFGao.js → alert-dialog-B6An1LDY.js} +1 -1
  10. package/dist/public/assets/{async-error-boundary-B0tQed36.js → async-error-boundary-Rf-s2_Ez.js} +1 -1
  11. package/dist/public/assets/browser-tab-BJxPUVJi.js +12 -0
  12. package/dist/public/assets/{card-xQfAy2Rx.js → card-BDQUHsPD.js} +1 -1
  13. package/dist/public/assets/chat-dock-context-DyKjqpND.js +1 -0
  14. package/dist/public/assets/commit-dialog-CYb1kJCq.js +1 -0
  15. package/dist/public/assets/commit-error-dialog-DvxaNRcH.js +1 -0
  16. package/dist/public/assets/commit-method-fields-Dijc502F.js +1 -0
  17. package/dist/public/assets/context-menu-C51M1WGZ.js +1 -0
  18. package/dist/public/assets/conversation-history-view-w_6Jd3d-.js +1 -0
  19. package/dist/public/assets/create-project-dialog-CSVkngnF.js +6 -0
  20. package/dist/public/assets/create-repo-dialog-CcFA8pXz.js +1 -0
  21. package/dist/public/assets/{delete-confirm-dialog-Dy0dOtPe.js → delete-confirm-dialog-uTSX_x-i.js} +1 -1
  22. package/dist/public/assets/diff-view-XN5G5F6E.js +4 -0
  23. package/dist/public/assets/dist-IeRYu0zQ.js +142 -0
  24. package/dist/public/assets/{explorer-tab-context-8bs_tiCO.js → explorer-tab-context-BdjTEbFh.js} +1 -1
  25. package/dist/public/assets/explorer-tab-view-A2ICDFPB.js +2 -0
  26. package/dist/public/assets/explorer-tree-B04Ba59E.js +1 -0
  27. package/dist/public/assets/explorer-view-CofaKFiu.js +75 -0
  28. package/dist/public/assets/git-history-dialog-V3kaff2G.js +1 -0
  29. package/dist/public/assets/index-B0xy4BkI.js +3360 -0
  30. package/dist/public/assets/index-Cndc50ux.css +1 -0
  31. package/dist/public/assets/interactive-BrHVRHmQ.js +21 -0
  32. package/dist/public/assets/{main-tab-context-DHeKIUsf.js → main-tab-context-zQSuRQQG.js} +1 -1
  33. package/dist/public/assets/mcp-server-card-DOsP-SQ8.js +1 -0
  34. package/dist/public/assets/merged-pr-dialog-BerRzLlh.js +1 -0
  35. package/dist/public/assets/model-list-controls-Dw98tMn7.js +1 -0
  36. package/dist/public/assets/{monaco-setup-x3evbdq3.js → monaco-setup-C87kBr4u.js} +1 -1
  37. package/dist/public/assets/onboarding-DVc9-yKJ.js +46 -0
  38. package/dist/public/assets/project-env-vars-view-BQwUd7y6.js +1 -0
  39. package/dist/public/assets/project-model-aliases-view-CobSp09z.js +1 -0
  40. package/dist/public/assets/project-settings-view-CPTd5lEi.js +1 -0
  41. package/dist/public/assets/{project-tools-view-BOszAFSe.js → project-tools-view-DShQghoz.js} +1 -1
  42. package/dist/public/assets/providers-list-view-Da9VMcuP.js +1 -0
  43. package/dist/public/assets/pull-request-dialog-BAarT9EM.js +1 -0
  44. package/dist/public/assets/pull-with-changes-dialog-BcF25mjQ.js +1 -0
  45. package/dist/public/assets/push-before-pr-dialog-BF0NzV2u.js +1 -0
  46. package/dist/public/assets/react-vendor-B7jFrulV.js +22 -0
  47. package/dist/public/assets/settings-general-view-DT4nElcL.js +1 -0
  48. package/dist/public/assets/settings-instructions-view-Bsr8wRMf.js +1 -0
  49. package/dist/public/assets/settings-list-B9wyVzKw.js +1 -0
  50. package/dist/public/assets/settings-logs-view-CyaGqUKH.js +5 -0
  51. package/dist/public/assets/settings-mcp-servers-view-BTUWHB5K.js +5 -0
  52. package/dist/public/assets/settings-models-view-DFwkVdt3.js +1 -0
  53. package/dist/public/assets/settings-rules-view-BhA9tjVh.js +8 -0
  54. package/dist/public/assets/settings-skills-view-BFoO9oZk.js +3 -0
  55. package/dist/public/assets/{settings-slack-view-OFUhhjTj.js → settings-slack-view-D4mrQ9C9.js} +1 -1
  56. package/dist/public/assets/settings-slash-commands-view-BLKPDUVG.js +1 -0
  57. package/dist/public/assets/settings-statistics-view-fa6iBiP8.js +1 -0
  58. package/dist/public/assets/settings-storage-view-LaiAi5gv.js +1 -0
  59. package/dist/public/assets/settings-subagents-view-BjMz2Ri_.js +2 -0
  60. package/dist/public/assets/settings-system-prompt-view-DNh1yqZh.js +1 -0
  61. package/dist/public/assets/settings-view-BoC66Wji.js +2 -0
  62. package/dist/public/assets/skeleton-CJSNEi3C.js +1 -0
  63. package/dist/public/assets/store-B_cK-gsT.js +8 -0
  64. package/dist/public/assets/{table-CYWF-RhQ.js → table-Cb2C1rQy.js} +1 -1
  65. package/dist/public/assets/tasks-view-B049aXpC.js +2 -0
  66. package/dist/public/assets/{terminal-panel-BxUHr-tZ.js → terminal-panel-Pv5xqCpm.js} +3 -3
  67. package/dist/public/assets/thread-tool-blocks-D9HUkPHB.js +1 -0
  68. package/dist/public/assets/{ui-components-DjoQfi8k.js → ui-components-DJLsrr5e.js} +1 -1
  69. package/dist/public/assets/{use-active-dock-panel-instance-BE69kVdb.js → use-active-dock-panel-instance-BlRDPUlZ.js} +1 -1
  70. package/dist/public/assets/{use-deferred-search-DXrcdbyC.js → use-deferred-search-0sV7mSzX.js} +1 -1
  71. package/dist/public/assets/use-installed-ai-files-DmLnugpj.js +1 -0
  72. package/dist/public/assets/{use-settings-page-loading-BC_wP7pl.js → use-settings-page-loading-BU2CDnfE.js} +1 -1
  73. package/dist/public/assets/{use-suspense-resource-rX-GqyWi.js → use-suspense-resource-CExc4Svq.js} +1 -1
  74. package/dist/public/assets/{use-toast-BFQ85Ly5.js → use-toast-hxHsVv6a.js} +1 -1
  75. package/dist/public/assets/{utils-Rlf65JHq.js → utils-SsF94Pkg.js} +1 -1
  76. package/dist/public/assets/{web-C8pB6Hir.js → web-BidDZaMC.js} +1 -1
  77. package/dist/public/assets/{web-g0yy51Pa.js → web-C_HuLxHj.js} +1 -1
  78. package/dist/public/assets/{web-CzV4mQzJ.js → web-DHUv7oQP.js} +1 -1
  79. package/dist/public/assets/{web-B_HK2K2n.js → web-JMCxQg4N.js} +1 -1
  80. package/dist/public/browser-preview-rpc.js +465 -7
  81. package/dist/public/index.html +14 -16
  82. package/dist/recommended-models.txt +5 -5
  83. package/package.json +1 -14
  84. package/dist/public/assets/api-BsPadLpi.js +0 -1
  85. package/dist/public/assets/browser-tab-D-Lv93tm.js +0 -8
  86. package/dist/public/assets/chat-dock-context-yQDhSXnw.js +0 -1
  87. package/dist/public/assets/commit-dialog-CoiwuVLU.js +0 -1
  88. package/dist/public/assets/commit-method-fields-DG3NtLE2.js +0 -1
  89. package/dist/public/assets/context-menu-M5y1Qb8G.js +0 -1
  90. package/dist/public/assets/conversation-history-view-C1Wu4_VR.js +0 -1
  91. package/dist/public/assets/create-project-dialog-DfvqFmmy.js +0 -6
  92. package/dist/public/assets/create-repo-dialog-wIb4blPC.js +0 -1
  93. package/dist/public/assets/diff-view-P5m6sKUJ.js +0 -4
  94. package/dist/public/assets/dist-oojFghRi.js +0 -134
  95. package/dist/public/assets/explorer-tab-view-B6z1DZzq.js +0 -2
  96. package/dist/public/assets/explorer-tree-BK2uuzSQ.js +0 -1
  97. package/dist/public/assets/explorer-view-D5F0ifAy.js +0 -75
  98. package/dist/public/assets/git-history-dialog-fm9qhkgI.js +0 -1
  99. package/dist/public/assets/git-ops-button-sVka4_mz.js +0 -2
  100. package/dist/public/assets/index-zVJV2Bjj.js +0 -3307
  101. package/dist/public/assets/index-zilB0ZSo.css +0 -1
  102. package/dist/public/assets/interactive-WtOIgeV4.js +0 -21
  103. package/dist/public/assets/local-tool-blocks-DlqmJ7__.js +0 -1
  104. package/dist/public/assets/mcp-server-card-V4Ddsgv3.js +0 -1
  105. package/dist/public/assets/merged-pr-dialog-Bi5pJzTO.js +0 -1
  106. package/dist/public/assets/model-list-controls-oiBE9X9_.js +0 -1
  107. package/dist/public/assets/onboarding-dbhmRuxw.js +0 -46
  108. package/dist/public/assets/project-env-vars-view-OVck-YEy.js +0 -1
  109. package/dist/public/assets/project-model-aliases-view-Cu-n2j94.js +0 -1
  110. package/dist/public/assets/project-settings-view-Dte7mVE6.js +0 -1
  111. package/dist/public/assets/providers-list-view-B1qojjPs.js +0 -1
  112. package/dist/public/assets/pull-request-dialog-D_KiLAdI.js +0 -1
  113. package/dist/public/assets/pull-with-changes-dialog-CJKvLUaf.js +0 -1
  114. package/dist/public/assets/push-before-pr-dialog-BZLScOoO.js +0 -1
  115. package/dist/public/assets/react-vendor-wGLyNWf9.js +0 -22
  116. package/dist/public/assets/settings-general-view-DCRKEVJG.js +0 -1
  117. package/dist/public/assets/settings-instructions-view-C3WTfmcU.js +0 -1
  118. package/dist/public/assets/settings-list-C5MACtux.js +0 -1
  119. package/dist/public/assets/settings-logs-view-YWMyQqu5.js +0 -5
  120. package/dist/public/assets/settings-mcp-servers-view-B9H8QqOa.js +0 -5
  121. package/dist/public/assets/settings-models-view-D0QpnAvv.js +0 -1
  122. package/dist/public/assets/settings-rules-view-CbjVigsB.js +0 -8
  123. package/dist/public/assets/settings-skills-view-BGztTaqQ.js +0 -3
  124. package/dist/public/assets/settings-slash-commands-view-DHsB_RxG.js +0 -1
  125. package/dist/public/assets/settings-statistics-view-Bp624KXI.js +0 -1
  126. package/dist/public/assets/settings-storage-view-CAFIGn34.js +0 -1
  127. package/dist/public/assets/settings-subagents-view-5V5RzBgw.js +0 -2
  128. package/dist/public/assets/settings-system-prompt-view-BiGYDmSw.js +0 -1
  129. package/dist/public/assets/settings-view-CAvpL6Tc.js +0 -2
  130. package/dist/public/assets/skeleton-B_xdlo2b.js +0 -1
  131. package/dist/public/assets/slug-utils-C5rnmMMP.js +0 -1
  132. package/dist/public/assets/store-CSyJjyNs.js +0 -7
  133. package/dist/public/assets/tasks-view-C6z5quOD.js +0 -2
  134. package/dist/public/assets/thread-tool-blocks-BfowiaTH.js +0 -1
  135. package/dist/public/assets/use-installed-ai-files-CQzyZpa8.js +0 -1
@@ -95,7 +95,7 @@ function findRunsRecursive(root, current, runs) {
95
95
  }
96
96
  }
97
97
 
98
- function buildRun(root, runDir) {
98
+ function readEvalMetadata(runDir) {
99
99
  let prompt = "";
100
100
  let evalId = null;
101
101
 
@@ -114,57 +114,69 @@ function buildRun(root, runDir) {
114
114
  if (prompt) break;
115
115
  }
116
116
 
117
- if (!prompt) {
118
- for (const candidate of [
119
- path.join(runDir, "transcript.md"),
120
- path.join(runDir, "outputs", "transcript.md"),
121
- ]) {
122
- if (!fs.existsSync(candidate)) continue;
123
- try {
124
- const text = fs.readFileSync(candidate, "utf-8");
125
- const match = text.match(/## Eval Prompt\n\n([\s\S]*?)(?=\n##|$)/);
126
- if (match) prompt = match[1].trim();
127
- } catch {
128
- // ignore
129
- }
130
- if (prompt) break;
117
+ return { prompt, evalId };
118
+ }
119
+
120
+ function extractPromptFromTranscript(runDir) {
121
+ for (const candidate of [
122
+ path.join(runDir, "transcript.md"),
123
+ path.join(runDir, "outputs", "transcript.md"),
124
+ ]) {
125
+ if (!fs.existsSync(candidate)) continue;
126
+ try {
127
+ const text = fs.readFileSync(candidate, "utf-8");
128
+ const match = text.match(/## Eval Prompt\n\n([\s\S]*?)(?=\n##|$)/);
129
+ if (match) return match[1].trim();
130
+ } catch {
131
+ // ignore
131
132
  }
132
133
  }
134
+ return "";
135
+ }
133
136
 
134
- if (!prompt) prompt = "(No prompt found)";
135
-
136
- const runId = path.relative(root, runDir).split(path.sep).join("-");
137
+ function collectOutputFiles(runDir) {
137
138
  const outputsDir = path.join(runDir, "outputs");
138
139
  const outputFiles = [];
139
- if (fs.existsSync(outputsDir) && fs.statSync(outputsDir).isDirectory()) {
140
- for (const name of fs.readdirSync(outputsDir).sort()) {
141
- const filePath = path.join(outputsDir, name);
142
- if (fs.statSync(filePath).isFile() && !METADATA_FILES.has(name)) {
143
- outputFiles.push(embedFile(filePath));
144
- }
140
+ if (!fs.existsSync(outputsDir) || !fs.statSync(outputsDir).isDirectory()) {
141
+ return outputFiles;
142
+ }
143
+ for (const name of fs.readdirSync(outputsDir).sort()) {
144
+ const filePath = path.join(outputsDir, name);
145
+ if (fs.statSync(filePath).isFile() && !METADATA_FILES.has(name)) {
146
+ outputFiles.push(embedFile(filePath));
145
147
  }
146
148
  }
149
+ return outputFiles;
150
+ }
147
151
 
148
- let grading = null;
152
+ function readGrading(runDir) {
149
153
  for (const candidate of [
150
154
  path.join(runDir, "grading.json"),
151
155
  path.join(runDir, "..", "grading.json"),
152
156
  ]) {
153
157
  if (!fs.existsSync(candidate)) continue;
154
158
  try {
155
- grading = JSON.parse(fs.readFileSync(candidate, "utf-8"));
159
+ const grading = JSON.parse(fs.readFileSync(candidate, "utf-8"));
160
+ if (grading) return grading;
156
161
  } catch {
157
162
  // ignore
158
163
  }
159
- if (grading) break;
160
164
  }
165
+ return null;
166
+ }
167
+
168
+ function buildRun(root, runDir) {
169
+ const { prompt: metadataPrompt, evalId } = readEvalMetadata(runDir);
170
+ const prompt = metadataPrompt || extractPromptFromTranscript(runDir) || "(No prompt found)";
171
+
172
+ const runId = path.relative(root, runDir).split(path.sep).join("-");
161
173
 
162
174
  return {
163
175
  id: runId,
164
176
  prompt,
165
177
  eval_id: evalId,
166
- outputs: outputFiles,
167
- grading,
178
+ outputs: collectOutputFiles(runDir),
179
+ grading: readGrading(runDir),
168
180
  };
169
181
  }
170
182
 
@@ -531,14 +543,3 @@ function main() {
531
543
  if (require.main === module) {
532
544
  main();
533
545
  }
534
-
535
- module.exports = {
536
- findRuns,
537
- buildRun,
538
- embedFile,
539
- loadPreviousIteration,
540
- generateHtml,
541
- createReviewServer,
542
- killPort,
543
- openUrl,
544
- };
@@ -38,117 +38,131 @@ function globEvalDirs(dir) {
38
38
  .map((entry) => path.join(dir, entry.name));
39
39
  }
40
40
 
41
- function loadRunResults(benchmarkDir) {
41
+ function resolveSearchDir(benchmarkDir) {
42
42
  const runsDir = path.join(benchmarkDir, "runs");
43
- let searchDir;
44
43
  if (fs.existsSync(runsDir) && fs.statSync(runsDir).isDirectory()) {
45
- searchDir = runsDir;
46
- } else if (globEvalDirs(benchmarkDir).length > 0) {
47
- searchDir = benchmarkDir;
48
- } else {
49
- console.log(
50
- `No eval directories found in ${benchmarkDir} or ${path.join(benchmarkDir, "runs")}`,
51
- );
52
- return {};
44
+ return runsDir;
45
+ }
46
+ if (globEvalDirs(benchmarkDir).length > 0) {
47
+ return benchmarkDir;
53
48
  }
49
+ console.log(`No eval directories found in ${benchmarkDir} or ${path.join(benchmarkDir, "runs")}`);
50
+ return null;
51
+ }
54
52
 
55
- const results = {};
53
+ function resolveEvalId(evalDir, evalIdx) {
54
+ const metadataPath = path.join(evalDir, "eval_metadata.json");
55
+ if (fs.existsSync(metadataPath)) {
56
+ try {
57
+ const metadata = JSON.parse(fs.readFileSync(metadataPath, "utf-8"));
58
+ return metadata.eval_id ?? evalIdx;
59
+ } catch {
60
+ return evalIdx;
61
+ }
62
+ }
63
+ const parts = path.basename(evalDir).split("-");
64
+ const parsed = parseInt(parts[1], 10);
65
+ return Number.isNaN(parsed) ? evalIdx : parsed;
66
+ }
67
+
68
+ function listRunDirs(configDir) {
69
+ return fs
70
+ .readdirSync(configDir)
71
+ .filter((name) => name.startsWith("run-"))
72
+ .map((name) => path.join(configDir, name))
73
+ .sort();
74
+ }
75
+
76
+ function loadGrading(runDir) {
77
+ const gradingFile = path.join(runDir, "grading.json");
78
+ if (!fs.existsSync(gradingFile)) {
79
+ console.log(`Warning: grading.json not found in ${runDir}`);
80
+ return null;
81
+ }
82
+ try {
83
+ return JSON.parse(fs.readFileSync(gradingFile, "utf-8"));
84
+ } catch (error) {
85
+ console.log(`Warning: Invalid JSON in ${gradingFile}: ${error.message}`);
86
+ return null;
87
+ }
88
+ }
89
+
90
+ function buildResult(evalId, runNumber, grading, runDir) {
91
+ const result = {
92
+ eval_id: evalId,
93
+ run_number: runNumber,
94
+ pass_rate: grading.summary?.pass_rate ?? 0.0,
95
+ passed: grading.summary?.passed ?? 0,
96
+ failed: grading.summary?.failed ?? 0,
97
+ total: grading.summary?.total ?? 0,
98
+ };
56
99
 
100
+ const timing = grading.timing ?? {};
101
+ result.time_seconds = timing.total_duration_seconds ?? 0.0;
102
+ const timingFile = path.join(runDir, "timing.json");
103
+ if (result.time_seconds === 0.0 && fs.existsSync(timingFile)) {
104
+ try {
105
+ const timingData = JSON.parse(fs.readFileSync(timingFile, "utf-8"));
106
+ result.time_seconds = timingData.total_duration_seconds ?? 0.0;
107
+ result.tokens = timingData.total_tokens ?? 0;
108
+ } catch {
109
+ // ignore invalid timing.json
110
+ }
111
+ }
112
+
113
+ const metrics = grading.execution_metrics ?? {};
114
+ result.tool_calls = metrics.total_tool_calls ?? 0;
115
+ if (!result.tokens) {
116
+ result.tokens = metrics.output_chars ?? 0;
117
+ }
118
+ result.errors = metrics.errors_encountered ?? 0;
119
+
120
+ const rawExpectations = grading.expectations ?? [];
121
+ for (const exp of rawExpectations) {
122
+ if (!("text" in exp) || !("passed" in exp)) {
123
+ console.log(
124
+ `Warning: expectation in ${path.join(runDir, "grading.json")} missing required fields (text, passed, evidence): ${JSON.stringify(exp)}`,
125
+ );
126
+ }
127
+ }
128
+ result.expectations = rawExpectations;
129
+
130
+ const notesSummary = grading.user_notes_summary ?? {};
131
+ result.notes = [
132
+ ...(notesSummary.uncertainties ?? []),
133
+ ...(notesSummary.needs_review ?? []),
134
+ ...(notesSummary.workarounds ?? []),
135
+ ];
136
+
137
+ return result;
138
+ }
139
+
140
+ function loadRunResults(benchmarkDir) {
141
+ const searchDir = resolveSearchDir(benchmarkDir);
142
+ if (!searchDir) return {};
143
+
144
+ const results = {};
57
145
  const evalDirs = globEvalDirs(searchDir).sort();
58
146
  for (let evalIdx = 0; evalIdx < evalDirs.length; evalIdx++) {
59
147
  const evalDir = evalDirs[evalIdx];
60
- const metadataPath = path.join(evalDir, "eval_metadata.json");
61
- let evalId = evalIdx;
62
- if (fs.existsSync(metadataPath)) {
63
- try {
64
- const metadata = JSON.parse(fs.readFileSync(metadataPath, "utf-8"));
65
- evalId = metadata.eval_id ?? evalIdx;
66
- } catch {
67
- evalId = evalIdx;
68
- }
69
- } else {
70
- const parts = path.basename(evalDir).split("-");
71
- const parsed = parseInt(parts[1], 10);
72
- evalId = Number.isNaN(parsed) ? evalIdx : parsed;
73
- }
148
+ const evalId = resolveEvalId(evalDir, evalIdx);
74
149
 
75
150
  for (const configName of fs.readdirSync(evalDir).sort()) {
76
151
  const configDir = path.join(evalDir, configName);
77
152
  if (!fs.statSync(configDir).isDirectory()) continue;
78
153
 
79
- const runDirs = fs
80
- .readdirSync(configDir)
81
- .filter((name) => name.startsWith("run-"))
82
- .map((name) => path.join(configDir, name));
154
+ const runDirs = listRunDirs(configDir);
83
155
  if (runDirs.length === 0) continue;
84
156
 
85
157
  if (!results[configName]) {
86
158
  results[configName] = [];
87
159
  }
88
160
 
89
- for (const runDir of runDirs.sort()) {
161
+ for (const runDir of runDirs) {
90
162
  const runNumber = parseInt(path.basename(runDir).split("-")[1], 10);
91
- const gradingFile = path.join(runDir, "grading.json");
92
- if (!fs.existsSync(gradingFile)) {
93
- console.log(`Warning: grading.json not found in ${runDir}`);
94
- continue;
95
- }
96
-
97
- let grading;
98
- try {
99
- grading = JSON.parse(fs.readFileSync(gradingFile, "utf-8"));
100
- } catch (error) {
101
- console.log(`Warning: Invalid JSON in ${gradingFile}: ${error.message}`);
102
- continue;
103
- }
104
-
105
- const result = {
106
- eval_id: evalId,
107
- run_number: runNumber,
108
- pass_rate: grading.summary?.pass_rate ?? 0.0,
109
- passed: grading.summary?.passed ?? 0,
110
- failed: grading.summary?.failed ?? 0,
111
- total: grading.summary?.total ?? 0,
112
- };
113
-
114
- const timing = grading.timing ?? {};
115
- result.time_seconds = timing.total_duration_seconds ?? 0.0;
116
- const timingFile = path.join(runDir, "timing.json");
117
- if (result.time_seconds === 0.0 && fs.existsSync(timingFile)) {
118
- try {
119
- const timingData = JSON.parse(fs.readFileSync(timingFile, "utf-8"));
120
- result.time_seconds = timingData.total_duration_seconds ?? 0.0;
121
- result.tokens = timingData.total_tokens ?? 0;
122
- } catch {
123
- // ignore invalid timing.json
124
- }
125
- }
126
-
127
- const metrics = grading.execution_metrics ?? {};
128
- result.tool_calls = metrics.total_tool_calls ?? 0;
129
- if (!result.tokens) {
130
- result.tokens = metrics.output_chars ?? 0;
131
- }
132
- result.errors = metrics.errors_encountered ?? 0;
133
-
134
- const rawExpectations = grading.expectations ?? [];
135
- for (const exp of rawExpectations) {
136
- if (!("text" in exp) || !("passed" in exp)) {
137
- console.log(
138
- `Warning: expectation in ${gradingFile} missing required fields (text, passed, evidence): ${JSON.stringify(exp)}`,
139
- );
140
- }
141
- }
142
- result.expectations = rawExpectations;
143
-
144
- const notesSummary = grading.user_notes_summary ?? {};
145
- result.notes = [
146
- ...(notesSummary.uncertainties ?? []),
147
- ...(notesSummary.needs_review ?? []),
148
- ...(notesSummary.workarounds ?? []),
149
- ];
150
-
151
- results[configName].push(result);
163
+ const grading = loadGrading(runDir);
164
+ if (!grading) continue;
165
+ results[configName].push(buildResult(evalId, runNumber, grading, runDir));
152
166
  }
153
167
  }
154
168
  }
@@ -368,10 +382,4 @@ if (require.main === module) {
368
382
  main();
369
383
  }
370
384
 
371
- module.exports = {
372
- calculateStats,
373
- loadRunResults,
374
- aggregateResults,
375
- generateBenchmark,
376
- generateMarkdown,
377
- };
385
+ module.exports = { calculateStats, buildResult, aggregateResults, generateMarkdown };
@@ -37,26 +37,8 @@ function scoreClass(correct, total) {
37
37
  return "score-bad";
38
38
  }
39
39
 
40
- function generateHtml(data, autoRefresh = false, skillName = "") {
41
- const history = data.history ?? [];
42
- const titlePrefix = skillName ? escapeHtml(`${skillName} \u2014 `) : "";
43
-
44
- const trainQueries = [];
45
- const testQueries = [];
46
- if (history.length) {
47
- for (const r of history[0].train_results ?? history[0].results ?? []) {
48
- trainQueries.push({ query: r.query, should_trigger: r.should_trigger ?? true });
49
- }
50
- if (history[0].test_results) {
51
- for (const r of history[0].test_results) {
52
- testQueries.push({ query: r.query, should_trigger: r.should_trigger ?? true });
53
- }
54
- }
55
- }
56
-
57
- const refreshTag = autoRefresh ? ' <meta http-equiv="refresh" content="5">\n' : "";
58
- const htmlParts = [
59
- `<!DOCTYPE html>
40
+ function buildHead(titlePrefix, refreshTag) {
41
+ return `<!DOCTYPE html>
60
42
  <html>
61
43
  <head>
62
44
  <meta charset="utf-8">
@@ -175,20 +157,23 @@ ${refreshTag} <title>${titlePrefix}Skill Description Optimization</title>
175
157
  <div class="explainer">
176
158
  <strong>Optimizing your skill's description.</strong> This page updates automatically as Tarsk tests different versions of your skill's description. Each row is an iteration — a new description attempt. The columns show test queries: green checkmarks mean the skill triggered correctly (or correctly didn't trigger), red crosses mean it got it wrong. The "Train" score shows performance on queries used to improve the description; the "Test" score shows performance on held-out queries the optimizer hasn't seen. When it's done, Tarsk will apply the best-performing description to your skill.
177
159
  </div>
178
- `,
179
- ];
160
+ `;
161
+ }
180
162
 
163
+ function buildSummary(data) {
181
164
  const bestTestScore = data.best_test_score;
182
- htmlParts.push(`
165
+ return `
183
166
  <div class="summary">
184
167
  <p><strong>Original:</strong> ${escapeHtml(data.original_description ?? "N/A")}</p>
185
168
  <p class="best"><strong>Best:</strong> ${escapeHtml(data.best_description ?? "N/A")}</p>
186
169
  <p><strong>Best Score:</strong> ${data.best_score ?? "N/A"} ${bestTestScore ? "(test)" : "(train)"}</p>
187
170
  <p><strong>Iterations:</strong> ${data.iterations_run ?? 0} | <strong>Train:</strong> ${data.train_size ?? "?"} | <strong>Test:</strong> ${data.test_size ?? "?"}</p>
188
171
  </div>
189
- `);
172
+ `;
173
+ }
190
174
 
191
- htmlParts.push(`
175
+ function buildLegend() {
176
+ return `
192
177
  <div class="legend">
193
178
  <span style="font-weight:600">Query columns:</span>
194
179
  <span class="legend-item"><span class="legend-swatch swatch-positive"></span> Should trigger</span>
@@ -196,9 +181,12 @@ ${refreshTag} <title>${titlePrefix}Skill Description Optimization</title>
196
181
  <span class="legend-item"><span class="legend-swatch swatch-train"></span> Train</span>
197
182
  <span class="legend-item"><span class="legend-swatch swatch-test"></span> Test</span>
198
183
  </div>
199
- `);
184
+ `;
185
+ }
200
186
 
201
- htmlParts.push(`
187
+ function buildTableHeader(trainQueries, testQueries) {
188
+ const parts = [
189
+ `
202
190
  <div class="table-container">
203
191
  <table>
204
192
  <thead>
@@ -207,37 +195,53 @@ ${refreshTag} <title>${titlePrefix}Skill Description Optimization</title>
207
195
  <th>Train</th>
208
196
  <th>Test</th>
209
197
  <th class="query-col">Description</th>
210
- `);
211
-
198
+ `,
199
+ ];
212
200
  for (const qinfo of trainQueries) {
213
201
  const polarity = qinfo.should_trigger ? "positive-col" : "negative-col";
214
- htmlParts.push(` <th class="${polarity}">${escapeHtml(qinfo.query)}</th>\n`);
202
+ parts.push(` <th class="${polarity}">${escapeHtml(qinfo.query)}</th>\n`);
215
203
  }
216
204
  for (const qinfo of testQueries) {
217
205
  const polarity = qinfo.should_trigger ? "positive-col" : "negative-col";
218
- htmlParts.push(
206
+ parts.push(
219
207
  ` <th class="test-col ${polarity}">${escapeHtml(qinfo.query)}</th>\n`,
220
208
  );
221
209
  }
222
-
223
- htmlParts.push(` </tr>
210
+ parts.push(` </tr>
224
211
  </thead>
225
212
  <tbody>
226
213
  `);
214
+ return parts.join("");
215
+ }
227
216
 
228
- let bestIter;
217
+ function buildResultCell(qinfo, byQuery, isTest) {
218
+ const r = byQuery[qinfo.query] ?? {};
219
+ const didPass = r.pass ?? false;
220
+ const triggers = r.triggers ?? 0;
221
+ const runs = r.runs ?? 0;
222
+ const icon = didPass ? "✓" : "✗";
223
+ const cssClass = didPass ? "pass" : "fail";
224
+ const testClass = isTest ? "test-result " : "";
225
+ return ` <td class="result ${testClass}${cssClass}">${icon}<span class="rate">${triggers}/${runs}</span></td>\n`;
226
+ }
227
+
228
+ function findBestIteration(history, testQueries) {
229
229
  if (testQueries.length) {
230
- bestIter = history.reduce(
230
+ return history.reduce(
231
231
  (best, h) => ((h.test_passed ?? 0) > (best.test_passed ?? 0) ? h : best),
232
232
  history[0],
233
233
  ).iteration;
234
- } else {
235
- bestIter = history.reduce(
236
- (best, h) =>
237
- (h.train_passed ?? h.passed ?? 0) > (best.train_passed ?? best.passed ?? 0) ? h : best,
238
- history[0],
239
- ).iteration;
240
234
  }
235
+ return history.reduce(
236
+ (best, h) =>
237
+ (h.train_passed ?? h.passed ?? 0) > (best.train_passed ?? best.passed ?? 0) ? h : best,
238
+ history[0],
239
+ ).iteration;
240
+ }
241
+
242
+ function buildTableRows(history, trainQueries, testQueries) {
243
+ const parts = [];
244
+ const bestIter = findBestIteration(history, testQueries);
241
245
 
242
246
  for (const h of history) {
243
247
  const iteration = h.iteration ?? "?";
@@ -248,55 +252,62 @@ ${refreshTag} <title>${titlePrefix}Skill Description Optimization</title>
248
252
  const testByQuery = testResults.length
249
253
  ? Object.fromEntries(testResults.map((r) => [r.query, r]))
250
254
  : {};
251
-
252
255
  const [trainCorrect, trainRuns] = aggregateRuns(trainResults);
253
256
  const [testCorrect, testRuns] = aggregateRuns(testResults);
254
257
  const trainClass = scoreClass(trainCorrect, trainRuns);
255
258
  const testClass = scoreClass(testCorrect, testRuns);
256
259
  const rowClass = iteration === bestIter ? "best-row" : "";
257
260
 
258
- htmlParts.push(` <tr class="${rowClass}">
261
+ parts.push(` <tr class="${rowClass}">
259
262
  <td>${iteration}</td>
260
263
  <td><span class="score ${trainClass}">${trainCorrect}/${trainRuns}</span></td>
261
264
  <td><span class="score ${testClass}">${testCorrect}/${testRuns}</span></td>
262
265
  <td class="description">${escapeHtml(description)}</td>
263
266
  `);
264
-
265
267
  for (const qinfo of trainQueries) {
266
- const r = trainByQuery[qinfo.query] ?? {};
267
- const didPass = r.pass ?? false;
268
- const triggers = r.triggers ?? 0;
269
- const runs = r.runs ?? 0;
270
- const icon = didPass ? "✓" : "✗";
271
- const cssClass = didPass ? "pass" : "fail";
272
- htmlParts.push(
273
- ` <td class="result ${cssClass}">${icon}<span class="rate">${triggers}/${runs}</span></td>\n`,
274
- );
268
+ parts.push(buildResultCell(qinfo, trainByQuery, false));
275
269
  }
276
-
277
270
  for (const qinfo of testQueries) {
278
- const r = testByQuery[qinfo.query] ?? {};
279
- const didPass = r.pass ?? false;
280
- const triggers = r.triggers ?? 0;
281
- const runs = r.runs ?? 0;
282
- const icon = didPass ? "✓" : "✗";
283
- const cssClass = didPass ? "pass" : "fail";
284
- htmlParts.push(
285
- ` <td class="result test-result ${cssClass}">${icon}<span class="rate">${triggers}/${runs}</span></td>\n`,
286
- );
271
+ parts.push(buildResultCell(qinfo, testByQuery, true));
287
272
  }
288
-
289
- htmlParts.push(" </tr>\n");
273
+ parts.push(" </tr>\n");
290
274
  }
291
275
 
292
- htmlParts.push(` </tbody>
276
+ parts.push(` </tbody>
293
277
  </table>
294
278
  </div>
295
279
  </body>
296
280
  </html>
297
281
  `);
282
+ return parts.join("");
283
+ }
284
+
285
+ function generateHtml(data, autoRefresh = false, skillName = "") {
286
+ const history = data.history ?? [];
287
+ const titlePrefix = skillName ? escapeHtml(`${skillName} \u2014 `) : "";
288
+
289
+ const trainQueries = [];
290
+ const testQueries = [];
291
+ if (history.length) {
292
+ for (const r of history[0].train_results ?? history[0].results ?? []) {
293
+ trainQueries.push({ query: r.query, should_trigger: r.should_trigger ?? true });
294
+ }
295
+ if (history[0].test_results) {
296
+ for (const r of history[0].test_results) {
297
+ testQueries.push({ query: r.query, should_trigger: r.should_trigger ?? true });
298
+ }
299
+ }
300
+ }
301
+
302
+ const refreshTag = autoRefresh ? ' <meta http-equiv="refresh" content="5">\n' : "";
298
303
 
299
- return htmlParts.join("");
304
+ return (
305
+ buildHead(titlePrefix, refreshTag) +
306
+ buildSummary(data) +
307
+ buildLegend() +
308
+ buildTableHeader(trainQueries, testQueries) +
309
+ buildTableRows(history, trainQueries, testQueries)
310
+ );
300
311
  }
301
312
 
302
313
  function parseArgs(argv) {