claude-flow 3.57.1 → 3.58.0

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 (77) hide show
  1. package/.claude/helpers/intelligence.cjs +3 -5
  2. package/.claude/proven-config.json +1 -1
  3. package/.claude-plugin/marketplace.json +11 -0
  4. package/README.md +1 -0
  5. package/node_modules/@claude-flow/codex/README.md +87 -9
  6. package/node_modules/@claude-flow/codex/dist/activity/cli.d.ts +3 -0
  7. package/node_modules/@claude-flow/codex/dist/activity/cli.d.ts.map +1 -0
  8. package/node_modules/@claude-flow/codex/dist/activity/cli.js +115 -0
  9. package/node_modules/@claude-flow/codex/dist/activity/cli.js.map +1 -0
  10. package/node_modules/@claude-flow/codex/dist/activity/files.d.ts +14 -0
  11. package/node_modules/@claude-flow/codex/dist/activity/files.d.ts.map +1 -0
  12. package/node_modules/@claude-flow/codex/dist/activity/files.js +142 -0
  13. package/node_modules/@claude-flow/codex/dist/activity/files.js.map +1 -0
  14. package/node_modules/@claude-flow/codex/dist/activity/parser.d.ts +21 -0
  15. package/node_modules/@claude-flow/codex/dist/activity/parser.d.ts.map +1 -0
  16. package/node_modules/@claude-flow/codex/dist/activity/parser.js +179 -0
  17. package/node_modules/@claude-flow/codex/dist/activity/parser.js.map +1 -0
  18. package/node_modules/@claude-flow/codex/dist/activity/reader.d.ts +20 -0
  19. package/node_modules/@claude-flow/codex/dist/activity/reader.d.ts.map +1 -0
  20. package/node_modules/@claude-flow/codex/dist/activity/reader.js +106 -0
  21. package/node_modules/@claude-flow/codex/dist/activity/reader.js.map +1 -0
  22. package/node_modules/@claude-flow/codex/dist/activity/sanitize.d.ts +4 -0
  23. package/node_modules/@claude-flow/codex/dist/activity/sanitize.d.ts.map +1 -0
  24. package/node_modules/@claude-flow/codex/dist/activity/sanitize.js +27 -0
  25. package/node_modules/@claude-flow/codex/dist/activity/sanitize.js.map +1 -0
  26. package/node_modules/@claude-flow/codex/dist/activity/types.d.ts +31 -0
  27. package/node_modules/@claude-flow/codex/dist/activity/types.d.ts.map +1 -0
  28. package/node_modules/@claude-flow/codex/dist/activity/types.js +5 -0
  29. package/node_modules/@claude-flow/codex/dist/activity/types.js.map +1 -0
  30. package/node_modules/@claude-flow/codex/dist/activity/viewer.d.ts +21 -0
  31. package/node_modules/@claude-flow/codex/dist/activity/viewer.d.ts.map +1 -0
  32. package/node_modules/@claude-flow/codex/dist/activity/viewer.js +168 -0
  33. package/node_modules/@claude-flow/codex/dist/activity/viewer.js.map +1 -0
  34. package/node_modules/@claude-flow/codex/dist/cli.js +3 -0
  35. package/node_modules/@claude-flow/codex/dist/cli.js.map +1 -1
  36. package/node_modules/@claude-flow/codex/dist/dual-mode/cli.js +3 -3
  37. package/node_modules/@claude-flow/codex/dist/dual-mode/cli.js.map +1 -1
  38. package/node_modules/@claude-flow/mcp/dist/server.d.ts.map +1 -1
  39. package/node_modules/@claude-flow/mcp/dist/server.js +5 -3
  40. package/node_modules/@claude-flow/mcp/dist/server.js.map +1 -1
  41. package/node_modules/@claude-flow/mcp/dist/types.d.ts +11 -0
  42. package/node_modules/@claude-flow/mcp/dist/types.d.ts.map +1 -1
  43. package/node_modules/@claude-flow/mcp/dist/types.js.map +1 -1
  44. package/package.json +3 -2
  45. package/v3/@claude-flow/cli/README.md +1 -0
  46. package/v3/@claude-flow/cli/catalog-manifest.json +2 -2
  47. package/v3/@claude-flow/cli/dist/src/commands/agent.js +4 -1
  48. package/v3/@claude-flow/cli/dist/src/commands/hooks.js +20 -1
  49. package/v3/@claude-flow/cli/dist/src/commands/mcp.js +66 -6
  50. package/v3/@claude-flow/cli/dist/src/commands/memory.js +78 -3
  51. package/v3/@claude-flow/cli/dist/src/commands/workflow.js +55 -15
  52. package/v3/@claude-flow/cli/dist/src/init/executor.js +9 -5
  53. package/v3/@claude-flow/cli/dist/src/init/helper-companions.d.ts +1 -1
  54. package/v3/@claude-flow/cli/dist/src/init/helper-companions.js +4 -3
  55. package/v3/@claude-flow/cli/dist/src/mcp-server.d.ts +5 -0
  56. package/v3/@claude-flow/cli/dist/src/mcp-server.js +39 -0
  57. package/v3/@claude-flow/cli/dist/src/mcp-tools/agent-tools.js +28 -0
  58. package/v3/@claude-flow/cli/dist/src/mcp-tools/claims-tools.js +25 -15
  59. package/v3/@claude-flow/cli/dist/src/mcp-tools/hooks-tools.js +9 -3
  60. package/v3/@claude-flow/cli/dist/src/mcp-tools/memory-tools.js +58 -9
  61. package/v3/@claude-flow/cli/dist/src/mcp-tools/workflow-tools.js +2 -1
  62. package/v3/@claude-flow/cli/dist/src/memory/graph-edge-writer.d.ts +16 -4
  63. package/v3/@claude-flow/cli/dist/src/memory/graph-edge-writer.js +19 -7
  64. package/v3/@claude-flow/cli/dist/src/memory/intelligence.js +4 -0
  65. package/v3/@claude-flow/cli/dist/src/memory/memory-initializer.js +55 -9
  66. package/v3/@claude-flow/cli/dist/src/memory/memory-peek.d.ts +28 -0
  67. package/v3/@claude-flow/cli/dist/src/memory/memory-peek.js +222 -0
  68. package/v3/@claude-flow/cli/dist/src/memory/model-cache-dir.d.ts +11 -0
  69. package/v3/@claude-flow/cli/dist/src/memory/model-cache-dir.js +17 -0
  70. package/v3/@claude-flow/cli/dist/src/ruvector/task-embedder.js +38 -4
  71. package/v3/@claude-flow/cli/dist/src/services/memory-backup.d.ts +6 -0
  72. package/v3/@claude-flow/cli/dist/src/services/memory-backup.js +109 -81
  73. package/v3/@claude-flow/cli/dist/src/services/policy-ledger-anchor.d.ts +2 -0
  74. package/v3/@claude-flow/cli/dist/src/services/policy-ledger-anchor.js +4 -4
  75. package/v3/@claude-flow/cli/dist/src/services/policy-runtime.js +54 -3
  76. package/v3/@claude-flow/cli/dist/src/services/worker-daemon.js +4 -0
  77. package/v3/@claude-flow/cli/package.json +5 -4
@@ -13,6 +13,7 @@ import { existsSync, writeFileSync, mkdirSync } from 'fs';
13
13
  import { join, resolve } from 'path';
14
14
  const CLAIMS_DIR = '.claude-flow/claims';
15
15
  const CLAIMS_FILE = 'claims.json';
16
+ const DEFAULT_MAX_CLAIMS = 5;
16
17
  function getClaimsPath() {
17
18
  return resolve(join(CLAIMS_DIR, CLAIMS_FILE));
18
19
  }
@@ -47,7 +48,7 @@ function parseClaimant(str) {
47
48
  export const claimsTools = [
48
49
  {
49
50
  name: 'claims_claim',
50
- description: 'Claim an issue for work (human or agent) Use when nothing native covers per-agent capability gating — Claude Code agents have file-system access by default. Pair claims_grant + claims_check before letting an agent run privileged ops. For trusted in-session work, no claims call is needed.',
51
+ description: 'Claim an issue for work (human or agent). Use when multiple agents might pick up the same issue — claiming makes ownership visible via claims_list before work starts.',
51
52
  category: 'claims',
52
53
  inputSchema: {
53
54
  type: 'object',
@@ -64,6 +65,10 @@ export const claimsTools = [
64
65
  type: 'string',
65
66
  description: 'Optional context about the work approach',
66
67
  },
68
+ ttlMs: {
69
+ type: 'number',
70
+ description: 'Optional time-to-live in milliseconds; when set, the claim records an expiresAt timestamp. Informational only today — no handler currently enforces it (e.g. claims_steal still requires claims_mark-stealable rather than checking expiry).',
71
+ },
67
72
  },
68
73
  required: ['issueId', 'claimant'],
69
74
  },
@@ -71,6 +76,7 @@ export const claimsTools = [
71
76
  const issueId = input.issueId;
72
77
  const claimantStr = input.claimant;
73
78
  const context = input.context;
79
+ const ttlMs = input.ttlMs;
74
80
  {
75
81
  const v = validateIdentifier(issueId, 'issueId');
76
82
  if (!v.valid)
@@ -86,6 +92,9 @@ export const claimsTools = [
86
92
  if (!v.valid)
87
93
  return { success: false, error: v.error };
88
94
  }
95
+ if (ttlMs !== undefined && (typeof ttlMs !== 'number' || !Number.isFinite(ttlMs) || ttlMs <= 0)) {
96
+ return { success: false, error: 'ttlMs must be a positive number' };
97
+ }
89
98
  const claimant = parseClaimant(claimantStr);
90
99
  if (!claimant) {
91
100
  return { success: false, error: 'Invalid claimant format. Use "human:userId:name" or "agent:agentId:agentType"' };
@@ -109,6 +118,7 @@ export const claimsTools = [
109
118
  statusChangedAt: now,
110
119
  progress: 0,
111
120
  context,
121
+ expiresAt: ttlMs !== undefined ? new Date(Date.now() + ttlMs).toISOString() : undefined,
112
122
  };
113
123
  store.claims[issueId] = claim;
114
124
  saveClaims(store);
@@ -121,7 +131,7 @@ export const claimsTools = [
121
131
  },
122
132
  {
123
133
  name: 'claims_release',
124
- description: 'Release a claim on an issue Use when nothing native covers per-agent capability gating — Claude Code agents have file-system access by default. Pair claims_grant + claims_check before letting an agent run privileged ops. For trusted in-session work, no claims call is needed.',
134
+ description: 'Release a claim on an issue. Use when the claimant stops working on it, freeing it for another claims_claim instead of leaving it claimed indefinitely.',
125
135
  category: 'claims',
126
136
  inputSchema: {
127
137
  type: 'object',
@@ -186,7 +196,7 @@ export const claimsTools = [
186
196
  },
187
197
  {
188
198
  name: 'claims_handoff',
189
- description: 'Request handoff of an issue to another claimant Use when nothing native covers per-agent capability gating — Claude Code agents have file-system access by default. Pair claims_grant + claims_check before letting an agent run privileged ops. For trusted in-session work, no claims call is needed.',
199
+ description: 'Request handoff of an issue to another claimant. Use when transferring in-progress work to a different agent, instead of releasing and having them claim it fresh.',
190
200
  category: 'claims',
191
201
  inputSchema: {
192
202
  type: 'object',
@@ -270,7 +280,7 @@ export const claimsTools = [
270
280
  },
271
281
  {
272
282
  name: 'claims_accept-handoff',
273
- description: 'Accept a pending handoff Use when nothing native covers per-agent capability gating — Claude Code agents have file-system access by default. Pair claims_grant + claims_check before letting an agent run privileged ops. For trusted in-session work, no claims call is needed.',
283
+ description: 'Accept a pending handoff. Use when the receiving claimant confirms a claims_handoff request, completing the ownership transfer.',
274
284
  category: 'claims',
275
285
  inputSchema: {
276
286
  type: 'object',
@@ -333,7 +343,7 @@ export const claimsTools = [
333
343
  },
334
344
  {
335
345
  name: 'claims_status',
336
- description: 'Update claim status Use when nothing native covers per-agent capability gating — Claude Code agents have file-system access by default. Pair claims_grant + claims_check before letting an agent run privileged ops. For trusted in-session work, no claims call is needed.',
346
+ description: 'Update claim status. Use when progress changes (active, blocked, review-requested, etc.) so claims_list and claims_load reflect current state.',
337
347
  category: 'claims',
338
348
  inputSchema: {
339
349
  type: 'object',
@@ -398,7 +408,7 @@ export const claimsTools = [
398
408
  },
399
409
  {
400
410
  name: 'claims_list',
401
- description: 'List all claims or filter by criteria Use when nothing native covers per-agent capability gating — Claude Code agents have file-system access by default. Pair claims_grant + claims_check before letting an agent run privileged ops. For trusted in-session work, no claims call is needed.',
411
+ description: 'List all claims or filter by criteria. Use when checking what is already claimed before starting new work, instead of guessing from issue comments.',
402
412
  category: 'claims',
403
413
  inputSchema: {
404
414
  type: 'object',
@@ -453,7 +463,7 @@ export const claimsTools = [
453
463
  },
454
464
  {
455
465
  name: 'claims_mark-stealable',
456
- description: 'Mark an issue as stealable by other agents Use when nothing native covers per-agent capability gating — Claude Code agents have file-system access by default. Pair claims_grant + claims_check before letting an agent run privileged ops. For trusted in-session work, no claims call is needed.',
466
+ description: 'Mark an issue as stealable by other agents. Use when a claimant is overloaded or stalled and wants another agent to pick it up via claims_steal.',
457
467
  category: 'claims',
458
468
  inputSchema: {
459
469
  type: 'object',
@@ -521,7 +531,7 @@ export const claimsTools = [
521
531
  },
522
532
  {
523
533
  name: 'claims_steal',
524
- description: 'Steal a stealable issue Use when nothing native covers per-agent capability gating — Claude Code agents have file-system access by default. Pair claims_grant + claims_check before letting an agent run privileged ops. For trusted in-session work, no claims call is needed.',
534
+ description: 'Steal a stealable issue. Use when picking up work already marked stealable via claims_mark-stealable, rather than claiming an issue someone else still owns.',
525
535
  category: 'claims',
526
536
  inputSchema: {
527
537
  type: 'object',
@@ -592,7 +602,7 @@ export const claimsTools = [
592
602
  },
593
603
  {
594
604
  name: 'claims_stealable',
595
- description: 'List all stealable issues Use when nothing native covers per-agent capability gating — Claude Code agents have file-system access by default. Pair claims_grant + claims_check before letting an agent run privileged ops. For trusted in-session work, no claims call is needed.',
605
+ description: 'List all stealable issues. Use when looking for available work that has been freed up via claims_mark-stealable.',
596
606
  category: 'claims',
597
607
  inputSchema: {
598
608
  type: 'object',
@@ -628,7 +638,7 @@ export const claimsTools = [
628
638
  },
629
639
  {
630
640
  name: 'claims_load',
631
- description: 'Get agent load information Use when nothing native covers per-agent capability gating — Claude Code agents have file-system access by default. Pair claims_grant + claims_check before letting an agent run privileged ops. For trusted in-session work, no claims call is needed.',
641
+ description: 'Get agent load information. Use when deciding which agent should take new work by comparing claim counts against claims_rebalance targets.',
632
642
  category: 'claims',
633
643
  inputSchema: {
634
644
  type: 'object',
@@ -689,8 +699,8 @@ export const claimsTools = [
689
699
  agentId: l.agentId,
690
700
  agentType: l.agentType,
691
701
  claimCount: l.claims.length,
692
- maxClaims: 5, // Default max
693
- utilization: l.claims.length / 5,
702
+ maxClaims: DEFAULT_MAX_CLAIMS,
703
+ utilization: l.claims.length / DEFAULT_MAX_CLAIMS,
694
704
  blockedCount: l.blockedCount,
695
705
  claims: l.claims.map(c => ({
696
706
  issueId: c.issueId,
@@ -711,7 +721,7 @@ export const claimsTools = [
711
721
  },
712
722
  {
713
723
  name: 'claims_board',
714
- description: 'Get a visual board view of all claims Use when nothing native covers per-agent capability gating — Claude Code agents have file-system access by default. Pair claims_grant + claims_check before letting an agent run privileged ops. For trusted in-session work, no claims call is needed.',
724
+ description: 'Get a visual board view of all claims. Use when scanning overall claim status across issues and agents instead of composing the view from claims_list yourself.',
715
725
  category: 'claims',
716
726
  inputSchema: {
717
727
  type: 'object',
@@ -760,7 +770,7 @@ export const claimsTools = [
760
770
  },
761
771
  {
762
772
  name: 'claims_rebalance',
763
- description: 'Suggest or apply load rebalancing across agents Use when nothing native covers per-agent capability gating — Claude Code agents have file-system access by default. Pair claims_grant + claims_check before letting an agent run privileged ops. For trusted in-session work, no claims call is needed.',
773
+ description: 'Suggest or apply load rebalancing across agents. Use when some agents are overloaded and others idle, instead of manually reassigning claims one at a time.',
764
774
  category: 'claims',
765
775
  inputSchema: {
766
776
  type: 'object',
@@ -794,7 +804,7 @@ export const claimsTools = [
794
804
  agentLoads.get(key).claims.push(claim);
795
805
  }
796
806
  const loads = Array.from(agentLoads.values());
797
- const maxClaims = 5;
807
+ const maxClaims = DEFAULT_MAX_CLAIMS;
798
808
  const avgLoad = loads.length > 0
799
809
  ? loads.reduce((sum, l) => sum + l.claims.length, 0) / loads.length
800
810
  : 0;
@@ -4175,12 +4175,12 @@ export const hooksIntelligenceLearn = {
4175
4175
  // Intelligence attention hook
4176
4176
  export const hooksIntelligenceAttention = {
4177
4177
  name: 'hooks_intelligence_attention',
4178
- description: 'Compute attention-weighted similarity using MoE/Flash/Hyperbolic Use when native Bash hooks (via Claude Code\'s settings.json) are wrong because you need Ruflo-side state — pattern persistence, neural training signals, model-routing learning, cost tracking, audit chain. For one-off shell commands, plain Bash hooks are fine.',
4178
+ description: 'Compute attention-weighted similarity using MoE/Flash. Use when native Bash hooks (via Claude Code\'s settings.json) are wrong because you need Ruflo-side state — pattern persistence, neural training signals, model-routing learning, cost tracking, audit chain. For one-off shell commands, plain Bash hooks are fine.',
4179
4179
  inputSchema: {
4180
4180
  type: 'object',
4181
4181
  properties: {
4182
4182
  query: { type: 'string', description: 'Query for attention computation' },
4183
- mode: { type: 'string', description: 'Attention mode (flash, moe, hyperbolic)' },
4183
+ mode: { type: 'string', description: 'Attention mode (flash, moe)' },
4184
4184
  topK: { type: 'number', description: 'Top-k results' },
4185
4185
  },
4186
4186
  required: ['query'],
@@ -4195,6 +4195,12 @@ export const hooksIntelligenceAttention = {
4195
4195
  if (!v.valid)
4196
4196
  return { success: false, error: v.error };
4197
4197
  }
4198
+ // #3548: only flash and moe have an implementation. Any other mode
4199
+ // (including the previously advertised 'hyperbolic') used to return an
4200
+ // empty stub whose note blamed a missing package; say so instead.
4201
+ if (mode !== 'flash' && mode !== 'moe') {
4202
+ return { success: false, error: `Attention mode "${mode}" is not implemented; supported modes: flash, moe` };
4203
+ }
4198
4204
  let implementation = 'placeholder';
4199
4205
  let embeddingSource = 'none';
4200
4206
  const results = [];
@@ -4370,7 +4376,7 @@ export const hooksIntelligenceAttention = {
4370
4376
  implementation,
4371
4377
  _embeddingSource: embeddingSource,
4372
4378
  _stub: implementation === 'none',
4373
- _note: implementation === 'none' ? 'No attention backend available. Install @ruvector/attention for real computation.' : undefined,
4379
+ _note: implementation === 'none' ? 'No attention backend available: flash/moe attention from @claude-flow/neural could not be loaded or failed.' : undefined,
4374
4380
  ...(embeddingSource === 'hash-fallback' && implementation !== 'none'
4375
4381
  ? { _embeddingNote: 'Query embeddings are hash-based (not semantic). Install @claude-flow/embeddings for real ONNX embeddings.' }
4376
4382
  : {}),
@@ -227,15 +227,31 @@ function loadLegacyStore() {
227
227
  /**
228
228
  * Mark migration as complete
229
229
  */
230
- function markMigrationComplete() {
230
+ function markMigrationComplete(counts) {
231
231
  const legacyDir = resolve(LEGACY_MEMORY_DIR);
232
232
  if (!existsSync(legacyDir))
233
233
  mkdirSync(legacyDir, { recursive: true });
234
234
  writeFileSync(resolve(join(LEGACY_MEMORY_DIR, MIGRATION_MARKER)), JSON.stringify({
235
235
  migratedAt: new Date().toISOString(),
236
236
  version: '3.0.0',
237
+ ...(counts ?? {}),
237
238
  }), 'utf-8');
238
239
  }
240
+ /**
241
+ * Counts recorded by the migration that wrote the marker, or null when no
242
+ * marker (or an older marker without counts) is present.
243
+ */
244
+ function readMigrationCounts() {
245
+ try {
246
+ const marker = JSON.parse(readFileSync(resolve(join(LEGACY_MEMORY_DIR, MIGRATION_MARKER)), 'utf-8'));
247
+ return typeof marker?.migrated === 'number' && typeof marker?.total === 'number'
248
+ ? { migrated: marker.migrated, total: marker.total }
249
+ : null;
250
+ }
251
+ catch {
252
+ return null;
253
+ }
254
+ }
239
255
  /**
240
256
  * Lazy-load memory initializer functions to avoid circular deps
241
257
  */
@@ -391,7 +407,7 @@ async function ensureInitialized(dbPath) {
391
407
  }
392
408
  }
393
409
  console.error(`[MCP Memory] Migrated ${migrated}/${Object.keys(legacyStore.entries).length} entries`);
394
- markMigrationComplete();
410
+ markMigrationComplete({ migrated, total: Object.keys(legacyStore.entries).length });
395
411
  }
396
412
  }
397
413
  }
@@ -636,7 +652,11 @@ export const memoryTools = [
636
652
  });
637
653
  return {
638
654
  results: r.results.map(e => ({
639
- id: e.id,
655
+ // #2271: searchEntries() truncates ids to 12 chars ("entry_" +
656
+ // the leading digits of Date.now()), so entries written within
657
+ // ~2.8h share one id and RRF fused them into a single result.
658
+ // SmartRetrieval dedupes on id: give it the (namespace, key) tuple.
659
+ id: JSON.stringify([e.namespace, e.key]),
640
660
  key: e.key,
641
661
  content: e.content,
642
662
  score: e.score,
@@ -966,11 +986,17 @@ export const memoryTools = [
966
986
  },
967
987
  handler: async (input) => {
968
988
  const force = input.force;
969
- // Remove migration marker if forcing
989
+ // Remove the migration marker if forcing. hasLegacyStore() checks the marker
990
+ // beside the legacy store, so that is the one force must clear (#4194);
991
+ // the old .swarm marker path is cleared too for compatibility.
970
992
  if (force) {
971
- const markerPath = getMigrationMarkerPath();
972
- if (existsSync(markerPath)) {
973
- unlinkSync(markerPath);
993
+ for (const markerPath of [
994
+ resolve(join(LEGACY_MEMORY_DIR, MIGRATION_MARKER)),
995
+ getMigrationMarkerPath(),
996
+ ]) {
997
+ if (existsSync(markerPath)) {
998
+ unlinkSync(markerPath);
999
+ }
974
1000
  }
975
1001
  }
976
1002
  // Check for legacy data
@@ -982,12 +1008,35 @@ export const memoryTools = [
982
1008
  migrated: 0,
983
1009
  };
984
1010
  }
1011
+ // A marker beside the store means a migration already ran; ensureInitialized
1012
+ // would move nothing, so say so instead of reporting the store's size.
1013
+ if (!hasLegacyStore()) {
1014
+ return {
1015
+ success: true,
1016
+ message: 'Legacy store already migrated (marker present); nothing was moved. Pass force: true to re-run.',
1017
+ migrated: 0,
1018
+ total: Object.keys(legacyStore.entries).length,
1019
+ };
1020
+ }
985
1021
  // Run migration via ensureInitialized
986
1022
  await ensureInitialized();
1023
+ // ensureInitialized swallows errors and records what it moved in the marker.
1024
+ const counts = readMigrationCounts();
1025
+ if (!counts) {
1026
+ return {
1027
+ success: false,
1028
+ message: 'Migration did not complete; no entries were recorded as migrated. See the MCP server log.',
1029
+ migrated: 0,
1030
+ total: Object.keys(legacyStore.entries).length,
1031
+ };
1032
+ }
987
1033
  return {
988
1034
  success: true,
989
- message: 'Migration completed',
990
- migrated: Object.keys(legacyStore.entries).length,
1035
+ message: counts.migrated === counts.total
1036
+ ? 'Migration completed'
1037
+ : `Migration completed with failures: ${counts.migrated} of ${counts.total} entries migrated`,
1038
+ migrated: counts.migrated,
1039
+ total: counts.total,
991
1040
  backend: await describeBackend(),
992
1041
  };
993
1042
  },
@@ -577,7 +577,8 @@ export const workflowTools = [
577
577
  const store = loadWorkflowStore();
578
578
  let workflows = Object.values(store.workflows);
579
579
  // Apply filters
580
- if (input.status) {
580
+ // 'all' is the CLI's explicit "no filter" value (`workflow list` sends it by default).
581
+ if (input.status && input.status !== 'all') {
581
582
  workflows = workflows.filter(w => w.status === input.status);
582
583
  }
583
584
  // Sort by creation date (newest first)
@@ -30,10 +30,12 @@
30
30
  * @module v3/cli/memory/graph-edge-writer
31
31
  */
32
32
  /**
33
- * True iff the memory.db bridge at `customDbPath` (or the default location)
34
- * is currently RFE1-encrypted-at-rest. Callers that receive `null` from
35
- * getBridgeDb()/insertGraphEdge() can use this to avoid recommending the
36
- * destructive `memory init` command for this specific, non-corrupt case.
33
+ * True iff the memory.db bridge at `customDbPath` — or, when omitted, the
34
+ * path `resolveDbPath()` resolves from the `CLAUDE_FLOW_DB_PATH` env binding
35
+ * / memory root default (#4291) — is currently RFE1-encrypted-at-rest.
36
+ * Callers that receive `null` from getBridgeDb()/insertGraphEdge() can use
37
+ * this to avoid recommending the destructive `memory init` command for this
38
+ * specific, non-corrupt case.
37
39
  */
38
40
  export declare function isBridgeDbEncryptedAtRest(customDbPath?: string): boolean;
39
41
  /** Test-only: clear the once-per-path warning dedup set. */
@@ -60,6 +62,16 @@ export declare function releaseBridgeDb(dbPath?: string): boolean;
60
62
  * #2431 fix: better-sqlite3 + WAL mode replaces sql.js + whole-file
61
63
  * writeFileSync. Eliminates the dual-write race that corrupted memory.db
62
64
  * when called alongside the memory bridge's better-sqlite3 writer.
65
+ *
66
+ * #4291 fix: the file is resolved via `resolveDbPath()` — the project's one
67
+ * documented path-priority rule (explicit `customDbPath` argument wins, then
68
+ * the `CLAUDE_FLOW_DB_PATH` env binding, then the plain `memory.db` default
69
+ * under the memory root) — instead of a local `customDbPath ?? <default>`
70
+ * expression that silently ignored `CLAUDE_FLOW_DB_PATH`. A caller that
71
+ * explicitly selected a store via that env var no longer gets answers read
72
+ * from a different, unselected database file. When the resolved file is
73
+ * missing (and `createIfMissing` is false) or encrypted at rest, this
74
+ * refuses (returns null) rather than falling back to any other store.
63
75
  */
64
76
  export declare function getBridgeDb(customDbPath?: string, opts?: {
65
77
  createIfMissing?: boolean;
@@ -33,7 +33,7 @@ import * as path from 'path';
33
33
  import * as fs from 'fs';
34
34
  import * as crypto from 'crypto';
35
35
  import { execFileSync } from 'child_process';
36
- import { getMemoryRoot } from './memory-initializer.js';
36
+ import { resolveDbPath } from './memory-initializer.js';
37
37
  import { encodeEmbedding } from './embedding-quantization.js';
38
38
  import { loadBetterSqlite3 } from './shared-sqlite.js';
39
39
  import { isEncryptedBlob } from '../encryption/vault.js';
@@ -125,13 +125,15 @@ function warnRfe1EncryptedOnce(dbPath) {
125
125
  'See #2889 (https://github.com/ruvnet/ruflo/issues/2889) for details and recovery options.');
126
126
  }
127
127
  /**
128
- * True iff the memory.db bridge at `customDbPath` (or the default location)
129
- * is currently RFE1-encrypted-at-rest. Callers that receive `null` from
130
- * getBridgeDb()/insertGraphEdge() can use this to avoid recommending the
131
- * destructive `memory init` command for this specific, non-corrupt case.
128
+ * True iff the memory.db bridge at `customDbPath` — or, when omitted, the
129
+ * path `resolveDbPath()` resolves from the `CLAUDE_FLOW_DB_PATH` env binding
130
+ * / memory root default (#4291) — is currently RFE1-encrypted-at-rest.
131
+ * Callers that receive `null` from getBridgeDb()/insertGraphEdge() can use
132
+ * this to avoid recommending the destructive `memory init` command for this
133
+ * specific, non-corrupt case.
132
134
  */
133
135
  export function isBridgeDbEncryptedAtRest(customDbPath) {
134
- const dbPath = customDbPath ?? path.join(getMemoryRoot(), 'memory.db');
136
+ const dbPath = resolveDbPath(customDbPath);
135
137
  return fs.existsSync(dbPath) && isRfe1EncryptedAtRest(dbPath);
136
138
  }
137
139
  /** Test-only: clear the once-per-path warning dedup set. */
@@ -222,9 +224,19 @@ export function releaseBridgeDb(dbPath) {
222
224
  * #2431 fix: better-sqlite3 + WAL mode replaces sql.js + whole-file
223
225
  * writeFileSync. Eliminates the dual-write race that corrupted memory.db
224
226
  * when called alongside the memory bridge's better-sqlite3 writer.
227
+ *
228
+ * #4291 fix: the file is resolved via `resolveDbPath()` — the project's one
229
+ * documented path-priority rule (explicit `customDbPath` argument wins, then
230
+ * the `CLAUDE_FLOW_DB_PATH` env binding, then the plain `memory.db` default
231
+ * under the memory root) — instead of a local `customDbPath ?? <default>`
232
+ * expression that silently ignored `CLAUDE_FLOW_DB_PATH`. A caller that
233
+ * explicitly selected a store via that env var no longer gets answers read
234
+ * from a different, unselected database file. When the resolved file is
235
+ * missing (and `createIfMissing` is false) or encrypted at rest, this
236
+ * refuses (returns null) rather than falling back to any other store.
225
237
  */
226
238
  export async function getBridgeDb(customDbPath, opts) {
227
- const dbPath = customDbPath ?? path.join(getMemoryRoot(), 'memory.db');
239
+ const dbPath = resolveDbPath(customDbPath);
228
240
  const createIfMissing = opts?.createIfMissing === true;
229
241
  if (_db && _dbPath === dbPath) {
230
242
  armIdleRelease();
@@ -1077,6 +1077,10 @@ export async function recordTrajectory(steps, verdict) {
1077
1077
  }
1078
1078
  bumpStat('trajectoriesRecorded');
1079
1079
  globalStats.lastAdaptation = Date.now();
1080
+ // #4283: the bank's own save is debounced, and the one-shot bins exit as
1081
+ // soon as the command resolves, so write the patterns now. Otherwise
1082
+ // stats.json counts patterns that never reach patterns.json.
1083
+ reasoningBank?.flushToDisk();
1080
1084
  savePersistedStats();
1081
1085
  return true;
1082
1086
  }
@@ -10,6 +10,7 @@ import { createRequire } from 'node:module';
10
10
  import { readFileMaybeEncrypted, writeDbImagePreservingFormat, writeFileRestricted } from '../fs-secure.js';
11
11
  import { restoreMemoryDbFromBackup } from '../services/memory-backup.js';
12
12
  import { validateIdentifier } from '../mcp-tools/validate-input.js';
13
+ import { resolveModelCacheDir } from './model-cache-dir.js';
13
14
  /**
14
15
  * ADR-323 — typed memory provenance. Distinguishes WHO/WHAT wrote a memory
15
16
  * entry (a user's stated claim vs an agent's own output vs a tool result vs
@@ -2009,6 +2010,8 @@ export async function applyTemporalDecay(dbPath) {
2009
2010
  };
2010
2011
  }
2011
2012
  }
2013
+ /** ADR-454: minimum interval before a 'retryable' degraded state re-attempts real init. */
2014
+ const MODEL_INIT_RETRY_INTERVAL_MS = 60_000;
2012
2015
  /**
2013
2016
  * State of the LOCAL embedding chain only (transformers.js / agentic-flow /
2014
2017
  * ruvector ONNX / hash). #3375: the AgentDB bridge's result is cached
@@ -2058,8 +2061,15 @@ export async function loadEmbeddingModel(options) {
2058
2061
  */
2059
2062
  async function loadLocalEmbeddingChain(verbose = false, startTime = Date.now()) {
2060
2063
  if (embeddingModelState?.loaded) {
2061
- return { success: true, dimensions: embeddingModelState.dimensions, modelName: 'cached', loadTime: 0 };
2064
+ const isRetryableDue = embeddingModelState.degradedReason === 'retryable' &&
2065
+ Date.now() - (embeddingModelState.lastAttemptAt ?? 0) >= MODEL_INIT_RETRY_INTERVAL_MS;
2066
+ if (!isRetryableDue) {
2067
+ return { success: true, dimensions: embeddingModelState.dimensions, modelName: 'cached', loadTime: 0 };
2068
+ }
2069
+ // Fall through to re-attempt real initialization — the retryable
2070
+ // failure (e.g. EACCES on the model cache dir) may now be fixed.
2062
2071
  }
2072
+ let sawRetryableFailure = false;
2063
2073
  try {
2064
2074
  // ADR-094: prefer @huggingface/transformers (clears protobufjs <7.5.5
2065
2075
  // critical RCE chain), fall back to legacy @xenova/transformers.
@@ -2068,6 +2078,7 @@ async function loadLocalEmbeddingChain(verbose = false, startTime = Date.now())
2068
2078
  // @claude-flow/embeddings/src/transformers-loader.ts.
2069
2079
  let transformersSource = null;
2070
2080
  let pipelineFn = null;
2081
+ let transformersModule = null;
2071
2082
  {
2072
2083
  const tryLoad = async (specifier) => {
2073
2084
  try {
@@ -2081,12 +2092,14 @@ async function loadLocalEmbeddingChain(verbose = false, startTime = Date.now())
2081
2092
  if (hf && typeof hf.pipeline === 'function') {
2082
2093
  pipelineFn = hf.pipeline;
2083
2094
  transformersSource = '@huggingface/transformers';
2095
+ transformersModule = hf;
2084
2096
  }
2085
2097
  else {
2086
2098
  const xen = await tryLoad('@xenova/transformers');
2087
2099
  if (xen && typeof xen.pipeline === 'function') {
2088
2100
  pipelineFn = xen.pipeline;
2089
2101
  transformersSource = '@xenova/transformers';
2102
+ transformersModule = xen;
2090
2103
  }
2091
2104
  }
2092
2105
  }
@@ -2103,6 +2116,14 @@ async function loadLocalEmbeddingChain(verbose = false, startTime = Date.now())
2103
2116
  if (verbose) {
2104
2117
  console.log(`Loading ONNX embedding model via ${transformersSource} (all-MiniLM-L6-v2)...`);
2105
2118
  }
2119
+ // ADR-454: route model downloads to a user-writable cache dir —
2120
+ // the package-relative default is root-owned under a system-prefix
2121
+ // global install, which throws EACCES for the runtime user.
2122
+ const cacheDir = resolveModelCacheDir();
2123
+ fs.mkdirSync(cacheDir, { recursive: true });
2124
+ const env = transformersModule?.env;
2125
+ if (env)
2126
+ env.cacheDir = cacheDir;
2106
2127
  const embedder = await pipelineFn('feature-extraction', 'Xenova/all-MiniLM-L6-v2');
2107
2128
  embeddingModelState = {
2108
2129
  loaded: true,
@@ -2118,6 +2139,10 @@ async function loadLocalEmbeddingChain(verbose = false, startTime = Date.now())
2118
2139
  };
2119
2140
  }
2120
2141
  catch (err) {
2142
+ // ADR-454: a real backend was importable and callable — this failure
2143
+ // (EACCES, ENOSPC, transient network error, etc.) is environmental,
2144
+ // not a missing dependency, so it must not be memoized permanently.
2145
+ sawRetryableFailure = true;
2121
2146
  if (verbose) {
2122
2147
  console.warn(`${transformersSource} pipeline init failed (${err instanceof Error ? err.message : String(err)}); ` +
2123
2148
  'falling through to ruvector ONNX / agentic-flow / hash fallback.');
@@ -2131,15 +2156,19 @@ async function loadLocalEmbeddingChain(verbose = false, startTime = Date.now())
2131
2156
  if (verbose) {
2132
2157
  console.log('Loading agentic-flow ReasoningBank embedding model...');
2133
2158
  }
2159
+ // #3113: register a callable, like the ruvector branch below —
2160
+ // generateLocalEmbedding() only invokes function models. The bundled
2161
+ // model is all-MiniLM-L6-v2 (384-dim, per agentic-flow's
2162
+ // getEmbeddingDimensions()).
2134
2163
  embeddingModelState = {
2135
2164
  loaded: true,
2136
- model: { embed: reasoningBank.computeEmbedding },
2165
+ model: (text) => reasoningBank.computeEmbedding(text),
2137
2166
  tokenizer: null,
2138
- dimensions: 768
2167
+ dimensions: 384
2139
2168
  };
2140
2169
  return {
2141
2170
  success: true,
2142
- dimensions: 768,
2171
+ dimensions: 384,
2143
2172
  modelName: 'agentic-flow/reasoningbank',
2144
2173
  loadTime: Date.now() - startTime
2145
2174
  };
@@ -2182,12 +2211,18 @@ async function loadLocalEmbeddingChain(verbose = false, startTime = Date.now())
2182
2211
  }
2183
2212
  // No agentic-flow root-entry fallback: no published root exports
2184
2213
  // `embeddings`, and importing it runs its CLI main() in 3.0.0-alpha.x.
2185
- // No ONNX model available - use fallback
2214
+ // No ONNX model available - use fallback.
2215
+ // ADR-454: mark whether this is a permanent (no backend importable at
2216
+ // all) or retryable (a backend existed but init failed for an
2217
+ // environmental reason) degraded state, so a later call can re-attempt
2218
+ // real initialization once the condition is fixed.
2186
2219
  embeddingModelState = {
2187
2220
  loaded: true,
2188
2221
  model: null, // Will use simple hash-based fallback
2189
2222
  tokenizer: null,
2190
- dimensions: 128 // Smaller fallback dimensions
2223
+ dimensions: 128, // Smaller fallback dimensions
2224
+ degradedReason: sawRetryableFailure ? 'retryable' : 'permanent',
2225
+ lastAttemptAt: Date.now()
2191
2226
  };
2192
2227
  return {
2193
2228
  success: true,
@@ -2253,7 +2288,15 @@ export async function generateLocalEmbedding(text) {
2253
2288
  // loadEmbeddingModel(), which is bridge-first — when the bridge answered
2254
2289
  // there, no local model was ever loaded and this function always returned
2255
2290
  // the hash fallback.
2256
- if (!embeddingModelState?.loaded) {
2291
+ //
2292
+ // ADR-454: also re-attempt when the memoized state is a 'retryable'
2293
+ // degraded fallback whose retry interval has elapsed — otherwise this
2294
+ // guard short-circuits past loadLocalEmbeddingChain()'s own retry check
2295
+ // and the fix (e.g. a now-writable cache dir) never gets exercised.
2296
+ const shouldRetryLoad = !embeddingModelState?.loaded ||
2297
+ (embeddingModelState.degradedReason === 'retryable' &&
2298
+ Date.now() - (embeddingModelState.lastAttemptAt ?? 0) >= MODEL_INIT_RETRY_INTERVAL_MS);
2299
+ if (shouldRetryLoad) {
2257
2300
  await loadLocalEmbeddingChain();
2258
2301
  }
2259
2302
  // #2461: loadEmbeddingModel() can leave embeddingModelState null when an
@@ -2272,10 +2315,13 @@ export async function generateLocalEmbedding(text) {
2272
2315
  if (state.model && typeof state.model === 'function') {
2273
2316
  try {
2274
2317
  const output = await state.model(text, { pooling: 'mean', normalize: true });
2275
- // Handle both @xenova/transformers (output.data) and ruvector (plain array) formats
2318
+ // Handle @xenova/transformers (output.data), ruvector (plain array) and
2319
+ // typed-array (ReasoningBank Float32Array, #3114) formats. DataView is an
2320
+ // ArrayBuffer view without an iterator (Array.from gives []), so exclude it.
2276
2321
  const embedding = output?.data
2277
2322
  ? Array.from(output.data)
2278
- : Array.isArray(output) ? output : null;
2323
+ : Array.isArray(output) ? output
2324
+ : ArrayBuffer.isView(output) && !(output instanceof DataView) ? Array.from(output) : null;
2279
2325
  if (embedding) {
2280
2326
  return {
2281
2327
  embedding,
@@ -0,0 +1,28 @@
1
+ export type MemoryRootSource = 'path' | 'CLAUDE_FLOW_DB_PATH' | 'CLAUDE_FLOW_MEMORY_PATH' | 'config' | 'default';
2
+ export interface MemoryPeekStore {
3
+ role: 'cli' | 'mcp';
4
+ path: string;
5
+ exists: boolean;
6
+ rows: number | null;
7
+ lastWrite: string | null;
8
+ sizeBytes: number | null;
9
+ walBytes: number | null;
10
+ encrypted?: boolean;
11
+ }
12
+ export interface MemoryPeekResult {
13
+ cwd: string;
14
+ root: {
15
+ path: string;
16
+ source: MemoryRootSource;
17
+ };
18
+ stores: MemoryPeekStore[];
19
+ sharedKeys: number | null;
20
+ }
21
+ /**
22
+ * Resolve the route and report both stores, read-only. Never creates,
23
+ * initializes, or migrates anything, on an empty project or any other.
24
+ */
25
+ export declare function memoryPeek(opts?: {
26
+ path?: string;
27
+ }): Promise<MemoryPeekResult>;
28
+ //# sourceMappingURL=memory-peek.d.ts.map