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,133 @@
1
+ /**
2
+ * GAIA Causal Failure-Avoidance Memory — ADR-135 Track I
3
+ *
4
+ * Records causal edges after each failed GAIA trajectory:
5
+ * "trying tool X on question type Y → caused failure Z"
6
+ *
7
+ * Before each new question, retrieves matching causal edges and injects
8
+ * an "avoid these approaches" hint into the agent's system prompt.
9
+ *
10
+ * This is one of ruflo's 6 architectural primitives distinguishing it
11
+ * from HAL: HAL is stateless across runs; ruflo accumulates causal memory.
12
+ *
13
+ * Storage: JSONL file at ~/.cache/ruflo/gaia/causal-edges.jsonl
14
+ * - Simple, portable, no runtime dependency on AgentDB
15
+ * - Production upgrade path: switch to AgentDB causal-edge MCP controller
16
+ * (`mcp__claude-flow__agentdb_causal-edge`) for cross-session persistence
17
+ * and embedding-based similarity matching.
18
+ *
19
+ * Expected lift:
20
+ * - First run (no edges yet): +0pp (empty hint → no overhead)
21
+ * - After 5+ runs (warm-up): +2-5pp compound
22
+ *
23
+ * NOT wired into gaia-bench.ts here — wiring is a follow-up PR once all
24
+ * in-flight iterators (29/31/34/35/37) have landed to avoid conflicts.
25
+ *
26
+ * Refs: ADR-135, ADR-133, #2156
27
+ */
28
+ import type { GaiaQuestion } from './gaia-loader.js';
29
+ import type { GaiaAgentResult } from './gaia-agent.js';
30
+ /** Observation failure categories derived from trajectory analysis. */
31
+ export type FailureType = 'empty_result' | 'timeout' | 'wrong_answer' | 'tool_error';
32
+ /**
33
+ * A causal edge: "in a question of signature S, using tool T in way W
34
+ * caused failure F." occurrenceCount increments each time the same
35
+ * (signature, tool, step) triple is observed again instead of duplicating.
36
+ */
37
+ export interface CausalEdge {
38
+ /** Deterministic hash of the normalised question text. */
39
+ questionSignature: string;
40
+ /** Tool name that failed (e.g. 'web_search', 'python_exec'). */
41
+ failedTool: string;
42
+ /** Brief description of what the failing step attempted. */
43
+ failedTrajectoryStep: string;
44
+ /** Categorised failure type. */
45
+ observedFailureType: FailureType;
46
+ /** ISO-8601 timestamp of first observation. */
47
+ createdAt: string;
48
+ /** Increments when the same edge is observed in a subsequent run. */
49
+ occurrenceCount: number;
50
+ }
51
+ /** Options for causal-memory operations. */
52
+ export interface CausalMemoryOptions {
53
+ /**
54
+ * Override the JSONL store path.
55
+ * Default: ~/.cache/ruflo/gaia/causal-edges.jsonl
56
+ */
57
+ storePath?: string;
58
+ /**
59
+ * Maximum edges to return per question signature when retrieving hints.
60
+ * Default: 5
61
+ */
62
+ maxEdgesPerSignature?: number;
63
+ /**
64
+ * Signature similarity threshold (0–1). Currently unused for the simple
65
+ * hash-based implementation; reserved for future RuVector upgrade.
66
+ * Default: 0.7
67
+ */
68
+ similarityThreshold?: number;
69
+ }
70
+ /** Result of a recordCausalFailures call. */
71
+ export interface RecordResult {
72
+ edgesRecorded: number;
73
+ storePath: string;
74
+ }
75
+ /** Result of a retrieveCausalHints call. */
76
+ export interface RetrieveResult {
77
+ /**
78
+ * System-prompt-ready hint string.
79
+ * Empty string when no edges match (caller must not inject empty hints).
80
+ */
81
+ hint: string;
82
+ edgesMatched: number;
83
+ }
84
+ /**
85
+ * Compute a deterministic question signature.
86
+ *
87
+ * Algorithm (v1, hash-based):
88
+ * 1. Lower-case the question text.
89
+ * 2. Collapse runs of whitespace to a single space and trim.
90
+ * 3. SHA-256 → first 16 hex characters (64-bit prefix, collision-unlikely
91
+ * for the ~450-question GAIA validation set).
92
+ *
93
+ * Future (v2, embedding-based): replace with RuVector cosine similarity so
94
+ * semantically similar questions (paraphrases, translated variants) share
95
+ * causal edges across runs.
96
+ */
97
+ export declare function computeQuestionSignature(questionText: string): string;
98
+ /** Derive a failure type from a completed agent result + known correctness. */
99
+ export declare function inferFailureType(result: GaiaAgentResult, wasCorrect: boolean): FailureType | null;
100
+ /**
101
+ * After a GAIA trajectory completes, analyse it for causal failure patterns
102
+ * and persist each observed edge in the JSONL store.
103
+ *
104
+ * Behaviour:
105
+ * - If `wasCorrect === true`, no edges are written (zero overhead).
106
+ * - Each (signature, tool, step) triple is deduplicated: if the same triple
107
+ * already exists in the store, its `occurrenceCount` is incremented in
108
+ * place rather than appending a new line.
109
+ * - Edges beyond `maxEdgesPerSignature` (default 5) per signature are
110
+ * discarded to keep the store bounded.
111
+ *
112
+ * @param question - The GaiaQuestion that was attempted.
113
+ * @param result - The agent result from runGaiaAgent().
114
+ * @param wasCorrect - Whether the final answer was judged correct.
115
+ * @param options - Optional store path and limits.
116
+ * @returns Number of edges written/updated and the resolved store path.
117
+ */
118
+ export declare function recordCausalFailures(question: GaiaQuestion, result: GaiaAgentResult, wasCorrect: boolean, options?: CausalMemoryOptions): Promise<RecordResult>;
119
+ /**
120
+ * Before running a new question, retrieve causal edges from prior failures
121
+ * that match the question's signature and format them as a system-prompt hint.
122
+ *
123
+ * Return contract:
124
+ * - No edges matched → `{ hint: '', edgesMatched: 0 }` — caller MUST NOT
125
+ * inject an empty hint (wastes tokens; may confuse the model).
126
+ * - 1+ edges matched → `{ hint: '[PRIOR FAILURES] …', edgesMatched: N }`.
127
+ *
128
+ * @param question - The GaiaQuestion about to be attempted.
129
+ * @param options - Optional store path and limits.
130
+ * @returns Formatted hint string and match count.
131
+ */
132
+ export declare function retrieveCausalHints(question: GaiaQuestion, options?: CausalMemoryOptions): Promise<RetrieveResult>;
133
+ //# sourceMappingURL=gaia-causal-memory.d.ts.map
@@ -0,0 +1,281 @@
1
+ /**
2
+ * GAIA Causal Failure-Avoidance Memory — ADR-135 Track I
3
+ *
4
+ * Records causal edges after each failed GAIA trajectory:
5
+ * "trying tool X on question type Y → caused failure Z"
6
+ *
7
+ * Before each new question, retrieves matching causal edges and injects
8
+ * an "avoid these approaches" hint into the agent's system prompt.
9
+ *
10
+ * This is one of ruflo's 6 architectural primitives distinguishing it
11
+ * from HAL: HAL is stateless across runs; ruflo accumulates causal memory.
12
+ *
13
+ * Storage: JSONL file at ~/.cache/ruflo/gaia/causal-edges.jsonl
14
+ * - Simple, portable, no runtime dependency on AgentDB
15
+ * - Production upgrade path: switch to AgentDB causal-edge MCP controller
16
+ * (`mcp__claude-flow__agentdb_causal-edge`) for cross-session persistence
17
+ * and embedding-based similarity matching.
18
+ *
19
+ * Expected lift:
20
+ * - First run (no edges yet): +0pp (empty hint → no overhead)
21
+ * - After 5+ runs (warm-up): +2-5pp compound
22
+ *
23
+ * NOT wired into gaia-bench.ts here — wiring is a follow-up PR once all
24
+ * in-flight iterators (29/31/34/35/37) have landed to avoid conflicts.
25
+ *
26
+ * Refs: ADR-135, ADR-133, #2156
27
+ */
28
+ import * as crypto from 'node:crypto';
29
+ import * as fs from 'node:fs';
30
+ import * as os from 'node:os';
31
+ import * as path from 'node:path';
32
+ import * as readline from 'node:readline';
33
+ // ---------------------------------------------------------------------------
34
+ // Internal helpers
35
+ // ---------------------------------------------------------------------------
36
+ const DEFAULT_STORE_SUFFIX = path.join('.cache', 'ruflo', 'gaia', 'causal-edges.jsonl');
37
+ const DEFAULT_MAX_EDGES = 5;
38
+ /** Resolve the JSONL store path from options, defaulting to ~/.cache/ruflo/… */
39
+ function resolveStorePath(options) {
40
+ if (options?.storePath) {
41
+ return options.storePath;
42
+ }
43
+ return path.join(os.homedir(), DEFAULT_STORE_SUFFIX);
44
+ }
45
+ /**
46
+ * Compute a deterministic question signature.
47
+ *
48
+ * Algorithm (v1, hash-based):
49
+ * 1. Lower-case the question text.
50
+ * 2. Collapse runs of whitespace to a single space and trim.
51
+ * 3. SHA-256 → first 16 hex characters (64-bit prefix, collision-unlikely
52
+ * for the ~450-question GAIA validation set).
53
+ *
54
+ * Future (v2, embedding-based): replace with RuVector cosine similarity so
55
+ * semantically similar questions (paraphrases, translated variants) share
56
+ * causal edges across runs.
57
+ */
58
+ export function computeQuestionSignature(questionText) {
59
+ const normalised = questionText.toLowerCase().replace(/\s+/g, ' ').trim();
60
+ return crypto.createHash('sha256').update(normalised, 'utf8').digest('hex').slice(0, 16);
61
+ }
62
+ /** Derive a failure type from a completed agent result + known correctness. */
63
+ export function inferFailureType(result, wasCorrect) {
64
+ if (wasCorrect) {
65
+ return null; // Not a failure — callers should skip this question.
66
+ }
67
+ if (result.timedOut === true) {
68
+ return 'timeout';
69
+ }
70
+ if (result.error) {
71
+ return 'tool_error';
72
+ }
73
+ if (result.finalAnswer === null || result.finalAnswer.trim() === '') {
74
+ return 'empty_result';
75
+ }
76
+ return 'wrong_answer';
77
+ }
78
+ /**
79
+ * Parse one JSONL line into a CausalEdge.
80
+ * Returns null if the line is empty, a comment, or malformed.
81
+ */
82
+ function parseLine(line) {
83
+ const trimmed = line.trim();
84
+ if (trimmed === '' || trimmed.startsWith('//') || trimmed.startsWith('#')) {
85
+ return null;
86
+ }
87
+ try {
88
+ const parsed = JSON.parse(trimmed);
89
+ // Minimal shape guard — avoids crashing on partially-written lines.
90
+ if (typeof parsed.questionSignature !== 'string' ||
91
+ typeof parsed.failedTool !== 'string' ||
92
+ typeof parsed.failedTrajectoryStep !== 'string' ||
93
+ typeof parsed.observedFailureType !== 'string' ||
94
+ typeof parsed.createdAt !== 'string' ||
95
+ typeof parsed.occurrenceCount !== 'number') {
96
+ return null;
97
+ }
98
+ return parsed;
99
+ }
100
+ catch {
101
+ // Corrupted line — skip gracefully.
102
+ return null;
103
+ }
104
+ }
105
+ /** Read all valid CausalEdge entries from the JSONL store. */
106
+ async function readAllEdges(storePath) {
107
+ if (!fs.existsSync(storePath)) {
108
+ return [];
109
+ }
110
+ const edges = [];
111
+ const fileStream = fs.createReadStream(storePath, { encoding: 'utf8' });
112
+ const rl = readline.createInterface({ input: fileStream, crlfDelay: Infinity });
113
+ for await (const line of rl) {
114
+ const edge = parseLine(line);
115
+ if (edge !== null) {
116
+ edges.push(edge);
117
+ }
118
+ }
119
+ return edges;
120
+ }
121
+ /** Write all edges back to the JSONL store (full rewrite for upsert support). */
122
+ function writeAllEdges(storePath, edges) {
123
+ const dir = path.dirname(storePath);
124
+ fs.mkdirSync(dir, { recursive: true });
125
+ const content = edges.map((e) => JSON.stringify(e)).join('\n') + (edges.length > 0 ? '\n' : '');
126
+ fs.writeFileSync(storePath, content, { encoding: 'utf8' });
127
+ }
128
+ // ---------------------------------------------------------------------------
129
+ // Trajectory analysis
130
+ // ---------------------------------------------------------------------------
131
+ /**
132
+ * Inspect a failed trajectory and extract tool-level failure events.
133
+ *
134
+ * The agent result carries `toolCallsByName` (tool → call count). For a
135
+ * failed run we attribute the failure to the most-used tool, since that is
136
+ * the tool whose repeated use did not converge to a correct answer.
137
+ *
138
+ * Returns an array of (tool, stepDescription, failureType) triples.
139
+ * Empty array when there is nothing attributable.
140
+ */
141
+ function extractFailureEvents(result, failureType) {
142
+ const events = [];
143
+ const toolCalls = result.toolCallsByName;
144
+ if (!toolCalls || Object.keys(toolCalls).length === 0) {
145
+ // No tool calls at all — record a synthetic "no_tool" event.
146
+ events.push({
147
+ tool: 'no_tool_called',
148
+ step: `Agent failed with type=${failureType} without making any tool calls`,
149
+ type: failureType,
150
+ });
151
+ return events;
152
+ }
153
+ // Attribute one event per tool that was called at least once.
154
+ for (const [toolName, callCount] of Object.entries(toolCalls)) {
155
+ const step = buildStepDescription(toolName, callCount, failureType);
156
+ events.push({ tool: toolName, step, type: failureType });
157
+ }
158
+ return events;
159
+ }
160
+ function buildStepDescription(toolName, callCount, failureType) {
161
+ const timesStr = callCount === 1 ? 'once' : `${callCount} times`;
162
+ switch (failureType) {
163
+ case 'empty_result':
164
+ return `${toolName} called ${timesStr} but returned empty/no-result`;
165
+ case 'timeout':
166
+ return `${toolName} called ${timesStr} but agent timed out before converging`;
167
+ case 'tool_error':
168
+ return `${toolName} called ${timesStr} but raised an execution error`;
169
+ case 'wrong_answer':
170
+ return `${toolName} called ${timesStr} but final answer was incorrect`;
171
+ default:
172
+ return `${toolName} called ${timesStr}; failure type=${failureType}`;
173
+ }
174
+ }
175
+ // ---------------------------------------------------------------------------
176
+ // Public API
177
+ // ---------------------------------------------------------------------------
178
+ /**
179
+ * After a GAIA trajectory completes, analyse it for causal failure patterns
180
+ * and persist each observed edge in the JSONL store.
181
+ *
182
+ * Behaviour:
183
+ * - If `wasCorrect === true`, no edges are written (zero overhead).
184
+ * - Each (signature, tool, step) triple is deduplicated: if the same triple
185
+ * already exists in the store, its `occurrenceCount` is incremented in
186
+ * place rather than appending a new line.
187
+ * - Edges beyond `maxEdgesPerSignature` (default 5) per signature are
188
+ * discarded to keep the store bounded.
189
+ *
190
+ * @param question - The GaiaQuestion that was attempted.
191
+ * @param result - The agent result from runGaiaAgent().
192
+ * @param wasCorrect - Whether the final answer was judged correct.
193
+ * @param options - Optional store path and limits.
194
+ * @returns Number of edges written/updated and the resolved store path.
195
+ */
196
+ export async function recordCausalFailures(question, result, wasCorrect, options) {
197
+ const storePath = resolveStorePath(options);
198
+ const maxEdges = options?.maxEdgesPerSignature ?? DEFAULT_MAX_EDGES;
199
+ const failureType = inferFailureType(result, wasCorrect);
200
+ if (failureType === null) {
201
+ // Correct answer — nothing to record.
202
+ return { edgesRecorded: 0, storePath };
203
+ }
204
+ const signature = computeQuestionSignature(question.question);
205
+ const failureEvents = extractFailureEvents(result, failureType);
206
+ if (failureEvents.length === 0) {
207
+ return { edgesRecorded: 0, storePath };
208
+ }
209
+ // Load existing edges, upsert, cap, rewrite.
210
+ const existing = await readAllEdges(storePath);
211
+ let edgesRecorded = 0;
212
+ for (const event of failureEvents) {
213
+ // Count how many edges already exist for this signature.
214
+ const sigEdges = existing.filter((e) => e.questionSignature === signature);
215
+ // Check for exact duplicate (signature + tool + step).
216
+ const dupIdx = existing.findIndex((e) => e.questionSignature === signature &&
217
+ e.failedTool === event.tool &&
218
+ e.failedTrajectoryStep === event.step);
219
+ if (dupIdx >= 0) {
220
+ // Increment occurrence count in place.
221
+ existing[dupIdx].occurrenceCount += 1;
222
+ edgesRecorded++;
223
+ continue;
224
+ }
225
+ // New edge — only add if we haven't hit the per-signature cap.
226
+ if (sigEdges.length >= maxEdges) {
227
+ continue; // Cap reached; discard.
228
+ }
229
+ const newEdge = {
230
+ questionSignature: signature,
231
+ failedTool: event.tool,
232
+ failedTrajectoryStep: event.step,
233
+ observedFailureType: event.type,
234
+ createdAt: new Date().toISOString(),
235
+ occurrenceCount: 1,
236
+ };
237
+ existing.push(newEdge);
238
+ edgesRecorded++;
239
+ }
240
+ if (edgesRecorded > 0) {
241
+ writeAllEdges(storePath, existing);
242
+ }
243
+ return { edgesRecorded, storePath };
244
+ }
245
+ /**
246
+ * Before running a new question, retrieve causal edges from prior failures
247
+ * that match the question's signature and format them as a system-prompt hint.
248
+ *
249
+ * Return contract:
250
+ * - No edges matched → `{ hint: '', edgesMatched: 0 }` — caller MUST NOT
251
+ * inject an empty hint (wastes tokens; may confuse the model).
252
+ * - 1+ edges matched → `{ hint: '[PRIOR FAILURES] …', edgesMatched: N }`.
253
+ *
254
+ * @param question - The GaiaQuestion about to be attempted.
255
+ * @param options - Optional store path and limits.
256
+ * @returns Formatted hint string and match count.
257
+ */
258
+ export async function retrieveCausalHints(question, options) {
259
+ const storePath = resolveStorePath(options);
260
+ const maxEdges = options?.maxEdgesPerSignature ?? DEFAULT_MAX_EDGES;
261
+ const signature = computeQuestionSignature(question.question);
262
+ const allEdges = await readAllEdges(storePath);
263
+ // Filter to this question's signature, sort by occurrence count descending
264
+ // so the most-reinforced warnings appear first.
265
+ const matched = allEdges
266
+ .filter((e) => e.questionSignature === signature)
267
+ .sort((a, b) => b.occurrenceCount - a.occurrenceCount)
268
+ .slice(0, maxEdges);
269
+ if (matched.length === 0) {
270
+ return { hint: '', edgesMatched: 0 };
271
+ }
272
+ const lines = matched.map((e) => {
273
+ const times = e.occurrenceCount === 1 ? '1 time' : `${e.occurrenceCount} times`;
274
+ return ` - ${e.failedTool} failed ${times} (${e.observedFailureType}): ${e.failedTrajectoryStep}`;
275
+ });
276
+ const hint = '[PRIOR FAILURES] On similar questions, these approaches failed:\n' +
277
+ lines.join('\n') +
278
+ '\nAvoid repeating these patterns. Try alternative tools or approaches.';
279
+ return { hint, edgesMatched: matched.length };
280
+ }
281
+ //# sourceMappingURL=gaia-causal-memory.js.map
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Smoke tests — ADR-135 Track I: Causal failure-avoidance edges
3
+ *
4
+ * All I/O is mocked (no real filesystem or network calls).
5
+ *
6
+ * Tests:
7
+ * 1. Record one failure → retrieve same-signature question → returns hint
8
+ * 2. Record 3 failures → retrieve unrelated question → returns empty hint
9
+ * 3. Same edge recorded twice → occurrenceCount=2, not duplicated
10
+ * 4. File doesn't exist → graceful empty result on retrieve
11
+ * 5. Corrupted JSONL line → skipped, doesn't crash
12
+ * 6. maxEdgesPerSignature cap respected (no new edge after cap)
13
+ * 7. Signature computation is deterministic (same input → same output)
14
+ * 8. Correct answer trajectory → no edges recorded
15
+ *
16
+ * Usage:
17
+ * npx tsx src/benchmarks/gaia-causal-memory.smoke.ts
18
+ *
19
+ * Refs: ADR-135, #2156
20
+ */
21
+ export {};
22
+ //# sourceMappingURL=gaia-causal-memory.smoke.d.ts.map