claude-flow 3.47.1 → 3.49.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/.proven-config-version +1 -0
- package/.claude/helpers/auto-memory-hook.mjs +6 -2
- package/.claude/helpers/hook-handler.cjs +7 -4
- package/.claude/helpers/learning-service.mjs +4 -2
- package/.claude/helpers/memory.cjs +1 -1
- package/.claude/helpers/metrics-db.mjs +4 -2
- package/.claude/helpers/router.cjs +1 -1
- package/.claude/helpers/session.cjs +1 -1
- package/.claude/proven-config.json +42 -0
- package/.claude-plugin/marketplace.json +16 -1
- package/README.md +1 -53
- package/README.zh-CN.md +1 -53
- package/node_modules/@claude-flow/codex/package.json +1 -1
- package/node_modules/@claude-flow/mcp/dist/tool-registry.d.ts.map +1 -1
- package/node_modules/@claude-flow/mcp/dist/tool-registry.js +11 -3
- package/node_modules/@claude-flow/mcp/dist/tool-registry.js.map +1 -1
- package/node_modules/@claude-flow/mcp/package.json +6 -3
- package/node_modules/@claude-flow/plugin-agent-federation/package.json +3 -3
- package/node_modules/@claude-flow/security/dist/policy/engine.d.ts +2 -6
- package/node_modules/@claude-flow/security/dist/policy/engine.d.ts.map +1 -1
- package/node_modules/@claude-flow/security/dist/policy/engine.js +35 -1
- package/node_modules/@claude-flow/security/dist/policy/engine.js.map +1 -1
- package/node_modules/@claude-flow/security/dist/policy/types.d.ts +18 -0
- package/node_modules/@claude-flow/security/dist/policy/types.d.ts.map +1 -1
- package/node_modules/@claude-flow/security/package.json +2 -3
- package/package.json +7 -7
- package/v3/@claude-flow/cli/README.md +1 -53
- package/v3/@claude-flow/cli/bin/cli.js +4 -1
- package/v3/@claude-flow/cli/catalog-manifest.json +4 -4
- package/v3/@claude-flow/cli/dist/src/commands/analyze.js +2 -2
- package/v3/@claude-flow/cli/dist/src/commands/config.js +5 -7
- package/v3/@claude-flow/cli/dist/src/commands/doctor.d.ts +19 -1
- package/v3/@claude-flow/cli/dist/src/commands/doctor.js +69 -8
- package/v3/@claude-flow/cli/dist/src/commands/hooks.js +22 -9
- package/v3/@claude-flow/cli/dist/src/commands/memory.js +119 -34
- package/v3/@claude-flow/cli/dist/src/commands/metaharness.js +1 -0
- package/v3/@claude-flow/cli/dist/src/commands/plugins.js +44 -7
- package/v3/@claude-flow/cli/dist/src/commands/policy.js +5 -2
- package/v3/@claude-flow/cli/dist/src/commands/security.js +113 -55
- package/v3/@claude-flow/cli/dist/src/commands/session.js +132 -25
- package/v3/@claude-flow/cli/dist/src/commands/swarm.js +11 -11
- package/v3/@claude-flow/cli/dist/src/commands/task.js +5 -4
- package/v3/@claude-flow/cli/dist/src/index.js +10 -1
- package/v3/@claude-flow/cli/dist/src/init/executor.js +11 -5
- package/v3/@claude-flow/cli/dist/src/init/helper-companions.d.ts +3 -0
- package/v3/@claude-flow/cli/dist/src/init/helper-companions.js +34 -0
- package/v3/@claude-flow/cli/dist/src/init/helper-integrity.d.ts +21 -0
- package/v3/@claude-flow/cli/dist/src/init/helper-integrity.js +62 -0
- package/v3/@claude-flow/cli/dist/src/init/helper-refresh.d.ts +18 -11
- package/v3/@claude-flow/cli/dist/src/init/helper-refresh.js +56 -13
- package/v3/@claude-flow/cli/dist/src/init/helpers-generator.js +14 -12
- package/v3/@claude-flow/cli/dist/src/mcp-server.js +10 -3
- package/v3/@claude-flow/cli/dist/src/mcp-tools/agentdb-tools.js +29 -62
- package/v3/@claude-flow/cli/dist/src/mcp-tools/hooks-tools.d.ts +16 -7
- package/v3/@claude-flow/cli/dist/src/mcp-tools/hooks-tools.js +226 -51
- package/v3/@claude-flow/cli/dist/src/mcp-tools/memory-tools.d.ts +7 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/memory-tools.js +131 -40
- package/v3/@claude-flow/cli/dist/src/mcp-tools/policy-enforcer.d.ts +8 -1
- package/v3/@claude-flow/cli/dist/src/mcp-tools/policy-enforcer.js +80 -8
- package/v3/@claude-flow/cli/dist/src/mcp-tools/session-tools.d.ts +15 -0
- package/v3/@claude-flow/cli/dist/src/mcp-tools/session-tools.js +270 -58
- package/v3/@claude-flow/cli/dist/src/memory/feedback-patterns.d.ts +13 -0
- package/v3/@claude-flow/cli/dist/src/memory/feedback-patterns.js +14 -0
- package/v3/@claude-flow/cli/dist/src/memory/live-memory-row.d.ts +3 -0
- package/v3/@claude-flow/cli/dist/src/memory/live-memory-row.js +5 -0
- package/v3/@claude-flow/cli/dist/src/memory/memory-bridge.d.ts +11 -20
- package/v3/@claude-flow/cli/dist/src/memory/memory-bridge.js +348 -134
- package/v3/@claude-flow/cli/dist/src/memory/memory-initializer.d.ts +9 -1
- package/v3/@claude-flow/cli/dist/src/memory/memory-initializer.js +47 -34
- package/v3/@claude-flow/cli/dist/src/plugins/manager.d.ts +37 -10
- package/v3/@claude-flow/cli/dist/src/plugins/manager.js +106 -20
- package/v3/@claude-flow/cli/dist/src/plugins/trust-policy.d.ts +61 -0
- package/v3/@claude-flow/cli/dist/src/plugins/trust-policy.js +84 -0
- package/v3/@claude-flow/cli/dist/src/ruvector/graph-analyzer.js +4 -1
- package/v3/@claude-flow/cli/dist/src/services/config-file-manager.js +30 -8
- package/v3/@claude-flow/cli/dist/src/services/memory-backup.d.ts +1 -1
- package/v3/@claude-flow/cli/dist/src/services/memory-backup.js +67 -27
- package/v3/@claude-flow/cli/dist/src/services/policy-runtime.d.ts +6 -0
- package/v3/@claude-flow/cli/dist/src/services/policy-runtime.js +27 -2
- package/v3/@claude-flow/cli/dist/src/services/worker-daemon.d.ts +2 -2
- package/v3/@claude-flow/cli/dist/src/services/worker-daemon.js +23 -14
- package/v3/@claude-flow/cli/package.json +9 -9
- package/v3/@claude-flow/guidance/package.json +5 -6
- package/v3/@claude-flow/shared/package.json +6 -3
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Tool definitions for session management with file persistence.
|
|
5
5
|
*/
|
|
6
6
|
import { existsSync, readFileSync, readdirSync, unlinkSync, statSync, writeFileSync } from 'node:fs';
|
|
7
|
+
import { randomUUID } from 'node:crypto';
|
|
7
8
|
import { join } from 'node:path';
|
|
8
9
|
import { getProjectCwd } from './types.js';
|
|
9
10
|
import { mkdirRestricted, readFileMaybeEncrypted, writeFileRestricted, } from '../fs-secure.js';
|
|
@@ -68,17 +69,126 @@ function listSessions() {
|
|
|
68
69
|
}
|
|
69
70
|
return sessions;
|
|
70
71
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
/** Snapshot format written by session_save since #3573. */
|
|
73
|
+
const MEMORY_SNAPSHOT_FORMAT = 'ruflo-session-memory/2';
|
|
74
|
+
const MEMORY_PAGE_SIZE = 500;
|
|
75
|
+
async function readLiveEntries(listEntries, dbPath, encryptWrites) {
|
|
76
|
+
const rows = [];
|
|
77
|
+
// Bounded: stop on a short page, once `total` rows are in hand, or after a
|
|
78
|
+
// hard cap, so a backend that ignores `offset` cannot loop forever.
|
|
79
|
+
for (let page = 0, offset = 0; page < 10_000; page++, offset += MEMORY_PAGE_SIZE) {
|
|
80
|
+
const result = await listEntries({
|
|
81
|
+
dbPath, includeContent: true, limit: MEMORY_PAGE_SIZE, offset,
|
|
82
|
+
...(encryptWrites === undefined ? {} : { encryptWrites }),
|
|
83
|
+
});
|
|
84
|
+
if (!result.success)
|
|
85
|
+
throw new Error(result.error || `could not list ${dbPath}`);
|
|
86
|
+
rows.push(...result.entries);
|
|
87
|
+
if (result.entries.length < MEMORY_PAGE_SIZE || rows.length >= result.total)
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
return rows;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* #3573: capture the memory the user actually has.
|
|
94
|
+
*
|
|
95
|
+
* Before this, session_save read only the pre-SQLite `.claude-flow/memory/store.json`,
|
|
96
|
+
* which a current install never writes, so `--include-memory` saved nothing and
|
|
97
|
+
* reported "Memory Entries: 0". The snapshot now covers the same store `memory
|
|
98
|
+
* list` reads (resolveDbPath), plus rows that exist only in the sibling AgentDB
|
|
99
|
+
* store written by the MCP path, de-duplicated by namespace+key (default CLI
|
|
100
|
+
* writes are mirrored into AgentDB, so most rows are in both). A legacy
|
|
101
|
+
* store.json is still captured for backward compatibility.
|
|
102
|
+
*/
|
|
103
|
+
async function captureMemorySnapshot() {
|
|
104
|
+
const report = {
|
|
105
|
+
requested: true, status: 'captured', entries: 0,
|
|
106
|
+
sources: { memoryDb: 0, agentdb: 0, legacyJson: 0 },
|
|
107
|
+
};
|
|
108
|
+
const entries = {};
|
|
109
|
+
let legacy;
|
|
110
|
+
const legacyPath = join(getProjectCwd(), STORAGE_DIR, 'memory', 'store.json');
|
|
111
|
+
if (existsSync(legacyPath)) {
|
|
75
112
|
try {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
113
|
+
legacy = JSON.parse(readFileSync(legacyPath, 'utf-8'));
|
|
114
|
+
const legacyEntries = legacy.entries || {};
|
|
115
|
+
for (const [id, entry] of Object.entries(legacyEntries)) {
|
|
116
|
+
entries[id] = entry;
|
|
117
|
+
report.sources.legacyJson++;
|
|
79
118
|
}
|
|
80
119
|
}
|
|
81
|
-
catch { /*
|
|
120
|
+
catch { /* unreadable legacy file is not memory we can restore */ }
|
|
121
|
+
}
|
|
122
|
+
let sawStore = legacy !== undefined;
|
|
123
|
+
try {
|
|
124
|
+
const { listEntries, resolveDbPath } = await import('../memory/memory-initializer.js');
|
|
125
|
+
const primary = resolveDbPath();
|
|
126
|
+
const seen = new Set();
|
|
127
|
+
if (existsSync(primary)) {
|
|
128
|
+
sawStore = true;
|
|
129
|
+
for (const row of await readLiveEntries(listEntries, primary)) {
|
|
130
|
+
const id = `${row.namespace}::${row.key}`;
|
|
131
|
+
seen.add(id);
|
|
132
|
+
entries[id] = {
|
|
133
|
+
key: row.key, value: row.content ?? '', namespace: row.namespace,
|
|
134
|
+
provenanceType: row.provenanceType, source: 'memory-db',
|
|
135
|
+
};
|
|
136
|
+
report.sources.memoryDb++;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
let sibling = null;
|
|
140
|
+
try {
|
|
141
|
+
const { siblingAgentDbPath } = await import('../memory/memory-bridge.js');
|
|
142
|
+
sibling = siblingAgentDbPath(primary);
|
|
143
|
+
}
|
|
144
|
+
catch { /* no bridge, no sibling store */ }
|
|
145
|
+
if (sibling && existsSync(sibling)) {
|
|
146
|
+
sawStore = true;
|
|
147
|
+
// The AgentDB store is read by native SQLite and must stay plaintext.
|
|
148
|
+
for (const row of await readLiveEntries(listEntries, sibling, false)) {
|
|
149
|
+
const id = `${row.namespace}::${row.key}`;
|
|
150
|
+
if (seen.has(id))
|
|
151
|
+
continue;
|
|
152
|
+
seen.add(id);
|
|
153
|
+
entries[id] = {
|
|
154
|
+
key: row.key, value: row.content ?? '', namespace: row.namespace,
|
|
155
|
+
provenanceType: row.provenanceType, source: 'agentdb',
|
|
156
|
+
};
|
|
157
|
+
report.sources.agentdb++;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
catch (e) {
|
|
162
|
+
report.status = 'error';
|
|
163
|
+
report.error = e.message;
|
|
164
|
+
}
|
|
165
|
+
report.entries = Object.keys(entries).length;
|
|
166
|
+
if (report.status !== 'error' && !sawStore)
|
|
167
|
+
report.status = 'no-store';
|
|
168
|
+
if (report.entries === 0 && legacy === undefined) {
|
|
169
|
+
return { report };
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
memory: {
|
|
173
|
+
format: MEMORY_SNAPSHOT_FORMAT,
|
|
174
|
+
entries,
|
|
175
|
+
...(legacy !== undefined ? { legacy } : {}),
|
|
176
|
+
},
|
|
177
|
+
report,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
// Load related stores for session data
|
|
181
|
+
async function loadRelatedStores(options) {
|
|
182
|
+
const data = {};
|
|
183
|
+
let memoryCapture = {
|
|
184
|
+
requested: false, status: 'not-requested', entries: 0,
|
|
185
|
+
sources: { memoryDb: 0, agentdb: 0, legacyJson: 0 },
|
|
186
|
+
};
|
|
187
|
+
if (options.includeMemory) {
|
|
188
|
+
const captured = await captureMemorySnapshot();
|
|
189
|
+
memoryCapture = captured.report;
|
|
190
|
+
if (captured.memory)
|
|
191
|
+
data.memory = captured.memory;
|
|
82
192
|
}
|
|
83
193
|
if (options.includeTasks) {
|
|
84
194
|
try {
|
|
@@ -98,7 +208,92 @@ function loadRelatedStores(options) {
|
|
|
98
208
|
}
|
|
99
209
|
catch { /* ignore */ }
|
|
100
210
|
}
|
|
101
|
-
return data;
|
|
211
|
+
return { data, memoryCapture };
|
|
212
|
+
}
|
|
213
|
+
/** Count entries in a memory snapshot of either format. */
|
|
214
|
+
function countMemoryEntries(memory) {
|
|
215
|
+
if (!memory || typeof memory !== 'object')
|
|
216
|
+
return 0;
|
|
217
|
+
return Object.keys(memory.entries || {}).length;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Restore a memory snapshot into the live store and report what was actually
|
|
221
|
+
* written. #3573: the previous restore swallowed every write failure and then
|
|
222
|
+
* echoed the count recorded at save time.
|
|
223
|
+
*/
|
|
224
|
+
async function restoreMemorySnapshot(memory) {
|
|
225
|
+
const outcome = { restored: 0, failed: 0, errors: [] };
|
|
226
|
+
const isV2 = memory.format === MEMORY_SNAPSHOT_FORMAT;
|
|
227
|
+
// Legacy snapshots are the old store.json itself; v2 snapshots carry it
|
|
228
|
+
// under `legacy` only when one existed at save time.
|
|
229
|
+
const legacyJson = isV2 ? memory.legacy : memory;
|
|
230
|
+
if (legacyJson !== undefined) {
|
|
231
|
+
const memoryDir = join(getProjectCwd(), STORAGE_DIR, 'memory');
|
|
232
|
+
if (!existsSync(memoryDir))
|
|
233
|
+
mkdirRestricted(memoryDir);
|
|
234
|
+
writeFileRestricted(join(memoryDir, 'store.json'), JSON.stringify(legacyJson, null, 2));
|
|
235
|
+
}
|
|
236
|
+
const entries = memory.entries;
|
|
237
|
+
if (!entries)
|
|
238
|
+
return outcome;
|
|
239
|
+
let storeEntry;
|
|
240
|
+
let isValidProvenanceType = () => false;
|
|
241
|
+
try {
|
|
242
|
+
const mod = await import('../memory/memory-initializer.js');
|
|
243
|
+
storeEntry = mod.storeEntry;
|
|
244
|
+
try {
|
|
245
|
+
if (typeof mod.isValidProvenanceType === 'function')
|
|
246
|
+
isValidProvenanceType = mod.isValidProvenanceType;
|
|
247
|
+
}
|
|
248
|
+
catch { /* optional: without it, provenance is simply not carried over */ }
|
|
249
|
+
}
|
|
250
|
+
catch (e) {
|
|
251
|
+
outcome.failed = Object.keys(entries).length;
|
|
252
|
+
outcome.errors.push(`memory store unavailable: ${e.message}`);
|
|
253
|
+
return outcome;
|
|
254
|
+
}
|
|
255
|
+
for (const entry of Object.values(entries)) {
|
|
256
|
+
const key = entry.key || entry.id || '';
|
|
257
|
+
const value = entry.value || entry.content || '';
|
|
258
|
+
if (!key || !value) {
|
|
259
|
+
outcome.failed++;
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
try {
|
|
263
|
+
// A snapshot from another version may carry a provenance value this one
|
|
264
|
+
// rejects; drop it rather than fail an otherwise-good row.
|
|
265
|
+
const provenance = entry.provenanceType && entry.provenanceType !== 'unknown'
|
|
266
|
+
&& isValidProvenanceType(entry.provenanceType) ? entry.provenanceType : undefined;
|
|
267
|
+
const result = await storeEntry({
|
|
268
|
+
key,
|
|
269
|
+
value,
|
|
270
|
+
namespace: entry.namespace || 'restored',
|
|
271
|
+
upsert: true,
|
|
272
|
+
...(provenance ? { provenanceType: provenance } : {}),
|
|
273
|
+
});
|
|
274
|
+
if (result && result.success === false) {
|
|
275
|
+
outcome.failed++;
|
|
276
|
+
if (result.error && outcome.errors.length < 5)
|
|
277
|
+
outcome.errors.push(`${key}: ${result.error}`);
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
outcome.restored++;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
catch (e) {
|
|
284
|
+
outcome.failed++;
|
|
285
|
+
if (outcome.errors.length < 5)
|
|
286
|
+
outcome.errors.push(`${key}: ${e.message}`);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return outcome;
|
|
290
|
+
}
|
|
291
|
+
/** A session record is an object that carries at least one of its defining fields. */
|
|
292
|
+
function isSessionRecordLike(value) {
|
|
293
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
294
|
+
return false;
|
|
295
|
+
const v = value;
|
|
296
|
+
return 'data' in v || 'stats' in v || 'sessionId' in v || 'savedAt' in v;
|
|
102
297
|
}
|
|
103
298
|
export const sessionTools = [
|
|
104
299
|
{
|
|
@@ -126,9 +321,9 @@ export const sessionTools = [
|
|
|
126
321
|
if (!v.valid)
|
|
127
322
|
return { success: false, error: v.error };
|
|
128
323
|
}
|
|
129
|
-
const sessionId = `session-${Date.now()}-${
|
|
324
|
+
const sessionId = `session-${Date.now()}-${randomUUID().slice(0, 8)}`;
|
|
130
325
|
// Load related data based on options
|
|
131
|
-
const data = loadRelatedStores({
|
|
326
|
+
const { data, memoryCapture } = await loadRelatedStores({
|
|
132
327
|
includeMemory: input.includeMemory,
|
|
133
328
|
includeTasks: input.includeTasks,
|
|
134
329
|
includeAgents: input.includeAgents,
|
|
@@ -137,7 +332,7 @@ export const sessionTools = [
|
|
|
137
332
|
const stats = {
|
|
138
333
|
tasks: data.tasks ? Object.keys(data.tasks.tasks || {}).length : 0,
|
|
139
334
|
agents: data.agents ? Object.keys(data.agents.agents || {}).length : 0,
|
|
140
|
-
memoryEntries:
|
|
335
|
+
memoryEntries: countMemoryEntries(data.memory),
|
|
141
336
|
totalSize: 0,
|
|
142
337
|
};
|
|
143
338
|
const session = {
|
|
@@ -157,6 +352,7 @@ export const sessionTools = [
|
|
|
157
352
|
name: session.name,
|
|
158
353
|
savedAt: session.savedAt,
|
|
159
354
|
stats: session.stats,
|
|
355
|
+
memoryCapture,
|
|
160
356
|
path: getSessionPath(sessionId),
|
|
161
357
|
};
|
|
162
358
|
},
|
|
@@ -170,6 +366,9 @@ export const sessionTools = [
|
|
|
170
366
|
properties: {
|
|
171
367
|
sessionId: { type: 'string', description: 'Session ID to restore' },
|
|
172
368
|
name: { type: 'string', description: 'Session name to restore' },
|
|
369
|
+
restoreMemory: { type: 'boolean', description: 'Restore memory (default true)' },
|
|
370
|
+
restoreTasks: { type: 'boolean', description: 'Restore tasks (default true)' },
|
|
371
|
+
restoreAgents: { type: 'boolean', description: 'Restore agents (default true)' },
|
|
173
372
|
},
|
|
174
373
|
},
|
|
175
374
|
handler: async (input) => {
|
|
@@ -203,43 +402,19 @@ export const sessionTools = [
|
|
|
203
402
|
}
|
|
204
403
|
}
|
|
205
404
|
if (session) {
|
|
206
|
-
// Restore data to respective stores
|
|
207
|
-
//
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
mkdirRestricted(memoryDir);
|
|
212
|
-
writeFileRestricted(join(memoryDir, 'store.json'), JSON.stringify(session.data.memory, null, 2));
|
|
213
|
-
// Also populate active sql.js SQLite database so memory-tools can find entries
|
|
214
|
-
try {
|
|
215
|
-
const { storeEntry } = await import('../memory/memory-initializer.js');
|
|
216
|
-
const memoryData = session.data.memory;
|
|
217
|
-
if (memoryData.entries) {
|
|
218
|
-
for (const entry of Object.values(memoryData.entries)) {
|
|
219
|
-
const key = entry.key || entry.id || '';
|
|
220
|
-
const value = entry.value || entry.content || '';
|
|
221
|
-
if (key && value) {
|
|
222
|
-
await storeEntry({
|
|
223
|
-
key,
|
|
224
|
-
value,
|
|
225
|
-
namespace: entry.namespace || 'restored',
|
|
226
|
-
upsert: true,
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
catch {
|
|
233
|
-
// Legacy JSON restore is the fallback -- sql.js import may not be available
|
|
234
|
-
}
|
|
405
|
+
// Restore data to respective stores. audit_1776853149979: tighten
|
|
406
|
+
// perms on the restored stores too.
|
|
407
|
+
let memoryRestore;
|
|
408
|
+
if (input.restoreMemory !== false && session.data?.memory) {
|
|
409
|
+
memoryRestore = await restoreMemorySnapshot(session.data.memory);
|
|
235
410
|
}
|
|
236
|
-
if (session.data?.tasks) {
|
|
411
|
+
if (input.restoreTasks !== false && session.data?.tasks) {
|
|
237
412
|
const taskDir = join(getProjectCwd(), STORAGE_DIR, 'tasks');
|
|
238
413
|
if (!existsSync(taskDir))
|
|
239
414
|
mkdirRestricted(taskDir);
|
|
240
415
|
writeFileRestricted(join(taskDir, 'store.json'), JSON.stringify(session.data.tasks, null, 2));
|
|
241
416
|
}
|
|
242
|
-
if (session.data?.agents) {
|
|
417
|
+
if (input.restoreAgents !== false && session.data?.agents) {
|
|
243
418
|
const agentDir = join(getProjectCwd(), STORAGE_DIR, 'agents');
|
|
244
419
|
if (!existsSync(agentDir))
|
|
245
420
|
mkdirRestricted(agentDir);
|
|
@@ -249,8 +424,18 @@ export const sessionTools = [
|
|
|
249
424
|
sessionId: session.sessionId,
|
|
250
425
|
name: session.name,
|
|
251
426
|
restored: true,
|
|
427
|
+
restoredComponents: {
|
|
428
|
+
memory: input.restoreMemory !== false && !!session.data?.memory,
|
|
429
|
+
tasks: input.restoreTasks !== false && !!session.data?.tasks,
|
|
430
|
+
agents: input.restoreAgents !== false && !!session.data?.agents,
|
|
431
|
+
},
|
|
252
432
|
restoredAt: new Date().toISOString(),
|
|
253
433
|
stats: session.stats,
|
|
434
|
+
// #3573: counts actually written, not the count recorded at save time.
|
|
435
|
+
...(memoryRestore ? {
|
|
436
|
+
memoryRestore,
|
|
437
|
+
stats: { ...session.stats, memoryEntriesRestored: memoryRestore.restored },
|
|
438
|
+
} : {}),
|
|
254
439
|
};
|
|
255
440
|
}
|
|
256
441
|
return {
|
|
@@ -437,7 +622,7 @@ export const sessionTools = [
|
|
|
437
622
|
properties: {
|
|
438
623
|
sessionId: { type: 'string', description: 'Session ID to export' },
|
|
439
624
|
outputPath: { type: 'string', description: 'File path to write the export to (optional)' },
|
|
440
|
-
includeMemory: { type: 'boolean', description: 'Include the memory snapshot (
|
|
625
|
+
includeMemory: { type: 'boolean', description: 'Include the memory snapshot (default true)' },
|
|
441
626
|
},
|
|
442
627
|
required: ['sessionId'],
|
|
443
628
|
},
|
|
@@ -449,6 +634,14 @@ export const sessionTools = [
|
|
|
449
634
|
const session = loadSession(sessionId);
|
|
450
635
|
if (!session)
|
|
451
636
|
return { sessionId, error: 'Session not found' };
|
|
637
|
+
// Apply the explicit exclusion before either returning or writing the
|
|
638
|
+
// snapshot. The saved source remains intact for a later full restore.
|
|
639
|
+
if (input.includeMemory === false) {
|
|
640
|
+
if (session.data)
|
|
641
|
+
delete session.data.memory;
|
|
642
|
+
session.stats = { ...session.stats, memoryEntries: 0, totalSize: 0 };
|
|
643
|
+
session.stats.totalSize = Buffer.byteLength(JSON.stringify(session), 'utf-8');
|
|
644
|
+
}
|
|
452
645
|
let path = null;
|
|
453
646
|
const outputPath = input.outputPath ? String(input.outputPath) : null;
|
|
454
647
|
if (outputPath) {
|
|
@@ -467,30 +660,40 @@ export const sessionTools = [
|
|
|
467
660
|
// #1916: `ruflo session import <file>` referenced an unregistered
|
|
468
661
|
// `session_import` tool. Reads a session JSON and re-saves it locally.
|
|
469
662
|
name: 'session_import',
|
|
470
|
-
description: 'Import a session
|
|
663
|
+
description: 'Import a session (produced by session_export) into the local session store and optionally activate it. Pass either inputPath (a session JSON file) or data (the session record itself). Use when native Read is wrong because the file is a structured session record that must be re-registered (new id, stats recomputed) rather than just read. For reading the file, native Read is fine. Pair with session_export on the source.',
|
|
471
664
|
category: 'session',
|
|
472
665
|
inputSchema: {
|
|
473
666
|
type: 'object',
|
|
474
667
|
properties: {
|
|
475
|
-
inputPath: { type: 'string', description: 'Path to the session JSON file to import' },
|
|
668
|
+
inputPath: { type: 'string', description: 'Path to the session JSON file to import (or pass data)' },
|
|
669
|
+
data: { type: 'object', description: 'The session record itself, as written by session_export (or pass inputPath)' },
|
|
476
670
|
name: { type: 'string', description: 'Override the imported session name' },
|
|
477
|
-
activate: { type: 'boolean', description: '
|
|
671
|
+
activate: { type: 'boolean', description: 'Restore the imported session into the active stores' },
|
|
478
672
|
},
|
|
479
|
-
required: ['inputPath'],
|
|
480
673
|
},
|
|
481
674
|
handler: async (input) => {
|
|
482
|
-
const inputPath = String(input.inputPath ?? '');
|
|
483
|
-
if (!inputPath || !existsSync(inputPath))
|
|
484
|
-
return { error: `File not found: ${inputPath || '(empty)'}` };
|
|
485
675
|
let parsed;
|
|
486
|
-
|
|
487
|
-
parsed =
|
|
676
|
+
if (input.data !== undefined && input.data !== null) {
|
|
677
|
+
parsed = input.data;
|
|
488
678
|
}
|
|
489
|
-
|
|
490
|
-
|
|
679
|
+
else {
|
|
680
|
+
const inputPath = String(input.inputPath ?? '');
|
|
681
|
+
if (!inputPath)
|
|
682
|
+
return { error: 'Provide inputPath (a session JSON file) or data (a session record)' };
|
|
683
|
+
if (!existsSync(inputPath))
|
|
684
|
+
return { error: `File not found: ${inputPath}` };
|
|
685
|
+
try {
|
|
686
|
+
parsed = JSON.parse(readFileSync(inputPath, 'utf-8'));
|
|
687
|
+
}
|
|
688
|
+
catch (e) {
|
|
689
|
+
return { error: `Invalid session JSON: ${e.message}` };
|
|
690
|
+
}
|
|
491
691
|
}
|
|
492
|
-
|
|
493
|
-
|
|
692
|
+
if (!isSessionRecordLike(parsed)) {
|
|
693
|
+
return { error: 'Not a session record: expected an object produced by session export' };
|
|
694
|
+
}
|
|
695
|
+
const newId = `session-${Date.now()}-${randomUUID().slice(0, 8)}`;
|
|
696
|
+
const stats = { tasks: 0, agents: 0, memoryEntries: 0, totalSize: 0, ...(parsed.stats || {}) };
|
|
494
697
|
const session = {
|
|
495
698
|
sessionId: newId,
|
|
496
699
|
name: input.name ? String(input.name) : (parsed.name || 'imported-session'),
|
|
@@ -500,6 +703,15 @@ export const sessionTools = [
|
|
|
500
703
|
data: parsed.data,
|
|
501
704
|
};
|
|
502
705
|
saveSession(session);
|
|
706
|
+
let activated = false;
|
|
707
|
+
if (input.activate === true) {
|
|
708
|
+
const restore = sessionTools.find(tool => tool.name === 'session_restore');
|
|
709
|
+
const result = await restore.handler({ sessionId: newId });
|
|
710
|
+
if (result.restored !== true) {
|
|
711
|
+
return { sessionId: newId, activated: false, error: result.error || 'Imported session could not be restored' };
|
|
712
|
+
}
|
|
713
|
+
activated = true;
|
|
714
|
+
}
|
|
503
715
|
return {
|
|
504
716
|
sessionId: newId,
|
|
505
717
|
name: session.name,
|
|
@@ -509,7 +721,7 @@ export const sessionTools = [
|
|
|
509
721
|
tasksImported: stats.tasks,
|
|
510
722
|
memoryEntriesImported: stats.memoryEntries,
|
|
511
723
|
},
|
|
512
|
-
activated
|
|
724
|
+
activated,
|
|
513
725
|
};
|
|
514
726
|
},
|
|
515
727
|
},
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Shared input contract for learned patterns supplied by feedback callers. */
|
|
2
|
+
export declare const feedbackPatternsSchema: {
|
|
3
|
+
type: string;
|
|
4
|
+
items: {
|
|
5
|
+
type: string;
|
|
6
|
+
minLength: number;
|
|
7
|
+
maxLength: number;
|
|
8
|
+
};
|
|
9
|
+
maxItems: number;
|
|
10
|
+
description: string;
|
|
11
|
+
};
|
|
12
|
+
export declare function validateFeedbackPatterns(value: unknown): string[] | undefined;
|
|
13
|
+
//# sourceMappingURL=feedback-patterns.d.ts.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Shared input contract for learned patterns supplied by feedback callers. */
|
|
2
|
+
export const feedbackPatternsSchema = {
|
|
3
|
+
type: 'array', items: { type: 'string', minLength: 1, maxLength: 10_000 }, maxItems: 100,
|
|
4
|
+
description: 'Learned patterns to retain; successful tasks with quality >= 0.9 may create reusable skills',
|
|
5
|
+
};
|
|
6
|
+
export function validateFeedbackPatterns(value) {
|
|
7
|
+
if (value === undefined)
|
|
8
|
+
return undefined;
|
|
9
|
+
if (!Array.isArray(value) || value.length > 100 || value.some(item => typeof item !== 'string' || !item.trim() || item.length > 10_000)) {
|
|
10
|
+
throw new Error('patterns must be an array of at most 100 non-empty strings (maximum 10000 characters each)');
|
|
11
|
+
}
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=feedback-patterns.js.map
|
|
@@ -36,6 +36,8 @@ export declare function shouldDisableNativeBridge(platform?: NodeJS.Platform, en
|
|
|
36
36
|
export declare function shouldSuppressInitLog(msg: string): boolean;
|
|
37
37
|
/** Test seam: forget cached registries so a test can exercise a fresh open. */
|
|
38
38
|
export declare function _resetRegistryCacheForTest(): void;
|
|
39
|
+
/** Inject registry construction without requiring the optional native package in tests. */
|
|
40
|
+
export declare function __setMemoryBridgeRegistryFactoryForTests(factory: (() => any) | null): void;
|
|
39
41
|
/** #3196: the sibling store AgentDB owns next to a given sql.js database. */
|
|
40
42
|
export declare function siblingAgentDbPath(dbPath: string): string | null;
|
|
41
43
|
/**
|
|
@@ -271,6 +273,7 @@ export declare function bridgeAddEmbedding(id: string, embedding: number[], entr
|
|
|
271
273
|
}, dbPath?: string): Promise<boolean | null>;
|
|
272
274
|
/**
|
|
273
275
|
* Get a named controller from AgentDB v3 via ControllerRegistry.
|
|
276
|
+
* This borrowed handle must not be retained across shutdownBridge().
|
|
274
277
|
* Returns null if unavailable.
|
|
275
278
|
*/
|
|
276
279
|
export declare function bridgeGetController(name: string, dbPath?: string): Promise<any | null>;
|
|
@@ -292,13 +295,15 @@ export declare function bridgeListControllers(dbPath?: string): Promise<Array<{
|
|
|
292
295
|
export declare function isBridgeAvailable(dbPath?: string): Promise<boolean>;
|
|
293
296
|
/**
|
|
294
297
|
* Get the ControllerRegistry instance (for advanced consumers).
|
|
298
|
+
* This is a borrowed handle, valid only until shutdownBridge(). Callers must
|
|
299
|
+
* finish using it before requesting shutdown; bridge operations own their own
|
|
300
|
+
* leases. Prefer bridgeListControllers() for status inspection.
|
|
295
301
|
*/
|
|
296
302
|
export declare function getControllerRegistry(dbPath?: string): Promise<any | null>;
|
|
297
303
|
/**
|
|
298
304
|
* Why the bridge last declined a write, or null when it has not.
|
|
299
305
|
*
|
|
300
|
-
*
|
|
301
|
-
* initialised fine can still fail every write — a schema mismatch throws
|
|
306
|
+
* A bridge that initialised fine can still fail every write — a schema mismatch throws
|
|
302
307
|
* per-operation while the registry stays healthy — and that case is exactly
|
|
303
308
|
* the one worth reporting, since the caller then demotes to a fallback whose
|
|
304
309
|
* error message describes something else entirely.
|
|
@@ -306,7 +311,7 @@ export declare function getControllerRegistry(dbPath?: string): Promise<any | nu
|
|
|
306
311
|
* Callers that surface a degraded-path error should include this so the
|
|
307
312
|
* operator learns the cause instead of only the symptom.
|
|
308
313
|
*/
|
|
309
|
-
export declare function getBridgeFailureReason(): string | null;
|
|
314
|
+
export declare function getBridgeFailureReason(dbPath?: string): string | null;
|
|
310
315
|
/**
|
|
311
316
|
* Install a pre-initialized registry for deterministic bridge tests.
|
|
312
317
|
*
|
|
@@ -321,10 +326,8 @@ export declare function __setMemoryBridgeRegistryForTests(registry: any | null):
|
|
|
321
326
|
* Shutdown the bridge and release resources.
|
|
322
327
|
*
|
|
323
328
|
* The cached state is cleared unconditionally. Previously the reset lived
|
|
324
|
-
* inside `if (registryInstance)`, so it could not clear a FAILED init
|
|
325
|
-
*
|
|
326
|
-
* precisely when init failed. A process that latched `bridgeAvailable = false`
|
|
327
|
-
* therefore had no recovery path short of a restart.
|
|
329
|
+
* inside `if (registryInstance)`, so it could not clear a FAILED init. This
|
|
330
|
+
* also clears per-path failed promises, allowing a later retry.
|
|
328
331
|
*/
|
|
329
332
|
export declare function shutdownBridge(): Promise<void>;
|
|
330
333
|
/**
|
|
@@ -589,15 +592,7 @@ export declare function bridgeHierarchicalRecall(params: {
|
|
|
589
592
|
topK?: number;
|
|
590
593
|
includeExpired?: boolean;
|
|
591
594
|
}): Promise<any>;
|
|
592
|
-
/**
|
|
593
|
-
* Run memory consolidation.
|
|
594
|
-
*
|
|
595
|
-
* Real MemoryConsolidation API (agentdb alpha.10+):
|
|
596
|
-
* consolidate() → Promise<ConsolidationReport>
|
|
597
|
-
* ConsolidationReport = { episodicProcessed, semanticCreated, memoriesForgotten, ... }
|
|
598
|
-
* Stub API (fallback):
|
|
599
|
-
* consolidate() → { promoted, pruned, timestamp }
|
|
600
|
-
*/
|
|
595
|
+
/** Run consolidation when a real controller is available. */
|
|
601
596
|
export declare function bridgeConsolidate(params: {
|
|
602
597
|
minAge?: number;
|
|
603
598
|
maxEntries?: number;
|
|
@@ -612,10 +607,6 @@ export declare function bridgeBatchOperation(params: {
|
|
|
612
607
|
operation: string;
|
|
613
608
|
entries: any[];
|
|
614
609
|
}): Promise<any>;
|
|
615
|
-
/**
|
|
616
|
-
* Synthesize context from memories.
|
|
617
|
-
* ContextSynthesizer.synthesize is a static method that takes MemoryPattern[] (not a string).
|
|
618
|
-
*/
|
|
619
610
|
export declare function bridgeContextSynthesize(params: {
|
|
620
611
|
query: string;
|
|
621
612
|
maxEntries?: number;
|