engrams 0.1.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/README.md +140 -0
- package/dist/cli.js +2114 -0
- package/dist/http.js +255 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2113 -0
- package/package.json +60 -0
package/dist/http.js
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
// src/http.ts
|
|
2
|
+
import { createServer } from "http";
|
|
3
|
+
import { randomBytes } from "crypto";
|
|
4
|
+
import { eq, and, isNull } from "drizzle-orm";
|
|
5
|
+
|
|
6
|
+
// ../core/dist/schema.js
|
|
7
|
+
import { sqliteTable, text, real, integer } from "drizzle-orm/sqlite-core";
|
|
8
|
+
var memories = sqliteTable("memories", {
|
|
9
|
+
id: text("id").primaryKey(),
|
|
10
|
+
content: text("content").notNull(),
|
|
11
|
+
detail: text("detail"),
|
|
12
|
+
domain: text("domain").notNull().default("general"),
|
|
13
|
+
sourceAgentId: text("source_agent_id").notNull(),
|
|
14
|
+
sourceAgentName: text("source_agent_name").notNull(),
|
|
15
|
+
crossAgentId: text("cross_agent_id"),
|
|
16
|
+
crossAgentName: text("cross_agent_name"),
|
|
17
|
+
sourceType: text("source_type").notNull(),
|
|
18
|
+
sourceDescription: text("source_description"),
|
|
19
|
+
confidence: real("confidence").notNull().default(0.7),
|
|
20
|
+
confirmedCount: integer("confirmed_count").notNull().default(0),
|
|
21
|
+
correctedCount: integer("corrected_count").notNull().default(0),
|
|
22
|
+
mistakeCount: integer("mistake_count").notNull().default(0),
|
|
23
|
+
usedCount: integer("used_count").notNull().default(0),
|
|
24
|
+
learnedAt: text("learned_at"),
|
|
25
|
+
confirmedAt: text("confirmed_at"),
|
|
26
|
+
lastUsedAt: text("last_used_at"),
|
|
27
|
+
deletedAt: text("deleted_at"),
|
|
28
|
+
hasPiiFlag: integer("has_pii_flag").notNull().default(0),
|
|
29
|
+
entityType: text("entity_type"),
|
|
30
|
+
entityName: text("entity_name"),
|
|
31
|
+
structuredData: text("structured_data")
|
|
32
|
+
});
|
|
33
|
+
var memoryConnections = sqliteTable("memory_connections", {
|
|
34
|
+
sourceMemoryId: text("source_memory_id").notNull().references(() => memories.id),
|
|
35
|
+
targetMemoryId: text("target_memory_id").notNull().references(() => memories.id),
|
|
36
|
+
relationship: text("relationship").notNull()
|
|
37
|
+
});
|
|
38
|
+
var memoryEvents = sqliteTable("memory_events", {
|
|
39
|
+
id: text("id").primaryKey(),
|
|
40
|
+
memoryId: text("memory_id").notNull().references(() => memories.id),
|
|
41
|
+
eventType: text("event_type").notNull(),
|
|
42
|
+
agentId: text("agent_id"),
|
|
43
|
+
agentName: text("agent_name"),
|
|
44
|
+
oldValue: text("old_value"),
|
|
45
|
+
newValue: text("new_value"),
|
|
46
|
+
timestamp: text("timestamp").notNull()
|
|
47
|
+
});
|
|
48
|
+
var agentPermissions = sqliteTable("agent_permissions", {
|
|
49
|
+
agentId: text("agent_id").notNull(),
|
|
50
|
+
domain: text("domain").notNull(),
|
|
51
|
+
canRead: integer("can_read").notNull().default(1),
|
|
52
|
+
canWrite: integer("can_write").notNull().default(1)
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
// ../core/dist/confidence.js
|
|
56
|
+
var DECAY_INTERVAL_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
57
|
+
function applyConfirm(_current) {
|
|
58
|
+
return 0.99;
|
|
59
|
+
}
|
|
60
|
+
function applyCorrect() {
|
|
61
|
+
return 0.5;
|
|
62
|
+
}
|
|
63
|
+
function applyMistake(current) {
|
|
64
|
+
return Math.max(current - 0.15, 0.1);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// ../core/dist/db.js
|
|
68
|
+
import Database from "better-sqlite3";
|
|
69
|
+
import { drizzle } from "drizzle-orm/better-sqlite3";
|
|
70
|
+
|
|
71
|
+
// ../core/dist/vec.js
|
|
72
|
+
import { createRequire } from "module";
|
|
73
|
+
var require2 = createRequire(import.meta.url);
|
|
74
|
+
|
|
75
|
+
// ../core/dist/embeddings.js
|
|
76
|
+
var CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
77
|
+
|
|
78
|
+
// src/http.ts
|
|
79
|
+
function generateId() {
|
|
80
|
+
return randomBytes(16).toString("hex");
|
|
81
|
+
}
|
|
82
|
+
function now() {
|
|
83
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
84
|
+
}
|
|
85
|
+
function json(res, data, status = 200) {
|
|
86
|
+
res.writeHead(status, { "Content-Type": "application/json" });
|
|
87
|
+
res.end(JSON.stringify(data));
|
|
88
|
+
}
|
|
89
|
+
function parseBody(req) {
|
|
90
|
+
return new Promise((resolve, reject) => {
|
|
91
|
+
let body = "";
|
|
92
|
+
req.on("data", (chunk) => body += chunk);
|
|
93
|
+
req.on("end", () => {
|
|
94
|
+
try {
|
|
95
|
+
resolve(body ? JSON.parse(body) : {});
|
|
96
|
+
} catch {
|
|
97
|
+
reject(new Error("Invalid JSON"));
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
req.on("error", reject);
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
function startHttpApi(db, sqlite, port = 3838) {
|
|
104
|
+
const server = createServer(async (req, res) => {
|
|
105
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
106
|
+
res.setHeader("Access-Control-Allow-Methods", "POST, OPTIONS");
|
|
107
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
|
108
|
+
if (req.method === "OPTIONS") {
|
|
109
|
+
res.writeHead(200);
|
|
110
|
+
res.end();
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
const url = req.url ?? "";
|
|
114
|
+
try {
|
|
115
|
+
const confirmMatch = url.match(/^\/api\/memory\/([^/]+)\/confirm$/);
|
|
116
|
+
if (confirmMatch && req.method === "POST") {
|
|
117
|
+
const id = confirmMatch[1];
|
|
118
|
+
const existing = db.select().from(memories).where(and(eq(memories.id, id), isNull(memories.deletedAt))).get();
|
|
119
|
+
if (!existing) return json(res, { error: "Not found" }, 404);
|
|
120
|
+
const newConfidence = applyConfirm(existing.confidence);
|
|
121
|
+
const timestamp = now();
|
|
122
|
+
db.update(memories).set({
|
|
123
|
+
confidence: newConfidence,
|
|
124
|
+
confirmedCount: existing.confirmedCount + 1,
|
|
125
|
+
confirmedAt: timestamp
|
|
126
|
+
}).where(eq(memories.id, id)).run();
|
|
127
|
+
db.insert(memoryEvents).values({
|
|
128
|
+
id: generateId(),
|
|
129
|
+
memoryId: id,
|
|
130
|
+
eventType: "confirmed",
|
|
131
|
+
agentName: "dashboard",
|
|
132
|
+
oldValue: JSON.stringify({ confidence: existing.confidence }),
|
|
133
|
+
newValue: JSON.stringify({ confidence: newConfidence }),
|
|
134
|
+
timestamp
|
|
135
|
+
}).run();
|
|
136
|
+
return json(res, { id, newConfidence });
|
|
137
|
+
}
|
|
138
|
+
const correctMatch = url.match(/^\/api\/memory\/([^/]+)\/correct$/);
|
|
139
|
+
if (correctMatch && req.method === "POST") {
|
|
140
|
+
const id = correctMatch[1];
|
|
141
|
+
const body = await parseBody(req);
|
|
142
|
+
const content = body.content;
|
|
143
|
+
if (!content) return json(res, { error: "content required" }, 400);
|
|
144
|
+
const existing = db.select().from(memories).where(and(eq(memories.id, id), isNull(memories.deletedAt))).get();
|
|
145
|
+
if (!existing) return json(res, { error: "Not found" }, 404);
|
|
146
|
+
const newConfidence = applyCorrect();
|
|
147
|
+
const timestamp = now();
|
|
148
|
+
db.update(memories).set({
|
|
149
|
+
content,
|
|
150
|
+
confidence: newConfidence,
|
|
151
|
+
correctedCount: existing.correctedCount + 1
|
|
152
|
+
}).where(eq(memories.id, id)).run();
|
|
153
|
+
db.insert(memoryEvents).values({
|
|
154
|
+
id: generateId(),
|
|
155
|
+
memoryId: id,
|
|
156
|
+
eventType: "corrected",
|
|
157
|
+
agentName: "dashboard",
|
|
158
|
+
oldValue: JSON.stringify({ content: existing.content }),
|
|
159
|
+
newValue: JSON.stringify({ content, confidence: newConfidence }),
|
|
160
|
+
timestamp
|
|
161
|
+
}).run();
|
|
162
|
+
return json(res, { id, newConfidence });
|
|
163
|
+
}
|
|
164
|
+
const flagMatch = url.match(/^\/api\/memory\/([^/]+)\/flag$/);
|
|
165
|
+
if (flagMatch && req.method === "POST") {
|
|
166
|
+
const id = flagMatch[1];
|
|
167
|
+
const existing = db.select().from(memories).where(and(eq(memories.id, id), isNull(memories.deletedAt))).get();
|
|
168
|
+
if (!existing) return json(res, { error: "Not found" }, 404);
|
|
169
|
+
const newConfidence = applyMistake(existing.confidence);
|
|
170
|
+
const timestamp = now();
|
|
171
|
+
db.update(memories).set({
|
|
172
|
+
confidence: newConfidence,
|
|
173
|
+
mistakeCount: existing.mistakeCount + 1
|
|
174
|
+
}).where(eq(memories.id, id)).run();
|
|
175
|
+
db.insert(memoryEvents).values({
|
|
176
|
+
id: generateId(),
|
|
177
|
+
memoryId: id,
|
|
178
|
+
eventType: "confidence_changed",
|
|
179
|
+
agentName: "dashboard",
|
|
180
|
+
oldValue: JSON.stringify({ confidence: existing.confidence }),
|
|
181
|
+
newValue: JSON.stringify({ confidence: newConfidence, flaggedAsMistake: true }),
|
|
182
|
+
timestamp
|
|
183
|
+
}).run();
|
|
184
|
+
return json(res, { id, newConfidence });
|
|
185
|
+
}
|
|
186
|
+
const deleteMatch = url.match(/^\/api\/memory\/([^/]+)\/delete$/);
|
|
187
|
+
if (deleteMatch && req.method === "POST") {
|
|
188
|
+
const id = deleteMatch[1];
|
|
189
|
+
const timestamp = now();
|
|
190
|
+
db.update(memories).set({ deletedAt: timestamp }).where(eq(memories.id, id)).run();
|
|
191
|
+
db.insert(memoryEvents).values({
|
|
192
|
+
id: generateId(),
|
|
193
|
+
memoryId: id,
|
|
194
|
+
eventType: "removed",
|
|
195
|
+
agentName: "dashboard",
|
|
196
|
+
newValue: JSON.stringify({ reason: "deleted via dashboard" }),
|
|
197
|
+
timestamp
|
|
198
|
+
}).run();
|
|
199
|
+
return json(res, { id, deleted: true });
|
|
200
|
+
}
|
|
201
|
+
const updateMatch = url.match(/^\/api\/memory\/([^/]+)\/update$/);
|
|
202
|
+
if (updateMatch && req.method === "POST") {
|
|
203
|
+
const id = updateMatch[1];
|
|
204
|
+
const body = await parseBody(req);
|
|
205
|
+
const updates = {};
|
|
206
|
+
if (body.content) updates.content = body.content;
|
|
207
|
+
if (body.detail) updates.detail = body.detail;
|
|
208
|
+
if (body.domain) updates.domain = body.domain;
|
|
209
|
+
if (Object.keys(updates).length === 0)
|
|
210
|
+
return json(res, { error: "No fields" }, 400);
|
|
211
|
+
db.update(memories).set(updates).where(eq(memories.id, id)).run();
|
|
212
|
+
return json(res, { id, updated: true });
|
|
213
|
+
}
|
|
214
|
+
if (url === "/api/permissions" && req.method === "POST") {
|
|
215
|
+
const body = await parseBody(req);
|
|
216
|
+
const agentId = body.agentId;
|
|
217
|
+
const domain = body.domain;
|
|
218
|
+
const canRead = body.canRead !== false ? 1 : 0;
|
|
219
|
+
const canWrite = body.canWrite !== false ? 1 : 0;
|
|
220
|
+
const existing = db.select().from(agentPermissions).where(
|
|
221
|
+
and(
|
|
222
|
+
eq(agentPermissions.agentId, agentId),
|
|
223
|
+
eq(agentPermissions.domain, domain)
|
|
224
|
+
)
|
|
225
|
+
).get();
|
|
226
|
+
if (existing) {
|
|
227
|
+
db.update(agentPermissions).set({ canRead, canWrite }).where(
|
|
228
|
+
and(
|
|
229
|
+
eq(agentPermissions.agentId, agentId),
|
|
230
|
+
eq(agentPermissions.domain, domain)
|
|
231
|
+
)
|
|
232
|
+
).run();
|
|
233
|
+
} else {
|
|
234
|
+
db.insert(agentPermissions).values({ agentId, domain, canRead, canWrite }).run();
|
|
235
|
+
}
|
|
236
|
+
return json(res, { agentId, domain, canRead: !!canRead, canWrite: !!canWrite });
|
|
237
|
+
}
|
|
238
|
+
if (url === "/api/clear-all" && req.method === "POST") {
|
|
239
|
+
const timestamp = now();
|
|
240
|
+
sqlite.prepare(`UPDATE memories SET deleted_at = ? WHERE deleted_at IS NULL`).run(timestamp);
|
|
241
|
+
return json(res, { cleared: true });
|
|
242
|
+
}
|
|
243
|
+
json(res, { error: "Not found" }, 404);
|
|
244
|
+
} catch (e) {
|
|
245
|
+
const message = e instanceof Error ? e.message : "Unknown error";
|
|
246
|
+
json(res, { error: message }, 500);
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
server.listen(port, () => {
|
|
250
|
+
});
|
|
251
|
+
return server;
|
|
252
|
+
}
|
|
253
|
+
export {
|
|
254
|
+
startHttpApi
|
|
255
|
+
};
|
package/dist/index.d.ts
ADDED