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,326 @@
1
+ /**
2
+ * GAIA Dataset Loader — ADR-133-PR1
3
+ *
4
+ * Authenticates to Hugging Face, downloads the `gaia-benchmark/GAIA`
5
+ * validation split, caches it under ~/.cache/ruflo/gaia/, and exposes
6
+ * a typed `loadGaia()` API consumed by the capability-gaia subcommand.
7
+ *
8
+ * Token resolution order (mirrors performance-capability.ts ANTHROPIC_API_KEY pattern):
9
+ * 1. $HF_TOKEN env var
10
+ * 2. gcloud secrets versions access latest --secret=huggingface-token
11
+ * 3. Fail with a clear error message
12
+ *
13
+ * This file is deliberately a skeleton / PR-1 checkpoint. The full
14
+ * dataset download is gated behind a real HF_TOKEN; a 5-question smoke
15
+ * fixture is provided for offline / CI-without-HF testing.
16
+ *
17
+ * Refs: ADR-133, #2156
18
+ */
19
+ import { execSync } from 'node:child_process';
20
+ import * as fs from 'node:fs';
21
+ import * as path from 'node:path';
22
+ import * as os from 'node:os';
23
+ import * as https from 'node:https';
24
+ import * as url from 'node:url';
25
+ // ---------------------------------------------------------------------------
26
+ // HF Token resolution
27
+ // ---------------------------------------------------------------------------
28
+ /**
29
+ * Resolve a Hugging Face API token using two fallbacks:
30
+ * 1. $HF_TOKEN env var
31
+ * 2. `gcloud secrets versions access latest --secret=huggingface-token`
32
+ *
33
+ * Throws with a clear error if neither is available.
34
+ */
35
+ export function resolveHfToken() {
36
+ const envToken = process.env.HF_TOKEN;
37
+ if (envToken && envToken.trim())
38
+ return envToken.trim();
39
+ try {
40
+ const out = execSync('gcloud secrets versions access latest --secret=huggingface-token 2>/dev/null', { encoding: 'utf-8', timeout: 10_000 }).trim();
41
+ if (out)
42
+ return out;
43
+ }
44
+ catch {
45
+ /* fall through */
46
+ }
47
+ throw new Error('HF_TOKEN not found. Set the env var or store it in GCP Secret Manager under the name ' +
48
+ '"huggingface-token" (e.g. `echo -n "$TOKEN" | gcloud secrets versions add huggingface-token --data-file=-`).');
49
+ }
50
+ // ---------------------------------------------------------------------------
51
+ // Cache helpers
52
+ // ---------------------------------------------------------------------------
53
+ function defaultCacheDir() {
54
+ return path.join(os.homedir(), '.cache', 'ruflo', 'gaia');
55
+ }
56
+ /**
57
+ * Export the default cache directory path (no side effects).
58
+ */
59
+ export function getDefaultCacheDir() {
60
+ return defaultCacheDir();
61
+ }
62
+ function ensureDir(dir) {
63
+ fs.mkdirSync(dir, { recursive: true });
64
+ }
65
+ function revisionCacheKey(revision, level) {
66
+ return `level${level}-${revision}.json`;
67
+ }
68
+ // ---------------------------------------------------------------------------
69
+ // 5-question smoke fixture (no HF token required)
70
+ // ---------------------------------------------------------------------------
71
+ /**
72
+ * Minimal offline fixture for smoke tests and CI environments without HF_TOKEN.
73
+ * Answers are verified by hand against publicly known facts.
74
+ * All questions are Level 1 (no file attachments, no multi-hop tool use required).
75
+ *
76
+ * IMPORTANT: Verify every answer key with `node -e 'console.log(…)'` before
77
+ * adding entries to this list — three answer-key bugs were caught in session #2156.
78
+ */
79
+ export const SMOKE_FIXTURE = [
80
+ {
81
+ task_id: 'smoke-001',
82
+ level: 1,
83
+ question: 'What is the capital of France?',
84
+ final_answer: 'Paris',
85
+ file_name: null,
86
+ file_path: null,
87
+ },
88
+ {
89
+ task_id: 'smoke-002',
90
+ level: 1,
91
+ question: 'How many sides does a hexagon have?',
92
+ final_answer: '6',
93
+ file_name: null,
94
+ file_path: null,
95
+ },
96
+ {
97
+ task_id: 'smoke-003',
98
+ level: 1,
99
+ question: 'What is 15 multiplied by 4?',
100
+ final_answer: '60',
101
+ file_name: null,
102
+ file_path: null,
103
+ },
104
+ {
105
+ task_id: 'smoke-004',
106
+ level: 1,
107
+ question: 'In what year did the Berlin Wall fall?',
108
+ final_answer: '1989',
109
+ file_name: null,
110
+ file_path: null,
111
+ },
112
+ {
113
+ task_id: 'smoke-005',
114
+ level: 1,
115
+ question: 'What chemical symbol represents gold on the periodic table?',
116
+ final_answer: 'Au',
117
+ file_name: null,
118
+ file_path: null,
119
+ },
120
+ ];
121
+ // ---------------------------------------------------------------------------
122
+ // HF dataset download (requires HF_TOKEN)
123
+ // ---------------------------------------------------------------------------
124
+ const HF_DATASET_REPO = 'gaia-benchmark/GAIA';
125
+ const HF_API_BASE = 'https://huggingface.co';
126
+ const HF_DATASETS_API = 'https://datasets-server.huggingface.co';
127
+ /**
128
+ * Fetch JSON from a URL with an Authorization header.
129
+ * Returns parsed JSON or throws.
130
+ */
131
+ async function fetchJson(url, token) {
132
+ return new Promise((resolve, reject) => {
133
+ const req = https.get(url, {
134
+ headers: {
135
+ Authorization: `Bearer ${token}`,
136
+ 'User-Agent': 'ruflo-gaia-loader/1.0',
137
+ },
138
+ }, (res) => {
139
+ if (res.statusCode === 401 || res.statusCode === 403) {
140
+ reject(new Error(`HF auth error ${res.statusCode} for ${url}. Check HF_TOKEN permissions (need read access to gaia-benchmark/GAIA).`));
141
+ res.resume();
142
+ return;
143
+ }
144
+ if (res.statusCode !== 200) {
145
+ reject(new Error(`HTTP ${res.statusCode} from ${url}`));
146
+ res.resume();
147
+ return;
148
+ }
149
+ const chunks = [];
150
+ res.on('data', (c) => chunks.push(c));
151
+ res.on('end', () => {
152
+ try {
153
+ resolve(JSON.parse(Buffer.concat(chunks).toString('utf-8')));
154
+ }
155
+ catch (e) {
156
+ reject(new Error(`JSON parse failed for ${url}: ${String(e)}`));
157
+ }
158
+ });
159
+ res.on('error', reject);
160
+ });
161
+ req.on('error', reject);
162
+ req.setTimeout(30_000, () => {
163
+ req.destroy(new Error(`Timeout fetching ${url}`));
164
+ });
165
+ });
166
+ }
167
+ /**
168
+ * Download the GAIA validation split for a given level from Hugging Face.
169
+ *
170
+ * Uses the HF Datasets Server API (paginated parquet/JSON rows endpoint).
171
+ * Caches the result locally so subsequent runs are instant.
172
+ *
173
+ * Uses per-level HF configs (2023_level1, 2023_level2, 2023_level3) so
174
+ * all questions for a given level fit within the 100-row API limit.
175
+ */
176
+ async function downloadGaiaLevel(level, token, cacheDir) {
177
+ ensureDir(cacheDir);
178
+ // Query a stable revision identifier (the dataset card commit SHA).
179
+ // For now we use 'main' as the revision key for the cache filename.
180
+ const revision = 'main';
181
+ const cacheFile = path.join(cacheDir, revisionCacheKey(revision, level));
182
+ if (fs.existsSync(cacheFile)) {
183
+ const cached = JSON.parse(fs.readFileSync(cacheFile, 'utf-8'));
184
+ return cached;
185
+ }
186
+ // HF Datasets Server rows endpoint for the per-level 2023 config.
187
+ // Using "2023_level{N}" config instead of "2023_all" avoids the pagination
188
+ // problem: "2023_all" has 165 rows but the API caps at 100 per request,
189
+ // which silently drops 23 of 53 Level-1 questions. The per-level configs
190
+ // each have ≤100 rows (L1=53, L2=86, L3=26) and fit in a single request.
191
+ const levelConfig = `2023_level${level}`;
192
+ const url = `${HF_DATASETS_API}/rows?dataset=${encodeURIComponent(HF_DATASET_REPO)}` +
193
+ `&config=${levelConfig}&split=validation&offset=0&length=100`;
194
+ const data = await fetchJson(url, token);
195
+ if (!data.rows || !Array.isArray(data.rows)) {
196
+ throw new Error(`Unexpected HF response shape — missing .rows array. Got: ${JSON.stringify(data).slice(0, 200)}`);
197
+ }
198
+ const questions = data.rows
199
+ .map((r) => r.row)
200
+ .filter((row) => Number(row['Level']) === level)
201
+ .map((row) => ({
202
+ task_id: String(row['task_id'] ?? ''),
203
+ level,
204
+ question: String(row['Question'] ?? ''),
205
+ final_answer: String(row['Final answer'] ?? '').trim(),
206
+ file_name: row['file_name'] ? String(row['file_name']) : null,
207
+ file_path: null, // attachment resolution is a PR-2 concern
208
+ annotator_metadata: row['Annotator Metadata'],
209
+ }))
210
+ .filter((q) => q.task_id && q.question && q.final_answer);
211
+ fs.writeFileSync(cacheFile, JSON.stringify(questions, null, 2), 'utf-8');
212
+ return questions;
213
+ }
214
+ // ---------------------------------------------------------------------------
215
+ // Attachment download (iter-53b)
216
+ // ---------------------------------------------------------------------------
217
+ /**
218
+ * Base URL for GAIA validation attachment files.
219
+ * Individual files are at: HF_FILE_BASE/<task_id>/<file_name>
220
+ *
221
+ * HF now serves files through the Xet storage layer; requests will redirect
222
+ * (301/302/303) to a Xet download URL that requires NO auth header.
223
+ * We only send the Authorization header to requests targeting huggingface.co.
224
+ */
225
+ const HF_FILE_BASE = 'https://huggingface.co/datasets/gaia-benchmark/GAIA/resolve/main/2023/validation';
226
+ /**
227
+ * Download a single GAIA attachment file to the cache directory.
228
+ * Follows HTTP redirects, sending auth only to huggingface.co domains.
229
+ * Returns the local file path on success, or null on any error.
230
+ */
231
+ async function downloadAttachment(taskId, fileName, token, cacheDir) {
232
+ const destPath = path.join(cacheDir, fileName);
233
+ if (fs.existsSync(destPath))
234
+ return destPath; // already cached
235
+ const fileUrl = `${HF_FILE_BASE}/${taskId}/${encodeURIComponent(fileName)}`;
236
+ ensureDir(cacheDir);
237
+ return new Promise((resolve) => {
238
+ function doGet(requestUrl, depth) {
239
+ if (depth > 5) {
240
+ resolve(null);
241
+ return;
242
+ }
243
+ const parsed = new url.URL(requestUrl);
244
+ const headers = {
245
+ 'User-Agent': 'ruflo-gaia-loader/1.0',
246
+ };
247
+ if (parsed.hostname.includes('huggingface.co')) {
248
+ headers['Authorization'] = `Bearer ${token}`;
249
+ }
250
+ const req = https.get(requestUrl, { headers }, (res) => {
251
+ if (res.statusCode && [301, 302, 303, 307, 308].includes(res.statusCode)) {
252
+ const loc = res.headers['location'];
253
+ res.resume();
254
+ if (loc)
255
+ doGet(loc, depth + 1);
256
+ else
257
+ resolve(null);
258
+ return;
259
+ }
260
+ if (res.statusCode !== 200) {
261
+ res.resume();
262
+ resolve(null);
263
+ return;
264
+ }
265
+ const chunks = [];
266
+ res.on('data', (c) => chunks.push(c));
267
+ res.on('end', () => {
268
+ try {
269
+ fs.writeFileSync(destPath, Buffer.concat(chunks));
270
+ resolve(destPath);
271
+ }
272
+ catch {
273
+ resolve(null);
274
+ }
275
+ });
276
+ res.on('error', () => resolve(null));
277
+ });
278
+ req.on('error', () => resolve(null));
279
+ req.setTimeout(60_000, () => { req.destroy(); resolve(null); });
280
+ }
281
+ doGet(fileUrl, 0);
282
+ });
283
+ }
284
+ /**
285
+ * Download all attachment files referenced by the questions list.
286
+ * Mutates each question's `file_path` field in place.
287
+ * Skips questions without a file_name.
288
+ */
289
+ export async function resolveAttachments(questions, token, cacheDir) {
290
+ const withFiles = questions.filter((q) => q.file_name);
291
+ await Promise.all(withFiles.map(async (q) => {
292
+ const localPath = await downloadAttachment(q.task_id, q.file_name, token, cacheDir);
293
+ q.file_path = localPath;
294
+ }));
295
+ }
296
+ // ---------------------------------------------------------------------------
297
+ // Public API
298
+ // ---------------------------------------------------------------------------
299
+ /**
300
+ * Load GAIA validation questions.
301
+ *
302
+ * - With `smokeOnly: true` (or when HF_TOKEN is unavailable): returns the 5-question
303
+ * smoke fixture — no network call, no token required.
304
+ * - Otherwise: authenticates to HF, downloads level N questions, caches locally.
305
+ *
306
+ * @throws if HF_TOKEN is missing and smokeOnly is false
307
+ */
308
+ export async function loadGaia(options = {}) {
309
+ const { level = 1, limit, smokeOnly = false, cacheDir = defaultCacheDir() } = options;
310
+ if (smokeOnly) {
311
+ const filtered = SMOKE_FIXTURE.filter((q) => q.level === level);
312
+ return limit !== undefined ? filtered.slice(0, limit) : filtered;
313
+ }
314
+ const token = resolveHfToken();
315
+ const questions = await downloadGaiaLevel(level, token, cacheDir);
316
+ // Resolve attachment files in parallel (iter-53b: populates file_path on each question)
317
+ await resolveAttachments(questions, token, cacheDir);
318
+ return limit !== undefined ? questions.slice(0, limit) : questions;
319
+ }
320
+ /**
321
+ * Returns the cache directory path (does not create it).
322
+ */
323
+ export function getGaiaCacheDir(override) {
324
+ return override ?? defaultCacheDir();
325
+ }
326
+ //# sourceMappingURL=gaia-loader.js.map
@@ -0,0 +1,35 @@
1
+ /**
2
+ * GAIA Tool: file_read — ADR-133-PR2 / iter-53b attachment-tools
3
+ *
4
+ * Reads a file from the local filesystem and returns its contents as a
5
+ * UTF-8 string. Performs content-type dispatch based on extension +
6
+ * magic bytes:
7
+ *
8
+ * Supported extraction formats (iter-53b):
9
+ * - Plain text, JSON, CSV, XML, HTML, Markdown, JS/TS, Python, shell, YAML
10
+ * - XLSX — openpyxl subprocess (cell values + fill colours)
11
+ * - PPTX — python-pptx subprocess (per-slide text)
12
+ * - PNG / JPEG / GIF / WebP — returns IMAGE_BASE64 marker for vision API
13
+ * - MP3 / WAV — OpenAI Whisper (tiny model) subprocess transcript
14
+ * - .py source file — returned as UTF-8 text (no execution)
15
+ *
16
+ * For PDF / DOCX / other binary: descriptive stub (PR-4 deferred).
17
+ *
18
+ * IMAGE_BASE64 marker format:
19
+ * [IMAGE_BASE64:{"mediaType":"image/png","base64":"...","path":"/abs/path"}]
20
+ *
21
+ * The agent loop in gaia-agent.ts must parse this marker when it appears
22
+ * in a tool_result and convert it to an Anthropic vision content block.
23
+ *
24
+ * Maximum file size: 5 MB. Paths must be absolute.
25
+ *
26
+ * Refs: ADR-133, #2156, iter-53b
27
+ */
28
+ import { GaiaTool, ToolDefinition } from './types.js';
29
+ export declare class FileReadTool implements GaiaTool {
30
+ readonly name = "file_read";
31
+ readonly definition: ToolDefinition;
32
+ execute(input: Record<string, unknown>): Promise<string>;
33
+ }
34
+ export declare function createFileReadTool(): FileReadTool;
35
+ //# sourceMappingURL=file_read.d.ts.map