@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.
Files changed (120) hide show
  1. package/.claude/helpers/.helpers-version +1 -1
  2. package/.claude/helpers/helpers.manifest.json +4 -4
  3. package/.claude/helpers/hook-handler.cjs +109 -23
  4. package/.claude/helpers/intelligence.cjs +51 -9
  5. package/.claude/helpers/router.cjs +0 -0
  6. package/.claude/skills/verification-quality/SKILL.md +14 -11
  7. package/README.md +1 -1
  8. package/catalog-manifest.json +2 -2
  9. package/dist/src/autopilot-state.d.ts +2 -0
  10. package/dist/src/autopilot-state.js +36 -8
  11. package/dist/src/command-resolution.d.ts +15 -0
  12. package/dist/src/command-resolution.js +25 -0
  13. package/dist/src/commands/agent.js +17 -17
  14. package/dist/src/commands/analyze.js +40 -29
  15. package/dist/src/commands/claims.js +6 -8
  16. package/dist/src/commands/daemon.js +86 -31
  17. package/dist/src/commands/memory.js +14 -2
  18. package/dist/src/commands/security-code-patterns.d.ts +2 -2
  19. package/dist/src/commands/security-code-patterns.js +61 -3
  20. package/dist/src/commands/security.d.ts +24 -0
  21. package/dist/src/commands/security.js +81 -35
  22. package/dist/src/commands/tui.d.ts +9 -0
  23. package/dist/src/commands/tui.js +29 -3
  24. package/dist/src/config/proven-config-refresh.d.ts +9 -0
  25. package/dist/src/config/proven-config-refresh.js +24 -3
  26. package/dist/src/funnel/local-signals.js +2 -1
  27. package/dist/src/index.js +6 -42
  28. package/dist/src/init/helpers-generator.js +109 -23
  29. package/dist/src/init/types.js +1 -1
  30. package/dist/src/mcp-tools/agent-execute-core.d.ts +2 -0
  31. package/dist/src/mcp-tools/agent-execute-core.js +15 -0
  32. package/dist/src/mcp-tools/agent-health-metrics.d.ts +22 -0
  33. package/dist/src/mcp-tools/agent-health-metrics.js +33 -0
  34. package/dist/src/mcp-tools/agent-tools.js +45 -22
  35. package/dist/src/mcp-tools/agentbbs-federation.d.ts +1 -1
  36. package/dist/src/mcp-tools/agentbbs-federation.js +7 -2
  37. package/dist/src/mcp-tools/agentdb-tools.js +25 -2
  38. package/dist/src/mcp-tools/aidefence-known-misses.d.ts +22 -0
  39. package/dist/src/mcp-tools/aidefence-known-misses.js +57 -0
  40. package/dist/src/mcp-tools/analyze-tools.js +10 -11
  41. package/dist/src/mcp-tools/autopilot-tools.js +2 -2
  42. package/dist/src/mcp-tools/browser-intent-tools.js +6 -2
  43. package/dist/src/mcp-tools/embeddings-tools.js +29 -7
  44. package/dist/src/mcp-tools/hooks-tools.js +4 -0
  45. package/dist/src/mcp-tools/memory-tools.js +70 -20
  46. package/dist/src/mcp-tools/neural-tools.js +28 -4
  47. package/dist/src/mcp-tools/ruvllm-tools.js +6 -0
  48. package/dist/src/mcp-tools/security-tools.js +55 -17
  49. package/dist/src/mcp-tools/workflow-tools.js +66 -17
  50. package/dist/src/memory/intelligence.d.ts +5 -1
  51. package/dist/src/memory/intelligence.js +105 -29
  52. package/dist/src/memory/memory-bridge.js +10 -3
  53. package/dist/src/memory/memory-initializer.js +2 -1
  54. package/dist/src/memory/search-defaults.d.ts +54 -0
  55. package/dist/src/memory/search-defaults.js +37 -0
  56. package/dist/src/memory/sona-optimizer.d.ts +28 -0
  57. package/dist/src/memory/sona-optimizer.js +59 -2
  58. package/dist/src/mods/command-registry/schema.d.ts +24 -24
  59. package/dist/src/parser.js +32 -22
  60. package/dist/src/production/monitoring.d.ts +1 -0
  61. package/dist/src/production/monitoring.js +19 -6
  62. package/dist/src/production/rate-limiter.js +5 -4
  63. package/dist/src/production/retry.js +3 -0
  64. package/dist/src/ruvector/agent-wasm.d.ts +10 -0
  65. package/dist/src/ruvector/agent-wasm.js +27 -1
  66. package/dist/src/ruvector/diff-classifier.d.ts +2 -4
  67. package/dist/src/ruvector/diff-classifier.js +202 -69
  68. package/dist/src/ruvector/ruvllm-wasm.d.ts +4 -0
  69. package/dist/src/ruvector/ruvllm-wasm.js +12 -1
  70. package/dist/src/services/config-file-manager.js +3 -3
  71. package/dist/src/services/policy-runtime.d.ts +1 -1
  72. package/dist/src/services/policy-runtime.js +7 -1
  73. package/dist/src/services/worker-daemon.d.ts +11 -0
  74. package/dist/src/services/worker-daemon.js +37 -0
  75. package/dist/src/transfer/store/discovery.js +16 -17
  76. package/dist/src/transfer/store/types.d.ts +4 -0
  77. package/node_modules/@claude-flow/codex/dist/cli.js +0 -0
  78. package/node_modules/@claude-flow/codex/dist/dual-mode/orchestrator.d.ts +1 -0
  79. package/node_modules/@claude-flow/codex/dist/dual-mode/orchestrator.d.ts.map +1 -1
  80. package/node_modules/@claude-flow/codex/dist/dual-mode/orchestrator.js +3 -1
  81. package/node_modules/@claude-flow/codex/dist/dual-mode/orchestrator.js.map +1 -1
  82. package/node_modules/@claude-flow/codex/dist/index.d.ts +3 -3
  83. package/node_modules/@claude-flow/codex/dist/index.js +1 -1
  84. package/node_modules/@claude-flow/codex/package.json +1 -1
  85. package/node_modules/@claude-flow/plugin-agent-federation/dist/bin.js +0 -0
  86. package/node_modules/@claude-flow/security/dist/input-validator.d.ts +6 -6
  87. package/node_modules/@claude-flow/security/dist/policy/envelope.js +6 -6
  88. package/node_modules/@claude-flow/security/dist/policy/envelope.js.map +1 -1
  89. package/node_modules/@claude-flow/security/package.json +1 -1
  90. package/package.json +7 -7
  91. package/plugins/ruflo-console/dist/tui.mjs +162 -160
  92. package/plugins/ruflo-metaharness/.claude-flow/daemon-state.json +179 -0
  93. package/plugins/ruflo-metaharness/.claude-flow/daemon.pid +1 -0
  94. package/plugins/ruflo-metaharness/.claude-flow/logs/daemon.log +32 -0
  95. package/plugins/ruflo-metaharness/.claude-flow/metrics/backup.json +23 -0
  96. package/plugins/ruflo-metaharness/.claude-flow/metrics/codebase-map.json +11 -0
  97. package/plugins/ruflo-metaharness/.claude-flow/metrics/consolidation.json +16 -0
  98. package/plugins/ruflo-metaharness/.claude-flow/metrics/harness-loop.json +91 -0
  99. package/plugins/ruflo-metaharness/.claude-flow/metrics/performance.json +17 -0
  100. package/plugins/ruflo-metaharness/.claude-flow/metrics/security-audit.json +12 -0
  101. package/plugins/ruflo-metaharness/.claude-flow/metrics/test-gaps.json +8 -0
  102. package/plugins/ruflo-metaharness/.claude-flow/neural/patterns.json +2809 -0
  103. package/plugins/ruflo-metaharness/.claude-flow/neural/stats.json +6 -0
  104. package/plugins/ruflo-metaharness/.claude-flow/policy/state.json +11 -0
  105. package/plugins/ruflo-metaharness/.claude-plugin/plugin.json +1 -1
  106. package/plugins/ruflo-metaharness/.swarm/agentdb-memory.db +0 -0
  107. package/plugins/ruflo-metaharness/.swarm/agentdb-memory.db-shm +0 -0
  108. package/plugins/ruflo-metaharness/.swarm/agentdb-memory.db-wal +0 -0
  109. package/plugins/ruflo-metaharness/.swarm/backups/memory-2026-10-14T16-09-38-164Z.db +0 -0
  110. package/plugins/ruflo-metaharness/.swarm/backups/store-agentdb-memory.db-2026-10-14T16-09-38-166Z.db +0 -0
  111. package/plugins/ruflo-metaharness/.swarm/ewc-fisher.json +397 -0
  112. package/plugins/ruflo-metaharness/.swarm/memory.db +0 -0
  113. package/plugins/ruflo-metaharness/.swarm/memory.db-shm +0 -0
  114. package/plugins/ruflo-metaharness/.swarm/memory.db-wal +0 -0
  115. package/plugins/ruflo-metaharness/CHANGELOG.md +3 -0
  116. package/plugins/ruflo-metaharness/agentdb.rvf +0 -0
  117. package/plugins/ruflo-metaharness/agentdb.rvf.lock +0 -0
  118. package/plugins/ruflo-metaharness/hooks/screen.ts +10 -3
  119. package/plugins/ruflo-metaharness/ruvector.db +0 -0
  120. 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: 'Compute an agent\'s rolling health score (0-1) from recent task success ratio + response-latency p50/p95 + error rate. Use when native Task tool is wrong because you\'re running a long-lived agent (autonomous loop / hive-mind worker / federation peer) and need to detect degradation before the breaker trips it. For one-shot Task invocations there is no history to score. Pair with hooks_post-task so the scores stay current.',
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 || 0.5;
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
- health: agent.health,
723
+ ...metrics,
724
+ healthStatus: healthStatus(metrics.health),
718
725
  status: agent.status,
719
- healthy: agent.health >= threshold,
726
+ healthy: metrics.health === null ? null : metrics.health >= threshold,
720
727
  taskCount: agent.taskCount,
721
- uptime: Date.now() - new Date(agent.createdAt).getTime(),
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 healthyAgents = agents.filter(a => a.health >= threshold);
727
- const degradedAgents = agents.filter(a => a.health >= 0.3 && a.health < threshold);
728
- const unhealthyAgents = agents.filter(a => a.health < 0.3);
729
- const avgHealth = agents.length > 0 ? agents.reduce((sum, a) => sum + a.health, 0) / agents.length : 1;
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: agents.map(a => {
733
- const uptime = Date.now() - new Date(a.createdAt).getTime();
747
+ agents: observed.map(({ agent, metrics }) => {
734
748
  return {
735
- id: a.agentId,
736
- type: a.agentType,
737
- health: a.health >= threshold ? 'healthy' : (a.health >= 0.3 ? 'degraded' : 'unhealthy'),
738
- uptime,
739
- tasks: { active: a.taskCount > 0 ? 1 : 0, queued: 0, completed: a.taskCount, failed: 0 },
740
- _note: 'Per-agent OS metrics not available — use system_metrics for real CPU/memory',
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(a => ({
760
- agentId: a.agentId,
761
- health: a.health,
762
- status: a.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 { type Server } from 'node:http';
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 { createServer } from 'node:http';
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
- return { ...graphResult, _graphNodeBackend: true, ...(legacyWarning && { warning: legacyWarning }) };
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
- return legacyWarning ? { ...baseResult, warning: legacyWarning } : baseResult;
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, classifyDiff, suggestReviewers, getGitDiffNumstat, } from '../ruvector/diff-classifier.js';
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: 'Analyze git diff for change risk assessment and classification Use when native `git diff` / `grep` / static analysis is wrong because you want LLM-graded change classification, reviewer recommendations, or risk scoring. For literal-text inspection, native tools are fine.',
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: 'Quick risk assessment for git diff Use when native `git diff` / `grep` / static analysis is wrong because you want LLM-graded change classification, reviewer recommendations, or risk scoring. For literal-text inspection, native tools are fine.',
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: 'Classify git diff change type Use when native `git diff` / `grep` / static analysis is wrong because you want LLM-graded change classification, reviewer recommendations, or risk scoring. For literal-text inspection, native tools are fine.',
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 files = getGitDiffNumstat(ref);
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 reviewers for git diff changes Use when native `git diff` / `grep` / static analysis is wrong because you want LLM-graded change classification, reviewer recommendations, or risk scoring. For literal-text inspection, native tools are fine.',
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: 'Assess risk for a specific file change Use when native `git diff` / `grep` / static analysis is wrong because you want LLM-graded change classification, reviewer recommendations, or risk scoring. For literal-text inspection, native tools are fine.',
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: 'Get quick statistics for git diff Use when native `git diff` / `grep` / static analysis is wrong because you want LLM-graded change classification, reviewer recommendations, or risk scoring. For literal-text inspection, native tools are fine.',
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
- // Poincaré distance
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 / (denom + 1e-15);
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
- 'Better hierarchical data representation',
785
- 'Exponential capacity in low dimensions',
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,