claude-flow 3.10.4 → 3.10.5

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 (58) hide show
  1. package/package.json +1 -1
  2. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-agent-planning.smoke.d.ts +18 -0
  3. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-agent-planning.smoke.js +253 -0
  4. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-agent.d.ts +198 -0
  5. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-agent.js +651 -0
  6. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-causal-memory.d.ts +133 -0
  7. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-causal-memory.js +281 -0
  8. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-causal-memory.smoke.d.ts +22 -0
  9. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-causal-memory.smoke.js +300 -0
  10. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-convergence.d.ts +138 -0
  11. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-convergence.js +260 -0
  12. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-convergence.smoke.d.ts +19 -0
  13. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-convergence.smoke.js +246 -0
  14. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-critic.d.ts +123 -0
  15. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-critic.js +312 -0
  16. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-critic.smoke.d.ts +21 -0
  17. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-critic.smoke.js +327 -0
  18. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-decomposer.d.ts +125 -0
  19. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-decomposer.js +350 -0
  20. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-decomposer.smoke.d.ts +21 -0
  21. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-decomposer.smoke.js +228 -0
  22. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-e2e-smoke.d.ts +27 -0
  23. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-e2e-smoke.js +136 -0
  24. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-extract.smoke.d.ts +45 -0
  25. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-extract.smoke.js +242 -0
  26. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-hardness/features.d.ts +46 -0
  27. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-hardness/features.js +170 -0
  28. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-hardness/predictor.d.ts +105 -0
  29. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-hardness/predictor.js +260 -0
  30. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-hardness/predictor.smoke.d.ts +20 -0
  31. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-hardness/predictor.smoke.js +235 -0
  32. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-hardness/train-data-loader.d.ts +51 -0
  33. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-hardness/train-data-loader.js +179 -0
  34. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-judge.d.ts +88 -0
  35. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-judge.js +437 -0
  36. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-loader.d.ts +87 -0
  37. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-loader.js +326 -0
  38. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-tools/file_read.d.ts +35 -0
  39. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-tools/file_read.js +403 -0
  40. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-tools/grounded_query.d.ts +126 -0
  41. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-tools/grounded_query.js +225 -0
  42. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-tools/index.d.ts +32 -0
  43. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-tools/index.js +36 -0
  44. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-tools/types.d.ts +62 -0
  45. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-tools/types.js +12 -0
  46. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-tools/web_search.d.ts +30 -0
  47. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-tools/web_search.js +210 -0
  48. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-voting.d.ts +88 -0
  49. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-voting.js +297 -0
  50. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-voting.smoke.d.ts +20 -0
  51. package/v3/@claude-flow/cli/dist/src/benchmarks/gaia-voting.smoke.js +332 -0
  52. package/v3/@claude-flow/cli/dist/src/commands/gaia-bench.d.ts +40 -0
  53. package/v3/@claude-flow/cli/dist/src/commands/gaia-bench.js +597 -0
  54. package/v3/@claude-flow/cli/dist/src/commands/index.js +2 -0
  55. package/v3/@claude-flow/cli/dist/src/commands/init.js +41 -4
  56. package/v3/@claude-flow/cli/dist/src/init/executor.js +25 -6
  57. package/v3/@claude-flow/cli/dist/src/init/mcp-generator.js +9 -4
  58. package/v3/@claude-flow/cli/package.json +1 -1
@@ -0,0 +1,228 @@
1
+ /**
2
+ * Smoke tests for gaia-decomposer.ts (ADR-135 Track E)
3
+ *
4
+ * All HTTP calls are mocked — no live API calls, no cost.
5
+ * Covers 7 assertions:
6
+ * 1. Atomic question → decomposed=false, single sub-question = original
7
+ * 2. 3-step question → decomposed=true, 3 sub-questions in dependency order
8
+ * 3. Malformed JSON response → fallback to atomic (graceful degradation)
9
+ * 4. API error → fallback to atomic (graceful degradation)
10
+ * 5. synthesizeFromSubAnswers — atomic question returns sub-answer directly (no API call)
11
+ * 6. synthesizeFromSubAnswers — valid JSON response → finalAnswer + reasoning
12
+ * 7. synthesizeFromSubAnswers — malformed JSON → fallback to last sub-answer
13
+ *
14
+ * Run with:
15
+ * npx tsx src/benchmarks/gaia-decomposer.smoke.ts
16
+ *
17
+ * Expected cost: $0 (all mocked).
18
+ */
19
+ import { decomposeQuestion, synthesizeFromSubAnswers } from './gaia-decomposer.js';
20
+ function installFetchMock(responder) {
21
+ globalThis.fetch = (url, init) => {
22
+ const r = responder(url, init);
23
+ return Promise.resolve(r);
24
+ };
25
+ }
26
+ function mockSuccessResponse(jsonBody) {
27
+ const bodyStr = JSON.stringify(jsonBody);
28
+ return {
29
+ ok: true,
30
+ status: 200,
31
+ text: () => Promise.resolve(bodyStr),
32
+ json: () => Promise.resolve(jsonBody),
33
+ };
34
+ }
35
+ function mockErrorResponse(status) {
36
+ return {
37
+ ok: false,
38
+ status,
39
+ text: () => Promise.resolve(`HTTP ${status} error`),
40
+ json: () => Promise.reject(new Error('error response has no JSON')),
41
+ };
42
+ }
43
+ /** Build a minimal Anthropic Messages API response shape. */
44
+ function anthropicTextResponse(text, inputTokens = 100, outputTokens = 50) {
45
+ return {
46
+ id: 'msg_test',
47
+ type: 'message',
48
+ role: 'assistant',
49
+ content: [{ type: 'text', text }],
50
+ model: 'claude-haiku-4-5',
51
+ stop_reason: 'end_turn',
52
+ usage: { input_tokens: inputTokens, output_tokens: outputTokens },
53
+ };
54
+ }
55
+ // ---------------------------------------------------------------------------
56
+ // Test runner
57
+ // ---------------------------------------------------------------------------
58
+ const PASS = '\x1b[32mPASS\x1b[0m';
59
+ const FAIL = '\x1b[31mFAIL\x1b[0m';
60
+ let failures = 0;
61
+ let passed = 0;
62
+ function check(label, condition) {
63
+ if (condition) {
64
+ console.log(` ${PASS} ${label}`);
65
+ passed++;
66
+ }
67
+ else {
68
+ console.log(` ${FAIL} ${label}`);
69
+ failures++;
70
+ }
71
+ }
72
+ // ---------------------------------------------------------------------------
73
+ // Smoke tests
74
+ // ---------------------------------------------------------------------------
75
+ async function runSmoke() {
76
+ console.log('\n=== gaia-decomposer smoke (mocked, $0) ===\n');
77
+ // Set a fake API key so resolveApiKey() doesn't throw
78
+ process.env.ANTHROPIC_API_KEY = 'sk-ant-test-key';
79
+ // ─────────────────────────────────────────────────────────────────────────
80
+ // Test 1: Atomic question → decomposed=false, single sub-question
81
+ // ─────────────────────────────────────────────────────────────────────────
82
+ console.log('-- Test 1: Atomic question --');
83
+ {
84
+ const atomicPayload = {
85
+ decomposed: false,
86
+ subQuestions: ['What year was the Eiffel Tower built?'],
87
+ synthesisHint: 'Use directly.',
88
+ };
89
+ installFetchMock(() => mockSuccessResponse(anthropicTextResponse(JSON.stringify(atomicPayload))));
90
+ const result = await decomposeQuestion('What year was the Eiffel Tower built?');
91
+ check('decomposed=false for atomic question', result.decomposed === false);
92
+ check('single sub-question equals original', result.subQuestions.length === 1 && result.subQuestions[0] === 'What year was the Eiffel Tower built?');
93
+ check('cost > 0 (Haiku tokens consumed)', result.cost > 0);
94
+ }
95
+ // ─────────────────────────────────────────────────────────────────────────
96
+ // Test 2: 3-step complex question → decomposed=true, 3 sub-questions in order
97
+ // ─────────────────────────────────────────────────────────────────────────
98
+ console.log('\n-- Test 2: 3-step complex question --');
99
+ {
100
+ const complexPayload = {
101
+ decomposed: true,
102
+ subQuestions: [
103
+ 'What year was the Eiffel Tower built?',
104
+ 'What decade contains the year 1889?',
105
+ 'Who directed the highest-grossing film of the 1880s?',
106
+ ],
107
+ synthesisHint: 'The answer to sub-question 3 is the final answer.',
108
+ };
109
+ installFetchMock(() => mockSuccessResponse(anthropicTextResponse(JSON.stringify(complexPayload))));
110
+ const question = 'Who directed the highest-grossing film of the decade the Eiffel Tower was built?';
111
+ const result = await decomposeQuestion(question);
112
+ check('decomposed=true for 3-step question', result.decomposed === true);
113
+ check('3 sub-questions returned', result.subQuestions.length === 3);
114
+ check('first sub-question asks about Eiffel Tower year', result.subQuestions[0].toLowerCase().includes('eiffel tower'));
115
+ check('synthesisHint is non-empty', result.synthesisHint.length > 0);
116
+ }
117
+ // ─────────────────────────────────────────────────────────────────────────
118
+ // Test 3: Malformed JSON response → fallback to atomic
119
+ // ─────────────────────────────────────────────────────────────────────────
120
+ console.log('\n-- Test 3: Malformed JSON fallback --');
121
+ {
122
+ installFetchMock(() => mockSuccessResponse(anthropicTextResponse('This is not JSON at all { broken')));
123
+ const question = 'What is the capital of France?';
124
+ const result = await decomposeQuestion(question);
125
+ check('malformed JSON → decomposed=false', result.decomposed === false);
126
+ check('malformed JSON → sub-question is original', result.subQuestions[0] === question);
127
+ }
128
+ // ─────────────────────────────────────────────────────────────────────────
129
+ // Test 4: API error → fallback to atomic
130
+ // ─────────────────────────────────────────────────────────────────────────
131
+ console.log('\n-- Test 4: API error fallback --');
132
+ {
133
+ installFetchMock(() => mockErrorResponse(500));
134
+ const question = 'How many planets are in the solar system?';
135
+ const result = await decomposeQuestion(question);
136
+ check('API error → decomposed=false', result.decomposed === false);
137
+ check('API error → sub-question is original', result.subQuestions[0] === question);
138
+ check('API error → cost=0', result.cost === 0);
139
+ }
140
+ // ─────────────────────────────────────────────────────────────────────────
141
+ // Test 5: synthesizeFromSubAnswers — atomic question (decomposed=false)
142
+ // returns sub-answer directly WITHOUT an API call
143
+ // ─────────────────────────────────────────────────────────────────────────
144
+ console.log('\n-- Test 5: synthesize — atomic passthrough (no API call) --');
145
+ {
146
+ // Install a fetch mock that throws if called — should NOT be called
147
+ let fetchCalled = false;
148
+ installFetchMock(() => {
149
+ fetchCalled = true;
150
+ return mockSuccessResponse({ finalAnswer: 'unexpected', reasoning: 'should not reach here' });
151
+ });
152
+ const atomicDecomposed = {
153
+ originalQuestion: 'What year was the Eiffel Tower built?',
154
+ subQuestions: ['What year was the Eiffel Tower built?'],
155
+ synthesisHint: 'Use directly.',
156
+ decomposed: false,
157
+ cost: 0,
158
+ };
159
+ const result = await synthesizeFromSubAnswers(atomicDecomposed, ['1889']);
160
+ check('atomic synthesize returns sub-answer directly', result.finalAnswer === '1889');
161
+ check('atomic synthesize has cost=0', result.cost === 0);
162
+ check('atomic synthesize did NOT call API', fetchCalled === false);
163
+ }
164
+ // ─────────────────────────────────────────────────────────────────────────
165
+ // Test 6: synthesizeFromSubAnswers — valid JSON → finalAnswer + reasoning
166
+ // ─────────────────────────────────────────────────────────────────────────
167
+ console.log('\n-- Test 6: synthesize — valid JSON response --');
168
+ {
169
+ const synthPayload = {
170
+ finalAnswer: 'Georges Méliès',
171
+ reasoning: 'The highest-grossing film of the 1880s was X. Its director was Georges Méliès.',
172
+ };
173
+ installFetchMock(() => mockSuccessResponse(anthropicTextResponse(JSON.stringify(synthPayload))));
174
+ const decomposed = {
175
+ originalQuestion: 'Who directed the highest-grossing film of the decade the Eiffel Tower was built?',
176
+ subQuestions: [
177
+ 'What year was the Eiffel Tower built?',
178
+ 'What decade contains 1889?',
179
+ 'Who directed the highest-grossing film of the 1880s?',
180
+ ],
181
+ synthesisHint: 'The director from sub-question 3 is the answer.',
182
+ decomposed: true,
183
+ cost: 0.0003,
184
+ };
185
+ const result = await synthesizeFromSubAnswers(decomposed, ['1889', '1880s', 'Georges Méliès']);
186
+ check('synthesize returns correct finalAnswer', result.finalAnswer === 'Georges Méliès');
187
+ check('synthesize reasoning is non-empty', result.reasoning.length > 0);
188
+ check('synthesize cost > 0 (Sonnet tokens consumed)', result.cost > 0);
189
+ }
190
+ // ─────────────────────────────────────────────────────────────────────────
191
+ // Test 7: synthesizeFromSubAnswers — malformed JSON → fallback to last sub-answer
192
+ // ─────────────────────────────────────────────────────────────────────────
193
+ console.log('\n-- Test 7: synthesize — malformed JSON fallback --');
194
+ {
195
+ installFetchMock(() => mockSuccessResponse(anthropicTextResponse('{ bad json here ]]}')));
196
+ const decomposed = {
197
+ originalQuestion: 'How many people live in Tokyo?',
198
+ subQuestions: [
199
+ 'What is the population of Tokyo metropolitan area?',
200
+ 'What is the population of Tokyo proper?',
201
+ ],
202
+ synthesisHint: 'Use the Tokyo proper figure.',
203
+ decomposed: true,
204
+ cost: 0.0003,
205
+ };
206
+ const result = await synthesizeFromSubAnswers(decomposed, ['37.4 million (metropolitan)', '13.96 million (proper)']);
207
+ check('malformed JSON fallback → returns last sub-answer', result.finalAnswer === '13.96 million (proper)');
208
+ check('malformed JSON fallback → reasoning mentions parse error', result.reasoning.toLowerCase().includes('parse error'));
209
+ }
210
+ // ─────────────────────────────────────────────────────────────────────────
211
+ // Summary
212
+ // ─────────────────────────────────────────────────────────────────────────
213
+ const total = passed + failures;
214
+ console.log(`\n=== smoke ${failures === 0 ? 'PASSED' : `FAILED (${failures}/${total} assertion(s))`} — ${passed}/${total} passed ===\n`);
215
+ if (failures > 0)
216
+ process.exit(1);
217
+ }
218
+ // Run when executed directly
219
+ const isMain = process.argv[1] && (process.argv[1].endsWith('gaia-decomposer.smoke.ts') ||
220
+ process.argv[1].endsWith('gaia-decomposer.smoke.js'));
221
+ if (isMain) {
222
+ runSmoke().catch((err) => {
223
+ console.error('Smoke failed with exception:', err);
224
+ process.exit(1);
225
+ });
226
+ }
227
+ export { runSmoke };
228
+ //# sourceMappingURL=gaia-decomposer.smoke.js.map
@@ -0,0 +1,27 @@
1
+ /**
2
+ * GAIA End-to-End Smoke — ADR-133
3
+ *
4
+ * Wires gaia-agent.ts + gaia-judge.ts into a single end-to-end pipeline:
5
+ *
6
+ * for each question in SMOKE_FIXTURE:
7
+ * 1. runGaiaAgent(question) — Haiku agent loop, ≤8 turns
8
+ * 2. judgeAnswer(question, result.finalAnswer) — exact-match fast-path,
9
+ * Sonnet LLM-judge only if exact-match misses
10
+ *
11
+ * Reports: pass rate, total cost, mean turn count.
12
+ * Asserts: ≥ 3/5 questions pass (lenient — smoke fixture is not trivial).
13
+ *
14
+ * Cost discipline:
15
+ * - Agent: claude-haiku-4-5 at $0.25/$1.25 per M tokens
16
+ * - Judge: claude-sonnet-4-6 at $3/$15 per M tokens (only when needed)
17
+ * - Expected total for 5 questions × ~2 turns × Haiku + 1-2 Sonnet
18
+ * judge calls ≈ $0.02
19
+ *
20
+ * Usage:
21
+ * ANTHROPIC_API_KEY=sk-ant-... npx tsx src/benchmarks/gaia-e2e-smoke.ts
22
+ *
23
+ * Refs: ADR-133, #2156
24
+ */
25
+ declare function runE2ESmoke(): Promise<void>;
26
+ export { runE2ESmoke };
27
+ //# sourceMappingURL=gaia-e2e-smoke.d.ts.map
@@ -0,0 +1,136 @@
1
+ /**
2
+ * GAIA End-to-End Smoke — ADR-133
3
+ *
4
+ * Wires gaia-agent.ts + gaia-judge.ts into a single end-to-end pipeline:
5
+ *
6
+ * for each question in SMOKE_FIXTURE:
7
+ * 1. runGaiaAgent(question) — Haiku agent loop, ≤8 turns
8
+ * 2. judgeAnswer(question, result.finalAnswer) — exact-match fast-path,
9
+ * Sonnet LLM-judge only if exact-match misses
10
+ *
11
+ * Reports: pass rate, total cost, mean turn count.
12
+ * Asserts: ≥ 3/5 questions pass (lenient — smoke fixture is not trivial).
13
+ *
14
+ * Cost discipline:
15
+ * - Agent: claude-haiku-4-5 at $0.25/$1.25 per M tokens
16
+ * - Judge: claude-sonnet-4-6 at $3/$15 per M tokens (only when needed)
17
+ * - Expected total for 5 questions × ~2 turns × Haiku + 1-2 Sonnet
18
+ * judge calls ≈ $0.02
19
+ *
20
+ * Usage:
21
+ * ANTHROPIC_API_KEY=sk-ant-... npx tsx src/benchmarks/gaia-e2e-smoke.ts
22
+ *
23
+ * Refs: ADR-133, #2156
24
+ */
25
+ import * as os from 'node:os';
26
+ import * as path from 'node:path';
27
+ import { SMOKE_FIXTURE, } from './gaia-loader.js';
28
+ import { runGaiaAgent, } from './gaia-agent.js';
29
+ import { judgeAnswer, } from './gaia-judge.js';
30
+ // ---------------------------------------------------------------------------
31
+ // Configuration
32
+ // ---------------------------------------------------------------------------
33
+ /** Agent model — Haiku only for cost discipline. */
34
+ const AGENT_MODEL = 'claude-haiku-4-5';
35
+ /** Judge model — Sonnet for semantic judgments (only when exact-match fails). */
36
+ const JUDGE_MODEL = 'claude-sonnet-4-6';
37
+ /** Minimum pass rate for smoke to succeed. */
38
+ const MIN_PASS_RATE = 3 / 5;
39
+ // Haiku pricing ($/M tokens)
40
+ const HAIKU_IN = 0.25;
41
+ const HAIKU_OUT = 1.25;
42
+ // Sonnet pricing ($/M tokens)
43
+ const SONNET_IN = 3.0;
44
+ const SONNET_OUT = 15.0;
45
+ // ---------------------------------------------------------------------------
46
+ // Runner
47
+ // ---------------------------------------------------------------------------
48
+ async function runE2ESmoke() {
49
+ const hasKey = !!(process.env.ANTHROPIC_API_KEY?.trim());
50
+ if (!hasKey) {
51
+ console.error('ANTHROPIC_API_KEY is required for the end-to-end smoke.\n' +
52
+ 'Set it with: export ANTHROPIC_API_KEY=sk-ant-...');
53
+ process.exit(1);
54
+ }
55
+ const cacheDir = path.join(os.homedir(), '.cache', 'ruflo', 'gaia', 'judgments');
56
+ const judgeOpts = { judgeModel: JUDGE_MODEL, cacheDir };
57
+ const questions = SMOKE_FIXTURE;
58
+ const rows = [];
59
+ console.log(`\n=== GAIA End-to-End Smoke (${questions.length} questions) ===\n`);
60
+ console.log(`Agent: ${AGENT_MODEL} | Judge: ${JUDGE_MODEL}\n` +
61
+ `Questions: ${questions.length} | Min pass rate: ${(MIN_PASS_RATE * 100).toFixed(0)}%\n`);
62
+ for (const q of questions) {
63
+ process.stdout.write(`[${q.task_id}] "${q.question.slice(0, 60)}..." `);
64
+ // Run agent
65
+ const agentResult = await runGaiaAgent(q, { model: AGENT_MODEL });
66
+ // Judge
67
+ const judgeResult = await judgeAnswer({ id: q.task_id, expected: q.final_answer, questionText: q.question }, agentResult.finalAnswer, judgeOpts);
68
+ rows.push({ question: q, agentResult, judgeResult });
69
+ const verdict = judgeResult.passed ? '\x1b[32mPASS\x1b[0m' : '\x1b[31mFAIL\x1b[0m';
70
+ const path_ = judgeResult.scoringPath;
71
+ console.log(`${verdict} (turns=${agentResult.turns}, ` +
72
+ `answer="${agentResult.finalAnswer ?? 'null'}", ` +
73
+ `expected="${q.final_answer}", path=${path_})`);
74
+ }
75
+ // ── Summary ──
76
+ console.log('\n--- Summary ---\n');
77
+ const passed = rows.filter((r) => r.judgeResult.passed).length;
78
+ const total = rows.length;
79
+ const passRate = passed / total;
80
+ let totalAgentCostUsd = 0;
81
+ let totalJudgeCostUsd = 0;
82
+ let totalTurns = 0;
83
+ for (const row of rows) {
84
+ const { agentResult, judgeResult } = row;
85
+ totalTurns += agentResult.turns;
86
+ totalAgentCostUsd +=
87
+ (agentResult.totalInputTokens / 1_000_000) * HAIKU_IN +
88
+ (agentResult.totalOutputTokens / 1_000_000) * HAIKU_OUT;
89
+ totalJudgeCostUsd += judgeResult.judgeCostUsd ?? 0;
90
+ }
91
+ const totalCostUsd = totalAgentCostUsd + totalJudgeCostUsd;
92
+ const meanTurns = totalTurns / total;
93
+ console.log(`Pass rate : ${passed}/${total} (${(passRate * 100).toFixed(0)}%)`);
94
+ console.log(`Mean turns : ${meanTurns.toFixed(1)}`);
95
+ console.log(`Agent cost : $${totalAgentCostUsd.toFixed(5)} (Haiku)`);
96
+ console.log(`Judge cost : $${totalJudgeCostUsd.toFixed(5)} (Sonnet, only when needed)`);
97
+ console.log(`Total cost : $${totalCostUsd.toFixed(5)}`);
98
+ // ── Per-row detail ──
99
+ console.log('\n--- Per-question detail ---\n');
100
+ for (const row of rows) {
101
+ const { question, agentResult, judgeResult } = row;
102
+ const verdict = judgeResult.passed ? 'PASS' : 'FAIL';
103
+ console.log(` ${verdict} ${question.task_id} turns=${agentResult.turns} ` +
104
+ `path=${judgeResult.scoringPath} ` +
105
+ `answer="${agentResult.finalAnswer ?? 'null'}" ` +
106
+ `expected="${question.final_answer}"`);
107
+ if (judgeResult.judgeReason) {
108
+ console.log(` reason: ${judgeResult.judgeReason}`);
109
+ }
110
+ if (agentResult.error) {
111
+ console.log(` error : ${agentResult.error}`);
112
+ }
113
+ }
114
+ // ── Assertion ──
115
+ console.log('');
116
+ if (passRate >= MIN_PASS_RATE) {
117
+ console.log(`\x1b[32mSmoke PASSED\x1b[0m — ${passed}/${total} ≥ ${(MIN_PASS_RATE * 100).toFixed(0)}% required.\n`);
118
+ }
119
+ else {
120
+ console.error(`\x1b[31mSmoke FAILED\x1b[0m — ${passed}/${total} < ${(MIN_PASS_RATE * 100).toFixed(0)}% required.\n`);
121
+ process.exit(1);
122
+ }
123
+ }
124
+ // ---------------------------------------------------------------------------
125
+ // Entry point
126
+ // ---------------------------------------------------------------------------
127
+ const isMain = process.argv[1] && (process.argv[1].endsWith('gaia-e2e-smoke.ts') ||
128
+ process.argv[1].endsWith('gaia-e2e-smoke.js'));
129
+ if (isMain) {
130
+ runE2ESmoke().catch((err) => {
131
+ console.error('E2E smoke failed:', err);
132
+ process.exit(1);
133
+ });
134
+ }
135
+ export { runE2ESmoke };
136
+ //# sourceMappingURL=gaia-e2e-smoke.js.map
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Smoke tests for extractFinalAnswer + buildUserMessage (iter 53a T2 narrowed).
3
+ *
4
+ * Iter 53a changes vs iter 52 T2:
5
+ * - Stage 2/3 (prose fallback) REMOVED — extractFinalAnswer is Stage 1 only.
6
+ * - Commitment prompt no longer has "FINAL_ANSWER: unknown" surrender instruction.
7
+ * - Reversed-text preprocessor PRESERVED (not the source of regressions).
8
+ *
9
+ * Anti-regression suite (7 cases):
10
+ * These are the exact failure modes from iter 52 T2 that must not regress.
11
+ * They test that the extraction logic is stable and correct for the 7 questions
12
+ * where iter 52 broke but iter 51 was correct.
13
+ *
14
+ * Run (after build):
15
+ * node dist/benchmarks/gaia-extract.smoke.js
16
+ *
17
+ * Exit 0 on all pass, 1 on any failure.
18
+ *
19
+ * Refs: ADR-133, ADR-135, iter 52 T2, iter 53a, #2156
20
+ */
21
+ interface AnthropicResponse {
22
+ id: string;
23
+ model: string;
24
+ stop_reason: string;
25
+ content: Array<{
26
+ type: string;
27
+ text?: string;
28
+ }>;
29
+ usage: {
30
+ input_tokens: number;
31
+ output_tokens: number;
32
+ };
33
+ }
34
+ declare let _extractFinalAnswer: (resp: AnthropicResponse) => string | null;
35
+ declare let _buildUserMessage: (question: string) => string;
36
+ declare function makeResp(text: string): AnthropicResponse;
37
+ interface TestCase {
38
+ label: string;
39
+ input: string;
40
+ expected: string | null;
41
+ isUserMsg?: boolean;
42
+ }
43
+ declare const CASES: TestCase[];
44
+ declare function main(): Promise<void>;
45
+ //# sourceMappingURL=gaia-extract.smoke.d.ts.map