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.
- package/LICENSE +21 -0
- package/README.md +1387 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +3753 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2267 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/archive.d.ts +95 -0
- package/dist/lib/archive.d.ts.map +1 -0
- package/dist/lib/archive.js +311 -0
- package/dist/lib/archive.js.map +1 -0
- package/dist/lib/ask.d.ts +77 -0
- package/dist/lib/ask.d.ts.map +1 -0
- package/dist/lib/ask.js +316 -0
- package/dist/lib/ask.js.map +1 -0
- package/dist/lib/audit.d.ts +47 -0
- package/dist/lib/audit.d.ts.map +1 -0
- package/dist/lib/audit.js +136 -0
- package/dist/lib/audit.js.map +1 -0
- package/dist/lib/bootstrap.d.ts +56 -0
- package/dist/lib/bootstrap.d.ts.map +1 -0
- package/dist/lib/bootstrap.js +163 -0
- package/dist/lib/bootstrap.js.map +1 -0
- package/dist/lib/config.d.ts +239 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +371 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/dashboard.d.ts +81 -0
- package/dist/lib/dashboard.d.ts.map +1 -0
- package/dist/lib/dashboard.js +314 -0
- package/dist/lib/dashboard.js.map +1 -0
- package/dist/lib/db.d.ts +182 -0
- package/dist/lib/db.d.ts.map +1 -0
- package/dist/lib/db.js +620 -0
- package/dist/lib/db.js.map +1 -0
- package/dist/lib/dbSearch.d.ts +65 -0
- package/dist/lib/dbSearch.d.ts.map +1 -0
- package/dist/lib/dbSearch.js +239 -0
- package/dist/lib/dbSearch.js.map +1 -0
- package/dist/lib/dbWrite.d.ts +56 -0
- package/dist/lib/dbWrite.d.ts.map +1 -0
- package/dist/lib/dbWrite.js +171 -0
- package/dist/lib/dbWrite.js.map +1 -0
- package/dist/lib/dream.d.ts +170 -0
- package/dist/lib/dream.d.ts.map +1 -0
- package/dist/lib/dream.js +706 -0
- package/dist/lib/dream.js.map +1 -0
- package/dist/lib/embeddings.d.ts +84 -0
- package/dist/lib/embeddings.d.ts.map +1 -0
- package/dist/lib/embeddings.js +226 -0
- package/dist/lib/embeddings.js.map +1 -0
- package/dist/lib/export.d.ts +92 -0
- package/dist/lib/export.d.ts.map +1 -0
- package/dist/lib/export.js +362 -0
- package/dist/lib/export.js.map +1 -0
- package/dist/lib/federated.d.ts +113 -0
- package/dist/lib/federated.d.ts.map +1 -0
- package/dist/lib/federated.js +346 -0
- package/dist/lib/federated.js.map +1 -0
- package/dist/lib/graph.d.ts +50 -0
- package/dist/lib/graph.d.ts.map +1 -0
- package/dist/lib/graph.js +118 -0
- package/dist/lib/graph.js.map +1 -0
- package/dist/lib/history.d.ts +39 -0
- package/dist/lib/history.d.ts.map +1 -0
- package/dist/lib/history.js +112 -0
- package/dist/lib/history.js.map +1 -0
- package/dist/lib/hybridSearch.d.ts +80 -0
- package/dist/lib/hybridSearch.d.ts.map +1 -0
- package/dist/lib/hybridSearch.js +296 -0
- package/dist/lib/hybridSearch.js.map +1 -0
- package/dist/lib/import.d.ts +52 -0
- package/dist/lib/import.d.ts.map +1 -0
- package/dist/lib/import.js +365 -0
- package/dist/lib/import.js.map +1 -0
- package/dist/lib/ingest.d.ts +51 -0
- package/dist/lib/ingest.d.ts.map +1 -0
- package/dist/lib/ingest.js +144 -0
- package/dist/lib/ingest.js.map +1 -0
- package/dist/lib/lensing.d.ts +35 -0
- package/dist/lib/lensing.d.ts.map +1 -0
- package/dist/lib/lensing.js +85 -0
- package/dist/lib/lensing.js.map +1 -0
- package/dist/lib/llm.d.ts +84 -0
- package/dist/lib/llm.d.ts.map +1 -0
- package/dist/lib/llm.js +386 -0
- package/dist/lib/llm.js.map +1 -0
- package/dist/lib/lock.d.ts +28 -0
- package/dist/lib/lock.d.ts.map +1 -0
- package/dist/lib/lock.js +145 -0
- package/dist/lib/lock.js.map +1 -0
- package/dist/lib/maintenance.d.ts +124 -0
- package/dist/lib/maintenance.d.ts.map +1 -0
- package/dist/lib/maintenance.js +587 -0
- package/dist/lib/maintenance.js.map +1 -0
- package/dist/lib/migrate.d.ts +19 -0
- package/dist/lib/migrate.d.ts.map +1 -0
- package/dist/lib/migrate.js +260 -0
- package/dist/lib/migrate.js.map +1 -0
- package/dist/lib/preferences.d.ts +49 -0
- package/dist/lib/preferences.d.ts.map +1 -0
- package/dist/lib/preferences.js +149 -0
- package/dist/lib/preferences.js.map +1 -0
- package/dist/lib/projectIdentity.d.ts +66 -0
- package/dist/lib/projectIdentity.d.ts.map +1 -0
- package/dist/lib/projectIdentity.js +148 -0
- package/dist/lib/projectIdentity.js.map +1 -0
- package/dist/lib/recall.d.ts +82 -0
- package/dist/lib/recall.d.ts.map +1 -0
- package/dist/lib/recall.js +289 -0
- package/dist/lib/recall.js.map +1 -0
- package/dist/lib/resolver.d.ts +116 -0
- package/dist/lib/resolver.d.ts.map +1 -0
- package/dist/lib/resolver.js +372 -0
- package/dist/lib/resolver.js.map +1 -0
- package/dist/lib/retry.d.ts +24 -0
- package/dist/lib/retry.d.ts.map +1 -0
- package/dist/lib/retry.js +60 -0
- package/dist/lib/retry.js.map +1 -0
- package/dist/lib/rulesGen.d.ts +51 -0
- package/dist/lib/rulesGen.d.ts.map +1 -0
- package/dist/lib/rulesGen.js +167 -0
- package/dist/lib/rulesGen.js.map +1 -0
- package/dist/lib/search.d.ts +51 -0
- package/dist/lib/search.d.ts.map +1 -0
- package/dist/lib/search.js +190 -0
- package/dist/lib/search.js.map +1 -0
- package/dist/lib/staticSearch.d.ts +70 -0
- package/dist/lib/staticSearch.d.ts.map +1 -0
- package/dist/lib/staticSearch.js +162 -0
- package/dist/lib/staticSearch.js.map +1 -0
- package/dist/lib/store.d.ts +79 -0
- package/dist/lib/store.d.ts.map +1 -0
- package/dist/lib/store.js +227 -0
- package/dist/lib/store.js.map +1 -0
- package/dist/lib/structuredIngest.d.ts +37 -0
- package/dist/lib/structuredIngest.d.ts.map +1 -0
- package/dist/lib/structuredIngest.js +208 -0
- package/dist/lib/structuredIngest.js.map +1 -0
- package/dist/lib/tags.d.ts +26 -0
- package/dist/lib/tags.d.ts.map +1 -0
- package/dist/lib/tags.js +109 -0
- package/dist/lib/tags.js.map +1 -0
- package/dist/lib/timeline.d.ts +34 -0
- package/dist/lib/timeline.d.ts.map +1 -0
- package/dist/lib/timeline.js +116 -0
- package/dist/lib/timeline.js.map +1 -0
- package/dist/lib/trace.d.ts +42 -0
- package/dist/lib/trace.d.ts.map +1 -0
- package/dist/lib/trace.js +338 -0
- package/dist/lib/trace.js.map +1 -0
- package/dist/lib/webIndex.d.ts +28 -0
- package/dist/lib/webIndex.d.ts.map +1 -0
- package/dist/lib/webIndex.js +208 -0
- package/dist/lib/webIndex.js.map +1 -0
- package/dist/lib/webIngest.d.ts +51 -0
- package/dist/lib/webIngest.d.ts.map +1 -0
- package/dist/lib/webIngest.js +533 -0
- package/dist/lib/webIngest.js.map +1 -0
- package/dist/lib/wikilinks.d.ts +63 -0
- package/dist/lib/wikilinks.d.ts.map +1 -0
- package/dist/lib/wikilinks.js +146 -0
- package/dist/lib/wikilinks.js.map +1 -0
- package/dist/sandbox/client.d.ts +82 -0
- package/dist/sandbox/client.d.ts.map +1 -0
- package/dist/sandbox/client.js +128 -0
- package/dist/sandbox/client.js.map +1 -0
- package/dist/sandbox/helper-template.d.ts +14 -0
- package/dist/sandbox/helper-template.d.ts.map +1 -0
- package/dist/sandbox/helper-template.js +285 -0
- package/dist/sandbox/helper-template.js.map +1 -0
- package/dist/sandbox/index.d.ts +10 -0
- package/dist/sandbox/index.d.ts.map +1 -0
- package/dist/sandbox/index.js +10 -0
- package/dist/sandbox/index.js.map +1 -0
- package/dist/sandbox/manager.d.ts +40 -0
- package/dist/sandbox/manager.d.ts.map +1 -0
- package/dist/sandbox/manager.js +220 -0
- package/dist/sandbox/manager.js.map +1 -0
- package/dist/sandbox/server.d.ts +44 -0
- package/dist/sandbox/server.d.ts.map +1 -0
- package/dist/sandbox/server.js +661 -0
- package/dist/sandbox/server.js.map +1 -0
- package/package.json +103 -0
- 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
|