ahok-skill 1.3.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.
Files changed (141) hide show
  1. package/.prettierrc +8 -0
  2. package/Dockerfile +59 -0
  3. package/RAW_SKILL.md +219 -0
  4. package/README.md +277 -0
  5. package/SKILL.md +58 -0
  6. package/bin/opm.js +268 -0
  7. package/data/openmemory.sqlite +0 -0
  8. package/data/openmemory.sqlite-shm +0 -0
  9. package/data/openmemory.sqlite-wal +0 -0
  10. package/dist/ai/graph.js +293 -0
  11. package/dist/ai/mcp.js +397 -0
  12. package/dist/cli.js +78 -0
  13. package/dist/core/cfg.js +87 -0
  14. package/dist/core/db.js +636 -0
  15. package/dist/core/memory.js +116 -0
  16. package/dist/core/migrate.js +227 -0
  17. package/dist/core/models.js +105 -0
  18. package/dist/core/telemetry.js +57 -0
  19. package/dist/core/types.js +2 -0
  20. package/dist/core/vector/postgres.js +52 -0
  21. package/dist/core/vector/valkey.js +246 -0
  22. package/dist/core/vector_store.js +2 -0
  23. package/dist/index.js +44 -0
  24. package/dist/memory/decay.js +301 -0
  25. package/dist/memory/embed.js +675 -0
  26. package/dist/memory/hsg.js +959 -0
  27. package/dist/memory/reflect.js +131 -0
  28. package/dist/memory/user_summary.js +99 -0
  29. package/dist/migrate.js +9 -0
  30. package/dist/ops/compress.js +255 -0
  31. package/dist/ops/dynamics.js +189 -0
  32. package/dist/ops/extract.js +333 -0
  33. package/dist/ops/ingest.js +214 -0
  34. package/dist/server/index.js +109 -0
  35. package/dist/server/middleware/auth.js +137 -0
  36. package/dist/server/routes/auth.js +186 -0
  37. package/dist/server/routes/compression.js +108 -0
  38. package/dist/server/routes/dashboard.js +399 -0
  39. package/dist/server/routes/docs.js +241 -0
  40. package/dist/server/routes/dynamics.js +312 -0
  41. package/dist/server/routes/ide.js +280 -0
  42. package/dist/server/routes/index.js +33 -0
  43. package/dist/server/routes/keys.js +132 -0
  44. package/dist/server/routes/langgraph.js +61 -0
  45. package/dist/server/routes/memory.js +213 -0
  46. package/dist/server/routes/sources.js +140 -0
  47. package/dist/server/routes/system.js +63 -0
  48. package/dist/server/routes/temporal.js +293 -0
  49. package/dist/server/routes/users.js +101 -0
  50. package/dist/server/routes/vercel.js +57 -0
  51. package/dist/server/server.js +211 -0
  52. package/dist/server.js +3 -0
  53. package/dist/sources/base.js +223 -0
  54. package/dist/sources/github.js +171 -0
  55. package/dist/sources/google_drive.js +166 -0
  56. package/dist/sources/google_sheets.js +112 -0
  57. package/dist/sources/google_slides.js +139 -0
  58. package/dist/sources/index.js +34 -0
  59. package/dist/sources/notion.js +165 -0
  60. package/dist/sources/onedrive.js +143 -0
  61. package/dist/sources/web_crawler.js +166 -0
  62. package/dist/temporal_graph/index.js +20 -0
  63. package/dist/temporal_graph/query.js +240 -0
  64. package/dist/temporal_graph/store.js +116 -0
  65. package/dist/temporal_graph/timeline.js +241 -0
  66. package/dist/temporal_graph/types.js +2 -0
  67. package/dist/utils/chunking.js +60 -0
  68. package/dist/utils/index.js +31 -0
  69. package/dist/utils/keyword.js +94 -0
  70. package/dist/utils/text.js +120 -0
  71. package/nodemon.json +7 -0
  72. package/package.json +50 -0
  73. package/references/api_reference.md +66 -0
  74. package/references/examples.md +45 -0
  75. package/src/ai/graph.ts +363 -0
  76. package/src/ai/mcp.ts +494 -0
  77. package/src/cli.ts +94 -0
  78. package/src/core/cfg.ts +110 -0
  79. package/src/core/db.ts +1052 -0
  80. package/src/core/memory.ts +99 -0
  81. package/src/core/migrate.ts +302 -0
  82. package/src/core/models.ts +107 -0
  83. package/src/core/telemetry.ts +47 -0
  84. package/src/core/types.ts +130 -0
  85. package/src/core/vector/postgres.ts +61 -0
  86. package/src/core/vector/valkey.ts +261 -0
  87. package/src/core/vector_store.ts +9 -0
  88. package/src/index.ts +5 -0
  89. package/src/memory/decay.ts +427 -0
  90. package/src/memory/embed.ts +707 -0
  91. package/src/memory/hsg.ts +1245 -0
  92. package/src/memory/reflect.ts +158 -0
  93. package/src/memory/user_summary.ts +110 -0
  94. package/src/migrate.ts +8 -0
  95. package/src/ops/compress.ts +296 -0
  96. package/src/ops/dynamics.ts +272 -0
  97. package/src/ops/extract.ts +360 -0
  98. package/src/ops/ingest.ts +286 -0
  99. package/src/server/index.ts +159 -0
  100. package/src/server/middleware/auth.ts +156 -0
  101. package/src/server/routes/auth.ts +223 -0
  102. package/src/server/routes/compression.ts +106 -0
  103. package/src/server/routes/dashboard.ts +420 -0
  104. package/src/server/routes/docs.ts +380 -0
  105. package/src/server/routes/dynamics.ts +516 -0
  106. package/src/server/routes/ide.ts +283 -0
  107. package/src/server/routes/index.ts +32 -0
  108. package/src/server/routes/keys.ts +131 -0
  109. package/src/server/routes/langgraph.ts +71 -0
  110. package/src/server/routes/memory.ts +440 -0
  111. package/src/server/routes/sources.ts +111 -0
  112. package/src/server/routes/system.ts +68 -0
  113. package/src/server/routes/temporal.ts +335 -0
  114. package/src/server/routes/users.ts +111 -0
  115. package/src/server/routes/vercel.ts +55 -0
  116. package/src/server/server.js +215 -0
  117. package/src/server.ts +1 -0
  118. package/src/sources/base.ts +257 -0
  119. package/src/sources/github.ts +156 -0
  120. package/src/sources/google_drive.ts +144 -0
  121. package/src/sources/google_sheets.ts +85 -0
  122. package/src/sources/google_slides.ts +115 -0
  123. package/src/sources/index.ts +19 -0
  124. package/src/sources/notion.ts +148 -0
  125. package/src/sources/onedrive.ts +131 -0
  126. package/src/sources/web_crawler.ts +161 -0
  127. package/src/temporal_graph/index.ts +4 -0
  128. package/src/temporal_graph/query.ts +299 -0
  129. package/src/temporal_graph/store.ts +156 -0
  130. package/src/temporal_graph/timeline.ts +319 -0
  131. package/src/temporal_graph/types.ts +41 -0
  132. package/src/utils/chunking.ts +66 -0
  133. package/src/utils/index.ts +25 -0
  134. package/src/utils/keyword.ts +137 -0
  135. package/src/utils/text.ts +115 -0
  136. package/tests/test_api_workspace_management.ts +413 -0
  137. package/tests/test_bulk_delete.ts +267 -0
  138. package/tests/test_omnibus.ts +166 -0
  139. package/tests/test_workspace_management.ts +278 -0
  140. package/tests/verify.ts +104 -0
  141. package/tsconfig.json +15 -0
@@ -0,0 +1,440 @@
1
+ import { q, vector_store } from "../../core/db";
2
+ import { now, rid, j, p } from "../../utils";
3
+ import {
4
+ add_hsg_memory,
5
+ hsg_query,
6
+ reinforce_memory,
7
+ update_memory,
8
+ } from "../../memory/hsg";
9
+ import { ingestDocument, ingestURL } from "../../ops/ingest";
10
+ import { env } from "../../core/cfg";
11
+ import { update_user_summary } from "../../memory/user_summary";
12
+ import type {
13
+ add_req,
14
+ q_req,
15
+ ingest_req,
16
+ ingest_url_req,
17
+ } from "../../core/types";
18
+
19
+ // Bulk operation interfaces
20
+ interface BulkDeleteRequest {
21
+ ids: string[];
22
+ user_id?: string;
23
+ }
24
+
25
+ interface BulkDeleteResponse {
26
+ deleted: number;
27
+ failed: string[];
28
+ }
29
+
30
+ // POST /memory/bulk-move - Move memories to different workspace
31
+ // Requirements: 7.1, 7.2
32
+ interface BulkMoveRequest {
33
+ ids: string[];
34
+ target_workspace_id: string | null; // null to orphan memories
35
+ user_id?: string;
36
+ }
37
+
38
+ interface BulkMoveResponse {
39
+ moved: number;
40
+ failed: string[];
41
+ }
42
+
43
+ export function mem(app: any) {
44
+ app.post("/memory/add", async (req: any, res: any) => {
45
+ const b = req.body as add_req & { memory_key_id?: string };
46
+ if (!b?.content) return res.status(400).json({ err: "content" });
47
+ try {
48
+ // Use memory_key_id from body if provided, otherwise use authenticated key's ID
49
+ const keyId = b.memory_key_id || req.key_id;
50
+ // Use user_id from body if provided, otherwise use authenticated user's ID
51
+ const userId = b.user_id || req.user?.user_id;
52
+
53
+ const m = await add_hsg_memory(
54
+ b.content,
55
+ j(b.tags || []),
56
+ b.metadata,
57
+ userId,
58
+ keyId, // Pass memory key ID
59
+ );
60
+ res.json(m);
61
+
62
+ if (b.user_id) {
63
+ update_user_summary(b.user_id).catch((e) =>
64
+ console.error("[mem] user summary update failed:", e),
65
+ );
66
+ }
67
+ } catch (e: any) {
68
+ res.status(500).json({ err: e.message });
69
+ }
70
+ });
71
+
72
+ app.post("/memory/ingest", async (req: any, res: any) => {
73
+ const b = req.body as ingest_req;
74
+ if (!b?.content_type || !b?.data)
75
+ return res.status(400).json({ err: "missing" });
76
+ try {
77
+ const userId = b.user_id || req.user?.user_id;
78
+ const r = await ingestDocument(
79
+ b.content_type,
80
+ b.data,
81
+ b.metadata,
82
+ b.config,
83
+ userId,
84
+ );
85
+ res.json(r);
86
+ } catch (e: any) {
87
+ res.status(500).json({ err: "ingest_fail", msg: e.message });
88
+ }
89
+ });
90
+
91
+ app.post("/memory/ingest/url", async (req: any, res: any) => {
92
+ const b = req.body as ingest_url_req;
93
+ if (!b?.url) return res.status(400).json({ err: "no_url" });
94
+ try {
95
+ const userId = b.user_id || req.user?.user_id;
96
+ const r = await ingestURL(b.url, b.metadata, b.config, userId);
97
+ res.json(r);
98
+ } catch (e: any) {
99
+ res.status(500).json({ err: "url_fail", msg: e.message });
100
+ }
101
+ });
102
+
103
+ app.post("/memory/query", async (req: any, res: any) => {
104
+ const b = req.body as q_req;
105
+ const k = b.k || 8;
106
+ try {
107
+ let userId = b.filters?.user_id || b.user_id;
108
+ // Enforce user isolation if authenticated and not admin
109
+ if (req.user && !req.user.is_admin) {
110
+ userId = req.user.user_id;
111
+ }
112
+
113
+ const f = {
114
+ sectors: b.filters?.sector ? [b.filters.sector] : undefined,
115
+ minSalience: b.filters?.min_score,
116
+ user_id: userId,
117
+ startTime: b.filters?.startTime,
118
+ endTime: b.filters?.endTime,
119
+ };
120
+ const m = await hsg_query(b.query, k, f);
121
+ res.json({
122
+ query: b.query,
123
+ matches: m.map((x: any) => ({
124
+ id: x.id,
125
+ content: x.content,
126
+ score: x.score,
127
+ sectors: x.sectors,
128
+ primary_sector: x.primary_sector,
129
+ path: x.path,
130
+ salience: x.salience,
131
+ last_seen_at: x.last_seen_at,
132
+ })),
133
+ });
134
+ } catch (e: any) {
135
+ res.json({ query: b.query, matches: [] });
136
+ }
137
+ });
138
+
139
+ app.post("/memory/reinforce", async (req: any, res: any) => {
140
+ const b = req.body as { id: string; boost?: number };
141
+ if (!b?.id) return res.status(400).json({ err: "id" });
142
+ try {
143
+ await reinforce_memory(b.id, b.boost);
144
+ res.json({ ok: true });
145
+ } catch (e: any) {
146
+ res.status(404).json({ err: "nf" });
147
+ }
148
+ });
149
+
150
+ app.patch("/memory/:id", async (req: any, res: any) => {
151
+ const id = req.params.id;
152
+ const b = req.body as {
153
+ content?: string;
154
+ tags?: string[];
155
+ metadata?: any;
156
+ user_id?: string;
157
+ };
158
+ if (!id) return res.status(400).json({ err: "id" });
159
+ try {
160
+ // Check if memory exists and user has permission
161
+ const m = await q.get_mem.get(id);
162
+ if (!m) return res.status(404).json({ err: "nf" });
163
+
164
+ // Enforce user ownership
165
+ if (req.user && !req.user.is_admin && m.user_id !== req.user.user_id) {
166
+ return res.status(403).json({ err: "forbidden" });
167
+ }
168
+ // Check explicit user_id match if provided (for admin or extra validation)
169
+ if (b.user_id && m.user_id !== b.user_id) {
170
+ return res.status(403).json({ err: "forbidden" });
171
+ }
172
+
173
+ const r = await update_memory(id, b.content, b.tags, b.metadata);
174
+ res.json(r);
175
+ } catch (e: any) {
176
+ if (e.message.includes("not found")) {
177
+ res.status(404).json({ err: "nf" });
178
+ } else {
179
+ res.status(500).json({ err: "internal" });
180
+ }
181
+ }
182
+ });
183
+
184
+ app.get("/memory/all", async (req: any, res: any) => {
185
+ try {
186
+ const u = req.query.u ? parseInt(req.query.u) : 0;
187
+ const l = req.query.l ? parseInt(req.query.l) : 100;
188
+ const s = req.query.sector;
189
+ let user_id = req.query.user_id;
190
+
191
+ // Enforce user isolation if authenticated and not admin
192
+ if (req.user && !req.user.is_admin) {
193
+ user_id = req.user.user_id;
194
+ }
195
+
196
+ let r;
197
+ if (user_id) {
198
+ // Filter by user_id
199
+ r = await q.all_mem_by_user.all(user_id, l, u);
200
+ } else if (s) {
201
+ // Filter by sector
202
+ r = await q.all_mem_by_sector.all(s, l, u);
203
+ } else {
204
+ // No filter
205
+ r = await q.all_mem.all(l, u);
206
+ }
207
+
208
+ if (req.query.key_id) {
209
+ r = r.filter((x: any) => x.memory_key_id === req.query.key_id);
210
+ }
211
+
212
+ const i = r.map((x: any) => ({
213
+ id: x.id,
214
+ content: x.content,
215
+ tags: p(x.tags),
216
+ metadata: p(x.meta),
217
+ created_at: x.created_at,
218
+ updated_at: x.updated_at,
219
+ last_seen_at: x.last_seen_at,
220
+ salience: x.salience,
221
+ decay_lambda: x.decay_lambda,
222
+ primary_sector: x.primary_sector,
223
+ version: x.version,
224
+ user_id: x.user_id,
225
+ memory_key_id: x.memory_key_id,
226
+ }));
227
+ res.json({ items: i });
228
+ } catch (e: any) {
229
+ res.status(500).json({ err: "internal" });
230
+ }
231
+ });
232
+
233
+ app.get("/memory/:id", async (req: any, res: any) => {
234
+ try {
235
+ const id = req.params.id;
236
+ const user_id = req.query.user_id;
237
+ const m = await q.get_mem.get(id);
238
+ if (!m) return res.status(404).json({ err: "nf" });
239
+
240
+ // Enforce user ownership
241
+ if (req.user && !req.user.is_admin && m.user_id !== req.user.user_id) {
242
+ return res.status(403).json({ err: "forbidden" });
243
+ }
244
+ // Check explicit user_id match if provided
245
+ if (user_id && m.user_id !== user_id) {
246
+ return res.status(403).json({ err: "forbidden" });
247
+ }
248
+
249
+ const v = await vector_store.getVectorsById(id);
250
+ const sec = v.map((x: any) => x.sector);
251
+ res.json({
252
+ id: m.id,
253
+ content: m.content,
254
+ primary_sector: m.primary_sector,
255
+ sectors: sec,
256
+ tags: p(m.tags),
257
+ metadata: p(m.meta),
258
+ created_at: m.created_at,
259
+ updated_at: m.updated_at,
260
+ last_seen_at: m.last_seen_at,
261
+ salience: m.salience,
262
+ decay_lambda: m.decay_lambda,
263
+ version: m.version,
264
+ user_id: m.user_id,
265
+ memory_key_id: m.memory_key_id,
266
+ });
267
+ } catch (e: any) {
268
+ res.status(500).json({ err: "internal" });
269
+ }
270
+ });
271
+
272
+ app.delete("/memory/:id", async (req: any, res: any) => {
273
+ try {
274
+ const id = req.params.id;
275
+ const user_id = req.query.user_id || req.body?.user_id;
276
+ const m = await q.get_mem.get(id);
277
+ if (!m) return res.status(404).json({ err: "nf" });
278
+
279
+ // Enforce user ownership
280
+ if (req.user && !req.user.is_admin && m.user_id !== req.user.user_id) {
281
+ return res.status(403).json({ err: "forbidden" });
282
+ }
283
+ // Check explicit user_id match if provided
284
+ if (user_id && m.user_id !== user_id) {
285
+ return res.status(403).json({ err: "forbidden" });
286
+ }
287
+
288
+ await q.del_mem.run(id);
289
+ await vector_store.deleteVectors(id);
290
+ await q.del_waypoints.run(id, id);
291
+ res.json({ ok: true });
292
+ } catch (e: any) {
293
+ res.status(500).json({ err: "internal" });
294
+ }
295
+ });
296
+
297
+ // POST /memory/bulk-delete - Delete multiple memories
298
+ // Requirements: 6.2, 6.3
299
+ app.post("/memory/bulk-delete", async (req: any, res: any) => {
300
+ const b = req.body as BulkDeleteRequest;
301
+
302
+ // Validate request body
303
+ if (!b?.ids || !Array.isArray(b.ids)) {
304
+ return res.status(400).json({ err: "ids array required" });
305
+ }
306
+
307
+ if (b.ids.length === 0) {
308
+ return res.json({ deleted: 0, failed: [] } as BulkDeleteResponse);
309
+ }
310
+
311
+ const user_id = b.user_id;
312
+ const deleted: string[] = [];
313
+ const failed: string[] = [];
314
+
315
+ // Process each memory individually for ownership verification and partial failure handling
316
+ for (const id of b.ids) {
317
+ try {
318
+ // Check if memory exists
319
+ const m = await q.get_mem.get(id);
320
+ if (!m) {
321
+ failed.push(id);
322
+ continue;
323
+ }
324
+
325
+ // Enforce user ownership
326
+ if (req.user && !req.user.is_admin && m.user_id !== req.user.user_id) {
327
+ failed.push(id);
328
+ continue;
329
+ }
330
+ // Check user ownership if explicit user_id is provided
331
+ if (user_id && m.user_id !== user_id) {
332
+ failed.push(id);
333
+ continue;
334
+ }
335
+
336
+ // Delete memory, vectors, and waypoints
337
+ await q.del_mem.run(id);
338
+ await vector_store.deleteVectors(id);
339
+ await q.del_waypoints.run(id, id);
340
+ deleted.push(id);
341
+ } catch (e: any) {
342
+ // Continue with remaining memories on individual failure
343
+ failed.push(id);
344
+ }
345
+ }
346
+
347
+ // Return partial success response (207 if there are failures, 200 if all succeeded)
348
+ const response: BulkDeleteResponse = {
349
+ deleted: deleted.length,
350
+ failed: failed,
351
+ };
352
+
353
+ if (failed.length > 0 && deleted.length > 0) {
354
+ // Partial success
355
+ return res.status(207).json(response);
356
+ } else if (failed.length > 0 && deleted.length === 0) {
357
+ // All failed
358
+ return res.status(207).json(response);
359
+ }
360
+
361
+ // All succeeded
362
+ res.json(response);
363
+ });
364
+
365
+ // POST /memory/bulk-move - Move memories to different workspace
366
+ // Requirements: 7.1, 7.2
367
+ app.post("/memory/bulk-move", async (req: any, res: any) => {
368
+ const b = req.body as BulkMoveRequest;
369
+
370
+ // Validate request body
371
+ if (!b?.ids || !Array.isArray(b.ids)) {
372
+ return res.status(400).json({ err: "ids array required" });
373
+ }
374
+
375
+ if (b.ids.length === 0) {
376
+ return res.json({ moved: 0, failed: [] } as BulkMoveResponse);
377
+ }
378
+
379
+ // target_workspace_id can be null (to orphan memories) or a string
380
+ if (b.target_workspace_id !== null && typeof b.target_workspace_id !== 'string') {
381
+ return res.status(400).json({ err: "target_workspace_id must be string or null" });
382
+ }
383
+
384
+ const user_id = b.user_id;
385
+ const moved: string[] = [];
386
+ const failed: string[] = [];
387
+
388
+ // Process each memory individually for ownership verification and partial failure handling
389
+ for (const id of b.ids) {
390
+ try {
391
+ // Check if memory exists
392
+ const m = await q.get_mem.get(id);
393
+ if (!m) {
394
+ failed.push(id);
395
+ continue;
396
+ }
397
+
398
+ // Enforce user ownership
399
+ if (req.user && !req.user.is_admin && m.user_id !== req.user.user_id) {
400
+ failed.push(id);
401
+ continue;
402
+ }
403
+ // Check user ownership if explicit user_id is provided
404
+ if (user_id && m.user_id !== user_id) {
405
+ failed.push(id);
406
+ continue;
407
+ }
408
+
409
+ // Update memory_key_id for this memory
410
+ await q.bulk_upd_mem_key.run(
411
+ b.target_workspace_id,
412
+ now(),
413
+ [id],
414
+ m.user_id
415
+ );
416
+ moved.push(id);
417
+ } catch (e: any) {
418
+ // Continue with remaining memories on individual failure
419
+ failed.push(id);
420
+ }
421
+ }
422
+
423
+ // Return partial success response (207 if there are failures, 200 if all succeeded)
424
+ const response: BulkMoveResponse = {
425
+ moved: moved.length,
426
+ failed: failed,
427
+ };
428
+
429
+ if (failed.length > 0 && moved.length > 0) {
430
+ // Partial success
431
+ return res.status(207).json(response);
432
+ } else if (failed.length > 0 && moved.length === 0) {
433
+ // All failed
434
+ return res.status(207).json(response);
435
+ }
436
+
437
+ // All succeeded
438
+ res.json(response);
439
+ });
440
+ }
@@ -0,0 +1,111 @@
1
+ /**
2
+ * sources webhook routes - ingest data from external sources via HTTP
3
+ *
4
+ * POST /sources/:source/ingest
5
+ * body: { creds: {...}, filters: {...}, user_id?: string }
6
+ *
7
+ * POST /sources/webhook/:source
8
+ * generic webhook endpoint for source-specific payloads
9
+ */
10
+
11
+ import * as sources from "../../sources";
12
+
13
+ export function src(app: any) {
14
+ // list available sources
15
+ app.get("/sources", async (req: any, res: any) => {
16
+ res.json({
17
+ sources: ["github", "notion", "google_drive", "google_sheets", "google_slides", "onedrive", "web_crawler"],
18
+ usage: {
19
+ ingest: "POST /sources/:source/ingest { creds: {}, filters: {}, user_id? }",
20
+ webhook: "POST /sources/webhook/:source (source-specific payload)"
21
+ }
22
+ });
23
+ });
24
+
25
+ // ingest from a source
26
+ app.post("/sources/:source/ingest", async (req: any, res: any) => {
27
+ const { source } = req.params;
28
+ const { creds = {}, filters = {}, user_id } = req.body || {};
29
+
30
+ const source_map: Record<string, any> = {
31
+ github: sources.github_source,
32
+ notion: sources.notion_source,
33
+ google_drive: sources.google_drive_source,
34
+ google_sheets: sources.google_sheets_source,
35
+ google_slides: sources.google_slides_source,
36
+ onedrive: sources.onedrive_source,
37
+ web_crawler: sources.web_crawler_source,
38
+ };
39
+
40
+ if (!source_map[source]) {
41
+ return res.status(400).json({ error: `unknown source: ${source}`, available: Object.keys(source_map) });
42
+ }
43
+
44
+ try {
45
+ const src = new source_map[source](user_id);
46
+ await src.connect(creds);
47
+ const ids = await src.ingest_all(filters);
48
+ res.json({ ok: true, ingested: ids.length, memory_ids: ids });
49
+ } catch (e: any) {
50
+ res.status(500).json({ error: e.message });
51
+ }
52
+ });
53
+
54
+ // webhook endpoint for github events
55
+ app.post("/sources/webhook/github", async (req: any, res: any) => {
56
+ const event_type = req.headers["x-github-event"];
57
+ const payload = req.body;
58
+
59
+ if (!payload) {
60
+ return res.status(400).json({ error: "no payload" });
61
+ }
62
+
63
+ try {
64
+ const { ingestDocument } = await import("../../ops/ingest");
65
+
66
+ // handle different github events
67
+ let content = "";
68
+ let meta: Record<string, any> = { source: "github_webhook", event: event_type };
69
+
70
+ if (event_type === "push") {
71
+ const commits = payload.commits || [];
72
+ content = commits.map((c: any) => `${c.message}\n${c.url}`).join("\n\n");
73
+ meta.repo = payload.repository?.full_name;
74
+ meta.ref = payload.ref;
75
+ } else if (event_type === "issues") {
76
+ content = `[${payload.action}] ${payload.issue?.title}\n${payload.issue?.body || ""}`;
77
+ meta.repo = payload.repository?.full_name;
78
+ meta.issue_number = payload.issue?.number;
79
+ } else if (event_type === "pull_request") {
80
+ content = `[${payload.action}] PR: ${payload.pull_request?.title}\n${payload.pull_request?.body || ""}`;
81
+ meta.repo = payload.repository?.full_name;
82
+ meta.pr_number = payload.pull_request?.number;
83
+ } else {
84
+ content = JSON.stringify(payload, null, 2);
85
+ }
86
+
87
+ if (content) {
88
+ const result = await ingestDocument("text", content, meta);
89
+ res.json({ ok: true, memory_id: result.root_memory_id, event: event_type });
90
+ } else {
91
+ res.json({ ok: true, skipped: true, reason: "no content" });
92
+ }
93
+ } catch (e: any) {
94
+ res.status(500).json({ error: e.message });
95
+ }
96
+ });
97
+
98
+ // generic webhook for notion
99
+ app.post("/sources/webhook/notion", async (req: any, res: any) => {
100
+ const payload = req.body;
101
+
102
+ try {
103
+ const { ingestDocument } = await import("../../ops/ingest");
104
+ const content = JSON.stringify(payload, null, 2);
105
+ const result = await ingestDocument("text", content, { source: "notion_webhook" });
106
+ res.json({ ok: true, memory_id: result.root_memory_id });
107
+ } catch (e: any) {
108
+ res.status(500).json({ error: e.message });
109
+ }
110
+ });
111
+ }
@@ -0,0 +1,68 @@
1
+ import { all_async } from "../../core/db";
2
+ import { sector_configs } from "../../memory/hsg";
3
+ import { getEmbeddingInfo } from "../../memory/embed";
4
+ import { tier, env } from "../../core/cfg";
5
+
6
+ const TIER_BENEFITS = {
7
+ hybrid: {
8
+ recall: 98,
9
+ qps: "700-800",
10
+ ram: "0.5gb/10k",
11
+ use: "For high accuracy",
12
+ },
13
+ fast: {
14
+ recall: 70,
15
+ qps: "700-850",
16
+ ram: "0.6GB/10k",
17
+ use: "Local apps, extensions",
18
+ },
19
+ smart: {
20
+ recall: 85,
21
+ qps: "500-600",
22
+ ram: "0.9GB/10k",
23
+ use: "Production servers",
24
+ },
25
+ deep: {
26
+ recall: 94,
27
+ qps: "350-400",
28
+ ram: "1.6GB/10k",
29
+ use: "Cloud, high-accuracy",
30
+ },
31
+ };
32
+
33
+ export function sys(app: any) {
34
+ app.get(
35
+ "/health",
36
+ async (incoming_http_request: any, outgoing_http_response: any) => {
37
+ outgoing_http_response.json({
38
+ ok: true,
39
+ version: "2.0-hsg-tiered",
40
+ embedding: getEmbeddingInfo(),
41
+ tier,
42
+ dim: env.vec_dim,
43
+ cache: env.cache_segments,
44
+ expected: TIER_BENEFITS[tier],
45
+ });
46
+ },
47
+ );
48
+
49
+ app.get(
50
+ "/sectors",
51
+ async (incoming_http_request: any, outgoing_http_response: any) => {
52
+ try {
53
+ const database_sector_statistics_rows = await all_async(`
54
+ select primary_sector as sector, count(*) as count, avg(salience) as avg_salience
55
+ from memories
56
+ group by primary_sector
57
+ `);
58
+ outgoing_http_response.json({
59
+ sectors: Object.keys(sector_configs),
60
+ configs: sector_configs,
61
+ stats: database_sector_statistics_rows,
62
+ });
63
+ } catch (unexpected_error_fetching_sectors) {
64
+ outgoing_http_response.status(500).json({ err: "internal" });
65
+ }
66
+ },
67
+ );
68
+ }