claude-flow 3.6.13 → 3.6.15
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/scheduled_tasks.lock +1 -0
- package/package.json +13 -2
- package/v3/@claude-flow/cli/dist/src/mcp-tools/agentdb-tools.js +36 -6
- package/v3/@claude-flow/cli/dist/src/mcp-tools/config-tools.js +21 -16
- package/v3/@claude-flow/cli/dist/src/mcp-tools/coordination-tools.js +27 -2
- package/v3/@claude-flow/cli/dist/src/mcp-tools/embeddings-tools.js +28 -1
- package/v3/@claude-flow/cli/dist/src/mcp-tools/hive-mind-tools.js +14 -3
- package/v3/@claude-flow/cli/dist/src/mcp-tools/hooks-tools.js +151 -48
- package/v3/@claude-flow/cli/dist/src/mcp-tools/neural-tools.js +71 -12
- package/v3/@claude-flow/cli/dist/src/mcp-tools/performance-tools.js +30 -7
- package/v3/@claude-flow/cli/dist/src/mcp-tools/session-tools.js +25 -11
- package/v3/@claude-flow/cli/dist/src/memory/memory-bridge.js +62 -3
- package/v3/@claude-flow/cli/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"sessionId":"36428a63-dfb2-42a4-a159-cf8be916193e","pid":86429,"procStart":"Sun May 3 19:17:47 2026","acquiredAt":1777840775529}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-flow",
|
|
3
|
-
"version": "3.6.
|
|
3
|
+
"version": "3.6.15",
|
|
4
4
|
"description": "Ruflo - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -70,7 +70,18 @@
|
|
|
70
70
|
},
|
|
71
71
|
"overrides": {
|
|
72
72
|
"hono": ">=4.11.4",
|
|
73
|
-
"@ruvector/rvf-wasm": "0.1.5"
|
|
73
|
+
"@ruvector/rvf-wasm": "0.1.5",
|
|
74
|
+
"@hono/node-server": ">=1.19.10",
|
|
75
|
+
"flatted": ">=3.4.0",
|
|
76
|
+
"tar": ">=7.5.0",
|
|
77
|
+
"picomatch": ">=4.0.3",
|
|
78
|
+
"path-to-regexp": ">=8.2.1",
|
|
79
|
+
"undici": ">=7.18.0",
|
|
80
|
+
"minimatch": ">=10.0.0",
|
|
81
|
+
"@isaacs/brace-expansion": ">=5.0.1",
|
|
82
|
+
"cacache": ">=20.0.0",
|
|
83
|
+
"make-fetch-happen": ">=15.0.0",
|
|
84
|
+
"express-rate-limit": ">=8.4.1"
|
|
74
85
|
},
|
|
75
86
|
"devDependencies": {
|
|
76
87
|
"@openai/codex": "^0.98.0",
|
|
@@ -121,13 +121,43 @@ export const agentdbPatternStore = {
|
|
|
121
121
|
const pattern = validateString(params.pattern, 'pattern');
|
|
122
122
|
if (!pattern)
|
|
123
123
|
return { success: false, error: 'pattern is required (non-empty string, max 100KB)' };
|
|
124
|
+
const type = validateString(params.type, 'type', 200) ?? 'general';
|
|
125
|
+
const confidence = validateScore(params.confidence, 0.8);
|
|
124
126
|
const bridge = await getBridge();
|
|
125
|
-
const result = await bridge.bridgeStorePattern({
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
127
|
+
const result = await bridge.bridgeStorePattern({ pattern, type, confidence });
|
|
128
|
+
if (result)
|
|
129
|
+
return result;
|
|
130
|
+
// ADR-093 F4: when the ReasoningBank controller registry returns
|
|
131
|
+
// null (the cause of audit-reported "AgentDB bridge not available"
|
|
132
|
+
// even though `agentdb_health.reasoningBank.enabled === true`), fall
|
|
133
|
+
// back to a direct memory_store write so the caller's pattern still
|
|
134
|
+
// persists. Surface the controller as `memory-store-fallback` so the
|
|
135
|
+
// path is observable instead of silently lost.
|
|
136
|
+
try {
|
|
137
|
+
const { storeEntry } = await import('../memory/memory-initializer.js');
|
|
138
|
+
const patternId = `pattern-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
139
|
+
const value = JSON.stringify({ pattern, type, confidence, _fallback: 'reasoningBank-unavailable' });
|
|
140
|
+
await storeEntry({
|
|
141
|
+
key: patternId,
|
|
142
|
+
value,
|
|
143
|
+
namespace: 'pattern',
|
|
144
|
+
tags: [type, 'reasoning-pattern', 'fallback'],
|
|
145
|
+
});
|
|
146
|
+
return {
|
|
147
|
+
success: true,
|
|
148
|
+
patternId,
|
|
149
|
+
controller: 'memory-store-fallback',
|
|
150
|
+
note: 'ReasoningBank controller registry unavailable. Pattern persisted via memory_store. Run `agentdb_health` to inspect controller registration.',
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
catch (fallbackErr) {
|
|
154
|
+
return {
|
|
155
|
+
success: false,
|
|
156
|
+
error: 'Pattern store failed: both ReasoningBank bridge and memory_store fallback unavailable',
|
|
157
|
+
fallbackError: sanitizeError(fallbackErr),
|
|
158
|
+
recommendation: 'Run agentdb_health to inspect controller registration and check that .swarm/memory.db is writable.',
|
|
159
|
+
};
|
|
160
|
+
}
|
|
131
161
|
}
|
|
132
162
|
catch (error) {
|
|
133
163
|
return { success: false, error: sanitizeError(error) };
|
|
@@ -224,30 +224,35 @@ export const configTools = [
|
|
|
224
224
|
const scope = input.scope || 'default';
|
|
225
225
|
const prefix = input.prefix;
|
|
226
226
|
const includeDefaults = input.includeDefaults !== false;
|
|
227
|
-
|
|
228
|
-
let configs = {};
|
|
227
|
+
const merged = new Map();
|
|
229
228
|
if (includeDefaults) {
|
|
230
|
-
|
|
229
|
+
for (const [key, value] of Object.entries(DEFAULT_CONFIG)) {
|
|
230
|
+
merged.set(key, { value, source: 'default' });
|
|
231
|
+
}
|
|
231
232
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
233
|
+
for (const [key, value] of Object.entries(store.values)) {
|
|
234
|
+
merged.set(key, { value, source: 'stored' });
|
|
235
|
+
}
|
|
236
|
+
// Always include keys from every scope so they're discoverable; the
|
|
237
|
+
// scope filter only narrows which set is used as the *winner*.
|
|
238
|
+
for (const [scopeName, scopeValues] of Object.entries(store.scopes)) {
|
|
239
|
+
for (const [key, value] of Object.entries(scopeValues)) {
|
|
240
|
+
if (scope === scopeName || scope === 'default') {
|
|
241
|
+
merged.set(key, { value, source: `scope:${scopeName}` });
|
|
242
|
+
}
|
|
243
|
+
else if (!merged.has(key)) {
|
|
244
|
+
// Surface scoped keys that aren't shadowed when listing default scope
|
|
245
|
+
merged.set(key, { value, source: `scope:${scopeName}` });
|
|
246
|
+
}
|
|
247
|
+
}
|
|
237
248
|
}
|
|
238
|
-
|
|
239
|
-
let entries = Object.entries(configs);
|
|
249
|
+
let entries = Array.from(merged.entries());
|
|
240
250
|
if (prefix) {
|
|
241
251
|
entries = entries.filter(([key]) => key.startsWith(prefix));
|
|
242
252
|
}
|
|
243
|
-
// Sort by key
|
|
244
253
|
entries.sort(([a], [b]) => a.localeCompare(b));
|
|
245
254
|
return {
|
|
246
|
-
configs: entries.map(([key, value]) => ({
|
|
247
|
-
key,
|
|
248
|
-
value,
|
|
249
|
-
source: store.values[key] !== undefined ? 'stored' : 'default',
|
|
250
|
-
})),
|
|
255
|
+
configs: entries.map(([key, { value, source }]) => ({ key, value, source })),
|
|
251
256
|
total: entries.length,
|
|
252
257
|
scope,
|
|
253
258
|
updatedAt: store.updatedAt,
|
|
@@ -634,15 +634,40 @@ export const coordinationTools = [
|
|
|
634
634
|
const agents = input.agents || Object.keys(store.nodes);
|
|
635
635
|
const strategy = input.strategy || 'parallel';
|
|
636
636
|
const orchestrationId = `orch-${Date.now()}`;
|
|
637
|
+
// ADR-093 F7: this tool only schedules an orchestration record — it
|
|
638
|
+
// does not actually execute. Previously it returned a hardcoded
|
|
639
|
+
// `estimatedCompletion: "50ms"` which was misleading. Now we return
|
|
640
|
+
// an honest stub-status with a note pointing callers at agent_spawn
|
|
641
|
+
// / Task tool / hive-mind tools for real orchestration. Persist the
|
|
642
|
+
// record so callers can list/inspect what was scheduled.
|
|
643
|
+
const orchestration = {
|
|
644
|
+
id: orchestrationId,
|
|
645
|
+
task,
|
|
646
|
+
strategy,
|
|
647
|
+
agents,
|
|
648
|
+
status: 'scheduled',
|
|
649
|
+
scheduledAt: new Date().toISOString(),
|
|
650
|
+
topology: store.topology.type,
|
|
651
|
+
};
|
|
652
|
+
const orchStore = store;
|
|
653
|
+
if (!Array.isArray(orchStore.orchestrations))
|
|
654
|
+
orchStore.orchestrations = [];
|
|
655
|
+
orchStore.orchestrations.push(orchestration);
|
|
656
|
+
if (orchStore.orchestrations.length > 100) {
|
|
657
|
+
orchStore.orchestrations = orchStore.orchestrations.slice(-100);
|
|
658
|
+
}
|
|
659
|
+
saveCoordStore(orchStore);
|
|
637
660
|
return {
|
|
638
661
|
success: true,
|
|
639
662
|
orchestrationId,
|
|
640
663
|
task,
|
|
641
664
|
strategy,
|
|
642
665
|
agents,
|
|
643
|
-
status: '
|
|
666
|
+
status: 'scheduled',
|
|
644
667
|
topology: store.topology.type,
|
|
645
|
-
|
|
668
|
+
// Honest stub: no executor wired up yet. Don't lie about completion time.
|
|
669
|
+
executor: 'none',
|
|
670
|
+
_note: 'coordination_orchestrate currently records the orchestration request but does not execute it. For real multi-agent execution use agent_spawn + the Task tool, or hive-mind_spawn for queen-led coordination.',
|
|
646
671
|
};
|
|
647
672
|
},
|
|
648
673
|
},
|
|
@@ -777,6 +777,24 @@ export const embeddingsTools = [
|
|
|
777
777
|
message: 'Embeddings not initialized. Run embeddings/init first.',
|
|
778
778
|
};
|
|
779
779
|
}
|
|
780
|
+
// ADR-093 F5: distinguish "@ruvector/core installed" from "wired into
|
|
781
|
+
// the embedding pipeline". Previously this collapsed both into a
|
|
782
|
+
// single `ruvector: boolean` field, which gave callers no way to
|
|
783
|
+
// tell whether re-running embeddings_init would help (#1698 partial
|
|
784
|
+
// regression on the MCP boundary).
|
|
785
|
+
let ruvectorAvailable = false;
|
|
786
|
+
let ruvectorVersion;
|
|
787
|
+
try {
|
|
788
|
+
const mod = await import('@ruvector/core');
|
|
789
|
+
ruvectorAvailable = !!mod;
|
|
790
|
+
try {
|
|
791
|
+
// Best-effort: many packages expose a `version` constant
|
|
792
|
+
ruvectorVersion = mod.version;
|
|
793
|
+
}
|
|
794
|
+
catch { /* ignore */ }
|
|
795
|
+
}
|
|
796
|
+
catch { /* not installed */ }
|
|
797
|
+
const ruvectorEnabled = config.neural.ruvector?.enabled ?? false;
|
|
780
798
|
return {
|
|
781
799
|
success: true,
|
|
782
800
|
initialized: true,
|
|
@@ -787,7 +805,16 @@ export const embeddingsTools = [
|
|
|
787
805
|
hyperbolic: config.hyperbolic,
|
|
788
806
|
neural: {
|
|
789
807
|
enabled: config.neural.enabled,
|
|
790
|
-
|
|
808
|
+
// Backwards-compatible: keep the boolean view (truthy when wired).
|
|
809
|
+
ruvector: ruvectorEnabled,
|
|
810
|
+
// New shape — additive, non-breaking. Callers that need to
|
|
811
|
+
// distinguish "package is installed" from "feature wired in"
|
|
812
|
+
// read these instead of guessing from a single bool.
|
|
813
|
+
ruvectorStatus: {
|
|
814
|
+
available: ruvectorAvailable,
|
|
815
|
+
enabled: ruvectorEnabled,
|
|
816
|
+
version: ruvectorVersion,
|
|
817
|
+
},
|
|
791
818
|
},
|
|
792
819
|
},
|
|
793
820
|
paths: {
|
|
@@ -218,6 +218,14 @@ export const hiveMindTools = [
|
|
|
218
218
|
type: 'object',
|
|
219
219
|
properties: {
|
|
220
220
|
topology: { type: 'string', enum: ['mesh', 'hierarchical', 'ring', 'star'], description: 'Network topology' },
|
|
221
|
+
// ADR-093 F3: schema now exposes the consensus strategy so callers
|
|
222
|
+
// can actually request raft / byzantine / quorum / etc. Default
|
|
223
|
+
// matches the documented anti-drift posture (raft).
|
|
224
|
+
consensus: {
|
|
225
|
+
type: 'string',
|
|
226
|
+
enum: ['raft', 'byzantine', 'gossip', 'crdt', 'quorum'],
|
|
227
|
+
description: 'Consensus strategy. Default: raft (anti-drift). Use byzantine for f<n/3 fault tolerance.',
|
|
228
|
+
},
|
|
221
229
|
queenId: { type: 'string', description: 'Initial queen agent ID' },
|
|
222
230
|
},
|
|
223
231
|
},
|
|
@@ -230,8 +238,10 @@ export const hiveMindTools = [
|
|
|
230
238
|
const state = loadHiveState();
|
|
231
239
|
const hiveId = `hive-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
232
240
|
const queenId = input.queenId || `queen-${Date.now()}`;
|
|
241
|
+
const requestedConsensus = input.consensus || 'raft';
|
|
233
242
|
state.initialized = true;
|
|
234
243
|
state.topology = input.topology || 'mesh';
|
|
244
|
+
state.consensusStrategy = requestedConsensus;
|
|
235
245
|
state.createdAt = new Date().toISOString();
|
|
236
246
|
state.queen = {
|
|
237
247
|
agentId: queenId,
|
|
@@ -243,12 +253,12 @@ export const hiveMindTools = [
|
|
|
243
253
|
success: true,
|
|
244
254
|
hiveId,
|
|
245
255
|
topology: state.topology,
|
|
246
|
-
consensus:
|
|
256
|
+
consensus: state.consensusStrategy,
|
|
247
257
|
queenId,
|
|
248
258
|
status: 'initialized',
|
|
249
259
|
config: {
|
|
250
260
|
topology: state.topology,
|
|
251
|
-
consensus:
|
|
261
|
+
consensus: state.consensusStrategy,
|
|
252
262
|
maxAgents: input.maxAgents || 15,
|
|
253
263
|
persist: input.persist !== false,
|
|
254
264
|
memoryBackend: input.memoryBackend || 'hybrid',
|
|
@@ -298,7 +308,8 @@ export const hiveMindTools = [
|
|
|
298
308
|
hiveId: `hive-${state.createdAt ? new Date(state.createdAt).getTime() : Date.now()}`,
|
|
299
309
|
status: state.initialized ? 'active' : 'offline',
|
|
300
310
|
topology: state.topology,
|
|
301
|
-
|
|
311
|
+
// ADR-093 F3: surface the persisted strategy instead of a hardcoded "byzantine".
|
|
312
|
+
consensus: state.consensusStrategy ?? 'byzantine',
|
|
302
313
|
queen: state.queen ? {
|
|
303
314
|
id: state.queen.agentId,
|
|
304
315
|
agentId: state.queen.agentId,
|
|
@@ -945,44 +945,55 @@ export const hooksMetrics = {
|
|
|
945
945
|
},
|
|
946
946
|
handler: async (params) => {
|
|
947
947
|
const period = params.period || '24h';
|
|
948
|
-
//
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
//
|
|
952
|
-
|
|
953
|
-
const
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
}
|
|
948
|
+
// ADR-093 F1: read from the same trajectory/pattern store that
|
|
949
|
+
// hooks_post-task and hooks_intelligence_stats write to. Previously
|
|
950
|
+
// this handler key-substring-filtered the memory store for "pattern",
|
|
951
|
+
// "route", "task" — none of which match the trajectory keys that
|
|
952
|
+
// post-task actually writes — so counters stayed at 0 forever (#1686).
|
|
953
|
+
const stats = getIntelligenceStatsFromMemory();
|
|
954
|
+
// Routing outcomes are persisted to a separate file (loadRoutingOutcomes)
|
|
955
|
+
// by post-task; surface them so the dashboard sees command counters too.
|
|
956
|
+
let routingOutcomes = [];
|
|
957
|
+
try {
|
|
958
|
+
routingOutcomes = loadRoutingOutcomes();
|
|
959
|
+
}
|
|
960
|
+
catch { /* non-fatal */ }
|
|
961
|
+
const totalCommands = routingOutcomes.length;
|
|
962
|
+
const successfulCommands = routingOutcomes.filter(o => o.success).length;
|
|
963
|
+
const successRate = totalCommands > 0 ? successfulCommands / totalCommands : null;
|
|
964
|
+
// Compute top agent from routing outcomes
|
|
965
|
+
const agentCounts = {};
|
|
966
|
+
for (const o of routingOutcomes) {
|
|
967
|
+
if (o.agent)
|
|
968
|
+
agentCounts[o.agent] = (agentCounts[o.agent] || 0) + 1;
|
|
969
|
+
}
|
|
970
|
+
const topAgent = Object.entries(agentCounts).sort((a, b) => b[1] - a[1])[0]?.[0] ?? null;
|
|
971
|
+
const successful = stats.trajectories.successful;
|
|
972
|
+
const total = stats.trajectories.total;
|
|
973
|
+
const failed = Math.max(0, total - successful);
|
|
966
974
|
return {
|
|
975
|
+
_real: true,
|
|
976
|
+
_dataSource: 'intelligence-stats + routing-outcomes',
|
|
967
977
|
period,
|
|
968
978
|
patterns: {
|
|
969
|
-
total:
|
|
970
|
-
successful
|
|
971
|
-
failed
|
|
972
|
-
avgConfidence: null,
|
|
979
|
+
total: stats.patterns.learned,
|
|
980
|
+
successful,
|
|
981
|
+
failed,
|
|
982
|
+
avgConfidence: stats.routing.avgConfidence || null,
|
|
973
983
|
},
|
|
974
984
|
agents: {
|
|
975
|
-
routingAccuracy: null,
|
|
976
|
-
totalRoutes:
|
|
977
|
-
topAgent
|
|
985
|
+
routingAccuracy: stats.routing.avgConfidence || null,
|
|
986
|
+
totalRoutes: stats.routing.decisions,
|
|
987
|
+
topAgent,
|
|
978
988
|
},
|
|
979
989
|
commands: {
|
|
980
|
-
totalExecuted:
|
|
981
|
-
successRate
|
|
990
|
+
totalExecuted: totalCommands,
|
|
991
|
+
successRate,
|
|
982
992
|
avgRiskScore: null,
|
|
983
993
|
},
|
|
984
|
-
|
|
985
|
-
|
|
994
|
+
_note: total === 0 && totalCommands === 0
|
|
995
|
+
? 'No metrics data collected yet. Run hooks_post-task / hooks_intelligence_trajectory-end / hooks_route to populate.'
|
|
996
|
+
: undefined,
|
|
986
997
|
lastUpdated: new Date().toISOString(),
|
|
987
998
|
};
|
|
988
999
|
},
|
|
@@ -2977,40 +2988,90 @@ export const hooksIntelligenceAttention = {
|
|
|
2977
2988
|
}
|
|
2978
2989
|
}
|
|
2979
2990
|
else if (mode === 'flash') {
|
|
2980
|
-
// Try Flash Attention
|
|
2991
|
+
// Try Flash Attention. ADR-093 F10: previously this attended over
|
|
2992
|
+
// synthetic cosine-derived keys/values with constant-vector values,
|
|
2993
|
+
// which produced uniform 0.333 weights and labels like "Flash
|
|
2994
|
+
// attention target #1/2/3". Now we attend over actual stored
|
|
2995
|
+
// patterns when available — real semantic content yields non-uniform
|
|
2996
|
+
// weights and human-readable labels.
|
|
2981
2997
|
const flash = await getFlashAttention();
|
|
2982
2998
|
if (flash) {
|
|
2983
2999
|
try {
|
|
2984
3000
|
const embResult = await getQueryEmbedding(query, 384);
|
|
2985
3001
|
embeddingSource = embResult.source;
|
|
2986
3002
|
const q = embResult.embedding;
|
|
3003
|
+
// Pull real stored patterns to attend over. If none exist yet,
|
|
3004
|
+
// fall back to the synthetic harness but mark it honestly.
|
|
3005
|
+
const realPatterns = [];
|
|
3006
|
+
try {
|
|
3007
|
+
const { searchEntries: searchFn } = await import('../memory/memory-initializer.js');
|
|
3008
|
+
const hits = await searchFn({ query, limit: topK });
|
|
3009
|
+
if (Array.isArray(hits)) {
|
|
3010
|
+
for (const h of hits.slice(0, topK)) {
|
|
3011
|
+
const content = h.content ?? h.value ?? '';
|
|
3012
|
+
const id = String(h.id ?? h.key ?? `pattern-${realPatterns.length}`);
|
|
3013
|
+
realPatterns.push({ id, content: String(content) });
|
|
3014
|
+
}
|
|
3015
|
+
}
|
|
3016
|
+
}
|
|
3017
|
+
catch { /* memory not initialized — fall through to synthetic */ }
|
|
3018
|
+
const useReal = realPatterns.length > 0;
|
|
2987
3019
|
const keys = [];
|
|
2988
3020
|
const values = [];
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
3021
|
+
const labels = [];
|
|
3022
|
+
if (useReal) {
|
|
3023
|
+
// Build keys from real pattern embeddings (re-embed if no vector cached)
|
|
3024
|
+
for (let k = 0; k < realPatterns.length; k++) {
|
|
3025
|
+
const p = realPatterns[k];
|
|
3026
|
+
let keyEmbedding;
|
|
3027
|
+
if (p.embedding && p.embedding.length === 384) {
|
|
3028
|
+
keyEmbedding = new Float32Array(p.embedding);
|
|
3029
|
+
}
|
|
3030
|
+
else {
|
|
3031
|
+
const enc = await getQueryEmbedding(p.content.slice(0, 1024), 384);
|
|
3032
|
+
keyEmbedding = enc.embedding;
|
|
3033
|
+
}
|
|
3034
|
+
const value = new Float32Array(384);
|
|
3035
|
+
// Value carries pattern identity strength — magnitude = recency proxy (k position)
|
|
3036
|
+
const strength = 1 / (k + 1);
|
|
3037
|
+
for (let i = 0; i < 384; i++)
|
|
3038
|
+
value[i] = keyEmbedding[i] * strength;
|
|
3039
|
+
keys.push(keyEmbedding);
|
|
3040
|
+
values.push(value);
|
|
3041
|
+
const label = p.content.length > 0
|
|
3042
|
+
? `${p.id}: ${p.content.slice(0, 60)}${p.content.length > 60 ? '…' : ''}`
|
|
3043
|
+
: p.id;
|
|
3044
|
+
labels.push(label);
|
|
3045
|
+
}
|
|
3046
|
+
}
|
|
3047
|
+
else {
|
|
3048
|
+
// No real patterns — surface a synthetic harness honestly.
|
|
3049
|
+
for (let k = 0; k < topK; k++) {
|
|
3050
|
+
const key = new Float32Array(384);
|
|
3051
|
+
const value = new Float32Array(384);
|
|
3052
|
+
for (let i = 0; i < 384; i++) {
|
|
3053
|
+
key[i] = Math.cos((k + 1) * (i + 1) * 0.01);
|
|
3054
|
+
value[i] = k + 1;
|
|
3055
|
+
}
|
|
3056
|
+
keys.push(key);
|
|
3057
|
+
values.push(value);
|
|
3058
|
+
labels.push(`(synthetic harness) pattern #${k + 1}`);
|
|
2996
3059
|
}
|
|
2997
|
-
keys.push(key);
|
|
2998
|
-
values.push(value);
|
|
2999
3060
|
}
|
|
3000
3061
|
const attentionResult = flash.attention([q], keys, values);
|
|
3001
3062
|
// Compute softmax weights from output magnitudes
|
|
3002
3063
|
const outputMags = attentionResult.output[0]
|
|
3003
|
-
? Array.from(attentionResult.output[0]).slice(0,
|
|
3004
|
-
: new Array(
|
|
3064
|
+
? Array.from(attentionResult.output[0]).slice(0, keys.length).map(v => Math.abs(v))
|
|
3065
|
+
: new Array(keys.length).fill(1);
|
|
3005
3066
|
const sumMags = outputMags.reduce((a, b) => a + b, 0) || 1;
|
|
3006
|
-
for (let i = 0; i <
|
|
3067
|
+
for (let i = 0; i < keys.length; i++) {
|
|
3007
3068
|
results.push({
|
|
3008
3069
|
index: i,
|
|
3009
3070
|
weight: outputMags[i] / sumMags,
|
|
3010
|
-
pattern:
|
|
3071
|
+
pattern: labels[i],
|
|
3011
3072
|
});
|
|
3012
3073
|
}
|
|
3013
|
-
implementation = 'real-flash-attention';
|
|
3074
|
+
implementation = useReal ? 'real-flash-attention+memory' : 'real-flash-attention+synthetic-harness';
|
|
3014
3075
|
}
|
|
3015
3076
|
catch {
|
|
3016
3077
|
// Fall back to placeholder
|
|
@@ -3325,21 +3386,61 @@ export const hooksWorkerDispatch = {
|
|
|
3325
3386
|
}
|
|
3326
3387
|
const workerId = `worker_${trigger}_${++workerIdCounter}_${Date.now().toString(36)}`;
|
|
3327
3388
|
const config = WORKER_CONFIGS[trigger];
|
|
3389
|
+
// ADR-093 F2: stop returning status:"completed" for a worker that
|
|
3390
|
+
// never ran (#1700 item 1). Detect daemon presence via PID file and
|
|
3391
|
+
// surface honest verdicts (`no-daemon` / `queued` / `synthetic`).
|
|
3392
|
+
const cwd = getProjectCwd();
|
|
3393
|
+
const pidFile = join(cwd, '.claude-flow', 'daemon.pid');
|
|
3394
|
+
let daemonPid = null;
|
|
3395
|
+
let daemonAlive = false;
|
|
3396
|
+
if (existsSync(pidFile)) {
|
|
3397
|
+
try {
|
|
3398
|
+
const raw = readFileSync(pidFile, 'utf-8').trim();
|
|
3399
|
+
const pid = parseInt(raw, 10);
|
|
3400
|
+
if (Number.isFinite(pid) && pid > 0) {
|
|
3401
|
+
daemonPid = pid;
|
|
3402
|
+
try {
|
|
3403
|
+
process.kill(pid, 0);
|
|
3404
|
+
daemonAlive = true;
|
|
3405
|
+
}
|
|
3406
|
+
catch {
|
|
3407
|
+
daemonAlive = false;
|
|
3408
|
+
}
|
|
3409
|
+
}
|
|
3410
|
+
}
|
|
3411
|
+
catch { /* unreadable PID file */ }
|
|
3412
|
+
}
|
|
3328
3413
|
const worker = {
|
|
3329
3414
|
id: workerId,
|
|
3330
3415
|
trigger,
|
|
3331
3416
|
context,
|
|
3332
|
-
status: '
|
|
3417
|
+
status: daemonAlive ? 'pending' : 'pending',
|
|
3333
3418
|
progress: 0,
|
|
3334
3419
|
phase: 'initializing',
|
|
3335
3420
|
startedAt: new Date(),
|
|
3336
3421
|
};
|
|
3337
3422
|
activeWorkers.set(workerId, worker);
|
|
3338
|
-
|
|
3423
|
+
// Determine honest status
|
|
3424
|
+
let reportedStatus;
|
|
3425
|
+
let note;
|
|
3426
|
+
if (!daemonAlive) {
|
|
3427
|
+
reportedStatus = 'no-daemon';
|
|
3428
|
+
note = 'No worker daemon detected. Run `claude-flow daemon start` to enable real worker execution. The dispatch was recorded in-process but no actual work will run.';
|
|
3429
|
+
}
|
|
3430
|
+
else if (background) {
|
|
3431
|
+
// Daemon is alive — record the queued worker. The daemon polls activeWorkers
|
|
3432
|
+
// via its own state file, so this constitutes a real queue entry.
|
|
3433
|
+
reportedStatus = 'queued';
|
|
3434
|
+
note = `Worker queued for daemon (pid ${daemonPid}). Poll hooks_worker-status to track progression — do not assume completion until status === "completed".`;
|
|
3435
|
+
}
|
|
3436
|
+
else {
|
|
3437
|
+
// Synchronous mode without a runner — be honest about it
|
|
3438
|
+
reportedStatus = 'synthetic-completed';
|
|
3339
3439
|
worker.progress = 100;
|
|
3340
3440
|
worker.phase = 'completed';
|
|
3341
3441
|
worker.status = 'completed';
|
|
3342
3442
|
worker.completedAt = new Date();
|
|
3443
|
+
note = 'Synchronous mode: worker record marked completed but no real work executed (no in-process runner). Use background:true with the daemon for real execution.';
|
|
3343
3444
|
}
|
|
3344
3445
|
return {
|
|
3345
3446
|
success: true,
|
|
@@ -3352,9 +3453,11 @@ export const hooksWorkerDispatch = {
|
|
|
3352
3453
|
estimatedDuration: config.estimatedDuration,
|
|
3353
3454
|
capabilities: config.capabilities,
|
|
3354
3455
|
},
|
|
3355
|
-
status:
|
|
3456
|
+
status: reportedStatus,
|
|
3457
|
+
daemonAlive,
|
|
3458
|
+
daemonPid: daemonAlive ? daemonPid : null,
|
|
3356
3459
|
background,
|
|
3357
|
-
note
|
|
3460
|
+
note,
|
|
3358
3461
|
timestamp: new Date().toISOString(),
|
|
3359
3462
|
};
|
|
3360
3463
|
},
|
|
@@ -222,10 +222,38 @@ export const neuralTools = [
|
|
|
222
222
|
continue;
|
|
223
223
|
const embedding = await generateEmbedding(text, 384);
|
|
224
224
|
const patternId = `${modelId}-train-${i}`;
|
|
225
|
+
// ADR-093 F11: extract a meaningful label instead of dumping raw
|
|
226
|
+
// training JSON as the pattern name. Audit reported neural_predict
|
|
227
|
+
// returned `label: <raw training data JSON>` because the previous
|
|
228
|
+
// fallback was `text.slice(0, 100)` where text was `JSON.stringify(entry)`.
|
|
229
|
+
let label;
|
|
230
|
+
if (typeof entry === 'string') {
|
|
231
|
+
label = entry.slice(0, 80);
|
|
232
|
+
}
|
|
233
|
+
else if (entry && typeof entry === 'object') {
|
|
234
|
+
const e = entry;
|
|
235
|
+
// Prefer common semantic fields over a JSON dump
|
|
236
|
+
const labelField = e.label ?? e.category ?? e.class ?? e.tag ?? e.intent ?? e.name ?? e.title;
|
|
237
|
+
if (typeof labelField === 'string' && labelField.length > 0) {
|
|
238
|
+
label = labelField.slice(0, 80);
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
const summaryField = e.text ?? e.input ?? e.task ?? e.description ?? e.content;
|
|
242
|
+
if (typeof summaryField === 'string' && summaryField.length > 0) {
|
|
243
|
+
label = `${summaryField.slice(0, 60)}${summaryField.length > 60 ? '…' : ''}`;
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
// Last resort: reduce to a stable short hash-like id
|
|
247
|
+
label = `${modelType}:entry-${i}`;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
label = `${modelType}:entry-${i}`;
|
|
253
|
+
}
|
|
225
254
|
store.patterns[patternId] = {
|
|
226
255
|
id: patternId,
|
|
227
|
-
name:
|
|
228
|
-
? String(entry.label) : text.slice(0, 100),
|
|
256
|
+
name: label,
|
|
229
257
|
type: modelType,
|
|
230
258
|
embedding,
|
|
231
259
|
metadata: { modelId, epoch: epochs, index: i, raw: entry },
|
|
@@ -294,36 +322,67 @@ export const neuralTools = [
|
|
|
294
322
|
const startTime = performance.now();
|
|
295
323
|
const embedding = await generateEmbedding(inputText, 384);
|
|
296
324
|
const latency = Math.round(performance.now() - startTime);
|
|
297
|
-
//
|
|
325
|
+
// ADR-093 F11: real classifier head over stored patterns. Previously
|
|
326
|
+
// confidence was the raw cosine similarity (often clamped to 0 when
|
|
327
|
+
// stored embeddings were stale or zero-vectored). Now we run k-NN
|
|
328
|
+
// with cosine distance and apply a temperature-controlled softmax
|
|
329
|
+
// over the top-K so confidence is a proper distribution that sums
|
|
330
|
+
// to 1, and we surface enough metadata to trust the result.
|
|
298
331
|
const storedPatterns = Object.values(store.patterns);
|
|
299
332
|
let predictions;
|
|
300
333
|
if (storedPatterns.length > 0) {
|
|
301
|
-
//
|
|
302
|
-
|
|
303
|
-
.map(p =>
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
334
|
+
// Step 1: k-NN with cosine
|
|
335
|
+
const scored = storedPatterns
|
|
336
|
+
.map(p => {
|
|
337
|
+
const sim = cosineSimilarity(embedding, p.embedding);
|
|
338
|
+
return {
|
|
339
|
+
patternId: p.id,
|
|
340
|
+
label: p.name || p.type || p.id,
|
|
341
|
+
cosineSimilarity: sim,
|
|
342
|
+
};
|
|
343
|
+
})
|
|
344
|
+
.sort((a, b) => b.cosineSimilarity - a.cosineSimilarity)
|
|
309
345
|
.slice(0, topK);
|
|
346
|
+
// Step 2: temperature-softmax over the top-K so confidence sums to 1.
|
|
347
|
+
// Temperature 0.1 sharpens differences between similar candidates.
|
|
348
|
+
const tau = 0.1;
|
|
349
|
+
const exps = scored.map(s => Math.exp(s.cosineSimilarity / tau));
|
|
350
|
+
const z = exps.reduce((a, b) => a + b, 0) || 1;
|
|
351
|
+
predictions = scored.map((s, i) => ({
|
|
352
|
+
label: s.label,
|
|
353
|
+
patternId: s.patternId,
|
|
354
|
+
cosineSimilarity: Number(s.cosineSimilarity.toFixed(4)),
|
|
355
|
+
confidence: Number((exps[i] / z).toFixed(4)),
|
|
356
|
+
}));
|
|
310
357
|
}
|
|
311
358
|
else {
|
|
312
|
-
// No patterns stored — no predictions possible
|
|
359
|
+
// No patterns stored — no predictions possible. Be honest about it
|
|
360
|
+
// instead of returning empty silently.
|
|
313
361
|
predictions = [];
|
|
314
362
|
}
|
|
363
|
+
const topConfidence = predictions[0]?.confidence ?? 0;
|
|
364
|
+
const topSimilarity = predictions[0]?.cosineSimilarity ?? 0;
|
|
315
365
|
return {
|
|
316
366
|
success: true,
|
|
317
367
|
_realEmbedding: !!realEmbeddings,
|
|
318
368
|
_embeddingSource: embeddingServiceName,
|
|
319
369
|
embeddingProvider: embeddingServiceName,
|
|
320
370
|
_hasStoredPatterns: storedPatterns.length > 0,
|
|
371
|
+
_classifierHead: storedPatterns.length > 0 ? 'knn-cosine+softmax(tau=0.1)' : 'none',
|
|
321
372
|
modelId: model?.id || 'default',
|
|
322
373
|
input: inputText,
|
|
323
374
|
predictions,
|
|
375
|
+
// Surface cosineSimilarity separately so callers know whether the
|
|
376
|
+
// softmax confidence reflects true match strength.
|
|
377
|
+
topPrediction: predictions[0]?.label ?? null,
|
|
378
|
+
topConfidence,
|
|
379
|
+
topSimilarity,
|
|
324
380
|
embedding: embedding.slice(0, 8), // Preview of embedding
|
|
325
381
|
embeddingDims: embedding.length,
|
|
326
382
|
latency,
|
|
383
|
+
...(storedPatterns.length === 0 ? {
|
|
384
|
+
_note: 'No patterns stored. Train with neural_train(modelType, trainingData) before predicting.',
|
|
385
|
+
} : {}),
|
|
327
386
|
};
|
|
328
387
|
},
|
|
329
388
|
},
|
|
@@ -74,7 +74,27 @@ export const performanceTools = [
|
|
|
74
74
|
const freeMem = os.freemem();
|
|
75
75
|
// Calculate real CPU usage percentage from load average
|
|
76
76
|
const cpuPercent = (loadAvg[0] / cpus.length) * 100;
|
|
77
|
-
//
|
|
77
|
+
// ADR-093 F8: replace hardcoded latency fixtures (50/40/100/200) with
|
|
78
|
+
// an actual self-measured latency probe. Throughput now reflects real
|
|
79
|
+
// metric collection cadence (calls/min over the stored history) rather
|
|
80
|
+
// than an arbitrary +1/+10 increment per call.
|
|
81
|
+
const probeStart = process.hrtime.bigint();
|
|
82
|
+
// Tiny CPU+memory work that mirrors a typical MCP tool call
|
|
83
|
+
let probeAcc = 0;
|
|
84
|
+
for (let i = 0; i < 1000; i++)
|
|
85
|
+
probeAcc += Math.sqrt(i);
|
|
86
|
+
const probeNs = Number(process.hrtime.bigint() - probeStart);
|
|
87
|
+
const selfLatencyMs = probeNs / 1e6;
|
|
88
|
+
const recent = store.metrics.slice(-10);
|
|
89
|
+
const recentLatencies = recent.map(m => m.latency.avg).filter(n => Number.isFinite(n));
|
|
90
|
+
recentLatencies.push(selfLatencyMs);
|
|
91
|
+
const sorted = [...recentLatencies].sort((a, b) => a - b);
|
|
92
|
+
const pct = (p) => sorted.length === 0 ? selfLatencyMs : sorted[Math.min(sorted.length - 1, Math.floor((p / 100) * sorted.length))];
|
|
93
|
+
const avg = recentLatencies.reduce((s, n) => s + n, 0) / Math.max(1, recentLatencies.length);
|
|
94
|
+
// Throughput from real cadence: count metric samples in the last 60s.
|
|
95
|
+
const cutoff = Date.now() - 60_000;
|
|
96
|
+
const samplesInLastMinute = store.metrics.filter(m => new Date(m.timestamp).getTime() >= cutoff).length + 1;
|
|
97
|
+
const opsPerSecond = samplesInLastMinute / 60;
|
|
78
98
|
const currentMetrics = {
|
|
79
99
|
timestamp: new Date().toISOString(),
|
|
80
100
|
cpu: { usage: Math.min(cpuPercent, 100), cores: cpus.length },
|
|
@@ -84,17 +104,20 @@ export const performanceTools = [
|
|
|
84
104
|
heap: Math.round(memUsage.heapUsed / 1024 / 1024),
|
|
85
105
|
},
|
|
86
106
|
latency: {
|
|
87
|
-
avg:
|
|
88
|
-
p50:
|
|
89
|
-
p95:
|
|
90
|
-
p99:
|
|
107
|
+
avg: Number(avg.toFixed(3)),
|
|
108
|
+
p50: Number(pct(50).toFixed(3)),
|
|
109
|
+
p95: Number(pct(95).toFixed(3)),
|
|
110
|
+
p99: Number(pct(99).toFixed(3)),
|
|
91
111
|
},
|
|
92
112
|
throughput: {
|
|
93
|
-
requests: store.metrics.length
|
|
94
|
-
operations:
|
|
113
|
+
requests: store.metrics.length + 1,
|
|
114
|
+
operations: Number(opsPerSecond.toFixed(2)),
|
|
95
115
|
},
|
|
96
116
|
errors: { count: 0, rate: 0 },
|
|
97
117
|
};
|
|
118
|
+
// probeAcc kept reachable to prevent V8 dead-code elimination of the loop
|
|
119
|
+
if (probeAcc < 0)
|
|
120
|
+
currentMetrics.errors.count = -1;
|
|
98
121
|
store.metrics.push(currentMetrics);
|
|
99
122
|
// Keep last 100 metrics
|
|
100
123
|
if (store.metrics.length > 100) {
|
|
@@ -260,29 +260,43 @@ export const sessionTools = [
|
|
|
260
260
|
},
|
|
261
261
|
},
|
|
262
262
|
handler: async (input) => {
|
|
263
|
-
|
|
263
|
+
const raw = listSessions();
|
|
264
|
+
let sessions = raw.map((s) => ({
|
|
265
|
+
...s,
|
|
266
|
+
sessionId: s.sessionId || s.id || 'unknown',
|
|
267
|
+
savedAt: s.savedAt || s.startedAt || '',
|
|
268
|
+
}));
|
|
264
269
|
// Sort
|
|
265
270
|
const sortBy = input.sortBy || 'date';
|
|
266
271
|
if (sortBy === 'date') {
|
|
267
|
-
sessions.sort((a, b) => new Date(b.savedAt).getTime() - new Date(a.savedAt).getTime());
|
|
272
|
+
sessions.sort((a, b) => new Date(String(b.savedAt || '')).getTime() - new Date(String(a.savedAt || '')).getTime());
|
|
268
273
|
}
|
|
269
274
|
else if (sortBy === 'name') {
|
|
270
|
-
sessions.sort((a, b) => a.name.localeCompare(b.name));
|
|
275
|
+
sessions.sort((a, b) => String(a.name || a.sessionId || '').localeCompare(String(b.name || b.sessionId || '')));
|
|
271
276
|
}
|
|
272
277
|
else if (sortBy === 'size') {
|
|
273
|
-
sessions.sort((a, b) => b.stats
|
|
278
|
+
sessions.sort((a, b) => (b.stats?.totalSize ?? 0) - (a.stats?.totalSize ?? 0));
|
|
274
279
|
}
|
|
275
280
|
// Apply limit
|
|
276
281
|
const limit = input.limit || 10;
|
|
277
282
|
sessions = sessions.slice(0, limit);
|
|
278
283
|
return {
|
|
279
|
-
sessions: sessions.map(s =>
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
284
|
+
sessions: sessions.map(s => {
|
|
285
|
+
// Project to a stable shape; pull through either source's metadata.
|
|
286
|
+
const projection = {
|
|
287
|
+
sessionId: s.sessionId,
|
|
288
|
+
name: s.name ?? s.sessionId,
|
|
289
|
+
description: s.description,
|
|
290
|
+
savedAt: s.savedAt,
|
|
291
|
+
stats: s.stats ?? null,
|
|
292
|
+
};
|
|
293
|
+
// Preserve auto-session shape fields when present
|
|
294
|
+
if (s.platform)
|
|
295
|
+
projection.platform = s.platform;
|
|
296
|
+
if (s.metrics)
|
|
297
|
+
projection.metrics = s.metrics;
|
|
298
|
+
return projection;
|
|
299
|
+
}),
|
|
286
300
|
total: sessions.length,
|
|
287
301
|
limit,
|
|
288
302
|
};
|
|
@@ -131,6 +131,56 @@ async function getRegistry(dbPath) {
|
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
133
|
catch { /* AgentDB not available */ }
|
|
134
|
+
// ADR-093 F9: post-init injection of low-risk disabled controllers.
|
|
135
|
+
// Try multiple constructor names because the agentdb API renamed
|
|
136
|
+
// SemanticRouter across alpha versions (alpha.10 had SemanticRouter,
|
|
137
|
+
// alpha.11+ removed it in favor of @ruvector/router separately;
|
|
138
|
+
// future versions may reintroduce). If the cwd's agentdb has a
|
|
139
|
+
// viable router-shaped controller, wire it; otherwise leave it
|
|
140
|
+
// unbound and let bridgeSemanticRoute return its actionable error.
|
|
141
|
+
try {
|
|
142
|
+
const agentdb = await import('agentdb');
|
|
143
|
+
const candidates = ['SemanticRouter', 'IntentRouter', 'TaskRouter'];
|
|
144
|
+
let routerInstance = null;
|
|
145
|
+
for (const name of candidates) {
|
|
146
|
+
const Ctor = agentdb[name];
|
|
147
|
+
if (typeof Ctor === 'function') {
|
|
148
|
+
try {
|
|
149
|
+
// Try with dimension config first (newer router classes), then no-args
|
|
150
|
+
const inst = (() => {
|
|
151
|
+
try {
|
|
152
|
+
return new Ctor({ dimension: 384 });
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
return new Ctor();
|
|
156
|
+
}
|
|
157
|
+
})();
|
|
158
|
+
if (inst && typeof inst.route === 'function') {
|
|
159
|
+
routerInstance = inst;
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
catch { /* try next candidate */ }
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (routerInstance && !reg.get('semanticRouter')) {
|
|
167
|
+
if (typeof reg.set === 'function')
|
|
168
|
+
reg.set('semanticRouter', routerInstance);
|
|
169
|
+
else
|
|
170
|
+
reg._controllers = { ...(reg._controllers || {}), semanticRouter: routerInstance };
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
catch { /* SemanticRouter optional — bridgeSemanticRoute will surface "not available" */ }
|
|
174
|
+
// Other disabled controllers remain disabled and tracked in
|
|
175
|
+
// ADR-093 F9 for future enablement:
|
|
176
|
+
// - mutationGuard (write protection — needs config)
|
|
177
|
+
// - attestationLog (needs sqlite db handle the registry does
|
|
178
|
+
// not currently expose)
|
|
179
|
+
// - gnnService (graph neural net — heavy deps, needs WASM/CUDA)
|
|
180
|
+
// - guardedVectorBackend (secured vector backend variant —
|
|
181
|
+
// needs key material)
|
|
182
|
+
// - rvfOptimizer (RVF format optimizer — needs RVF storage)
|
|
183
|
+
// - graphAdapter (graph DB adapter — needs graph DB)
|
|
134
184
|
}
|
|
135
185
|
catch {
|
|
136
186
|
// Intelligence module not available — learning stays unwired
|
|
@@ -1545,13 +1595,22 @@ export async function bridgeSemanticRoute(params) {
|
|
|
1545
1595
|
return null;
|
|
1546
1596
|
try {
|
|
1547
1597
|
const router = registry.get('semanticRouter');
|
|
1548
|
-
if (!router)
|
|
1549
|
-
|
|
1598
|
+
if (!router) {
|
|
1599
|
+
// ADR-093 F9: surface an actionable error pointing callers at the
|
|
1600
|
+
// alternative routing surfaces that DO work, instead of just
|
|
1601
|
+
// saying "not available".
|
|
1602
|
+
return {
|
|
1603
|
+
route: null,
|
|
1604
|
+
error: 'SemanticRouter not available in current agentdb build',
|
|
1605
|
+
recommendation: 'Use bridgeRouteTask (registers as `agentdb_route` MCP tool) for keyword+pattern routing, or hooks_model-route for ADR-026 model selection.',
|
|
1606
|
+
controller: 'none',
|
|
1607
|
+
};
|
|
1608
|
+
}
|
|
1550
1609
|
const result = await router.route(params.input);
|
|
1551
1610
|
return { route: result, controller: 'semanticRouter' };
|
|
1552
1611
|
}
|
|
1553
1612
|
catch (e) {
|
|
1554
|
-
return { route: null, error: e.message };
|
|
1613
|
+
return { route: null, error: e.message, controller: 'error' };
|
|
1555
1614
|
}
|
|
1556
1615
|
}
|
|
1557
1616
|
// ===== RaBitQ data export =====
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@claude-flow/cli",
|
|
3
|
-
"version": "3.6.
|
|
3
|
+
"version": "3.6.15",
|
|
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",
|