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.
- package/.claude/helpers/intelligence.cjs +3 -5
- package/.claude/proven-config.json +1 -1
- package/.claude-plugin/marketplace.json +11 -0
- package/README.md +1 -0
- package/node_modules/@claude-flow/codex/README.md +87 -9
- package/node_modules/@claude-flow/codex/dist/activity/cli.d.ts +3 -0
- package/node_modules/@claude-flow/codex/dist/activity/cli.d.ts.map +1 -0
- package/node_modules/@claude-flow/codex/dist/activity/cli.js +115 -0
- package/node_modules/@claude-flow/codex/dist/activity/cli.js.map +1 -0
- package/node_modules/@claude-flow/codex/dist/activity/files.d.ts +14 -0
- package/node_modules/@claude-flow/codex/dist/activity/files.d.ts.map +1 -0
- package/node_modules/@claude-flow/codex/dist/activity/files.js +142 -0
- package/node_modules/@claude-flow/codex/dist/activity/files.js.map +1 -0
- package/node_modules/@claude-flow/codex/dist/activity/parser.d.ts +21 -0
- package/node_modules/@claude-flow/codex/dist/activity/parser.d.ts.map +1 -0
- package/node_modules/@claude-flow/codex/dist/activity/parser.js +179 -0
- package/node_modules/@claude-flow/codex/dist/activity/parser.js.map +1 -0
- package/node_modules/@claude-flow/codex/dist/activity/reader.d.ts +20 -0
- package/node_modules/@claude-flow/codex/dist/activity/reader.d.ts.map +1 -0
- package/node_modules/@claude-flow/codex/dist/activity/reader.js +106 -0
- package/node_modules/@claude-flow/codex/dist/activity/reader.js.map +1 -0
- package/node_modules/@claude-flow/codex/dist/activity/sanitize.d.ts +4 -0
- package/node_modules/@claude-flow/codex/dist/activity/sanitize.d.ts.map +1 -0
- package/node_modules/@claude-flow/codex/dist/activity/sanitize.js +27 -0
- package/node_modules/@claude-flow/codex/dist/activity/sanitize.js.map +1 -0
- package/node_modules/@claude-flow/codex/dist/activity/types.d.ts +31 -0
- package/node_modules/@claude-flow/codex/dist/activity/types.d.ts.map +1 -0
- package/node_modules/@claude-flow/codex/dist/activity/types.js +5 -0
- package/node_modules/@claude-flow/codex/dist/activity/types.js.map +1 -0
- package/node_modules/@claude-flow/codex/dist/activity/viewer.d.ts +21 -0
- package/node_modules/@claude-flow/codex/dist/activity/viewer.d.ts.map +1 -0
- package/node_modules/@claude-flow/codex/dist/activity/viewer.js +168 -0
- package/node_modules/@claude-flow/codex/dist/activity/viewer.js.map +1 -0
- package/node_modules/@claude-flow/codex/dist/cli.js +3 -0
- package/node_modules/@claude-flow/codex/dist/cli.js.map +1 -1
- package/node_modules/@claude-flow/codex/dist/dual-mode/cli.js +3 -3
- package/node_modules/@claude-flow/codex/dist/dual-mode/cli.js.map +1 -1
- package/node_modules/@claude-flow/mcp/dist/server.d.ts.map +1 -1
- package/node_modules/@claude-flow/mcp/dist/server.js +5 -3
- package/node_modules/@claude-flow/mcp/dist/server.js.map +1 -1
- package/node_modules/@claude-flow/mcp/dist/types.d.ts +11 -0
- package/node_modules/@claude-flow/mcp/dist/types.d.ts.map +1 -1
- package/node_modules/@claude-flow/mcp/dist/types.js.map +1 -1
- package/package.json +3 -2
- package/v3/@claude-flow/cli/README.md +1 -0
- package/v3/@claude-flow/cli/catalog-manifest.json +2 -2
- package/v3/@claude-flow/cli/dist/src/commands/agent.js +4 -1
- package/v3/@claude-flow/cli/dist/src/commands/hooks.js +20 -1
- package/v3/@claude-flow/cli/dist/src/commands/mcp.js +66 -6
- package/v3/@claude-flow/cli/dist/src/commands/memory.js +78 -3
- package/v3/@claude-flow/cli/dist/src/commands/workflow.js +55 -15
- package/v3/@claude-flow/cli/dist/src/init/executor.js +9 -5
- package/v3/@claude-flow/cli/dist/src/init/helper-companions.d.ts +1 -1
- package/v3/@claude-flow/cli/dist/src/init/helper-companions.js +4 -3
- package/v3/@claude-flow/cli/dist/src/mcp-server.d.ts +5 -0
- package/v3/@claude-flow/cli/dist/src/mcp-server.js +39 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/agent-tools.js +28 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/claims-tools.js +25 -15
- package/v3/@claude-flow/cli/dist/src/mcp-tools/hooks-tools.js +9 -3
- package/v3/@claude-flow/cli/dist/src/mcp-tools/memory-tools.js +58 -9
- package/v3/@claude-flow/cli/dist/src/mcp-tools/workflow-tools.js +2 -1
- package/v3/@claude-flow/cli/dist/src/memory/graph-edge-writer.d.ts +16 -4
- package/v3/@claude-flow/cli/dist/src/memory/graph-edge-writer.js +19 -7
- package/v3/@claude-flow/cli/dist/src/memory/intelligence.js +4 -0
- package/v3/@claude-flow/cli/dist/src/memory/memory-initializer.js +55 -9
- package/v3/@claude-flow/cli/dist/src/memory/memory-peek.d.ts +28 -0
- package/v3/@claude-flow/cli/dist/src/memory/memory-peek.js +222 -0
- package/v3/@claude-flow/cli/dist/src/memory/model-cache-dir.d.ts +11 -0
- package/v3/@claude-flow/cli/dist/src/memory/model-cache-dir.js +17 -0
- package/v3/@claude-flow/cli/dist/src/ruvector/task-embedder.js +38 -4
- package/v3/@claude-flow/cli/dist/src/services/memory-backup.d.ts +6 -0
- package/v3/@claude-flow/cli/dist/src/services/memory-backup.js +109 -81
- package/v3/@claude-flow/cli/dist/src/services/policy-ledger-anchor.d.ts +2 -0
- package/v3/@claude-flow/cli/dist/src/services/policy-ledger-anchor.js +4 -4
- package/v3/@claude-flow/cli/dist/src/services/policy-runtime.js +54 -3
- package/v3/@claude-flow/cli/dist/src/services/worker-daemon.js +4 -0
- 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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
693
|
-
utilization: l.claims.length /
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
972
|
-
|
|
973
|
-
|
|
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:
|
|
990
|
-
|
|
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
|
-
|
|
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`
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
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 {
|
|
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`
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*
|
|
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
|
|
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
|
|
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
|
-
|
|
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:
|
|
2165
|
+
model: (text) => reasoningBank.computeEmbedding(text),
|
|
2137
2166
|
tokenizer: null,
|
|
2138
|
-
dimensions:
|
|
2167
|
+
dimensions: 384
|
|
2139
2168
|
};
|
|
2140
2169
|
return {
|
|
2141
2170
|
success: true,
|
|
2142
|
-
dimensions:
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|