@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.
Files changed (49) hide show
  1. package/README.md +22 -10
  2. package/dist/{chunk-RFLG2CCR.js → chunk-7DNVIKQ3.js} +100 -10
  3. package/dist/chunk-7DNVIKQ3.js.map +1 -0
  4. package/dist/{chunk-JSQBXYDM.js → chunk-BTR4T5L3.js} +157 -26
  5. package/dist/chunk-BTR4T5L3.js.map +1 -0
  6. package/dist/{chunk-VXULEX3A.cjs → chunk-CSGZH2SG.cjs} +9 -9
  7. package/dist/chunk-CSGZH2SG.cjs.map +1 -0
  8. package/dist/{chunk-JNWCMHOB.js → chunk-ITGUJZUL.js} +2 -2
  9. package/dist/{chunk-JNWCMHOB.js.map → chunk-ITGUJZUL.js.map} +1 -1
  10. package/dist/{chunk-NHFPLDZK.js → chunk-J2C5USXH.js} +3 -3
  11. package/dist/{chunk-CRPEAZ44.cjs → chunk-JYQB2DOK.cjs} +161 -30
  12. package/dist/chunk-JYQB2DOK.cjs.map +1 -0
  13. package/dist/{chunk-ZY2C2CJQ.cjs → chunk-LA5OP5VI.cjs} +2 -2
  14. package/dist/chunk-LA5OP5VI.cjs.map +1 -0
  15. package/dist/{chunk-NMUPGLJW.cjs → chunk-NYZMAY73.cjs} +110 -20
  16. package/dist/chunk-NYZMAY73.cjs.map +1 -0
  17. package/dist/cli/index.cjs +25 -25
  18. package/dist/cli/index.cjs.map +1 -1
  19. package/dist/cli/index.js +7 -7
  20. package/dist/cli/index.js.map +1 -1
  21. package/dist/index.cjs +5 -5
  22. package/dist/index.cjs.map +1 -1
  23. package/dist/index.d.cts +64 -2
  24. package/dist/index.d.ts +64 -2
  25. package/dist/index.js +4 -4
  26. package/dist/memory-service-B2BAEKR2.cjs +9 -0
  27. package/dist/memory-service-B2BAEKR2.cjs.map +1 -0
  28. package/dist/memory-service-ZTLGPIUH.js +9 -0
  29. package/dist/{server-BTbRv-yX.d.ts → server-CtNlCow7.d.cts} +62 -0
  30. package/dist/{server-BTbRv-yX.d.cts → server-CtNlCow7.d.ts} +62 -0
  31. package/dist/server.cjs +4 -4
  32. package/dist/server.cjs.map +1 -1
  33. package/dist/server.d.cts +1 -1
  34. package/dist/server.d.ts +1 -1
  35. package/dist/server.js +3 -3
  36. package/package.json +2 -2
  37. package/templates/.env.example +1 -1
  38. package/templates/openclaw-memory.config.ts +2 -2
  39. package/dist/chunk-CRPEAZ44.cjs.map +0 -1
  40. package/dist/chunk-JSQBXYDM.js.map +0 -1
  41. package/dist/chunk-NMUPGLJW.cjs.map +0 -1
  42. package/dist/chunk-RFLG2CCR.js.map +0 -1
  43. package/dist/chunk-VXULEX3A.cjs.map +0 -1
  44. package/dist/chunk-ZY2C2CJQ.cjs.map +0 -1
  45. package/dist/memory-service-6WDMF6KX.cjs +0 -9
  46. package/dist/memory-service-6WDMF6KX.cjs.map +0 -1
  47. package/dist/memory-service-GKEG6J2D.js +0 -9
  48. /package/dist/{chunk-NHFPLDZK.js.map → chunk-J2C5USXH.js.map} +0 -0
  49. /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
- [![npm version](https://img.shields.io/npm/v/openclaw-memory)](https://www.npmjs.com/package/openclaw-memory)
8
+ [![npm version](https://img.shields.io/npm/v/@poprobertdaniel/openclaw-memory)](https://www.npmjs.com/package/@poprobertdaniel/openclaw-memory)
9
9
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
10
10
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.x-blue)](https://www.typescriptlang.org/)
11
11
  [![Bun](https://img.shields.io/badge/Bun-%E2%89%A51.0-black)](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-4o-mini',
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/openclaw/openclaw-memory.git
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
- } from "./chunk-JSQBXYDM.js";
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-JNWCMHOB.js";
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
- return new Elysia5().get("/api/health", async () => {
493
+ const healthHandler = async () => {
493
494
  return await orchestrator.healthCheck();
494
- }).post("/api/sync/retry", async ({ set }) => {
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
- }).get("/api/sync/queue", () => {
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
- if (typeof globalThis.Bun !== "undefined") {
730
- const bun = globalThis.Bun;
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-RFLG2CCR.js.map
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
- if (typeof globalThis.Bun !== "undefined") {
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(50);
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(`[sync-queue] Processing ${items.length} items`);
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.sqlite.updateSyncQueueItem(item.id, item.attempts + 1, errorMsg);
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 exceeded max retries`);
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).then((results) => {
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.searchSemantic(request, scopes, limit).then((results) => {
1695
- layerStats.qdrant.count = results.length;
1696
- allResults.push(...results);
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.searchGraph(request, limit).then((results) => {
1703
- layerStats.age.count = results.length;
1704
- allResults.push(...results);
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-JSQBXYDM.js.map
2012
+ //# sourceMappingURL=chunk-BTR4T5L3.js.map