shieldcortex 3.0.3 → 3.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.
Files changed (135) hide show
  1. package/README.md +5 -2
  2. package/dashboard/.next/standalone/dashboard/.next/BUILD_ID +1 -1
  3. package/dashboard/.next/standalone/dashboard/.next/build-manifest.json +2 -2
  4. package/dashboard/.next/standalone/dashboard/.next/prerender-manifest.json +3 -3
  5. package/dashboard/.next/standalone/dashboard/.next/required-server-files.json +4 -4
  6. package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.html +2 -2
  7. package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.rsc +1 -1
  8. package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  9. package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  10. package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  11. package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  12. package/dashboard/.next/standalone/dashboard/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  13. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  14. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.html +1 -1
  15. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.rsc +2 -2
  16. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  17. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  18. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  19. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  20. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  21. package/dashboard/.next/standalone/dashboard/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  22. package/dashboard/.next/standalone/dashboard/.next/server/app/index.html +1 -1
  23. package/dashboard/.next/standalone/dashboard/.next/server/app/index.rsc +3 -3
  24. package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  25. package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/_full.segment.rsc +3 -3
  26. package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/_head.segment.rsc +1 -1
  27. package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/_index.segment.rsc +2 -2
  28. package/dashboard/.next/standalone/dashboard/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  29. package/dashboard/.next/standalone/dashboard/.next/server/app/page/react-loadable-manifest.json +1 -1
  30. package/dashboard/.next/standalone/dashboard/.next/server/app/page_client-reference-manifest.js +1 -1
  31. package/dashboard/.next/standalone/dashboard/.next/server/chunks/ssr/dashboard_3051539d._.js +1 -1
  32. package/dashboard/.next/standalone/dashboard/.next/server/pages/404.html +1 -1
  33. package/dashboard/.next/standalone/dashboard/.next/server/pages/500.html +2 -2
  34. package/dashboard/.next/standalone/dashboard/.next/server/server-reference-manifest.js +1 -1
  35. package/dashboard/.next/standalone/dashboard/.next/server/server-reference-manifest.json +1 -1
  36. package/dashboard/.next/standalone/dashboard/.next/static/chunks/0a69eb25d08447ee.js +1 -0
  37. package/dashboard/.next/standalone/dashboard/.next/static/chunks/9232a2d99b47b21f.js +3 -0
  38. package/dashboard/.next/standalone/dashboard/.next/static/chunks/97537d3db46c8467.css +3 -0
  39. package/dashboard/.next/standalone/dashboard/.next/static/chunks/aa6e9b8a52353969.js +9 -0
  40. package/dashboard/.next/standalone/dashboard/node_modules/@img/sharp-darwin-arm64/lib/sharp-darwin-arm64.node +0 -0
  41. package/dashboard/.next/standalone/{node_modules/@img/sharp-linux-x64 → dashboard/node_modules/@img/sharp-darwin-arm64}/package.json +7 -13
  42. package/dashboard/.next/standalone/dashboard/node_modules/@img/{sharp-libvips-linux-x64 → sharp-libvips-darwin-arm64}/README.md +2 -2
  43. package/dashboard/.next/standalone/dashboard/node_modules/@img/{sharp-libvips-linux-x64 → sharp-libvips-darwin-arm64}/lib/glib-2.0/include/glibconfig.h +8 -9
  44. package/dashboard/.next/standalone/dashboard/node_modules/@img/{sharp-libvips-linux-x64/lib/libvips-cpp.so.8.17.3 → sharp-libvips-darwin-arm64/lib/libvips-cpp.8.17.3.dylib} +0 -0
  45. package/dashboard/.next/standalone/dashboard/node_modules/@img/{sharp-libvips-linux-x64 → sharp-libvips-darwin-arm64}/package.json +5 -11
  46. package/dashboard/.next/standalone/dashboard/server.js +1 -1
  47. package/dashboard/.next/standalone/{dashboard/node_modules/@img/sharp-linux-x64 → node_modules/@img/sharp-darwin-arm64}/package.json +7 -13
  48. package/dashboard/.next/standalone/node_modules/@img/{sharp-libvips-linux-x64 → sharp-libvips-darwin-arm64}/package.json +5 -11
  49. package/dist/api/routes/admin.d.ts +12 -0
  50. package/dist/api/routes/admin.js +502 -0
  51. package/dist/api/routes/graph.d.ts +4 -0
  52. package/dist/api/routes/graph.js +333 -0
  53. package/dist/api/routes/incidents.d.ts +2 -0
  54. package/dist/api/routes/incidents.js +32 -0
  55. package/dist/api/routes/memories.d.ts +4 -0
  56. package/dist/api/routes/memories.js +659 -0
  57. package/dist/api/routes/recall.d.ts +4 -0
  58. package/dist/api/routes/recall.js +36 -0
  59. package/dist/api/routes/system.d.ts +9 -0
  60. package/dist/api/routes/system.js +266 -0
  61. package/dist/api/visualization-server.js +31 -1913
  62. package/dist/cloud/cli.d.ts +1 -0
  63. package/dist/cloud/cli.js +40 -0
  64. package/dist/cloud/config.d.ts +10 -0
  65. package/dist/cloud/config.js +54 -0
  66. package/dist/cloud/graph-sync.d.ts +45 -0
  67. package/dist/cloud/graph-sync.js +257 -0
  68. package/dist/cloud/memory-sync.d.ts +36 -0
  69. package/dist/cloud/memory-sync.js +183 -0
  70. package/dist/cloud/sync-queue.d.ts +24 -0
  71. package/dist/cloud/sync-queue.js +126 -7
  72. package/dist/database/init.js +24 -0
  73. package/dist/graph/backfill.js +3 -5
  74. package/dist/graph/resolve.d.ts +10 -0
  75. package/dist/graph/resolve.js +63 -1
  76. package/dist/index.d.ts +2 -0
  77. package/dist/index.js +61 -4
  78. package/dist/memory/search.d.ts +37 -0
  79. package/dist/memory/search.js +143 -0
  80. package/dist/memory/store.js +47 -171
  81. package/dist/memory/types.d.ts +2 -0
  82. package/dist/service/install.d.ts +1 -0
  83. package/dist/service/install.js +43 -1
  84. package/dist/tools/recall.d.ts +1 -1
  85. package/hooks/openclaw/cortex-memory/handler.ts +5 -141
  86. package/hooks/openclaw/cortex-memory/runtime.mjs +129 -0
  87. package/package.json +8 -4
  88. package/plugins/openclaw/dist/index.js +5 -39
  89. package/scripts/run-jest.mjs +25 -1
  90. package/dashboard/.next/standalone/dashboard/.next/static/chunks/be6970da20a17c0b.js +0 -9
  91. package/dashboard/.next/standalone/dashboard/.next/static/chunks/e63d2228780629dd.css +0 -3
  92. package/dashboard/.next/standalone/dashboard/.next/static/chunks/f69fd1c5e71fbbfd.js +0 -1
  93. package/dashboard/.next/standalone/dashboard/.next/static/chunks/fa5217550a8ab9a6.js +0 -3
  94. package/dashboard/.next/standalone/dashboard/node_modules/@img/sharp-libvips-linuxmusl-x64/README.md +0 -46
  95. package/dashboard/.next/standalone/dashboard/node_modules/@img/sharp-libvips-linuxmusl-x64/lib/glib-2.0/include/glibconfig.h +0 -221
  96. package/dashboard/.next/standalone/dashboard/node_modules/@img/sharp-libvips-linuxmusl-x64/lib/index.js +0 -1
  97. package/dashboard/.next/standalone/dashboard/node_modules/@img/sharp-libvips-linuxmusl-x64/lib/libvips-cpp.so.8.17.3 +0 -0
  98. package/dashboard/.next/standalone/dashboard/node_modules/@img/sharp-libvips-linuxmusl-x64/package.json +0 -42
  99. package/dashboard/.next/standalone/dashboard/node_modules/@img/sharp-libvips-linuxmusl-x64/versions.json +0 -30
  100. package/dashboard/.next/standalone/dashboard/node_modules/@img/sharp-linux-x64/lib/sharp-linux-x64.node +0 -0
  101. package/dashboard/.next/standalone/dashboard/node_modules/@img/sharp-linuxmusl-x64/lib/sharp-linuxmusl-x64.node +0 -0
  102. package/dashboard/.next/standalone/dashboard/node_modules/@img/sharp-linuxmusl-x64/package.json +0 -46
  103. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/_tsc.js +0 -133818
  104. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/_tsserver.js +0 -659
  105. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/_typingsInstaller.js +0 -222
  106. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/cs/diagnosticMessages.generated.json +0 -2122
  107. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/de/diagnosticMessages.generated.json +0 -2122
  108. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/es/diagnosticMessages.generated.json +0 -2122
  109. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/fr/diagnosticMessages.generated.json +0 -2122
  110. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/it/diagnosticMessages.generated.json +0 -2122
  111. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/ja/diagnosticMessages.generated.json +0 -2122
  112. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/ko/diagnosticMessages.generated.json +0 -2122
  113. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/pl/diagnosticMessages.generated.json +0 -2122
  114. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/pt-br/diagnosticMessages.generated.json +0 -2122
  115. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/ru/diagnosticMessages.generated.json +0 -2122
  116. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/tr/diagnosticMessages.generated.json +0 -2122
  117. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/tsc.js +0 -8
  118. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/tsserver.js +0 -8
  119. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/tsserverlibrary.js +0 -21
  120. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/typesMap.json +0 -497
  121. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/typescript.js +0 -200276
  122. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/typingsInstaller.js +0 -8
  123. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/watchGuard.js +0 -53
  124. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/zh-cn/diagnosticMessages.generated.json +0 -2122
  125. package/dashboard/.next/standalone/dashboard/node_modules/typescript/lib/zh-tw/diagnosticMessages.generated.json +0 -2122
  126. package/dashboard/.next/standalone/dashboard/node_modules/typescript/package.json +0 -120
  127. package/dashboard/.next/standalone/node_modules/@img/sharp-libvips-linuxmusl-x64/package.json +0 -42
  128. package/dashboard/.next/standalone/node_modules/@img/sharp-linuxmusl-x64/package.json +0 -46
  129. package/scripts/start-dashboard.sh +0 -41
  130. package/scripts/stop-dashboard.sh +0 -21
  131. /package/dashboard/.next/standalone/dashboard/.next/static/{THy6JENQ0c1sq6jQhvIDp → RnvqrTXo_jN8SuMdaNcIj}/_buildManifest.js +0 -0
  132. /package/dashboard/.next/standalone/dashboard/.next/static/{THy6JENQ0c1sq6jQhvIDp → RnvqrTXo_jN8SuMdaNcIj}/_clientMiddlewareManifest.json +0 -0
  133. /package/dashboard/.next/standalone/dashboard/.next/static/{THy6JENQ0c1sq6jQhvIDp → RnvqrTXo_jN8SuMdaNcIj}/_ssgManifest.js +0 -0
  134. /package/dashboard/.next/standalone/dashboard/node_modules/@img/{sharp-libvips-linux-x64 → sharp-libvips-darwin-arm64}/lib/index.js +0 -0
  135. /package/dashboard/.next/standalone/dashboard/node_modules/@img/{sharp-libvips-linux-x64 → sharp-libvips-darwin-arm64}/versions.json +0 -0
@@ -0,0 +1,333 @@
1
+ import { getDatabase } from '../../database/init.js';
2
+ function parseAliases(raw) {
3
+ try {
4
+ return JSON.parse(raw || '[]');
5
+ }
6
+ catch {
7
+ return [];
8
+ }
9
+ }
10
+ export function registerGraphRoutes(app, requireNotLocked) {
11
+ app.get('/api/graph/entities', requireNotLocked, (req, res) => {
12
+ try {
13
+ const db = getDatabase();
14
+ const type = typeof req.query.type === 'string' ? req.query.type : undefined;
15
+ const minMentions = typeof req.query.minMentions === 'string' ? parseInt(req.query.minMentions, 10) : 0;
16
+ const limit = typeof req.query.limit === 'string' ? Math.min(parseInt(req.query.limit, 10), 500) : 100;
17
+ const offset = typeof req.query.offset === 'string' ? parseInt(req.query.offset, 10) : 0;
18
+ let whereClause = 'WHERE 1=1';
19
+ const params = [];
20
+ if (type) {
21
+ whereClause += ' AND type = ?';
22
+ params.push(type);
23
+ }
24
+ if (minMentions > 0) {
25
+ whereClause += ' AND memory_count >= ?';
26
+ params.push(minMentions);
27
+ }
28
+ const total = db.prepare(`SELECT COUNT(*) as count FROM entities ${whereClause}`).get(...params).count;
29
+ const rows = db.prepare(`SELECT * FROM entities ${whereClause} ORDER BY memory_count DESC LIMIT ? OFFSET ?`).all(...params, limit, offset);
30
+ res.json({
31
+ entities: rows.map((row) => ({
32
+ id: row.id,
33
+ name: row.name,
34
+ type: row.type,
35
+ memoryCount: row.memory_count ?? 0,
36
+ aliases: parseAliases(row.aliases),
37
+ createdAt: row.created_at,
38
+ updatedAt: row.updated_at,
39
+ })),
40
+ total,
41
+ offset,
42
+ limit,
43
+ hasMore: offset + limit < total,
44
+ });
45
+ }
46
+ catch (error) {
47
+ res.status(500).json({ error: error.message });
48
+ }
49
+ });
50
+ app.get('/api/graph/entities/:id/triples', requireNotLocked, (req, res) => {
51
+ try {
52
+ const db = getDatabase();
53
+ const id = parseInt(req.params.id, 10);
54
+ if (Number.isNaN(id)) {
55
+ return res.status(400).json({ error: 'Invalid entity ID' });
56
+ }
57
+ const rows = db.prepare(`
58
+ SELECT t.*, s.name as subject_name, s.type as subject_type,
59
+ o.name as object_name, o.type as object_type
60
+ FROM triples t
61
+ JOIN entities s ON s.id = t.subject_id
62
+ JOIN entities o ON o.id = t.object_id
63
+ WHERE t.subject_id = ? OR t.object_id = ?
64
+ ORDER BY t.created_at DESC
65
+ `).all(id, id);
66
+ res.json({ triples: rows });
67
+ }
68
+ catch (error) {
69
+ res.status(500).json({ error: error.message });
70
+ }
71
+ });
72
+ app.get('/api/graph/entities/:id/memories', requireNotLocked, (req, res) => {
73
+ try {
74
+ const db = getDatabase();
75
+ const id = parseInt(req.params.id, 10);
76
+ if (Number.isNaN(id)) {
77
+ return res.status(400).json({ error: 'Invalid entity ID' });
78
+ }
79
+ const rows = db.prepare(`
80
+ SELECT m.id, m.title, m.type, m.category, m.salience, m.created_at
81
+ FROM memories m
82
+ JOIN memory_entities me ON me.memory_id = m.id
83
+ WHERE me.entity_id = ?
84
+ ORDER BY m.salience DESC, m.created_at DESC
85
+ LIMIT 50
86
+ `).all(id);
87
+ res.json({ memories: rows });
88
+ }
89
+ catch (error) {
90
+ res.status(500).json({ error: error.message });
91
+ }
92
+ });
93
+ app.get('/api/graph/entities/:id/neighbourhood', requireNotLocked, (req, res) => {
94
+ try {
95
+ const db = getDatabase();
96
+ const id = parseInt(req.params.id, 10);
97
+ if (Number.isNaN(id)) {
98
+ return res.status(400).json({ error: 'Invalid entity ID' });
99
+ }
100
+ const focal = db.prepare('SELECT id, name, type, memory_count as memoryCount, aliases FROM entities WHERE id = ?').get(id);
101
+ if (!focal) {
102
+ return res.status(404).json({ error: 'Entity not found' });
103
+ }
104
+ focal.aliases = parseAliases(focal.aliases);
105
+ const triplesAll = db.prepare(`
106
+ SELECT t.id, t.subject_id, t.object_id, t.predicate,
107
+ s.name as subject_name, s.type as subject_type, s.memory_count as subject_count,
108
+ o.name as object_name, o.type as object_type, o.memory_count as object_count
109
+ FROM triples t
110
+ JOIN entities s ON s.id = t.subject_id
111
+ JOIN entities o ON o.id = t.object_id
112
+ WHERE (t.subject_id = ? OR t.object_id = ?)
113
+ ORDER BY
114
+ CASE WHEN t.predicate != 'related_to' THEN 0 ELSE 1 END,
115
+ CASE WHEN t.subject_id = ? THEN o.memory_count ELSE s.memory_count END DESC
116
+ `).all(id, id, id);
117
+ const neighbourIds = new Map();
118
+ const meaningfulTriples = [];
119
+ const relatedToTriples = [];
120
+ for (const triple of triplesAll) {
121
+ const neighbourId = triple.subject_id === id ? triple.object_id : triple.subject_id;
122
+ const count = triple.subject_id === id ? triple.object_count : triple.subject_count;
123
+ if (neighbourId === id)
124
+ continue;
125
+ if (triple.predicate !== 'related_to') {
126
+ meaningfulTriples.push(triple);
127
+ if (!neighbourIds.has(neighbourId)) {
128
+ neighbourIds.set(neighbourId, { predicate: triple.predicate, count });
129
+ }
130
+ }
131
+ else {
132
+ relatedToTriples.push(triple);
133
+ }
134
+ }
135
+ for (const triple of relatedToTriples) {
136
+ if (neighbourIds.size >= 25)
137
+ break;
138
+ const neighbourId = triple.subject_id === id ? triple.object_id : triple.subject_id;
139
+ const count = triple.subject_id === id ? triple.object_count : triple.subject_count;
140
+ if (!neighbourIds.has(neighbourId)) {
141
+ neighbourIds.set(neighbourId, { predicate: 'related_to', count });
142
+ }
143
+ }
144
+ const includedTriples = [
145
+ ...meaningfulTriples.filter((triple) => {
146
+ const neighbourId = triple.subject_id === id ? triple.object_id : triple.subject_id;
147
+ return neighbourIds.has(neighbourId);
148
+ }),
149
+ ...relatedToTriples.filter((triple) => {
150
+ const neighbourId = triple.subject_id === id ? triple.object_id : triple.subject_id;
151
+ return neighbourIds.has(neighbourId);
152
+ }),
153
+ ];
154
+ const seenTriples = new Set();
155
+ const uniqueTriples = includedTriples.filter((triple) => {
156
+ if (seenTriples.has(triple.id))
157
+ return false;
158
+ seenTriples.add(triple.id);
159
+ return true;
160
+ });
161
+ const neighbours = [];
162
+ if (neighbourIds.size > 0) {
163
+ const ids = [...neighbourIds.keys()];
164
+ const placeholders = ids.map(() => '?').join(',');
165
+ const rows = db.prepare(`
166
+ SELECT id, name, type, memory_count as memoryCount, aliases
167
+ FROM entities WHERE id IN (${placeholders})
168
+ `).all(...ids);
169
+ for (const row of rows) {
170
+ row.aliases = parseAliases(row.aliases);
171
+ neighbours.push(row);
172
+ }
173
+ }
174
+ res.json({
175
+ focal,
176
+ neighbours,
177
+ triples: uniqueTriples,
178
+ totalConnections: triplesAll.length,
179
+ });
180
+ }
181
+ catch (error) {
182
+ res.status(500).json({ error: error.message });
183
+ }
184
+ });
185
+ app.get('/api/graph/triples', requireNotLocked, (req, res) => {
186
+ try {
187
+ const db = getDatabase();
188
+ const predicate = typeof req.query.predicate === 'string' ? req.query.predicate : undefined;
189
+ const limit = typeof req.query.limit === 'string' ? Math.min(parseInt(req.query.limit, 10), 10000) : 100;
190
+ const offset = typeof req.query.offset === 'string' ? parseInt(req.query.offset, 10) : 0;
191
+ let whereClause = '';
192
+ const params = [];
193
+ if (predicate) {
194
+ whereClause = 'WHERE t.predicate = ?';
195
+ params.push(predicate);
196
+ }
197
+ const total = db.prepare(`SELECT COUNT(*) as count FROM triples t ${whereClause}`).get(...params).count;
198
+ const triples = db.prepare(`
199
+ SELECT t.*, s.name as subject_name, s.type as subject_type,
200
+ o.name as object_name, o.type as object_type
201
+ FROM triples t
202
+ JOIN entities s ON s.id = t.subject_id
203
+ JOIN entities o ON o.id = t.object_id
204
+ ${whereClause}
205
+ ORDER BY t.created_at DESC
206
+ LIMIT ? OFFSET ?
207
+ `).all(...params, limit, offset);
208
+ res.json({ triples, total, offset, limit, hasMore: offset + limit < total });
209
+ }
210
+ catch (error) {
211
+ res.status(500).json({ error: error.message });
212
+ }
213
+ });
214
+ app.get('/api/graph/search', requireNotLocked, (req, res) => {
215
+ try {
216
+ const db = getDatabase();
217
+ const q = typeof req.query.q === 'string' ? req.query.q : '';
218
+ if (!q) {
219
+ return res.status(400).json({ error: 'Query parameter "q" is required' });
220
+ }
221
+ const rows = db.prepare(`SELECT * FROM entities WHERE LOWER(name) LIKE ? ORDER BY memory_count DESC LIMIT 20`).all(`%${q.toLowerCase()}%`);
222
+ res.json({
223
+ entities: rows.map((row) => ({
224
+ id: row.id,
225
+ name: row.name,
226
+ type: row.type,
227
+ memoryCount: row.memory_count ?? 0,
228
+ aliases: parseAliases(row.aliases),
229
+ })),
230
+ });
231
+ }
232
+ catch (error) {
233
+ res.status(500).json({ error: error.message });
234
+ }
235
+ });
236
+ app.get('/api/graph/paths', requireNotLocked, (req, res) => {
237
+ try {
238
+ const db = getDatabase();
239
+ const fromName = typeof req.query.from === 'string' ? req.query.from : '';
240
+ const toName = typeof req.query.to === 'string' ? req.query.to : '';
241
+ if (!fromName || !toName) {
242
+ return res.status(400).json({ error: 'Both "from" and "to" query parameters are required' });
243
+ }
244
+ const fromRow = db.prepare('SELECT * FROM entities WHERE LOWER(name) = LOWER(?)').get(fromName);
245
+ if (!fromRow) {
246
+ return res.status(404).json({ error: `Entity "${fromName}" not found` });
247
+ }
248
+ const toRow = db.prepare('SELECT * FROM entities WHERE LOWER(name) = LOWER(?)').get(toName);
249
+ if (!toRow) {
250
+ return res.status(404).json({ error: `Entity "${toName}" not found` });
251
+ }
252
+ if (fromRow.id === toRow.id) {
253
+ return res.json({ path: [{ entity: fromRow.name, predicate: '(self)' }], sourceMemories: [] });
254
+ }
255
+ const maxDepth = 4;
256
+ const visited = new Map();
257
+ visited.set(fromRow.id, {
258
+ id: fromRow.id,
259
+ name: fromRow.name,
260
+ parentId: null,
261
+ predicate: '',
262
+ sourceMemoryId: null,
263
+ });
264
+ let frontier = [fromRow.id];
265
+ let found = false;
266
+ for (let depth = 0; depth < maxDepth && !found; depth++) {
267
+ const nextFrontier = [];
268
+ for (const nodeId of frontier) {
269
+ const outgoing = db.prepare('SELECT t.object_id as next_id, t.predicate, t.source_memory_id, e.name FROM triples t JOIN entities e ON e.id = t.object_id WHERE t.subject_id = ?').all(nodeId);
270
+ for (const row of outgoing) {
271
+ if (!visited.has(row.next_id)) {
272
+ visited.set(row.next_id, {
273
+ id: row.next_id,
274
+ name: row.name,
275
+ parentId: nodeId,
276
+ predicate: row.predicate,
277
+ sourceMemoryId: row.source_memory_id,
278
+ });
279
+ nextFrontier.push(row.next_id);
280
+ if (row.next_id === toRow.id) {
281
+ found = true;
282
+ break;
283
+ }
284
+ }
285
+ }
286
+ if (found)
287
+ break;
288
+ const incoming = db.prepare('SELECT t.subject_id as next_id, t.predicate, t.source_memory_id, e.name FROM triples t JOIN entities e ON e.id = t.subject_id WHERE t.object_id = ?').all(nodeId);
289
+ for (const row of incoming) {
290
+ if (!visited.has(row.next_id)) {
291
+ visited.set(row.next_id, {
292
+ id: row.next_id,
293
+ name: row.name,
294
+ parentId: nodeId,
295
+ predicate: `~${row.predicate}`,
296
+ sourceMemoryId: row.source_memory_id,
297
+ });
298
+ nextFrontier.push(row.next_id);
299
+ if (row.next_id === toRow.id) {
300
+ found = true;
301
+ break;
302
+ }
303
+ }
304
+ }
305
+ if (found)
306
+ break;
307
+ }
308
+ frontier = nextFrontier;
309
+ if (frontier.length === 0)
310
+ break;
311
+ }
312
+ if (!found) {
313
+ return res.json({ path: [], sourceMemories: [], message: 'No path found' });
314
+ }
315
+ const path = [];
316
+ const sourceMemoryIds = [];
317
+ let current = visited.get(toRow.id);
318
+ while (current) {
319
+ path.unshift({ entity: current.name, predicate: current.predicate });
320
+ if (current.sourceMemoryId)
321
+ sourceMemoryIds.push(current.sourceMemoryId);
322
+ current = current.parentId !== null ? visited.get(current.parentId) : undefined;
323
+ }
324
+ const sourceMemories = sourceMemoryIds.length > 0
325
+ ? db.prepare(`SELECT id, title FROM memories WHERE id IN (${sourceMemoryIds.map(() => '?').join(',')})`).all(...sourceMemoryIds)
326
+ : [];
327
+ res.json({ path, sourceMemories });
328
+ }
329
+ catch (error) {
330
+ res.status(500).json({ error: error.message });
331
+ }
332
+ });
333
+ }
@@ -0,0 +1,2 @@
1
+ import type { Express } from 'express';
2
+ export declare function registerIncidentRoutes(app: Express): void;
@@ -0,0 +1,32 @@
1
+ import { queryIncidentReplay } from '../../defence/audit/queries.js';
2
+ export function registerIncidentRoutes(app) {
3
+ app.get('/api/v1/incidents/replay', (req, res) => {
4
+ try {
5
+ const limit = Math.min(parseInt(req.query.limit, 10) || 200, 500);
6
+ const startTime = typeof req.query.startTime === 'string' ? req.query.startTime : undefined;
7
+ const endTime = typeof req.query.endTime === 'string' ? req.query.endTime : undefined;
8
+ const project = typeof req.query.project === 'string' ? req.query.project : undefined;
9
+ const sourceIdentifier = typeof req.query.sourceIdentifier === 'string' ? req.query.sourceIdentifier : undefined;
10
+ const memoryId = req.query.memoryId ? parseInt(req.query.memoryId, 10) : undefined;
11
+ const events = queryIncidentReplay({
12
+ startTime,
13
+ endTime,
14
+ project,
15
+ sourceIdentifier,
16
+ memoryId,
17
+ limit,
18
+ });
19
+ res.json({
20
+ events,
21
+ total: events.length,
22
+ coverage: {
23
+ sources: ['defence_audit', 'quarantine', 'events'],
24
+ note: 'Replay is best-effort. Durable audit and quarantine history is complete; generic event coverage depends on the retained events table window.',
25
+ },
26
+ });
27
+ }
28
+ catch (error) {
29
+ res.status(500).json({ error: error.message });
30
+ }
31
+ });
32
+ }
@@ -0,0 +1,4 @@
1
+ import type { Express, Request, Response } from 'express';
2
+ type Middleware = (_req: Request, res: Response, next: (err?: unknown) => void) => void;
3
+ export declare function registerMemoryRoutes(app: Express, requireNotLocked: Middleware): void;
4
+ export {};