modular-studio 1.0.5 → 1.0.6

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 (79) hide show
  1. package/README.md +122 -122
  2. package/dist/assets/{Badge-22Ai0eyi.js → Badge-DrUmDAXz.js} +1 -1
  3. package/dist/assets/{Input-Bgp734xs.js → Input-ndEGQSgx.js} +1 -1
  4. package/dist/assets/KnowledgeTab-CxlC76Rf.js +4 -0
  5. package/dist/assets/MemoryTab-CUScYWs9.js +16 -0
  6. package/dist/assets/QualificationTab-BqnWSQHm.js +1 -0
  7. package/dist/assets/ReviewTab-DKYl6cR9.js +103 -0
  8. package/dist/assets/{Section-DoJrmytO.js → Section-CgmwAj_2.js} +1 -1
  9. package/dist/assets/{TestTab-PDyMF8Fw.js → TestTab-iJ2vCf9l.js} +15 -15
  10. package/dist/assets/ToolsTab-C10Ulm8b.js +1 -0
  11. package/dist/assets/icons-MKpPNvV8.js +1 -0
  12. package/dist/assets/index-B_ip7Amg.css +1 -0
  13. package/dist/assets/index-gBy3427k.js +143 -0
  14. package/dist/assets/services-CTWXQK6j.js +356 -0
  15. package/dist/index.html +18 -18
  16. package/dist-server/server/index.d.ts.map +1 -1
  17. package/dist-server/server/index.js +4 -0
  18. package/dist-server/server/mcp/manager.d.ts.map +1 -1
  19. package/dist-server/server/mcp/manager.js +16 -2
  20. package/dist-server/server/routes/agents.d.ts.map +1 -1
  21. package/dist-server/server/routes/agents.js +27 -0
  22. package/dist-server/server/routes/cache.d.ts +3 -0
  23. package/dist-server/server/routes/cache.d.ts.map +1 -0
  24. package/dist-server/server/routes/cache.js +55 -0
  25. package/dist-server/server/routes/connectors.d.ts.map +1 -1
  26. package/dist-server/server/routes/connectors.js +47 -17
  27. package/dist-server/server/routes/lessons.d.ts +3 -0
  28. package/dist-server/server/routes/lessons.d.ts.map +1 -0
  29. package/dist-server/server/routes/lessons.js +46 -0
  30. package/dist-server/server/routes/memory.d.ts.map +1 -1
  31. package/dist-server/server/routes/memory.js +31 -0
  32. package/dist-server/server/routes/qualification.d.ts.map +1 -1
  33. package/dist-server/server/routes/qualification.js +292 -334
  34. package/dist-server/server/routes/repo-index.d.ts.map +1 -1
  35. package/dist-server/server/routes/repo-index.js +7 -0
  36. package/dist-server/server/routes/skills-search.d.ts.map +1 -1
  37. package/dist-server/server/routes/skills-search.js +182 -26
  38. package/dist-server/server/services/adapters/hindsightAdapter.d.ts +28 -0
  39. package/dist-server/server/services/adapters/hindsightAdapter.d.ts.map +1 -0
  40. package/dist-server/server/services/adapters/hindsightAdapter.js +63 -0
  41. package/dist-server/server/services/adapters/postgresAdapter.js +30 -30
  42. package/dist-server/server/services/adapters/sqliteAdapter.js +29 -29
  43. package/dist-server/server/services/agentStore.d.ts +2 -1
  44. package/dist-server/server/services/agentStore.d.ts.map +1 -1
  45. package/dist-server/server/services/agentStore.js +2 -1
  46. package/dist-server/server/services/correctionDetector.d.ts +22 -0
  47. package/dist-server/server/services/correctionDetector.d.ts.map +1 -0
  48. package/dist-server/server/services/correctionDetector.js +91 -0
  49. package/dist-server/server/services/hindsightClient.d.ts +15 -0
  50. package/dist-server/server/services/hindsightClient.d.ts.map +1 -0
  51. package/dist-server/server/services/hindsightClient.js +47 -0
  52. package/dist-server/server/services/lessonExtractor.d.ts +19 -0
  53. package/dist-server/server/services/lessonExtractor.d.ts.map +1 -0
  54. package/dist-server/server/services/lessonExtractor.js +87 -0
  55. package/dist-server/server/services/responseCache.d.ts +24 -0
  56. package/dist-server/server/services/responseCache.d.ts.map +1 -0
  57. package/dist-server/server/services/responseCache.js +163 -0
  58. package/dist-server/server/services/sqliteStore.d.ts +8 -0
  59. package/dist-server/server/services/sqliteStore.d.ts.map +1 -1
  60. package/dist-server/server/services/sqliteStore.js +53 -13
  61. package/dist-server/src/store/knowledgeBase.d.ts +1 -0
  62. package/dist-server/src/store/knowledgeBase.d.ts.map +1 -1
  63. package/dist-server/src/store/lessonStore.d.ts +26 -0
  64. package/dist-server/src/store/lessonStore.d.ts.map +1 -0
  65. package/dist-server/src/store/lessonStore.js +64 -0
  66. package/dist-server/src/store/memoryStore.d.ts +12 -1
  67. package/dist-server/src/store/memoryStore.d.ts.map +1 -1
  68. package/dist-server/src/store/memoryStore.js +9 -0
  69. package/dist-server/tsconfig.server.tsbuildinfo +1 -1
  70. package/package.json +105 -104
  71. package/dist/assets/KnowledgeTab-DABxirZh.js +0 -4
  72. package/dist/assets/MemoryTab-DZeYElIT.js +0 -16
  73. package/dist/assets/QualificationTab-Dfpy3J30.js +0 -1
  74. package/dist/assets/ReviewTab-SD8lQuCc.js +0 -103
  75. package/dist/assets/ToolsTab-B83qGCmG.js +0 -1
  76. package/dist/assets/icons-C2EV-le6.js +0 -1
  77. package/dist/assets/index-DkpMAxX7.css +0 -1
  78. package/dist/assets/index-q24ug5Qs.js +0 -143
  79. package/dist/assets/services-BaKotDf0.js +0 -343
package/dist/index.html CHANGED
@@ -1,20 +1,20 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
- <link href="https://cdn.jsdelivr.net/npm/geist@1.3.1/dist/fonts/geist-sans/style.min.css" rel="stylesheet" />
8
- <link href="https://cdn.jsdelivr.net/npm/geist@1.3.1/dist/fonts/geist-mono/style.min.css" rel="stylesheet" />
9
- <title>MODULAR — Agent Patchbay</title>
10
- <script type="module" crossorigin src="/assets/index-q24ug5Qs.js"></script>
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <link href="https://cdn.jsdelivr.net/npm/geist@1.3.1/dist/fonts/geist-sans/style.min.css" rel="stylesheet" />
8
+ <link href="https://cdn.jsdelivr.net/npm/geist@1.3.1/dist/fonts/geist-mono/style.min.css" rel="stylesheet" />
9
+ <title>MODULAR — Agent Patchbay</title>
10
+ <script type="module" crossorigin src="/assets/index-gBy3427k.js"></script>
11
11
  <link rel="modulepreload" crossorigin href="/assets/vendor-D1h_O76p.js">
12
12
  <link rel="modulepreload" crossorigin href="/assets/stores-CeKWz7ou.js">
13
- <link rel="modulepreload" crossorigin href="/assets/services-BaKotDf0.js">
14
- <link rel="modulepreload" crossorigin href="/assets/icons-C2EV-le6.js">
15
- <link rel="stylesheet" crossorigin href="/assets/index-DkpMAxX7.css">
16
- </head>
17
- <body style="margin:0;padding:0;height:100vh;overflow:hidden">
18
- <div id="root" style="height:100%;overflow:hidden"></div>
19
- </body>
20
- </html>
13
+ <link rel="modulepreload" crossorigin href="/assets/services-CTWXQK6j.js">
14
+ <link rel="modulepreload" crossorigin href="/assets/icons-MKpPNvV8.js">
15
+ <link rel="stylesheet" crossorigin href="/assets/index-B_ip7Amg.css">
16
+ </head>
17
+ <body style="margin:0;padding:0;height:100vh;overflow:hidden">
18
+ <div id="root" style="height:100%;overflow:hidden"></div>
19
+ </body>
20
+ </html>
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../server/index.ts"],"names":[],"mappings":"AAgCA,wBAAgB,SAAS,gDA6FxB;AA+BD,wBAAgB,WAAW,CAAC,IAAI,GAAE,MAAa,sGAwB9C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../server/index.ts"],"names":[],"mappings":"AAkCA,wBAAgB,SAAS,gDA+FxB;AA+BD,wBAAgB,WAAW,CAAC,IAAI,GAAE,MAAa,sGAwB9C"}
@@ -27,6 +27,8 @@ import embeddingRoutes from './routes/embeddings.js';
27
27
  import embeddingService from './services/embeddingService.js';
28
28
  import conversationRoutes from './routes/conversations.js';
29
29
  import memoryRoutes from './routes/memory.js';
30
+ import cacheRoutes from './routes/cache.js';
31
+ import lessonRoutes from './routes/lessons.js';
30
32
  const __dirname = dirname(fileURLToPath(import.meta.url));
31
33
  export function createApp() {
32
34
  const app = express();
@@ -89,6 +91,8 @@ export function createApp() {
89
91
  app.use('/api/embeddings', embeddingRoutes);
90
92
  app.use('/api/conversations', conversationRoutes);
91
93
  app.use('/api/memory', memoryRoutes);
94
+ app.use('/api/cache', cacheRoutes);
95
+ app.use('/api/lessons', lessonRoutes);
92
96
  // API 404 catch-all — log unmatched API routes for debugging
93
97
  app.use('/api', (_req, res) => {
94
98
  console.warn(`[API 404] ${_req.method} ${_req.originalUrl}`);
@@ -1 +1 @@
1
- {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../../server/mcp/manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEjF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAGnD,UAAU,aAAa;IACrB,MAAM,EAAE,eAAe,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,oBAAoB,GAAG,IAAI,CAAC;IACvC,MAAM,EAAE,cAAc,GAAG,YAAY,GAAG,WAAW,GAAG,OAAO,CAAC;IAC9D,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAC5E,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,qBAAa,UAAU;IACrB,OAAO,CAAC,WAAW,CAAoC;IAGvD,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAElC;IAGH,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAEhC;IAEH,OAAO,CAAC,kBAAkB;IA0B1B,OAAO,CAAC,sBAAsB;IAc9B,OAAO,CAAC,eAAe;IASvB,SAAS,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI;IAkBxC,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAI9B,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAIhD,WAAW,IAAI,KAAK,CAAC,eAAe,GAAG;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;QAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IAS7G,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,CAAA;KAAE,CAAC;IA8F/E,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;IAWvF,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAc3C,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;QAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE;CAW1H;AAED,eAAO,MAAM,UAAU,YAAmB,CAAC"}
1
+ {"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../../server/mcp/manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEjF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAGnD,UAAU,aAAa;IACrB,MAAM,EAAE,eAAe,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,oBAAoB,GAAG,IAAI,CAAC;IACvC,MAAM,EAAE,cAAc,GAAG,YAAY,GAAG,WAAW,GAAG,OAAO,CAAC;IAC9D,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAC5E,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,qBAAa,UAAU;IACrB,OAAO,CAAC,WAAW,CAAoC;IAGvD,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAElC;IAGH,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAEhC;IAEH,OAAO,CAAC,kBAAkB;IAyC1B,OAAO,CAAC,sBAAsB;IAc9B,OAAO,CAAC,eAAe;IASvB,SAAS,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI;IAkBxC,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAI9B,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAIhD,WAAW,IAAI,KAAK,CAAC,eAAe,GAAG;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;QAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;IAS7G,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,CAAA;KAAE,CAAC;IA8F/E,QAAQ,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;IAWvF,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAc3C,SAAS,CAAC,EAAE,EAAE,MAAM,GAAG;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;QAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE;CAW1H;AAED,eAAO,MAAM,UAAU,YAAmB,CAAC"}
@@ -16,8 +16,22 @@ export class McpManager {
16
16
  if (!command) {
17
17
  throw new Error('MCP command cannot be empty');
18
18
  }
19
- // Extract base command (remove path prefixes)
20
- const baseCommand = command.split(/[/\\]/).pop()?.split('.')[0] || '';
19
+ // Extract base command (remove path prefixes and .cmd/.exe/.bat extensions)
20
+ const baseCommand = (command.split(/[/\\]/).pop() || '')
21
+ .replace(/\.(cmd|exe|bat)$/i, '')
22
+ .toLowerCase();
23
+ // On Windows, allow `cmd /c <allowed>` pattern — check that the actual program is allowed
24
+ if (baseCommand === 'cmd' && args.length >= 2) {
25
+ const cmdFlag = args[0].toLowerCase();
26
+ if (cmdFlag === '/c' || cmdFlag === '/k') {
27
+ const actualCommand = (args[1].split(/[/\\]/).pop() || '')
28
+ .replace(/\.(cmd|exe|bat)$/i, '')
29
+ .toLowerCase();
30
+ if (this.ALLOWED_MCP_COMMANDS.has(actualCommand)) {
31
+ return; // cmd /c npx ... is fine
32
+ }
33
+ }
34
+ }
21
35
  // Check if command is in allowlist or starts with allowed prefix
22
36
  const isAllowed = this.ALLOWED_MCP_COMMANDS.has(baseCommand) ||
23
37
  Array.from(this.ALLOWED_MCP_COMMANDS).some(allowed => command.startsWith(allowed));
@@ -1 +1 @@
1
- {"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../../server/routes/agents.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAeH,QAAA,MAAM,MAAM,4CAAW,CAAC;AA6HxB,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../../server/routes/agents.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAeH,QAAA,MAAM,MAAM,4CAAW,CAAC;AA0JxB,eAAe,MAAM,CAAC"}
@@ -73,6 +73,32 @@ router.put('/:id', (req, res) => {
73
73
  res.status(500).json({ status: 'error', error: err.message });
74
74
  }
75
75
  });
76
+ // Explicit save — always creates a version snapshot then persists new state
77
+ router.post('/:id/save', (req, res) => {
78
+ try {
79
+ const { state, label, changeSummary } = req.body;
80
+ if (!state || typeof state !== 'object') {
81
+ res.status(400).json({ status: 'error', error: 'Invalid body' });
82
+ return;
83
+ }
84
+ const existing = loadAgent(req.params.id);
85
+ const versionEntry = existing
86
+ ? createAgentVersion(req.params.id, existing.version, label, changeSummary)
87
+ : null;
88
+ saveAgent(req.params.id, state);
89
+ res.json({
90
+ status: 'ok',
91
+ data: {
92
+ id: req.params.id,
93
+ version: state.version,
94
+ versionId: versionEntry?.id ?? null,
95
+ },
96
+ });
97
+ }
98
+ catch (err) {
99
+ res.status(500).json({ status: 'error', error: err.message });
100
+ }
101
+ });
76
102
  // Delete agent
77
103
  router.delete('/:id', (req, res) => {
78
104
  try {
@@ -96,6 +122,7 @@ router.get('/:id/versions', (req, res) => {
96
122
  version: v.version,
97
123
  timestamp: v.timestamp,
98
124
  label: v.label,
125
+ changeSummary: v.changeSummary,
99
126
  }));
100
127
  res.json({ status: 'ok', data: formatted });
101
128
  }
@@ -0,0 +1,3 @@
1
+ declare const router: import("express-serve-static-core").Router;
2
+ export default router;
3
+ //# sourceMappingURL=cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../../server/routes/cache.ts"],"names":[],"mappings":"AAGA,QAAA,MAAM,MAAM,4CAAW,CAAC;AAqDxB,eAAe,MAAM,CAAC"}
@@ -0,0 +1,55 @@
1
+ import { Router } from 'express';
2
+ import { checkCache, storeResponse, getCacheStats, purgeCache, evictExpired, evictLRU } from '../services/responseCache.js';
3
+ const router = Router();
4
+ // GET /api/cache/stats
5
+ router.get('/stats', async (_req, res) => {
6
+ try {
7
+ const stats = await getCacheStats();
8
+ res.json({ status: 'success', ...stats });
9
+ }
10
+ catch (err) {
11
+ res.status(500).json({ status: 'error', error: err instanceof Error ? err.message : 'Unknown error' });
12
+ }
13
+ });
14
+ // DELETE /api/cache/purge?agentId=
15
+ router.delete('/purge', async (req, res) => {
16
+ try {
17
+ const agentId = typeof req.query['agentId'] === 'string' ? req.query['agentId'] : undefined;
18
+ await purgeCache(agentId);
19
+ res.json({ status: 'success' });
20
+ }
21
+ catch (err) {
22
+ res.status(500).json({ status: 'error', error: err instanceof Error ? err.message : 'Unknown error' });
23
+ }
24
+ });
25
+ // POST /api/cache/check — { query, agentId, model, systemPromptHash, ttl? }
26
+ router.post('/check', async (req, res) => {
27
+ try {
28
+ const { query, agentId, model, systemPromptHash, ttl } = req.body;
29
+ if (!query || !agentId || !model || !systemPromptHash) {
30
+ return res.status(400).json({ status: 'error', error: 'Missing required fields' });
31
+ }
32
+ const hit = await checkCache(query, agentId, model, systemPromptHash, ttl ?? 3600);
33
+ res.json({ status: 'success', hit: hit !== null, cached: hit });
34
+ }
35
+ catch (err) {
36
+ res.status(500).json({ status: 'error', error: err instanceof Error ? err.message : 'Unknown error' });
37
+ }
38
+ });
39
+ // POST /api/cache/store — { query, response, agentId, model, systemPromptHash, ttl? }
40
+ router.post('/store', async (req, res) => {
41
+ try {
42
+ const { query, response, agentId, model, systemPromptHash, ttl } = req.body;
43
+ if (!query || !response || !agentId || !model || !systemPromptHash) {
44
+ return res.status(400).json({ status: 'error', error: 'Missing required fields' });
45
+ }
46
+ await storeResponse(query, response, agentId, model, systemPromptHash, ttl ?? 3600);
47
+ await evictExpired();
48
+ await evictLRU(1000);
49
+ res.json({ status: 'success' });
50
+ }
51
+ catch (err) {
52
+ res.status(500).json({ status: 'error', error: err instanceof Error ? err.message : 'Unknown error' });
53
+ }
54
+ });
55
+ export default router;
@@ -1 +1 @@
1
- {"version":3,"file":"connectors.d.ts","sourceRoot":"","sources":["../../../server/routes/connectors.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,QAAA,MAAM,MAAM,4CAAW,CAAC;AA2jBxB,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"connectors.d.ts","sourceRoot":"","sources":["../../../server/routes/connectors.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,QAAA,MAAM,MAAM,4CAAW,CAAC;AA4lBxB,eAAe,MAAM,CAAC"}
@@ -487,25 +487,20 @@ router.post('/notion/fetch', async (req, res) => {
487
487
  ? body.pageUrls.filter((s) => typeof s === 'string')
488
488
  : [];
489
489
  try {
490
- let items = [];
491
- if (databaseIds.length > 0) {
492
- for (const dbId of databaseIds) {
493
- const rows = await queryDatabase(dbId.trim(), apiKey);
494
- items.push(...rows);
495
- }
490
+ const items = [];
491
+ for (const dbId of databaseIds) {
492
+ items.push(...(await queryDatabase(dbId.trim(), apiKey)));
496
493
  }
497
- else if (pageUrls.length > 0) {
498
- for (const url of pageUrls) {
499
- const pageId = extractPageId(url);
500
- if (!pageId)
501
- continue;
502
- const page = await fetchPage(pageId, apiKey);
503
- if (page)
504
- items.push(page);
505
- }
494
+ for (const url of pageUrls) {
495
+ const pageId = extractPageId(url);
496
+ if (!pageId)
497
+ continue;
498
+ const page = await fetchPage(pageId, apiKey);
499
+ if (page)
500
+ items.push(page);
506
501
  }
507
- else {
508
- items = await searchWorkspace(apiKey);
502
+ if (databaseIds.length === 0 && pageUrls.length === 0) {
503
+ items.push(...(await searchWorkspace(apiKey)));
509
504
  }
510
505
  res.json({ status: 'ok', data: items });
511
506
  }
@@ -518,4 +513,39 @@ router.post('/notion/fetch', async (req, res) => {
518
513
  res.status(500).json({ status: 'error', error: 'Failed to fetch from Notion. Check API key permissions.' });
519
514
  }
520
515
  });
516
+ /**
517
+ * POST /api/connectors/notion/search
518
+ * Search the Notion workspace by keyword.
519
+ * Body: { apiKey?, query: string }
520
+ */
521
+ router.post('/notion/search', async (req, res) => {
522
+ const body = req.body;
523
+ const apiKey = (typeof body.apiKey === 'string' && body.apiKey) || (sessionKeys.get('notion') ?? '');
524
+ if (!apiKey) {
525
+ res.status(401).json({ status: 'error', error: 'No API key. Test connection first.' });
526
+ return;
527
+ }
528
+ const query = typeof body.query === 'string' ? body.query : '';
529
+ try {
530
+ const resp = await fetch('https://api.notion.com/v1/search', {
531
+ method: 'POST', headers: notionHeaders(apiKey), body: JSON.stringify(query ? { query } : {}),
532
+ });
533
+ if (!resp.ok) {
534
+ res.status(resp.status).json({ status: 'error', error: `Notion search failed: ${resp.status}` });
535
+ return;
536
+ }
537
+ const data = await resp.json();
538
+ const results = data.results.map(r => ({
539
+ id: r.id,
540
+ title: r.object === 'database'
541
+ ? (r.title?.[0]?.plain_text ?? r.id)
542
+ : getPageTitle(r),
543
+ type: r.object,
544
+ }));
545
+ res.json({ status: 'ok', data: results });
546
+ }
547
+ catch {
548
+ res.status(500).json({ status: 'error', error: 'Failed to search Notion workspace.' });
549
+ }
550
+ });
521
551
  export default router;
@@ -0,0 +1,3 @@
1
+ declare const router: import("express-serve-static-core").Router;
2
+ export default router;
3
+ //# sourceMappingURL=lessons.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lessons.d.ts","sourceRoot":"","sources":["../../../server/routes/lessons.ts"],"names":[],"mappings":"AAOA,QAAA,MAAM,MAAM,4CAAW,CAAC;AA8DxB,eAAe,MAAM,CAAC"}
@@ -0,0 +1,46 @@
1
+ import { Router } from 'express';
2
+ import { readConfig } from '../config.js';
3
+ import { detectCorrection } from '../services/correctionDetector.js';
4
+ import { extractLesson } from '../services/lessonExtractor.js';
5
+ const router = Router();
6
+ function genId() {
7
+ return `lesson-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
8
+ }
9
+ router.post('/extract', async (req, res) => {
10
+ const { userMessage, previousAssistant, providerId, model, agentId } = req.body;
11
+ if (!userMessage || !providerId || !model) {
12
+ res.status(400).json({ error: 'Missing required fields' });
13
+ return;
14
+ }
15
+ const correction = detectCorrection(userMessage, previousAssistant ?? '');
16
+ if (!correction) {
17
+ res.json({ lesson: null });
18
+ return;
19
+ }
20
+ const config = readConfig();
21
+ const provider = config.providers.find((p) => p.id === providerId);
22
+ if (!provider) {
23
+ res.json({ lesson: null });
24
+ return;
25
+ }
26
+ try {
27
+ const extracted = await extractLesson(correction, { type: provider.type, baseUrl: provider.baseUrl, apiKey: provider.apiKey }, model);
28
+ if (!extracted) {
29
+ res.json({ lesson: null });
30
+ return;
31
+ }
32
+ const lesson = {
33
+ rule: extracted.rule,
34
+ category: extracted.category,
35
+ agentId: agentId ?? '',
36
+ sourceUserMessage: userMessage,
37
+ sourcePreviousAssistant: previousAssistant ?? '',
38
+ };
39
+ res.json({ lesson: { ...lesson, id: genId(), createdAt: Date.now(), appliedCount: 0, status: 'pending' } });
40
+ }
41
+ catch (err) {
42
+ const message = err instanceof Error ? err.message : 'Extraction failed';
43
+ res.status(500).json({ error: message });
44
+ }
45
+ });
46
+ export default router;
@@ -1 +1 @@
1
- {"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../../server/routes/memory.ts"],"names":[],"mappings":"AASA,QAAA,MAAM,MAAM,4CAAW,CAAC;AAmSxB,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../../server/routes/memory.ts"],"names":[],"mappings":"AAUA,QAAA,MAAM,MAAM,4CAAW,CAAC;AA8TxB,eAAe,MAAM,CAAC"}
@@ -1,6 +1,7 @@
1
1
  import { Router } from 'express';
2
2
  import { SqliteAdapter } from '../services/adapters/sqliteAdapter.js';
3
3
  import { PostgresAdapter } from '../services/adapters/postgresAdapter.js';
4
+ import { HindsightAdapter } from '../services/adapters/hindsightAdapter.js';
4
5
  import { extractFacts, extractFactsWithLlm } from '../services/factExtractor.js';
5
6
  import { rankFacts } from '../services/memoryScorer.js';
6
7
  import { readConfig, writeConfig } from '../config.js';
@@ -18,6 +19,9 @@ async function getAdapter() {
18
19
  if (currentBackend === 'postgres' && connectionString) {
19
20
  currentAdapter = new PostgresAdapter(connectionString);
20
21
  }
22
+ else if (currentBackend === 'hindsight') {
23
+ currentAdapter = new HindsightAdapter(connectionString ?? 'http://localhost:8888');
24
+ }
21
25
  else {
22
26
  currentAdapter = new SqliteAdapter();
23
27
  }
@@ -35,6 +39,9 @@ async function switchAdapter(backend, connStr) {
35
39
  if (backend === 'postgres' && connStr) {
36
40
  currentAdapter = new PostgresAdapter(connStr);
37
41
  }
42
+ else if (backend === 'hindsight') {
43
+ currentAdapter = new HindsightAdapter(connStr ?? 'http://localhost:8888');
44
+ }
38
45
  else {
39
46
  currentAdapter = new SqliteAdapter();
40
47
  }
@@ -239,6 +246,9 @@ router.post('/config', async (req, res) => {
239
246
  if (backend === 'postgres') {
240
247
  testAdapter = new PostgresAdapter(connStr);
241
248
  }
249
+ else if (backend === 'hindsight') {
250
+ testAdapter = new HindsightAdapter(connStr ?? 'http://localhost:8888');
251
+ }
242
252
  else {
243
253
  testAdapter = new SqliteAdapter();
244
254
  }
@@ -264,6 +274,27 @@ router.post('/config', async (req, res) => {
264
274
  });
265
275
  }
266
276
  });
277
+ // POST /api/memory/hindsight/reflect - generate higher-order insight (Hindsight only)
278
+ router.post('/hindsight/reflect', async (req, res) => {
279
+ try {
280
+ const { query } = req.body;
281
+ if (!query) {
282
+ return res.status(400).json({ status: 'error', error: 'Missing required field: query' });
283
+ }
284
+ const adapter = await getAdapter();
285
+ if (!(adapter instanceof HindsightAdapter)) {
286
+ return res.status(400).json({ status: 'error', error: 'reflect requires hindsight backend' });
287
+ }
288
+ const insight = await adapter.reflect(query);
289
+ res.json({ status: 'success', insight });
290
+ }
291
+ catch (error) {
292
+ res.status(500).json({
293
+ status: 'error',
294
+ error: error instanceof Error ? error.message : 'Unknown error'
295
+ });
296
+ }
297
+ });
267
298
  // GET /api/memory/health - backend health check (never 500 — always returns a health object)
268
299
  router.get('/health', async (_req, res) => {
269
300
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"qualification.d.ts","sourceRoot":"","sources":["../../../server/routes/qualification.ts"],"names":[],"mappings":"AAKA,QAAA,MAAM,MAAM,4CAAW,CAAC;AAijBxB,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"qualification.d.ts","sourceRoot":"","sources":["../../../server/routes/qualification.ts"],"names":[],"mappings":"AAOA,QAAA,MAAM,MAAM,4CAAW,CAAC;AAkexB,eAAe,MAAM,CAAC"}