gnosys 4.0.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.
Files changed (188) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1387 -0
  3. package/dist/cli.d.ts +7 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +3753 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/index.d.ts +8 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +2267 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/lib/archive.d.ts +95 -0
  12. package/dist/lib/archive.d.ts.map +1 -0
  13. package/dist/lib/archive.js +311 -0
  14. package/dist/lib/archive.js.map +1 -0
  15. package/dist/lib/ask.d.ts +77 -0
  16. package/dist/lib/ask.d.ts.map +1 -0
  17. package/dist/lib/ask.js +316 -0
  18. package/dist/lib/ask.js.map +1 -0
  19. package/dist/lib/audit.d.ts +47 -0
  20. package/dist/lib/audit.d.ts.map +1 -0
  21. package/dist/lib/audit.js +136 -0
  22. package/dist/lib/audit.js.map +1 -0
  23. package/dist/lib/bootstrap.d.ts +56 -0
  24. package/dist/lib/bootstrap.d.ts.map +1 -0
  25. package/dist/lib/bootstrap.js +163 -0
  26. package/dist/lib/bootstrap.js.map +1 -0
  27. package/dist/lib/config.d.ts +239 -0
  28. package/dist/lib/config.d.ts.map +1 -0
  29. package/dist/lib/config.js +371 -0
  30. package/dist/lib/config.js.map +1 -0
  31. package/dist/lib/dashboard.d.ts +81 -0
  32. package/dist/lib/dashboard.d.ts.map +1 -0
  33. package/dist/lib/dashboard.js +314 -0
  34. package/dist/lib/dashboard.js.map +1 -0
  35. package/dist/lib/db.d.ts +182 -0
  36. package/dist/lib/db.d.ts.map +1 -0
  37. package/dist/lib/db.js +620 -0
  38. package/dist/lib/db.js.map +1 -0
  39. package/dist/lib/dbSearch.d.ts +65 -0
  40. package/dist/lib/dbSearch.d.ts.map +1 -0
  41. package/dist/lib/dbSearch.js +239 -0
  42. package/dist/lib/dbSearch.js.map +1 -0
  43. package/dist/lib/dbWrite.d.ts +56 -0
  44. package/dist/lib/dbWrite.d.ts.map +1 -0
  45. package/dist/lib/dbWrite.js +171 -0
  46. package/dist/lib/dbWrite.js.map +1 -0
  47. package/dist/lib/dream.d.ts +170 -0
  48. package/dist/lib/dream.d.ts.map +1 -0
  49. package/dist/lib/dream.js +706 -0
  50. package/dist/lib/dream.js.map +1 -0
  51. package/dist/lib/embeddings.d.ts +84 -0
  52. package/dist/lib/embeddings.d.ts.map +1 -0
  53. package/dist/lib/embeddings.js +226 -0
  54. package/dist/lib/embeddings.js.map +1 -0
  55. package/dist/lib/export.d.ts +92 -0
  56. package/dist/lib/export.d.ts.map +1 -0
  57. package/dist/lib/export.js +362 -0
  58. package/dist/lib/export.js.map +1 -0
  59. package/dist/lib/federated.d.ts +113 -0
  60. package/dist/lib/federated.d.ts.map +1 -0
  61. package/dist/lib/federated.js +346 -0
  62. package/dist/lib/federated.js.map +1 -0
  63. package/dist/lib/graph.d.ts +50 -0
  64. package/dist/lib/graph.d.ts.map +1 -0
  65. package/dist/lib/graph.js +118 -0
  66. package/dist/lib/graph.js.map +1 -0
  67. package/dist/lib/history.d.ts +39 -0
  68. package/dist/lib/history.d.ts.map +1 -0
  69. package/dist/lib/history.js +112 -0
  70. package/dist/lib/history.js.map +1 -0
  71. package/dist/lib/hybridSearch.d.ts +80 -0
  72. package/dist/lib/hybridSearch.d.ts.map +1 -0
  73. package/dist/lib/hybridSearch.js +296 -0
  74. package/dist/lib/hybridSearch.js.map +1 -0
  75. package/dist/lib/import.d.ts +52 -0
  76. package/dist/lib/import.d.ts.map +1 -0
  77. package/dist/lib/import.js +365 -0
  78. package/dist/lib/import.js.map +1 -0
  79. package/dist/lib/ingest.d.ts +51 -0
  80. package/dist/lib/ingest.d.ts.map +1 -0
  81. package/dist/lib/ingest.js +144 -0
  82. package/dist/lib/ingest.js.map +1 -0
  83. package/dist/lib/lensing.d.ts +35 -0
  84. package/dist/lib/lensing.d.ts.map +1 -0
  85. package/dist/lib/lensing.js +85 -0
  86. package/dist/lib/lensing.js.map +1 -0
  87. package/dist/lib/llm.d.ts +84 -0
  88. package/dist/lib/llm.d.ts.map +1 -0
  89. package/dist/lib/llm.js +386 -0
  90. package/dist/lib/llm.js.map +1 -0
  91. package/dist/lib/lock.d.ts +28 -0
  92. package/dist/lib/lock.d.ts.map +1 -0
  93. package/dist/lib/lock.js +145 -0
  94. package/dist/lib/lock.js.map +1 -0
  95. package/dist/lib/maintenance.d.ts +124 -0
  96. package/dist/lib/maintenance.d.ts.map +1 -0
  97. package/dist/lib/maintenance.js +587 -0
  98. package/dist/lib/maintenance.js.map +1 -0
  99. package/dist/lib/migrate.d.ts +19 -0
  100. package/dist/lib/migrate.d.ts.map +1 -0
  101. package/dist/lib/migrate.js +260 -0
  102. package/dist/lib/migrate.js.map +1 -0
  103. package/dist/lib/preferences.d.ts +49 -0
  104. package/dist/lib/preferences.d.ts.map +1 -0
  105. package/dist/lib/preferences.js +149 -0
  106. package/dist/lib/preferences.js.map +1 -0
  107. package/dist/lib/projectIdentity.d.ts +66 -0
  108. package/dist/lib/projectIdentity.d.ts.map +1 -0
  109. package/dist/lib/projectIdentity.js +148 -0
  110. package/dist/lib/projectIdentity.js.map +1 -0
  111. package/dist/lib/recall.d.ts +82 -0
  112. package/dist/lib/recall.d.ts.map +1 -0
  113. package/dist/lib/recall.js +289 -0
  114. package/dist/lib/recall.js.map +1 -0
  115. package/dist/lib/resolver.d.ts +116 -0
  116. package/dist/lib/resolver.d.ts.map +1 -0
  117. package/dist/lib/resolver.js +372 -0
  118. package/dist/lib/resolver.js.map +1 -0
  119. package/dist/lib/retry.d.ts +24 -0
  120. package/dist/lib/retry.d.ts.map +1 -0
  121. package/dist/lib/retry.js +60 -0
  122. package/dist/lib/retry.js.map +1 -0
  123. package/dist/lib/rulesGen.d.ts +51 -0
  124. package/dist/lib/rulesGen.d.ts.map +1 -0
  125. package/dist/lib/rulesGen.js +167 -0
  126. package/dist/lib/rulesGen.js.map +1 -0
  127. package/dist/lib/search.d.ts +51 -0
  128. package/dist/lib/search.d.ts.map +1 -0
  129. package/dist/lib/search.js +190 -0
  130. package/dist/lib/search.js.map +1 -0
  131. package/dist/lib/staticSearch.d.ts +70 -0
  132. package/dist/lib/staticSearch.d.ts.map +1 -0
  133. package/dist/lib/staticSearch.js +162 -0
  134. package/dist/lib/staticSearch.js.map +1 -0
  135. package/dist/lib/store.d.ts +79 -0
  136. package/dist/lib/store.d.ts.map +1 -0
  137. package/dist/lib/store.js +227 -0
  138. package/dist/lib/store.js.map +1 -0
  139. package/dist/lib/structuredIngest.d.ts +37 -0
  140. package/dist/lib/structuredIngest.d.ts.map +1 -0
  141. package/dist/lib/structuredIngest.js +208 -0
  142. package/dist/lib/structuredIngest.js.map +1 -0
  143. package/dist/lib/tags.d.ts +26 -0
  144. package/dist/lib/tags.d.ts.map +1 -0
  145. package/dist/lib/tags.js +109 -0
  146. package/dist/lib/tags.js.map +1 -0
  147. package/dist/lib/timeline.d.ts +34 -0
  148. package/dist/lib/timeline.d.ts.map +1 -0
  149. package/dist/lib/timeline.js +116 -0
  150. package/dist/lib/timeline.js.map +1 -0
  151. package/dist/lib/trace.d.ts +42 -0
  152. package/dist/lib/trace.d.ts.map +1 -0
  153. package/dist/lib/trace.js +338 -0
  154. package/dist/lib/trace.js.map +1 -0
  155. package/dist/lib/webIndex.d.ts +28 -0
  156. package/dist/lib/webIndex.d.ts.map +1 -0
  157. package/dist/lib/webIndex.js +208 -0
  158. package/dist/lib/webIndex.js.map +1 -0
  159. package/dist/lib/webIngest.d.ts +51 -0
  160. package/dist/lib/webIngest.d.ts.map +1 -0
  161. package/dist/lib/webIngest.js +533 -0
  162. package/dist/lib/webIngest.js.map +1 -0
  163. package/dist/lib/wikilinks.d.ts +63 -0
  164. package/dist/lib/wikilinks.d.ts.map +1 -0
  165. package/dist/lib/wikilinks.js +146 -0
  166. package/dist/lib/wikilinks.js.map +1 -0
  167. package/dist/sandbox/client.d.ts +82 -0
  168. package/dist/sandbox/client.d.ts.map +1 -0
  169. package/dist/sandbox/client.js +128 -0
  170. package/dist/sandbox/client.js.map +1 -0
  171. package/dist/sandbox/helper-template.d.ts +14 -0
  172. package/dist/sandbox/helper-template.d.ts.map +1 -0
  173. package/dist/sandbox/helper-template.js +285 -0
  174. package/dist/sandbox/helper-template.js.map +1 -0
  175. package/dist/sandbox/index.d.ts +10 -0
  176. package/dist/sandbox/index.d.ts.map +1 -0
  177. package/dist/sandbox/index.js +10 -0
  178. package/dist/sandbox/index.js.map +1 -0
  179. package/dist/sandbox/manager.d.ts +40 -0
  180. package/dist/sandbox/manager.d.ts.map +1 -0
  181. package/dist/sandbox/manager.js +220 -0
  182. package/dist/sandbox/manager.js.map +1 -0
  183. package/dist/sandbox/server.d.ts +44 -0
  184. package/dist/sandbox/server.d.ts.map +1 -0
  185. package/dist/sandbox/server.js +661 -0
  186. package/dist/sandbox/server.js.map +1 -0
  187. package/package.json +103 -0
  188. package/prompts/synthesize.md +21 -0
@@ -0,0 +1,661 @@
1
+ /**
2
+ * Gnosys Sandbox Server
3
+ *
4
+ * Lightweight background Node process that holds a GnosysDB connection
5
+ * and processes requests over a Unix domain socket (or named pipe on Windows).
6
+ *
7
+ * Phase 9b: Integrates Dream Mode (idle-triggered), preferences, and sync.
8
+ *
9
+ * No Docker. No external deps. Just Node + better-sqlite3.
10
+ */
11
+ import net from "net";
12
+ import fs from "fs";
13
+ import path from "path";
14
+ import os from "os";
15
+ import { GnosysDB } from "../lib/db.js";
16
+ import { federatedSearch } from "../lib/federated.js";
17
+ import { setPreference, getPreference, getAllPreferences, deletePreference, searchPreferences } from "../lib/preferences.js";
18
+ import { GnosysDreamEngine, DreamScheduler, DEFAULT_DREAM_CONFIG } from "../lib/dream.js";
19
+ import { DEFAULT_CONFIG } from "../lib/config.js";
20
+ import { generateRulesBlock } from "../lib/rulesGen.js";
21
+ // ─── Socket + PID paths ─────────────────────────────────────────────────
22
+ export function getSandboxDir() {
23
+ const dir = path.join(os.homedir(), ".gnosys", "sandbox");
24
+ fs.mkdirSync(dir, { recursive: true });
25
+ return dir;
26
+ }
27
+ export function getSocketPath() {
28
+ if (process.platform === "win32") {
29
+ return "\\\\.\\pipe\\gnosys-sandbox";
30
+ }
31
+ return path.join(getSandboxDir(), "gnosys.sock");
32
+ }
33
+ export function getPidPath() {
34
+ return path.join(getSandboxDir(), "gnosys.pid");
35
+ }
36
+ let dreamState = {
37
+ enabled: false,
38
+ idleMinutes: DEFAULT_DREAM_CONFIG.idleMinutes,
39
+ lastDreamReport: null,
40
+ dreamsCompleted: 0,
41
+ isDreaming: false,
42
+ };
43
+ let dreamScheduler = null;
44
+ /**
45
+ * Initialize Dream Mode in the sandbox.
46
+ * Called once during server startup.
47
+ */
48
+ export function initDreamMode(db, config, dreamConfig) {
49
+ const mergedConfig = {
50
+ ...DEFAULT_DREAM_CONFIG,
51
+ ...config.dream,
52
+ ...dreamConfig,
53
+ enabled: true, // Always enable in sandbox if caller invokes this
54
+ };
55
+ dreamState.enabled = true;
56
+ dreamState.idleMinutes = mergedConfig.idleMinutes;
57
+ const engine = new GnosysDreamEngine(db, config, mergedConfig);
58
+ // Create a scheduler that wraps progress tracking
59
+ const scheduler = new DreamScheduler(engine, mergedConfig);
60
+ // Monkey-patch the scheduler's private checkIdle to track dream state
61
+ const originalStart = scheduler.start.bind(scheduler);
62
+ scheduler.start = function () {
63
+ originalStart();
64
+ // Override the internal check interval to track state
65
+ const CHECK_INTERVAL = 60_000;
66
+ const origCheckIdle = scheduler.checkIdle;
67
+ if (origCheckIdle) {
68
+ scheduler.checkIdle = async function () {
69
+ dreamState.isDreaming = scheduler.isDreaming();
70
+ await origCheckIdle.call(scheduler);
71
+ dreamState.isDreaming = scheduler.isDreaming();
72
+ if (!scheduler.isDreaming() && dreamState.isDreaming) {
73
+ dreamState.dreamsCompleted++;
74
+ }
75
+ };
76
+ }
77
+ };
78
+ return scheduler;
79
+ }
80
+ // ─── Request Handler ─────────────────────────────────────────────────────
81
+ export function handleRequest(db, req) {
82
+ // Record activity for idle detection (Dream Mode)
83
+ if (dreamScheduler) {
84
+ dreamScheduler.recordActivity();
85
+ }
86
+ try {
87
+ switch (req.method) {
88
+ case "ping":
89
+ return { id: req.id, ok: true, result: { status: "ok", pid: process.pid } };
90
+ case "add": {
91
+ const { content, title, category = "decisions", project_id, scope = "project", tags = "[]", relevance = "", author = "ai", authority = "declared", confidence = 0.9 } = req.params;
92
+ if (!content)
93
+ return { id: req.id, ok: false, error: "content is required" };
94
+ const now = new Date().toISOString();
95
+ const memTitle = title || content.slice(0, 80);
96
+ const id = `mem-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
97
+ db.insertMemory({
98
+ id,
99
+ title: memTitle,
100
+ category,
101
+ content,
102
+ summary: null,
103
+ tags: typeof tags === "string" ? tags : JSON.stringify(tags),
104
+ relevance: relevance || content.slice(0, 200),
105
+ author,
106
+ authority,
107
+ confidence: Number(confidence),
108
+ reinforcement_count: 0,
109
+ content_hash: "",
110
+ status: "active",
111
+ tier: "active",
112
+ supersedes: null,
113
+ superseded_by: null,
114
+ last_reinforced: null,
115
+ created: now,
116
+ modified: now,
117
+ embedding: null,
118
+ source_path: null,
119
+ project_id: project_id || null,
120
+ scope: scope,
121
+ });
122
+ return { id: req.id, ok: true, result: { id, title: memTitle } };
123
+ }
124
+ case "recall": {
125
+ const { query, limit = 10, project_id } = req.params;
126
+ if (!query)
127
+ return { id: req.id, ok: false, error: "query is required" };
128
+ // Use federated search if available, fall back to FTS
129
+ try {
130
+ const results = federatedSearch(db, query, {
131
+ limit: Number(limit),
132
+ projectId: project_id,
133
+ });
134
+ return {
135
+ id: req.id,
136
+ ok: true,
137
+ result: results.map((r) => {
138
+ // Enrich with full memory data when available
139
+ const mem = db.getMemory(r.id);
140
+ return {
141
+ id: r.id,
142
+ title: r.title,
143
+ content: mem?.content || r.snippet,
144
+ category: r.category,
145
+ confidence: mem?.confidence ?? 0,
146
+ score: r.score,
147
+ };
148
+ }),
149
+ };
150
+ }
151
+ catch {
152
+ // Fall back to basic FTS
153
+ const results = db.searchFts(query, Number(limit));
154
+ return {
155
+ id: req.id,
156
+ ok: true,
157
+ result: results.map((r) => ({
158
+ id: r.id,
159
+ title: r.title,
160
+ snippet: r.snippet,
161
+ rank: r.rank,
162
+ })),
163
+ };
164
+ }
165
+ }
166
+ case "reinforce": {
167
+ const { id: memId, query } = req.params;
168
+ let targetId = memId;
169
+ // If query provided instead of ID, find best match
170
+ if (!targetId && query) {
171
+ const results = db.searchFts(query, 1);
172
+ if (results.length === 0) {
173
+ return { id: req.id, ok: false, error: `No memory found matching "${query}"` };
174
+ }
175
+ targetId = results[0].id;
176
+ }
177
+ if (!targetId)
178
+ return { id: req.id, ok: false, error: "id or query is required" };
179
+ const mem = db.getMemory(targetId);
180
+ if (!mem)
181
+ return { id: req.id, ok: false, error: `Memory not found: ${targetId}` };
182
+ db.updateMemory(targetId, {
183
+ reinforcement_count: mem.reinforcement_count + 1,
184
+ confidence: Math.min(1.0, mem.confidence + 0.05),
185
+ last_reinforced: new Date().toISOString(),
186
+ modified: new Date().toISOString(),
187
+ });
188
+ return {
189
+ id: req.id,
190
+ ok: true,
191
+ result: {
192
+ id: targetId,
193
+ reinforcement_count: mem.reinforcement_count + 1,
194
+ confidence: Math.min(1.0, mem.confidence + 0.05),
195
+ },
196
+ };
197
+ }
198
+ case "get": {
199
+ const { id: memId } = req.params;
200
+ if (!memId)
201
+ return { id: req.id, ok: false, error: "id is required" };
202
+ const mem = db.getMemory(memId);
203
+ if (!mem)
204
+ return { id: req.id, ok: false, error: `Memory not found: ${memId}` };
205
+ return { id: req.id, ok: true, result: mem };
206
+ }
207
+ case "list": {
208
+ const { category, project_id, limit = 50 } = req.params;
209
+ let memories;
210
+ if (project_id) {
211
+ memories = db.getMemoriesByProject(project_id);
212
+ }
213
+ else if (category) {
214
+ memories = db.getMemoriesByCategory(category);
215
+ }
216
+ else {
217
+ memories = db.getActiveMemories();
218
+ }
219
+ return {
220
+ id: req.id,
221
+ ok: true,
222
+ result: memories.slice(0, Number(limit)).map((m) => ({
223
+ id: m.id,
224
+ title: m.title,
225
+ category: m.category,
226
+ confidence: m.confidence,
227
+ project_id: m.project_id,
228
+ scope: m.scope,
229
+ })),
230
+ };
231
+ }
232
+ case "stats": {
233
+ const counts = db.getMemoryCount();
234
+ const categories = db.getCategories();
235
+ const projects = db.getAllProjects();
236
+ return {
237
+ id: req.id,
238
+ ok: true,
239
+ result: {
240
+ ...counts,
241
+ categories,
242
+ projects: projects.length,
243
+ },
244
+ };
245
+ }
246
+ // ─── Phase 9b: Preference methods ───────────────────────────────
247
+ case "pref_set": {
248
+ const { key, value, title: prefTitle, tags: prefTags, confidence: prefConf } = req.params;
249
+ if (!key || !value)
250
+ return { id: req.id, ok: false, error: "key and value are required" };
251
+ const pref = setPreference(db, key, value, {
252
+ title: prefTitle,
253
+ tags: prefTags ? (Array.isArray(prefTags) ? prefTags : JSON.parse(prefTags)) : undefined,
254
+ confidence: prefConf ? Number(prefConf) : undefined,
255
+ });
256
+ return { id: req.id, ok: true, result: pref };
257
+ }
258
+ case "pref_get": {
259
+ const { key } = req.params;
260
+ if (!key)
261
+ return { id: req.id, ok: false, error: "key is required" };
262
+ const pref = getPreference(db, key);
263
+ if (!pref)
264
+ return { id: req.id, ok: false, error: `Preference not found: ${key}` };
265
+ return { id: req.id, ok: true, result: pref };
266
+ }
267
+ case "pref_list": {
268
+ const prefs = getAllPreferences(db);
269
+ return {
270
+ id: req.id,
271
+ ok: true,
272
+ result: prefs.map((p) => ({ key: p.key, value: p.value, title: p.title })),
273
+ };
274
+ }
275
+ case "pref_delete": {
276
+ const { key } = req.params;
277
+ if (!key)
278
+ return { id: req.id, ok: false, error: "key is required" };
279
+ const deleted = deletePreference(db, key);
280
+ return { id: req.id, ok: true, result: { deleted } };
281
+ }
282
+ case "pref_search": {
283
+ const { query } = req.params;
284
+ if (!query)
285
+ return { id: req.id, ok: false, error: "query is required" };
286
+ const results = searchPreferences(db, query);
287
+ return { id: req.id, ok: true, result: results };
288
+ }
289
+ // ─── Phase 9b: Dream Mode status ────────────────────────────────
290
+ case "dream_status": {
291
+ return {
292
+ id: req.id,
293
+ ok: true,
294
+ result: {
295
+ ...dreamState,
296
+ isDreaming: dreamScheduler?.isDreaming() ?? false,
297
+ },
298
+ };
299
+ }
300
+ // ─── Phase 9b: Sync rules ───────────────────────────────────────
301
+ case "sync": {
302
+ const { project_dir, agent_rules_target, project_id } = req.params;
303
+ if (!project_dir)
304
+ return { id: req.id, ok: false, error: "project_dir is required" };
305
+ if (!agent_rules_target)
306
+ return { id: req.id, ok: false, error: "agent_rules_target is required" };
307
+ // syncRules is async — but we handle it synchronously via blocking
308
+ // Actually, we need to return a promise-based result. Since the handler is sync,
309
+ // we'll use a simpler approach: generate the block and inject directly.
310
+ const preferences = getAllPreferences(db);
311
+ let projectConventions = [];
312
+ if (project_id) {
313
+ const projectMems = db.getMemoriesByProject(project_id);
314
+ projectConventions = projectMems.filter((m) => (m.category === "decisions" || m.category === "conventions") && m.status === "active");
315
+ }
316
+ const block = generateRulesBlock(preferences, projectConventions);
317
+ return {
318
+ id: req.id,
319
+ ok: true,
320
+ result: {
321
+ block,
322
+ prefCount: preferences.length,
323
+ conventionCount: projectConventions.length,
324
+ },
325
+ };
326
+ }
327
+ // ─── Phase 10: Reflection API ─────────────────────────────────────
328
+ case "reflect": {
329
+ const { outcome, memory_ids, success, notes, confidence_delta, } = req.params;
330
+ if (!outcome)
331
+ return { id: req.id, ok: false, error: "outcome is required" };
332
+ const now = new Date().toISOString();
333
+ const memoryIdList = memory_ids
334
+ ? (Array.isArray(memory_ids) ? memory_ids : JSON.parse(memory_ids))
335
+ : [];
336
+ const isSuccess = success !== false; // default true
337
+ const delta = confidence_delta != null
338
+ ? Number(confidence_delta)
339
+ : (isSuccess ? 0.05 : -0.1);
340
+ // Track which memories were updated
341
+ const updated = [];
342
+ const relationships = [];
343
+ // If no explicit memory_ids, search for related memories
344
+ let targetIds = memoryIdList;
345
+ if (targetIds.length === 0) {
346
+ const results = db.searchFts(outcome, 5);
347
+ targetIds = results.map((r) => r.id);
348
+ }
349
+ // Update confidence on each related memory
350
+ for (const memId of targetIds) {
351
+ const mem = db.getMemory(memId);
352
+ if (!mem)
353
+ continue;
354
+ const newConf = Math.max(0.05, Math.min(1.0, mem.confidence + delta));
355
+ const updates = {
356
+ confidence: newConf,
357
+ modified: now,
358
+ };
359
+ if (isSuccess) {
360
+ updates.reinforcement_count = mem.reinforcement_count + 1;
361
+ updates.last_reinforced = now;
362
+ }
363
+ db.updateMemory(memId, updates);
364
+ updated.push({
365
+ id: memId,
366
+ confidence: newConf,
367
+ reinforcement_count: (updates.reinforcement_count ?? mem.reinforcement_count),
368
+ });
369
+ }
370
+ // Create a reflection memory that records the outcome
371
+ const reflectionId = `mem-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
372
+ const reflectionContent = [
373
+ `## Reflection: ${isSuccess ? "Success" : "Failure"}`,
374
+ "",
375
+ `**Outcome:** ${outcome}`,
376
+ notes ? `\n**Notes:** ${notes}` : "",
377
+ "",
378
+ `**Related memories:** ${targetIds.join(", ")}`,
379
+ `**Confidence adjustment:** ${delta > 0 ? "+" : ""}${delta.toFixed(2)}`,
380
+ ].filter(Boolean).join("\n");
381
+ db.insertMemory({
382
+ id: reflectionId,
383
+ title: `Reflection: ${outcome.slice(0, 60)}`,
384
+ category: "reflections",
385
+ content: reflectionContent,
386
+ summary: null,
387
+ tags: JSON.stringify(["reflection", isSuccess ? "success" : "failure"]),
388
+ relevance: `reflection outcome ${outcome}`,
389
+ author: "ai",
390
+ authority: "observed",
391
+ confidence: 0.8,
392
+ reinforcement_count: 0,
393
+ content_hash: "",
394
+ status: "active",
395
+ tier: "active",
396
+ supersedes: null,
397
+ superseded_by: null,
398
+ last_reinforced: null,
399
+ created: now,
400
+ modified: now,
401
+ embedding: null,
402
+ source_path: null,
403
+ project_id: null,
404
+ scope: "user",
405
+ });
406
+ // Add relationships: reflection → each related memory
407
+ for (const memId of targetIds) {
408
+ db.insertRelationship({
409
+ source_id: reflectionId,
410
+ target_id: memId,
411
+ rel_type: isSuccess ? "validates" : "contradicts",
412
+ label: `Reflection ${isSuccess ? "validated" : "contradicted"} this memory`,
413
+ confidence: 0.9,
414
+ created: now,
415
+ });
416
+ relationships.push({
417
+ source_id: reflectionId,
418
+ target_id: memId,
419
+ rel_type: isSuccess ? "validates" : "contradicts",
420
+ });
421
+ }
422
+ // Consolidation: if success, link related memories to each other
423
+ if (isSuccess && targetIds.length > 1) {
424
+ for (let i = 0; i < targetIds.length - 1; i++) {
425
+ db.insertRelationship({
426
+ source_id: targetIds[i],
427
+ target_id: targetIds[i + 1],
428
+ rel_type: "corroborates",
429
+ label: `Linked by successful reflection on: ${outcome.slice(0, 60)}`,
430
+ confidence: 0.7,
431
+ created: now,
432
+ });
433
+ }
434
+ }
435
+ return {
436
+ id: req.id,
437
+ ok: true,
438
+ result: {
439
+ reflection_id: reflectionId,
440
+ outcome: isSuccess ? "success" : "failure",
441
+ memories_updated: updated,
442
+ relationships_created: relationships.length,
443
+ confidence_delta: delta,
444
+ },
445
+ };
446
+ }
447
+ // ─── Phase 10: Traverse relationship chains ────────────────────────
448
+ case "traverse": {
449
+ const { id: startId, depth = 3, rel_types, } = req.params;
450
+ if (!startId)
451
+ return { id: req.id, ok: false, error: "id is required" };
452
+ const maxDepth = Math.min(Number(depth), 10); // safety cap
453
+ const allowedTypes = rel_types
454
+ ? (Array.isArray(rel_types) ? rel_types : JSON.parse(rel_types))
455
+ : null;
456
+ const visited = new Set();
457
+ const nodes = [];
458
+ const queue = [];
459
+ // Start node
460
+ const startMem = db.getMemory(startId);
461
+ if (!startMem)
462
+ return { id: req.id, ok: false, error: `Memory not found: ${startId}` };
463
+ visited.add(startId);
464
+ nodes.push({
465
+ id: startMem.id,
466
+ title: startMem.title,
467
+ category: startMem.category,
468
+ confidence: startMem.confidence,
469
+ depth: 0,
470
+ via_rel: null,
471
+ via_from: null,
472
+ });
473
+ queue.push({ id: startId, depth: 0, via_rel: null, via_from: null });
474
+ while (queue.length > 0) {
475
+ const current = queue.shift();
476
+ if (current.depth >= maxDepth)
477
+ continue;
478
+ // Get outgoing relationships
479
+ const outgoing = db.getRelationshipsFrom(current.id);
480
+ const incoming = db.getRelationshipsTo(current.id);
481
+ const allRels = [
482
+ ...outgoing.map((r) => ({ targetId: r.target_id, rel_type: r.rel_type })),
483
+ ...incoming.map((r) => ({ targetId: r.source_id, rel_type: r.rel_type })),
484
+ ];
485
+ for (const rel of allRels) {
486
+ if (visited.has(rel.targetId))
487
+ continue;
488
+ if (allowedTypes && !allowedTypes.includes(rel.rel_type))
489
+ continue;
490
+ visited.add(rel.targetId);
491
+ const mem = db.getMemory(rel.targetId);
492
+ if (!mem)
493
+ continue;
494
+ const node = {
495
+ id: mem.id,
496
+ title: mem.title,
497
+ category: mem.category,
498
+ confidence: mem.confidence,
499
+ depth: current.depth + 1,
500
+ via_rel: rel.rel_type,
501
+ via_from: current.id,
502
+ };
503
+ nodes.push(node);
504
+ queue.push({
505
+ id: mem.id,
506
+ depth: current.depth + 1,
507
+ via_rel: rel.rel_type,
508
+ via_from: current.id,
509
+ });
510
+ }
511
+ }
512
+ return {
513
+ id: req.id,
514
+ ok: true,
515
+ result: {
516
+ root: startId,
517
+ depth: maxDepth,
518
+ nodes,
519
+ total: nodes.length,
520
+ },
521
+ };
522
+ }
523
+ case "shutdown":
524
+ // Stop dream scheduler before shutting down
525
+ if (dreamScheduler) {
526
+ dreamScheduler.stop();
527
+ }
528
+ setTimeout(() => {
529
+ db.close();
530
+ process.exit(0);
531
+ }, 100);
532
+ return { id: req.id, ok: true, result: { message: "Shutting down" } };
533
+ default:
534
+ return { id: req.id, ok: false, error: `Unknown method: ${req.method}` };
535
+ }
536
+ }
537
+ catch (err) {
538
+ return {
539
+ id: req.id,
540
+ ok: false,
541
+ error: err instanceof Error ? err.message : String(err),
542
+ };
543
+ }
544
+ }
545
+ // ─── Server ──────────────────────────────────────────────────────────────
546
+ export function startServer(dbPath) {
547
+ const socketPath = getSocketPath();
548
+ // Clean up stale socket
549
+ if (process.platform !== "win32" && fs.existsSync(socketPath)) {
550
+ fs.unlinkSync(socketPath);
551
+ }
552
+ // Open database (with retry for network shares — Dropbox, iCloud, NAS)
553
+ const dbDir = dbPath || GnosysDB.getCentralDbDir();
554
+ const isNetworkPath = dbPath ? true : false;
555
+ const db = new GnosysDB(dbDir, isNetworkPath ? { retries: 5, retryDelayMs: 1000 } : undefined);
556
+ if (!db.isAvailable()) {
557
+ console.error("Failed to open GnosysDB. Is better-sqlite3 installed?");
558
+ if (isNetworkPath) {
559
+ console.error(`Network path "${dbDir}" may be unavailable. Check the path is mounted and accessible.`);
560
+ }
561
+ process.exit(1);
562
+ }
563
+ if (isNetworkPath) {
564
+ console.log(`Using network DB path: ${dbDir}`);
565
+ }
566
+ // ─── Phase 9b: Initialize Dream Mode ─────────────────────────────
567
+ // loadConfig is async and needs a store path — in sandbox mode we use defaults
568
+ // and allow env-based overrides.
569
+ const config = {
570
+ ...DEFAULT_CONFIG,
571
+ dream: {
572
+ ...DEFAULT_CONFIG.dream,
573
+ enabled: process.env.GNOSYS_DREAM_ENABLED !== "false",
574
+ idleMinutes: parseInt(process.env.GNOSYS_DREAM_IDLE_MINUTES || "10", 10),
575
+ },
576
+ };
577
+ if (config.dream?.enabled !== false) {
578
+ try {
579
+ dreamScheduler = initDreamMode(db, config);
580
+ if (dreamScheduler) {
581
+ dreamScheduler.start();
582
+ console.log(`Dream Mode enabled (idle threshold: ${dreamState.idleMinutes}min)`);
583
+ }
584
+ }
585
+ catch (err) {
586
+ console.error(`Dream Mode init failed: ${err instanceof Error ? err.message : String(err)}`);
587
+ }
588
+ }
589
+ const server = net.createServer((socket) => {
590
+ let buffer = "";
591
+ socket.on("data", (data) => {
592
+ buffer += data.toString();
593
+ // Process complete JSON messages (newline-delimited)
594
+ let newlineIdx;
595
+ while ((newlineIdx = buffer.indexOf("\n")) !== -1) {
596
+ const line = buffer.slice(0, newlineIdx).trim();
597
+ buffer = buffer.slice(newlineIdx + 1);
598
+ if (!line)
599
+ continue;
600
+ try {
601
+ const raw = JSON.parse(line);
602
+ // Validate required fields before casting
603
+ if (typeof raw !== "object" || raw === null ||
604
+ typeof raw.id !== "string" ||
605
+ typeof raw.method !== "string" ||
606
+ (raw.params !== undefined && (typeof raw.params !== "object" || raw.params === null))) {
607
+ throw new Error("Missing required fields: id (string), method (string)");
608
+ }
609
+ const req = {
610
+ id: raw.id,
611
+ method: raw.method,
612
+ params: raw.params || {},
613
+ };
614
+ const res = handleRequest(db, req);
615
+ socket.write(JSON.stringify(res) + "\n");
616
+ }
617
+ catch (err) {
618
+ socket.write(JSON.stringify({
619
+ id: "error",
620
+ ok: false,
621
+ error: `Invalid request: ${err instanceof Error ? err.message : String(err)}`,
622
+ }) + "\n");
623
+ }
624
+ }
625
+ });
626
+ socket.on("error", () => {
627
+ // Client disconnected — that's fine
628
+ });
629
+ });
630
+ server.listen(socketPath, () => {
631
+ // Write PID file
632
+ fs.writeFileSync(getPidPath(), String(process.pid));
633
+ console.log(`Gnosys sandbox running (pid: ${process.pid}, socket: ${socketPath})`);
634
+ });
635
+ // Cleanup on exit
636
+ const cleanup = () => {
637
+ try {
638
+ if (dreamScheduler)
639
+ dreamScheduler.stop();
640
+ db.close();
641
+ if (fs.existsSync(socketPath))
642
+ fs.unlinkSync(socketPath);
643
+ if (fs.existsSync(getPidPath()))
644
+ fs.unlinkSync(getPidPath());
645
+ }
646
+ catch {
647
+ // Best effort
648
+ }
649
+ };
650
+ process.on("SIGTERM", () => { cleanup(); process.exit(0); });
651
+ process.on("SIGINT", () => { cleanup(); process.exit(0); });
652
+ process.on("exit", cleanup);
653
+ return server;
654
+ }
655
+ // ─── Entry point (when run directly) ─────────────────────────────────────
656
+ if (process.argv[1] && (process.argv[1].endsWith("sandbox/server.js") ||
657
+ process.argv[1].endsWith("sandbox/server.ts"))) {
658
+ const dbPath = process.argv.find((a) => a.startsWith("--db-path="))?.split("=")[1];
659
+ startServer(dbPath);
660
+ }
661
+ //# sourceMappingURL=server.js.map