claude-flow 3.59.3 → 3.59.5

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.
@@ -1,5 +1,5 @@
1
1
  {
2
- "adoptedAt": 1792547603690,
2
+ "adoptedAt": 1792556117950,
3
3
  "championId": "sha256:6141a8ea990c5063b77e090ae8f37f9c539d8aa8f58dcceb30f3a82f97e57319",
4
4
  "manifest": {
5
5
  "schema": "ruflo.proven-config/v1",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-flow",
3
- "version": "3.59.3",
3
+ "version": "3.59.5",
4
4
  "workspaces": [
5
5
  "v3/@claude-flow/codex",
6
6
  "v3/@claude-flow/mcp",
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "generation": 7,
4
- "generatedAt": "2026-10-21T01:53:55.354Z",
5
- "gitSha": "eb808c24",
4
+ "generatedAt": "2026-10-21T04:15:49.428Z",
5
+ "gitSha": "5f5f5941",
6
6
  "catalog": {
7
7
  "agents": 176,
8
8
  "tools": 432,
@@ -27,7 +27,7 @@
27
27
  import * as fs from 'fs';
28
28
  import * as path from 'path';
29
29
  import { output } from '../output.js';
30
- import { runDistillation, defaultMemoryDbPath, } from '../services/memory-distillation.js';
30
+ import { runDistillationForStores, projectMemoryDbPaths, defaultMemoryDbPath, } from '../services/memory-distillation.js';
31
31
  const DEFAULT_CONFIG = {
32
32
  dryRun: false,
33
33
  batchSize: 200,
@@ -139,7 +139,15 @@ const runCommand = {
139
139
  'Refusing to run the LLM judge against a $0 budget.');
140
140
  return { success: false, exitCode: 1 };
141
141
  }
142
- const dbPath = resolveDbPath(ctx);
142
+ // An explicit --db remains a single-store operation. Without it, cover
143
+ // both stores the CLI/MCP surfaces can write (#3446): memory.db and the
144
+ // native AgentDB sibling. An MCP-only project often has only the latter,
145
+ // which is why the worker used to skip with "target table ... missing".
146
+ const explicitDb = typeof ctx.flags.db === 'string' && ctx.flags.db.trim()
147
+ ? path.resolve(ctx.flags.db)
148
+ : undefined;
149
+ const dbPaths = explicitDb ? [explicitDb] : projectMemoryDbPaths(ctx.cwd || process.cwd());
150
+ const dbPath = explicitDb ?? dbPaths[0];
143
151
  output.writeln();
144
152
  output.writeln(output.bold('Memory Distillation (ADR-174)'));
145
153
  output.writeln(output.dim('─'.repeat(55)));
@@ -148,7 +156,6 @@ const runCommand = {
148
156
  const spinner = output.createSpinner({ text: 'Distilling memory_entries...', spinner: 'dots' });
149
157
  spinner.start();
150
158
  const distillOptions = {
151
- dbPath,
152
159
  namespaces: config.namespaces,
153
160
  batchSize: config.batchSize,
154
161
  maxEntries: config.maxEntries,
@@ -158,22 +165,31 @@ const runCommand = {
158
165
  sinceRowid: config.sinceRowid,
159
166
  verbose: ctx.flags.verbose === true,
160
167
  };
161
- let report;
168
+ let outcome;
162
169
  try {
163
- report = await runDistillation(distillOptions);
170
+ outcome = await runDistillationForStores(dbPaths, distillOptions);
164
171
  }
165
172
  catch (error) {
166
173
  spinner.fail('Distillation failed');
167
174
  output.printError(error instanceof Error ? error.message : String(error));
168
175
  return { success: false, exitCode: 1 };
169
176
  }
177
+ const report = outcome.report;
170
178
  if (report.skipped) {
171
179
  spinner.succeed('Distillation skipped');
172
180
  output.writeln(output.warning(`skipped: ${report.skipped}`));
173
- return { success: true, data: { dbPath, config, report } };
181
+ return { success: true, data: { dbPath, dbPaths, stores: outcome.stores, config, report } };
174
182
  }
175
183
  spinner.succeed(config.dryRun ? 'Dry-run complete' : 'Distillation complete');
176
184
  output.writeln();
185
+ for (const store of outcome.stores) {
186
+ if (store.report.skipped) {
187
+ output.writeln(output.warning(`${store.dbPath}: ${store.report.skipped}`));
188
+ }
189
+ }
190
+ if (outcome.stores.length > 1) {
191
+ output.writeln(output.dim(`Stores covered: ${outcome.stores.length}`));
192
+ }
177
193
  output.printTable({
178
194
  columns: [
179
195
  { key: 'metric', header: 'Metric', width: 24 },
@@ -198,7 +214,7 @@ const runCommand = {
198
214
  output.writeln(output.bold('By Provenance'));
199
215
  output.printList(provenanceEntries.map(([tier, count]) => `${output.highlight(tier)}: ${count}`));
200
216
  }
201
- return { success: true, data: { dbPath, config, report } };
217
+ return { success: true, data: { dbPath, dbPaths, stores: outcome.stores, config, report } };
202
218
  },
203
219
  };
204
220
  // ============================================================================
@@ -361,6 +361,24 @@ export const hiveMindTools = [
361
361
  description: 'Consensus strategy. Default: raft (anti-drift). Use byzantine for f<n/3 fault tolerance.',
362
362
  },
363
363
  queenId: { type: 'string', description: 'Initial queen agent ID' },
364
+ maxAgents: {
365
+ type: 'number',
366
+ minimum: 1,
367
+ maximum: 1000,
368
+ default: 15,
369
+ description: 'Maximum number of agents the hive may contain (default: 15)',
370
+ },
371
+ memoryBackend: {
372
+ type: 'string',
373
+ enum: ['agentdb', 'sqlite', 'hybrid'],
374
+ default: 'hybrid',
375
+ description: 'Memory backend requested for this hive (default: hybrid)',
376
+ },
377
+ persist: {
378
+ type: 'boolean',
379
+ default: true,
380
+ description: 'Persist hive state to .claude-flow/hive-mind/state.json. Set false for an ephemeral init that writes no durable state.',
381
+ },
364
382
  bootstrapSecret: { type: 'string', description: 'Operator secret (RUFLO_HIVE_BOOTSTRAP_SECRET or .claude-flow/hive-mind/bootstrap.secret). Only needed by remote/HTTP callers; local stdio/CLI callers omit it. See ADR-476.' },
365
383
  },
366
384
  },
@@ -372,16 +390,32 @@ export const hiveMindTools = [
372
390
  if (bootstrapError) {
373
391
  return { success: false, error: bootstrapError };
374
392
  }
375
- // Local first init materialises the 0600 operator secret so a later
376
- // remote/HTTP deployment has something to be told out of band. The
377
- // value is never returned.
378
- if (classifyCaller(context) === 'local')
379
- ensureBootstrapSecret();
393
+ // Validate the advertised init contract before creating any durable
394
+ // state (the operator secret is a filesystem write, so it must come
395
+ // after every input check).
396
+ if (input.persist !== undefined && typeof input.persist !== 'boolean') {
397
+ return { success: false, error: 'persist must be a boolean' };
398
+ }
399
+ const persist = input.persist !== false;
400
+ const maxAgents = input.maxAgents === undefined ? 15 : Number(input.maxAgents);
401
+ if (!Number.isInteger(maxAgents) || maxAgents < 1 || maxAgents > 1000) {
402
+ return { success: false, error: 'maxAgents must be an integer between 1 and 1000' };
403
+ }
404
+ const memoryBackend = input.memoryBackend === undefined ? 'hybrid' : String(input.memoryBackend);
405
+ if (memoryBackend !== 'agentdb' && memoryBackend !== 'sqlite' && memoryBackend !== 'hybrid') {
406
+ return { success: false, error: 'memoryBackend must be one of: agentdb, sqlite, hybrid' };
407
+ }
380
408
  if (input.queenId) {
381
409
  const v = validateIdentifier(input.queenId, 'queenId');
382
410
  if (!v.valid)
383
411
  return { success: false, error: v.error };
384
412
  }
413
+ // Local first init materialises the 0600 operator secret so a later
414
+ // remote/HTTP deployment has something to be told out of band. The
415
+ // value is never returned. An ephemeral init (persist:false) must not
416
+ // write it either.
417
+ if (persist && classifyCaller(context) === 'local')
418
+ ensureBootstrapSecret();
385
419
  const state = loadHiveState();
386
420
  const hiveId = `hive-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
387
421
  const queenId = input.queenId || `queen-${Date.now()}`;
@@ -389,6 +423,9 @@ export const hiveMindTools = [
389
423
  state.initialized = true;
390
424
  state.topology = input.topology || 'mesh';
391
425
  state.consensusStrategy = requestedConsensus;
426
+ state.maxAgents = maxAgents;
427
+ state.memoryBackend = memoryBackend;
428
+ state.persist = persist;
392
429
  state.createdAt = new Date().toISOString();
393
430
  state.queen = {
394
431
  agentId: queenId,
@@ -403,7 +440,8 @@ export const hiveMindTools = [
403
440
  if (!state.hiveToken) {
404
441
  state.hiveToken = randomBytes(32).toString('hex');
405
442
  }
406
- saveHiveState(state);
443
+ if (persist)
444
+ saveHiveState(state);
407
445
  return {
408
446
  success: true,
409
447
  hiveId,
@@ -411,6 +449,7 @@ export const hiveMindTools = [
411
449
  consensus: state.consensusStrategy,
412
450
  queenId,
413
451
  status: 'initialized',
452
+ persisted: persist,
414
453
  // Deliberately NOT echoing state.hiveToken here (even though this
415
454
  // call is itself now bootstrap-secret-gated): the CLI never reads
416
455
  // it off this response (it uses getHiveTokenForCli()'s direct
@@ -419,9 +458,9 @@ export const hiveMindTools = [
419
458
  config: {
420
459
  topology: state.topology,
421
460
  consensus: state.consensusStrategy,
422
- maxAgents: input.maxAgents || 15,
423
- persist: input.persist !== false,
424
- memoryBackend: input.memoryBackend || 'hybrid',
461
+ maxAgents,
462
+ persist,
463
+ memoryBackend,
425
464
  },
426
465
  createdAt: state.createdAt,
427
466
  };
@@ -468,8 +507,10 @@ export const hiveMindTools = [
468
507
  hiveId: `hive-${state.createdAt ? new Date(state.createdAt).getTime() : Date.now()}`,
469
508
  status: state.initialized ? 'active' : 'offline',
470
509
  topology: state.topology,
471
- // ADR-093 F3: surface the persisted strategy instead of a hardcoded "byzantine".
472
- consensus: state.consensusStrategy ?? 'byzantine',
510
+ // ADR-093 F3: surface the persisted strategy instead of a hardcoded
511
+ // "byzantine". Init's documented default is raft, so legacy state
512
+ // without the field must not drift to a different protocol.
513
+ consensus: state.consensusStrategy ?? 'raft',
473
514
  queen: state.queen ? {
474
515
  id: state.queen.agentId,
475
516
  agentId: state.queen.agentId,
@@ -508,6 +549,8 @@ export const hiveMindTools = [
508
549
  // Additional fields
509
550
  id: `hive-${state.createdAt ? new Date(state.createdAt).getTime() : Date.now()}`,
510
551
  initialized: state.initialized,
552
+ maxAgents: state.maxAgents ?? 15,
553
+ memoryBackend: state.memoryBackend ?? 'hybrid',
511
554
  workerCount: state.workers.length,
512
555
  pendingConsensus: state.consensus.pending.length,
513
556
  sharedMemoryKeys: Object.keys(state.sharedMemory).length,
@@ -37,6 +37,42 @@ export interface DistillReport {
37
37
  * Incremental, $0, transactional, provenance-tagged.
38
38
  */
39
39
  export declare function runDistillation(options: DistillOptions): Promise<DistillReport>;
40
- /** Where the memory DB lives, for daemon/CLI callers. */
40
+ /**
41
+ * The two stores the CLI/MCP surface can write:
42
+ * - memory.db is the sql.js/CLI store (also used with encryption at rest);
43
+ * - agentdb-memory.db is the native AgentDB store the MCP bridge writes to.
44
+ *
45
+ * #3446: distillation used to default to memory.db only. In an MCP-only
46
+ * project that file often has no AgentDB target tables, so the daemon's
47
+ * consolidate worker skipped every tick while the rows accumulated in the
48
+ * sibling store. Keep both paths here (same root resolution as backup) so
49
+ * callers can cover every store without guessing which surface wrote.
50
+ */
51
+ export declare function projectMemoryDbPaths(cwd?: string): string[];
52
+ /** Where the primary memory DB lives, for callers that still need one path. */
41
53
  export declare function defaultMemoryDbPath(cwd?: string): string;
54
+ export interface StoreDistillResult {
55
+ dbPath: string;
56
+ report: DistillReport;
57
+ }
58
+ export interface MultiStoreDistillResult {
59
+ stores: StoreDistillResult[];
60
+ report: DistillReport;
61
+ }
62
+ /**
63
+ * Merge per-store reports without hiding a store's outcome. A run over both
64
+ * stores is only `skipped` when every store skipped; if one store distilled
65
+ * and the other had no schema/DB, the successful counts remain authoritative
66
+ * and the per-store skip stays available in `stores`.
67
+ */
68
+ export declare function aggregateDistillReports(stores: StoreDistillResult[], dryRun?: boolean): DistillReport;
69
+ /**
70
+ * Run one distillation pass against each provided store.
71
+ *
72
+ * `runner` is injectable so callers/tests can exercise the multi-store
73
+ * orchestration without a native SQLite build; the default is the real
74
+ * service. A runner failure is converted to a per-store skip so one bad store
75
+ * cannot prevent the other from being distilled.
76
+ */
77
+ export declare function runDistillationForStores(dbPaths: string[], options?: Omit<DistillOptions, 'dbPath'>, runner?: (options: DistillOptions) => Promise<DistillReport>): Promise<MultiStoreDistillResult>;
42
78
  //# sourceMappingURL=memory-distillation.d.ts.map
@@ -28,6 +28,7 @@ import * as fs from 'fs';
28
28
  import * as path from 'path';
29
29
  import { distillTrajectoryContent, serialiseDistilled } from '../memory/structured-distill.js';
30
30
  import { loadBetterSqlite3 } from '../memory/shared-sqlite.js';
31
+ import { resolveMemoryRoot } from '../memory/memory-root.js';
31
32
  function emptyReport(dryRun, extra = {}) {
32
33
  return {
33
34
  processed: 0, episodes: 0, episodeEmbeddings: 0,
@@ -346,8 +347,87 @@ export async function runDistillation(options) {
346
347
  }
347
348
  return report;
348
349
  }
349
- /** Where the memory DB lives, for daemon/CLI callers. */
350
+ /**
351
+ * The two stores the CLI/MCP surface can write:
352
+ * - memory.db is the sql.js/CLI store (also used with encryption at rest);
353
+ * - agentdb-memory.db is the native AgentDB store the MCP bridge writes to.
354
+ *
355
+ * #3446: distillation used to default to memory.db only. In an MCP-only
356
+ * project that file often has no AgentDB target tables, so the daemon's
357
+ * consolidate worker skipped every tick while the rows accumulated in the
358
+ * sibling store. Keep both paths here (same root resolution as backup) so
359
+ * callers can cover every store without guessing which surface wrote.
360
+ */
361
+ export function projectMemoryDbPaths(cwd = process.cwd()) {
362
+ // An explicit project DB pin stays a single-store operation, matching
363
+ // memory-backup's precedence.
364
+ const override = process.env.CLAUDE_FLOW_DB_PATH;
365
+ if (override && override.trim().length > 0)
366
+ return [path.resolve(cwd, override)];
367
+ const root = resolveMemoryRoot(cwd);
368
+ return [path.join(root, 'memory.db'), path.join(root, 'agentdb-memory.db')];
369
+ }
370
+ /** Where the primary memory DB lives, for callers that still need one path. */
350
371
  export function defaultMemoryDbPath(cwd = process.cwd()) {
351
- return path.join(cwd, '.swarm', 'memory.db');
372
+ return projectMemoryDbPaths(cwd)[0];
373
+ }
374
+ /**
375
+ * Merge per-store reports without hiding a store's outcome. A run over both
376
+ * stores is only `skipped` when every store skipped; if one store distilled
377
+ * and the other had no schema/DB, the successful counts remain authoritative
378
+ * and the per-store skip stays available in `stores`.
379
+ */
380
+ export function aggregateDistillReports(stores, dryRun = false) {
381
+ const report = emptyReport(dryRun);
382
+ const skipped = [];
383
+ for (const { report: part } of stores) {
384
+ report.processed += part.processed;
385
+ report.episodes += part.episodes;
386
+ report.episodeEmbeddings += part.episodeEmbeddings;
387
+ report.patterns += part.patterns;
388
+ report.patternEmbeddings += part.patternEmbeddings;
389
+ report.causalEdges += part.causalEdges;
390
+ report.promoted += part.promoted;
391
+ report.spendUsd += part.spendUsd;
392
+ report.namespaces = [...new Set([...report.namespaces, ...part.namespaces])];
393
+ for (const [tier, count] of Object.entries(part.byProvenance)) {
394
+ report.byProvenance[tier] = (report.byProvenance[tier] ?? 0) + count;
395
+ }
396
+ if (part.corrupt)
397
+ report.corrupt = true;
398
+ if (part.skipped)
399
+ skipped.push(part.skipped);
400
+ }
401
+ if (stores.length > 0 && stores.every(store => store.report.skipped)) {
402
+ report.skipped = [...new Set(skipped)].join('; ');
403
+ }
404
+ return report;
405
+ }
406
+ /**
407
+ * Run one distillation pass against each provided store.
408
+ *
409
+ * `runner` is injectable so callers/tests can exercise the multi-store
410
+ * orchestration without a native SQLite build; the default is the real
411
+ * service. A runner failure is converted to a per-store skip so one bad store
412
+ * cannot prevent the other from being distilled.
413
+ */
414
+ export async function runDistillationForStores(dbPaths, options = {}, runner = runDistillation) {
415
+ const stores = [];
416
+ for (const dbPath of [...new Set(dbPaths.map(p => path.resolve(p)))]) {
417
+ let report;
418
+ try {
419
+ report = await runner({ ...options, dbPath });
420
+ }
421
+ catch (error) {
422
+ report = emptyReport(options.dryRun === true, {
423
+ skipped: `error: ${error instanceof Error ? error.message : String(error)}`,
424
+ });
425
+ }
426
+ stores.push({ dbPath, report });
427
+ }
428
+ if (stores.length === 0) {
429
+ return { stores, report: emptyReport(options.dryRun === true, { skipped: 'no-db' }) };
430
+ }
431
+ return { stores, report: aggregateDistillReports(stores, options.dryRun === true) };
352
432
  }
353
433
  //# sourceMappingURL=memory-distillation.js.map
@@ -23,7 +23,7 @@ import { HeadlessWorkerExecutor, isHeadlessWorker, } from './headless-worker-exe
23
23
  // service itself is owned/frozen elsewhere (memory-distillation.ts) — it is
24
24
  // incremental (rowid cursor), non-destructive, transactional, and
25
25
  // quick_check-gated, so it's safe to call unconditionally on every tick.
26
- import { runDistillation, defaultMemoryDbPath } from './memory-distillation.js';
26
+ import { runDistillationForStores, projectMemoryDbPaths, } from './memory-distillation.js';
27
27
  import { backupProjectMemoryDbs } from './memory-backup.js';
28
28
  import { resolveGitWorkspaceIdentity } from './git-workspace-identity.js';
29
29
  import { getWorkspaceLeaseRegistry } from './workspace-lease.js';
@@ -1614,13 +1614,15 @@ export class WorkerDaemon extends EventEmitter {
1614
1614
  return disabledResult;
1615
1615
  }
1616
1616
  let report;
1617
+ let stores = [];
1617
1618
  try {
1618
- report = await runDistillation({
1619
- dbPath: defaultMemoryDbPath(this.projectRoot),
1619
+ const outcome = await runDistillationForStores(projectMemoryDbPaths(this.projectRoot), {
1620
1620
  maxEntries: CONSOLIDATE_MAX_ENTRIES_PER_TICK,
1621
1621
  batchSize: CONSOLIDATE_BATCH_SIZE,
1622
1622
  dedupDistance: CONSOLIDATE_DEDUP_DISTANCE,
1623
1623
  });
1624
+ report = outcome.report;
1625
+ stores = outcome.stores;
1624
1626
  }
1625
1627
  catch (error) {
1626
1628
  // Defensive only — runDistillation() is internally try/catch'd and
@@ -1638,6 +1640,11 @@ export class WorkerDaemon extends EventEmitter {
1638
1640
  writeFileSync(consolidateFile, JSON.stringify(errorResult, null, 2));
1639
1641
  return errorResult;
1640
1642
  }
1643
+ for (const store of stores) {
1644
+ if (store.report.skipped) {
1645
+ this.log('info', `Consolidate worker: ${store.dbPath} skipped (${store.report.skipped})`);
1646
+ }
1647
+ }
1641
1648
  if (report.corrupt) {
1642
1649
  this.log('warn', `Consolidate worker: memory DB reports corruption — ${report.skipped ?? 'skipped'}`);
1643
1650
  }
@@ -1667,6 +1674,12 @@ export class WorkerDaemon extends EventEmitter {
1667
1674
  dryRun: report.dryRun,
1668
1675
  corrupt: report.corrupt ?? false,
1669
1676
  skipped: report.skipped,
1677
+ stores: stores.map(({ dbPath, report: part }) => ({
1678
+ dbPath,
1679
+ processed: part.processed,
1680
+ patterns: part.patterns,
1681
+ skipped: part.skipped,
1682
+ })),
1670
1683
  };
1671
1684
  writeFileSync(consolidateFile, JSON.stringify(result, null, 2));
1672
1685
  return result;
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@claude-flow/cli",
3
- "version": "3.59.3",
3
+ "version": "3.59.5",
4
4
  "type": "module",
5
5
  "description": "Ruflo CLI - Enterprise AI agent orchestration with 60+ specialized agents, swarm coordination, MCP server, self-learning hooks, and vector memory for Claude Code",
6
6
  "main": "dist/src/index.js",