gnosys 5.13.0 → 5.13.1
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 +9 -2
- package/dist/lib/db.d.ts +6 -0
- package/dist/lib/db.js +9 -0
- package/dist/lib/dreamCommand.js +12 -3
- package/dist/lib/dreamRunLog.d.ts +10 -0
- package/dist/lib/dreamRunLog.js +14 -0
- package/dist/lib/recall.js +43 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1173,9 +1173,16 @@ dreamCmd
|
|
|
1173
1173
|
.option("--force", "Run even if this machine is not the designated dream node")
|
|
1174
1174
|
.option("--scheduled", "Run as the machine-level scheduler (applies night/idle/cooldown gates)")
|
|
1175
1175
|
.option("--json", "Output raw JSON report")
|
|
1176
|
-
.action(async (opts)
|
|
1176
|
+
.action(async function (opts) {
|
|
1177
|
+
// v5.13.1: the bare `gnosys dream` parent declares the same flags, and
|
|
1178
|
+
// commander resolves parent-declared options onto the PARENT during
|
|
1179
|
+
// `dream run --scheduled` — so run's opts arrived empty and every
|
|
1180
|
+
// scheduled invocation (the launchd agent's exact command line) ran as
|
|
1181
|
+
// a MANUAL dream, bypassing the night/idle/dreamworthiness gates.
|
|
1182
|
+
// Merge parent opts (same pattern as `dream log` below).
|
|
1183
|
+
const merged = { ...(this.parent?.opts() ?? {}), ...opts };
|
|
1177
1184
|
const { runDreamCommand } = await import("./lib/dreamCommand.js");
|
|
1178
|
-
await runDreamCommand(
|
|
1185
|
+
await runDreamCommand(merged);
|
|
1179
1186
|
});
|
|
1180
1187
|
// `gnosys dream log` — view recent dream runs from audit_log
|
|
1181
1188
|
dreamCmd
|
package/dist/lib/db.d.ts
CHANGED
|
@@ -330,6 +330,12 @@ export declare class GnosysDB {
|
|
|
330
330
|
tags: string;
|
|
331
331
|
content: string;
|
|
332
332
|
}>;
|
|
333
|
+
/**
|
|
334
|
+
* v5.13.1: top active memories for wildcard recall. The gnosys://recall
|
|
335
|
+
* resource asks for "the best memories, no query" — rank by how often a
|
|
336
|
+
* memory proved useful (reinforcement), then confidence, then recency.
|
|
337
|
+
*/
|
|
338
|
+
getTopActiveMemories(limit?: number): DbMemory[];
|
|
333
339
|
/** v5.13.0: memories whose embedding column is NULL (embedding-health check). */
|
|
334
340
|
countMemoriesMissingEmbedding(): number;
|
|
335
341
|
updateEmbedding(id: string, embedding: Buffer): void;
|
package/dist/lib/db.js
CHANGED
|
@@ -1264,6 +1264,15 @@ export class GnosysDB {
|
|
|
1264
1264
|
const lim = limit && limit > 0 ? ` LIMIT ${Math.floor(limit)}` : "";
|
|
1265
1265
|
return this.withRecovery(() => this.prep(`SELECT id, title, relevance, tags, content FROM memories ${where} ORDER BY modified DESC${lim}`).all());
|
|
1266
1266
|
}
|
|
1267
|
+
/**
|
|
1268
|
+
* v5.13.1: top active memories for wildcard recall. The gnosys://recall
|
|
1269
|
+
* resource asks for "the best memories, no query" — rank by how often a
|
|
1270
|
+
* memory proved useful (reinforcement), then confidence, then recency.
|
|
1271
|
+
*/
|
|
1272
|
+
getTopActiveMemories(limit = 15) {
|
|
1273
|
+
return this.withRecovery(() => this.prep(`SELECT ${LEAN_MEMORY_PROJECTION} FROM memories WHERE tier = 'active' AND status = 'active'
|
|
1274
|
+
ORDER BY reinforcement_count DESC, confidence DESC, modified DESC LIMIT ?`).all(limit));
|
|
1275
|
+
}
|
|
1267
1276
|
/** v5.13.0: memories whose embedding column is NULL (embedding-health check). */
|
|
1268
1277
|
countMemoriesMissingEmbedding() {
|
|
1269
1278
|
return this.withRecovery(() => {
|
package/dist/lib/dreamCommand.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { GnosysResolver } from "./resolver.js";
|
|
2
2
|
import { GnosysDB } from "./db.js";
|
|
3
3
|
import { loadConfig } from "./config.js";
|
|
4
|
-
import { acquireDreamLock, appendDreamRun,
|
|
4
|
+
import { acquireDreamLock, appendDreamRun, countDreamworthyChanges, createSkipRunRecord, getSystemIdleMinutes, isInsideNightWindow, readDreamState, } from "./dreamRunLog.js";
|
|
5
5
|
export async function runDreamCommand(opts) {
|
|
6
6
|
const resolver = new GnosysResolver();
|
|
7
7
|
await resolver.resolve();
|
|
@@ -15,7 +15,14 @@ export async function runDreamCommand(opts) {
|
|
|
15
15
|
const { getMachineId } = await import("./remote.js");
|
|
16
16
|
const storePath = stores[0].path;
|
|
17
17
|
const cfg = await loadConfig(storePath);
|
|
18
|
-
|
|
18
|
+
// v5.13.1: Dream operates on the brain — the central DB — not the
|
|
19
|
+
// project store the command happened to be run from. Opening
|
|
20
|
+
// stores[0].path meant `gnosys dream run` failed with "run gnosys
|
|
21
|
+
// migrate" from any project directory whose local .gnosys has no
|
|
22
|
+
// migrated DB, even though the central DB was fine (and the designation
|
|
23
|
+
// check below already used openCentral). DbClass alias keeps the lazy
|
|
24
|
+
// import pattern.
|
|
25
|
+
const db = DbClass.openCentral();
|
|
19
26
|
if (!db.isAvailable() || !db.isMigrated()) {
|
|
20
27
|
console.error("Dream Mode requires gnosys.db (v2.0). Run 'gnosys migrate' first.");
|
|
21
28
|
process.exit(1);
|
|
@@ -114,7 +121,9 @@ export async function runDreamCommand(opts) {
|
|
|
114
121
|
gates.push({ name: "system-idle", passed: true, details: { idleMinutes } });
|
|
115
122
|
const state = readDreamState();
|
|
116
123
|
const memories = db.getActiveMemories();
|
|
117
|
-
|
|
124
|
+
// v5.13.1: max(date-watermark changes, count delta) — self-heals
|
|
125
|
+
// stale/polluted state files and catches bulk imports/deletions.
|
|
126
|
+
const changed = countDreamworthyChanges(memories, state);
|
|
118
127
|
const lastRunMs = state.lastSuccessfulRunAt ? Date.parse(state.lastSuccessfulRunAt) : 0;
|
|
119
128
|
const hoursSince = lastRunMs ? (Date.now() - lastRunMs) / 3_600_000 : Infinity;
|
|
120
129
|
const enoughMemories = changed >= cfg.dream.minNewMemoriesToDream;
|
|
@@ -107,6 +107,16 @@ export declare function memoryWatermark(memories: DbMemory[]): {
|
|
|
107
107
|
maxModified: string | undefined;
|
|
108
108
|
};
|
|
109
109
|
export declare function countChangedMemoriesSince(memories: DbMemory[], sinceIso?: string): number;
|
|
110
|
+
/**
|
|
111
|
+
* v5.13.1: total change signal for the dreamworthiness gate. The date-only
|
|
112
|
+
* watermark misses bulk imports, deletions, and stale/polluted state files
|
|
113
|
+
* (a test-suite bug wrote lastMemoryCount=5 against 1,200+ memory brains,
|
|
114
|
+
* suppressing scheduled dreaming indefinitely — fixed in v5.13.0, but
|
|
115
|
+
* already-polluted machines need the gate itself to self-heal). A mismatch
|
|
116
|
+
* between the stored memory count and reality IS change — take the max of
|
|
117
|
+
* both signals.
|
|
118
|
+
*/
|
|
119
|
+
export declare function countDreamworthyChanges(memories: DbMemory[], state: DreamState): number;
|
|
110
120
|
export declare function isInsideNightWindow(now: Date, schedule: DreamConfig["schedule"]): boolean;
|
|
111
121
|
export declare function getSystemIdleMinutes(): number | null;
|
|
112
122
|
export declare function createSkipRunRecord(input: {
|
package/dist/lib/dreamRunLog.js
CHANGED
|
@@ -175,6 +175,20 @@ export function countChangedMemoriesSince(memories, sinceIso) {
|
|
|
175
175
|
return memories.length;
|
|
176
176
|
return memories.filter((m) => Date.parse(m.modified || m.created) > sinceMs).length;
|
|
177
177
|
}
|
|
178
|
+
/**
|
|
179
|
+
* v5.13.1: total change signal for the dreamworthiness gate. The date-only
|
|
180
|
+
* watermark misses bulk imports, deletions, and stale/polluted state files
|
|
181
|
+
* (a test-suite bug wrote lastMemoryCount=5 against 1,200+ memory brains,
|
|
182
|
+
* suppressing scheduled dreaming indefinitely — fixed in v5.13.0, but
|
|
183
|
+
* already-polluted machines need the gate itself to self-heal). A mismatch
|
|
184
|
+
* between the stored memory count and reality IS change — take the max of
|
|
185
|
+
* both signals.
|
|
186
|
+
*/
|
|
187
|
+
export function countDreamworthyChanges(memories, state) {
|
|
188
|
+
const byDate = countChangedMemoriesSince(memories, state.lastMemoryMaxModified);
|
|
189
|
+
const countDelta = state.lastMemoryCount == null ? 0 : Math.abs(memories.length - state.lastMemoryCount);
|
|
190
|
+
return Math.max(byDate, countDelta);
|
|
191
|
+
}
|
|
178
192
|
export function isInsideNightWindow(now, schedule) {
|
|
179
193
|
const start = schedule.startHour;
|
|
180
194
|
const end = schedule.endHour;
|
package/dist/lib/recall.js
CHANGED
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
import { GnosysArchive } from "./archive.js";
|
|
24
24
|
import { mergeOverlayDiscoverResults, pendingAddToDbMemory, } from "./clientReadOverlay.js";
|
|
25
25
|
import { auditLog } from "./audit.js";
|
|
26
|
+
import { ftsTerms } from "./ftsQuery.js";
|
|
26
27
|
/** Default recall config */
|
|
27
28
|
const DEFAULT_RECALL_CONFIG = {
|
|
28
29
|
aggressive: true,
|
|
@@ -150,6 +151,48 @@ function recallFromDb(query, db, limit, cfg, traceId, pendingOverlay) {
|
|
|
150
151
|
const memories = [];
|
|
151
152
|
// Step 1: FTS5 discover on gnosys.db
|
|
152
153
|
const fetchLimit = Math.max(limit * 2, 15);
|
|
154
|
+
// v5.13.1: wildcard recall. The gnosys://recall resource passes "*"
|
|
155
|
+
// meaning "inject the top memories, no specific query" — but "*" was
|
|
156
|
+
// never a valid FTS5 match-all (it's a syntax error), so the resource
|
|
157
|
+
// returned zero memories since it shipped in v4.0.0. Any query with no
|
|
158
|
+
// searchable terms now serves top active memories ranked by
|
|
159
|
+
// reinforcement, confidence, and recency.
|
|
160
|
+
if (ftsTerms(query).length === 0) {
|
|
161
|
+
const top = db.getTopActiveMemories(fetchLimit);
|
|
162
|
+
for (let i = 0; i < top.length; i++) {
|
|
163
|
+
const mem = top[i];
|
|
164
|
+
memories.push({
|
|
165
|
+
id: mem.id,
|
|
166
|
+
title: mem.title,
|
|
167
|
+
category: mem.category,
|
|
168
|
+
relevance: mem.relevance || "",
|
|
169
|
+
confidence: mem.confidence,
|
|
170
|
+
path: mem.id,
|
|
171
|
+
fromArchive: false,
|
|
172
|
+
snippet: mem.content.substring(0, 300),
|
|
173
|
+
// Rank-derived score, floored above minRelevance (0.4 default) so
|
|
174
|
+
// non-aggressive mode keeps top memories too.
|
|
175
|
+
relevanceScore: Math.max(0.9 - i * 0.02, 0.5),
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
const wildcardCounts = db.getMemoryCount();
|
|
179
|
+
const result = applyRecallFiltering(memories, top.length, wildcardCounts.archived, limit, cfg, start);
|
|
180
|
+
db.logAudit({
|
|
181
|
+
timestamp: new Date().toISOString(),
|
|
182
|
+
operation: "recall",
|
|
183
|
+
memory_id: null,
|
|
184
|
+
details: JSON.stringify({
|
|
185
|
+
query,
|
|
186
|
+
wildcard: true,
|
|
187
|
+
aggressive: cfg.aggressive,
|
|
188
|
+
totalCandidates: memories.length,
|
|
189
|
+
filtered: result.memories.length,
|
|
190
|
+
}),
|
|
191
|
+
duration_ms: Math.round(result.recallTimeMs),
|
|
192
|
+
trace_id: traceId || null,
|
|
193
|
+
});
|
|
194
|
+
return result;
|
|
195
|
+
}
|
|
153
196
|
let dbResults = db.discoverFts(query, fetchLimit);
|
|
154
197
|
if (pendingOverlay?.length) {
|
|
155
198
|
dbResults = mergeOverlayDiscoverResults(dbResults, pendingOverlay, query, fetchLimit, (p) => ({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gnosys",
|
|
3
|
-
"version": "5.13.
|
|
3
|
+
"version": "5.13.1",
|
|
4
4
|
"description": "Gnosys — Persistent Memory for AI Agents. Sandbox-first runtime, central SQLite brain, federated search, Dream Mode, Web Knowledge Base, Obsidian export.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|