@claude-flow/cli 3.56.8 → 3.56.10
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/.helpers-version +1 -1
- package/.claude/helpers/helpers.manifest.json +4 -4
- package/.claude/helpers/hook-handler.cjs +109 -23
- package/.claude/helpers/intelligence.cjs +51 -9
- package/.claude/helpers/router.cjs +0 -0
- package/.claude/skills/verification-quality/SKILL.md +14 -11
- package/README.md +1 -1
- package/catalog-manifest.json +2 -2
- package/dist/src/autopilot-state.d.ts +2 -0
- package/dist/src/autopilot-state.js +36 -8
- package/dist/src/command-resolution.d.ts +15 -0
- package/dist/src/command-resolution.js +25 -0
- package/dist/src/commands/agent.js +17 -17
- package/dist/src/commands/analyze.js +40 -29
- package/dist/src/commands/claims.js +6 -8
- package/dist/src/commands/daemon.js +86 -31
- package/dist/src/commands/memory.js +14 -2
- package/dist/src/commands/security-code-patterns.d.ts +2 -2
- package/dist/src/commands/security-code-patterns.js +61 -3
- package/dist/src/commands/security.d.ts +24 -0
- package/dist/src/commands/security.js +81 -35
- package/dist/src/commands/tui.d.ts +9 -0
- package/dist/src/commands/tui.js +29 -3
- package/dist/src/config/proven-config-refresh.d.ts +9 -0
- package/dist/src/config/proven-config-refresh.js +24 -3
- package/dist/src/funnel/local-signals.js +2 -1
- package/dist/src/index.js +6 -42
- package/dist/src/init/helpers-generator.js +109 -23
- package/dist/src/init/types.js +1 -1
- package/dist/src/mcp-tools/agent-execute-core.d.ts +2 -0
- package/dist/src/mcp-tools/agent-execute-core.js +15 -0
- package/dist/src/mcp-tools/agent-health-metrics.d.ts +22 -0
- package/dist/src/mcp-tools/agent-health-metrics.js +33 -0
- package/dist/src/mcp-tools/agent-tools.js +45 -22
- package/dist/src/mcp-tools/agentbbs-federation.d.ts +1 -1
- package/dist/src/mcp-tools/agentbbs-federation.js +7 -2
- package/dist/src/mcp-tools/agentdb-tools.js +25 -2
- package/dist/src/mcp-tools/aidefence-known-misses.d.ts +22 -0
- package/dist/src/mcp-tools/aidefence-known-misses.js +57 -0
- package/dist/src/mcp-tools/analyze-tools.js +10 -11
- package/dist/src/mcp-tools/autopilot-tools.js +2 -2
- package/dist/src/mcp-tools/browser-intent-tools.js +6 -2
- package/dist/src/mcp-tools/embeddings-tools.js +29 -7
- package/dist/src/mcp-tools/hooks-tools.js +4 -0
- package/dist/src/mcp-tools/memory-tools.js +70 -20
- package/dist/src/mcp-tools/neural-tools.js +28 -4
- package/dist/src/mcp-tools/ruvllm-tools.js +6 -0
- package/dist/src/mcp-tools/security-tools.js +55 -17
- package/dist/src/mcp-tools/workflow-tools.js +66 -17
- package/dist/src/memory/intelligence.d.ts +5 -1
- package/dist/src/memory/intelligence.js +105 -29
- package/dist/src/memory/memory-bridge.js +10 -3
- package/dist/src/memory/memory-initializer.js +2 -1
- package/dist/src/memory/search-defaults.d.ts +54 -0
- package/dist/src/memory/search-defaults.js +37 -0
- package/dist/src/memory/sona-optimizer.d.ts +28 -0
- package/dist/src/memory/sona-optimizer.js +59 -2
- package/dist/src/mods/command-registry/schema.d.ts +24 -24
- package/dist/src/parser.js +32 -22
- package/dist/src/production/monitoring.d.ts +1 -0
- package/dist/src/production/monitoring.js +19 -6
- package/dist/src/production/rate-limiter.js +5 -4
- package/dist/src/production/retry.js +3 -0
- package/dist/src/ruvector/agent-wasm.d.ts +10 -0
- package/dist/src/ruvector/agent-wasm.js +27 -1
- package/dist/src/ruvector/diff-classifier.d.ts +2 -4
- package/dist/src/ruvector/diff-classifier.js +202 -69
- package/dist/src/ruvector/ruvllm-wasm.d.ts +4 -0
- package/dist/src/ruvector/ruvllm-wasm.js +12 -1
- package/dist/src/services/config-file-manager.js +3 -3
- package/dist/src/services/policy-runtime.d.ts +1 -1
- package/dist/src/services/policy-runtime.js +7 -1
- package/dist/src/services/worker-daemon.d.ts +11 -0
- package/dist/src/services/worker-daemon.js +37 -0
- package/dist/src/transfer/store/discovery.js +16 -17
- package/dist/src/transfer/store/types.d.ts +4 -0
- package/node_modules/@claude-flow/codex/dist/cli.js +0 -0
- package/node_modules/@claude-flow/codex/dist/dual-mode/orchestrator.d.ts +1 -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 +3 -1
- package/node_modules/@claude-flow/codex/dist/dual-mode/orchestrator.js.map +1 -1
- package/node_modules/@claude-flow/codex/dist/index.d.ts +3 -3
- package/node_modules/@claude-flow/codex/dist/index.js +1 -1
- package/node_modules/@claude-flow/codex/package.json +1 -1
- package/node_modules/@claude-flow/plugin-agent-federation/dist/bin.js +0 -0
- package/node_modules/@claude-flow/security/dist/input-validator.d.ts +6 -6
- package/node_modules/@claude-flow/security/dist/policy/envelope.js +6 -6
- package/node_modules/@claude-flow/security/dist/policy/envelope.js.map +1 -1
- package/node_modules/@claude-flow/security/package.json +1 -1
- package/package.json +7 -7
- package/plugins/ruflo-console/dist/tui.mjs +162 -160
- package/plugins/ruflo-metaharness/.claude-flow/daemon-state.json +179 -0
- package/plugins/ruflo-metaharness/.claude-flow/daemon.pid +1 -0
- package/plugins/ruflo-metaharness/.claude-flow/logs/daemon.log +32 -0
- package/plugins/ruflo-metaharness/.claude-flow/metrics/backup.json +23 -0
- package/plugins/ruflo-metaharness/.claude-flow/metrics/codebase-map.json +11 -0
- package/plugins/ruflo-metaharness/.claude-flow/metrics/consolidation.json +16 -0
- package/plugins/ruflo-metaharness/.claude-flow/metrics/harness-loop.json +91 -0
- package/plugins/ruflo-metaharness/.claude-flow/metrics/performance.json +17 -0
- package/plugins/ruflo-metaharness/.claude-flow/metrics/security-audit.json +12 -0
- package/plugins/ruflo-metaharness/.claude-flow/metrics/test-gaps.json +8 -0
- package/plugins/ruflo-metaharness/.claude-flow/neural/patterns.json +2809 -0
- package/plugins/ruflo-metaharness/.claude-flow/neural/stats.json +6 -0
- package/plugins/ruflo-metaharness/.claude-flow/policy/state.json +11 -0
- package/plugins/ruflo-metaharness/.claude-plugin/plugin.json +1 -1
- package/plugins/ruflo-metaharness/.swarm/agentdb-memory.db +0 -0
- package/plugins/ruflo-metaharness/.swarm/agentdb-memory.db-shm +0 -0
- package/plugins/ruflo-metaharness/.swarm/agentdb-memory.db-wal +0 -0
- package/plugins/ruflo-metaharness/.swarm/backups/memory-2026-10-14T16-09-38-164Z.db +0 -0
- package/plugins/ruflo-metaharness/.swarm/backups/store-agentdb-memory.db-2026-10-14T16-09-38-166Z.db +0 -0
- package/plugins/ruflo-metaharness/.swarm/ewc-fisher.json +397 -0
- package/plugins/ruflo-metaharness/.swarm/memory.db +0 -0
- package/plugins/ruflo-metaharness/.swarm/memory.db-shm +0 -0
- package/plugins/ruflo-metaharness/.swarm/memory.db-wal +0 -0
- package/plugins/ruflo-metaharness/CHANGELOG.md +3 -0
- package/plugins/ruflo-metaharness/agentdb.rvf +0 -0
- package/plugins/ruflo-metaharness/agentdb.rvf.lock +0 -0
- package/plugins/ruflo-metaharness/hooks/screen.ts +10 -3
- package/plugins/ruflo-metaharness/ruvector.db +0 -0
- package/plugins/ruflo-metaharness/scripts/smoke.sh +4 -4
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
11
11
|
import { join } from 'node:path';
|
|
12
12
|
import { getProjectCwd } from './types.js';
|
|
13
|
+
import { appendAgentExecution, summarizeAgentExecutions } from './agent-health-metrics.js';
|
|
13
14
|
import { configManager } from '../services/config-file-manager.js';
|
|
14
15
|
const STORAGE_DIR = '.claude-flow';
|
|
15
16
|
const AGENT_DIR = 'agents';
|
|
@@ -669,6 +670,20 @@ export async function executeAgentTask(input) {
|
|
|
669
670
|
}
|
|
670
671
|
if (out)
|
|
671
672
|
current.lastResult = out;
|
|
673
|
+
const previousMeasuredHealth = summarizeAgentExecutions(current.executionHistory).health;
|
|
674
|
+
const mayUpdateStoredHealth = previousMeasuredHealth === null
|
|
675
|
+
? current.health === agent.health
|
|
676
|
+
: current.health === previousMeasuredHealth;
|
|
677
|
+
current.executionHistory = appendAgentExecution(current.executionHistory, {
|
|
678
|
+
at: new Date().toISOString(),
|
|
679
|
+
success: result.success,
|
|
680
|
+
durationMs: result.durationMs ?? Date.now() - startedAt,
|
|
681
|
+
});
|
|
682
|
+
// An explicit agent_update during the provider call still wins. The
|
|
683
|
+
// measured value remains available from executionHistory in agent_health.
|
|
684
|
+
if (mayUpdateStoredHealth) {
|
|
685
|
+
current.health = summarizeAgentExecutions(current.executionHistory).health ?? current.health;
|
|
686
|
+
}
|
|
672
687
|
saveAgentStore(latest);
|
|
673
688
|
};
|
|
674
689
|
if (result.success) {
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** Bounded execution evidence for agent_health. Registry records are not processes. */
|
|
2
|
+
export interface AgentExecutionSample {
|
|
3
|
+
at: string;
|
|
4
|
+
success: boolean;
|
|
5
|
+
durationMs: number;
|
|
6
|
+
}
|
|
7
|
+
export declare const AGENT_HEALTH_WINDOW = 20;
|
|
8
|
+
export declare function appendAgentExecution(value: unknown, sample: AgentExecutionSample): AgentExecutionSample[];
|
|
9
|
+
export declare function summarizeAgentExecutions(value: unknown): {
|
|
10
|
+
health: number | null;
|
|
11
|
+
errorRate: number | null;
|
|
12
|
+
sampleCount: number;
|
|
13
|
+
successes: number;
|
|
14
|
+
failures: number;
|
|
15
|
+
latency: {
|
|
16
|
+
avg: number | null;
|
|
17
|
+
p50: number | null;
|
|
18
|
+
p95: number | null;
|
|
19
|
+
p99: number | null;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=agent-health-metrics.d.ts.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export const AGENT_HEALTH_WINDOW = 20;
|
|
2
|
+
function validSamples(value) {
|
|
3
|
+
if (!Array.isArray(value))
|
|
4
|
+
return [];
|
|
5
|
+
return value.filter((sample) => sample !== null && typeof sample === 'object' &&
|
|
6
|
+
typeof sample.at === 'string' && typeof sample.success === 'boolean' &&
|
|
7
|
+
typeof sample.durationMs === 'number' && Number.isFinite(sample.durationMs) && sample.durationMs >= 0).slice(-AGENT_HEALTH_WINDOW);
|
|
8
|
+
}
|
|
9
|
+
export function appendAgentExecution(value, sample) {
|
|
10
|
+
return [...validSamples(value), sample].slice(-AGENT_HEALTH_WINDOW);
|
|
11
|
+
}
|
|
12
|
+
export function summarizeAgentExecutions(value) {
|
|
13
|
+
const samples = validSamples(value);
|
|
14
|
+
const sampleCount = samples.length;
|
|
15
|
+
const successes = samples.filter(sample => sample.success).length;
|
|
16
|
+
const failures = sampleCount - successes;
|
|
17
|
+
const durations = samples.map(sample => sample.durationMs).sort((a, b) => a - b);
|
|
18
|
+
const percentile = (p) => sampleCount ? durations[Math.ceil(p * sampleCount) - 1] : null;
|
|
19
|
+
return {
|
|
20
|
+
health: sampleCount ? successes / sampleCount : null,
|
|
21
|
+
errorRate: sampleCount ? failures / sampleCount : null,
|
|
22
|
+
sampleCount,
|
|
23
|
+
successes,
|
|
24
|
+
failures,
|
|
25
|
+
latency: {
|
|
26
|
+
avg: sampleCount ? durations.reduce((sum, duration) => sum + duration, 0) / sampleCount : null,
|
|
27
|
+
p50: percentile(0.5),
|
|
28
|
+
p95: percentile(0.95),
|
|
29
|
+
p99: percentile(0.99),
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=agent-health-metrics.js.map
|
|
@@ -10,6 +10,7 @@ import { readRecordStore } from './record-store.js';
|
|
|
10
10
|
import { getProjectCwd } from './types.js';
|
|
11
11
|
import { validateIdentifier, validateText, validateAgentSpawn } from './validate-input.js';
|
|
12
12
|
import { executeAgentTask } from './agent-execute-core.js';
|
|
13
|
+
import { AGENT_HEALTH_WINDOW, summarizeAgentExecutions } from './agent-health-metrics.js';
|
|
13
14
|
import { pheromoneAgentEligibility } from './swarm-tools.js';
|
|
14
15
|
// Storage paths
|
|
15
16
|
const STORAGE_DIR = '.claude-flow';
|
|
@@ -691,7 +692,7 @@ export const agentTools = [
|
|
|
691
692
|
},
|
|
692
693
|
{
|
|
693
694
|
name: 'agent_health',
|
|
694
|
-
description: '
|
|
695
|
+
description: 'Report measured agent_execute provider-call outcomes over the last 20 calls: API success-ratio health (0-1), error rate, and response-latency p50/p95. This does not measure task correctness. Agents with no observed calls have unknown health; registration is not proof of a running process. Native Task and hooks_post-task outcomes are not recorded here. Use when you need measured reliability for agents driven through agent_execute; native Task results are invisible to this tool, so inspect those directly.',
|
|
695
696
|
category: 'agent',
|
|
696
697
|
inputSchema: {
|
|
697
698
|
type: 'object',
|
|
@@ -708,58 +709,80 @@ export const agentTools = [
|
|
|
708
709
|
}
|
|
709
710
|
const store = loadAgentStore();
|
|
710
711
|
const agents = Object.values(store.agents).filter(a => a.status !== 'terminated');
|
|
711
|
-
const threshold = input.threshold
|
|
712
|
+
const threshold = input.threshold === undefined ? 0.5 : input.threshold;
|
|
713
|
+
if (!Number.isFinite(threshold) || threshold < 0 || threshold > 1) {
|
|
714
|
+
return { error: 'threshold must be a finite number between 0 and 1' };
|
|
715
|
+
}
|
|
716
|
+
const healthStatus = (health) => health === null ? 'unknown' : health >= threshold ? 'healthy' : health >= 0.3 ? 'degraded' : 'unhealthy';
|
|
712
717
|
if (input.agentId) {
|
|
713
718
|
const agent = store.agents[input.agentId];
|
|
714
719
|
if (agent) {
|
|
720
|
+
const metrics = summarizeAgentExecutions(agent.executionHistory);
|
|
715
721
|
return {
|
|
716
722
|
agentId: agent.agentId,
|
|
717
|
-
|
|
723
|
+
...metrics,
|
|
724
|
+
healthStatus: healthStatus(metrics.health),
|
|
718
725
|
status: agent.status,
|
|
719
|
-
healthy:
|
|
726
|
+
healthy: metrics.health === null ? null : metrics.health >= threshold,
|
|
720
727
|
taskCount: agent.taskCount,
|
|
721
|
-
uptime:
|
|
728
|
+
uptime: null,
|
|
729
|
+
registryAgeMs: Date.now() - new Date(agent.createdAt).getTime(),
|
|
730
|
+
windowSize: AGENT_HEALTH_WINDOW,
|
|
731
|
+
source: 'agent_execute',
|
|
722
732
|
};
|
|
723
733
|
}
|
|
724
734
|
return { agentId: input.agentId, error: 'Agent not found' };
|
|
725
735
|
}
|
|
726
|
-
const
|
|
727
|
-
const
|
|
728
|
-
const
|
|
729
|
-
const
|
|
736
|
+
const observed = agents.map(agent => ({ agent, metrics: summarizeAgentExecutions(agent.executionHistory) }));
|
|
737
|
+
const healthyAgents = observed.filter(({ metrics }) => healthStatus(metrics.health) === 'healthy');
|
|
738
|
+
const degradedAgents = observed.filter(({ metrics }) => healthStatus(metrics.health) === 'degraded');
|
|
739
|
+
const unhealthyAgents = observed.filter(({ metrics }) => healthStatus(metrics.health) === 'unhealthy');
|
|
740
|
+
const measuredAgents = observed.filter(({ metrics }) => metrics.health !== null);
|
|
741
|
+
const unknownCount = agents.length - measuredAgents.length;
|
|
742
|
+
const avgHealth = measuredAgents.length > 0
|
|
743
|
+
? measuredAgents.reduce((sum, { metrics }) => sum + metrics.health, 0) / measuredAgents.length
|
|
744
|
+
: null;
|
|
730
745
|
return {
|
|
731
746
|
// CLI expected fields
|
|
732
|
-
agents:
|
|
733
|
-
const uptime = Date.now() - new Date(a.createdAt).getTime();
|
|
747
|
+
agents: observed.map(({ agent, metrics }) => {
|
|
734
748
|
return {
|
|
735
|
-
id:
|
|
736
|
-
type:
|
|
737
|
-
health:
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
749
|
+
id: agent.agentId,
|
|
750
|
+
type: agent.agentType,
|
|
751
|
+
health: healthStatus(metrics.health),
|
|
752
|
+
score: metrics.health,
|
|
753
|
+
sampleCount: metrics.sampleCount,
|
|
754
|
+
errorRate: metrics.errorRate,
|
|
755
|
+
latency: metrics.latency,
|
|
756
|
+
errors: { count: metrics.failures },
|
|
757
|
+
uptime: null,
|
|
758
|
+
registryAgeMs: Date.now() - new Date(agent.createdAt).getTime(),
|
|
759
|
+
tasks: { active: agent.status === 'busy' ? 1 : 0, queued: null, completed: metrics.successes, failed: metrics.failures },
|
|
760
|
+
_note: `Provider-call outcomes and latency cover only the last ${AGENT_HEALTH_WINDOW} agent_execute calls; task correctness, process liveness and CPU/memory are not observed`,
|
|
741
761
|
};
|
|
742
762
|
}),
|
|
743
763
|
overall: {
|
|
744
764
|
healthy: healthyAgents.length,
|
|
745
765
|
degraded: degradedAgents.length,
|
|
746
766
|
unhealthy: unhealthyAgents.length,
|
|
767
|
+
unknown: unknownCount,
|
|
747
768
|
cpu: null,
|
|
748
769
|
memory: null,
|
|
749
770
|
_note: 'Per-agent CPU/memory not available — use system_metrics for real OS-level stats',
|
|
750
|
-
score: Math.round(avgHealth * 100),
|
|
771
|
+
score: avgHealth === null ? null : Math.round(avgHealth * 100),
|
|
751
772
|
issues: unhealthyAgents.length,
|
|
752
773
|
},
|
|
753
774
|
// Additional fields
|
|
754
775
|
total: agents.length,
|
|
776
|
+
observedCount: measuredAgents.length,
|
|
777
|
+
unknownCount,
|
|
755
778
|
healthyCount: healthyAgents.length,
|
|
756
779
|
unhealthyCount: unhealthyAgents.length,
|
|
757
780
|
threshold,
|
|
758
781
|
avgHealth,
|
|
759
|
-
unhealthyAgents: unhealthyAgents.map(
|
|
760
|
-
agentId:
|
|
761
|
-
health:
|
|
762
|
-
status:
|
|
782
|
+
unhealthyAgents: unhealthyAgents.map(({ agent, metrics }) => ({
|
|
783
|
+
agentId: agent.agentId,
|
|
784
|
+
health: metrics.health,
|
|
785
|
+
status: agent.status,
|
|
763
786
|
})),
|
|
764
787
|
};
|
|
765
788
|
},
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
*
|
|
51
51
|
* @module @claude-flow/cli/mcp-tools/agentbbs-federation
|
|
52
52
|
*/
|
|
53
|
-
import {
|
|
53
|
+
import type { Server } from 'node:http';
|
|
54
54
|
/** Max envelopes accepted from one peer in one sync. */
|
|
55
55
|
export declare const MAX_ENVELOPES_PER_SYNC = 5000;
|
|
56
56
|
/** Max serialized bytes for a single envelope. */
|
|
@@ -53,7 +53,12 @@
|
|
|
53
53
|
import { existsSync, mkdirSync, readFileSync, writeFileSync, appendFileSync, chmodSync } from 'node:fs';
|
|
54
54
|
import { join } from 'node:path';
|
|
55
55
|
import { createHash, randomBytes, timingSafeEqual } from 'node:crypto';
|
|
56
|
-
import {
|
|
56
|
+
import { createRequire } from 'node:module';
|
|
57
|
+
// Lazy, CommonJS-style load of node:http: an ESM `import … from 'node:http'`
|
|
58
|
+
// materialises every export of the builtin, which trips http's lazy undici
|
|
59
|
+
// getters (~2.5 MB heap / ~6 MB RSS) at MCP startup even when no server is
|
|
60
|
+
// ever started. require() of the builtin does not touch those getters.
|
|
61
|
+
const loadHttp = () => createRequire(import.meta.url)('node:http');
|
|
57
62
|
/** Max envelopes accepted from one peer in one sync. */
|
|
58
63
|
export const MAX_ENVELOPES_PER_SYNC = 5_000;
|
|
59
64
|
/** Max serialized bytes for a single envelope. */
|
|
@@ -389,7 +394,7 @@ export async function syncRoomFromPeer(basePath, peer, roomId, fetchImpl = fetch
|
|
|
389
394
|
*/
|
|
390
395
|
export function serveFederation(basePath, opts = {}) {
|
|
391
396
|
const host = opts.bindHost ?? '127.0.0.1';
|
|
392
|
-
const server = createServer((req, res) => {
|
|
397
|
+
const server = loadHttp().createServer((req, res) => {
|
|
393
398
|
const send = (code, obj) => {
|
|
394
399
|
const buf = Buffer.from(JSON.stringify(obj));
|
|
395
400
|
res.writeHead(code, { 'content-type': 'application/json', 'content-length': buf.length });
|
|
@@ -80,6 +80,21 @@ async function bridgeUnavailableResult() {
|
|
|
80
80
|
hint: 'Check Node version + try `ruflo memory init` to initialize manually.',
|
|
81
81
|
};
|
|
82
82
|
}
|
|
83
|
+
// #4135 / #4161: graph reads (agentdb_graph-query, agentdb_graph-pathfinder) open the
|
|
84
|
+
// graph_edges database; under encryption at rest that file is an RFE1 blob they cannot
|
|
85
|
+
// open. A recorded edge is then unreadable even though the write reports success, so the
|
|
86
|
+
// write says so instead of letting a caller assume it can read the edge back.
|
|
87
|
+
async function unreadableEdgeWarning() {
|
|
88
|
+
try {
|
|
89
|
+
const { isBridgeDbEncryptedAtRest } = await getGraphEdgeWriter();
|
|
90
|
+
if (!isBridgeDbEncryptedAtRest())
|
|
91
|
+
return undefined;
|
|
92
|
+
return 'Edge recorded, but graph reads (agentdb_graph-query, agentdb_graph-pathfinder) cannot open the graph_edges database because it is encrypted at rest (RFE1), so this edge cannot be read back yet. Your data is intact (#2889, #4135).';
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
83
98
|
let memInitMod = null;
|
|
84
99
|
async function getMemInit() {
|
|
85
100
|
if (!memInitMod)
|
|
@@ -554,7 +569,9 @@ export const agentdbCausalEdge = {
|
|
|
554
569
|
// Also record in AgentDB bridge for compatibility
|
|
555
570
|
const bridge = await getBridge();
|
|
556
571
|
await bridge.bridgeRecordCausalEdge({ sourceId, targetId, relation, weight: typeof params.weight === 'number' ? validateScore(params.weight, 0.5) : undefined }).catch(() => { });
|
|
557
|
-
|
|
572
|
+
const unreadable = await unreadableEdgeWarning();
|
|
573
|
+
const warning = [legacyWarning, unreadable].filter(Boolean).join(' ');
|
|
574
|
+
return { ...graphResult, _graphNodeBackend: true, ...(unreadable && { readable: false }), ...(warning && { warning }) };
|
|
558
575
|
}
|
|
559
576
|
}
|
|
560
577
|
}
|
|
@@ -567,7 +584,9 @@ export const agentdbCausalEdge = {
|
|
|
567
584
|
weight: typeof params.weight === 'number' ? validateScore(params.weight, 0.5) : undefined,
|
|
568
585
|
});
|
|
569
586
|
const baseResult = result ?? { success: false, error: 'AgentDB bridge not available. Use memory_store/memory_search instead.' };
|
|
570
|
-
|
|
587
|
+
const unreadable = baseResult.success === false ? undefined : await unreadableEdgeWarning();
|
|
588
|
+
const warning = [legacyWarning, unreadable].filter(Boolean).join(' ');
|
|
589
|
+
return warning ? { ...baseResult, ...(unreadable && { readable: false }), warning } : baseResult;
|
|
571
590
|
}
|
|
572
591
|
catch (error) {
|
|
573
592
|
return { success: false, error: sanitizeError(error) };
|
|
@@ -1157,6 +1176,10 @@ export const agentdbGraphQuery = {
|
|
|
1157
1176
|
catch (error) {
|
|
1158
1177
|
return { success: false, error: `Retained SQL graph query failed: ${sanitizeError(error)}`, mode, nodeId };
|
|
1159
1178
|
}
|
|
1179
|
+
// #4135: name the real reason when the retained store is encrypted at rest.
|
|
1180
|
+
const encryptedAtRest = await getGraphEdgeWriter().then((w) => w.isBridgeDbEncryptedAtRest()).catch(() => false);
|
|
1181
|
+
if (encryptedAtRest)
|
|
1182
|
+
return { success: false, ...(await bridgeUnavailableResult()), mode, nodeId };
|
|
1160
1183
|
return { success: false, error: 'Retained SQL graph_edges backend is unavailable for k-hop query', mode, nodeId };
|
|
1161
1184
|
}
|
|
1162
1185
|
// ── semantic mode ────────────────────────────────────────────────────────
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Known-miss registry for aidefence_learn (#4147).
|
|
3
|
+
*
|
|
4
|
+
* The detector's learned patterns are written by `learnFromDetection`, but
|
|
5
|
+
* they are only created from threats the detector already found, and
|
|
6
|
+
* `detect()` never reads them back. A threat the detector MISSED (a false
|
|
7
|
+
* negative, the case feedback matters most for) therefore had nowhere to go.
|
|
8
|
+
*
|
|
9
|
+
* This registry stores a SHA-256 of the whitespace/case-normalised input so a
|
|
10
|
+
* reported miss is flagged next time the identical text is scanned. It is an
|
|
11
|
+
* exact-match list, not generalisation: the raw text is never stored.
|
|
12
|
+
*/
|
|
13
|
+
export interface KnownMiss {
|
|
14
|
+
hash: string;
|
|
15
|
+
threatType: string;
|
|
16
|
+
reportedAt: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function hashInput(input: string): string;
|
|
19
|
+
/** Returns true when the miss was newly recorded, false when it was already known. */
|
|
20
|
+
export declare function recordKnownMiss(input: string, threatType: string, cwd?: string): boolean;
|
|
21
|
+
export declare function findKnownMiss(input: string, cwd?: string): KnownMiss | undefined;
|
|
22
|
+
//# sourceMappingURL=aidefence-known-misses.d.ts.map
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Known-miss registry for aidefence_learn (#4147).
|
|
3
|
+
*
|
|
4
|
+
* The detector's learned patterns are written by `learnFromDetection`, but
|
|
5
|
+
* they are only created from threats the detector already found, and
|
|
6
|
+
* `detect()` never reads them back. A threat the detector MISSED (a false
|
|
7
|
+
* negative, the case feedback matters most for) therefore had nowhere to go.
|
|
8
|
+
*
|
|
9
|
+
* This registry stores a SHA-256 of the whitespace/case-normalised input so a
|
|
10
|
+
* reported miss is flagged next time the identical text is scanned. It is an
|
|
11
|
+
* exact-match list, not generalisation: the raw text is never stored.
|
|
12
|
+
*/
|
|
13
|
+
import { createHash } from 'node:crypto';
|
|
14
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
15
|
+
import { join } from 'node:path';
|
|
16
|
+
const MAX_ENTRIES = 500;
|
|
17
|
+
function storePath(cwd) {
|
|
18
|
+
return join(cwd, '.claude-flow', 'aidefence-known-misses.json');
|
|
19
|
+
}
|
|
20
|
+
export function hashInput(input) {
|
|
21
|
+
const normalised = input.normalize('NFKC').toLowerCase().replace(/\s+/g, ' ').trim();
|
|
22
|
+
return createHash('sha256').update(normalised).digest('hex');
|
|
23
|
+
}
|
|
24
|
+
function load(cwd) {
|
|
25
|
+
try {
|
|
26
|
+
const p = storePath(cwd);
|
|
27
|
+
if (!existsSync(p))
|
|
28
|
+
return [];
|
|
29
|
+
const parsed = JSON.parse(readFileSync(p, 'utf8'));
|
|
30
|
+
return Array.isArray(parsed) ? parsed.filter((e) => e && typeof e.hash === 'string') : [];
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return [];
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/** Returns true when the miss was newly recorded, false when it was already known. */
|
|
37
|
+
export function recordKnownMiss(input, threatType, cwd = process.cwd()) {
|
|
38
|
+
const hash = hashInput(input);
|
|
39
|
+
const entries = load(cwd);
|
|
40
|
+
if (entries.some((e) => e.hash === hash))
|
|
41
|
+
return false;
|
|
42
|
+
entries.push({ hash, threatType, reportedAt: new Date().toISOString() });
|
|
43
|
+
const dir = join(cwd, '.claude-flow');
|
|
44
|
+
mkdirSync(dir, { recursive: true });
|
|
45
|
+
const tmp = `${storePath(cwd)}.${process.pid}.tmp`;
|
|
46
|
+
writeFileSync(tmp, JSON.stringify(entries.slice(-MAX_ENTRIES), null, 2), { mode: 0o600 });
|
|
47
|
+
renameSync(tmp, storePath(cwd));
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
export function findKnownMiss(input, cwd = process.cwd()) {
|
|
51
|
+
const entries = load(cwd);
|
|
52
|
+
if (entries.length === 0)
|
|
53
|
+
return undefined;
|
|
54
|
+
const hash = hashInput(input);
|
|
55
|
+
return entries.find((e) => e.hash === hash);
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=aidefence-known-misses.js.map
|
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
* Provides diff analysis and classification via MCP protocol
|
|
4
4
|
*/
|
|
5
5
|
import { validateGitRef, validatePath } from './validate-input.js';
|
|
6
|
-
import { analyzeDiff, assessFileRisk, assessOverallRisk,
|
|
6
|
+
import { analyzeDiff, assessFileRisk, assessOverallRisk, suggestReviewers, getGitDiffNumstat, } from '../ruvector/diff-classifier.js';
|
|
7
7
|
/**
|
|
8
8
|
* Diff Analysis Tool
|
|
9
9
|
* Analyzes git diffs for change risk assessment and classification
|
|
10
10
|
*/
|
|
11
11
|
export const analyzeDiffTool = {
|
|
12
12
|
name: 'analyze_diff',
|
|
13
|
-
description: '
|
|
13
|
+
description: 'Heuristically analyze git diff paths, changed lines, and one-commit subjects for risk and classification. Use when preparing a review that needs a quick evidence-based summary; inspect the diff for final judgment.',
|
|
14
14
|
category: 'analyze',
|
|
15
15
|
tags: ['diff', 'risk', 'classification', 'git'],
|
|
16
16
|
inputSchema: {
|
|
@@ -85,7 +85,7 @@ export const analyzeDiffTool = {
|
|
|
85
85
|
*/
|
|
86
86
|
export const diffRiskTool = {
|
|
87
87
|
name: 'analyze_diff-risk',
|
|
88
|
-
description: '
|
|
88
|
+
description: 'Heuristically assess git diff risk from changed file paths, sizes, and status. Use when triaging which files merit closer human review; this is not a security guarantee.',
|
|
89
89
|
category: 'analyze',
|
|
90
90
|
tags: ['diff', 'risk', 'git'],
|
|
91
91
|
inputSchema: {
|
|
@@ -130,7 +130,7 @@ export const diffRiskTool = {
|
|
|
130
130
|
*/
|
|
131
131
|
export const diffClassifyTool = {
|
|
132
132
|
name: 'analyze_diff-classify',
|
|
133
|
-
description: '
|
|
133
|
+
description: 'Heuristically classify a git diff from changed lines, file paths, and a single-commit subject when available. Use when you need a coarse change category; inspect the diff to confirm.',
|
|
134
134
|
category: 'analyze',
|
|
135
135
|
tags: ['diff', 'classification', 'git'],
|
|
136
136
|
inputSchema: {
|
|
@@ -151,12 +151,11 @@ export const diffClassifyTool = {
|
|
|
151
151
|
}
|
|
152
152
|
const ref = params.ref || 'HEAD';
|
|
153
153
|
try {
|
|
154
|
-
const
|
|
155
|
-
const classification = classifyDiff(files);
|
|
154
|
+
const result = await analyzeDiff({ ref });
|
|
156
155
|
return {
|
|
157
156
|
ref,
|
|
158
|
-
classification,
|
|
159
|
-
files: files.length,
|
|
157
|
+
classification: result.classification,
|
|
158
|
+
files: result.files.length,
|
|
160
159
|
};
|
|
161
160
|
}
|
|
162
161
|
catch (error) {
|
|
@@ -174,7 +173,7 @@ export const diffClassifyTool = {
|
|
|
174
173
|
*/
|
|
175
174
|
export const diffReviewersTool = {
|
|
176
175
|
name: 'analyze_diff-reviewers',
|
|
177
|
-
description: 'Suggest
|
|
176
|
+
description: 'Suggest reviewer roles from git diff paths and risk; does not identify people or read CODEOWNERS. Use when triaging expertise for a change, then consult CODEOWNERS to assign people.',
|
|
178
177
|
category: 'analyze',
|
|
179
178
|
tags: ['diff', 'reviewers', 'git'],
|
|
180
179
|
inputSchema: {
|
|
@@ -225,7 +224,7 @@ export const diffReviewersTool = {
|
|
|
225
224
|
*/
|
|
226
225
|
export const fileRiskTool = {
|
|
227
226
|
name: 'analyze_file-risk',
|
|
228
|
-
description: '
|
|
227
|
+
description: 'Heuristically assess risk for a file path and optional change metadata. Use when triaging a changed file for focused review; inspect the source for final judgment.',
|
|
229
228
|
category: 'analyze',
|
|
230
229
|
tags: ['file', 'risk'],
|
|
231
230
|
inputSchema: {
|
|
@@ -280,7 +279,7 @@ export const fileRiskTool = {
|
|
|
280
279
|
*/
|
|
281
280
|
export const diffStatsTool = {
|
|
282
281
|
name: 'analyze_diff-stats',
|
|
283
|
-
description: '
|
|
282
|
+
description: 'Count changed files, additions, and deletions in a git diff. Use when you need a quick size summary before inspecting the full patch.',
|
|
284
283
|
category: 'analyze',
|
|
285
284
|
tags: ['diff', 'stats', 'git'],
|
|
286
285
|
inputSchema: {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* @module @claude-flow/cli/mcp-tools/autopilot
|
|
9
9
|
*/
|
|
10
10
|
import { validateText } from './validate-input.js';
|
|
11
|
-
import { loadState, saveState, appendLog, loadLog, discoverTasks, isTerminal, tryLoadLearning, validateNumber, validateConfiguredTaskSources, VALID_TASK_SOURCES, } from '../autopilot-state.js';
|
|
11
|
+
import { loadState, saveState, appendLog, loadLog, discoverTasks, isTerminal, tryLoadLearning, getLearningLoadError, validateNumber, validateConfiguredTaskSources, VALID_TASK_SOURCES, } from '../autopilot-state.js';
|
|
12
12
|
function ok(data) {
|
|
13
13
|
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
14
14
|
}
|
|
@@ -175,7 +175,7 @@ const autopilotLearn = {
|
|
|
175
175
|
]);
|
|
176
176
|
return ok({ metrics, patterns });
|
|
177
177
|
}
|
|
178
|
-
return ok({ available: false, reason: 'AgentDB/AutopilotLearning not initialized', patterns: [] });
|
|
178
|
+
return ok({ available: false, reason: getLearningLoadError() ?? 'AgentDB/AutopilotLearning not initialized', patterns: [] });
|
|
179
179
|
},
|
|
180
180
|
};
|
|
181
181
|
const autopilotHistory = {
|
|
@@ -85,9 +85,13 @@
|
|
|
85
85
|
* @module @claude-flow/cli/mcp-tools/browser-intent
|
|
86
86
|
*/
|
|
87
87
|
import { createRequire } from 'node:module';
|
|
88
|
-
import { createServer } from 'node:http';
|
|
89
88
|
import { readFileSync, existsSync } from 'node:fs';
|
|
90
89
|
import { dirname, join } from 'node:path';
|
|
90
|
+
// Lazy, CommonJS-style load of node:http: an ESM `import … from 'node:http'`
|
|
91
|
+
// materialises every export of the builtin, which trips http's lazy undici
|
|
92
|
+
// getters (~2.5 MB heap / ~6 MB RSS) at MCP startup even when no server is
|
|
93
|
+
// ever started. require() of the builtin does not touch those getters.
|
|
94
|
+
const loadHttp = () => createRequire(import.meta.url)('node:http');
|
|
91
95
|
import { validateText, validateIdentifier } from './validate-input.js';
|
|
92
96
|
import { execBrowserCommand } from './browser-tools.js';
|
|
93
97
|
import { storeEntry } from '../memory/memory-initializer.js';
|
|
@@ -175,7 +179,7 @@ export function resolvePageAgentLLMConfig(env = process.env) {
|
|
|
175
179
|
export const PLACEHOLDER_API_KEY = 'ruflo-proxied-key';
|
|
176
180
|
export function startLocalLLMProxy(real) {
|
|
177
181
|
return new Promise((resolve, reject) => {
|
|
178
|
-
const server = createServer((req, res) => {
|
|
182
|
+
const server = loadHttp().createServer((req, res) => {
|
|
179
183
|
const target = `${real.baseURL.replace(/\/+$/, '')}${req.url ?? ''}`;
|
|
180
184
|
const chunks = [];
|
|
181
185
|
req.on('data', (c) => chunks.push(c));
|
|
@@ -93,16 +93,28 @@ function toPoincare(euclidean, curvature) {
|
|
|
93
93
|
const factor = Math.tanh(sqrtC * norm / 2) / (sqrtC * norm + 1e-15);
|
|
94
94
|
return euclidean.map(x => x * factor);
|
|
95
95
|
}
|
|
96
|
-
//
|
|
96
|
+
// #4159: a point is usable only strictly inside the ball of radius 1/sqrt(c).
|
|
97
|
+
function insidePoincareBall(p, curvature) {
|
|
98
|
+
const c = Math.abs(curvature);
|
|
99
|
+
return c * p.reduce((sum, x) => sum + x * x, 0) < 1;
|
|
100
|
+
}
|
|
101
|
+
// Poincaré distance, d_c(a,b) = acosh(1 + 2c|a-b|^2 / ((1-c|a|^2)(1-c|b|^2))) / sqrt(c).
|
|
102
|
+
// Returns NaN for a point on or outside the ball (the formula is undefined there);
|
|
103
|
+
// callers must check Number.isFinite before reporting it.
|
|
97
104
|
function poincareDistance(a, b, curvature) {
|
|
98
105
|
const c = Math.abs(curvature);
|
|
106
|
+
if (a.length !== b.length || !insidePoincareBall(a, curvature) || !insidePoincareBall(b, curvature)) {
|
|
107
|
+
return NaN;
|
|
108
|
+
}
|
|
99
109
|
const diffSq = a.reduce((sum, _, i) => sum + (a[i] - b[i]) ** 2, 0);
|
|
100
110
|
const normASq = a.reduce((sum, x) => sum + x * x, 0);
|
|
101
111
|
const normBSq = b.reduce((sum, x) => sum + x * x, 0);
|
|
102
|
-
const denom = (1 - normASq) * (1 - normBSq);
|
|
103
|
-
const delta = 2 * diffSq /
|
|
112
|
+
const denom = (1 - c * normASq) * (1 - c * normBSq);
|
|
113
|
+
const delta = (2 * c * diffSq) / denom;
|
|
104
114
|
return (1 / Math.sqrt(c)) * Math.acosh(1 + delta);
|
|
105
115
|
}
|
|
116
|
+
const HYPERBOLIC_RADIUS_NOTE = 'convert is an exponential map at the origin: it preserves the input norm ordering, so unit-norm text embeddings all land at the same radius. ' +
|
|
117
|
+
'It does not encode hierarchy; depth/tree structure has to come from the hierarchy itself (for example depth chosen as the radius, or embeddings trained on the tree edges).';
|
|
106
118
|
// Cosine similarity
|
|
107
119
|
function cosineSimilarity(a, b) {
|
|
108
120
|
const dot = a.reduce((sum, _, i) => sum + a[i] * b[i], 0);
|
|
@@ -339,6 +351,9 @@ export const embeddingsTools = [
|
|
|
339
351
|
const poinc1 = toPoincare(emb1, config.hyperbolic.curvature);
|
|
340
352
|
const poinc2 = toPoincare(emb2, config.hyperbolic.curvature);
|
|
341
353
|
distance = poincareDistance(poinc1, poinc2, config.hyperbolic.curvature);
|
|
354
|
+
if (!Number.isFinite(distance)) {
|
|
355
|
+
return { success: false, error: 'Hyperbolic distance is undefined for these inputs (a converted point reached the ball boundary).' };
|
|
356
|
+
}
|
|
342
357
|
similarity = 1 / (1 + distance);
|
|
343
358
|
break;
|
|
344
359
|
case 'euclidean':
|
|
@@ -739,6 +754,7 @@ export const embeddingsTools = [
|
|
|
739
754
|
poincare,
|
|
740
755
|
curvature,
|
|
741
756
|
poincareNorm: Math.sqrt(poincare.reduce((sum, x) => sum + x * x, 0)),
|
|
757
|
+
note: HYPERBOLIC_RADIUS_NOTE,
|
|
742
758
|
};
|
|
743
759
|
case 'distance':
|
|
744
760
|
const emb1 = input.embedding1;
|
|
@@ -747,6 +763,13 @@ export const embeddingsTools = [
|
|
|
747
763
|
return { success: false, error: 'embedding1 and embedding2 required for distance action' };
|
|
748
764
|
}
|
|
749
765
|
const dist = poincareDistance(emb1, emb2, curvature);
|
|
766
|
+
if (!Number.isFinite(dist)) {
|
|
767
|
+
return {
|
|
768
|
+
success: false,
|
|
769
|
+
error: 'Poincaré distance needs two points of equal length strictly inside the ball (|x| < 1/sqrt(c)); got a point on or outside it, or mismatched lengths. Run convert on Euclidean vectors first.',
|
|
770
|
+
curvature,
|
|
771
|
+
};
|
|
772
|
+
}
|
|
750
773
|
return {
|
|
751
774
|
success: true,
|
|
752
775
|
action: 'distance',
|
|
@@ -780,11 +803,10 @@ export const embeddingsTools = [
|
|
|
780
803
|
epsilon: config.hyperbolic.epsilon,
|
|
781
804
|
maxNorm: config.hyperbolic.maxNorm,
|
|
782
805
|
},
|
|
806
|
+
note: HYPERBOLIC_RADIUS_NOTE,
|
|
783
807
|
benefits: [
|
|
784
|
-
'
|
|
785
|
-
'
|
|
786
|
-
'Preserves tree-like structures',
|
|
787
|
-
'Natural for taxonomy embeddings',
|
|
808
|
+
'Exponential capacity in low dimensions when the points are placed by hierarchy (for example by depth)',
|
|
809
|
+
'Natural geometry for taxonomy embeddings that are trained or constructed for it',
|
|
788
810
|
],
|
|
789
811
|
};
|
|
790
812
|
}
|
|
@@ -3253,6 +3253,8 @@ export const hooksTrajectoryEnd = {
|
|
|
3253
3253
|
learned: result.learned,
|
|
3254
3254
|
patternKey: result.patternKey,
|
|
3255
3255
|
confidence: result.confidence,
|
|
3256
|
+
reinforcedNeighbour: result.reinforcedNeighbour,
|
|
3257
|
+
routingGate: result.routingGate,
|
|
3256
3258
|
};
|
|
3257
3259
|
}
|
|
3258
3260
|
catch {
|
|
@@ -3416,6 +3418,8 @@ export const hooksTrajectoryEnd = {
|
|
|
3416
3418
|
sonaUpdate: sonaResult.learned,
|
|
3417
3419
|
sonaPatternKey: sonaResult.patternKey || undefined,
|
|
3418
3420
|
sonaConfidence: sonaResult.confidence || undefined,
|
|
3421
|
+
sonaReinforcedNeighbour: sonaResult.reinforcedNeighbour || undefined,
|
|
3422
|
+
sonaRoutingGate: sonaResult.routingGate,
|
|
3419
3423
|
ewcConsolidation: ewcResult.consolidated,
|
|
3420
3424
|
ewcPenalty: ewcResult.penalty || undefined,
|
|
3421
3425
|
patternsExtracted,
|