claude-flow 3.32.34 → 3.32.36
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 +47 -4
- 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.map +1 -1
- package/node_modules/@claude-flow/codex/dist/initializer.js +9 -13
- 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 +4 -4
- package/v3/@claude-flow/cli/dist/src/commands/agent.js +50 -2
- package/v3/@claude-flow/cli/dist/src/commands/doctor.js +159 -11
- package/v3/@claude-flow/cli/dist/src/commands/hooks.js +63 -4
- 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/metaharness.js +4 -0
- package/v3/@claude-flow/cli/dist/src/commands/swarm.js +89 -5
- package/v3/@claude-flow/cli/dist/src/config-adapter.js +1 -0
- package/v3/@claude-flow/cli/dist/src/index.js +3 -2
- package/v3/@claude-flow/cli/dist/src/init/executor.js +16 -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/agent-tools.js +10 -0
- 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 +120 -60
- package/v3/@claude-flow/cli/dist/src/mcp-tools/memory-tools.js +14 -2
- package/v3/@claude-flow/cli/dist/src/mcp-tools/metaharness-tools.js +7 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/swarm-tools.d.ts +16 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/swarm-tools.js +172 -7
- package/v3/@claude-flow/cli/dist/src/memory/memory-bridge.d.ts +2 -0
- package/v3/@claude-flow/cli/dist/src/memory/memory-bridge.js +5 -1
- package/v3/@claude-flow/cli/dist/src/memory/memory-initializer.d.ts +9 -1
- package/v3/@claude-flow/cli/dist/src/memory/memory-initializer.js +66 -19
- package/v3/@claude-flow/cli/dist/src/services/daemon-autostart.js +7 -4
- package/v3/@claude-flow/cli/dist/src/services/flywheel-receipt.d.ts +3 -0
- package/v3/@claude-flow/cli/dist/src/services/flywheel-receipt.js +1 -0
- package/v3/@claude-flow/cli/dist/src/services/harness-flywheel-runtime.d.ts +6 -0
- package/v3/@claude-flow/cli/dist/src/services/harness-flywheel-runtime.js +19 -19
- package/v3/@claude-flow/cli/dist/src/services/harness-flywheel.d.ts +2 -0
- package/v3/@claude-flow/cli/dist/src/services/harness-flywheel.js +1 -0
- package/v3/@claude-flow/cli/dist/src/services/harness-project-anchor.d.ts +23 -0
- package/v3/@claude-flow/cli/dist/src/services/harness-project-anchor.js +129 -0
- package/v3/@claude-flow/cli/dist/src/services/learned-routing.d.ts +34 -0
- package/v3/@claude-flow/cli/dist/src/services/learned-routing.js +85 -0
- 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/pheromone-adaptive.d.ts +71 -0
- package/v3/@claude-flow/cli/dist/src/services/pheromone-adaptive.js +214 -0
- package/v3/@claude-flow/cli/dist/src/services/worker-daemon.js +5 -1
- package/v3/@claude-flow/cli/package.json +1 -1
- package/v3/@claude-flow/shared/dist/core/config/loader.js +1 -1
- package/v3/@claude-flow/shared/dist/core/config/schema.d.ts +12 -12
- package/v3/@claude-flow/shared/dist/core/config/schema.js +1 -1
- package/v3/@claude-flow/shared/dist/core/interfaces/coordinator.interface.d.ts +1 -1
- package/v3/@claude-flow/shared/dist/types/swarm.types.js +18 -0
|
@@ -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
|
},
|
|
@@ -8,6 +8,7 @@ import { dirname, join, resolve } from 'path';
|
|
|
8
8
|
import { getProjectCwd } from './types.js';
|
|
9
9
|
import { validateIdentifier, validateText, validatePath } from './validate-input.js';
|
|
10
10
|
import { checkCommandLoop, recordCommandOutcome } from './tool-loop-guardrail.js';
|
|
11
|
+
import { buildLearnedRoutingPatterns, } from '../services/learned-routing.js';
|
|
11
12
|
// Real vector search functions - lazy loaded to avoid circular imports
|
|
12
13
|
let searchEntriesFn = null;
|
|
13
14
|
/**
|
|
@@ -185,6 +186,13 @@ function saveRoutingOutcomes(outcomes) {
|
|
|
185
186
|
// Cap at 500 entries to bound file size
|
|
186
187
|
const capped = outcomes.slice(-500);
|
|
187
188
|
writeFileSync(ROUTING_OUTCOMES_PATH, JSON.stringify({ outcomes: capped }, null, 2));
|
|
189
|
+
// A long-lived MCP process must observe the new label on the next route.
|
|
190
|
+
// The prior singleton cache made the learned store inert until restart.
|
|
191
|
+
semanticRouter = null;
|
|
192
|
+
nativeVectorDb = null;
|
|
193
|
+
semanticRouterInitialized = false;
|
|
194
|
+
routerBackend = 'none';
|
|
195
|
+
TASK_PATTERN_EMBEDDINGS.clear();
|
|
188
196
|
}
|
|
189
197
|
catch { /* non-critical */ }
|
|
190
198
|
}
|
|
@@ -194,24 +202,7 @@ function saveRoutingOutcomes(outcomes) {
|
|
|
194
202
|
* merged into both the native HNSW and pure-JS semantic routers.
|
|
195
203
|
*/
|
|
196
204
|
function loadLearnedPatterns() {
|
|
197
|
-
|
|
198
|
-
const byAgent = {};
|
|
199
|
-
for (const o of outcomes) {
|
|
200
|
-
if (!o.success || !o.agent || !o.keywords?.length)
|
|
201
|
-
continue;
|
|
202
|
-
if (!byAgent[o.agent])
|
|
203
|
-
byAgent[o.agent] = new Set();
|
|
204
|
-
for (const kw of o.keywords)
|
|
205
|
-
byAgent[o.agent].add(kw);
|
|
206
|
-
}
|
|
207
|
-
const patterns = {};
|
|
208
|
-
for (const [agent, kwSet] of Object.entries(byAgent)) {
|
|
209
|
-
patterns[`learned-${agent}`] = {
|
|
210
|
-
keywords: [...kwSet].slice(0, 50),
|
|
211
|
-
agents: [agent],
|
|
212
|
-
};
|
|
213
|
-
}
|
|
214
|
-
return patterns;
|
|
205
|
+
return buildLearnedRoutingPatterns(loadRoutingOutcomes());
|
|
215
206
|
}
|
|
216
207
|
/**
|
|
217
208
|
* Merge static TASK_PATTERNS with runtime-learned patterns.
|
|
@@ -290,46 +281,55 @@ async function getSemanticRouter() {
|
|
|
290
281
|
// STEP 1: Try native VectorDb from @ruvector/router (HNSW-backed)
|
|
291
282
|
// Note: Native VectorDb uses a persistent database file which can have lock issues
|
|
292
283
|
// in concurrent environments. We try it first but fall back gracefully to pure JS.
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
const
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
284
|
+
// Deterministic/test and lock-constrained environments may force the
|
|
285
|
+
// pure-JS router; production continues to prefer the native backend.
|
|
286
|
+
if (process.env.CLAUDE_FLOW_DISABLE_NATIVE_ROUTER !== '1')
|
|
287
|
+
try {
|
|
288
|
+
// Use createRequire for ESM compatibility with native modules
|
|
289
|
+
const { createRequire } = await import('module');
|
|
290
|
+
const require = createRequire(import.meta.url);
|
|
291
|
+
const router = require('@ruvector/router');
|
|
292
|
+
if (router.VectorDb && router.DistanceMetric) {
|
|
293
|
+
// Try to create VectorDb - may fail with lock error in concurrent envs
|
|
294
|
+
const db = new router.VectorDb({
|
|
295
|
+
dimensions: 384,
|
|
296
|
+
distanceMetric: router.DistanceMetric.Cosine,
|
|
297
|
+
hnswM: 16,
|
|
298
|
+
hnswEfConstruction: 200,
|
|
299
|
+
hnswEfSearch: 100,
|
|
300
|
+
});
|
|
301
|
+
// Initialize with static + runtime-learned task patterns
|
|
302
|
+
for (const [patternName, { keywords }] of Object.entries(getMergedTaskPatterns())) {
|
|
303
|
+
for (const keyword of keywords) {
|
|
304
|
+
const embedding = generateSimpleEmbedding(keyword);
|
|
305
|
+
db.insert(`${patternName}:${keyword}`, embedding);
|
|
306
|
+
TASK_PATTERN_EMBEDDINGS.set(`${patternName}:${keyword}`, embedding);
|
|
307
|
+
}
|
|
313
308
|
}
|
|
309
|
+
nativeVectorDb = db;
|
|
310
|
+
routerBackend = 'native';
|
|
311
|
+
console.log('[hooks] Semantic router initialized: native VectorDb (HNSW, 16k+ routes/s)');
|
|
312
|
+
return { router: null, backend: routerBackend, native: nativeVectorDb };
|
|
314
313
|
}
|
|
315
|
-
nativeVectorDb = db;
|
|
316
|
-
routerBackend = 'native';
|
|
317
|
-
console.log('[hooks] Semantic router initialized: native VectorDb (HNSW, 16k+ routes/s)');
|
|
318
|
-
return { router: null, backend: routerBackend, native: nativeVectorDb };
|
|
319
314
|
}
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
}
|
|
315
|
+
catch (err) {
|
|
316
|
+
// Native not available or database locked - fall back to pure JS
|
|
317
|
+
// Common errors: "Database already open. Cannot acquire lock." or "MODULE_NOT_FOUND"
|
|
318
|
+
// This is expected in concurrent environments or when binary isn't installed
|
|
319
|
+
}
|
|
326
320
|
// STEP 2: Fall back to pure JS SemanticRouter
|
|
327
321
|
try {
|
|
328
322
|
const { SemanticRouter } = await import('../ruvector/semantic-router.js');
|
|
329
323
|
semanticRouter = new SemanticRouter({ dimension: 384 });
|
|
330
|
-
for (const [patternName, { keywords, agents }] of Object.entries(getMergedTaskPatterns())) {
|
|
324
|
+
for (const [patternName, { keywords, agents, source, support, reliability }] of Object.entries(getMergedTaskPatterns())) {
|
|
331
325
|
const embeddings = keywords.map(kw => generateSimpleEmbedding(kw));
|
|
332
|
-
semanticRouter.addIntentWithEmbeddings(patternName, embeddings, {
|
|
326
|
+
semanticRouter.addIntentWithEmbeddings(patternName, embeddings, {
|
|
327
|
+
agents,
|
|
328
|
+
keywords,
|
|
329
|
+
source: source ?? 'static',
|
|
330
|
+
support,
|
|
331
|
+
reliability,
|
|
332
|
+
});
|
|
333
333
|
// Cache embeddings for keywords
|
|
334
334
|
keywords.forEach((kw, i) => {
|
|
335
335
|
TASK_PATTERN_EMBEDDINGS.set(kw, embeddings[i]);
|
|
@@ -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: {
|
|
@@ -946,6 +964,9 @@ export const hooksRoute = {
|
|
|
946
964
|
score: 1 - r.score, // Native uses distance (lower is better), convert to similarity
|
|
947
965
|
metadata: {
|
|
948
966
|
agents: pattern?.agents || (patternName.startsWith('learned-') ? [patternName.slice(8)] : ['coder']),
|
|
967
|
+
source: pattern?.source ?? (patternName.startsWith('learned-') ? 'learned' : 'static'),
|
|
968
|
+
support: pattern?.support,
|
|
969
|
+
reliability: pattern?.reliability,
|
|
949
970
|
},
|
|
950
971
|
};
|
|
951
972
|
});
|
|
@@ -966,8 +987,18 @@ export const hooksRoute = {
|
|
|
966
987
|
let agents;
|
|
967
988
|
let confidence;
|
|
968
989
|
let matchedPattern = '';
|
|
969
|
-
|
|
970
|
-
|
|
990
|
+
const eligibleSemantic = semanticResult.find((match) => {
|
|
991
|
+
if (match.score <= 0.4)
|
|
992
|
+
return false;
|
|
993
|
+
const learned = match.intent.startsWith('learned-') || match.metadata.source === 'learned';
|
|
994
|
+
if (!learned)
|
|
995
|
+
return true;
|
|
996
|
+
return match.score >= 0.65
|
|
997
|
+
&& Number(match.metadata.support ?? 0) >= 2
|
|
998
|
+
&& Number(match.metadata.reliability ?? 0) >= 0.75;
|
|
999
|
+
});
|
|
1000
|
+
if (eligibleSemantic) {
|
|
1001
|
+
const topMatch = eligibleSemantic;
|
|
971
1002
|
agents = topMatch.metadata.agents || ['coder', 'researcher'];
|
|
972
1003
|
confidence = topMatch.score;
|
|
973
1004
|
matchedPattern = topMatch.intent;
|
|
@@ -1061,21 +1092,28 @@ export const hooksMetrics = {
|
|
|
1061
1092
|
agentCounts[o.agent] = (agentCounts[o.agent] || 0) + 1;
|
|
1062
1093
|
}
|
|
1063
1094
|
const topAgent = Object.entries(agentCounts).sort((a, b) => b[1] - a[1])[0]?.[0] ?? null;
|
|
1064
|
-
const successful = stats.trajectories.successful;
|
|
1065
|
-
const total = stats.trajectories.total;
|
|
1066
|
-
const failed = Math.max(0, total - successful);
|
|
1067
1095
|
return {
|
|
1068
1096
|
_real: true,
|
|
1069
1097
|
_dataSource: 'intelligence-stats + routing-outcomes',
|
|
1070
1098
|
period,
|
|
1071
1099
|
patterns: {
|
|
1072
1100
|
total: stats.patterns.learned,
|
|
1073
|
-
successful,
|
|
1074
|
-
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,
|
|
1075
1108
|
avgConfidence: stats.routing.avgConfidence || null,
|
|
1076
1109
|
},
|
|
1077
1110
|
agents: {
|
|
1078
|
-
|
|
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,
|
|
1079
1117
|
totalRoutes: stats.routing.decisions,
|
|
1080
1118
|
topAgent,
|
|
1081
1119
|
},
|
|
@@ -1084,7 +1122,7 @@ export const hooksMetrics = {
|
|
|
1084
1122
|
successRate,
|
|
1085
1123
|
avgRiskScore: null,
|
|
1086
1124
|
},
|
|
1087
|
-
_note:
|
|
1125
|
+
_note: stats.patterns.learned === 0 && totalCommands === 0
|
|
1088
1126
|
? 'No metrics data collected yet. Run hooks_post-task / hooks_intelligence_trajectory-end / hooks_route to populate.'
|
|
1089
1127
|
: undefined,
|
|
1090
1128
|
lastUpdated: new Date().toISOString(),
|
|
@@ -1249,6 +1287,10 @@ export const hooksPostTask = {
|
|
|
1249
1287
|
agent: { type: 'string', description: 'Agent that completed the task' },
|
|
1250
1288
|
quality: { type: 'number', description: 'Quality score (0-1)' },
|
|
1251
1289
|
task: { type: 'string', description: 'Task description text (used for learning keyword extraction)' },
|
|
1290
|
+
duration: { type: 'number', description: 'Observed task duration in milliseconds (used by pheromone-adaptive topology)' },
|
|
1291
|
+
latencyBudgetMs: { type: 'number', description: 'Latency budget used to normalize duration (default 60000ms)' },
|
|
1292
|
+
consensusAlignment: { type: 'number', description: 'Agreement with accepted swarm consensus in [0,1] (default quality)' },
|
|
1293
|
+
agentRole: { type: 'string', description: 'Role for role-local pheromone normalization (default agent)' },
|
|
1252
1294
|
storeDecisions: { type: 'boolean', description: 'Also store routing decision in memory DB' },
|
|
1253
1295
|
// ADR-147 P2: nested-subagent spawn-tree capture
|
|
1254
1296
|
parentAgentId: { type: 'string', description: 'ID of the parent agent (from Claude Code\'s parent_agent_id OTel span tag / x-claude-code-parent-agent-id header). Omit for top-level work.' },
|
|
@@ -1294,6 +1336,7 @@ export const hooksPostTask = {
|
|
|
1294
1336
|
const bridge = await import('../memory/memory-bridge.js');
|
|
1295
1337
|
feedbackResult = await bridge.bridgeRecordFeedback({
|
|
1296
1338
|
taskId,
|
|
1339
|
+
task: params.task || undefined,
|
|
1297
1340
|
success,
|
|
1298
1341
|
quality,
|
|
1299
1342
|
agent,
|
|
@@ -1405,6 +1448,22 @@ export const hooksPostTask = {
|
|
|
1405
1448
|
}
|
|
1406
1449
|
catch { /* non-critical */ }
|
|
1407
1450
|
}
|
|
1451
|
+
let pheromone;
|
|
1452
|
+
if (agent) {
|
|
1453
|
+
try {
|
|
1454
|
+
const { recordSwarmPheromoneSignal } = await import('./swarm-tools.js');
|
|
1455
|
+
const observedDuration = Math.max(0, Number(params.duration ?? (Date.now() - startTime)));
|
|
1456
|
+
const latencyBudgetMs = Math.max(1, Number(params.latencyBudgetMs ?? 60_000));
|
|
1457
|
+
pheromone = recordSwarmPheromoneSignal({
|
|
1458
|
+
agentId: agent,
|
|
1459
|
+
role: String(params.agentRole ?? agent),
|
|
1460
|
+
taskSuccess: success ? 1 : 0,
|
|
1461
|
+
normalizedLatency: Math.max(0, Math.min(1, observedDuration / latencyBudgetMs)),
|
|
1462
|
+
consensusAlignment: Math.max(0, Math.min(1, Number(params.consensusAlignment ?? quality))),
|
|
1463
|
+
});
|
|
1464
|
+
}
|
|
1465
|
+
catch { /* no active APSC swarm or optional path unavailable */ }
|
|
1466
|
+
}
|
|
1408
1467
|
const duration = Date.now() - startTime;
|
|
1409
1468
|
// Persist to auto-memory-store for statusline visibility
|
|
1410
1469
|
try {
|
|
@@ -1444,6 +1503,7 @@ export const hooksPostTask = {
|
|
|
1444
1503
|
outcomePersisted,
|
|
1445
1504
|
},
|
|
1446
1505
|
quality,
|
|
1506
|
+
pheromone,
|
|
1447
1507
|
feedback: feedbackResult ? {
|
|
1448
1508
|
recorded: feedbackResult.success,
|
|
1449
1509
|
controller: feedbackResult.controller,
|
|
@@ -3102,7 +3162,7 @@ export const hooksIntelligenceStats = {
|
|
|
3102
3162
|
catch {
|
|
3103
3163
|
memoryStats = {
|
|
3104
3164
|
trajectories: { total: 0, successful: 0 },
|
|
3105
|
-
patterns: { learned: 0, categories: {} },
|
|
3165
|
+
patterns: { learned: 0, successful: 0, failed: 0, unknown: 0, described: 0, categories: {} },
|
|
3106
3166
|
memory: { indexSize: 0, totalAccessCount: 0, memorySizeBytes: 0 },
|
|
3107
3167
|
routing: { decisions: 0, avgConfidence: 0 },
|
|
3108
3168
|
};
|
|
@@ -1175,6 +1175,7 @@ export const memoryTools = [
|
|
|
1175
1175
|
handler: async (input) => {
|
|
1176
1176
|
await ensureInitialized();
|
|
1177
1177
|
const { listEntries, deleteEntry } = await getMemoryFunctions();
|
|
1178
|
+
const startedAt = Date.now();
|
|
1178
1179
|
const dryRun = input.dryRun !== false; // default true
|
|
1179
1180
|
const namespace = input.namespace ? String(input.namespace) : undefined;
|
|
1180
1181
|
if (namespace) {
|
|
@@ -1193,6 +1194,7 @@ export const memoryTools = [
|
|
|
1193
1194
|
});
|
|
1194
1195
|
let freedBytes = 0;
|
|
1195
1196
|
let deleted = 0;
|
|
1197
|
+
let vectorsRemoved = 0;
|
|
1196
1198
|
if (!dryRun) {
|
|
1197
1199
|
for (const e of expired) {
|
|
1198
1200
|
try {
|
|
@@ -1206,11 +1208,21 @@ export const memoryTools = [
|
|
|
1206
1208
|
else {
|
|
1207
1209
|
freedBytes = expired.reduce((s, e) => s + (e.size || 0), 0);
|
|
1208
1210
|
}
|
|
1211
|
+
if (!dryRun) {
|
|
1212
|
+
const { reconcileHNSWIndex } = await import('../memory/memory-initializer.js');
|
|
1213
|
+
vectorsRemoved = await reconcileHNSWIndex();
|
|
1214
|
+
}
|
|
1215
|
+
const formatted = freedBytes < 1024
|
|
1216
|
+
? `${freedBytes} B`
|
|
1217
|
+
: freedBytes < 1024 * 1024
|
|
1218
|
+
? `${(freedBytes / 1024).toFixed(1)} KiB`
|
|
1219
|
+
: `${(freedBytes / (1024 * 1024)).toFixed(1)} MiB`;
|
|
1209
1220
|
return {
|
|
1210
1221
|
dryRun,
|
|
1211
1222
|
candidates: { expired: expired.length, stale: 0, lowQuality: 0, total: expired.length },
|
|
1212
|
-
deleted: { entries: dryRun ? 0 : deleted, vectors:
|
|
1213
|
-
freed: { bytes: freedBytes },
|
|
1223
|
+
deleted: { entries: dryRun ? 0 : deleted, vectors: vectorsRemoved, patterns: 0 },
|
|
1224
|
+
freed: { bytes: freedBytes, formatted },
|
|
1225
|
+
duration: Date.now() - startedAt,
|
|
1214
1226
|
note: dryRun ? 'dry run — re-run with dryRun:false to delete' : undefined,
|
|
1215
1227
|
};
|
|
1216
1228
|
},
|
|
@@ -700,6 +700,9 @@ export const metaharnessTools = [
|
|
|
700
700
|
confirm: { type: 'boolean', description: 'Required for promote; never inferred', default: false },
|
|
701
701
|
maxConcurrency: { type: 'number', description: 'ADR-324 hard local candidate-evaluation concurrency cap (1-8)', default: 2 },
|
|
702
702
|
timeoutMs: { type: 'number', description: 'Abort concurrent evaluation after this wall-clock limit', default: 120000 },
|
|
703
|
+
anchorPath: { type: 'string', description: 'Project-contained human-labelled anchor JSON; requires anchorHash' },
|
|
704
|
+
anchorHash: { type: 'string', description: 'Pinned sha256 of canonical anchor tasks; requires anchorPath' },
|
|
705
|
+
anchorManifestPath: { type: 'string', description: 'Project-contained anchor manifest path (default .claude/eval/flywheel-anchor.manifest.json)' },
|
|
703
706
|
approvalIds: { type: 'array', items: { type: 'string' }, description: 'Scoped ADR-324 approval IDs for privileged promotion' },
|
|
704
707
|
},
|
|
705
708
|
required: ['operation'],
|
|
@@ -721,6 +724,7 @@ export const metaharnessTools = [
|
|
|
721
724
|
if (operation === 'receipts') {
|
|
722
725
|
const data = listFlywheelReceipts(projectRoot).map(({ receipt, state }) => ({
|
|
723
726
|
receiptId: receipt.payload.receiptId,
|
|
727
|
+
anchorRef: receipt.payload.anchorRef,
|
|
724
728
|
candidateId: receipt.payload.candidateId,
|
|
725
729
|
baselineRef: receipt.payload.baselineRef,
|
|
726
730
|
decision: receipt.payload.decision,
|
|
@@ -748,6 +752,9 @@ export const metaharnessTools = [
|
|
|
748
752
|
receiptPublicKeyPem: publicKeyPath ? readFileSync(publicKeyPath, 'utf8') : undefined,
|
|
749
753
|
maxConcurrency: Number(input.maxConcurrency ?? 2),
|
|
750
754
|
evaluationTimeoutMs: Number(input.timeoutMs ?? 120_000),
|
|
755
|
+
anchorPath: input.anchorPath ? String(input.anchorPath) : undefined,
|
|
756
|
+
anchorHash: input.anchorHash ? String(input.anchorHash) : undefined,
|
|
757
|
+
anchorManifestPath: input.anchorManifestPath ? String(input.anchorManifestPath) : undefined,
|
|
751
758
|
});
|
|
752
759
|
return { success: data.ran, data, degraded: false, exitCode: data.ran ? 0 : 1 };
|
|
753
760
|
}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* Replaces previous stub implementations with real state tracking.
|
|
6
6
|
*/
|
|
7
7
|
import { type MCPTool } from './types.js';
|
|
8
|
+
import { type ApscDecision, type ApscSignal } from '../services/pheromone-adaptive.js';
|
|
8
9
|
interface SwarmState {
|
|
9
10
|
swarmId: string;
|
|
10
11
|
topology: string;
|
|
@@ -32,6 +33,21 @@ interface SwarmStore {
|
|
|
32
33
|
}
|
|
33
34
|
export declare function loadSwarmStore(): SwarmStore;
|
|
34
35
|
export declare function saveSwarmStore(store: SwarmStore): void;
|
|
36
|
+
/** Shared by hooks_post-task so outcome labels immediately drive APSC. */
|
|
37
|
+
export declare function recordSwarmPheromoneSignal(signal: ApscSignal): {
|
|
38
|
+
active: false;
|
|
39
|
+
reason: string;
|
|
40
|
+
} | {
|
|
41
|
+
active: true;
|
|
42
|
+
swarmId: string;
|
|
43
|
+
decision: ApscDecision;
|
|
44
|
+
};
|
|
45
|
+
/** Scheduling gate used by agent_execute. Dry-run topologies remain eligible. */
|
|
46
|
+
export declare function pheromoneAgentEligibility(agentId: string): {
|
|
47
|
+
eligible: boolean;
|
|
48
|
+
active: boolean;
|
|
49
|
+
reason?: string;
|
|
50
|
+
};
|
|
35
51
|
export declare const swarmTools: MCPTool[];
|
|
36
52
|
export {};
|
|
37
53
|
//# sourceMappingURL=swarm-tools.d.ts.map
|