@kodax-ai/kodax 0.7.95 → 0.7.96-alpha.2

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 (163) hide show
  1. package/CHANGELOG.md +178 -1
  2. package/LICENSE +158 -158
  3. package/README.md +149 -24
  4. package/README_CN.md +117 -24
  5. package/config-templates/config.example.jsonc +2 -1
  6. package/config-templates/integrations/a2a.example.jsonc +91 -91
  7. package/config-templates/integrations/extensions.example.jsonc +7 -7
  8. package/config-templates/integrations/mcp.example.jsonc +16 -16
  9. package/dist/builtin/code-review/SKILL.md +82 -82
  10. package/dist/builtin/git-workflow/SKILL.md +84 -84
  11. package/dist/builtin/skill-creator/SKILL.md +127 -127
  12. package/dist/builtin/skill-creator/agents/analyzer.md +12 -12
  13. package/dist/builtin/skill-creator/agents/comparator.md +13 -13
  14. package/dist/builtin/skill-creator/agents/grader.md +13 -13
  15. package/dist/builtin/skill-creator/references/schemas.md +227 -227
  16. package/dist/builtin/skill-creator/scripts/aggregate-benchmark.d.ts +46 -46
  17. package/dist/builtin/skill-creator/scripts/aggregate-benchmark.js +208 -208
  18. package/dist/builtin/skill-creator/scripts/analyze-benchmark.d.ts +46 -46
  19. package/dist/builtin/skill-creator/scripts/analyze-benchmark.js +286 -286
  20. package/dist/builtin/skill-creator/scripts/compare-runs.d.ts +62 -62
  21. package/dist/builtin/skill-creator/scripts/compare-runs.js +330 -330
  22. package/dist/builtin/skill-creator/scripts/generate-review.d.ts +33 -33
  23. package/dist/builtin/skill-creator/scripts/generate-review.js +414 -414
  24. package/dist/builtin/skill-creator/scripts/grade-evals.d.ts +73 -73
  25. package/dist/builtin/skill-creator/scripts/grade-evals.js +402 -402
  26. package/dist/builtin/skill-creator/scripts/improve-description.d.ts +23 -23
  27. package/dist/builtin/skill-creator/scripts/improve-description.js +160 -160
  28. package/dist/builtin/skill-creator/scripts/init-skill.d.ts +14 -14
  29. package/dist/builtin/skill-creator/scripts/init-skill.js +155 -155
  30. package/dist/builtin/skill-creator/scripts/install-skill.d.ts +29 -29
  31. package/dist/builtin/skill-creator/scripts/install-skill.js +173 -173
  32. package/dist/builtin/skill-creator/scripts/package-skill.d.ts +38 -38
  33. package/dist/builtin/skill-creator/scripts/package-skill.js +121 -121
  34. package/dist/builtin/skill-creator/scripts/quick-validate.d.ts +8 -8
  35. package/dist/builtin/skill-creator/scripts/quick-validate.js +163 -163
  36. package/dist/builtin/skill-creator/scripts/run-eval.d.ts +66 -66
  37. package/dist/builtin/skill-creator/scripts/run-eval.js +353 -353
  38. package/dist/builtin/skill-creator/scripts/run-loop.d.ts +49 -49
  39. package/dist/builtin/skill-creator/scripts/run-loop.js +242 -242
  40. package/dist/builtin/skill-creator/scripts/run-trigger-eval.d.ts +58 -58
  41. package/dist/builtin/skill-creator/scripts/run-trigger-eval.js +224 -224
  42. package/dist/builtin/tdd/SKILL.md +56 -56
  43. package/dist/chunks/agent-4FABF6WK.js +2 -0
  44. package/dist/chunks/argument-completer-72VHB2VL.js +2 -0
  45. package/dist/chunks/{chunk-7JXA3533.js → chunk-4OIQZWA5.js} +223 -226
  46. package/dist/chunks/{chunk-BNVSKIAB.js → chunk-6BVF3HZ7.js} +136 -136
  47. package/dist/chunks/chunk-7JZZIFZH.js +501 -0
  48. package/dist/chunks/chunk-AC7Z2OIY.js +2 -0
  49. package/dist/chunks/{chunk-JS452J2F.js → chunk-C4KYZAFU.js} +2 -2
  50. package/dist/chunks/chunk-CHUPIWIF.js +251 -0
  51. package/dist/chunks/{chunk-C4DTUKTR.js → chunk-GYUU7XLH.js} +5 -4
  52. package/dist/chunks/{chunk-OK4AXDC5.js → chunk-HAX55GOQ.js} +1 -1
  53. package/dist/chunks/chunk-JKTJCGO2.js +418 -0
  54. package/dist/chunks/{chunk-O743AJ5V.js → chunk-LQYETRFK.js} +171 -175
  55. package/dist/chunks/{chunk-2PEDBGKN.js → chunk-LTUZTQ6W.js} +1 -1
  56. package/dist/chunks/chunk-LYWDUSKO.js +2 -0
  57. package/dist/chunks/chunk-NGH6T4FD.js +92 -0
  58. package/dist/chunks/{chunk-SRIJX5ZP.js → chunk-NVRWCSA5.js} +1 -1
  59. package/dist/chunks/chunk-O4DS7RIQ.js +384 -0
  60. package/dist/chunks/chunk-ORXU6OWZ.js +2 -0
  61. package/dist/chunks/chunk-POTW3O65.js +124 -0
  62. package/dist/chunks/{chunk-2TM3W3GK.js → chunk-RIQSS56Y.js} +1 -1
  63. package/dist/chunks/chunk-U27XKEN5.js +407 -0
  64. package/dist/chunks/{chunk-6ABQJUK6.js → chunk-XUX6OUCA.js} +2 -2
  65. package/dist/chunks/{client-JDOS3YJR.js → client-4C456K4O.js} +1 -1
  66. package/dist/chunks/compaction-config-3VQRDFF4.js +2 -0
  67. package/dist/chunks/{construction-bootstrap-YCWHHUYO.js → construction-bootstrap-AHN7EDGG.js} +1 -1
  68. package/dist/chunks/dist-BCBQXAGI.js +2 -0
  69. package/dist/chunks/dist-QK2YDWU6.js +2 -0
  70. package/dist/chunks/host-VIHV7ADG.js +2 -0
  71. package/dist/chunks/run-manager-NBVMU3KV.js +2 -0
  72. package/dist/chunks/utils-MEJZ2IGI.js +2 -0
  73. package/dist/index.d.ts +22 -21
  74. package/dist/index.js +7 -7
  75. package/dist/kodax_bootstrap.js +26 -26
  76. package/dist/kodax_cli.js +1905 -1882
  77. package/dist/native/darwin-arm64/LICENSE-APACHE.txt +13 -0
  78. package/dist/native/darwin-arm64/kodax-text-transaction.node +0 -0
  79. package/dist/native/darwin-arm64/manifest.json +16 -0
  80. package/dist/native/darwin-x64/LICENSE-APACHE.txt +13 -0
  81. package/dist/native/darwin-x64/kodax-text-transaction.node +0 -0
  82. package/dist/native/darwin-x64/manifest.json +16 -0
  83. package/dist/native/linux-arm64/LICENSE-APACHE.txt +13 -0
  84. package/dist/native/linux-arm64/kodax-text-transaction.node +0 -0
  85. package/dist/native/linux-arm64/manifest.json +16 -0
  86. package/dist/native/linux-x64/LICENSE-APACHE.txt +13 -0
  87. package/dist/native/linux-x64/kodax-text-transaction.node +0 -0
  88. package/dist/native/linux-x64/manifest.json +16 -0
  89. package/dist/native/win32-x64/LICENSE-APACHE.txt +13 -0
  90. package/dist/native/win32-x64/NOTICE-windows-sandbox.txt +8 -0
  91. package/dist/native/win32-x64/kodax-windows-sandbox.exe +0 -0
  92. package/dist/native/win32-x64/kodax-windows-text-transaction.node +0 -0
  93. package/dist/native/win32-x64/manifest.json +30 -0
  94. package/dist/provider-capabilities.json +108 -2
  95. package/dist/runtime-worker.js +1745 -1727
  96. package/dist/sandbox-network-broker.js +1715 -0
  97. package/dist/sdk-a2a.d.ts +13 -12
  98. package/dist/sdk-a2a.js +1 -1
  99. package/dist/sdk-agent.d.ts +27 -18
  100. package/dist/sdk-agent.js +1 -1
  101. package/dist/sdk-coding.d.ts +107 -58
  102. package/dist/sdk-coding.js +1 -1
  103. package/dist/sdk-experimental-memory.d.ts +4 -4
  104. package/dist/sdk-experimental-memory.js +1 -1
  105. package/dist/sdk-llm.d.ts +9 -10
  106. package/dist/sdk-llm.js +1 -1
  107. package/dist/sdk-mcp.js +1 -1
  108. package/dist/sdk-media.d.ts +1 -1
  109. package/dist/sdk-media.js +1 -1
  110. package/dist/sdk-repl.d.ts +25 -17
  111. package/dist/sdk-repl.js +1 -1
  112. package/dist/sdk-runtime.d.ts +41 -17
  113. package/dist/sdk-runtime.js +1 -1
  114. package/dist/sdk-sandbox.d.ts +17 -3
  115. package/dist/sdk-sandbox.js +1 -1
  116. package/dist/sdk-session.d.ts +8 -7
  117. package/dist/sdk-session.js +1 -1
  118. package/dist/sdk-skills.js +1 -1
  119. package/dist/semantic-worker.js +61 -61
  120. package/dist/types-chunks/{base.d-vopO1rml.d.ts → base.d-COo8E2dk.d.ts} +18 -27
  121. package/dist/types-chunks/{bash-prefix-extractor.d-B2rxNI4L.d.ts → bash-prefix-extractor.d-DQOlA8cy.d.ts} +72 -52
  122. package/dist/types-chunks/{capability-learning.d-CE1tm29C.d.ts → capability-learning.d-lRPgA3Or.d.ts} +1 -1
  123. package/dist/types-chunks/{capsule.d-Y7QRS4J2.d.ts → capsule.d-0zxsgXg_.d.ts} +3 -3
  124. package/dist/types-chunks/{controller.d-BR-UVeUR.d.ts → controller.d-BTtyIIC1.d.ts} +1 -1
  125. package/dist/types-chunks/{controller.d-C5noq5-w.d.ts → controller.d-C7tXUcXo.d.ts} +2 -2
  126. package/dist/types-chunks/{guardrail.d-DqHD-61O.d.ts → guardrail.d-DKTyEHJa.d.ts} +5 -5
  127. package/dist/types-chunks/{history-retrieval.d-CKU8zmt_.d.ts → history-retrieval.d-CHnL99S7.d.ts} +2 -2
  128. package/dist/types-chunks/{public-api.d-D7fXaWW8.d.ts → public-api.d-Dx3L3W4Z.d.ts} +7 -4
  129. package/dist/types-chunks/{repl.d-BYDb14s6.d.ts → repl.d-mO01olIa.d.ts} +5 -5
  130. package/dist/types-chunks/{resolver.d-CwHUwbdE.d.ts → resolver.d-DlXPjeyx.d.ts} +38 -6
  131. package/dist/types-chunks/{review-inbox.d-BSgFewyj.d.ts → review-inbox.d-WDSe1nze.d.ts} +1 -1
  132. package/dist/types-chunks/{run-manager.d-BVJSntpS.d.ts → run-manager.d-DENbbzgR.d.ts} +1 -1
  133. package/dist/types-chunks/{sdk-session-CqEG84oP.d.ts → sdk-session-DYHGXXH4.d.ts} +3 -3
  134. package/dist/types-chunks/{shell-command-sets.d-DqaaBwhU.d.ts → shell-command-sets.d-CjFqS4dp.d.ts} +1 -1
  135. package/dist/types-chunks/{side-query.d-iH0P9ASy.d.ts → side-query.d-j8A3sFv_.d.ts} +2 -2
  136. package/dist/types-chunks/{types.d-rUOjXych.d.ts → types.d-BbZjR0Wr.d.ts} +1 -1
  137. package/dist/types-chunks/{types.d-CaTXAW_Q.d.ts → types.d-BkTIKdH6.d.ts} +2 -2
  138. package/dist/types-chunks/{types.d-C5xQEFcj.d.ts → types.d-DncLrpu_.d.ts} +4 -4
  139. package/dist/types-chunks/{types.d-CK9A9rpP.d.ts → types.d-Xy0f3x4i.d.ts} +9 -0
  140. package/dist/types-chunks/{utils.d-CWUyLiXo.d.ts → utils.d-CnxefMos.d.ts} +6 -6
  141. package/package.json +10 -3
  142. package/public_docs/README.md +130 -0
  143. package/public_docs/configuration/sandbox.md +289 -0
  144. package/public_docs/sdk/embedder-guide.md +6674 -0
  145. package/scripts/kodax-bin.cjs +28 -28
  146. package/scripts/production-env.cjs +25 -25
  147. package/dist/chunks/agent-XXZMXKKA.js +0 -2
  148. package/dist/chunks/argument-completer-KCYDGTQW.js +0 -2
  149. package/dist/chunks/chunk-77PNP27P.js +0 -92
  150. package/dist/chunks/chunk-7QBWHCIZ.js +0 -2
  151. package/dist/chunks/chunk-BHE66SP3.js +0 -123
  152. package/dist/chunks/chunk-BR2OSB2I.js +0 -722
  153. package/dist/chunks/chunk-DSMONSVB.js +0 -407
  154. package/dist/chunks/chunk-Q5VRWSKG.js +0 -418
  155. package/dist/chunks/chunk-XWRPOGJN.js +0 -4
  156. package/dist/chunks/chunk-Y5VIQBT6.js +0 -383
  157. package/dist/chunks/compaction-config-EGS4577X.js +0 -2
  158. package/dist/chunks/dist-ITTXQHRJ.js +0 -2
  159. package/dist/chunks/dist-KFYN3OKD.js +0 -2
  160. package/dist/chunks/host-VAYADIPW.js +0 -2
  161. package/dist/chunks/run-manager-52IJZMZJ.js +0 -2
  162. package/dist/chunks/utils-P7PBSL3R.js +0 -2
  163. package/dist/sandbox-workspace-session.js +0 -1705
@@ -1,406 +1,406 @@
1
- #!/usr/bin/env node
2
-
3
- import { readdir, readFile, writeFile } from 'node:fs/promises';
4
- import path from 'node:path';
5
- import { fileURLToPath } from 'node:url';
6
- import {
7
- computePassSummary,
8
- ensureDirectory,
9
- extractJsonObject,
10
- loadKodaXSDK,
11
- loadRelativeText,
12
- readJsonFile,
13
- truncateText,
14
- } from './utils.js';
15
-
16
- function normalizeExpectationText(value) {
17
- return String(value ?? '').trim();
18
- }
19
-
20
- function normalizeExpectedRubric(evalMetadata) {
21
- const assertions = Array.isArray(evalMetadata?.assertions) ? evalMetadata.assertions : [];
22
- const items = assertions
23
- .map((assertion) => {
24
- if (typeof assertion === 'string') {
25
- return normalizeExpectationText(assertion);
26
- }
27
- if (assertion && typeof assertion === 'object' && typeof assertion.text === 'string') {
28
- return normalizeExpectationText(assertion.text);
29
- }
30
- return '';
31
- })
32
- .filter(Boolean);
33
-
34
- if (items.length > 0) {
35
- return items;
36
- }
37
-
38
- const expectedOutput = normalizeExpectationText(evalMetadata?.expected_output);
39
- return expectedOutput ? [expectedOutput] : [];
40
- }
41
-
42
- function normalizeStringArray(value) {
43
- if (!Array.isArray(value)) {
44
- return [];
45
- }
46
- return value
47
- .map((item) => String(item ?? '').trim())
48
- .filter(Boolean);
49
- }
50
-
51
- function normalizeExpectationEntry(entry, fallbackText) {
52
- const text = normalizeExpectationText(entry?.text ?? fallbackText);
53
- return {
54
- text,
55
- passed: entry?.passed === true,
56
- evidence: normalizeExpectationText(entry?.evidence),
57
- };
58
- }
59
-
60
- function normalizeParsedGrading(rawText, rubric) {
61
- const parsed = extractJsonObject(rawText) ?? {};
62
- const returnedExpectations = Array.isArray(parsed.expectations) ? parsed.expectations : [];
63
- const normalizedExpectations = [];
64
- const consumed = new Set();
65
-
66
- for (const rubricText of rubric) {
67
- const matchIndex = returnedExpectations.findIndex((entry, index) => {
68
- if (consumed.has(index)) {
69
- return false;
70
- }
71
-
72
- const candidate = normalizeExpectationText(entry?.text);
73
- return candidate.toLowerCase() === rubricText.toLowerCase();
74
- });
75
-
76
- if (matchIndex === -1) {
77
- normalizedExpectations.push({
78
- text: rubricText,
79
- passed: false,
80
- evidence: '',
81
- });
82
- continue;
83
- }
84
-
85
- consumed.add(matchIndex);
86
- normalizedExpectations.push(normalizeExpectationEntry(returnedExpectations[matchIndex], rubricText));
87
- }
88
-
89
- if (rubric.length === 0) {
90
- for (const entry of returnedExpectations) {
91
- const normalized = normalizeExpectationEntry(entry, '');
92
- if (normalized.text) {
93
- normalizedExpectations.push(normalized);
94
- }
95
- }
96
- }
97
-
98
- return {
99
- overall_summary: normalizeExpectationText(parsed.overall_summary),
100
- expectations: normalizedExpectations,
101
- user_notes_summary: {
102
- uncertainties: normalizeStringArray(parsed.user_notes_summary?.uncertainties),
103
- needs_review: normalizeStringArray(parsed.user_notes_summary?.needs_review),
104
- workarounds: normalizeStringArray(parsed.user_notes_summary?.workarounds),
105
- },
106
- };
107
- }
108
-
109
- async function listDirectories(dirPath) {
110
- const entries = await readdir(dirPath, { withFileTypes: true }).catch(() => []);
111
- return entries
112
- .filter((entry) => entry.isDirectory())
113
- .map((entry) => path.join(dirPath, entry.name))
114
- .sort((left, right) => left.localeCompare(right));
115
- }
116
-
117
- async function loadRunContext(runDir) {
118
- const configDir = path.dirname(runDir);
119
- const evalDir = path.dirname(configDir);
120
- const configName = path.basename(configDir);
121
- const runId = path.basename(runDir);
122
- const outputsDir = path.join(runDir, 'outputs');
123
- const evalMetadata = await readJsonFile(path.join(evalDir, 'eval_metadata.json'), {});
124
- const resultText = await readFile(path.join(outputsDir, 'result.md'), 'utf8').catch(() => '');
125
- const promptText = await readFile(path.join(outputsDir, 'prompt.txt'), 'utf8').catch(() => '');
126
- const transcript = await readFile(path.join(runDir, 'transcript.md'), 'utf8').catch(() => '');
127
- const metrics = await readJsonFile(path.join(outputsDir, 'metrics.json'), {});
128
- const result = await readJsonFile(path.join(runDir, 'result.json'), {});
129
- const timing = await readJsonFile(path.join(runDir, 'timing.json'), {});
130
-
131
- return {
132
- evalDir,
133
- runDir,
134
- runId,
135
- configName,
136
- evalMetadata,
137
- outputsDir,
138
- resultText,
139
- promptText,
140
- transcript,
141
- metrics,
142
- result,
143
- timing,
144
- };
145
- }
146
-
147
- export function buildGradingPrompt(input) {
148
- const rubric = normalizeExpectedRubric(input.evalMetadata);
149
- const rubricText = rubric.length > 0
150
- ? rubric.map((item, index) => `${index + 1}. ${item}`).join('\n')
151
- : '(No explicit assertions; infer one expectation from expected_output if present.)';
152
-
153
- return `${input.agentInstructions.trim()}
154
-
155
- Return JSON with this shape:
156
- {
157
- "overall_summary": "short summary",
158
- "expectations": [
159
- {
160
- "text": "expectation text",
161
- "passed": true,
162
- "evidence": "short evidence grounded in the output"
163
- }
164
- ],
165
- "user_notes_summary": {
166
- "uncertainties": [],
167
- "needs_review": [],
168
- "workarounds": []
169
- }
170
- }
171
-
172
- ## Run Metadata
173
- - eval_id: ${input.evalMetadata.eval_id ?? 'unknown'}
174
- - eval_name: ${input.evalMetadata.eval_name ?? 'unknown'}
175
- - config: ${input.configName}
176
- - run_id: ${input.runId}
177
-
178
- ## Eval Prompt
179
- ${truncateText(input.evalMetadata.prompt ?? input.promptText, 4000)}
180
-
181
- ## Expected Outcome
182
- ${truncateText(input.evalMetadata.expected_output ?? '', 2000)}
183
-
184
- ## Assertions
185
- ${rubricText}
186
-
187
- ## Final Output
188
- ${truncateText(input.resultText, 12000)}
189
-
190
- ## Result Flags
191
- ${JSON.stringify({
192
- success: input.result.success ?? null,
193
- signal: input.result.signal ?? null,
194
- interrupted: input.result.interrupted ?? false,
195
- limit_reached: input.result.limit_reached ?? input.result.limitReached ?? false,
196
- }, null, 2)}
197
-
198
- ## Execution Metrics
199
- ${JSON.stringify({
200
- total_tool_calls: input.metrics.total_tool_calls ?? input.result.execution_metrics?.total_tool_calls ?? 0,
201
- errors_encountered: input.metrics.errors_encountered ?? input.result.execution_metrics?.errors_encountered ?? 0,
202
- output_chars: input.metrics.output_chars ?? input.result.execution_metrics?.output_chars ?? input.resultText.length,
203
- total_tokens: input.timing.total_tokens ?? 0,
204
- total_duration_seconds: input.timing.total_duration_seconds ?? 0,
205
- }, null, 2)}
206
-
207
- ## Transcript Excerpt
208
- ${truncateText(input.transcript, 5000)}
209
- `;
210
- }
211
-
212
- async function defaultRunJudge(prompt, options) {
213
- const { runKodaX } = await loadKodaXSDK();
214
- const result = await runKodaX(
215
- {
216
- provider: options.provider ?? 'anthropic',
217
- model: options.model,
218
- maxIter: options.maxIter ?? 20,
219
- reasoningMode: options.reasoningMode ?? 'balanced',
220
- thinking: options.reasoningMode ? options.reasoningMode !== 'off' : true,
221
- context: {
222
- gitRoot: path.resolve(options.cwd ?? options.workspaceDir ?? process.cwd()),
223
- },
224
- },
225
- prompt
226
- );
227
- return result.lastText;
228
- }
229
-
230
- export async function gradeRun(runDir, options, runner = defaultRunJudge) {
231
- const run = await loadRunContext(path.resolve(runDir));
232
- const rubric = normalizeExpectedRubric(run.evalMetadata);
233
- const agentInstructions = await loadRelativeText(import.meta.url, '../agents/grader.md');
234
- const prompt = buildGradingPrompt({
235
- ...run,
236
- agentInstructions,
237
- });
238
- const rawResponse = await runner(prompt, {
239
- ...options,
240
- runDir: run.runDir,
241
- configName: run.configName,
242
- evalMetadata: run.evalMetadata,
243
- runId: run.runId,
244
- });
245
- const parsed = normalizeParsedGrading(rawResponse, rubric);
246
- const executionMetrics = {
247
- total_tool_calls: run.metrics.total_tool_calls ?? run.result.execution_metrics?.total_tool_calls ?? 0,
248
- errors_encountered: run.metrics.errors_encountered ?? run.result.execution_metrics?.errors_encountered ?? 0,
249
- output_chars: run.metrics.output_chars ?? run.result.execution_metrics?.output_chars ?? run.resultText.length,
250
- };
251
-
252
- const grading = {
253
- summary: computePassSummary(parsed.expectations),
254
- expectations: parsed.expectations,
255
- execution_metrics: executionMetrics,
256
- user_notes_summary: parsed.user_notes_summary,
257
- overall_summary: parsed.overall_summary,
258
- timing: {
259
- total_tokens: run.timing.total_tokens ?? 0,
260
- total_duration_seconds: run.timing.total_duration_seconds ?? 0,
261
- },
262
- meta: {
263
- generated_at: new Date().toISOString(),
264
- eval_id: run.evalMetadata.eval_id ?? null,
265
- eval_name: run.evalMetadata.eval_name ?? null,
266
- config: run.configName,
267
- run_id: run.runId,
268
- },
269
- };
270
-
271
- await writeFile(
272
- path.join(run.runDir, 'grading.json'),
273
- `${JSON.stringify(grading, null, 2)}\n`,
274
- 'utf8'
275
- );
276
-
277
- return {
278
- runDir: run.runDir,
279
- grading,
280
- prompt,
281
- rawResponse,
282
- };
283
- }
284
-
285
- export async function gradeWorkspace(options, runner = defaultRunJudge) {
286
- const workspaceDir = path.resolve(options.workspaceDir);
287
- await ensureDirectory(workspaceDir);
288
-
289
- const configFilter = new Set(
290
- (options.configs ?? [])
291
- .map((item) => String(item).trim())
292
- .filter(Boolean)
293
- );
294
- const processedRuns = [];
295
- const skippedRuns = [];
296
-
297
- for (const evalDir of await listDirectories(workspaceDir)) {
298
- if (!path.basename(evalDir).startsWith('eval-')) {
299
- continue;
300
- }
301
-
302
- for (const configDir of await listDirectories(evalDir)) {
303
- const configName = path.basename(configDir);
304
- if (configFilter.size > 0 && !configFilter.has(configName)) {
305
- continue;
306
- }
307
-
308
- for (const runDir of await listDirectories(configDir)) {
309
- if (!path.basename(runDir).startsWith('run-')) {
310
- continue;
311
- }
312
-
313
- const gradingPath = path.join(runDir, 'grading.json');
314
- const existing = await readJsonFile(gradingPath, null);
315
- if (existing && options.overwrite !== true) {
316
- skippedRuns.push(path.relative(workspaceDir, runDir).replace(/\\/g, '/'));
317
- continue;
318
- }
319
-
320
- const graded = await gradeRun(runDir, options, runner);
321
- processedRuns.push({
322
- run: path.relative(workspaceDir, runDir).replace(/\\/g, '/'),
323
- summary: graded.grading.summary,
324
- config: graded.grading.meta.config,
325
- eval_id: graded.grading.meta.eval_id,
326
- });
327
- }
328
- }
329
- }
330
-
331
- const summary = {
332
- workspace: workspaceDir,
333
- generated_at: new Date().toISOString(),
334
- processed: processedRuns.length,
335
- skipped: skippedRuns.length,
336
- processed_runs: processedRuns,
337
- skipped_runs: skippedRuns,
338
- };
339
-
340
- await writeFile(
341
- path.join(workspaceDir, 'grading-summary.json'),
342
- `${JSON.stringify(summary, null, 2)}\n`,
343
- 'utf8'
344
- );
345
-
346
- return summary;
347
- }
348
-
349
- function parseArgs(argv) {
350
- const args = {
351
- workspaceDir: argv[2] ?? '',
352
- provider: 'anthropic',
353
- model: undefined,
354
- reasoningMode: 'balanced',
355
- maxIter: 20,
356
- cwd: process.cwd(),
357
- overwrite: false,
358
- configs: [],
359
- };
360
-
361
- for (let index = 3; index < argv.length; index += 1) {
362
- const token = argv[index];
363
- if (token === '--provider' && argv[index + 1]) {
364
- args.provider = argv[++index];
365
- } else if (token === '--model' && argv[index + 1]) {
366
- args.model = argv[++index];
367
- } else if (token === '--reasoning' && argv[index + 1]) {
368
- args.reasoningMode = argv[++index];
369
- } else if (token === '--max-iter' && argv[index + 1]) {
370
- args.maxIter = Number(argv[++index]);
371
- } else if (token === '--cwd' && argv[index + 1]) {
372
- args.cwd = argv[++index];
373
- } else if (token === '--overwrite') {
374
- args.overwrite = true;
375
- } else if (token === '--configs' && argv[index + 1]) {
376
- args.configs = argv[++index]
377
- .split(',')
378
- .map((item) => item.trim())
379
- .filter(Boolean);
380
- }
381
- }
382
-
383
- return args;
384
- }
385
-
1
+ #!/usr/bin/env node
2
+
3
+ import { readdir, readFile, writeFile } from 'node:fs/promises';
4
+ import path from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ import {
7
+ computePassSummary,
8
+ ensureDirectory,
9
+ extractJsonObject,
10
+ loadKodaXSDK,
11
+ loadRelativeText,
12
+ readJsonFile,
13
+ truncateText,
14
+ } from './utils.js';
15
+
16
+ function normalizeExpectationText(value) {
17
+ return String(value ?? '').trim();
18
+ }
19
+
20
+ function normalizeExpectedRubric(evalMetadata) {
21
+ const assertions = Array.isArray(evalMetadata?.assertions) ? evalMetadata.assertions : [];
22
+ const items = assertions
23
+ .map((assertion) => {
24
+ if (typeof assertion === 'string') {
25
+ return normalizeExpectationText(assertion);
26
+ }
27
+ if (assertion && typeof assertion === 'object' && typeof assertion.text === 'string') {
28
+ return normalizeExpectationText(assertion.text);
29
+ }
30
+ return '';
31
+ })
32
+ .filter(Boolean);
33
+
34
+ if (items.length > 0) {
35
+ return items;
36
+ }
37
+
38
+ const expectedOutput = normalizeExpectationText(evalMetadata?.expected_output);
39
+ return expectedOutput ? [expectedOutput] : [];
40
+ }
41
+
42
+ function normalizeStringArray(value) {
43
+ if (!Array.isArray(value)) {
44
+ return [];
45
+ }
46
+ return value
47
+ .map((item) => String(item ?? '').trim())
48
+ .filter(Boolean);
49
+ }
50
+
51
+ function normalizeExpectationEntry(entry, fallbackText) {
52
+ const text = normalizeExpectationText(entry?.text ?? fallbackText);
53
+ return {
54
+ text,
55
+ passed: entry?.passed === true,
56
+ evidence: normalizeExpectationText(entry?.evidence),
57
+ };
58
+ }
59
+
60
+ function normalizeParsedGrading(rawText, rubric) {
61
+ const parsed = extractJsonObject(rawText) ?? {};
62
+ const returnedExpectations = Array.isArray(parsed.expectations) ? parsed.expectations : [];
63
+ const normalizedExpectations = [];
64
+ const consumed = new Set();
65
+
66
+ for (const rubricText of rubric) {
67
+ const matchIndex = returnedExpectations.findIndex((entry, index) => {
68
+ if (consumed.has(index)) {
69
+ return false;
70
+ }
71
+
72
+ const candidate = normalizeExpectationText(entry?.text);
73
+ return candidate.toLowerCase() === rubricText.toLowerCase();
74
+ });
75
+
76
+ if (matchIndex === -1) {
77
+ normalizedExpectations.push({
78
+ text: rubricText,
79
+ passed: false,
80
+ evidence: '',
81
+ });
82
+ continue;
83
+ }
84
+
85
+ consumed.add(matchIndex);
86
+ normalizedExpectations.push(normalizeExpectationEntry(returnedExpectations[matchIndex], rubricText));
87
+ }
88
+
89
+ if (rubric.length === 0) {
90
+ for (const entry of returnedExpectations) {
91
+ const normalized = normalizeExpectationEntry(entry, '');
92
+ if (normalized.text) {
93
+ normalizedExpectations.push(normalized);
94
+ }
95
+ }
96
+ }
97
+
98
+ return {
99
+ overall_summary: normalizeExpectationText(parsed.overall_summary),
100
+ expectations: normalizedExpectations,
101
+ user_notes_summary: {
102
+ uncertainties: normalizeStringArray(parsed.user_notes_summary?.uncertainties),
103
+ needs_review: normalizeStringArray(parsed.user_notes_summary?.needs_review),
104
+ workarounds: normalizeStringArray(parsed.user_notes_summary?.workarounds),
105
+ },
106
+ };
107
+ }
108
+
109
+ async function listDirectories(dirPath) {
110
+ const entries = await readdir(dirPath, { withFileTypes: true }).catch(() => []);
111
+ return entries
112
+ .filter((entry) => entry.isDirectory())
113
+ .map((entry) => path.join(dirPath, entry.name))
114
+ .sort((left, right) => left.localeCompare(right));
115
+ }
116
+
117
+ async function loadRunContext(runDir) {
118
+ const configDir = path.dirname(runDir);
119
+ const evalDir = path.dirname(configDir);
120
+ const configName = path.basename(configDir);
121
+ const runId = path.basename(runDir);
122
+ const outputsDir = path.join(runDir, 'outputs');
123
+ const evalMetadata = await readJsonFile(path.join(evalDir, 'eval_metadata.json'), {});
124
+ const resultText = await readFile(path.join(outputsDir, 'result.md'), 'utf8').catch(() => '');
125
+ const promptText = await readFile(path.join(outputsDir, 'prompt.txt'), 'utf8').catch(() => '');
126
+ const transcript = await readFile(path.join(runDir, 'transcript.md'), 'utf8').catch(() => '');
127
+ const metrics = await readJsonFile(path.join(outputsDir, 'metrics.json'), {});
128
+ const result = await readJsonFile(path.join(runDir, 'result.json'), {});
129
+ const timing = await readJsonFile(path.join(runDir, 'timing.json'), {});
130
+
131
+ return {
132
+ evalDir,
133
+ runDir,
134
+ runId,
135
+ configName,
136
+ evalMetadata,
137
+ outputsDir,
138
+ resultText,
139
+ promptText,
140
+ transcript,
141
+ metrics,
142
+ result,
143
+ timing,
144
+ };
145
+ }
146
+
147
+ export function buildGradingPrompt(input) {
148
+ const rubric = normalizeExpectedRubric(input.evalMetadata);
149
+ const rubricText = rubric.length > 0
150
+ ? rubric.map((item, index) => `${index + 1}. ${item}`).join('\n')
151
+ : '(No explicit assertions; infer one expectation from expected_output if present.)';
152
+
153
+ return `${input.agentInstructions.trim()}
154
+
155
+ Return JSON with this shape:
156
+ {
157
+ "overall_summary": "short summary",
158
+ "expectations": [
159
+ {
160
+ "text": "expectation text",
161
+ "passed": true,
162
+ "evidence": "short evidence grounded in the output"
163
+ }
164
+ ],
165
+ "user_notes_summary": {
166
+ "uncertainties": [],
167
+ "needs_review": [],
168
+ "workarounds": []
169
+ }
170
+ }
171
+
172
+ ## Run Metadata
173
+ - eval_id: ${input.evalMetadata.eval_id ?? 'unknown'}
174
+ - eval_name: ${input.evalMetadata.eval_name ?? 'unknown'}
175
+ - config: ${input.configName}
176
+ - run_id: ${input.runId}
177
+
178
+ ## Eval Prompt
179
+ ${truncateText(input.evalMetadata.prompt ?? input.promptText, 4000)}
180
+
181
+ ## Expected Outcome
182
+ ${truncateText(input.evalMetadata.expected_output ?? '', 2000)}
183
+
184
+ ## Assertions
185
+ ${rubricText}
186
+
187
+ ## Final Output
188
+ ${truncateText(input.resultText, 12000)}
189
+
190
+ ## Result Flags
191
+ ${JSON.stringify({
192
+ success: input.result.success ?? null,
193
+ signal: input.result.signal ?? null,
194
+ interrupted: input.result.interrupted ?? false,
195
+ limit_reached: input.result.limit_reached ?? input.result.limitReached ?? false,
196
+ }, null, 2)}
197
+
198
+ ## Execution Metrics
199
+ ${JSON.stringify({
200
+ total_tool_calls: input.metrics.total_tool_calls ?? input.result.execution_metrics?.total_tool_calls ?? 0,
201
+ errors_encountered: input.metrics.errors_encountered ?? input.result.execution_metrics?.errors_encountered ?? 0,
202
+ output_chars: input.metrics.output_chars ?? input.result.execution_metrics?.output_chars ?? input.resultText.length,
203
+ total_tokens: input.timing.total_tokens ?? 0,
204
+ total_duration_seconds: input.timing.total_duration_seconds ?? 0,
205
+ }, null, 2)}
206
+
207
+ ## Transcript Excerpt
208
+ ${truncateText(input.transcript, 5000)}
209
+ `;
210
+ }
211
+
212
+ async function defaultRunJudge(prompt, options) {
213
+ const { runKodaX } = await loadKodaXSDK();
214
+ const result = await runKodaX(
215
+ {
216
+ provider: options.provider ?? 'anthropic',
217
+ model: options.model,
218
+ maxIter: options.maxIter ?? 20,
219
+ reasoningMode: options.reasoningMode ?? 'balanced',
220
+ thinking: options.reasoningMode ? options.reasoningMode !== 'off' : true,
221
+ context: {
222
+ gitRoot: path.resolve(options.cwd ?? options.workspaceDir ?? process.cwd()),
223
+ },
224
+ },
225
+ prompt
226
+ );
227
+ return result.lastText;
228
+ }
229
+
230
+ export async function gradeRun(runDir, options, runner = defaultRunJudge) {
231
+ const run = await loadRunContext(path.resolve(runDir));
232
+ const rubric = normalizeExpectedRubric(run.evalMetadata);
233
+ const agentInstructions = await loadRelativeText(import.meta.url, '../agents/grader.md');
234
+ const prompt = buildGradingPrompt({
235
+ ...run,
236
+ agentInstructions,
237
+ });
238
+ const rawResponse = await runner(prompt, {
239
+ ...options,
240
+ runDir: run.runDir,
241
+ configName: run.configName,
242
+ evalMetadata: run.evalMetadata,
243
+ runId: run.runId,
244
+ });
245
+ const parsed = normalizeParsedGrading(rawResponse, rubric);
246
+ const executionMetrics = {
247
+ total_tool_calls: run.metrics.total_tool_calls ?? run.result.execution_metrics?.total_tool_calls ?? 0,
248
+ errors_encountered: run.metrics.errors_encountered ?? run.result.execution_metrics?.errors_encountered ?? 0,
249
+ output_chars: run.metrics.output_chars ?? run.result.execution_metrics?.output_chars ?? run.resultText.length,
250
+ };
251
+
252
+ const grading = {
253
+ summary: computePassSummary(parsed.expectations),
254
+ expectations: parsed.expectations,
255
+ execution_metrics: executionMetrics,
256
+ user_notes_summary: parsed.user_notes_summary,
257
+ overall_summary: parsed.overall_summary,
258
+ timing: {
259
+ total_tokens: run.timing.total_tokens ?? 0,
260
+ total_duration_seconds: run.timing.total_duration_seconds ?? 0,
261
+ },
262
+ meta: {
263
+ generated_at: new Date().toISOString(),
264
+ eval_id: run.evalMetadata.eval_id ?? null,
265
+ eval_name: run.evalMetadata.eval_name ?? null,
266
+ config: run.configName,
267
+ run_id: run.runId,
268
+ },
269
+ };
270
+
271
+ await writeFile(
272
+ path.join(run.runDir, 'grading.json'),
273
+ `${JSON.stringify(grading, null, 2)}\n`,
274
+ 'utf8'
275
+ );
276
+
277
+ return {
278
+ runDir: run.runDir,
279
+ grading,
280
+ prompt,
281
+ rawResponse,
282
+ };
283
+ }
284
+
285
+ export async function gradeWorkspace(options, runner = defaultRunJudge) {
286
+ const workspaceDir = path.resolve(options.workspaceDir);
287
+ await ensureDirectory(workspaceDir);
288
+
289
+ const configFilter = new Set(
290
+ (options.configs ?? [])
291
+ .map((item) => String(item).trim())
292
+ .filter(Boolean)
293
+ );
294
+ const processedRuns = [];
295
+ const skippedRuns = [];
296
+
297
+ for (const evalDir of await listDirectories(workspaceDir)) {
298
+ if (!path.basename(evalDir).startsWith('eval-')) {
299
+ continue;
300
+ }
301
+
302
+ for (const configDir of await listDirectories(evalDir)) {
303
+ const configName = path.basename(configDir);
304
+ if (configFilter.size > 0 && !configFilter.has(configName)) {
305
+ continue;
306
+ }
307
+
308
+ for (const runDir of await listDirectories(configDir)) {
309
+ if (!path.basename(runDir).startsWith('run-')) {
310
+ continue;
311
+ }
312
+
313
+ const gradingPath = path.join(runDir, 'grading.json');
314
+ const existing = await readJsonFile(gradingPath, null);
315
+ if (existing && options.overwrite !== true) {
316
+ skippedRuns.push(path.relative(workspaceDir, runDir).replace(/\\/g, '/'));
317
+ continue;
318
+ }
319
+
320
+ const graded = await gradeRun(runDir, options, runner);
321
+ processedRuns.push({
322
+ run: path.relative(workspaceDir, runDir).replace(/\\/g, '/'),
323
+ summary: graded.grading.summary,
324
+ config: graded.grading.meta.config,
325
+ eval_id: graded.grading.meta.eval_id,
326
+ });
327
+ }
328
+ }
329
+ }
330
+
331
+ const summary = {
332
+ workspace: workspaceDir,
333
+ generated_at: new Date().toISOString(),
334
+ processed: processedRuns.length,
335
+ skipped: skippedRuns.length,
336
+ processed_runs: processedRuns,
337
+ skipped_runs: skippedRuns,
338
+ };
339
+
340
+ await writeFile(
341
+ path.join(workspaceDir, 'grading-summary.json'),
342
+ `${JSON.stringify(summary, null, 2)}\n`,
343
+ 'utf8'
344
+ );
345
+
346
+ return summary;
347
+ }
348
+
349
+ function parseArgs(argv) {
350
+ const args = {
351
+ workspaceDir: argv[2] ?? '',
352
+ provider: 'anthropic',
353
+ model: undefined,
354
+ reasoningMode: 'balanced',
355
+ maxIter: 20,
356
+ cwd: process.cwd(),
357
+ overwrite: false,
358
+ configs: [],
359
+ };
360
+
361
+ for (let index = 3; index < argv.length; index += 1) {
362
+ const token = argv[index];
363
+ if (token === '--provider' && argv[index + 1]) {
364
+ args.provider = argv[++index];
365
+ } else if (token === '--model' && argv[index + 1]) {
366
+ args.model = argv[++index];
367
+ } else if (token === '--reasoning' && argv[index + 1]) {
368
+ args.reasoningMode = argv[++index];
369
+ } else if (token === '--max-iter' && argv[index + 1]) {
370
+ args.maxIter = Number(argv[++index]);
371
+ } else if (token === '--cwd' && argv[index + 1]) {
372
+ args.cwd = argv[++index];
373
+ } else if (token === '--overwrite') {
374
+ args.overwrite = true;
375
+ } else if (token === '--configs' && argv[index + 1]) {
376
+ args.configs = argv[++index]
377
+ .split(',')
378
+ .map((item) => item.trim())
379
+ .filter(Boolean);
380
+ }
381
+ }
382
+
383
+ return args;
384
+ }
385
+
386
386
  export async function main(argv = process.argv) {
387
387
  const args = parseArgs(argv);
388
- if (!args.workspaceDir) {
389
- console.error('Usage: node scripts/grade-evals.js <workspace> [--provider anthropic] [--configs with_skill,without_skill] [--overwrite]');
390
- process.exit(1);
391
- }
392
-
393
- const summary = await gradeWorkspace(args);
394
- process.stdout.write(`${JSON.stringify(summary, null, 2)}\n`);
395
- }
396
-
388
+ if (!args.workspaceDir) {
389
+ console.error('Usage: node scripts/grade-evals.js <workspace> [--provider anthropic] [--configs with_skill,without_skill] [--overwrite]');
390
+ process.exit(1);
391
+ }
392
+
393
+ const summary = await gradeWorkspace(args);
394
+ process.stdout.write(`${JSON.stringify(summary, null, 2)}\n`);
395
+ }
396
+
397
397
  const isDirectRun = process.env.KODAX_MODULE_BUNDLE !== 'true'
398
398
  && process.argv[1]
399
- && fileURLToPath(import.meta.url) === path.resolve(process.argv[1]);
400
-
401
- if (isDirectRun) {
402
- main().catch((error) => {
403
- console.error(error instanceof Error ? error.message : String(error));
404
- process.exit(1);
405
- });
406
- }
399
+ && fileURLToPath(import.meta.url) === path.resolve(process.argv[1]);
400
+
401
+ if (isDirectRun) {
402
+ main().catch((error) => {
403
+ console.error(error instanceof Error ? error.message : String(error));
404
+ process.exit(1);
405
+ });
406
+ }