@claude-flow/cli 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/.helpers-version +1 -1
- package/.claude/helpers/helpers.manifest.json +3 -3
- package/.claude/helpers/intelligence.cjs +3 -5
- package/README.md +1 -0
- package/catalog-manifest.json +2 -2
- package/dist/src/commands/agent.js +4 -1
- package/dist/src/commands/hooks.js +20 -1
- package/dist/src/commands/mcp.js +66 -6
- package/dist/src/commands/memory.js +78 -3
- package/dist/src/commands/workflow.js +55 -15
- package/dist/src/init/executor.js +9 -5
- package/dist/src/init/helper-companions.d.ts +1 -1
- package/dist/src/init/helper-companions.js +4 -3
- package/dist/src/mcp-server.d.ts +5 -0
- package/dist/src/mcp-server.js +39 -0
- package/dist/src/mcp-tools/agent-tools.js +28 -0
- package/dist/src/mcp-tools/claims-tools.js +25 -15
- package/dist/src/mcp-tools/hooks-tools.js +9 -3
- package/dist/src/mcp-tools/memory-tools.js +58 -9
- package/dist/src/mcp-tools/workflow-tools.js +2 -1
- package/dist/src/memory/graph-edge-writer.d.ts +16 -4
- package/dist/src/memory/graph-edge-writer.js +19 -7
- package/dist/src/memory/intelligence.js +4 -0
- package/dist/src/memory/memory-initializer.js +55 -9
- package/dist/src/memory/memory-peek.d.ts +28 -0
- package/dist/src/memory/memory-peek.js +222 -0
- package/dist/src/memory/model-cache-dir.d.ts +11 -0
- package/dist/src/memory/model-cache-dir.js +17 -0
- package/dist/src/ruvector/task-embedder.js +38 -4
- package/dist/src/services/memory-backup.d.ts +6 -0
- package/dist/src/services/memory-backup.js +109 -81
- package/dist/src/services/policy-ledger-anchor.d.ts +2 -0
- package/dist/src/services/policy-ledger-anchor.js +4 -4
- package/dist/src/services/policy-runtime.js +54 -3
- package/dist/src/services/worker-daemon.js +4 -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 +5 -4
- package/plugins/ruflo-console/dist/tui.mjs +159 -159
|
@@ -216,6 +216,7 @@ export const agentTools = [
|
|
|
216
216
|
properties: {
|
|
217
217
|
agentType: { type: 'string', description: 'Type of agent to spawn' },
|
|
218
218
|
agentId: { type: 'string', description: 'Optional custom agent ID' },
|
|
219
|
+
name: { type: 'string', description: 'Optional display name for the agent (shown by agent_list, agent_status and ruflo-console)' },
|
|
219
220
|
// #2085 — accept swarmId so spawned agents register in the
|
|
220
221
|
// swarm.agents array that swarm_status reports. Omit to register
|
|
221
222
|
// with the most-recently-created swarm.
|
|
@@ -241,6 +242,18 @@ export const agentTools = [
|
|
|
241
242
|
}
|
|
242
243
|
const agentId = input.agentId || `agent-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
243
244
|
const agentType = input.agentType;
|
|
245
|
+
// #4300 — older callers (including `agent spawn` before this fix) send
|
|
246
|
+
// the name only as metadata.name.
|
|
247
|
+
const metadata = input.metadata;
|
|
248
|
+
const rawName = input.name ?? metadata?.name;
|
|
249
|
+
let name;
|
|
250
|
+
if (rawName !== undefined && rawName !== '') {
|
|
251
|
+
const vName = validateText(rawName, 'name', 200);
|
|
252
|
+
if (!vName.valid) {
|
|
253
|
+
return { success: false, error: `Input validation failed: ${vName.error}` };
|
|
254
|
+
}
|
|
255
|
+
name = vName.sanitized;
|
|
256
|
+
}
|
|
244
257
|
const config = input.config || {};
|
|
245
258
|
// Add explicit model to config if provided
|
|
246
259
|
if (input.model) {
|
|
@@ -262,6 +275,7 @@ export const agentTools = [
|
|
|
262
275
|
const agent = {
|
|
263
276
|
agentId,
|
|
264
277
|
agentType,
|
|
278
|
+
...(name ? { name } : {}),
|
|
265
279
|
status: 'idle',
|
|
266
280
|
health: 1.0,
|
|
267
281
|
taskCount: 0,
|
|
@@ -347,6 +361,7 @@ export const agentTools = [
|
|
|
347
361
|
success: true,
|
|
348
362
|
agentId,
|
|
349
363
|
agentType: agent.agentType,
|
|
364
|
+
...(agent.name ? { name: agent.name } : {}),
|
|
350
365
|
model: agent.model,
|
|
351
366
|
modelRoutedBy: routingResult.routedBy,
|
|
352
367
|
...(routingResult.modelId ? { modelId: routingResult.modelId } : {}),
|
|
@@ -501,6 +516,7 @@ export const agentTools = [
|
|
|
501
516
|
return {
|
|
502
517
|
agentId: agent.agentId,
|
|
503
518
|
agentType: agent.agentType,
|
|
519
|
+
...(agent.name ? { name: agent.name } : {}),
|
|
504
520
|
status: agent.status,
|
|
505
521
|
health: agent.health,
|
|
506
522
|
taskCount: agent.taskCount,
|
|
@@ -525,6 +541,7 @@ export const agentTools = [
|
|
|
525
541
|
properties: {
|
|
526
542
|
status: { type: 'string', description: 'Filter by status' },
|
|
527
543
|
domain: { type: 'string', description: 'Filter by domain' },
|
|
544
|
+
agentType: { type: 'string', description: 'Filter by agent type' },
|
|
528
545
|
includeTerminated: { type: 'boolean', description: 'Include terminated agents' },
|
|
529
546
|
},
|
|
530
547
|
},
|
|
@@ -539,6 +556,11 @@ export const agentTools = [
|
|
|
539
556
|
if (!v.valid)
|
|
540
557
|
return { agents: [], total: 0, error: `Input validation failed: ${v.error}` };
|
|
541
558
|
}
|
|
559
|
+
if (input.agentType) {
|
|
560
|
+
const v = validateIdentifier(input.agentType, 'agentType');
|
|
561
|
+
if (!v.valid)
|
|
562
|
+
return { agents: [], total: 0, error: `Input validation failed: ${v.error}` };
|
|
563
|
+
}
|
|
542
564
|
let agents = Object.values(loadAllAgents()); // #1916: includes hive-mind-spawned workers
|
|
543
565
|
// Filter by status
|
|
544
566
|
if (input.status) {
|
|
@@ -551,10 +573,15 @@ export const agentTools = [
|
|
|
551
573
|
if (input.domain) {
|
|
552
574
|
agents = agents.filter(a => a.domain === input.domain);
|
|
553
575
|
}
|
|
576
|
+
// Filter by agent type
|
|
577
|
+
if (input.agentType) {
|
|
578
|
+
agents = agents.filter(a => a.agentType === input.agentType);
|
|
579
|
+
}
|
|
554
580
|
return {
|
|
555
581
|
agents: agents.map(a => ({
|
|
556
582
|
agentId: a.agentId,
|
|
557
583
|
agentType: a.agentType,
|
|
584
|
+
...(a.name ? { name: a.name } : {}),
|
|
558
585
|
status: a.status,
|
|
559
586
|
health: a.health,
|
|
560
587
|
taskCount: a.taskCount,
|
|
@@ -565,6 +592,7 @@ export const agentTools = [
|
|
|
565
592
|
filters: {
|
|
566
593
|
status: input.status,
|
|
567
594
|
domain: input.domain,
|
|
595
|
+
agentType: input.agentType,
|
|
568
596
|
includeTerminated: input.includeTerminated,
|
|
569
597
|
},
|
|
570
598
|
};
|
|
@@ -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
|
}
|