gnosys 5.11.4 → 5.12.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/dist/cli.js +324 -5150
- package/dist/index.js +364 -235
- package/dist/lib/addCommand.d.ts +9 -0
- package/dist/lib/addCommand.js +103 -0
- package/dist/lib/addStructuredCommand.d.ts +16 -0
- package/dist/lib/addStructuredCommand.js +103 -0
- package/dist/lib/ambiguityCommand.d.ts +4 -0
- package/dist/lib/ambiguityCommand.js +36 -0
- package/dist/lib/apiKeyVault.d.ts +78 -0
- package/dist/lib/apiKeyVault.js +447 -0
- package/dist/lib/askCommand.d.ts +13 -0
- package/dist/lib/askCommand.js +145 -0
- package/dist/lib/audioExtract.js +4 -1
- package/dist/lib/auditCommand.d.ts +7 -0
- package/dist/lib/auditCommand.js +27 -0
- package/dist/lib/backupCommand.d.ts +6 -0
- package/dist/lib/backupCommand.js +54 -0
- package/dist/lib/bootstrapCommand.d.ts +15 -0
- package/dist/lib/bootstrapCommand.js +51 -0
- package/dist/lib/briefingCommand.d.ts +7 -0
- package/dist/lib/briefingCommand.js +92 -0
- package/dist/lib/centralizeCommand.d.ts +5 -0
- package/dist/lib/centralizeCommand.js +16 -0
- package/dist/lib/chatCommand.d.ts +12 -0
- package/dist/lib/chatCommand.js +46 -0
- package/dist/lib/checkCommand.d.ts +4 -0
- package/dist/lib/checkCommand.js +133 -0
- package/dist/lib/clientReadOverlay.d.ts +27 -0
- package/dist/lib/clientReadOverlay.js +73 -0
- package/dist/lib/clientReadResolve.d.ts +32 -0
- package/dist/lib/clientReadResolve.js +84 -0
- package/dist/lib/commitContextCommand.d.ts +9 -0
- package/dist/lib/commitContextCommand.js +142 -0
- package/dist/lib/config.d.ts +43 -3
- package/dist/lib/config.js +58 -57
- package/dist/lib/configCommand.d.ts +10 -0
- package/dist/lib/configCommand.js +321 -0
- package/dist/lib/connectCommand.d.ts +8 -0
- package/dist/lib/connectCommand.js +19 -0
- package/dist/lib/db.d.ts +52 -0
- package/dist/lib/db.js +169 -1
- package/dist/lib/dearchiveCommand.d.ts +7 -0
- package/dist/lib/dearchiveCommand.js +41 -0
- package/dist/lib/discoverCommand.d.ts +9 -0
- package/dist/lib/discoverCommand.js +87 -0
- package/dist/lib/doctorCommand.d.ts +6 -0
- package/dist/lib/doctorCommand.js +256 -0
- package/dist/lib/dream.d.ts +42 -2
- package/dist/lib/dream.js +290 -30
- package/dist/lib/dreamCommand.d.ts +10 -0
- package/dist/lib/dreamCommand.js +195 -0
- package/dist/lib/dreamLaunchd.d.ts +2 -0
- package/dist/lib/dreamLaunchd.js +72 -0
- package/dist/lib/dreamLogCommand.d.ts +10 -0
- package/dist/lib/dreamLogCommand.js +58 -0
- package/dist/lib/dreamReport.d.ts +7 -0
- package/dist/lib/dreamReport.js +114 -0
- package/dist/lib/dreamRunLog.d.ts +121 -0
- package/dist/lib/dreamRunLog.js +212 -0
- package/dist/lib/embeddings.js +3 -0
- package/dist/lib/exportCommand.d.ts +18 -0
- package/dist/lib/exportCommand.js +101 -0
- package/dist/lib/fsearchCommand.d.ts +8 -0
- package/dist/lib/fsearchCommand.js +44 -0
- package/dist/lib/graphCommand.d.ts +4 -0
- package/dist/lib/graphCommand.js +68 -0
- package/dist/lib/helperGenerateCommand.d.ts +5 -0
- package/dist/lib/helperGenerateCommand.js +27 -0
- package/dist/lib/historyCommand.d.ts +5 -0
- package/dist/lib/historyCommand.js +51 -0
- package/dist/lib/hybridSearchCommand.d.ts +12 -0
- package/dist/lib/hybridSearchCommand.js +95 -0
- package/dist/lib/importCommand.d.ts +16 -0
- package/dist/lib/importCommand.js +89 -0
- package/dist/lib/importProjectCommand.d.ts +6 -0
- package/dist/lib/importProjectCommand.js +43 -0
- package/dist/lib/ingestCommand.d.ts +13 -0
- package/dist/lib/ingestCommand.js +95 -0
- package/dist/lib/installOutput.d.ts +36 -0
- package/dist/lib/installOutput.js +55 -0
- package/dist/lib/lensCommand.d.ts +20 -0
- package/dist/lib/lensCommand.js +61 -0
- package/dist/lib/lensing.d.ts +1 -0
- package/dist/lib/lensing.js +50 -9
- package/dist/lib/linksCommand.d.ts +7 -0
- package/dist/lib/linksCommand.js +48 -0
- package/dist/lib/listCommand.d.ts +8 -0
- package/dist/lib/listCommand.js +74 -0
- package/dist/lib/llm.d.ts +1 -1
- package/dist/lib/llm.js +26 -8
- package/dist/lib/localDiskCheck.d.ts +17 -0
- package/dist/lib/localDiskCheck.js +54 -0
- package/dist/lib/machineConfig.d.ts +11 -1
- package/dist/lib/machineConfig.js +16 -0
- package/dist/lib/machineRegistry.d.ts +61 -0
- package/dist/lib/machineRegistry.js +80 -0
- package/dist/lib/maintainCommand.d.ts +8 -0
- package/dist/lib/maintainCommand.js +34 -0
- package/dist/lib/masterLease.d.ts +20 -0
- package/dist/lib/masterLease.js +68 -0
- package/dist/lib/migrateCommand.d.ts +7 -0
- package/dist/lib/migrateCommand.js +158 -0
- package/dist/lib/migrateDbCommand.d.ts +9 -0
- package/dist/lib/migrateDbCommand.js +94 -0
- package/dist/lib/modelValidation.d.ts +5 -0
- package/dist/lib/modelValidation.js +27 -0
- package/dist/lib/openrouterTiers.d.ts +29 -0
- package/dist/lib/openrouterTiers.js +113 -0
- package/dist/lib/prefCommand.d.ts +10 -0
- package/dist/lib/prefCommand.js +118 -0
- package/dist/lib/projectsCommand.d.ts +8 -0
- package/dist/lib/projectsCommand.js +131 -0
- package/dist/lib/readCommand.d.ts +7 -0
- package/dist/lib/readCommand.js +62 -0
- package/dist/lib/recall.d.ts +3 -0
- package/dist/lib/recall.js +19 -4
- package/dist/lib/recallCommand.d.ts +11 -0
- package/dist/lib/recallCommand.js +112 -0
- package/dist/lib/reflectCommand.d.ts +8 -0
- package/dist/lib/reflectCommand.js +61 -0
- package/dist/lib/reindexCommand.d.ts +4 -0
- package/dist/lib/reindexCommand.js +34 -0
- package/dist/lib/reindexGraphCommand.d.ts +4 -0
- package/dist/lib/reindexGraphCommand.js +12 -0
- package/dist/lib/reinforceCommand.d.ts +8 -0
- package/dist/lib/reinforceCommand.js +40 -0
- package/dist/lib/remote.d.ts +5 -1
- package/dist/lib/remote.js +5 -1
- package/dist/lib/remoteWizard.d.ts +24 -5
- package/dist/lib/remoteWizard.js +308 -319
- package/dist/lib/restoreCommand.d.ts +5 -0
- package/dist/lib/restoreCommand.js +35 -0
- package/dist/lib/sandboxStartCommand.d.ts +6 -0
- package/dist/lib/sandboxStartCommand.js +25 -0
- package/dist/lib/sandboxStatusCommand.d.ts +4 -0
- package/dist/lib/sandboxStatusCommand.js +24 -0
- package/dist/lib/sandboxStopCommand.d.ts +4 -0
- package/dist/lib/sandboxStopCommand.js +21 -0
- package/dist/lib/searchCommand.d.ts +9 -0
- package/dist/lib/searchCommand.js +90 -0
- package/dist/lib/semanticSearchCommand.d.ts +8 -0
- package/dist/lib/semanticSearchCommand.js +52 -0
- package/dist/lib/setup/configSetRender.js +2 -0
- package/dist/lib/setup/providerGlyphs.d.ts +19 -0
- package/dist/lib/setup/providerGlyphs.js +42 -0
- package/dist/lib/setup/remoteRender.d.ts +31 -1
- package/dist/lib/setup/remoteRender.js +95 -4
- package/dist/lib/setup/sections/providers.d.ts +17 -0
- package/dist/lib/setup/sections/providers.js +255 -0
- package/dist/lib/setup/sections/routing.d.ts +2 -6
- package/dist/lib/setup/sections/routing.js +33 -85
- package/dist/lib/setup/sections/taskRoutingEditor.d.ts +17 -0
- package/dist/lib/setup/sections/taskRoutingEditor.js +149 -0
- package/dist/lib/setup/summary.d.ts +9 -0
- package/dist/lib/setup/summary.js +51 -37
- package/dist/lib/setup/ui/status.d.ts +1 -0
- package/dist/lib/setup/ui/status.js +2 -0
- package/dist/lib/setup.d.ts +108 -3
- package/dist/lib/setup.js +762 -157
- package/dist/lib/setupKeys.d.ts +42 -0
- package/dist/lib/setupKeys.js +564 -0
- package/dist/lib/setupRemoteCommand.d.ts +4 -0
- package/dist/lib/setupRemoteCommand.js +28 -0
- package/dist/lib/setupRemotePullCommand.d.ts +5 -0
- package/dist/lib/setupRemotePullCommand.js +52 -0
- package/dist/lib/setupRemotePushCommand.d.ts +5 -0
- package/dist/lib/setupRemotePushCommand.js +57 -0
- package/dist/lib/setupRemoteResolveCommand.d.ts +4 -0
- package/dist/lib/setupRemoteResolveCommand.js +48 -0
- package/dist/lib/setupRemoteStatusCommand.d.ts +4 -0
- package/dist/lib/setupRemoteStatusCommand.js +73 -0
- package/dist/lib/setupRemoteSyncCommand.d.ts +6 -0
- package/dist/lib/setupRemoteSyncCommand.js +65 -0
- package/dist/lib/setupSyncProjectsCommand.d.ts +4 -0
- package/dist/lib/setupSyncProjectsCommand.js +292 -0
- package/dist/lib/staleCommand.d.ts +8 -0
- package/dist/lib/staleCommand.js +34 -0
- package/dist/lib/statsCommand.d.ts +6 -0
- package/dist/lib/statsCommand.js +142 -0
- package/dist/lib/statusCommand.d.ts +18 -0
- package/dist/lib/statusCommand.js +250 -0
- package/dist/lib/storesCommand.d.ts +2 -0
- package/dist/lib/storesCommand.js +4 -0
- package/dist/lib/syncClient.d.ts +47 -0
- package/dist/lib/syncClient.js +212 -0
- package/dist/lib/syncCommand.d.ts +6 -0
- package/dist/lib/syncCommand.js +57 -0
- package/dist/lib/syncDoctorCommand.d.ts +5 -0
- package/dist/lib/syncDoctorCommand.js +100 -0
- package/dist/lib/syncIngest.d.ts +19 -0
- package/dist/lib/syncIngest.js +152 -0
- package/dist/lib/syncIngestLaunchd.d.ts +8 -0
- package/dist/lib/syncIngestLaunchd.js +93 -0
- package/dist/lib/syncIngestStartup.d.ts +5 -0
- package/dist/lib/syncIngestStartup.js +29 -0
- package/dist/lib/syncIngestSystemd.d.ts +10 -0
- package/dist/lib/syncIngestSystemd.js +97 -0
- package/dist/lib/syncIngestTimer.d.ts +8 -0
- package/dist/lib/syncIngestTimer.js +27 -0
- package/dist/lib/syncIngestTimerCommand.d.ts +7 -0
- package/dist/lib/syncIngestTimerCommand.js +83 -0
- package/dist/lib/syncLock.d.ts +6 -0
- package/dist/lib/syncLock.js +74 -0
- package/dist/lib/syncSnapshot.d.ts +30 -0
- package/dist/lib/syncSnapshot.js +184 -0
- package/dist/lib/syncStaging.d.ts +81 -0
- package/dist/lib/syncStaging.js +239 -0
- package/dist/lib/tagsAddCommand.d.ts +8 -0
- package/dist/lib/tagsAddCommand.js +18 -0
- package/dist/lib/tagsCommand.d.ts +4 -0
- package/dist/lib/tagsCommand.js +16 -0
- package/dist/lib/timelineCommand.d.ts +7 -0
- package/dist/lib/timelineCommand.js +49 -0
- package/dist/lib/traceCommand.d.ts +6 -0
- package/dist/lib/traceCommand.js +39 -0
- package/dist/lib/traverseCommand.d.ts +6 -0
- package/dist/lib/traverseCommand.js +58 -0
- package/dist/lib/updateCommand.d.ts +13 -0
- package/dist/lib/updateCommand.js +67 -0
- package/dist/lib/updateStatusCommand.d.ts +5 -0
- package/dist/lib/updateStatusCommand.js +38 -0
- package/dist/lib/webAddCommand.d.ts +8 -0
- package/dist/lib/webAddCommand.js +55 -0
- package/dist/lib/webBuildCommand.d.ts +10 -0
- package/dist/lib/webBuildCommand.js +65 -0
- package/dist/lib/webBuildIndexCommand.d.ts +8 -0
- package/dist/lib/webBuildIndexCommand.js +37 -0
- package/dist/lib/webIngestCommand.d.ts +11 -0
- package/dist/lib/webIngestCommand.js +51 -0
- package/dist/lib/webInitCommand.d.ts +9 -0
- package/dist/lib/webInitCommand.js +167 -0
- package/dist/lib/webRemoveCommand.d.ts +5 -0
- package/dist/lib/webRemoveCommand.js +41 -0
- package/dist/lib/webStatusCommand.d.ts +5 -0
- package/dist/lib/webStatusCommand.js +94 -0
- package/dist/lib/webUpdateCommand.d.ts +7 -0
- package/dist/lib/webUpdateCommand.js +72 -0
- package/dist/lib/workingSetCommand.d.ts +6 -0
- package/dist/lib/workingSetCommand.js +37 -0
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -58,6 +58,10 @@ import { setPreference, getPreference, getAllPreferences, deletePreference, KNOW
|
|
|
58
58
|
import { syncRules, generateRulesBlock } from "./lib/rulesGen.js";
|
|
59
59
|
import { federatedSearch, detectAmbiguity, generateBriefing, generateAllBriefings, getWorkingSet, formatWorkingSet, detectCurrentProject } from "./lib/federated.js";
|
|
60
60
|
import { generatePortfolio, formatPortfolioCompact, formatPortfolioMarkdown, generateStatusPrompt } from "./lib/portfolio.js";
|
|
61
|
+
import { applyPendingOverlay, mergeOverlayDiscoverResults, mergeOverlaySearchResults, pendingAddToDbMemory, } from "./lib/clientReadOverlay.js";
|
|
62
|
+
import { readMachineConfig } from "./lib/machineConfig.js";
|
|
63
|
+
import { getConfiguredRemotePath } from "./lib/remote.js";
|
|
64
|
+
import { closeClientReadContext, openClientReadContext, } from "./lib/syncClient.js";
|
|
61
65
|
// Initialize resolver (discovers all layered stores)
|
|
62
66
|
const resolver = new GnosysResolver();
|
|
63
67
|
let config = DEFAULT_CONFIG;
|
|
@@ -136,14 +140,30 @@ let askEngine = null;
|
|
|
136
140
|
let gnosysDb = null;
|
|
137
141
|
/** v3.0: Central DB at ~/.gnosys/gnosys.db */
|
|
138
142
|
let centralDb = null;
|
|
139
|
-
/** v2.0: Dream scheduler (idle-time consolidation) */
|
|
140
|
-
let dreamScheduler = null;
|
|
141
143
|
// ─── Multi-Project Support ───────────────────────────────────────────────
|
|
142
144
|
// Each tool call can optionally pass a `projectRoot` to target a specific
|
|
143
145
|
// project's .gnosys store. This is STATELESS — no race conditions when
|
|
144
146
|
// multiple agents call tools in parallel.
|
|
145
147
|
/** Common Zod schema fragment for projectRoot parameter */
|
|
146
148
|
const projectRootParam = z.string().optional().describe("Optional project root path for multi-project support. When provided, this tool operates on projectRoot/.gnosys instead of the default store. Use gnosys_stores to see all available stores.");
|
|
149
|
+
function applyClientReadToCentralDb(localDb) {
|
|
150
|
+
if (!localDb?.isAvailable()) {
|
|
151
|
+
return { centralDb: localDb, clientRead: null };
|
|
152
|
+
}
|
|
153
|
+
const mc = readMachineConfig();
|
|
154
|
+
if (!mc?.remote.enabled || mc.remote.role !== "client") {
|
|
155
|
+
return { centralDb: localDb, clientRead: null };
|
|
156
|
+
}
|
|
157
|
+
const masterPath = getConfiguredRemotePath(localDb);
|
|
158
|
+
if (!masterPath)
|
|
159
|
+
return { centralDb: localDb, clientRead: null };
|
|
160
|
+
const clientRead = openClientReadContext(localDb, masterPath, mc.machineId);
|
|
161
|
+
return { centralDb: clientRead.db, clientRead };
|
|
162
|
+
}
|
|
163
|
+
function releaseClientReadFromContext(ctx) {
|
|
164
|
+
if (ctx.clientRead)
|
|
165
|
+
closeClientReadContext(ctx.clientRead);
|
|
166
|
+
}
|
|
147
167
|
async function resolveToolContext(projectRoot) {
|
|
148
168
|
if (!projectRoot) {
|
|
149
169
|
// Default context — use module-level state
|
|
@@ -155,6 +175,7 @@ async function resolveToolContext(projectRoot) {
|
|
|
155
175
|
const identity = await readProjectIdentity(parentDir);
|
|
156
176
|
projectId = identity?.projectId || null;
|
|
157
177
|
}
|
|
178
|
+
const applied = applyClientReadToCentralDb(centralDb);
|
|
158
179
|
return {
|
|
159
180
|
resolver,
|
|
160
181
|
store: writeTarget?.store || null,
|
|
@@ -162,8 +183,9 @@ async function resolveToolContext(projectRoot) {
|
|
|
162
183
|
config,
|
|
163
184
|
search,
|
|
164
185
|
gnosysDb,
|
|
165
|
-
centralDb,
|
|
186
|
+
centralDb: applied.centralDb,
|
|
166
187
|
projectId,
|
|
188
|
+
clientRead: applied.clientRead,
|
|
167
189
|
};
|
|
168
190
|
}
|
|
169
191
|
// Scoped context — resolve for this specific project
|
|
@@ -192,6 +214,7 @@ async function resolveToolContext(projectRoot) {
|
|
|
192
214
|
// Removed: new GnosysDB(scopedStorePath) which created an empty
|
|
193
215
|
// gnosys.db in the project's .gnosys/ directory.
|
|
194
216
|
}
|
|
217
|
+
const applied = applyClientReadToCentralDb(centralDb);
|
|
195
218
|
return {
|
|
196
219
|
resolver: scopedResolver,
|
|
197
220
|
store: scopedWriteTarget?.store || null,
|
|
@@ -199,8 +222,9 @@ async function resolveToolContext(projectRoot) {
|
|
|
199
222
|
config: scopedConfig,
|
|
200
223
|
search: scopedSearch,
|
|
201
224
|
gnosysDb: scopedDb,
|
|
202
|
-
centralDb,
|
|
225
|
+
centralDb: applied.centralDb,
|
|
203
226
|
projectId,
|
|
227
|
+
clientRead: applied.clientRead,
|
|
204
228
|
};
|
|
205
229
|
}
|
|
206
230
|
/**
|
|
@@ -244,50 +268,65 @@ regTool("gnosys_discover", "Discover relevant memories by describing what you're
|
|
|
244
268
|
projectRoot: projectRootParam,
|
|
245
269
|
}, async ({ query, limit, projectRoot }) => {
|
|
246
270
|
const ctx = await resolveToolContext(projectRoot);
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
271
|
+
try {
|
|
272
|
+
// v2.0 DB-backed fast path
|
|
273
|
+
if (ctx.centralDb?.isAvailable() && ctx.centralDb?.isMigrated()) {
|
|
274
|
+
const lim = limit || 20;
|
|
275
|
+
let results = ctx.centralDb.discoverFts(query, lim);
|
|
276
|
+
if (ctx.clientRead?.pendingOverlay.length) {
|
|
277
|
+
results = mergeOverlayDiscoverResults(results, ctx.clientRead.pendingOverlay, query, lim, (p) => ({
|
|
278
|
+
id: p.id,
|
|
279
|
+
title: p.title,
|
|
280
|
+
relevance: "",
|
|
281
|
+
rank: 0,
|
|
282
|
+
project_id: p.project_id,
|
|
283
|
+
}));
|
|
284
|
+
}
|
|
285
|
+
if (results.length === 0) {
|
|
286
|
+
return {
|
|
287
|
+
content: [{ type: "text", text: `No memories found for "${query}". Try different keywords.` }],
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
const formatted = results
|
|
291
|
+
.map((r) => `**${r.title}**\n ID: ${r.id}${r.relevance ? `\n Relevance: ${r.relevance}` : ""}`)
|
|
292
|
+
.join("\n\n");
|
|
293
|
+
return {
|
|
294
|
+
content: [{ type: "text", text: `Found ${results.length} relevant memories for "${query}":\n\n${formatted}\n\nUse gnosys_read to load any of these.` }],
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
// v1.x legacy path
|
|
298
|
+
if (!ctx.search) {
|
|
299
|
+
return {
|
|
300
|
+
content: [{ type: "text", text: "Search index not initialized." }],
|
|
301
|
+
isError: true,
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
const results = ctx.search.discover(query, limit || 20);
|
|
250
305
|
if (results.length === 0) {
|
|
251
306
|
return {
|
|
252
|
-
content: [
|
|
307
|
+
content: [
|
|
308
|
+
{
|
|
309
|
+
type: "text",
|
|
310
|
+
text: `No memories found for "${query}". Try different keywords or use gnosys_search for full-text search.`,
|
|
311
|
+
},
|
|
312
|
+
],
|
|
253
313
|
};
|
|
254
314
|
}
|
|
255
315
|
const formatted = results
|
|
256
|
-
.map((r) => `**${r.title}**\n
|
|
316
|
+
.map((r) => `**${r.title}**\n Path: ${r.relative_path}${r.relevance ? `\n Relevance: ${r.relevance}` : ""}`)
|
|
257
317
|
.join("\n\n");
|
|
258
|
-
return {
|
|
259
|
-
content: [{ type: "text", text: `Found ${results.length} relevant memories for "${query}":\n\n${formatted}\n\nUse gnosys_read to load any of these.` }],
|
|
260
|
-
};
|
|
261
|
-
}
|
|
262
|
-
// v1.x legacy path
|
|
263
|
-
if (!ctx.search) {
|
|
264
|
-
return {
|
|
265
|
-
content: [{ type: "text", text: "Search index not initialized." }],
|
|
266
|
-
isError: true,
|
|
267
|
-
};
|
|
268
|
-
}
|
|
269
|
-
const results = ctx.search.discover(query, limit || 20);
|
|
270
|
-
if (results.length === 0) {
|
|
271
318
|
return {
|
|
272
319
|
content: [
|
|
273
320
|
{
|
|
274
321
|
type: "text",
|
|
275
|
-
text: `
|
|
322
|
+
text: `Found ${results.length} relevant memories for "${query}":\n\n${formatted}\n\nUse gnosys_read to load any of these.`,
|
|
276
323
|
},
|
|
277
324
|
],
|
|
278
325
|
};
|
|
279
326
|
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
return {
|
|
284
|
-
content: [
|
|
285
|
-
{
|
|
286
|
-
type: "text",
|
|
287
|
-
text: `Found ${results.length} relevant memories for "${query}":\n\n${formatted}\n\nUse gnosys_read to load any of these.`,
|
|
288
|
-
},
|
|
289
|
-
],
|
|
290
|
-
};
|
|
327
|
+
finally {
|
|
328
|
+
releaseClientReadFromContext(ctx);
|
|
329
|
+
}
|
|
291
330
|
});
|
|
292
331
|
// ─── Tool: gnosys_read ───────────────────────────────────────────────────
|
|
293
332
|
regTool("gnosys_read", "Read a specific memory. Accepts a memory ID (e.g., 'arch-012') or layer-prefixed path (e.g., 'project:decisions/why-not-rag.md'). Without a prefix, searches all stores in precedence order.", {
|
|
@@ -295,62 +334,72 @@ regTool("gnosys_read", "Read a specific memory. Accepts a memory ID (e.g., 'arch
|
|
|
295
334
|
projectRoot: projectRootParam,
|
|
296
335
|
}, async ({ path: memPath, projectRoot }) => {
|
|
297
336
|
const ctx = await resolveToolContext(projectRoot);
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
337
|
+
try {
|
|
338
|
+
// v2.0 DB-backed fast path: try reading by memory ID from gnosys.db first
|
|
339
|
+
if (ctx.centralDb?.isAvailable() && ctx.centralDb?.isMigrated()) {
|
|
340
|
+
let dbMem = ctx.centralDb.getMemory(memPath);
|
|
341
|
+
if (!dbMem && ctx.clientRead?.pendingOverlay.length) {
|
|
342
|
+
const pending = ctx.clientRead.pendingOverlay.find((p) => p.id === memPath);
|
|
343
|
+
if (pending)
|
|
344
|
+
dbMem = pendingAddToDbMemory(pending);
|
|
345
|
+
}
|
|
346
|
+
if (dbMem) {
|
|
347
|
+
const tags = dbMem.tags || "[]";
|
|
348
|
+
const headerLines = [
|
|
349
|
+
`---`,
|
|
350
|
+
`id: ${dbMem.id}`,
|
|
351
|
+
`title: '${dbMem.title}'`,
|
|
352
|
+
`category: ${dbMem.category}`,
|
|
353
|
+
`tags: ${tags}`,
|
|
354
|
+
`relevance: ${dbMem.relevance}`,
|
|
355
|
+
`author: ${dbMem.author}`,
|
|
356
|
+
`authority: ${dbMem.authority}`,
|
|
357
|
+
`confidence: ${dbMem.confidence}`,
|
|
358
|
+
`status: ${dbMem.status}`,
|
|
359
|
+
`tier: ${dbMem.tier}`,
|
|
360
|
+
`created: '${dbMem.created}'`,
|
|
361
|
+
`modified: '${dbMem.modified}'`,
|
|
362
|
+
];
|
|
363
|
+
if (dbMem.source_file) {
|
|
364
|
+
headerLines.push(`source_file: ${dbMem.source_file}${dbMem.source_page != null ? ` (page ${Number(dbMem.source_page)})` : ""}`);
|
|
365
|
+
}
|
|
366
|
+
if (dbMem.source_path)
|
|
367
|
+
headerLines.push(`source_path: ${dbMem.source_path}`);
|
|
368
|
+
headerLines.push(`---`);
|
|
369
|
+
const header = headerLines.join("\n");
|
|
370
|
+
return {
|
|
371
|
+
content: [{ type: "text", text: `[Source: gnosys.db]\n\n${header}\n\n${dbMem.content}` }],
|
|
372
|
+
};
|
|
320
373
|
}
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
374
|
+
// Not found in db — fall through to legacy path
|
|
375
|
+
}
|
|
376
|
+
// v1.x legacy path
|
|
377
|
+
const memory = await ctx.resolver.readMemory(memPath);
|
|
378
|
+
if (!memory) {
|
|
325
379
|
return {
|
|
326
|
-
content: [{ type: "text", text: `
|
|
380
|
+
content: [{ type: "text", text: `Memory not found: ${memPath}` }],
|
|
381
|
+
isError: true,
|
|
327
382
|
};
|
|
328
383
|
}
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
384
|
+
let raw;
|
|
385
|
+
try {
|
|
386
|
+
raw = await fs.readFile(memory.filePath, "utf-8");
|
|
387
|
+
}
|
|
388
|
+
catch (err) {
|
|
389
|
+
return { content: [{ type: "text", text: formatMcpError("reading memory", err) }], isError: true };
|
|
390
|
+
}
|
|
334
391
|
return {
|
|
335
|
-
content: [
|
|
336
|
-
|
|
392
|
+
content: [
|
|
393
|
+
{
|
|
394
|
+
type: "text",
|
|
395
|
+
text: `[Source: ${memory.sourceLabel}]\n\n${raw}`,
|
|
396
|
+
},
|
|
397
|
+
],
|
|
337
398
|
};
|
|
338
399
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
raw = await fs.readFile(memory.filePath, "utf-8");
|
|
342
|
-
}
|
|
343
|
-
catch (err) {
|
|
344
|
-
return { content: [{ type: "text", text: formatMcpError("reading memory", err) }], isError: true };
|
|
400
|
+
finally {
|
|
401
|
+
releaseClientReadFromContext(ctx);
|
|
345
402
|
}
|
|
346
|
-
return {
|
|
347
|
-
content: [
|
|
348
|
-
{
|
|
349
|
-
type: "text",
|
|
350
|
-
text: `[Source: ${memory.sourceLabel}]\n\n${raw}`,
|
|
351
|
-
},
|
|
352
|
-
],
|
|
353
|
-
};
|
|
354
403
|
});
|
|
355
404
|
// ─── Tool: gnosys_search ─────────────────────────────────────────────────
|
|
356
405
|
regTool("gnosys_search", "Search memories by keyword across all stores. Returns matching file paths with relevance snippets.", {
|
|
@@ -359,50 +408,65 @@ regTool("gnosys_search", "Search memories by keyword across all stores. Returns
|
|
|
359
408
|
projectRoot: projectRootParam,
|
|
360
409
|
}, async ({ query, limit, projectRoot }) => {
|
|
361
410
|
const ctx = await resolveToolContext(projectRoot);
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
411
|
+
try {
|
|
412
|
+
// v2.0 DB-backed fast path
|
|
413
|
+
if (ctx.centralDb?.isAvailable() && ctx.centralDb?.isMigrated()) {
|
|
414
|
+
const lim = limit || 20;
|
|
415
|
+
let results = ctx.centralDb.searchFts(query, lim);
|
|
416
|
+
if (ctx.clientRead?.pendingOverlay.length) {
|
|
417
|
+
results = mergeOverlaySearchResults(results, ctx.clientRead.pendingOverlay, query, lim, (p) => ({
|
|
418
|
+
id: p.id,
|
|
419
|
+
title: p.title,
|
|
420
|
+
snippet: p.content.substring(0, 200),
|
|
421
|
+
rank: 0,
|
|
422
|
+
project_id: p.project_id,
|
|
423
|
+
}));
|
|
424
|
+
}
|
|
425
|
+
if (results.length === 0) {
|
|
426
|
+
return {
|
|
427
|
+
content: [{ type: "text", text: `No results for "${query}". Try different keywords.` }],
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
const formatted = results
|
|
431
|
+
.map((r) => `**${r.title}** (${r.id})\n${r.snippet.replace(/>>>/g, "**").replace(/<<</g, "**")}`)
|
|
432
|
+
.join("\n\n");
|
|
433
|
+
return {
|
|
434
|
+
content: [{ type: "text", text: `Found ${results.length} results for "${query}":\n\n${formatted}` }],
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
// v1.x legacy path
|
|
438
|
+
if (!ctx.search) {
|
|
439
|
+
return {
|
|
440
|
+
content: [{ type: "text", text: "Search index not initialized." }],
|
|
441
|
+
isError: true,
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
const results = ctx.search.search(query, limit || 20);
|
|
365
445
|
if (results.length === 0) {
|
|
366
446
|
return {
|
|
367
|
-
content: [
|
|
447
|
+
content: [
|
|
448
|
+
{
|
|
449
|
+
type: "text",
|
|
450
|
+
text: `No results for "${query}". Try different keywords or use gnosys_discover.`,
|
|
451
|
+
},
|
|
452
|
+
],
|
|
368
453
|
};
|
|
369
454
|
}
|
|
370
455
|
const formatted = results
|
|
371
|
-
.map((r) => `**${r.title}** (${r.
|
|
456
|
+
.map((r) => `**${r.title}** (${r.relative_path})\n${r.snippet.replace(/>>>/g, "**").replace(/<<</g, "**")}`)
|
|
372
457
|
.join("\n\n");
|
|
373
|
-
return {
|
|
374
|
-
content: [{ type: "text", text: `Found ${results.length} results for "${query}":\n\n${formatted}` }],
|
|
375
|
-
};
|
|
376
|
-
}
|
|
377
|
-
// v1.x legacy path
|
|
378
|
-
if (!ctx.search) {
|
|
379
|
-
return {
|
|
380
|
-
content: [{ type: "text", text: "Search index not initialized." }],
|
|
381
|
-
isError: true,
|
|
382
|
-
};
|
|
383
|
-
}
|
|
384
|
-
const results = ctx.search.search(query, limit || 20);
|
|
385
|
-
if (results.length === 0) {
|
|
386
458
|
return {
|
|
387
459
|
content: [
|
|
388
460
|
{
|
|
389
461
|
type: "text",
|
|
390
|
-
text: `
|
|
462
|
+
text: `Found ${results.length} results for "${query}":\n\n${formatted}`,
|
|
391
463
|
},
|
|
392
464
|
],
|
|
393
465
|
};
|
|
394
466
|
}
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
return {
|
|
399
|
-
content: [
|
|
400
|
-
{
|
|
401
|
-
type: "text",
|
|
402
|
-
text: `Found ${results.length} results for "${query}":\n\n${formatted}`,
|
|
403
|
-
},
|
|
404
|
-
],
|
|
405
|
-
};
|
|
467
|
+
finally {
|
|
468
|
+
releaseClientReadFromContext(ctx);
|
|
469
|
+
}
|
|
406
470
|
});
|
|
407
471
|
// ─── Tool: gnosys_list ───────────────────────────────────────────────────
|
|
408
472
|
regTool("gnosys_list", "List memories across all stores, optionally filtered by category, tag, or store layer.", {
|
|
@@ -413,41 +477,76 @@ regTool("gnosys_list", "List memories across all stores, optionally filtered by
|
|
|
413
477
|
projectRoot: projectRootParam,
|
|
414
478
|
}, async ({ category, tag, store: storeFilter, status, projectRoot }) => {
|
|
415
479
|
const ctx = await resolveToolContext(projectRoot);
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
480
|
+
try {
|
|
481
|
+
// DB-first: read from central DB instead of scanning markdown files
|
|
482
|
+
const db = ctx.centralDb;
|
|
483
|
+
if (db?.isAvailable()) {
|
|
484
|
+
let dbMemories = status === "active" || !status
|
|
485
|
+
? db.getActiveMemories()
|
|
486
|
+
: db.getAllMemories();
|
|
487
|
+
if (ctx.clientRead?.pendingOverlay.length && (status === "active" || !status)) {
|
|
488
|
+
dbMemories = applyPendingOverlay(dbMemories, ctx.clientRead.pendingOverlay, new Set()).memories;
|
|
489
|
+
}
|
|
490
|
+
// Apply filters on DB results
|
|
491
|
+
if (status && status !== "active") {
|
|
492
|
+
dbMemories = dbMemories.filter((m) => m.status === status);
|
|
493
|
+
}
|
|
494
|
+
if (storeFilter) {
|
|
495
|
+
dbMemories = dbMemories.filter((m) => m.scope === storeFilter);
|
|
496
|
+
}
|
|
497
|
+
if (category) {
|
|
498
|
+
dbMemories = dbMemories.filter((m) => m.category === category);
|
|
499
|
+
}
|
|
500
|
+
if (tag) {
|
|
501
|
+
dbMemories = dbMemories.filter((m) => {
|
|
502
|
+
try {
|
|
503
|
+
const parsed = JSON.parse(m.tags || "[]");
|
|
504
|
+
const tagList = Array.isArray(parsed)
|
|
505
|
+
? parsed
|
|
506
|
+
: Object.values(parsed).flat();
|
|
507
|
+
return tagList.includes(tag);
|
|
508
|
+
}
|
|
509
|
+
catch {
|
|
510
|
+
return false;
|
|
511
|
+
}
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
// Filter by project if we have a project ID (so scoped queries only see their project)
|
|
515
|
+
if (ctx.projectId && !storeFilter) {
|
|
516
|
+
dbMemories = dbMemories.filter((m) => m.project_id === ctx.projectId || m.scope !== "project");
|
|
517
|
+
}
|
|
518
|
+
const lines = dbMemories.map((m) => `- [${m.scope}] **${m.title}** (${m.category}/${m.id}) [${m.status}]`);
|
|
519
|
+
return {
|
|
520
|
+
content: [
|
|
521
|
+
{
|
|
522
|
+
type: "text",
|
|
523
|
+
text: lines.length > 0
|
|
524
|
+
? `${lines.length} memories:\n\n${lines.join("\n")}`
|
|
525
|
+
: "No memories match the filter.",
|
|
526
|
+
},
|
|
527
|
+
],
|
|
528
|
+
};
|
|
425
529
|
}
|
|
530
|
+
// Fallback: read from markdown files if central DB unavailable
|
|
531
|
+
let memories = await ctx.resolver.getAllMemories();
|
|
426
532
|
if (storeFilter) {
|
|
427
|
-
|
|
533
|
+
memories = memories.filter((m) => m.sourceLayer === storeFilter || m.sourceLabel === storeFilter);
|
|
428
534
|
}
|
|
429
535
|
if (category) {
|
|
430
|
-
|
|
536
|
+
memories = memories.filter((m) => m.frontmatter.category === category);
|
|
431
537
|
}
|
|
432
538
|
if (tag) {
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
: Object.values(parsed).flat();
|
|
439
|
-
return tagList.includes(tag);
|
|
440
|
-
}
|
|
441
|
-
catch {
|
|
442
|
-
return false;
|
|
443
|
-
}
|
|
539
|
+
memories = memories.filter((m) => {
|
|
540
|
+
const tags = Array.isArray(m.frontmatter.tags)
|
|
541
|
+
? m.frontmatter.tags
|
|
542
|
+
: Object.values(m.frontmatter.tags).flat();
|
|
543
|
+
return tags.includes(tag);
|
|
444
544
|
});
|
|
445
545
|
}
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
dbMemories = dbMemories.filter((m) => m.project_id === ctx.projectId || m.scope !== "project");
|
|
546
|
+
if (status) {
|
|
547
|
+
memories = memories.filter((m) => m.frontmatter.status === status);
|
|
449
548
|
}
|
|
450
|
-
const lines =
|
|
549
|
+
const lines = memories.map((m) => `- [${m.sourceLabel}] **${m.frontmatter.title}** (${m.relativePath}) [${m.frontmatter.status}]`);
|
|
451
550
|
return {
|
|
452
551
|
content: [
|
|
453
552
|
{
|
|
@@ -459,36 +558,9 @@ regTool("gnosys_list", "List memories across all stores, optionally filtered by
|
|
|
459
558
|
],
|
|
460
559
|
};
|
|
461
560
|
}
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
if (storeFilter) {
|
|
465
|
-
memories = memories.filter((m) => m.sourceLayer === storeFilter || m.sourceLabel === storeFilter);
|
|
466
|
-
}
|
|
467
|
-
if (category) {
|
|
468
|
-
memories = memories.filter((m) => m.frontmatter.category === category);
|
|
469
|
-
}
|
|
470
|
-
if (tag) {
|
|
471
|
-
memories = memories.filter((m) => {
|
|
472
|
-
const tags = Array.isArray(m.frontmatter.tags)
|
|
473
|
-
? m.frontmatter.tags
|
|
474
|
-
: Object.values(m.frontmatter.tags).flat();
|
|
475
|
-
return tags.includes(tag);
|
|
476
|
-
});
|
|
477
|
-
}
|
|
478
|
-
if (status) {
|
|
479
|
-
memories = memories.filter((m) => m.frontmatter.status === status);
|
|
561
|
+
finally {
|
|
562
|
+
releaseClientReadFromContext(ctx);
|
|
480
563
|
}
|
|
481
|
-
const lines = memories.map((m) => `- [${m.sourceLabel}] **${m.frontmatter.title}** (${m.relativePath}) [${m.frontmatter.status}]`);
|
|
482
|
-
return {
|
|
483
|
-
content: [
|
|
484
|
-
{
|
|
485
|
-
type: "text",
|
|
486
|
-
text: lines.length > 0
|
|
487
|
-
? `${lines.length} memories:\n\n${lines.join("\n")}`
|
|
488
|
-
: "No memories match the filter.",
|
|
489
|
-
},
|
|
490
|
-
],
|
|
491
|
-
};
|
|
492
564
|
});
|
|
493
565
|
// ─── Tool: gnosys_add ────────────────────────────────────────────────────
|
|
494
566
|
regTool("gnosys_add", "Add a new memory. Accepts raw text — an LLM structures it into an atomic memory. Writes to the project store by default. Use store='personal' for cross-project knowledge, or store='global' to explicitly write to shared org knowledge.", {
|
|
@@ -1905,8 +1977,6 @@ regTool("gnosys_dream", "Run a Dream Mode cycle — idle-time consolidation that
|
|
|
1905
1977
|
],
|
|
1906
1978
|
};
|
|
1907
1979
|
}
|
|
1908
|
-
// Record activity to reset idle timer (if scheduler is running)
|
|
1909
|
-
dreamScheduler?.recordActivity();
|
|
1910
1980
|
const dreamConfig = {
|
|
1911
1981
|
enabled: true,
|
|
1912
1982
|
idleMinutes: 0, // Run immediately (manual trigger)
|
|
@@ -1924,6 +1994,34 @@ regTool("gnosys_dream", "Run a Dream Mode cycle — idle-time consolidation that
|
|
|
1924
1994
|
const report = await engine.dream((phase, detail) => {
|
|
1925
1995
|
console.error(`[dream:${phase}] ${detail}`);
|
|
1926
1996
|
});
|
|
1997
|
+
const { appendDreamRun } = await import("./lib/dreamRunLog.js");
|
|
1998
|
+
appendDreamRun({
|
|
1999
|
+
...report,
|
|
2000
|
+
id: report.id || `dream-${Date.now()}`,
|
|
2001
|
+
trigger: report.trigger || "manual",
|
|
2002
|
+
status: report.aborted ? "aborted" : report.errors.length > 0 ? "failed" : "completed",
|
|
2003
|
+
machine: report.machine || { hostname: "unknown" },
|
|
2004
|
+
provider: report.provider || dreamConfig.provider,
|
|
2005
|
+
phases: report.phases || [],
|
|
2006
|
+
llmCalls: report.llmCalls || [],
|
|
2007
|
+
totals: report.totals || {
|
|
2008
|
+
llmCallsMade: 0,
|
|
2009
|
+
llmCallsSkipped: 0,
|
|
2010
|
+
estimatedInputTokens: 0,
|
|
2011
|
+
estimatedOutputTokens: 0,
|
|
2012
|
+
estimatedCostUsd: 0,
|
|
2013
|
+
},
|
|
2014
|
+
effectiveness: report.effectiveness || {
|
|
2015
|
+
usefulOutputScore: 0,
|
|
2016
|
+
costPerUsefulOutput: null,
|
|
2017
|
+
decaysApplied: report.decayUpdated,
|
|
2018
|
+
summariesGenerated: report.summariesGenerated,
|
|
2019
|
+
summariesUpdated: report.summariesUpdated,
|
|
2020
|
+
reviewSuggestions: report.reviewSuggestions.length,
|
|
2021
|
+
relationshipsDiscovered: report.relationshipsDiscovered,
|
|
2022
|
+
},
|
|
2023
|
+
gates: [],
|
|
2024
|
+
});
|
|
1927
2025
|
return {
|
|
1928
2026
|
content: [
|
|
1929
2027
|
{
|
|
@@ -2072,8 +2170,6 @@ regResource("gnosys_recall", "gnosys://recall", {
|
|
|
2072
2170
|
priority: 1, // Highest priority — always inject
|
|
2073
2171
|
},
|
|
2074
2172
|
}, async () => {
|
|
2075
|
-
// Record activity for dream scheduler (this fires on every turn)
|
|
2076
|
-
dreamScheduler?.recordActivity();
|
|
2077
2173
|
if (!search) {
|
|
2078
2174
|
return {
|
|
2079
2175
|
contents: [
|
|
@@ -2086,23 +2182,31 @@ regResource("gnosys_recall", "gnosys://recall", {
|
|
|
2086
2182
|
};
|
|
2087
2183
|
}
|
|
2088
2184
|
const storePath = resolver.getWriteTarget()?.store.getStorePath() || "";
|
|
2089
|
-
const
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2185
|
+
const applied = applyClientReadToCentralDb(centralDb);
|
|
2186
|
+
try {
|
|
2187
|
+
const result = await recall("*", {
|
|
2188
|
+
limit: config.recall?.maxMemories || 8,
|
|
2189
|
+
search,
|
|
2190
|
+
resolver,
|
|
2191
|
+
storePath,
|
|
2192
|
+
recallConfig: config.recall,
|
|
2193
|
+
gnosysDb: applied.centralDb || undefined,
|
|
2194
|
+
pendingOverlay: applied.clientRead?.pendingOverlay,
|
|
2195
|
+
});
|
|
2196
|
+
return {
|
|
2197
|
+
contents: [
|
|
2198
|
+
{
|
|
2199
|
+
uri: "gnosys://recall",
|
|
2200
|
+
mimeType: "text/markdown",
|
|
2201
|
+
text: formatRecall(result),
|
|
2202
|
+
},
|
|
2203
|
+
],
|
|
2204
|
+
};
|
|
2205
|
+
}
|
|
2206
|
+
finally {
|
|
2207
|
+
if (applied.clientRead)
|
|
2208
|
+
closeClientReadContext(applied.clientRead);
|
|
2209
|
+
}
|
|
2106
2210
|
});
|
|
2107
2211
|
// ─── Tool: gnosys_recall (query-specific fallback) ──────────────────────
|
|
2108
2212
|
// For hosts that don't support MCP Resources, or when the agent wants to
|
|
@@ -2118,28 +2222,34 @@ regTool("gnosys_recall", "Fast memory recall — inject relevant memories as con
|
|
|
2118
2222
|
}, async ({ query, limit, traceId, aggressive, projectRoot }) => {
|
|
2119
2223
|
try {
|
|
2120
2224
|
const ctx = await resolveToolContext(projectRoot);
|
|
2121
|
-
|
|
2225
|
+
try {
|
|
2226
|
+
if (!ctx.search) {
|
|
2227
|
+
return {
|
|
2228
|
+
content: [{ type: "text", text: "<gnosys: no-strong-recall-needed>" }],
|
|
2229
|
+
};
|
|
2230
|
+
}
|
|
2231
|
+
const storePath = ctx.resolver.getWriteTarget()?.store.getStorePath() || "";
|
|
2232
|
+
const recallConfig = {
|
|
2233
|
+
...ctx.config.recall,
|
|
2234
|
+
...(aggressive !== undefined ? { aggressive } : {}),
|
|
2235
|
+
};
|
|
2236
|
+
const result = await recall(query, {
|
|
2237
|
+
limit: Math.min(limit || recallConfig.maxMemories, 15),
|
|
2238
|
+
search: ctx.search,
|
|
2239
|
+
resolver: ctx.resolver,
|
|
2240
|
+
storePath,
|
|
2241
|
+
traceId,
|
|
2242
|
+
recallConfig,
|
|
2243
|
+
gnosysDb: ctx.centralDb || undefined,
|
|
2244
|
+
pendingOverlay: ctx.clientRead?.pendingOverlay,
|
|
2245
|
+
});
|
|
2122
2246
|
return {
|
|
2123
|
-
content: [{ type: "text", text:
|
|
2247
|
+
content: [{ type: "text", text: formatRecall(result) }],
|
|
2124
2248
|
};
|
|
2125
2249
|
}
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
...(aggressive !== undefined ? { aggressive } : {}),
|
|
2130
|
-
};
|
|
2131
|
-
const result = await recall(query, {
|
|
2132
|
-
limit: Math.min(limit || recallConfig.maxMemories, 15),
|
|
2133
|
-
search: ctx.search,
|
|
2134
|
-
resolver: ctx.resolver,
|
|
2135
|
-
storePath,
|
|
2136
|
-
traceId,
|
|
2137
|
-
recallConfig,
|
|
2138
|
-
gnosysDb: ctx.centralDb || undefined,
|
|
2139
|
-
});
|
|
2140
|
-
return {
|
|
2141
|
-
content: [{ type: "text", text: formatRecall(result) }],
|
|
2142
|
-
};
|
|
2250
|
+
finally {
|
|
2251
|
+
releaseClientReadFromContext(ctx);
|
|
2252
|
+
}
|
|
2143
2253
|
}
|
|
2144
2254
|
catch (err) {
|
|
2145
2255
|
return { content: [{ type: "text", text: formatMcpError("recalling memories", err) }], isError: true };
|
|
@@ -2383,28 +2493,34 @@ regTool("gnosys_federated_search", "Search across all scopes (project → user
|
|
|
2383
2493
|
projectRoot: z.string().optional().describe("Project root directory for context detection"),
|
|
2384
2494
|
includeGlobal: z.boolean().optional().describe("Include global-scope memories (default: true)"),
|
|
2385
2495
|
}, async ({ query, limit, projectRoot, includeGlobal }) => {
|
|
2386
|
-
|
|
2387
|
-
|
|
2496
|
+
const ctx = await resolveToolContext(projectRoot);
|
|
2497
|
+
try {
|
|
2498
|
+
if (!ctx.centralDb?.isAvailable()) {
|
|
2499
|
+
return { content: [{ type: "text", text: "Central DB not available. Run gnosys_init first." }], isError: true };
|
|
2500
|
+
}
|
|
2501
|
+
// Auto-detect current project
|
|
2502
|
+
const projectId = await detectCurrentProject(ctx.centralDb, projectRoot || undefined);
|
|
2503
|
+
const results = federatedSearch(ctx.centralDb, query, {
|
|
2504
|
+
limit: limit || 20,
|
|
2505
|
+
projectId,
|
|
2506
|
+
includeGlobal: includeGlobal !== false,
|
|
2507
|
+
});
|
|
2508
|
+
if (results.length === 0) {
|
|
2509
|
+
return { content: [{ type: "text", text: `No results for "${query}" across any scope.` }] };
|
|
2510
|
+
}
|
|
2511
|
+
const lines = results.map((r, i) => {
|
|
2512
|
+
const projectLabel = r.projectName ? ` [${r.projectName}]` : "";
|
|
2513
|
+
const boostLabel = r.boosts.length > 0 ? ` (${r.boosts.join(", ")})` : "";
|
|
2514
|
+
return `${i + 1}. **${r.title}** (${r.category})${projectLabel}\n scope: ${r.scope} | score: ${r.score.toFixed(4)}${boostLabel}\n ${r.snippet}`;
|
|
2515
|
+
});
|
|
2516
|
+
const contextNote = projectId ? `Context: project ${projectId}` : "Context: no project detected";
|
|
2517
|
+
return {
|
|
2518
|
+
content: [{ type: "text", text: `${contextNote}\n\n${lines.join("\n\n")}` }],
|
|
2519
|
+
};
|
|
2388
2520
|
}
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
const results = federatedSearch(centralDb, query, {
|
|
2392
|
-
limit: limit || 20,
|
|
2393
|
-
projectId,
|
|
2394
|
-
includeGlobal: includeGlobal !== false,
|
|
2395
|
-
});
|
|
2396
|
-
if (results.length === 0) {
|
|
2397
|
-
return { content: [{ type: "text", text: `No results for "${query}" across any scope.` }] };
|
|
2521
|
+
finally {
|
|
2522
|
+
releaseClientReadFromContext(ctx);
|
|
2398
2523
|
}
|
|
2399
|
-
const lines = results.map((r, i) => {
|
|
2400
|
-
const projectLabel = r.projectName ? ` [${r.projectName}]` : "";
|
|
2401
|
-
const boostLabel = r.boosts.length > 0 ? ` (${r.boosts.join(", ")})` : "";
|
|
2402
|
-
return `${i + 1}. **${r.title}** (${r.category})${projectLabel}\n scope: ${r.scope} | score: ${r.score.toFixed(4)}${boostLabel}\n ${r.snippet}`;
|
|
2403
|
-
});
|
|
2404
|
-
const contextNote = projectId ? `Context: project ${projectId}` : "Context: no project detected";
|
|
2405
|
-
return {
|
|
2406
|
-
content: [{ type: "text", text: `${contextNote}\n\n${lines.join("\n\n")}` }],
|
|
2407
|
-
};
|
|
2408
2524
|
});
|
|
2409
2525
|
// ─── Tool: gnosys_detect_ambiguity ──────────────────────────────────────
|
|
2410
2526
|
regTool("gnosys_detect_ambiguity", "Check if a query matches memories in multiple projects. Use before write operations to confirm the target project when ambiguity exists.", {
|
|
@@ -2500,7 +2616,8 @@ regTool("gnosys_remote_status", "Check the status of remote sync (multi-machine)
|
|
|
2500
2616
|
if (!localDb.isAvailable()) {
|
|
2501
2617
|
return { content: [{ type: "text", text: "Local DB not available." }], isError: true };
|
|
2502
2618
|
}
|
|
2503
|
-
const
|
|
2619
|
+
const { getConfiguredRemotePath } = await import("./lib/remote.js");
|
|
2620
|
+
const remotePath = getConfiguredRemotePath(localDb);
|
|
2504
2621
|
if (!remotePath) {
|
|
2505
2622
|
return {
|
|
2506
2623
|
content: [{
|
|
@@ -2509,6 +2626,14 @@ regTool("gnosys_remote_status", "Check the status of remote sync (multi-machine)
|
|
|
2509
2626
|
}],
|
|
2510
2627
|
};
|
|
2511
2628
|
}
|
|
2629
|
+
const { readMachineConfig } = await import("./lib/machineConfig.js");
|
|
2630
|
+
if (readMachineConfig()?.remote.role) {
|
|
2631
|
+
const { getV13SyncStatus } = await import("./lib/syncClient.js");
|
|
2632
|
+
const v13 = getV13SyncStatus(localDb);
|
|
2633
|
+
return {
|
|
2634
|
+
content: [{ type: "text", text: JSON.stringify(v13, null, 2) }],
|
|
2635
|
+
};
|
|
2636
|
+
}
|
|
2512
2637
|
const { RemoteSync } = await import("./lib/remote.js");
|
|
2513
2638
|
const sync = new RemoteSync(localDb, remotePath);
|
|
2514
2639
|
const status = await sync.getStatus();
|
|
@@ -2887,11 +3012,10 @@ async function initHeavyDeps() {
|
|
|
2887
3012
|
const embCount = embeddings.hasEmbeddings() ? embeddings.count() : 0;
|
|
2888
3013
|
console.error(`Hybrid search: ${embCount > 0 ? `ready (${embCount} embeddings)` : "available (run gnosys_reindex to build embeddings)"}`);
|
|
2889
3014
|
console.error(`Ask engine: ${askEngine.isLLMAvailable ? `ready (${askEngine.providerName}/${askEngine.modelName})` : "disabled (configure LLM provider)"}`);
|
|
2890
|
-
// Dream mode
|
|
3015
|
+
// Dream mode scheduling is machine-level now (`gnosys dream run --scheduled`
|
|
3016
|
+
// via launchd). MCP may still run manual dream tool calls, but it no longer
|
|
3017
|
+
// owns an idle timer per connection.
|
|
2891
3018
|
if (gnosysDb && config.dream?.enabled) {
|
|
2892
|
-
const { GnosysDreamEngine, DreamScheduler } = await import("./lib/dream.js");
|
|
2893
|
-
const dreamEngine = new GnosysDreamEngine(gnosysDb, config, config.dream);
|
|
2894
|
-
dreamScheduler = new DreamScheduler(dreamEngine, config.dream);
|
|
2895
3019
|
// Layer 3: probe the dream provider if this machine is the dream node.
|
|
2896
3020
|
try {
|
|
2897
3021
|
const designated = gnosysDb.getDreamMachineId();
|
|
@@ -2933,9 +3057,8 @@ async function initHeavyDeps() {
|
|
|
2933
3057
|
}
|
|
2934
3058
|
}
|
|
2935
3059
|
catch {
|
|
2936
|
-
// Probe failed — non-fatal.
|
|
3060
|
+
// Probe failed — non-fatal.
|
|
2937
3061
|
}
|
|
2938
|
-
dreamScheduler.start();
|
|
2939
3062
|
const designated = gnosysDb.getDreamMachineId();
|
|
2940
3063
|
const localId = gnosysDb.getMeta("machine_id");
|
|
2941
3064
|
if (!designated) {
|
|
@@ -2945,7 +3068,7 @@ async function initHeavyDeps() {
|
|
|
2945
3068
|
console.error(`Dream Mode: enabled — designated to '${designated}'. This machine (${localId || "?"}) will not dream.`);
|
|
2946
3069
|
}
|
|
2947
3070
|
else {
|
|
2948
|
-
console.error(`Dream Mode: enabled on this machine (
|
|
3071
|
+
console.error(`Dream Mode: enabled on this machine (scheduled outside MCP via launchd, max ${config.dream.maxRuntimeMinutes}min)`);
|
|
2949
3072
|
}
|
|
2950
3073
|
}
|
|
2951
3074
|
else {
|
|
@@ -2985,6 +3108,12 @@ export async function startMcpServer() {
|
|
|
2985
3108
|
console.error("Gnosys MCP server starting.");
|
|
2986
3109
|
console.error("Active stores:");
|
|
2987
3110
|
console.error(resolver.getSummary());
|
|
3111
|
+
// v13: background ingest sweep on master-role MCP startup (non-blocking).
|
|
3112
|
+
void import("./lib/syncIngestStartup.js")
|
|
3113
|
+
.then(({ maybeRunStartupIngestSweep }) => maybeRunStartupIngestSweep())
|
|
3114
|
+
.catch((err) => {
|
|
3115
|
+
console.error(`[sync] Startup ingest sweep failed: ${err instanceof Error ? err.message : err}`);
|
|
3116
|
+
});
|
|
2988
3117
|
// Initialize search from the first writable store. Everything in this
|
|
2989
3118
|
// block is FAST — opening the search index + tag registry + loading
|
|
2990
3119
|
// gnosys.json. The slow stuff (LLM providers, transformers embeddings,
|