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 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(opts);
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(() => {
@@ -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, countChangedMemoriesSince, createSkipRunRecord, getSystemIdleMinutes, isInsideNightWindow, readDreamState, } from "./dreamRunLog.js";
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
- const db = new DbClass(storePath);
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
- const changed = countChangedMemoriesSince(memories, state.lastMemoryMaxModified);
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: {
@@ -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;
@@ -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.0",
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",