claude-flow 3.32.35 → 3.32.37
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.
- package/.claude/helpers/hook-handler.cjs +134 -3
- package/.claude/helpers/intelligence.cjs +83 -5
- package/node_modules/@claude-flow/codex/.agents/skills/github-automation/SKILL.md +32 -0
- package/node_modules/@claude-flow/codex/.agents/skills/performance-analysis/SKILL.md +32 -0
- package/node_modules/@claude-flow/codex/dist/dual-mode/cli.d.ts +4 -0
- package/node_modules/@claude-flow/codex/dist/dual-mode/cli.d.ts.map +1 -1
- package/node_modules/@claude-flow/codex/dist/dual-mode/cli.js +20 -1
- package/node_modules/@claude-flow/codex/dist/dual-mode/cli.js.map +1 -1
- package/node_modules/@claude-flow/codex/dist/dual-mode/orchestrator.d.ts +4 -0
- package/node_modules/@claude-flow/codex/dist/dual-mode/orchestrator.d.ts.map +1 -1
- package/node_modules/@claude-flow/codex/dist/dual-mode/orchestrator.js +18 -2
- package/node_modules/@claude-flow/codex/dist/dual-mode/orchestrator.js.map +1 -1
- package/node_modules/@claude-flow/codex/dist/generators/skill-md.d.ts +11 -5
- package/node_modules/@claude-flow/codex/dist/generators/skill-md.d.ts.map +1 -1
- package/node_modules/@claude-flow/codex/dist/generators/skill-md.js +84 -849
- package/node_modules/@claude-flow/codex/dist/generators/skill-md.js.map +1 -1
- package/node_modules/@claude-flow/codex/dist/initializer.d.ts +5 -0
- package/node_modules/@claude-flow/codex/dist/initializer.d.ts.map +1 -1
- package/node_modules/@claude-flow/codex/dist/initializer.js +54 -20
- package/node_modules/@claude-flow/codex/dist/initializer.js.map +1 -1
- package/package.json +1 -1
- package/v3/@claude-flow/cli/bin/cli.js +25 -1
- package/v3/@claude-flow/cli/catalog-manifest.json +2 -2
- package/v3/@claude-flow/cli/dist/src/commands/agent.js +1 -1
- package/v3/@claude-flow/cli/dist/src/commands/doctor.js +159 -11
- package/v3/@claude-flow/cli/dist/src/commands/hooks.js +39 -4
- package/v3/@claude-flow/cli/dist/src/commands/init.js +52 -8
- package/v3/@claude-flow/cli/dist/src/commands/mcp.js +1 -1
- package/v3/@claude-flow/cli/dist/src/commands/memory-distill.js +1 -0
- package/v3/@claude-flow/cli/dist/src/commands/swarm.js +28 -0
- package/v3/@claude-flow/cli/dist/src/init/executor.js +45 -10
- package/v3/@claude-flow/cli/dist/src/init/helpers-generator.js +15 -4
- package/v3/@claude-flow/cli/dist/src/init/statusline-generator.js +18 -5
- package/v3/@claude-flow/cli/dist/src/mcp-server.d.ts +25 -0
- package/v3/@claude-flow/cli/dist/src/mcp-server.js +57 -2
- package/v3/@claude-flow/cli/dist/src/mcp-tools/embeddings-tools.js +51 -10
- package/v3/@claude-flow/cli/dist/src/mcp-tools/hooks-tools.js +34 -8
- package/v3/@claude-flow/cli/dist/src/mcp-tools/metaharness-tools.js +1 -1
- package/v3/@claude-flow/cli/dist/src/memory/memory-bridge.d.ts +12 -0
- package/v3/@claude-flow/cli/dist/src/memory/memory-bridge.js +29 -6
- package/v3/@claude-flow/cli/dist/src/memory/memory-initializer.js +11 -8
- package/v3/@claude-flow/cli/dist/src/services/memory-distillation.d.ts +1 -0
- package/v3/@claude-flow/cli/dist/src/services/memory-distillation.js +81 -5
- package/v3/@claude-flow/cli/dist/src/services/worker-daemon.js +1 -0
- package/v3/@claude-flow/cli/package.json +1 -1
|
@@ -50,13 +50,16 @@ async function getRealEmbeddingFunction() {
|
|
|
50
50
|
}
|
|
51
51
|
return realEmbeddingFn;
|
|
52
52
|
}
|
|
53
|
-
// Generate real ONNX embedding (falls back to deterministic hash if ONNX unavailable)
|
|
54
53
|
async function generateRealEmbedding(text, dimension) {
|
|
55
54
|
const realFn = await getRealEmbeddingFunction();
|
|
56
55
|
if (realFn) {
|
|
57
56
|
try {
|
|
58
57
|
const result = await realFn(text);
|
|
59
|
-
return
|
|
58
|
+
return {
|
|
59
|
+
embedding: result.embedding,
|
|
60
|
+
backend: result.backend ?? 'onnx',
|
|
61
|
+
model: result.model,
|
|
62
|
+
};
|
|
60
63
|
}
|
|
61
64
|
catch {
|
|
62
65
|
// Fall through to fallback
|
|
@@ -75,7 +78,11 @@ async function generateRealEmbedding(text, dimension) {
|
|
|
75
78
|
}
|
|
76
79
|
// L2 normalize
|
|
77
80
|
const norm = Math.sqrt(embedding.reduce((sum, x) => sum + x * x, 0));
|
|
78
|
-
return
|
|
81
|
+
return {
|
|
82
|
+
embedding: embedding.map(x => x / norm),
|
|
83
|
+
backend: 'mock',
|
|
84
|
+
model: 'hash-fallback',
|
|
85
|
+
};
|
|
79
86
|
}
|
|
80
87
|
// Convert Euclidean embedding to Poincaré ball
|
|
81
88
|
function toPoincare(euclidean, curvature) {
|
|
@@ -238,7 +245,8 @@ export const embeddingsTools = [
|
|
|
238
245
|
}
|
|
239
246
|
const useHyperbolic = input.hyperbolic === true && config.hyperbolic.enabled;
|
|
240
247
|
// Generate real ONNX embedding
|
|
241
|
-
const
|
|
248
|
+
const generated = await generateRealEmbedding(text, config.dimension);
|
|
249
|
+
const embedding = generated.embedding;
|
|
242
250
|
let result;
|
|
243
251
|
let geometry;
|
|
244
252
|
if (useHyperbolic) {
|
|
@@ -254,6 +262,8 @@ export const embeddingsTools = [
|
|
|
254
262
|
embedding: result,
|
|
255
263
|
metadata: {
|
|
256
264
|
model: config.model,
|
|
265
|
+
embeddingBackend: generated.backend,
|
|
266
|
+
semanticGrounded: generated.backend === 'onnx',
|
|
257
267
|
dimension: config.dimension,
|
|
258
268
|
geometry,
|
|
259
269
|
curvature: useHyperbolic ? config.hyperbolic.curvature : null,
|
|
@@ -309,10 +319,13 @@ export const embeddingsTools = [
|
|
|
309
319
|
return { success: false, error: v.error };
|
|
310
320
|
}
|
|
311
321
|
// Generate real ONNX embeddings for both texts
|
|
312
|
-
const [
|
|
322
|
+
const [generated1, generated2] = await Promise.all([
|
|
313
323
|
generateRealEmbedding(text1, config.dimension),
|
|
314
324
|
generateRealEmbedding(text2, config.dimension)
|
|
315
325
|
]);
|
|
326
|
+
const emb1 = generated1.embedding;
|
|
327
|
+
const emb2 = generated2.embedding;
|
|
328
|
+
const embeddingBackend = generated1.backend === 'onnx' && generated2.backend === 'onnx' ? 'onnx' : 'mock';
|
|
316
329
|
let similarity;
|
|
317
330
|
let distance;
|
|
318
331
|
switch (metric) {
|
|
@@ -341,14 +354,21 @@ export const embeddingsTools = [
|
|
|
341
354
|
similarity,
|
|
342
355
|
distance,
|
|
343
356
|
metric,
|
|
357
|
+
embeddingBackend,
|
|
358
|
+
semanticGrounded: embeddingBackend === 'onnx',
|
|
344
359
|
texts: {
|
|
345
360
|
text1: { length: text1.length, preview: text1.slice(0, 50) },
|
|
346
361
|
text2: { length: text2.length, preview: text2.slice(0, 50) },
|
|
347
362
|
},
|
|
348
|
-
interpretation:
|
|
349
|
-
similarity > 0.
|
|
350
|
-
similarity > 0.
|
|
351
|
-
similarity > 0.
|
|
363
|
+
interpretation: embeddingBackend === 'onnx'
|
|
364
|
+
? similarity > 0.8 ? 'very similar'
|
|
365
|
+
: similarity > 0.6 ? 'similar'
|
|
366
|
+
: similarity > 0.4 ? 'somewhat similar'
|
|
367
|
+
: similarity > 0.2 ? 'different' : 'very different'
|
|
368
|
+
: null,
|
|
369
|
+
warning: embeddingBackend === 'mock'
|
|
370
|
+
? 'Hash fallback scores are deterministic but not semantically meaningful.'
|
|
371
|
+
: undefined,
|
|
352
372
|
};
|
|
353
373
|
},
|
|
354
374
|
},
|
|
@@ -426,6 +446,8 @@ export const embeddingsTools = [
|
|
|
426
446
|
})),
|
|
427
447
|
metadata: {
|
|
428
448
|
model: config.model,
|
|
449
|
+
embeddingBackend: queryEmbedding.backend,
|
|
450
|
+
semanticGrounded: queryEmbedding.backend === 'onnx',
|
|
429
451
|
topK,
|
|
430
452
|
threshold,
|
|
431
453
|
namespace: namespace || 'all',
|
|
@@ -433,6 +455,9 @@ export const embeddingsTools = [
|
|
|
433
455
|
indexType: config.hyperbolic.enabled ? 'HNSW (hyperbolic)' : 'HNSW (euclidean)',
|
|
434
456
|
resultCount: searchResult.results.length
|
|
435
457
|
},
|
|
458
|
+
warning: queryEmbedding.backend === 'mock'
|
|
459
|
+
? 'Results use a hash fallback and are not semantically ranked.'
|
|
460
|
+
: undefined,
|
|
436
461
|
};
|
|
437
462
|
}
|
|
438
463
|
catch {
|
|
@@ -444,12 +469,17 @@ export const embeddingsTools = [
|
|
|
444
469
|
results: [],
|
|
445
470
|
metadata: {
|
|
446
471
|
model: config.model,
|
|
472
|
+
embeddingBackend: queryEmbedding.backend,
|
|
473
|
+
semanticGrounded: queryEmbedding.backend === 'onnx',
|
|
447
474
|
topK,
|
|
448
475
|
threshold,
|
|
449
476
|
namespace: namespace || 'all',
|
|
450
477
|
searchTime: `${searchTime}ms`,
|
|
451
478
|
indexType: config.hyperbolic.enabled ? 'HNSW (hyperbolic)' : 'HNSW (euclidean)',
|
|
452
479
|
},
|
|
480
|
+
warning: queryEmbedding.backend === 'mock'
|
|
481
|
+
? 'Hash fallback is active; semantic search is unavailable.'
|
|
482
|
+
: undefined,
|
|
453
483
|
message: 'No embeddings indexed yet. Use memory store to add documents.',
|
|
454
484
|
};
|
|
455
485
|
}
|
|
@@ -795,6 +825,8 @@ export const embeddingsTools = [
|
|
|
795
825
|
}
|
|
796
826
|
catch { /* not installed */ }
|
|
797
827
|
const ruvectorEnabled = config.neural.ruvector?.enabled ?? false;
|
|
828
|
+
const backendProbe = await generateRealEmbedding('ruflo embedding backend probe', config.dimension);
|
|
829
|
+
const semanticGrounded = backendProbe.backend === 'onnx';
|
|
798
830
|
return {
|
|
799
831
|
success: true,
|
|
800
832
|
initialized: true,
|
|
@@ -822,11 +854,20 @@ export const embeddingsTools = [
|
|
|
822
854
|
models: config.modelPath,
|
|
823
855
|
},
|
|
824
856
|
initializedAt: config.initialized,
|
|
857
|
+
embeddingBackend: backendProbe.backend,
|
|
858
|
+
semanticGrounded,
|
|
859
|
+
warning: semanticGrounded
|
|
860
|
+
? undefined
|
|
861
|
+
: 'Hash fallback is active. Similarity scores are deterministic but not semantically meaningful.',
|
|
825
862
|
capabilities: {
|
|
826
863
|
onnxModels: ['Xenova/all-MiniLM-L6-v2', 'Xenova/all-mpnet-base-v2'],
|
|
827
864
|
geometries: ['euclidean', 'poincare'],
|
|
828
865
|
normalizations: ['L2', 'L1', 'minmax', 'zscore'],
|
|
829
|
-
features: [
|
|
866
|
+
features: [
|
|
867
|
+
...(semanticGrounded ? ['semantic search'] : []),
|
|
868
|
+
'hyperbolic projection',
|
|
869
|
+
'neural substrate',
|
|
870
|
+
],
|
|
830
871
|
},
|
|
831
872
|
};
|
|
832
873
|
},
|
|
@@ -439,6 +439,20 @@ function getIntelligenceStatsFromMemory() {
|
|
|
439
439
|
const category = e.metadata?.category || 'general';
|
|
440
440
|
categories[category] = (categories[category] || 0) + 1;
|
|
441
441
|
});
|
|
442
|
+
const successfulPatterns = patternEntries.filter(e => e.metadata?.success === true).length;
|
|
443
|
+
const failedPatterns = patternEntries.filter(e => e.metadata?.success === false).length;
|
|
444
|
+
const describedPatterns = patternEntries.filter(e => {
|
|
445
|
+
if (typeof e.metadata?.task === 'string' && e.metadata.task.trim())
|
|
446
|
+
return true;
|
|
447
|
+
try {
|
|
448
|
+
const value = typeof e.value === 'string' ? JSON.parse(e.value) : e.value;
|
|
449
|
+
return typeof value?.task === 'string' &&
|
|
450
|
+
Boolean(String(value.task).trim());
|
|
451
|
+
}
|
|
452
|
+
catch {
|
|
453
|
+
return false;
|
|
454
|
+
}
|
|
455
|
+
}).length;
|
|
442
456
|
// Count routing decisions
|
|
443
457
|
const routingEntries = entries.filter(e => e.key.includes('routing') ||
|
|
444
458
|
e.metadata?.type === 'routing-decision');
|
|
@@ -472,6 +486,10 @@ function getIntelligenceStatsFromMemory() {
|
|
|
472
486
|
},
|
|
473
487
|
patterns: {
|
|
474
488
|
learned: patternEntries.length,
|
|
489
|
+
successful: successfulPatterns,
|
|
490
|
+
failed: failedPatterns,
|
|
491
|
+
unknown: Math.max(0, patternEntries.length - successfulPatterns - failedPatterns),
|
|
492
|
+
described: describedPatterns,
|
|
475
493
|
categories,
|
|
476
494
|
},
|
|
477
495
|
memory: {
|
|
@@ -1074,21 +1092,28 @@ export const hooksMetrics = {
|
|
|
1074
1092
|
agentCounts[o.agent] = (agentCounts[o.agent] || 0) + 1;
|
|
1075
1093
|
}
|
|
1076
1094
|
const topAgent = Object.entries(agentCounts).sort((a, b) => b[1] - a[1])[0]?.[0] ?? null;
|
|
1077
|
-
const successful = stats.trajectories.successful;
|
|
1078
|
-
const total = stats.trajectories.total;
|
|
1079
|
-
const failed = Math.max(0, total - successful);
|
|
1080
1095
|
return {
|
|
1081
1096
|
_real: true,
|
|
1082
1097
|
_dataSource: 'intelligence-stats + routing-outcomes',
|
|
1083
1098
|
period,
|
|
1084
1099
|
patterns: {
|
|
1085
1100
|
total: stats.patterns.learned,
|
|
1086
|
-
successful,
|
|
1087
|
-
failed,
|
|
1101
|
+
successful: stats.patterns.successful,
|
|
1102
|
+
failed: stats.patterns.failed,
|
|
1103
|
+
unknown: stats.patterns.unknown,
|
|
1104
|
+
described: stats.patterns.described,
|
|
1105
|
+
descriptionCoverage: stats.patterns.learned > 0
|
|
1106
|
+
? stats.patterns.described / stats.patterns.learned
|
|
1107
|
+
: null,
|
|
1088
1108
|
avgConfidence: stats.routing.avgConfidence || null,
|
|
1089
1109
|
},
|
|
1090
1110
|
agents: {
|
|
1091
|
-
|
|
1111
|
+
// Confidence is a model self-score, not observed routing accuracy
|
|
1112
|
+
// (#2809). Keep the old key as an explicit null for compatibility
|
|
1113
|
+
// while exposing the truthful additive field.
|
|
1114
|
+
routingAccuracy: null,
|
|
1115
|
+
averageConfidence: stats.routing.avgConfidence || null,
|
|
1116
|
+
outcomeSuccessRate: successRate,
|
|
1092
1117
|
totalRoutes: stats.routing.decisions,
|
|
1093
1118
|
topAgent,
|
|
1094
1119
|
},
|
|
@@ -1097,7 +1122,7 @@ export const hooksMetrics = {
|
|
|
1097
1122
|
successRate,
|
|
1098
1123
|
avgRiskScore: null,
|
|
1099
1124
|
},
|
|
1100
|
-
_note:
|
|
1125
|
+
_note: stats.patterns.learned === 0 && totalCommands === 0
|
|
1101
1126
|
? 'No metrics data collected yet. Run hooks_post-task / hooks_intelligence_trajectory-end / hooks_route to populate.'
|
|
1102
1127
|
: undefined,
|
|
1103
1128
|
lastUpdated: new Date().toISOString(),
|
|
@@ -1311,6 +1336,7 @@ export const hooksPostTask = {
|
|
|
1311
1336
|
const bridge = await import('../memory/memory-bridge.js');
|
|
1312
1337
|
feedbackResult = await bridge.bridgeRecordFeedback({
|
|
1313
1338
|
taskId,
|
|
1339
|
+
task: params.task || undefined,
|
|
1314
1340
|
success,
|
|
1315
1341
|
quality,
|
|
1316
1342
|
agent,
|
|
@@ -3136,7 +3162,7 @@ export const hooksIntelligenceStats = {
|
|
|
3136
3162
|
catch {
|
|
3137
3163
|
memoryStats = {
|
|
3138
3164
|
trajectories: { total: 0, successful: 0 },
|
|
3139
|
-
patterns: { learned: 0, categories: {} },
|
|
3165
|
+
patterns: { learned: 0, successful: 0, failed: 0, unknown: 0, described: 0, categories: {} },
|
|
3140
3166
|
memory: { indexSize: 0, totalAccessCount: 0, memorySizeBytes: 0 },
|
|
3141
3167
|
routing: { decisions: 0, avgConfidence: 0 },
|
|
3142
3168
|
};
|
|
@@ -199,7 +199,7 @@ export const metaharnessTools = [
|
|
|
199
199
|
},
|
|
200
200
|
{
|
|
201
201
|
name: 'metaharness_genome',
|
|
202
|
-
description: 'ADR-150 — 7-section categorical readiness report from `metaharness genome <path>` (repo_type / agent_topology / risk_score / mcp_surface / test_confidence / publish_readiness). Use when you need the categorical view (vs numeric score). Pair with metaharness_score for the full readiness picture — score-alone is wrong because two harnesses with the same harnessFit can have very different agent_topology and mcp_surface. ' + MCP_SUCCESS_SEMANTIC,
|
|
202
|
+
description: 'ADR-150 — 7-section categorical readiness report from `metaharness genome <path>` (repo_type / agent_topology / risk_score / mcp_surface / test_confidence / publish_readiness). Upstream needs-work/blocked exit statuses are valid verdicts and return successfully as data.verdict + data.verdictExitCode; only a missing or malformed report is an error. Use when you need the categorical view (vs numeric score). Pair with metaharness_score for the full readiness picture — score-alone is wrong because two harnesses with the same harnessFit can have very different agent_topology and mcp_surface. ' + MCP_SUCCESS_SEMANTIC,
|
|
203
203
|
category: 'metaharness',
|
|
204
204
|
inputSchema: {
|
|
205
205
|
type: 'object',
|
|
@@ -275,6 +275,16 @@ export declare function getControllerRegistry(dbPath?: string): Promise<any | nu
|
|
|
275
275
|
* operator learns the cause instead of only the symptom.
|
|
276
276
|
*/
|
|
277
277
|
export declare function getBridgeFailureReason(): string | null;
|
|
278
|
+
/**
|
|
279
|
+
* Install a pre-initialized registry for deterministic bridge tests.
|
|
280
|
+
*
|
|
281
|
+
* The CLI test runner intentionally externalizes the optional
|
|
282
|
+
* `@claude-flow/memory` package so an unbuilt workspace can still exercise
|
|
283
|
+
* fallback paths. That also makes module-level mocking of ControllerRegistry
|
|
284
|
+
* environment-dependent. This narrow seam keeps native SQL regression tests
|
|
285
|
+
* independent of package build order without changing production startup.
|
|
286
|
+
*/
|
|
287
|
+
export declare function __setMemoryBridgeRegistryForTests(registry: any | null): void;
|
|
278
288
|
/**
|
|
279
289
|
* Shutdown the bridge and release resources.
|
|
280
290
|
*
|
|
@@ -322,6 +332,8 @@ export declare function bridgeSearchPatterns(options: {
|
|
|
322
332
|
*/
|
|
323
333
|
export declare function bridgeRecordFeedback(options: {
|
|
324
334
|
taskId: string;
|
|
335
|
+
/** Human-readable task text used as the semantic learning signal. */
|
|
336
|
+
task?: string;
|
|
325
337
|
success: boolean;
|
|
326
338
|
quality: number;
|
|
327
339
|
agent?: string;
|
|
@@ -23,6 +23,10 @@ import { createRequire } from 'node:module';
|
|
|
23
23
|
let registryPromise = null;
|
|
24
24
|
let registryInstance = null;
|
|
25
25
|
let bridgeAvailable = null;
|
|
26
|
+
// #2652/#2120: rows created before the status column existed receive NULL
|
|
27
|
+
// during migration. They are live rows, not tombstones. Every user-facing
|
|
28
|
+
// read/delete path must agree with list() about their visibility.
|
|
29
|
+
const ACTIVE_MEMORY_ROW_SQL = `(status = 'active' OR status IS NULL)`;
|
|
26
30
|
/**
|
|
27
31
|
* Why the bridge is unavailable, when it is.
|
|
28
32
|
*
|
|
@@ -994,7 +998,7 @@ export async function bridgeSearchEntries(options) {
|
|
|
994
998
|
const stmt = ctx.db.prepare(`
|
|
995
999
|
SELECT id, key, namespace, content, embedding, provenance_type
|
|
996
1000
|
FROM memory_entries
|
|
997
|
-
WHERE
|
|
1001
|
+
WHERE ${ACTIVE_MEMORY_ROW_SQL} ${whereExtra}
|
|
998
1002
|
LIMIT 1000
|
|
999
1003
|
`);
|
|
1000
1004
|
rows = filterParams.length > 0 ? stmt.all(...filterParams) : stmt.all();
|
|
@@ -1114,7 +1118,7 @@ export async function bridgeListEntries(options) {
|
|
|
1114
1118
|
// the `status = 'active'` filter matched zero. Treat NULL as
|
|
1115
1119
|
// "legacy-active" — the safe default for any entry that predates the
|
|
1116
1120
|
// status column.
|
|
1117
|
-
const statusFilter =
|
|
1121
|
+
const statusFilter = ACTIVE_MEMORY_ROW_SQL;
|
|
1118
1122
|
// Count
|
|
1119
1123
|
let total = 0;
|
|
1120
1124
|
try {
|
|
@@ -1206,7 +1210,7 @@ export async function bridgeGetEntry(options) {
|
|
|
1206
1210
|
const stmt = ctx.db.prepare(`
|
|
1207
1211
|
SELECT id, key, namespace, content, embedding, access_count, created_at, updated_at, tags
|
|
1208
1212
|
FROM memory_entries
|
|
1209
|
-
WHERE
|
|
1213
|
+
WHERE ${ACTIVE_MEMORY_ROW_SQL} AND key = ? AND namespace = ?
|
|
1210
1214
|
LIMIT 1
|
|
1211
1215
|
`);
|
|
1212
1216
|
row = stmt.get(key, namespace);
|
|
@@ -1274,7 +1278,7 @@ export async function bridgeDeleteEntry(options) {
|
|
|
1274
1278
|
const result = ctx.db.prepare(`
|
|
1275
1279
|
UPDATE memory_entries
|
|
1276
1280
|
SET status = 'deleted', updated_at = ?
|
|
1277
|
-
WHERE key = ? AND namespace = ? AND
|
|
1281
|
+
WHERE key = ? AND namespace = ? AND ${ACTIVE_MEMORY_ROW_SQL}
|
|
1278
1282
|
`).run(Date.now(), key, namespace);
|
|
1279
1283
|
changes = result?.changes ?? 0;
|
|
1280
1284
|
}
|
|
@@ -1306,7 +1310,7 @@ export async function bridgeDeleteEntry(options) {
|
|
|
1306
1310
|
}
|
|
1307
1311
|
let remaining = 0;
|
|
1308
1312
|
try {
|
|
1309
|
-
const row = ctx.db.prepare(`SELECT COUNT(*) as cnt FROM memory_entries WHERE
|
|
1313
|
+
const row = ctx.db.prepare(`SELECT COUNT(*) as cnt FROM memory_entries WHERE ${ACTIVE_MEMORY_ROW_SQL}`).get();
|
|
1310
1314
|
remaining = row?.cnt ?? 0;
|
|
1311
1315
|
}
|
|
1312
1316
|
catch {
|
|
@@ -1659,6 +1663,21 @@ export async function getControllerRegistry(dbPath) {
|
|
|
1659
1663
|
export function getBridgeFailureReason() {
|
|
1660
1664
|
return bridgeFailureReason;
|
|
1661
1665
|
}
|
|
1666
|
+
/**
|
|
1667
|
+
* Install a pre-initialized registry for deterministic bridge tests.
|
|
1668
|
+
*
|
|
1669
|
+
* The CLI test runner intentionally externalizes the optional
|
|
1670
|
+
* `@claude-flow/memory` package so an unbuilt workspace can still exercise
|
|
1671
|
+
* fallback paths. That also makes module-level mocking of ControllerRegistry
|
|
1672
|
+
* environment-dependent. This narrow seam keeps native SQL regression tests
|
|
1673
|
+
* independent of package build order without changing production startup.
|
|
1674
|
+
*/
|
|
1675
|
+
export function __setMemoryBridgeRegistryForTests(registry) {
|
|
1676
|
+
registryInstance = registry;
|
|
1677
|
+
registryPromise = registry ? Promise.resolve(registry) : null;
|
|
1678
|
+
bridgeAvailable = registry ? true : null;
|
|
1679
|
+
bridgeFailureReason = null;
|
|
1680
|
+
}
|
|
1662
1681
|
/**
|
|
1663
1682
|
* Shutdown the bridge and release resources.
|
|
1664
1683
|
*
|
|
@@ -1840,11 +1859,15 @@ export async function bridgeRecordFeedback(options) {
|
|
|
1840
1859
|
try {
|
|
1841
1860
|
const intelligence = await import('./intelligence.js');
|
|
1842
1861
|
const verdict = options.success ? 'success' : 'failure';
|
|
1862
|
+
const taskContext = options.task?.trim();
|
|
1843
1863
|
const recorded = await intelligence.recordTrajectory([{
|
|
1844
1864
|
type: 'action',
|
|
1845
|
-
|
|
1865
|
+
// Put the task description first so the embedding represents the
|
|
1866
|
+
// work, not just the bookkeeping suffix (#2812).
|
|
1867
|
+
content: `${taskContext ? `${taskContext} — ` : ''}Task ${options.taskId} completed by ${options.agent || 'unknown'} — success=${options.success}, quality=${options.quality.toFixed(3)}`,
|
|
1846
1868
|
metadata: {
|
|
1847
1869
|
taskId: options.taskId,
|
|
1870
|
+
task: taskContext,
|
|
1848
1871
|
agent: options.agent,
|
|
1849
1872
|
quality: options.quality,
|
|
1850
1873
|
duration: options.duration,
|
|
@@ -153,6 +153,10 @@ export function resolveDbPath(cliFlag) {
|
|
|
153
153
|
}
|
|
154
154
|
return path.join(getMemoryRoot(), 'memory.db');
|
|
155
155
|
}
|
|
156
|
+
// #2652/#2120: legacy rows with NULL status predate soft-delete semantics and
|
|
157
|
+
// are active. Keep fallback retrieve/delete aligned with list and the native
|
|
158
|
+
// bridge instead of making a row visible to one command but not another.
|
|
159
|
+
const ACTIVE_MEMORY_ROW_SQL = `(status = 'active' OR status IS NULL)`;
|
|
156
160
|
// ADR-053: Lazy import of AgentDB v3 bridge
|
|
157
161
|
let _bridge;
|
|
158
162
|
async function getBridge() {
|
|
@@ -1608,8 +1612,7 @@ export async function repairVectorIndexes(dbPath, opts = {}) {
|
|
|
1608
1612
|
*/
|
|
1609
1613
|
export async function initializeMemoryDatabase(options) {
|
|
1610
1614
|
const { backend = 'hybrid', dbPath: customPath, force = false, verbose = false, migrate = true } = options;
|
|
1611
|
-
const
|
|
1612
|
-
const dbPath = customPath || path.join(swarmDir, 'memory.db');
|
|
1615
|
+
const dbPath = resolveDbPath(customPath);
|
|
1613
1616
|
const dbDir = path.dirname(dbPath);
|
|
1614
1617
|
try {
|
|
1615
1618
|
// Create directory if needed
|
|
@@ -2857,7 +2860,7 @@ export async function listEntries(options) {
|
|
|
2857
2860
|
// that predate the status column may have NULL after migration.
|
|
2858
2861
|
// See memory-bridge.ts:bridgeListEntries for full context.
|
|
2859
2862
|
// Get total count
|
|
2860
|
-
const whereClauses = [
|
|
2863
|
+
const whereClauses = [ACTIVE_MEMORY_ROW_SQL];
|
|
2861
2864
|
const whereParams = [];
|
|
2862
2865
|
if (namespace) {
|
|
2863
2866
|
whereClauses.push('namespace = ?');
|
|
@@ -2967,7 +2970,7 @@ export async function getEntry(options) {
|
|
|
2967
2970
|
const getStmt = db.prepare(`
|
|
2968
2971
|
SELECT id, key, namespace, content, embedding, access_count, created_at, updated_at, tags
|
|
2969
2972
|
FROM memory_entries
|
|
2970
|
-
WHERE
|
|
2973
|
+
WHERE ${ACTIVE_MEMORY_ROW_SQL}
|
|
2971
2974
|
AND key = ?
|
|
2972
2975
|
AND namespace = ?
|
|
2973
2976
|
LIMIT 1
|
|
@@ -3081,7 +3084,7 @@ export async function deleteEntry(options) {
|
|
|
3081
3084
|
// Check if entry exists first
|
|
3082
3085
|
const checkStmt = db.prepare(`
|
|
3083
3086
|
SELECT id FROM memory_entries
|
|
3084
|
-
WHERE
|
|
3087
|
+
WHERE ${ACTIVE_MEMORY_ROW_SQL}
|
|
3085
3088
|
AND key = ?
|
|
3086
3089
|
AND namespace = ?
|
|
3087
3090
|
LIMIT 1
|
|
@@ -3095,7 +3098,7 @@ export async function deleteEntry(options) {
|
|
|
3095
3098
|
const checkResult = checkRows.length > 0 ? [{ values: checkRows }] : [];
|
|
3096
3099
|
if (!checkResult[0]?.values?.[0]) {
|
|
3097
3100
|
// Get remaining count before closing
|
|
3098
|
-
const countResult = db.exec(`SELECT COUNT(*) FROM memory_entries WHERE
|
|
3101
|
+
const countResult = db.exec(`SELECT COUNT(*) FROM memory_entries WHERE ${ACTIVE_MEMORY_ROW_SQL}`);
|
|
3099
3102
|
const remainingEntries = countResult[0]?.values?.[0]?.[0] || 0;
|
|
3100
3103
|
db.close();
|
|
3101
3104
|
return {
|
|
@@ -3116,10 +3119,10 @@ export async function deleteEntry(options) {
|
|
|
3116
3119
|
updated_at = strftime('%s', 'now') * 1000
|
|
3117
3120
|
WHERE key = ?
|
|
3118
3121
|
AND namespace = ?
|
|
3119
|
-
AND
|
|
3122
|
+
AND ${ACTIVE_MEMORY_ROW_SQL}
|
|
3120
3123
|
`, [key, namespace]);
|
|
3121
3124
|
// Get remaining count
|
|
3122
|
-
const countResult = db.exec(`SELECT COUNT(*) FROM memory_entries WHERE
|
|
3125
|
+
const countResult = db.exec(`SELECT COUNT(*) FROM memory_entries WHERE ${ACTIVE_MEMORY_ROW_SQL}`);
|
|
3123
3126
|
const remainingEntries = countResult[0]?.values?.[0]?.[0] || 0;
|
|
3124
3127
|
// Save updated database
|
|
3125
3128
|
const data = db.export();
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Turns raw `memory_entries` (what ruflo has been RECORDING for thousands of
|
|
5
5
|
* commits) into the structured intelligence substrate it has never populated:
|
|
6
|
-
* `episodes`
|
|
6
|
+
* `episodes` (+ `episode_embeddings`) → `reasoning_patterns`
|
|
7
|
+
* (+ `pattern_embeddings`) → `causal_edges`.
|
|
7
8
|
* This is the DISTILL/CONSOLIDATE half of the RETRIEVE→JUDGE→DISTILL→CONSOLIDATE
|
|
8
9
|
* pipeline; RETRIEVE (embeddings) was already populated, the rest was empty
|
|
9
10
|
* because the daemon's `consolidate` worker was a stub (see ADR-174 root cause).
|
|
@@ -28,7 +29,8 @@ import * as path from 'path';
|
|
|
28
29
|
import { distillTrajectoryContent, serialiseDistilled } from '../memory/structured-distill.js';
|
|
29
30
|
function emptyReport(dryRun, extra = {}) {
|
|
30
31
|
return {
|
|
31
|
-
processed: 0, episodes: 0,
|
|
32
|
+
processed: 0, episodes: 0, episodeEmbeddings: 0,
|
|
33
|
+
patterns: 0, patternEmbeddings: 0, causalEdges: 0,
|
|
32
34
|
promoted: 0, byProvenance: {}, namespaces: [], dryRun, spendUsd: 0, ...extra,
|
|
33
35
|
};
|
|
34
36
|
}
|
|
@@ -74,6 +76,20 @@ function judgeFeedback(content) {
|
|
|
74
76
|
catch { /* not JSON — treat as neutral */ }
|
|
75
77
|
return { success: true, reward: 0.5 };
|
|
76
78
|
}
|
|
79
|
+
/** Preserve an execution-observed lesson for Reflexion without presenting
|
|
80
|
+
* proxy/structural summaries as genuine self-critique. */
|
|
81
|
+
function feedbackCritique(content) {
|
|
82
|
+
try {
|
|
83
|
+
const value = JSON.parse(content);
|
|
84
|
+
for (const key of ['critique', 'error', 'message', 'summary', 'stderr']) {
|
|
85
|
+
if (typeof value[key] === 'string' && value[key].trim()) {
|
|
86
|
+
return value[key].trim().slice(0, 2000);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
catch { /* retain the original execution feedback below */ }
|
|
91
|
+
return `Observed execution feedback: ${content}`.slice(0, 2000);
|
|
92
|
+
}
|
|
77
93
|
/**
|
|
78
94
|
* Distill accumulated memory into the structured intelligence tables.
|
|
79
95
|
* Incremental, $0, transactional, provenance-tagged.
|
|
@@ -115,6 +131,17 @@ export async function runDistillation(options) {
|
|
|
115
131
|
db.close();
|
|
116
132
|
return { ...report, corrupt: true, skipped: 'memory DB reports corruption — run recoverMemoryDatabase first' };
|
|
117
133
|
}
|
|
134
|
+
// #2677 — older AgentDB stores may have episodes but no embedding table.
|
|
135
|
+
// Create the stable AgentDB v1 table only after the corruption gate, and
|
|
136
|
+
// never create it during a dry-run.
|
|
137
|
+
if (!dryRun && !tableExists('episode_embeddings')) {
|
|
138
|
+
db.exec(`CREATE TABLE episode_embeddings (
|
|
139
|
+
episode_id INTEGER PRIMARY KEY,
|
|
140
|
+
embedding BLOB NOT NULL,
|
|
141
|
+
embedding_model TEXT DEFAULT 'all-MiniLM-L6-v2',
|
|
142
|
+
FOREIGN KEY(episode_id) REFERENCES episodes(id) ON DELETE CASCADE
|
|
143
|
+
)`);
|
|
144
|
+
}
|
|
118
145
|
// M0: incremental cursor table (per namespace, by monotonic rowid).
|
|
119
146
|
db.exec(`CREATE TABLE IF NOT EXISTS distill_state (
|
|
120
147
|
namespace TEXT PRIMARY KEY,
|
|
@@ -128,10 +155,54 @@ export async function runDistillation(options) {
|
|
|
128
155
|
const getCursor = db.prepare('SELECT last_rowid FROM distill_state WHERE namespace = ?');
|
|
129
156
|
const setCursor = db.prepare('INSERT INTO distill_state (namespace, last_rowid, last_run_at) VALUES (?, ?, ?) ' +
|
|
130
157
|
'ON CONFLICT(namespace) DO UPDATE SET last_rowid = excluded.last_rowid, last_run_at = excluded.last_run_at');
|
|
131
|
-
const insEpisode = db.prepare('INSERT INTO episodes (session_id, task, input, output, reward, success, tags, metadata) VALUES (
|
|
158
|
+
const insEpisode = db.prepare('INSERT INTO episodes (session_id, task, input, output, critique, reward, success, tags, metadata) VALUES (?,?,?,?,?,?,?,?,?)');
|
|
159
|
+
const insEpisodeEmbedding = dryRun ? null : db.prepare('INSERT OR REPLACE INTO episode_embeddings (episode_id, embedding) VALUES (?, ?)');
|
|
132
160
|
const insPattern = db.prepare('INSERT INTO reasoning_patterns (task_type, approach, success_rate, uses, avg_reward, tags, metadata) VALUES (?,?,?,?,?,?,?)');
|
|
133
161
|
const insPatEmb = db.prepare('INSERT OR REPLACE INTO pattern_embeddings (pattern_id, embedding) VALUES (?, ?)');
|
|
134
162
|
const insEdge = db.prepare('INSERT INTO causal_edges (from_memory_id, from_memory_type, to_memory_id, to_memory_type, similarity, confidence, mechanism, metadata) VALUES (?,?,?,?,?,?,?,?)');
|
|
163
|
+
// Repair episodes produced by older distillation releases. Their metadata
|
|
164
|
+
// records sourceIds; if that provenance is absent, exact input matching is
|
|
165
|
+
// a conservative fallback. Never invent a vector or call a model.
|
|
166
|
+
if (!dryRun) {
|
|
167
|
+
const missingEpisodes = db.prepare(`
|
|
168
|
+
SELECT e.id, e.input, e.metadata
|
|
169
|
+
FROM episodes e
|
|
170
|
+
LEFT JOIN episode_embeddings ee ON ee.episode_id = e.id
|
|
171
|
+
WHERE ee.episode_id IS NULL AND e.session_id LIKE 'distill:%'
|
|
172
|
+
`).all();
|
|
173
|
+
const embeddingById = db.prepare('SELECT embedding FROM memory_entries WHERE id = ? AND embedding IS NOT NULL');
|
|
174
|
+
const embeddingByContent = db.prepare('SELECT embedding FROM memory_entries WHERE content = ? AND embedding IS NOT NULL LIMIT 1');
|
|
175
|
+
const missingCritiques = db.prepare(`
|
|
176
|
+
SELECT id, input FROM episodes
|
|
177
|
+
WHERE session_id LIKE 'distill:feedback%'
|
|
178
|
+
AND length(trim(coalesce(critique,''))) = 0
|
|
179
|
+
`).all();
|
|
180
|
+
const updateCritique = db.prepare('UPDATE episodes SET critique = ? WHERE id = ?');
|
|
181
|
+
const backfill = db.transaction(() => {
|
|
182
|
+
for (const episode of missingEpisodes) {
|
|
183
|
+
let sourceId;
|
|
184
|
+
try {
|
|
185
|
+
const metadata = JSON.parse(episode.metadata ?? '{}');
|
|
186
|
+
if (Array.isArray(metadata.sourceIds) && typeof metadata.sourceIds[0] === 'string') {
|
|
187
|
+
sourceId = metadata.sourceIds[0];
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
catch { /* use exact-content fallback */ }
|
|
191
|
+
let raw = sourceId ? embeddingById.get(sourceId)?.embedding : undefined;
|
|
192
|
+
if (!raw)
|
|
193
|
+
raw = embeddingByContent.get(episode.input ?? '')?.embedding;
|
|
194
|
+
const vector = parseEmbedding(raw);
|
|
195
|
+
if (!vector)
|
|
196
|
+
continue;
|
|
197
|
+
insEpisodeEmbedding.run(episode.id, Buffer.from(Float32Array.from(vector).buffer));
|
|
198
|
+
report.episodeEmbeddings++;
|
|
199
|
+
}
|
|
200
|
+
for (const episode of missingCritiques) {
|
|
201
|
+
updateCritique.run(feedbackCritique(episode.input ?? ''), episode.id);
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
backfill();
|
|
205
|
+
}
|
|
135
206
|
let remaining = typeof maxEntries === 'number' ? maxEntries : Infinity;
|
|
136
207
|
for (const ns of nsList) {
|
|
137
208
|
if (remaining <= 0)
|
|
@@ -200,8 +271,14 @@ export async function runDistillation(options) {
|
|
|
200
271
|
sourceIds: cl.members.map(m => m.id).slice(0, 25),
|
|
201
272
|
paths: distilled.paths.slice(0, 10), namespace: ns, distilledBy: 'structural',
|
|
202
273
|
};
|
|
203
|
-
const epInfo = insEpisode.run(`distill:${ns}`, approach, cl.rep.content.slice(0, 2000), '', avgReward, promoted && cl.successCount > 0 ? 1 : 0, JSON.stringify(distilled.labels), JSON.stringify(provMeta));
|
|
274
|
+
const epInfo = insEpisode.run(`distill:${ns}`, approach, cl.rep.content.slice(0, 2000), '', cl.provenance === 'oracle:test-exec' ? feedbackCritique(cl.rep.content) : null, avgReward, promoted && cl.successCount > 0 ? 1 : 0, JSON.stringify(distilled.labels), JSON.stringify(provMeta));
|
|
204
275
|
report.episodes++;
|
|
276
|
+
const epId = Number(epInfo.lastInsertRowid);
|
|
277
|
+
// ReflexionMemory.retrieveRelevant() INNER JOINs this table.
|
|
278
|
+
// Reuse the source embedding so every distilled episode is
|
|
279
|
+
// immediately retrievable without an embedding-model call.
|
|
280
|
+
insEpisodeEmbedding.run(epId, Buffer.from(Float32Array.from(embVec).buffer));
|
|
281
|
+
report.episodeEmbeddings++;
|
|
205
282
|
const patInfo = insPattern.run(taskType, approach, successRate, uses, avgReward, JSON.stringify(distilled.labels), JSON.stringify(provMeta));
|
|
206
283
|
const patternId = Number(patInfo.lastInsertRowid);
|
|
207
284
|
report.patterns++;
|
|
@@ -217,7 +294,6 @@ export async function runDistillation(options) {
|
|
|
217
294
|
// rowids). Explicitly typed co-occurrence / proxy-tier / non-
|
|
218
295
|
// promoted: may rank retrieval, must NOT justify autonomous action
|
|
219
296
|
// (ADR-174 edge contract).
|
|
220
|
-
const epId = Number(epInfo.lastInsertRowid);
|
|
221
297
|
if (prevEpId !== null) {
|
|
222
298
|
insEdge.run(prevEpId, 'episode', epId, 'episode', 0, 0.3, 'co-occurrence', JSON.stringify({
|
|
223
299
|
edge_type: 'cooccurrence',
|
|
@@ -1545,6 +1545,7 @@ export class WorkerDaemon extends EventEmitter {
|
|
|
1545
1545
|
// instead of becoming their own distinct pattern.
|
|
1546
1546
|
duplicatesRemoved: Math.max(0, report.processed - report.patterns),
|
|
1547
1547
|
episodes: report.episodes,
|
|
1548
|
+
episodeEmbeddings: report.episodeEmbeddings,
|
|
1548
1549
|
patternEmbeddings: report.patternEmbeddings,
|
|
1549
1550
|
causalEdges: report.causalEdges,
|
|
1550
1551
|
promoted: report.promoted,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@claude-flow/cli",
|
|
3
|
-
"version": "3.32.
|
|
3
|
+
"version": "3.32.37",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Ruflo CLI - Enterprise AI agent orchestration with 60+ specialized agents, swarm coordination, MCP server, self-learning hooks, and vector memory for Claude Code",
|
|
6
6
|
"main": "dist/src/index.js",
|