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,335 @@
1
+ import { insert_fact, update_fact, invalidate_fact, delete_fact, apply_confidence_decay, get_active_facts_count, get_total_facts_count } from '../../temporal_graph/store'
2
+ import { query_facts_at_time, get_current_fact, query_facts_in_range, search_facts, get_facts_by_subject, get_related_facts } from '../../temporal_graph/query'
3
+ import { get_subject_timeline, get_predicate_timeline, get_changes_in_window, compare_time_points, get_change_frequency, get_volatile_facts } from '../../temporal_graph/timeline'
4
+
5
+ export const create_temporal_fact = async (req: any, res: any) => {
6
+ try {
7
+ const { subject, predicate, object, valid_from, confidence, metadata } = req.body
8
+
9
+ if (!subject || !predicate || !object) {
10
+ return res.status(400).json({ error: 'Missing required fields: subject, predicate, object' })
11
+ }
12
+
13
+ const valid_from_date = valid_from ? new Date(valid_from) : new Date()
14
+ const conf = confidence !== undefined ? Math.max(0, Math.min(1, confidence)) : 1.0
15
+
16
+ const id = await insert_fact(subject, predicate, object, valid_from_date, conf, metadata)
17
+
18
+ res.json({
19
+ id,
20
+ subject,
21
+ predicate,
22
+ object,
23
+ valid_from: valid_from_date.toISOString(),
24
+ confidence: conf,
25
+ message: 'Fact created successfully'
26
+ })
27
+ } catch (error) {
28
+ console.error('[TEMPORAL API] Error creating fact:', error)
29
+ res.status(500).json({ error: 'Failed to create fact' })
30
+ }
31
+ }
32
+
33
+
34
+ export const get_temporal_fact = async (req: any, res: any) => {
35
+ try {
36
+ const { subject, predicate, object, at, min_confidence } = req.query
37
+
38
+ if (!subject && !predicate && !object) {
39
+ return res.status(400).json({ error: 'At least one of subject, predicate, or object is required' })
40
+ }
41
+
42
+ const at_date = at ? new Date(at) : new Date()
43
+ const min_conf = min_confidence ? parseFloat(min_confidence) : 0.1
44
+
45
+ const facts = await query_facts_at_time(subject, predicate, object, at_date, min_conf)
46
+
47
+ res.json({
48
+ facts,
49
+ query: { subject, predicate, object, at: at_date.toISOString(), min_confidence: min_conf },
50
+ count: facts.length
51
+ })
52
+ } catch (error) {
53
+ console.error('[TEMPORAL API] Error querying facts:', error)
54
+ res.status(500).json({ error: 'Failed to query facts' })
55
+ }
56
+ }
57
+
58
+
59
+ export const get_current_temporal_fact = async (req: any, res: any) => {
60
+ try {
61
+ const { subject, predicate } = req.query
62
+
63
+ if (!subject || !predicate) {
64
+ return res.status(400).json({ error: 'Both subject and predicate are required' })
65
+ }
66
+
67
+ const fact = await get_current_fact(subject, predicate)
68
+
69
+ if (!fact) {
70
+ return res.status(404).json({ error: 'No current fact found', subject, predicate })
71
+ }
72
+
73
+ res.json({ fact })
74
+ } catch (error) {
75
+ console.error('[TEMPORAL API] Error getting current fact:', error)
76
+ res.status(500).json({ error: 'Failed to get current fact' })
77
+ }
78
+ }
79
+
80
+
81
+ export const get_entity_timeline = async (req: any, res: any) => {
82
+ try {
83
+ const { subject, predicate } = req.query
84
+
85
+ if (!subject) {
86
+ return res.status(400).json({ error: 'Subject parameter is required' })
87
+ }
88
+
89
+ const timeline = await get_subject_timeline(subject, predicate)
90
+
91
+ res.json({
92
+ subject,
93
+ predicate,
94
+ timeline,
95
+ count: timeline.length
96
+ })
97
+ } catch (error) {
98
+ console.error('[TEMPORAL API] Error getting timeline:', error)
99
+ res.status(500).json({ error: 'Failed to get timeline' })
100
+ }
101
+ }
102
+
103
+
104
+ export const get_predicate_history = async (req: any, res: any) => {
105
+ try {
106
+ const { predicate, from, to } = req.query
107
+
108
+ if (!predicate) {
109
+ return res.status(400).json({ error: 'Predicate parameter is required' })
110
+ }
111
+
112
+ const from_date = from ? new Date(from) : undefined
113
+ const to_date = to ? new Date(to) : undefined
114
+
115
+ const timeline = await get_predicate_timeline(predicate, from_date, to_date)
116
+
117
+ res.json({
118
+ predicate,
119
+ from: from_date?.toISOString(),
120
+ to: to_date?.toISOString(),
121
+ timeline,
122
+ count: timeline.length
123
+ })
124
+ } catch (error) {
125
+ console.error('[TEMPORAL API] Error getting predicate timeline:', error)
126
+ res.status(500).json({ error: 'Failed to get predicate timeline' })
127
+ }
128
+ }
129
+
130
+
131
+ export const update_temporal_fact = async (req: any, res: any) => {
132
+ try {
133
+ const { id } = req.params
134
+ const { confidence, metadata } = req.body
135
+
136
+ if (!id) {
137
+ return res.status(400).json({ error: 'Fact ID is required' })
138
+ }
139
+
140
+ if (confidence === undefined && metadata === undefined) {
141
+ return res.status(400).json({ error: 'At least one of confidence or metadata must be provided' })
142
+ }
143
+
144
+ const conf = confidence !== undefined ? Math.max(0, Math.min(1, confidence)) : undefined
145
+
146
+ await update_fact(id, conf, metadata)
147
+
148
+ res.json({ id, confidence: conf, metadata, message: 'Fact updated successfully' })
149
+ } catch (error) {
150
+ console.error('[TEMPORAL API] Error updating fact:', error)
151
+ res.status(500).json({ error: 'Failed to update fact' })
152
+ }
153
+ }
154
+
155
+
156
+ export const invalidate_temporal_fact = async (req: any, res: any) => {
157
+ try {
158
+ const { id } = req.params
159
+ const { valid_to } = req.body
160
+
161
+ if (!id) {
162
+ return res.status(400).json({ error: 'Fact ID is required' })
163
+ }
164
+
165
+ const valid_to_date = valid_to ? new Date(valid_to) : new Date()
166
+
167
+ await invalidate_fact(id, valid_to_date)
168
+
169
+ res.json({ id, valid_to: valid_to_date.toISOString(), message: 'Fact invalidated successfully' })
170
+ } catch (error) {
171
+ console.error('[TEMPORAL API] Error invalidating fact:', error)
172
+ res.status(500).json({ error: 'Failed to invalidate fact' })
173
+ }
174
+ }
175
+
176
+
177
+ export const get_subject_facts = async (req: any, res: any) => {
178
+ try {
179
+ const { subject } = req.params
180
+ const { at, include_historical } = req.query
181
+
182
+ if (!subject) {
183
+ return res.status(400).json({ error: 'Subject parameter is required' })
184
+ }
185
+
186
+ const at_date = at ? new Date(at) : undefined
187
+ const include_hist = include_historical === 'true'
188
+
189
+ const facts = await get_facts_by_subject(subject, at_date, include_hist)
190
+
191
+ res.json({
192
+ subject,
193
+ at: at_date?.toISOString(),
194
+ include_historical: include_hist,
195
+ facts,
196
+ count: facts.length
197
+ })
198
+ } catch (error) {
199
+ console.error('[TEMPORAL API] Error getting subject facts:', error)
200
+ res.status(500).json({ error: 'Failed to get subject facts' })
201
+ }
202
+ }
203
+
204
+
205
+ export const search_temporal_facts = async (req: any, res: any) => {
206
+ try {
207
+ const { pattern, field = 'subject', at } = req.query
208
+
209
+ if (!pattern) {
210
+ return res.status(400).json({ error: 'Pattern parameter is required' })
211
+ }
212
+
213
+ if (!['subject', 'predicate', 'object'].includes(field)) {
214
+ return res.status(400).json({ error: 'Field must be one of: subject, predicate, object' })
215
+ }
216
+
217
+ const at_date = at ? new Date(at) : undefined
218
+ const facts = await search_facts(pattern, field as any, at_date)
219
+
220
+ res.json({
221
+ pattern,
222
+ field,
223
+ at: at_date?.toISOString(),
224
+ facts,
225
+ count: facts.length
226
+ })
227
+ } catch (error) {
228
+ console.error('[TEMPORAL API] Error searching facts:', error)
229
+ res.status(500).json({ error: 'Failed to search facts' })
230
+ }
231
+ }
232
+
233
+
234
+ export const get_temporal_stats = async (req: any, res: any) => {
235
+ try {
236
+ const active_facts = await get_active_facts_count()
237
+ const total_facts = await get_total_facts_count()
238
+ const historical_facts = total_facts - active_facts
239
+
240
+ res.json({
241
+ active_facts,
242
+ historical_facts,
243
+ total_facts,
244
+ historical_percentage: total_facts > 0 ? ((historical_facts / total_facts) * 100).toFixed(2) + '%' : '0%'
245
+ })
246
+ } catch (error) {
247
+ console.error('[TEMPORAL API] Error getting stats:', error)
248
+ res.status(500).json({ error: 'Failed to get statistics' })
249
+ }
250
+ }
251
+
252
+
253
+ export const apply_decay = async (req: any, res: any) => {
254
+ try {
255
+ const { decay_rate = 0.01 } = req.body
256
+
257
+ const updated = await apply_confidence_decay(decay_rate)
258
+
259
+ res.json({
260
+ decay_rate,
261
+ facts_updated: updated,
262
+ message: 'Confidence decay applied successfully'
263
+ })
264
+ } catch (error) {
265
+ console.error('[TEMPORAL API] Error applying decay:', error)
266
+ res.status(500).json({ error: 'Failed to apply confidence decay' })
267
+ }
268
+ }
269
+
270
+
271
+ export const compare_facts = async (req: any, res: any) => {
272
+ try {
273
+ const { subject, time1, time2 } = req.query
274
+
275
+ if (!subject || !time1 || !time2) {
276
+ return res.status(400).json({ error: 'subject, time1, and time2 parameters are required' })
277
+ }
278
+
279
+ const t1 = new Date(time1)
280
+ const t2 = new Date(time2)
281
+
282
+ const comparison = await compare_time_points(subject, t1, t2)
283
+
284
+ res.json({
285
+ subject,
286
+ time1: t1.toISOString(),
287
+ time2: t2.toISOString(),
288
+ ...comparison,
289
+ summary: {
290
+ added: comparison.added.length,
291
+ removed: comparison.removed.length,
292
+ changed: comparison.changed.length,
293
+ unchanged: comparison.unchanged.length
294
+ }
295
+ })
296
+ } catch (error) {
297
+ console.error('[TEMPORAL API] Error comparing facts:', error)
298
+ res.status(500).json({ error: 'Failed to compare facts' })
299
+ }
300
+ }
301
+
302
+
303
+ export const get_most_volatile = async (req: any, res: any) => {
304
+ try {
305
+ const { subject, limit = 10 } = req.query
306
+
307
+ const volatile = await get_volatile_facts(subject, parseInt(limit))
308
+
309
+ res.json({
310
+ subject,
311
+ limit: parseInt(limit),
312
+ volatile_facts: volatile,
313
+ count: volatile.length
314
+ })
315
+ } catch (error) {
316
+ console.error('[TEMPORAL API] Error getting volatile facts:', error)
317
+ res.status(500).json({ error: 'Failed to get volatile facts' })
318
+ }
319
+ }
320
+
321
+ export function temporal(app: any) {
322
+ app.post('/api/temporal/fact', create_temporal_fact)
323
+ app.get('/api/temporal/fact', get_temporal_fact)
324
+ app.get('/api/temporal/fact/current', get_current_temporal_fact)
325
+ app.patch('/api/temporal/fact/:id', update_temporal_fact)
326
+ app.delete('/api/temporal/fact/:id', invalidate_temporal_fact)
327
+
328
+ app.get('/api/temporal/timeline', get_entity_timeline)
329
+ app.get('/api/temporal/subject/:subject', get_subject_facts)
330
+ app.get('/api/temporal/search', search_temporal_facts)
331
+ app.get('/api/temporal/compare', compare_facts)
332
+ app.get('/api/temporal/stats', get_temporal_stats)
333
+ app.post('/api/temporal/decay', apply_decay)
334
+ app.get('/api/temporal/volatile', get_most_volatile)
335
+ }
@@ -0,0 +1,111 @@
1
+ import { q, vector_store } from "../../core/db";
2
+ import { p } from "../../utils";
3
+ import {
4
+ update_user_summary,
5
+ auto_update_user_summaries,
6
+ } from "../../memory/user_summary";
7
+
8
+ export const usr = (app: any) => {
9
+ app.get("/users/:user_id/summary", async (req: any, res: any) => {
10
+ try {
11
+ const { user_id } = req.params;
12
+ if (!user_id)
13
+ return res.status(400).json({ error: "user_id required" });
14
+
15
+ const user = await q.get_user.get(user_id);
16
+ if (!user) return res.status(404).json({ error: "user not found" });
17
+
18
+ res.json({
19
+ user_id: user.user_id,
20
+ summary: user.summary,
21
+ reflection_count: user.reflection_count,
22
+ updated_at: user.updated_at,
23
+ });
24
+ } catch (err: any) {
25
+ res.status(500).json({ error: err.message });
26
+ }
27
+ });
28
+
29
+ app.post(
30
+ "/users/:user_id/summary/regenerate",
31
+ async (req: any, res: any) => {
32
+ try {
33
+ const { user_id } = req.params;
34
+ if (!user_id)
35
+ return res.status(400).json({ err: "user_id required" });
36
+
37
+ await update_user_summary(user_id);
38
+ const user = await q.get_user.get(user_id);
39
+
40
+ res.json({
41
+ ok: true,
42
+ user_id,
43
+ summary: user?.summary,
44
+ reflection_count: user?.reflection_count,
45
+ });
46
+ } catch (err: any) {
47
+ res.status(500).json({ err: err.message });
48
+ }
49
+ },
50
+ );
51
+
52
+ app.post("/users/summaries/regenerate-all", async (req: any, res: any) => {
53
+ try {
54
+ const result = await auto_update_user_summaries();
55
+ res.json({ ok: true, updated: result.updated });
56
+ } catch (err: any) {
57
+ res.status(500).json({ err: err.message });
58
+ }
59
+ });
60
+
61
+ app.get("/users/:user_id/memories", async (req: any, res: any) => {
62
+ try {
63
+ const { user_id } = req.params;
64
+ if (!user_id)
65
+ return res.status(400).json({ err: "user_id required" });
66
+
67
+ const l = req.query.l ? parseInt(req.query.l) : 100;
68
+ const u = req.query.u ? parseInt(req.query.u) : 0;
69
+
70
+ const r = await q.all_mem_by_user.all(user_id, l, u);
71
+ const i = r.map((x: any) => ({
72
+ id: x.id,
73
+ content: x.content,
74
+ tags: p(x.tags),
75
+ metadata: p(x.meta),
76
+ created_at: x.created_at,
77
+ updated_at: x.updated_at,
78
+ last_seen_at: x.last_seen_at,
79
+ salience: x.salience,
80
+ decay_lambda: x.decay_lambda,
81
+ primary_sector: x.primary_sector,
82
+ version: x.version,
83
+ }));
84
+ res.json({ user_id, items: i });
85
+ } catch (err: any) {
86
+ res.status(500).json({ err: err.message });
87
+ }
88
+ });
89
+
90
+ app.delete("/users/:user_id/memories", async (req: any, res: any) => {
91
+ try {
92
+ const { user_id } = req.params;
93
+ if (!user_id)
94
+ return res.status(400).json({ err: "user_id required" });
95
+
96
+ const mems = await q.all_mem_by_user.all(user_id, 10000, 0);
97
+ let deleted = 0;
98
+
99
+ for (const m of mems) {
100
+ await q.del_mem.run(m.id);
101
+ await vector_store.deleteVectors(m.id);
102
+ await q.del_waypoints.run(m.id, m.id);
103
+ deleted++;
104
+ }
105
+
106
+ res.json({ ok: true, deleted });
107
+ } catch (err: any) {
108
+ res.status(500).json({ err: err.message });
109
+ }
110
+ });
111
+ };
@@ -0,0 +1,55 @@
1
+ import { hsg_query, add_hsg_memory } from "../../memory/hsg";
2
+ import { j } from "../../utils";
3
+
4
+ export function vercel(app: any) {
5
+ // Simple memory query endpoint for Vercel AI SDK adapters
6
+ app.post("/query", async (req: any, res: any) => {
7
+ try {
8
+ const b = req.body || {};
9
+ const query: string = String(b.query || "").slice(0, 4000);
10
+ const user_id: string | undefined = b.user_id || req.query.user_id;
11
+ const k: number = Math.max(1, Math.min(32, Number(b.k) || 8));
12
+ if (!query) return res.status(400).json({ err: "query" });
13
+
14
+ const startTime: number | undefined = b.startTime ? Number(b.startTime) : undefined;
15
+ const endTime: number | undefined = b.endTime ? Number(b.endTime) : undefined;
16
+ const matches = await hsg_query(query, k, { user_id, startTime, endTime });
17
+ const lines = matches.map((m: any) => `- (${(m.score ?? 0).toFixed(2)}) ${m.content}`);
18
+ const result = lines.join("\n");
19
+
20
+ res.json({
21
+ query,
22
+ user_id: user_id || null,
23
+ k,
24
+ result,
25
+ matches: matches.map((m: any) => ({
26
+ id: m.id,
27
+ content: m.content,
28
+ score: m.score,
29
+ sectors: m.sectors,
30
+ primary_sector: m.primary_sector,
31
+ last_seen_at: m.last_seen_at,
32
+ })),
33
+ });
34
+ } catch (e: any) {
35
+ res.status(500).json({ err: "internal", msg: e?.message || String(e) });
36
+ }
37
+ });
38
+
39
+ // Simple memory store endpoint for chat transcripts or summaries
40
+ app.post("/memories", async (req: any, res: any) => {
41
+ try {
42
+ const b = req.body || {};
43
+ const content: string = String(b.content || "").trim();
44
+ const user_id: string | undefined = b.user_id || req.query.user_id;
45
+ const tags: string[] = Array.isArray(b.tags) ? b.tags : [];
46
+ const metadata: any = b.metadata || undefined;
47
+ if (!content) return res.status(400).json({ err: "content" });
48
+
49
+ const r = await add_hsg_memory(content, j(tags), metadata, user_id);
50
+ res.json(r);
51
+ } catch (e: any) {
52
+ res.status(500).json({ err: "internal", msg: e?.message || String(e) });
53
+ }
54
+ });
55
+ }