@claude-flow/cli 3.41.4 → 3.42.1
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 +2 -2
- package/catalog-manifest.json +2 -2
- package/dist/src/commands/doctor.d.ts +7 -0
- package/dist/src/commands/doctor.js +58 -35
- package/dist/src/commands/hive-mind.js +4 -3
- package/dist/src/commands/swarm.js +89 -18
- package/dist/src/init/claudemd-generator.js +2 -2
- package/dist/src/mcp-server.js +12 -0
- package/dist/src/mcp-tools/agentbbs-tools.d.ts +15 -0
- package/dist/src/mcp-tools/agentbbs-tools.js +98 -14
- package/dist/src/mcp-tools/agentdb-tools.js +17 -1
- package/dist/src/mcp-tools/hive-mind-tools.d.ts +8 -0
- package/dist/src/mcp-tools/hive-mind-tools.js +80 -2
- package/dist/src/mcp-tools/memory-tools.js +102 -31
- package/dist/src/mcp-tools/policy-enforcer.d.ts +126 -0
- package/dist/src/mcp-tools/policy-enforcer.js +177 -0
- package/dist/src/mcp-tools/seraphina-tools.js +7 -2
- package/dist/src/mcp-tools/x-federation-tools.d.ts +12 -0
- package/dist/src/mcp-tools/x-federation-tools.js +87 -5
- package/dist/src/memory/intelligence.d.ts +14 -2
- package/dist/src/memory/intelligence.js +33 -12
- package/dist/src/memory/memory-bridge.js +10 -2
- package/dist/src/memory/memory-initializer.js +19 -2
- package/dist/src/ruvector/graph-backend.js +112 -26
- package/dist/src/services/policy-runtime.js +63 -5
- package/node_modules/@claude-flow/codex/dist/dual-mode/orchestrator.d.ts +4 -0
- package/node_modules/@claude-flow/codex/dist/dual-mode/orchestrator.d.ts.map +1 -1
- package/node_modules/@claude-flow/codex/dist/dual-mode/orchestrator.js +38 -1
- package/node_modules/@claude-flow/codex/dist/dual-mode/orchestrator.js.map +1 -1
- package/node_modules/@claude-flow/codex/package.json +2 -1
- package/node_modules/@claude-flow/security/dist/index.d.ts +1 -1
- package/node_modules/@claude-flow/security/dist/index.d.ts.map +1 -1
- package/node_modules/@claude-flow/security/dist/index.js +1 -1
- package/node_modules/@claude-flow/security/dist/index.js.map +1 -1
- package/node_modules/@claude-flow/security/dist/mcp-caller-identity.d.ts +11 -0
- package/node_modules/@claude-flow/security/dist/mcp-caller-identity.d.ts.map +1 -1
- package/node_modules/@claude-flow/security/dist/mcp-caller-identity.js +0 -0
- package/node_modules/@claude-flow/security/dist/mcp-caller-identity.js.map +1 -1
- package/node_modules/@claude-flow/security/package.json +1 -0
- package/package.json +1 -1
- package/dist/src/ruvector/diskann-backend.d.ts +0 -78
- package/dist/src/ruvector/diskann-backend.js +0 -310
|
@@ -1 +1 @@
|
|
|
1
|
-
3.
|
|
1
|
+
3.42.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifest": {
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.42.1",
|
|
4
4
|
"files": {
|
|
5
5
|
"auto-memory-hook.mjs": "85fe05c757421c52137c0bc8545a0896bab6b4714538c2a11d1d0835bfcc8c1c",
|
|
6
6
|
"hook-handler.cjs": "209d9fafe10e17d1be0866727f6f9cf9ac66f9a0793f1c793a4f58319e8e4583",
|
|
@@ -8,6 +8,6 @@
|
|
|
8
8
|
"statusline.cjs": "4a48353b4f1566fa4379b00fd0321b6676a22b6cc91fbbd8380ac5183d619468"
|
|
9
9
|
}
|
|
10
10
|
},
|
|
11
|
-
"signature": "
|
|
11
|
+
"signature": "dp+TQNrg1n2wi3wjdGNFDBLjRnKSN7vuEvm4z8IC6Vmzlfh4DaTHX4sozhmxAt5GeVcLxjEq6urtK8s0vUADCQ==",
|
|
12
12
|
"algorithm": "ed25519"
|
|
13
13
|
}
|
package/catalog-manifest.json
CHANGED
|
@@ -5,6 +5,13 @@
|
|
|
5
5
|
* Created with ruv.io
|
|
6
6
|
*/
|
|
7
7
|
import type { Command } from '../types.js';
|
|
8
|
+
interface HealthCheck {
|
|
9
|
+
name: string;
|
|
10
|
+
status: 'pass' | 'warn' | 'fail';
|
|
11
|
+
message: string;
|
|
12
|
+
fix?: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function checkMemoryPersistenceDriver(): Promise<HealthCheck>;
|
|
8
15
|
export declare const doctorCommand: Command;
|
|
9
16
|
export default doctorCommand;
|
|
10
17
|
//# sourceMappingURL=doctor.d.ts.map
|
|
@@ -506,23 +506,13 @@ async function checkMemoryStructuralIntegrity() {
|
|
|
506
506
|
catch { /* best-effort */ }
|
|
507
507
|
}
|
|
508
508
|
}
|
|
509
|
-
// #2968
|
|
510
|
-
//
|
|
511
|
-
//
|
|
512
|
-
// sql.js
|
|
513
|
-
//
|
|
514
|
-
//
|
|
515
|
-
|
|
516
|
-
// check gives a standing, read-only signal so the driver split is visible
|
|
517
|
-
// any time doctor runs, independent of any single store call.
|
|
518
|
-
//
|
|
519
|
-
// Table count in the on-disk memory.db is the cheap, reliable signal from
|
|
520
|
-
// the issue report: the native driver's schema produces 47 tables, the
|
|
521
|
-
// sql.js fallback's produces only 10. Deliberately does NOT change install
|
|
522
|
-
// behavior (that's option 2 from #2968, explicitly out of scope here) —
|
|
523
|
-
// this only reports, it never repairs.
|
|
524
|
-
const MEMORY_DRIVER_NATIVE_TABLE_FLOOR = 20; // roughly midpoint of sql.js's ~10 and native's ~47
|
|
525
|
-
async function checkMemoryPersistenceDriver() {
|
|
509
|
+
// #2968/#3321 — read-only native SQLite capability probe. A skipped
|
|
510
|
+
// postinstall can leave the wrapper importable but its binding unavailable.
|
|
511
|
+
// Schema size cannot identify the runtime driver or the database's history:
|
|
512
|
+
// memory init creates its schema with sql.js even when native is available.
|
|
513
|
+
// This probe does not verify schema compatibility or cross-process writes;
|
|
514
|
+
// integrity checks and memory store's persistWarning retain their own roles.
|
|
515
|
+
export async function checkMemoryPersistenceDriver() {
|
|
526
516
|
const NAME = 'Memory Persistence Driver';
|
|
527
517
|
const dbPath = await resolveMemoryDbPath();
|
|
528
518
|
if (!dbPath) {
|
|
@@ -549,6 +539,7 @@ async function checkMemoryPersistenceDriver() {
|
|
|
549
539
|
let tableCount = null;
|
|
550
540
|
let nativeUnavailableReason = null;
|
|
551
541
|
let nativeOpenOtherError = null;
|
|
542
|
+
let nativeQueryError = null;
|
|
552
543
|
if (Database) {
|
|
553
544
|
let db;
|
|
554
545
|
try {
|
|
@@ -566,10 +557,15 @@ async function checkMemoryPersistenceDriver() {
|
|
|
566
557
|
if (db) {
|
|
567
558
|
try {
|
|
568
559
|
const row = db.prepare("SELECT count(*) AS c FROM sqlite_master WHERE type='table'").get();
|
|
569
|
-
|
|
560
|
+
if (typeof row?.c === 'number' && Number.isSafeInteger(row.c) && row.c >= 0) {
|
|
561
|
+
tableCount = row.c;
|
|
562
|
+
}
|
|
563
|
+
else {
|
|
564
|
+
nativeQueryError = 'query returned no reliable table count';
|
|
565
|
+
}
|
|
570
566
|
}
|
|
571
|
-
catch {
|
|
572
|
-
|
|
567
|
+
catch (e) {
|
|
568
|
+
nativeQueryError = e.message || String(e);
|
|
573
569
|
}
|
|
574
570
|
finally {
|
|
575
571
|
try {
|
|
@@ -589,7 +585,10 @@ async function checkMemoryPersistenceDriver() {
|
|
|
589
585
|
if (sdb) {
|
|
590
586
|
try {
|
|
591
587
|
const res = sdb.exec("SELECT count(*) FROM sqlite_master WHERE type='table'");
|
|
592
|
-
|
|
588
|
+
const count = res[0]?.values?.[0]?.[0];
|
|
589
|
+
if (typeof count === 'number' && Number.isSafeInteger(count) && count >= 0) {
|
|
590
|
+
tableCount = count;
|
|
591
|
+
}
|
|
593
592
|
}
|
|
594
593
|
catch {
|
|
595
594
|
// leave null
|
|
@@ -604,12 +603,12 @@ async function checkMemoryPersistenceDriver() {
|
|
|
604
603
|
}
|
|
605
604
|
const tableSummary = tableCount === null
|
|
606
605
|
? 'table count unavailable'
|
|
607
|
-
: `${tableCount} tables
|
|
606
|
+
: `${tableCount} tables`;
|
|
608
607
|
if (nativeUnavailableReason) {
|
|
609
608
|
return {
|
|
610
609
|
name: NAME,
|
|
611
610
|
status: 'warn',
|
|
612
|
-
message: `${dbPath} —
|
|
611
|
+
message: `${dbPath} — native better-sqlite3 binding unavailable: ${nativeUnavailableReason} — native read-only probe unavailable; runtime driver and write persistence not verified (#2968) [${tableSummary}; sql.js main-image-only fallback]`,
|
|
613
612
|
fix: 'reinstall with npm install scripts enabled, or run `npm rebuild better-sqlite3`; then rerun this check',
|
|
614
613
|
};
|
|
615
614
|
}
|
|
@@ -620,18 +619,17 @@ async function checkMemoryPersistenceDriver() {
|
|
|
620
619
|
message: `${dbPath} — native better-sqlite3 module loadable, but could not open this database (${nativeOpenOtherError}) — see Memory Integrity above for the corruption/encryption diagnosis [${tableSummary}]`,
|
|
621
620
|
};
|
|
622
621
|
}
|
|
623
|
-
if (
|
|
622
|
+
if (nativeQueryError || tableCount === null) {
|
|
624
623
|
return {
|
|
625
624
|
name: NAME,
|
|
626
625
|
status: 'warn',
|
|
627
|
-
message: `${dbPath} —
|
|
628
|
-
fix: 'back up .swarm/memory.db then `claude-flow memory init --force` to rebuild under the native driver',
|
|
626
|
+
message: `${dbPath} — native better-sqlite3 opened read-only, but table count unavailable: ${nativeQueryError ?? 'query returned no reliable result'}; runtime driver and write persistence not verified`,
|
|
629
627
|
};
|
|
630
628
|
}
|
|
631
629
|
return {
|
|
632
630
|
name: NAME,
|
|
633
631
|
status: 'pass',
|
|
634
|
-
message: `${dbPath} —
|
|
632
|
+
message: `${dbPath} — native better-sqlite3 read-only open and schema query succeeded [${tableSummary}]; runtime driver, schema compatibility, and write persistence not verified`,
|
|
635
633
|
};
|
|
636
634
|
}
|
|
637
635
|
// Check 1 (--component memory, deep path) — #2737 part 2 strengthens this:
|
|
@@ -2011,20 +2009,45 @@ async function checkMetaharnessDeclaredPackages() {
|
|
|
2011
2009
|
}
|
|
2012
2010
|
async function checkMetaharness() {
|
|
2013
2011
|
try {
|
|
2014
|
-
|
|
2015
|
-
//
|
|
2016
|
-
|
|
2017
|
-
|
|
2012
|
+
// `metaharness` has no --version flag (it falls through to the usage
|
|
2013
|
+
// banner, which never matches a version regex) and shelling out via
|
|
2014
|
+
// `npx metaharness@latest` ignores the installed version and hits the
|
|
2015
|
+
// network every run. Resolve the version from the installed package's
|
|
2016
|
+
// own package.json instead. `import.meta.resolve` is the reliable route:
|
|
2017
|
+
// `require('metaharness/package.json')` is blocked by the package's
|
|
2018
|
+
// `exports` map, and `createRequire().resolve()` fails on its ESM-only
|
|
2019
|
+
// entry point.
|
|
2020
|
+
const resolved = import.meta.resolve('metaharness');
|
|
2021
|
+
let dir = dirname(fileURLToPath(resolved));
|
|
2022
|
+
let version = null;
|
|
2023
|
+
for (let i = 0; i < 8; i++) {
|
|
2024
|
+
const pj = join(dir, 'package.json');
|
|
2025
|
+
if (existsSync(pj)) {
|
|
2026
|
+
try {
|
|
2027
|
+
const j = JSON.parse(readFileSync(pj, 'utf-8'));
|
|
2028
|
+
if (j.name === 'metaharness') {
|
|
2029
|
+
version = j.version ?? null;
|
|
2030
|
+
break;
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
catch { /* keep walking */ }
|
|
2034
|
+
}
|
|
2035
|
+
const parent = dirname(dir);
|
|
2036
|
+
if (parent === dir)
|
|
2037
|
+
break;
|
|
2038
|
+
dir = parent;
|
|
2039
|
+
}
|
|
2040
|
+
if (!version) {
|
|
2018
2041
|
return {
|
|
2019
2042
|
name: 'MetaHarness (ADR-150)',
|
|
2020
2043
|
status: 'warn',
|
|
2021
|
-
message: 'Installed but
|
|
2044
|
+
message: 'Installed but its package.json was not found while walking up from the resolved module; integration may still work',
|
|
2022
2045
|
};
|
|
2023
2046
|
}
|
|
2024
2047
|
return {
|
|
2025
2048
|
name: 'MetaHarness (ADR-150)',
|
|
2026
2049
|
status: 'pass',
|
|
2027
|
-
message: `v${
|
|
2050
|
+
message: `v${version} — run \`npx ruflo metaharness score\` for the full scorecard`,
|
|
2028
2051
|
};
|
|
2029
2052
|
}
|
|
2030
2053
|
catch {
|
|
@@ -2348,7 +2371,7 @@ export const doctorCommand = {
|
|
|
2348
2371
|
checkDaemonStatus,
|
|
2349
2372
|
checkMemoryDatabase,
|
|
2350
2373
|
checkMemoryStructuralIntegrity, // #2737 — bounded, native quick_check on every default run
|
|
2351
|
-
checkMemoryPersistenceDriver, // #2968 —
|
|
2374
|
+
checkMemoryPersistenceDriver, // #2968/#3321 — read-only native capability probe
|
|
2352
2375
|
checkLearningBridge, // #2545 — can the auto-memory hook actually load @claude-flow/memory?
|
|
2353
2376
|
checkApiKeys,
|
|
2354
2377
|
checkMcpServers,
|
|
@@ -2386,7 +2409,7 @@ export const doctorCommand = {
|
|
|
2386
2409
|
'memory': [
|
|
2387
2410
|
checkMemoryDatabase, // existing: exists + statable (unchanged)
|
|
2388
2411
|
checkMemoryIntegrity, // #2677 check 1: sql.js open + PRAGMA integrity_check
|
|
2389
|
-
checkMemoryPersistenceDriver, // #2968:
|
|
2412
|
+
checkMemoryPersistenceDriver, // #2968/#3321: read-only native capability probe
|
|
2390
2413
|
checkMemoryContent, // #2677 check 2: memory_entries content coverage
|
|
2391
2414
|
checkMemoryEmbeddingCoverage, // #2677 check 3: vector coverage on populated rows
|
|
2392
2415
|
checkMemoryReflexionCoverage, // #2677 check 6: episodes are retrievable
|
|
@@ -13,6 +13,7 @@ import { mkdir, writeFile } from 'fs/promises';
|
|
|
13
13
|
import { existsSync } from 'fs';
|
|
14
14
|
import { join } from 'path';
|
|
15
15
|
import { resolveClaudeLaunchCommand } from '../runtime/claude-command.js';
|
|
16
|
+
import { getHiveTokenForCli } from '../mcp-tools/hive-mind-tools.js';
|
|
16
17
|
// Hive topologies
|
|
17
18
|
const TOPOLOGIES = [
|
|
18
19
|
{ value: 'hierarchical', label: 'Hierarchical', hint: 'Queen-led with worker agents' },
|
|
@@ -977,7 +978,7 @@ const joinCommand = {
|
|
|
977
978
|
return { success: false, exitCode: 1 };
|
|
978
979
|
}
|
|
979
980
|
try {
|
|
980
|
-
const result = await callMCPTool('hive-mind_join', { agentId, role: ctx.flags.role });
|
|
981
|
+
const result = await callMCPTool('hive-mind_join', { agentId, role: ctx.flags.role, hiveToken: getHiveTokenForCli() });
|
|
981
982
|
if (!result.success) {
|
|
982
983
|
output.printError(result.error || 'Failed');
|
|
983
984
|
return { success: false, exitCode: 1 };
|
|
@@ -1003,7 +1004,7 @@ const leaveCommand = {
|
|
|
1003
1004
|
return { success: false, exitCode: 1 };
|
|
1004
1005
|
}
|
|
1005
1006
|
try {
|
|
1006
|
-
const result = await callMCPTool('hive-mind_leave', { agentId });
|
|
1007
|
+
const result = await callMCPTool('hive-mind_leave', { agentId, hiveToken: getHiveTokenForCli() });
|
|
1007
1008
|
if (!result.success) {
|
|
1008
1009
|
output.printError(result.error || 'Failed');
|
|
1009
1010
|
return { success: false, exitCode: 1 };
|
|
@@ -1032,7 +1033,7 @@ const consensusCommand = {
|
|
|
1032
1033
|
action: async (ctx) => {
|
|
1033
1034
|
const action = ctx.flags.action || 'list';
|
|
1034
1035
|
try {
|
|
1035
|
-
const result = await callMCPTool('hive-mind_consensus', { action, proposalId: ctx.flags.proposalId, type: ctx.flags.type, value: ctx.flags.value, vote: ctx.flags.vote === 'yes', voterId: ctx.flags.voterId });
|
|
1036
|
+
const result = await callMCPTool('hive-mind_consensus', { action, proposalId: ctx.flags.proposalId, type: ctx.flags.type, value: ctx.flags.value, vote: ctx.flags.vote === 'yes', voterId: ctx.flags.voterId, hiveToken: getHiveTokenForCli() });
|
|
1036
1037
|
if (ctx.flags.format === 'json') {
|
|
1037
1038
|
output.printJson(result);
|
|
1038
1039
|
return { success: true, data: result };
|
|
@@ -8,6 +8,56 @@ import { callMCPTool, MCPClientError } from '../mcp-client.js';
|
|
|
8
8
|
import { swarmJoinCommand } from './agntcy/swarm-join.js';
|
|
9
9
|
import * as fs from 'fs';
|
|
10
10
|
import * as path from 'path';
|
|
11
|
+
// Read the CLI-side swarm state file (`.swarm/state.json`), written by
|
|
12
|
+
// `swarm init` and rewritten by `swarm start` / `swarm stop`.
|
|
13
|
+
function readLocalSwarmState() {
|
|
14
|
+
const swarmStateFile = path.join(process.cwd(), '.swarm', 'state.json');
|
|
15
|
+
if (!fs.existsSync(swarmStateFile))
|
|
16
|
+
return null;
|
|
17
|
+
try {
|
|
18
|
+
return JSON.parse(fs.readFileSync(swarmStateFile, 'utf-8'));
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
// Ignore parse errors
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
// Resolve the id of the current swarm. Three writers persist it, under two
|
|
26
|
+
// different keys and in two different files:
|
|
27
|
+
//
|
|
28
|
+
// `.swarm/state.json` `id` ← `swarm init`
|
|
29
|
+
// `.swarm/state.json` `swarmId` ← `swarm start`
|
|
30
|
+
// `.claude-flow/swarm/swarm-state.json` `swarms[id].swarmId` ← MCP `swarm_init`
|
|
31
|
+
//
|
|
32
|
+
// The status payload used to read only the first key of the first file, so a
|
|
33
|
+
// Claude Code session (which drives the MCP path) reported an active swarm
|
|
34
|
+
// with agents but no usable id. Check every writer, most local first.
|
|
35
|
+
function resolveSwarmId(swarmState) {
|
|
36
|
+
// A state file left behind by a previous `swarm stop` must not shadow a
|
|
37
|
+
// swarm that is still live in the MCP store — otherwise `stop` names the
|
|
38
|
+
// dead id while `swarm_shutdown`'s own "most recent running" fallback
|
|
39
|
+
// terminates a different swarm.
|
|
40
|
+
if (swarmState?.status !== 'stopped') {
|
|
41
|
+
const fromState = swarmState?.id ?? swarmState?.swarmId;
|
|
42
|
+
if (typeof fromState === 'string' && fromState)
|
|
43
|
+
return fromState;
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
const storePath = path.join(process.cwd(), '.claude-flow', 'swarm', 'swarm-state.json');
|
|
47
|
+
if (!fs.existsSync(storePath))
|
|
48
|
+
return null;
|
|
49
|
+
const store = JSON.parse(fs.readFileSync(storePath, 'utf-8'));
|
|
50
|
+
// Most recently updated swarm that has not been shut down.
|
|
51
|
+
const live = Object.values(store.swarms ?? {})
|
|
52
|
+
.filter(swarm => swarm.status !== 'terminated')
|
|
53
|
+
.sort((a, b) => new Date(b.updatedAt ?? 0).getTime() - new Date(a.updatedAt ?? 0).getTime());
|
|
54
|
+
return live[0]?.swarmId ?? null;
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
// Ignore — an unreadable MCP store just means no id to resolve.
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
11
61
|
// Get dynamic swarm status from memory/session files
|
|
12
62
|
function getSwarmStatus(swarmId) {
|
|
13
63
|
const swarmDir = path.join(process.cwd(), '.swarm');
|
|
@@ -17,16 +67,7 @@ function getSwarmStatus(swarmId) {
|
|
|
17
67
|
path.join(process.cwd(), '.claude', 'memory.db'),
|
|
18
68
|
];
|
|
19
69
|
// Check for active swarm state file
|
|
20
|
-
const
|
|
21
|
-
let swarmState = null;
|
|
22
|
-
if (fs.existsSync(swarmStateFile)) {
|
|
23
|
-
try {
|
|
24
|
-
swarmState = JSON.parse(fs.readFileSync(swarmStateFile, 'utf-8'));
|
|
25
|
-
}
|
|
26
|
-
catch {
|
|
27
|
-
// Ignore parse errors
|
|
28
|
-
}
|
|
29
|
-
}
|
|
70
|
+
const swarmState = readLocalSwarmState();
|
|
30
71
|
// Count active agents from process files
|
|
31
72
|
let activeAgents = 0;
|
|
32
73
|
let totalAgents = 0;
|
|
@@ -175,10 +216,22 @@ function getSwarmStatus(swarmId) {
|
|
|
175
216
|
status = 'completed';
|
|
176
217
|
}
|
|
177
218
|
else if (swarmState) {
|
|
178
|
-
|
|
219
|
+
// The file's own lifecycle state wins over the "a file exists, so we are
|
|
220
|
+
// ready" default — a swarm explicitly recorded as stopped is not ready.
|
|
221
|
+
// Live agents still win above: they are observed reality, whereas the file
|
|
222
|
+
// records an intent that may be stale.
|
|
223
|
+
status = swarmState.status === 'stopped' ? 'stopped' : 'ready';
|
|
179
224
|
}
|
|
225
|
+
// Resolve once — the id also settles whether there is a swarm to report.
|
|
226
|
+
// An MCP `swarm_init` with no agents spawned yet leaves no `.swarm/state.json`
|
|
227
|
+
// and no agent store, so without this the payload contradicted itself:
|
|
228
|
+
// a real `id` alongside `hasActiveSwarm: false`.
|
|
229
|
+
const resolvedId = swarmId || resolveSwarmId(swarmState);
|
|
180
230
|
return {
|
|
181
|
-
|
|
231
|
+
// `null` when genuinely unknown — never a sentinel string. `status` and
|
|
232
|
+
// `hasActiveSwarm` already carry the "no active swarm" fact, and a
|
|
233
|
+
// consumer reading `.id` must get an id or nothing.
|
|
234
|
+
id: resolvedId,
|
|
182
235
|
topology: swarmState?.topology || 'none',
|
|
183
236
|
status,
|
|
184
237
|
objective: swarmState?.objective || 'No active objective',
|
|
@@ -290,7 +343,13 @@ function getSwarmStatus(swarmId) {
|
|
|
290
343
|
}
|
|
291
344
|
return { consensusRounds, messagesSent, conflictsResolved };
|
|
292
345
|
})(),
|
|
293
|
-
|
|
346
|
+
// A state file left behind by `swarm stop` is not an active swarm — without
|
|
347
|
+
// this it claimed one it could not name (`id: null`, zero agents), which is
|
|
348
|
+
// the same contradiction from the other side. Live agents or a resolvable
|
|
349
|
+
// id still count, so a stopped file never masks real activity.
|
|
350
|
+
hasActiveSwarm: (!!swarmState && swarmState.status !== 'stopped')
|
|
351
|
+
|| totalAgents > 0
|
|
352
|
+
|| resolvedId !== null
|
|
294
353
|
};
|
|
295
354
|
}
|
|
296
355
|
// Swarm topologies
|
|
@@ -630,6 +689,9 @@ const startCommand = {
|
|
|
630
689
|
if (!fs.existsSync(swarmDir))
|
|
631
690
|
fs.mkdirSync(swarmDir, { recursive: true });
|
|
632
691
|
const executionState = {
|
|
692
|
+
// `id` mirrors what `swarm init` writes so both files share one key;
|
|
693
|
+
// `swarmId` stays for existing files and readers.
|
|
694
|
+
id: swarmId,
|
|
633
695
|
swarmId,
|
|
634
696
|
objective,
|
|
635
697
|
strategy,
|
|
@@ -673,7 +735,7 @@ const statusCommand = {
|
|
|
673
735
|
output.writeln();
|
|
674
736
|
return { success: true, data: status };
|
|
675
737
|
}
|
|
676
|
-
output.writeln(output.bold(`Swarm Status: ${status.id}`));
|
|
738
|
+
output.writeln(output.bold(`Swarm Status: ${status.id ?? output.dim('unknown id')}`));
|
|
677
739
|
output.writeln();
|
|
678
740
|
// Progress bar
|
|
679
741
|
output.writeln(`Overall Progress: ${output.progressBar(status.progress, 100, 40)}`);
|
|
@@ -747,10 +809,16 @@ const stopCommand = {
|
|
|
747
809
|
}
|
|
748
810
|
],
|
|
749
811
|
action: async (ctx) => {
|
|
750
|
-
|
|
812
|
+
// Bare `swarm stop` used to hard-fail, and no CLI surface handed out an
|
|
813
|
+
// id to pass (there is no `swarm list`). Default to the persisted swarm;
|
|
814
|
+
// an explicit argument still wins.
|
|
815
|
+
const swarmId = ctx.args[0] || resolveSwarmId(readLocalSwarmState());
|
|
751
816
|
const force = ctx.flags.force;
|
|
752
817
|
if (!swarmId) {
|
|
753
|
-
output.printError('
|
|
818
|
+
output.printError('No swarm found to stop');
|
|
819
|
+
output.writeln(output.dim(' Find an id with: claude-flow swarm status --format json (the "id" field)'));
|
|
820
|
+
output.writeln(output.dim(' Or pass one: claude-flow swarm stop <swarm-id>'));
|
|
821
|
+
output.writeln(output.dim(' Or start one: claude-flow swarm init'));
|
|
754
822
|
return { success: false, exitCode: 1 };
|
|
755
823
|
}
|
|
756
824
|
if (ctx.interactive && !force) {
|
|
@@ -810,11 +878,14 @@ const scaleCommand = {
|
|
|
810
878
|
}
|
|
811
879
|
],
|
|
812
880
|
action: async (ctx) => {
|
|
813
|
-
|
|
881
|
+
// Same resolution as `stop` — the id is persisted, so don't demand it.
|
|
882
|
+
const swarmId = ctx.args[0] || resolveSwarmId(readLocalSwarmState());
|
|
814
883
|
const targetAgents = ctx.flags.agents;
|
|
815
884
|
const agentType = ctx.flags.type;
|
|
816
885
|
if (!swarmId) {
|
|
817
|
-
output.printError('
|
|
886
|
+
output.printError('No swarm found to scale');
|
|
887
|
+
output.writeln(output.dim(' Find an id with: claude-flow swarm status --format json (the "id" field)'));
|
|
888
|
+
output.writeln(output.dim(' Or pass one: claude-flow swarm scale <swarm-id> --agents N'));
|
|
818
889
|
return { success: false, exitCode: 1 };
|
|
819
890
|
}
|
|
820
891
|
if (!targetAgents) {
|
|
@@ -258,7 +258,7 @@ function performanceSection() {
|
|
|
258
258
|
|
|
259
259
|
- Always benchmark before AND after optimization
|
|
260
260
|
- Always profile before optimizing — never guess bottlenecks
|
|
261
|
-
- Use HNSW
|
|
261
|
+
- Use HNSW for vector search, Int8 quantization for memory reduction — measure the actual speedup/recall on your own corpus via \`node scripts/benchmark-intelligence.mjs --only=hnsw\` rather than trusting a hardcoded multiplier (recall@10 varies by corpus size — 0.99 at N=5k, 0.92 at N=20k on the reference host, dream-cycle 2026-08-30)
|
|
262
262
|
|
|
263
263
|
\`\`\`bash
|
|
264
264
|
npx @claude-flow/cli@latest performance benchmark --suite all
|
|
@@ -293,7 +293,7 @@ function intelligenceSystem() {
|
|
|
293
293
|
Pipeline: **RETRIEVE** (vector search) → **JUDGE** (success/failure) → **DISTILL** (extract patterns) → **CONSOLIDATE** (persist)
|
|
294
294
|
|
|
295
295
|
- **ONNX Embeddings**: all-MiniLM-L6-v2, 384-dim
|
|
296
|
-
- **HNSW
|
|
296
|
+
- **HNSW**: faster than brute-force search, magnitude scales with corpus size — reproduce with \`node scripts/benchmark-intelligence.mjs --only=hnsw\` rather than a hardcoded multiplier
|
|
297
297
|
- **SONA**: Sub-millisecond pattern adaptation
|
|
298
298
|
- **Claude Bridge**: Auto-imports \`~/.claude/projects/*/memory/*.md\` into AgentDB`;
|
|
299
299
|
}
|
package/dist/src/mcp-server.js
CHANGED
|
@@ -26,6 +26,7 @@ import * as os from 'os';
|
|
|
26
26
|
import { fileURLToPath } from 'url';
|
|
27
27
|
import { dirname } from 'path';
|
|
28
28
|
import { trackRequest } from './mcp-tools/request-tracker.js';
|
|
29
|
+
import { isPolicyEnforcementEnabled, loadMcpPolicy, evaluateToolCall, } from './mcp-tools/policy-enforcer.js';
|
|
29
30
|
// ESM-compatible __dirname
|
|
30
31
|
const __filename = fileURLToPath(import.meta.url);
|
|
31
32
|
const __dirname = dirname(__filename);
|
|
@@ -503,6 +504,17 @@ export class MCPServerManager extends EventEmitter {
|
|
|
503
504
|
error: { code: -32601, message: `Tool not found: ${toolName}` },
|
|
504
505
|
};
|
|
505
506
|
}
|
|
507
|
+
if (isPolicyEnforcementEnabled()) {
|
|
508
|
+
const check = evaluateToolCall(loadMcpPolicy(), sessionId, toolName);
|
|
509
|
+
if (!check.allowed) {
|
|
510
|
+
trackRequest(toolName, false);
|
|
511
|
+
return {
|
|
512
|
+
jsonrpc: '2.0',
|
|
513
|
+
id: message.id,
|
|
514
|
+
error: { code: -32001, message: `Policy denied: ${check.reason}` },
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
}
|
|
506
518
|
try {
|
|
507
519
|
const result = await callMCPTool(toolName, toolParams, { sessionId });
|
|
508
520
|
trackRequest(toolName, true);
|
|
@@ -35,5 +35,20 @@
|
|
|
35
35
|
* @module @claude-flow/cli/mcp-tools/agentbbs
|
|
36
36
|
*/
|
|
37
37
|
import type { MCPTool } from './types.js';
|
|
38
|
+
/** Last envelope's seq by scanning only the file tail; full-read fallback keeps it exact. */
|
|
39
|
+
export declare function nextSeq(path: string): number;
|
|
40
|
+
/** Timestamp (ms) of the most recent PeerHello for a room, or -Infinity if none. */
|
|
41
|
+
export declare function lastPeerHelloMs(logPath: string): number;
|
|
42
|
+
/**
|
|
43
|
+
* Whether a `register` call should append a PeerHello: only for a genuinely new
|
|
44
|
+
* registration, or once the heartbeat window has elapsed since the last one.
|
|
45
|
+
* A re-register within the window is a retry and must NOT spam a near-identical
|
|
46
|
+
* hello. Pure — the decision the handler makes, exported for test.
|
|
47
|
+
*/
|
|
48
|
+
export declare function shouldEmitHello(alreadyRegistered: boolean, nowMs: number, lastHelloMs: number, windowSecs: number): boolean;
|
|
49
|
+
/** Collapse duplicate envelopeIds, preserving order (first occurrence wins). Pure. */
|
|
50
|
+
export declare function dedupEnvelopes<T extends {
|
|
51
|
+
envelopeId: string;
|
|
52
|
+
}>(list: T[]): T[];
|
|
38
53
|
export declare const agentbbsTools: MCPTool[];
|
|
39
54
|
//# sourceMappingURL=agentbbs-tools.d.ts.map
|