@poprobertdaniel/openclaw-memory 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -10
- package/dist/{chunk-RFLG2CCR.js → chunk-7DNVIKQ3.js} +100 -10
- package/dist/chunk-7DNVIKQ3.js.map +1 -0
- package/dist/{chunk-JSQBXYDM.js → chunk-BTR4T5L3.js} +157 -26
- package/dist/chunk-BTR4T5L3.js.map +1 -0
- package/dist/{chunk-VXULEX3A.cjs → chunk-CSGZH2SG.cjs} +9 -9
- package/dist/chunk-CSGZH2SG.cjs.map +1 -0
- package/dist/{chunk-JNWCMHOB.js → chunk-ITGUJZUL.js} +2 -2
- package/dist/{chunk-JNWCMHOB.js.map → chunk-ITGUJZUL.js.map} +1 -1
- package/dist/{chunk-NHFPLDZK.js → chunk-J2C5USXH.js} +3 -3
- package/dist/{chunk-CRPEAZ44.cjs → chunk-JYQB2DOK.cjs} +161 -30
- package/dist/chunk-JYQB2DOK.cjs.map +1 -0
- package/dist/{chunk-ZY2C2CJQ.cjs → chunk-LA5OP5VI.cjs} +2 -2
- package/dist/chunk-LA5OP5VI.cjs.map +1 -0
- package/dist/{chunk-NMUPGLJW.cjs → chunk-NYZMAY73.cjs} +110 -20
- package/dist/chunk-NYZMAY73.cjs.map +1 -0
- package/dist/cli/index.cjs +25 -25
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +7 -7
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +5 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +64 -2
- package/dist/index.d.ts +64 -2
- package/dist/index.js +4 -4
- package/dist/memory-service-B2BAEKR2.cjs +9 -0
- package/dist/memory-service-B2BAEKR2.cjs.map +1 -0
- package/dist/memory-service-ZTLGPIUH.js +9 -0
- package/dist/{server-BTbRv-yX.d.ts → server-CtNlCow7.d.cts} +62 -0
- package/dist/{server-BTbRv-yX.d.cts → server-CtNlCow7.d.ts} +62 -0
- package/dist/server.cjs +4 -4
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.js +3 -3
- package/package.json +2 -2
- package/templates/.env.example +1 -1
- package/templates/openclaw-memory.config.ts +2 -2
- package/dist/chunk-CRPEAZ44.cjs.map +0 -1
- package/dist/chunk-JSQBXYDM.js.map +0 -1
- package/dist/chunk-NMUPGLJW.cjs.map +0 -1
- package/dist/chunk-RFLG2CCR.js.map +0 -1
- package/dist/chunk-VXULEX3A.cjs.map +0 -1
- package/dist/chunk-ZY2C2CJQ.cjs.map +0 -1
- package/dist/memory-service-6WDMF6KX.cjs +0 -9
- package/dist/memory-service-6WDMF6KX.cjs.map +0 -1
- package/dist/memory-service-GKEG6J2D.js +0 -9
- /package/dist/{chunk-NHFPLDZK.js.map → chunk-J2C5USXH.js.map} +0 -0
- /package/dist/{memory-service-GKEG6J2D.js.map → memory-service-ZTLGPIUH.js.map} +0 -0
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
**Triple-layer memory for AI agents.**
|
|
6
6
|
Local cache · Semantic search · Knowledge graph
|
|
7
7
|
|
|
8
|
-
[](https://www.npmjs.com/package/openclaw-memory)
|
|
8
|
+
[](https://www.npmjs.com/package/@poprobertdaniel/openclaw-memory)
|
|
9
9
|
[](https://opensource.org/licenses/MIT)
|
|
10
10
|
[](https://www.typescriptlang.org/)
|
|
11
11
|
[](https://bun.sh/)
|
|
@@ -23,7 +23,7 @@ openclaw-memory is a standalone memory service with three storage layers — **S
|
|
|
23
23
|
Start with zero dependencies (SQLite only). Scale to vectors and graphs when ready.
|
|
24
24
|
|
|
25
25
|
```ts
|
|
26
|
-
import { MemoryService } from 'openclaw-memory';
|
|
26
|
+
import { MemoryService } from '@poprobertdaniel/openclaw-memory';
|
|
27
27
|
|
|
28
28
|
const memory = new MemoryService({ tier: 'lite' });
|
|
29
29
|
await memory.init();
|
|
@@ -85,19 +85,19 @@ const results = await memory.search({
|
|
|
85
85
|
### 1. Install
|
|
86
86
|
|
|
87
87
|
```bash
|
|
88
|
-
bun add openclaw-memory
|
|
88
|
+
bun add @poprobertdaniel/openclaw-memory
|
|
89
89
|
# or
|
|
90
|
-
npm install openclaw-memory
|
|
90
|
+
npm install @poprobertdaniel/openclaw-memory
|
|
91
91
|
```
|
|
92
92
|
|
|
93
93
|
### 2. Initialize
|
|
94
94
|
|
|
95
95
|
```bash
|
|
96
96
|
# Interactive setup wizard
|
|
97
|
-
npx openclaw-memory init
|
|
97
|
+
npx @poprobertdaniel/openclaw-memory init
|
|
98
98
|
|
|
99
99
|
# Or start immediately with defaults (Lite mode)
|
|
100
|
-
npx openclaw-memory start
|
|
100
|
+
npx @poprobertdaniel/openclaw-memory start
|
|
101
101
|
```
|
|
102
102
|
|
|
103
103
|
### 3. Use the CLI
|
|
@@ -117,7 +117,7 @@ openclaw-memory status
|
|
|
117
117
|
### 4. Or use the programmatic API
|
|
118
118
|
|
|
119
119
|
```ts
|
|
120
|
-
import { MemoryService } from 'openclaw-memory';
|
|
120
|
+
import { MemoryService } from '@poprobertdaniel/openclaw-memory';
|
|
121
121
|
|
|
122
122
|
const memory = new MemoryService({
|
|
123
123
|
tier: 'lite',
|
|
@@ -286,7 +286,7 @@ All endpoints accept/return JSON. Authentication via `Authorization: Bearer <tok
|
|
|
286
286
|
Create `openclaw-memory.config.ts` in your project root:
|
|
287
287
|
|
|
288
288
|
```ts
|
|
289
|
-
import { defineConfig } from 'openclaw-memory';
|
|
289
|
+
import { defineConfig } from '@poprobertdaniel/openclaw-memory';
|
|
290
290
|
|
|
291
291
|
export default defineConfig({
|
|
292
292
|
tier: 'standard',
|
|
@@ -308,7 +308,7 @@ export default defineConfig({
|
|
|
308
308
|
},
|
|
309
309
|
extraction: {
|
|
310
310
|
apiKey: process.env.OPENAI_API_KEY,
|
|
311
|
-
model: 'gpt-
|
|
311
|
+
model: 'gpt-5-nano',
|
|
312
312
|
enabled: true,
|
|
313
313
|
},
|
|
314
314
|
});
|
|
@@ -346,6 +346,18 @@ PGDATABASE=agent_memory
|
|
|
346
346
|
|
|
347
347
|
### Docker Compose
|
|
348
348
|
|
|
349
|
+
### Defaults
|
|
350
|
+
|
|
351
|
+
| Setting | Default | Override |
|
|
352
|
+
|---------|---------|----------|
|
|
353
|
+
| Port | `7777` | `OPENCLAW_MEMORY_PORT` |
|
|
354
|
+
| SQLite path | `~/.openclaw-memory/memory.sqlite` | `SQLITE_PATH` |
|
|
355
|
+
| Qdrant collection | `openclaw_memories` | `QDRANT_COLLECTION` |
|
|
356
|
+
| AGE graph | `agent_memory` | `AGE_GRAPH` |
|
|
357
|
+
| Embedding model | `text-embedding-3-small` (1536 dims) | `EMBEDDING_MODEL` |
|
|
358
|
+
| Entity extraction model | `gpt-5-nano` | `EXTRACTION_MODEL` |
|
|
359
|
+
| Auth | enabled | `MEMORY_AUTH_TOKEN` |
|
|
360
|
+
|
|
349
361
|
For Standard and Full tiers, use the included Docker templates:
|
|
350
362
|
|
|
351
363
|
```bash
|
|
@@ -375,7 +387,7 @@ docker compose -f node_modules/openclaw-memory/docker/full.yml up -d
|
|
|
375
387
|
|
|
376
388
|
```bash
|
|
377
389
|
# Clone
|
|
378
|
-
git clone https://github.com/
|
|
390
|
+
git clone https://github.com/robipop22/openclaw-memory.git
|
|
379
391
|
cd openclaw-memory
|
|
380
392
|
|
|
381
393
|
# Install
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ConversationSummarizer,
|
|
3
3
|
SearchEngine,
|
|
4
|
-
StorageOrchestrator
|
|
5
|
-
|
|
4
|
+
StorageOrchestrator,
|
|
5
|
+
SyncQueueProcessor
|
|
6
|
+
} from "./chunk-BTR4T5L3.js";
|
|
6
7
|
import {
|
|
7
8
|
AuthError,
|
|
8
9
|
MemoryError,
|
|
@@ -10,7 +11,7 @@ import {
|
|
|
10
11
|
ValidationError,
|
|
11
12
|
configSummary,
|
|
12
13
|
loadConfig
|
|
13
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-ITGUJZUL.js";
|
|
14
15
|
|
|
15
16
|
// src/server.ts
|
|
16
17
|
import { fileURLToPath } from "url";
|
|
@@ -489,9 +490,10 @@ import { Elysia as Elysia5, t as t5 } from "elysia";
|
|
|
489
490
|
import fs from "fs";
|
|
490
491
|
import path from "path";
|
|
491
492
|
function adminRoutes(orchestrator) {
|
|
492
|
-
|
|
493
|
+
const healthHandler = async () => {
|
|
493
494
|
return await orchestrator.healthCheck();
|
|
494
|
-
}
|
|
495
|
+
};
|
|
496
|
+
return new Elysia5().get("/api/health", healthHandler).get("/health", healthHandler).post("/api/sync/retry", async ({ set }) => {
|
|
495
497
|
try {
|
|
496
498
|
const result = await orchestrator.retrySyncQueue();
|
|
497
499
|
return result;
|
|
@@ -502,7 +504,63 @@ function adminRoutes(orchestrator) {
|
|
|
502
504
|
details: error instanceof Error ? error.message : String(error)
|
|
503
505
|
};
|
|
504
506
|
}
|
|
505
|
-
}).
|
|
507
|
+
}).post(
|
|
508
|
+
"/api/admin/resync",
|
|
509
|
+
async ({ query, set }) => {
|
|
510
|
+
const layerParam = parseLayerParam(query.layer);
|
|
511
|
+
if (!layerParam) {
|
|
512
|
+
set.status = 400;
|
|
513
|
+
return {
|
|
514
|
+
error: "Invalid layer parameter",
|
|
515
|
+
details: "Use ?layer=qdrant|age|both"
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
const batch = parseBatchParam(query.batch);
|
|
519
|
+
if (batch === null) {
|
|
520
|
+
set.status = 400;
|
|
521
|
+
return {
|
|
522
|
+
error: "Invalid batch parameter",
|
|
523
|
+
details: "Use a positive integer for ?batch=50"
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
const layers = layerParam === "both" ? ["qdrant", "age"] : [layerParam];
|
|
527
|
+
try {
|
|
528
|
+
const memories = listAllMemories(orchestrator);
|
|
529
|
+
let queuedItems = 0;
|
|
530
|
+
for (const memory of memories) {
|
|
531
|
+
for (const layer of layers) {
|
|
532
|
+
orchestrator.sqlite.addToSyncQueue(memory.id, layer, "upsert");
|
|
533
|
+
queuedItems++;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
const syncResults = await SyncQueueProcessor.withBatchSize(batch, async () => {
|
|
537
|
+
return await orchestrator.retrySyncQueue();
|
|
538
|
+
});
|
|
539
|
+
return {
|
|
540
|
+
layer: layerParam,
|
|
541
|
+
layers,
|
|
542
|
+
batch,
|
|
543
|
+
memory_count: memories.length,
|
|
544
|
+
queued_items: queuedItems,
|
|
545
|
+
sync_results: syncResults
|
|
546
|
+
};
|
|
547
|
+
} catch (error) {
|
|
548
|
+
set.status = 500;
|
|
549
|
+
return {
|
|
550
|
+
error: "Resync failed",
|
|
551
|
+
details: error instanceof Error ? error.message : String(error)
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
},
|
|
555
|
+
{
|
|
556
|
+
query: t5.Object({
|
|
557
|
+
layer: t5.Optional(
|
|
558
|
+
t5.Union([t5.Literal("qdrant"), t5.Literal("age"), t5.Literal("both")])
|
|
559
|
+
),
|
|
560
|
+
batch: t5.Optional(t5.String())
|
|
561
|
+
})
|
|
562
|
+
}
|
|
563
|
+
).get("/api/sync/queue", () => {
|
|
506
564
|
const items = orchestrator.sqlite.getSyncQueue(100);
|
|
507
565
|
return { items, count: items.length };
|
|
508
566
|
}).post(
|
|
@@ -531,6 +589,33 @@ function adminRoutes(orchestrator) {
|
|
|
531
589
|
return { error: "Not yet implemented" };
|
|
532
590
|
});
|
|
533
591
|
}
|
|
592
|
+
function parseLayerParam(layer) {
|
|
593
|
+
if (!layer) return "both";
|
|
594
|
+
if (layer === "qdrant" || layer === "age" || layer === "both") return layer;
|
|
595
|
+
return null;
|
|
596
|
+
}
|
|
597
|
+
function parseBatchParam(batch) {
|
|
598
|
+
if (!batch) return 50;
|
|
599
|
+
const parsed = Number.parseInt(batch, 10);
|
|
600
|
+
if (!Number.isFinite(parsed) || parsed <= 0) return null;
|
|
601
|
+
return parsed;
|
|
602
|
+
}
|
|
603
|
+
function listAllMemories(orchestrator) {
|
|
604
|
+
const pageSize = 5e3;
|
|
605
|
+
const memories = [];
|
|
606
|
+
let offset = 0;
|
|
607
|
+
while (true) {
|
|
608
|
+
const page = orchestrator.sqlite.listMemories({
|
|
609
|
+
limit: pageSize,
|
|
610
|
+
offset,
|
|
611
|
+
order: "asc"
|
|
612
|
+
});
|
|
613
|
+
memories.push(...page);
|
|
614
|
+
if (page.length < pageSize) break;
|
|
615
|
+
offset += pageSize;
|
|
616
|
+
}
|
|
617
|
+
return memories;
|
|
618
|
+
}
|
|
534
619
|
async function migrateMarkdownFiles(orchestrator, request) {
|
|
535
620
|
const { markdown_paths, agent_id, dry_run } = request;
|
|
536
621
|
let migrated = 0;
|
|
@@ -653,7 +738,7 @@ function inferTags(heading, fileName) {
|
|
|
653
738
|
// src/api/router.ts
|
|
654
739
|
function createApp(orchestrator, config) {
|
|
655
740
|
const app = new Elysia6().use(cors()).derive(({ headers, set, path: path2 }) => {
|
|
656
|
-
if (path2 === "/api/health") return {};
|
|
741
|
+
if (path2 === "/api/health" || path2 === "/health") return {};
|
|
657
742
|
if (!config.auth.enabled || !config.auth.token) return {};
|
|
658
743
|
const authHeader = headers.authorization;
|
|
659
744
|
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
@@ -689,6 +774,11 @@ function createApp(orchestrator, config) {
|
|
|
689
774
|
set.status = 400;
|
|
690
775
|
return { error: "Validation error", details: msg2 };
|
|
691
776
|
}
|
|
777
|
+
if (code === "NOT_FOUND") {
|
|
778
|
+
const msg2 = error && "message" in error ? error.message : "Route not found";
|
|
779
|
+
set.status = 404;
|
|
780
|
+
return { error: msg2, code };
|
|
781
|
+
}
|
|
692
782
|
const msg = error instanceof Error ? error.stack || error.message : String(error);
|
|
693
783
|
console.error(`[api] Unhandled error: ${msg}`);
|
|
694
784
|
set.status = 500;
|
|
@@ -726,8 +816,8 @@ async function main() {
|
|
|
726
816
|
}
|
|
727
817
|
function isMainModule() {
|
|
728
818
|
try {
|
|
729
|
-
|
|
730
|
-
|
|
819
|
+
const bun = globalThis.Bun;
|
|
820
|
+
if (typeof bun !== "undefined") {
|
|
731
821
|
return bun.main === fileURLToPath(import.meta.url);
|
|
732
822
|
}
|
|
733
823
|
if (process.argv[1]) {
|
|
@@ -749,4 +839,4 @@ export {
|
|
|
749
839
|
createApp,
|
|
750
840
|
createServer
|
|
751
841
|
};
|
|
752
|
-
//# sourceMappingURL=chunk-
|
|
842
|
+
//# sourceMappingURL=chunk-7DNVIKQ3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server.ts","../src/api/router.ts","../src/api/memories.ts","../src/api/search.ts","../src/api/conversations.ts","../src/api/entities.ts","../src/api/admin.ts"],"sourcesContent":["import { fileURLToPath } from \"node:url\";\nimport { resolve } from \"node:path\";\nimport { loadConfig, configSummary } from \"./config/index.js\";\nimport { StorageOrchestrator } from \"./storage/orchestrator.js\";\nimport { createApp } from \"./api/router.js\";\n\n// ── Server Entry Point ──────────────────────────────────────────────────\n\nexport async function createServer(configPath?: string) {\n const config = await loadConfig(configPath);\n const orchestrator = new StorageOrchestrator(config);\n await orchestrator.init();\n\n const app = createApp(orchestrator, config);\n\n return { app, orchestrator, config };\n}\n\n// ── Main ────────────────────────────────────────────────────────────────\n\nasync function main() {\n console.log(\"┌──────────────────────────────────────────┐\");\n console.log(\"│ openclaw-memory service v0.1.0 │\");\n console.log(\"│ Triple-Layer Memory System │\");\n console.log(\"└──────────────────────────────────────────┘\");\n\n const { app, orchestrator, config } = await createServer();\n\n console.log(\"[config]\");\n console.log(configSummary(config).split(\"\\n\").map((l) => ` ${l}`).join(\"\\n\"));\n\n app.listen(config.port);\n console.log(`[server] Listening on http://${config.host}:${config.port}`);\n console.log(`[server] Health check: http://localhost:${config.port}/api/health`);\n\n const shutdown = async () => {\n console.log(\"\\n[server] Shutting down...\");\n await orchestrator.close();\n process.exit(0);\n };\n\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n}\n\n// Run if executed directly (works in both Bun and Node ESM)\nfunction isMainModule(): boolean {\n try {\n // Bun runtime\n const bun = (globalThis as typeof globalThis & { Bun?: { main?: string } }).Bun;\n if (typeof bun !== \"undefined\") {\n return bun.main === fileURLToPath(import.meta.url);\n }\n // Node.js\n if (process.argv[1]) {\n return resolve(process.argv[1]) === resolve(fileURLToPath(import.meta.url));\n }\n return false;\n } catch {\n return false;\n }\n}\n\nif (isMainModule()) {\n main().catch((error) => {\n console.error(\"[fatal]\", error);\n process.exit(1);\n });\n}\n","import { Elysia } from \"elysia\";\nimport { cors } from \"@elysiajs/cors\";\nimport type { ResolvedConfig } from \"../config/index.js\";\nimport type { StorageOrchestrator } from \"../storage/orchestrator.js\";\nimport { AuthError, MemoryError, NotFoundError, ValidationError } from \"../core/errors.js\";\nimport { memoriesRoutes } from \"./memories.js\";\nimport { searchRoutes } from \"./search.js\";\nimport { conversationRoutes } from \"./conversations.js\";\nimport { entityRoutes } from \"./entities.js\";\nimport { adminRoutes } from \"./admin.js\";\n\n// ── App Router ──────────────────────────────────────────────────────────\n\nexport function createApp(orchestrator: StorageOrchestrator, config: ResolvedConfig) {\n const app = new Elysia()\n .use(cors())\n\n // ── Auth Middleware ────────────────────────────────────────────────\n .derive(({ headers, set, path }) => {\n // Skip auth for health check\n if (path === \"/api/health\" || path === \"/health\") return {};\n\n // Skip auth if disabled\n if (!config.auth.enabled || !config.auth.token) return {};\n\n const authHeader = headers.authorization;\n if (!authHeader || !authHeader.startsWith(\"Bearer \")) {\n set.status = 401;\n throw new AuthError(\"Missing or invalid Authorization header\");\n }\n\n const token = authHeader.slice(7);\n if (token !== config.auth.token) {\n set.status = 403;\n throw new AuthError(\"Invalid token\");\n }\n\n return {};\n })\n\n // ── Error Handler ─────────────────────────────────────────────────\n .onError(({ code, error, set }) => {\n // Handle AuthError\n if (error instanceof AuthError) {\n const status = error.message.includes(\"Invalid token\") ? 403 : 401;\n set.status = status;\n return { error: error.message, code: error.code };\n }\n\n // Handle other custom errors\n if (error instanceof NotFoundError) {\n set.status = 404;\n return { error: error.message, code: error.code };\n }\n\n if (error instanceof ValidationError) {\n set.status = 400;\n return { error: error.message, code: error.code, details: error.details };\n }\n\n if (error instanceof MemoryError) {\n set.status = 500;\n return { error: error.message, code: error.code };\n }\n\n // Elysia validation errors\n if (code === \"VALIDATION\") {\n const msg = error && \"message\" in error ? (error as Error).message : String(error);\n set.status = 400;\n return { error: \"Validation error\", details: msg };\n }\n\n // Elysia built-in not found errors\n if (code === \"NOT_FOUND\") {\n const msg =\n error && \"message\" in error ? (error as Error).message : \"Route not found\";\n set.status = 404;\n return { error: msg, code };\n }\n\n // Unknown errors\n const msg = error instanceof Error ? error.stack || error.message : String(error);\n console.error(`[api] Unhandled error: ${msg}`);\n set.status = 500;\n return { error: \"Internal server error\" };\n })\n\n // ── Mount Routes ──────────────────────────────────────────────────\n .use(memoriesRoutes(orchestrator))\n .use(searchRoutes(orchestrator))\n .use(conversationRoutes(orchestrator, config))\n .use(entityRoutes(orchestrator))\n .use(adminRoutes(orchestrator));\n\n return app;\n}\n","import { Elysia, t } from \"elysia\";\nimport type { StorageOrchestrator } from \"../storage/orchestrator.js\";\nimport type { CreateMemoryRequest, UpdateMemoryRequest, MemoryScope, MemorySource } from \"../core/types.js\";\n\n// ── Memory CRUD Routes ──────────────────────────────────────────────────\n\nexport function memoriesRoutes(orchestrator: StorageOrchestrator) {\n return new Elysia({ prefix: \"/api/memories\" })\n // POST /api/memories — Create a new memory\n .post(\n \"/\",\n async ({ body, set }) => {\n try {\n const result = await orchestrator.createMemory(body as CreateMemoryRequest);\n set.status = 201;\n return result;\n } catch (error) {\n set.status = 500;\n return {\n error: \"Failed to create memory\",\n details: error instanceof Error ? error.message : String(error),\n };\n }\n },\n {\n body: t.Object({\n agent_id: t.String(),\n scope: t.Union([\n t.Literal(\"user\"),\n t.Literal(\"agent\"),\n t.Literal(\"global\"),\n t.Literal(\"project\"),\n t.Literal(\"session\"),\n ]),\n subject_id: t.Optional(t.Nullable(t.String())),\n content: t.String(),\n tags: t.Optional(t.Array(t.String())),\n source: t.Optional(\n t.Union([\n t.Literal(\"explicit\"),\n t.Literal(\"derived\"),\n t.Literal(\"observation\"),\n t.Literal(\"conversation_summary\"),\n t.Literal(\"entity_extraction\"),\n t.Literal(\"daily_digest\"),\n t.Literal(\"migration\"),\n ])\n ),\n created_by: t.Optional(t.Nullable(t.String())),\n extract_entities: t.Optional(t.Boolean()),\n expires_at: t.Optional(t.Nullable(t.String())),\n }),\n }\n )\n\n // GET /api/memories/:id — Get a memory by ID\n .get(\"/:id\", ({ params, set }) => {\n const memory = orchestrator.sqlite.getMemory(params.id);\n if (!memory) {\n set.status = 404;\n return { error: \"Memory not found\" };\n }\n return memory;\n })\n\n // PUT /api/memories/:id — Update a memory\n .put(\n \"/:id\",\n async ({ params, body, set }) => {\n try {\n const result = await orchestrator.updateMemory(\n params.id,\n body as UpdateMemoryRequest\n );\n if (!result) {\n set.status = 404;\n return { error: \"Memory not found\" };\n }\n return result;\n } catch (error) {\n set.status = 500;\n return {\n error: \"Failed to update memory\",\n details: error instanceof Error ? error.message : String(error),\n };\n }\n },\n {\n body: t.Object({\n content: t.Optional(t.String()),\n tags: t.Optional(t.Array(t.String())),\n scope: t.Optional(\n t.Union([\n t.Literal(\"user\"),\n t.Literal(\"agent\"),\n t.Literal(\"global\"),\n t.Literal(\"project\"),\n t.Literal(\"session\"),\n ])\n ),\n subject_id: t.Optional(t.Nullable(t.String())),\n expires_at: t.Optional(t.Nullable(t.String())),\n extract_entities: t.Optional(t.Boolean()),\n }),\n }\n )\n\n // DELETE /api/memories/:id — Delete a memory\n .delete(\"/:id\", async ({ params, set }) => {\n try {\n const deleted = await orchestrator.deleteMemory(params.id);\n if (!deleted) {\n set.status = 404;\n return { error: \"Memory not found\" };\n }\n return { deleted: true, id: params.id };\n } catch (error) {\n set.status = 500;\n return {\n error: \"Failed to delete memory\",\n details: error instanceof Error ? error.message : String(error),\n };\n }\n })\n\n // GET /api/memories — List memories with filters\n .get(\"/\", ({ query }) => {\n const memories = orchestrator.sqlite.listMemories({\n agent_id: query.agent_id as string | undefined,\n scope: query.scope as MemoryScope | undefined,\n subject_id: query.subject_id as string | undefined,\n source: query.source as MemorySource | undefined,\n tags: query.tags as string | undefined,\n limit: query.limit ? parseInt(String(query.limit), 10) : undefined,\n offset: query.offset ? parseInt(String(query.offset), 10) : undefined,\n order: (query.order as \"asc\" | \"desc\") || \"desc\",\n });\n return { memories, count: memories.length };\n });\n}\n","import { Elysia, t } from \"elysia\";\nimport { SearchEngine } from \"../search/engine.js\";\nimport type { StorageOrchestrator } from \"../storage/orchestrator.js\";\nimport type { SearchRequest } from \"../core/types.js\";\n\n// ── Search Routes ───────────────────────────────────────────────────────\n\nexport function searchRoutes(orchestrator: StorageOrchestrator) {\n const searchEngine = new SearchEngine(orchestrator);\n\n return new Elysia({ prefix: \"/api/search\" })\n // POST /api/search — Smart search (auto-selects layers)\n .post(\n \"/\",\n async ({ body, set }) => {\n try {\n const req = body as SearchRequest;\n if (!req.agent_id) req.cross_agent = true;\n return await searchEngine.search(req);\n } catch (error) {\n set.status = 500;\n return {\n error: \"Search failed\",\n details: error instanceof Error ? error.message : String(error),\n };\n }\n },\n {\n body: t.Object({\n agent_id: t.Optional(t.String()),\n query: t.String(),\n scopes: t.Optional(\n t.Array(\n t.Union([\n t.Literal(\"user\"),\n t.Literal(\"agent\"),\n t.Literal(\"global\"),\n t.Literal(\"project\"),\n t.Literal(\"session\"),\n ])\n )\n ),\n subject_id: t.Optional(t.Nullable(t.String())),\n limit: t.Optional(t.Number()),\n include_graph: t.Optional(t.Boolean()),\n cross_agent: t.Optional(t.Boolean()),\n strategy: t.Optional(\n t.Union([\n t.Literal(\"auto\"),\n t.Literal(\"semantic\"),\n t.Literal(\"fulltext\"),\n t.Literal(\"graph\"),\n t.Literal(\"all\"),\n ])\n ),\n }),\n }\n )\n\n // POST /api/search/semantic — Force Qdrant semantic search\n .post(\n \"/semantic\",\n async ({ body, set }) => {\n try {\n return await searchEngine.search({\n ...(body as SearchRequest),\n strategy: \"semantic\",\n });\n } catch (error) {\n set.status = 500;\n return {\n error: \"Semantic search failed\",\n details: error instanceof Error ? error.message : String(error),\n };\n }\n },\n {\n body: t.Object({\n agent_id: t.Optional(t.String()),\n query: t.String(),\n scopes: t.Optional(\n t.Array(\n t.Union([\n t.Literal(\"user\"),\n t.Literal(\"agent\"),\n t.Literal(\"global\"),\n t.Literal(\"project\"),\n t.Literal(\"session\"),\n ])\n )\n ),\n subject_id: t.Optional(t.Nullable(t.String())),\n limit: t.Optional(t.Number()),\n cross_agent: t.Optional(t.Boolean()),\n }),\n }\n )\n\n // POST /api/search/graph — Force AGE graph traversal\n .post(\n \"/graph\",\n async ({ body, set }) => {\n try {\n return await searchEngine.search({\n ...(body as SearchRequest),\n strategy: \"graph\",\n include_graph: true,\n });\n } catch (error) {\n set.status = 500;\n return {\n error: \"Graph search failed\",\n details: error instanceof Error ? error.message : String(error),\n };\n }\n },\n {\n body: t.Object({\n agent_id: t.Optional(t.String()),\n query: t.String(),\n scopes: t.Optional(\n t.Array(\n t.Union([\n t.Literal(\"user\"),\n t.Literal(\"agent\"),\n t.Literal(\"global\"),\n t.Literal(\"project\"),\n t.Literal(\"session\"),\n ])\n )\n ),\n subject_id: t.Optional(t.Nullable(t.String())),\n limit: t.Optional(t.Number()),\n }),\n }\n )\n\n // POST /api/search/fulltext — Force SQLite FTS search\n .post(\n \"/fulltext\",\n async ({ body, set }) => {\n try {\n return await searchEngine.search({\n ...(body as SearchRequest),\n strategy: \"fulltext\",\n include_graph: false,\n });\n } catch (error) {\n set.status = 500;\n return {\n error: \"Fulltext search failed\",\n details: error instanceof Error ? error.message : String(error),\n };\n }\n },\n {\n body: t.Object({\n agent_id: t.Optional(t.String()),\n query: t.String(),\n scopes: t.Optional(t.Array(t.String())),\n subject_id: t.Optional(t.Nullable(t.String())),\n limit: t.Optional(t.Number()),\n }),\n }\n );\n}\n","import { Elysia, t } from \"elysia\";\nimport type { StorageOrchestrator } from \"../storage/orchestrator.js\";\nimport type { ResolvedConfig } from \"../config/index.js\";\nimport { ConversationSummarizer } from \"../extraction/summarizer.js\";\nimport type { ConversationLogEntry, CreateMemoryRequest, SummarizeResponse } from \"../core/types.js\";\n\n// ── Conversation Routes ─────────────────────────────────────────────────\n\nexport function conversationRoutes(\n orchestrator: StorageOrchestrator,\n config: ResolvedConfig\n) {\n // Summarizer is only available if extraction config is present\n const summarizer = config.extraction\n ? new ConversationSummarizer({\n apiKey: config.extraction.apiKey,\n baseUrl: config.extraction.baseUrl,\n model: config.extraction.model,\n })\n : null;\n\n return new Elysia({ prefix: \"/api/conversations\" })\n // POST /api/conversations/log — Append conversation entry\n .post(\n \"/log\",\n ({ body, set }) => {\n try {\n orchestrator.sqlite.appendConversationLog(body as ConversationLogEntry);\n set.status = 201;\n return { ok: true };\n } catch (error) {\n set.status = 500;\n return {\n error: \"Failed to append conversation log\",\n details: error instanceof Error ? error.message : String(error),\n };\n }\n },\n {\n body: t.Object({\n agent_id: t.String(),\n session_id: t.String(),\n user_id: t.String(),\n channel: t.String(),\n role: t.Union([\n t.Literal(\"user\"),\n t.Literal(\"assistant\"),\n t.Literal(\"system\"),\n ]),\n content: t.String(),\n timestamp: t.String(),\n }),\n }\n )\n\n // POST /api/conversations/summarize — Summarize a session\n .post(\n \"/summarize\",\n async ({ body, set }) => {\n if (!summarizer) {\n set.status = 501;\n return { error: \"Summarization not available — extraction config required\" };\n }\n\n try {\n const { agent_id, session_id, user_id, channel, messages } = body;\n\n const summary = await summarizer.summarize(messages);\n if (!summary) {\n set.status = 422;\n return { error: \"Failed to generate summary\" };\n }\n\n const memoryReq: CreateMemoryRequest = {\n agent_id,\n scope: \"session\",\n subject_id: user_id,\n content: summary,\n tags: [\"conversation_summary\", channel, `session:${session_id}`],\n source: \"conversation_summary\",\n created_by: agent_id,\n extract_entities: true,\n };\n\n const result = await orchestrator.createMemory(memoryReq);\n\n const response: SummarizeResponse = {\n memory_id: result.id,\n summary,\n entities_extracted: result.entities,\n relationships_created: 0,\n };\n\n set.status = 201;\n return response;\n } catch (error) {\n set.status = 500;\n return {\n error: \"Summarization failed\",\n details: error instanceof Error ? error.message : String(error),\n };\n }\n },\n {\n body: t.Object({\n agent_id: t.String(),\n session_id: t.String(),\n user_id: t.String(),\n channel: t.String(),\n messages: t.Array(\n t.Object({\n role: t.Union([\n t.Literal(\"user\"),\n t.Literal(\"assistant\"),\n t.Literal(\"system\"),\n ]),\n content: t.String(),\n timestamp: t.String(),\n })\n ),\n reason: t.Optional(t.String()),\n }),\n }\n );\n}\n","import { Elysia, t } from \"elysia\";\nimport type { StorageOrchestrator } from \"../storage/orchestrator.js\";\n\n// ── Entity Routes ───────────────────────────────────────────────────────\n\nexport function entityRoutes(orchestrator: StorageOrchestrator) {\n return new Elysia({ prefix: \"/api/entities\" })\n // GET /api/entities/:type — List entities by type\n .get(\"/:type\", async ({ params, query, set }) => {\n if (!orchestrator.age) {\n set.status = 501;\n return { error: \"Graph layer not available — requires Full tier\" };\n }\n\n try {\n const entities = await orchestrator.age.listEntities(\n params.type,\n query.agent_id as string | undefined,\n query.limit ? parseInt(String(query.limit), 10) : 50\n );\n return { entities, count: entities.length };\n } catch (error) {\n set.status = 500;\n return {\n error: \"Failed to list entities\",\n details: error instanceof Error ? error.message : String(error),\n };\n }\n })\n\n // GET /api/entities/:type/:id — Get entity with relationships\n .get(\"/:type/:id\", async ({ params, set }) => {\n if (!orchestrator.age) {\n set.status = 501;\n return { error: \"Graph layer not available — requires Full tier\" };\n }\n\n try {\n const result = await orchestrator.age.getEntityWithRelationships(\n params.type,\n params.id\n );\n if (!result.entity) {\n set.status = 404;\n return { error: \"Entity not found\" };\n }\n return result;\n } catch (error) {\n set.status = 500;\n return {\n error: \"Failed to get entity\",\n details: error instanceof Error ? error.message : String(error),\n };\n }\n })\n\n // GET /api/entities/:type/:id/related — Get related entities\n .get(\"/:type/:id/related\", async ({ params, query, set }) => {\n if (!orchestrator.age) {\n set.status = 501;\n return { error: \"Graph layer not available — requires Full tier\" };\n }\n\n try {\n const depth = query.depth ? parseInt(String(query.depth), 10) : 2;\n const related = await orchestrator.age.getRelatedEntities(\n params.id,\n depth\n );\n return { related, count: related.length };\n } catch (error) {\n set.status = 500;\n return {\n error: \"Failed to get related entities\",\n details: error instanceof Error ? error.message : String(error),\n };\n }\n })\n\n // POST /api/entities/extract — Manually trigger entity extraction\n .post(\n \"/extract\",\n async ({ body, set }) => {\n if (!orchestrator.entityExtractor) {\n set.status = 501;\n return { error: \"Entity extraction not available — requires extraction config\" };\n }\n\n try {\n const result = await orchestrator.entityExtractor.extract(body.text);\n return result;\n } catch (error) {\n set.status = 500;\n return {\n error: \"Entity extraction failed\",\n details: error instanceof Error ? error.message : String(error),\n };\n }\n },\n {\n body: t.Object({\n text: t.String(),\n }),\n }\n );\n}\n","import { Elysia, t } from \"elysia\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { StorageOrchestrator } from \"../storage/orchestrator.js\";\nimport type { CreateMemoryRequest, MigrateMarkdownRequest } from \"../core/types.js\";\nimport { SyncQueueProcessor } from \"../storage/sync-queue.js\";\n\n// ── Admin Routes ────────────────────────────────────────────────────────\n\nexport function adminRoutes(orchestrator: StorageOrchestrator) {\n const healthHandler = async () => {\n return await orchestrator.healthCheck();\n };\n\n return new Elysia()\n // GET /api/health and /health — Health checks\n .get(\"/api/health\", healthHandler)\n .get(\"/health\", healthHandler)\n\n // POST /api/sync/retry — Retry failed L2/L3 syncs\n .post(\"/api/sync/retry\", async ({ set }) => {\n try {\n const result = await orchestrator.retrySyncQueue();\n return result;\n } catch (error) {\n set.status = 500;\n return {\n error: \"Sync retry failed\",\n details: error instanceof Error ? error.message : String(error),\n };\n }\n })\n\n // POST /api/admin/resync — Requeue all memories for L2/L3 sync\n .post(\n \"/api/admin/resync\",\n async ({ query, set }) => {\n const layerParam = parseLayerParam(query.layer as string | undefined);\n if (!layerParam) {\n set.status = 400;\n return {\n error: \"Invalid layer parameter\",\n details: \"Use ?layer=qdrant|age|both\",\n };\n }\n\n const batch = parseBatchParam(query.batch as string | undefined);\n if (batch === null) {\n set.status = 400;\n return {\n error: \"Invalid batch parameter\",\n details: \"Use a positive integer for ?batch=50\",\n };\n }\n\n const layers: Array<\"qdrant\" | \"age\"> =\n layerParam === \"both\" ? [\"qdrant\", \"age\"] : [layerParam];\n\n try {\n const memories = listAllMemories(orchestrator);\n let queuedItems = 0;\n\n for (const memory of memories) {\n for (const layer of layers) {\n orchestrator.sqlite.addToSyncQueue(memory.id, layer, \"upsert\");\n queuedItems++;\n }\n }\n\n const syncResults = await SyncQueueProcessor.withBatchSize(batch, async () => {\n return await orchestrator.retrySyncQueue();\n });\n\n return {\n layer: layerParam,\n layers,\n batch,\n memory_count: memories.length,\n queued_items: queuedItems,\n sync_results: syncResults,\n };\n } catch (error) {\n set.status = 500;\n return {\n error: \"Resync failed\",\n details: error instanceof Error ? error.message : String(error),\n };\n }\n },\n {\n query: t.Object({\n layer: t.Optional(\n t.Union([t.Literal(\"qdrant\"), t.Literal(\"age\"), t.Literal(\"both\")])\n ),\n batch: t.Optional(t.String()),\n }),\n }\n )\n\n // GET /api/sync/queue — View pending sync queue\n .get(\"/api/sync/queue\", () => {\n const items = orchestrator.sqlite.getSyncQueue(100);\n return { items, count: items.length };\n })\n\n // POST /api/admin/migrate-markdown — Migrate markdown files\n .post(\n \"/api/admin/migrate-markdown\",\n async ({ body, set }) => {\n try {\n const results = await migrateMarkdownFiles(orchestrator, body);\n return results;\n } catch (error) {\n set.status = 500;\n return {\n error: \"Migration failed\",\n details: error instanceof Error ? error.message : String(error),\n };\n }\n },\n {\n body: t.Object({\n markdown_paths: t.Array(t.String()),\n agent_id: t.String(),\n dry_run: t.Optional(t.Boolean()),\n }),\n }\n )\n\n // POST /api/admin/daily-digest — Trigger daily digest\n .post(\"/api/admin/daily-digest\", async ({ set }) => {\n set.status = 501;\n return { error: \"Not yet implemented\" };\n });\n}\n\ntype ResyncLayer = \"qdrant\" | \"age\" | \"both\";\n\nfunction parseLayerParam(layer: string | undefined): ResyncLayer | null {\n if (!layer) return \"both\";\n if (layer === \"qdrant\" || layer === \"age\" || layer === \"both\") return layer;\n return null;\n}\n\nfunction parseBatchParam(batch: string | undefined): number | null {\n if (!batch) return 50;\n const parsed = Number.parseInt(batch, 10);\n if (!Number.isFinite(parsed) || parsed <= 0) return null;\n return parsed;\n}\n\nfunction listAllMemories(orchestrator: StorageOrchestrator) {\n const pageSize = 5_000;\n const memories: ReturnType<typeof orchestrator.sqlite.listMemories> = [];\n let offset = 0;\n\n while (true) {\n const page = orchestrator.sqlite.listMemories({\n limit: pageSize,\n offset,\n order: \"asc\",\n });\n\n memories.push(...page);\n if (page.length < pageSize) break;\n offset += pageSize;\n }\n\n return memories;\n}\n\n// ── Markdown Migration ──────────────────────────────────────────────────\n\nasync function migrateMarkdownFiles(\n orchestrator: StorageOrchestrator,\n request: MigrateMarkdownRequest\n): Promise<{\n migrated: number;\n skipped: number;\n errors: string[];\n memories: Array<{ id: string; content_preview: string }>;\n}> {\n const { markdown_paths, agent_id, dry_run } = request;\n let migrated = 0;\n let skipped = 0;\n const errors: string[] = [];\n const memories: Array<{ id: string; content_preview: string }> = [];\n\n for (const filePath of markdown_paths) {\n try {\n if (!fs.existsSync(filePath)) {\n errors.push(`File not found: ${filePath}`);\n skipped++;\n continue;\n }\n\n const content = fs.readFileSync(filePath, \"utf-8\");\n const fileName = path.basename(filePath, \".md\");\n const sections = parseMarkdownSections(content);\n\n for (const section of sections) {\n if (section.content.trim().length < 10) {\n skipped++;\n continue;\n }\n\n if (dry_run) {\n memories.push({\n id: \"(dry-run)\",\n content_preview: section.content.slice(0, 100),\n });\n migrated++;\n continue;\n }\n\n const scope = inferScope(section.heading, fileName);\n const source = inferSource(fileName);\n const tags = inferTags(section.heading, fileName);\n\n const req: CreateMemoryRequest = {\n agent_id,\n scope,\n subject_id: null,\n content: section.content.trim(),\n tags,\n source: source || \"migration\",\n created_by: \"migration\",\n extract_entities: true,\n };\n\n try {\n const result = await orchestrator.createMemory(req);\n memories.push({\n id: result.id,\n content_preview: section.content.slice(0, 100),\n });\n migrated++;\n } catch (error) {\n errors.push(\n `Failed to migrate section \"${section.heading}\": ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n } catch (error) {\n errors.push(\n `Failed to process ${filePath}: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n\n return { migrated, skipped, errors, memories };\n}\n\n// ── Markdown Parsing Helpers ────────────────────────────────────────────\n\ninterface MarkdownSection {\n heading: string;\n content: string;\n level: number;\n}\n\nfunction parseMarkdownSections(markdown: string): MarkdownSection[] {\n const lines = markdown.split(\"\\n\");\n const sections: MarkdownSection[] = [];\n let currentHeading = \"root\";\n let currentLevel = 0;\n let currentContent: string[] = [];\n\n for (const line of lines) {\n const headingMatch = line.match(/^(#{1,3})\\s+(.+)/);\n if (headingMatch) {\n if (currentContent.length > 0) {\n sections.push({\n heading: currentHeading,\n content: currentContent.join(\"\\n\").trim(),\n level: currentLevel,\n });\n }\n currentHeading = headingMatch[2].trim();\n currentLevel = headingMatch[1].length;\n currentContent = [];\n } else {\n currentContent.push(line);\n }\n }\n\n if (currentContent.length > 0) {\n sections.push({\n heading: currentHeading,\n content: currentContent.join(\"\\n\").trim(),\n level: currentLevel,\n });\n }\n\n return sections;\n}\n\nfunction inferScope(heading: string, fileName: string): CreateMemoryRequest[\"scope\"] {\n const h = heading.toLowerCase();\n const f = fileName.toLowerCase();\n\n if (h.includes(\"about\") || h.includes(\"personal\")) return \"user\";\n if (h.includes(\"project\")) return \"project\";\n if (h.includes(\"agent\")) return \"agent\";\n if (h.includes(\"session\") || f.match(/^\\d{4}-\\d{2}-\\d{2}/)) return \"session\";\n return \"global\";\n}\n\nfunction inferSource(fileName: string): CreateMemoryRequest[\"source\"] | null {\n if (fileName.match(/^\\d{4}-\\d{2}-\\d{2}/)) return \"daily_digest\";\n return \"migration\";\n}\n\nfunction inferTags(heading: string, fileName: string): string[] {\n const tags: string[] = [\"migration\"];\n if (fileName.match(/^\\d{4}-\\d{2}-\\d{2}/)) {\n tags.push(\"daily\", fileName);\n }\n if (heading !== \"root\") {\n tags.push(heading.toLowerCase().replace(/[^a-z0-9]+/g, \"-\"));\n }\n return tags;\n}\n"],"mappings":"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","names":["Elysia","Elysia","t","Elysia","t","Elysia","t","Elysia","t","Elysia","t","Elysia","t","Elysia","t","Elysia","path","msg"]}
|
|
@@ -6,7 +6,8 @@ import fs from "fs";
|
|
|
6
6
|
import path from "path";
|
|
7
7
|
import { createRequire } from "module";
|
|
8
8
|
function createDatabase(dbPath) {
|
|
9
|
-
|
|
9
|
+
const bun = globalThis.Bun;
|
|
10
|
+
if (typeof bun !== "undefined") {
|
|
10
11
|
const req = createRequire(import.meta.url);
|
|
11
12
|
const { Database } = req("bun:sqlite");
|
|
12
13
|
const db = new Database(dbPath, { create: true });
|
|
@@ -1044,7 +1045,11 @@ function slugify(input) {
|
|
|
1044
1045
|
}
|
|
1045
1046
|
|
|
1046
1047
|
// src/storage/sync-queue.ts
|
|
1047
|
-
var SyncQueueProcessor = class {
|
|
1048
|
+
var SyncQueueProcessor = class _SyncQueueProcessor {
|
|
1049
|
+
static batchSizeOverride = null;
|
|
1050
|
+
static SQLITE_ATTEMPT_CEILING = 5;
|
|
1051
|
+
static DEFAULT_BATCH_SIZE = 50;
|
|
1052
|
+
static DEFAULT_MAX_RETRIES = 20;
|
|
1048
1053
|
sqlite;
|
|
1049
1054
|
qdrant;
|
|
1050
1055
|
age;
|
|
@@ -1057,6 +1062,18 @@ var SyncQueueProcessor = class {
|
|
|
1057
1062
|
this.age = age;
|
|
1058
1063
|
this.embeddings = embeddings;
|
|
1059
1064
|
}
|
|
1065
|
+
static async withBatchSize(batchSize, fn) {
|
|
1066
|
+
const previous = _SyncQueueProcessor.batchSizeOverride;
|
|
1067
|
+
_SyncQueueProcessor.batchSizeOverride = clampPositiveInt(
|
|
1068
|
+
batchSize,
|
|
1069
|
+
_SyncQueueProcessor.DEFAULT_BATCH_SIZE
|
|
1070
|
+
);
|
|
1071
|
+
try {
|
|
1072
|
+
return await fn();
|
|
1073
|
+
} finally {
|
|
1074
|
+
_SyncQueueProcessor.batchSizeOverride = previous;
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1060
1077
|
start(intervalMs = 6e4) {
|
|
1061
1078
|
if (this.interval) return;
|
|
1062
1079
|
console.log(`[sync-queue] Starting processor (every ${intervalMs / 1e3}s)`);
|
|
@@ -1072,18 +1089,22 @@ var SyncQueueProcessor = class {
|
|
|
1072
1089
|
console.log("[sync-queue] Stopped");
|
|
1073
1090
|
}
|
|
1074
1091
|
}
|
|
1075
|
-
async processQueue() {
|
|
1092
|
+
async processQueue(batchSize) {
|
|
1076
1093
|
if (this.processing) return { processed: 0, succeeded: 0, failed: 0 };
|
|
1077
1094
|
this.processing = true;
|
|
1078
1095
|
let processed = 0;
|
|
1079
1096
|
let succeeded = 0;
|
|
1080
1097
|
let failed = 0;
|
|
1098
|
+
const effectiveBatchSize = this.resolveBatchSize(batchSize);
|
|
1099
|
+
const maxRetries = this.resolveMaxRetries();
|
|
1081
1100
|
try {
|
|
1082
|
-
const items = this.sqlite.getSyncQueue(
|
|
1101
|
+
const items = this.sqlite.getSyncQueue(effectiveBatchSize);
|
|
1083
1102
|
if (items.length === 0) {
|
|
1084
1103
|
return { processed: 0, succeeded: 0, failed: 0 };
|
|
1085
1104
|
}
|
|
1086
|
-
console.log(
|
|
1105
|
+
console.log(
|
|
1106
|
+
`[sync-queue] Processing ${items.length} items (batch=${effectiveBatchSize}, maxRetries=${maxRetries})`
|
|
1107
|
+
);
|
|
1087
1108
|
for (const item of items) {
|
|
1088
1109
|
processed++;
|
|
1089
1110
|
try {
|
|
@@ -1092,7 +1113,7 @@ var SyncQueueProcessor = class {
|
|
|
1092
1113
|
succeeded++;
|
|
1093
1114
|
} catch (error) {
|
|
1094
1115
|
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
1095
|
-
this.
|
|
1116
|
+
this.handleFailure(item, errorMsg, maxRetries);
|
|
1096
1117
|
failed++;
|
|
1097
1118
|
console.warn(
|
|
1098
1119
|
`[sync-queue] Failed item ${item.id} (${item.layer}/${item.operation}/${item.memory_id}): ${errorMsg}`
|
|
@@ -1101,7 +1122,7 @@ var SyncQueueProcessor = class {
|
|
|
1101
1122
|
}
|
|
1102
1123
|
const cleared = this.sqlite.clearCompletedSyncItems();
|
|
1103
1124
|
if (cleared > 0) {
|
|
1104
|
-
console.log(`[sync-queue] Cleared ${cleared} items that
|
|
1125
|
+
console.log(`[sync-queue] Cleared ${cleared} items that reached retry limit`);
|
|
1105
1126
|
}
|
|
1106
1127
|
} finally {
|
|
1107
1128
|
this.processing = false;
|
|
@@ -1145,7 +1166,75 @@ var SyncQueueProcessor = class {
|
|
|
1145
1166
|
await this.age.linkMemoryToEntity(memory.id, entityId);
|
|
1146
1167
|
}
|
|
1147
1168
|
}
|
|
1169
|
+
resolveBatchSize(batchSize) {
|
|
1170
|
+
if (batchSize !== void 0) {
|
|
1171
|
+
return clampPositiveInt(batchSize, _SyncQueueProcessor.DEFAULT_BATCH_SIZE);
|
|
1172
|
+
}
|
|
1173
|
+
if (_SyncQueueProcessor.batchSizeOverride !== null) {
|
|
1174
|
+
return _SyncQueueProcessor.batchSizeOverride;
|
|
1175
|
+
}
|
|
1176
|
+
return clampPositiveInt(
|
|
1177
|
+
parseInt(process.env.SYNC_QUEUE_BATCH_SIZE || "", 10),
|
|
1178
|
+
_SyncQueueProcessor.DEFAULT_BATCH_SIZE
|
|
1179
|
+
);
|
|
1180
|
+
}
|
|
1181
|
+
resolveMaxRetries() {
|
|
1182
|
+
return Math.max(
|
|
1183
|
+
_SyncQueueProcessor.SQLITE_ATTEMPT_CEILING,
|
|
1184
|
+
clampPositiveInt(
|
|
1185
|
+
parseInt(process.env.SYNC_QUEUE_MAX_RETRIES || "", 10),
|
|
1186
|
+
_SyncQueueProcessor.DEFAULT_MAX_RETRIES
|
|
1187
|
+
)
|
|
1188
|
+
);
|
|
1189
|
+
}
|
|
1190
|
+
handleFailure(item, errorMessage, maxRetries) {
|
|
1191
|
+
const priorLogicalAttempts = extractLogicalAttempts(item.last_error, item.attempts);
|
|
1192
|
+
const nextLogicalAttempts = priorLogicalAttempts + 1;
|
|
1193
|
+
const physicalNextAttempt = item.attempts + 1;
|
|
1194
|
+
if (nextLogicalAttempts >= maxRetries) {
|
|
1195
|
+
this.sqlite.updateSyncQueueItem(
|
|
1196
|
+
item.id,
|
|
1197
|
+
_SyncQueueProcessor.SQLITE_ATTEMPT_CEILING,
|
|
1198
|
+
formatAttemptError(errorMessage, nextLogicalAttempts, maxRetries)
|
|
1199
|
+
);
|
|
1200
|
+
return;
|
|
1201
|
+
}
|
|
1202
|
+
if (physicalNextAttempt >= _SyncQueueProcessor.SQLITE_ATTEMPT_CEILING) {
|
|
1203
|
+
this.sqlite.updateSyncQueueItem(
|
|
1204
|
+
item.id,
|
|
1205
|
+
0,
|
|
1206
|
+
formatAttemptError(errorMessage, nextLogicalAttempts, maxRetries)
|
|
1207
|
+
);
|
|
1208
|
+
return;
|
|
1209
|
+
}
|
|
1210
|
+
this.sqlite.updateSyncQueueItem(
|
|
1211
|
+
item.id,
|
|
1212
|
+
physicalNextAttempt,
|
|
1213
|
+
formatAttemptError(errorMessage, nextLogicalAttempts, maxRetries)
|
|
1214
|
+
);
|
|
1215
|
+
}
|
|
1148
1216
|
};
|
|
1217
|
+
function clampPositiveInt(value, fallback) {
|
|
1218
|
+
if (!Number.isFinite(value)) return fallback;
|
|
1219
|
+
const rounded = Math.floor(value);
|
|
1220
|
+
if (rounded <= 0) return fallback;
|
|
1221
|
+
return rounded;
|
|
1222
|
+
}
|
|
1223
|
+
function formatAttemptError(errorMessage, attempts, maxRetries) {
|
|
1224
|
+
return `[attempt ${attempts}/${maxRetries}] ${errorMessage}`;
|
|
1225
|
+
}
|
|
1226
|
+
function extractLogicalAttempts(lastError, fallback) {
|
|
1227
|
+
if (lastError) {
|
|
1228
|
+
const match = /^\[attempt (\d+)\/\d+\]\s*/.exec(lastError);
|
|
1229
|
+
if (match) {
|
|
1230
|
+
const parsed = Number.parseInt(match[1], 10);
|
|
1231
|
+
if (Number.isFinite(parsed) && parsed >= 0) {
|
|
1232
|
+
return parsed;
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
return fallback;
|
|
1237
|
+
}
|
|
1149
1238
|
|
|
1150
1239
|
// src/extraction/embeddings.ts
|
|
1151
1240
|
import OpenAI from "openai";
|
|
@@ -1664,7 +1753,8 @@ function applyBoosts(result, layerAppearances) {
|
|
|
1664
1753
|
}
|
|
1665
1754
|
|
|
1666
1755
|
// src/search/engine.ts
|
|
1667
|
-
var SearchEngine = class {
|
|
1756
|
+
var SearchEngine = class _SearchEngine {
|
|
1757
|
+
static LAYER_TIMEOUT_MS = 5e3;
|
|
1668
1758
|
orchestrator;
|
|
1669
1759
|
constructor(orchestrator) {
|
|
1670
1760
|
this.orchestrator = orchestrator;
|
|
@@ -1683,34 +1773,38 @@ var SearchEngine = class {
|
|
|
1683
1773
|
const searches = [];
|
|
1684
1774
|
if (shouldSearchFulltext(strategy)) {
|
|
1685
1775
|
searches.push(
|
|
1686
|
-
this.searchFulltext(request, scopes, limit)
|
|
1687
|
-
layerStats.sqlite.count = results.length;
|
|
1688
|
-
allResults.push(...results);
|
|
1689
|
-
})
|
|
1776
|
+
this.collectLayerResults("sqlite", () => this.searchFulltext(request, scopes, limit), layerStats, allResults)
|
|
1690
1777
|
);
|
|
1691
1778
|
}
|
|
1692
1779
|
if (shouldSearchSemantic(strategy) && this.orchestrator.qdrant && this.orchestrator.embeddings) {
|
|
1693
1780
|
searches.push(
|
|
1694
|
-
this.
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1781
|
+
this.collectLayerResults(
|
|
1782
|
+
"qdrant",
|
|
1783
|
+
() => this.withLayerTimeout(
|
|
1784
|
+
"qdrant",
|
|
1785
|
+
() => this.searchSemantic(request, scopes, limit),
|
|
1786
|
+
_SearchEngine.LAYER_TIMEOUT_MS
|
|
1787
|
+
),
|
|
1788
|
+
layerStats,
|
|
1789
|
+
allResults
|
|
1790
|
+
)
|
|
1698
1791
|
);
|
|
1699
1792
|
}
|
|
1700
1793
|
if (shouldSearchGraph(strategy) && includeGraph && this.orchestrator.age) {
|
|
1701
1794
|
searches.push(
|
|
1702
|
-
this.
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1795
|
+
this.collectLayerResults(
|
|
1796
|
+
"age",
|
|
1797
|
+
() => this.withLayerTimeout(
|
|
1798
|
+
"age",
|
|
1799
|
+
() => this.searchGraph(request, limit),
|
|
1800
|
+
_SearchEngine.LAYER_TIMEOUT_MS
|
|
1801
|
+
),
|
|
1802
|
+
layerStats,
|
|
1803
|
+
allResults
|
|
1804
|
+
)
|
|
1706
1805
|
);
|
|
1707
1806
|
}
|
|
1708
|
-
const startTime = Date.now();
|
|
1709
1807
|
await Promise.allSettled(searches);
|
|
1710
|
-
const elapsed = Date.now() - startTime;
|
|
1711
|
-
if (layerStats.sqlite.count > 0) layerStats.sqlite.ms = elapsed;
|
|
1712
|
-
if (layerStats.qdrant.count > 0) layerStats.qdrant.ms = elapsed;
|
|
1713
|
-
if (layerStats.age.count > 0) layerStats.age.ms = elapsed;
|
|
1714
1808
|
const merged = this.mergeResults(allResults, limit);
|
|
1715
1809
|
return {
|
|
1716
1810
|
results: merged,
|
|
@@ -1772,6 +1866,42 @@ var SearchEngine = class {
|
|
|
1772
1866
|
return [];
|
|
1773
1867
|
}
|
|
1774
1868
|
}
|
|
1869
|
+
async collectLayerResults(layer, fn, layerStats, allResults) {
|
|
1870
|
+
const startedAt = Date.now();
|
|
1871
|
+
try {
|
|
1872
|
+
const results = await fn();
|
|
1873
|
+
layerStats[layer].count = results.length;
|
|
1874
|
+
allResults.push(...results);
|
|
1875
|
+
} finally {
|
|
1876
|
+
layerStats[layer].ms = Date.now() - startedAt;
|
|
1877
|
+
}
|
|
1878
|
+
}
|
|
1879
|
+
async withLayerTimeout(layer, fn, timeoutMs) {
|
|
1880
|
+
let timeoutId;
|
|
1881
|
+
let timedOut = false;
|
|
1882
|
+
const layerPromise = fn().catch((error) => {
|
|
1883
|
+
console.warn(`[search] ${layer} search failed: ${error}`);
|
|
1884
|
+
return [];
|
|
1885
|
+
});
|
|
1886
|
+
const timeoutPromise = new Promise((resolve) => {
|
|
1887
|
+
timeoutId = setTimeout(() => {
|
|
1888
|
+
timedOut = true;
|
|
1889
|
+
console.warn(
|
|
1890
|
+
`[search] ${layer} search timed out after ${timeoutMs}ms, returning results from other layers`
|
|
1891
|
+
);
|
|
1892
|
+
resolve([]);
|
|
1893
|
+
}, timeoutMs);
|
|
1894
|
+
});
|
|
1895
|
+
try {
|
|
1896
|
+
const results = await Promise.race([layerPromise, timeoutPromise]);
|
|
1897
|
+
return results;
|
|
1898
|
+
} finally {
|
|
1899
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
1900
|
+
if (timedOut) {
|
|
1901
|
+
void layerPromise;
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1775
1905
|
// ── Result Merging ──────────────────────────────────────────────────
|
|
1776
1906
|
mergeResults(allResults, limit) {
|
|
1777
1907
|
const byId = /* @__PURE__ */ new Map();
|
|
@@ -1874,8 +2004,9 @@ var ConversationSummarizer = class {
|
|
|
1874
2004
|
};
|
|
1875
2005
|
|
|
1876
2006
|
export {
|
|
2007
|
+
SyncQueueProcessor,
|
|
1877
2008
|
StorageOrchestrator,
|
|
1878
2009
|
SearchEngine,
|
|
1879
2010
|
ConversationSummarizer
|
|
1880
2011
|
};
|
|
1881
|
-
//# sourceMappingURL=chunk-
|
|
2012
|
+
//# sourceMappingURL=chunk-BTR4T5L3.js.map
|