kore-memory 3.0.1__tar.gz → 3.0.2__tar.gz

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 (136) hide show
  1. {kore_memory-3.0.1 → kore_memory-3.0.2}/CHANGELOG.md +49 -0
  2. {kore_memory-3.0.1 → kore_memory-3.0.2}/PKG-INFO +1 -1
  3. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/auth.py +7 -1
  4. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/compressor.py +3 -2
  5. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/config.py +1 -1
  6. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/context_assembler.py +4 -3
  7. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/filesystem_watcher.py +9 -1
  8. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/main.py +3 -3
  9. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/mcp_server.py +18 -3
  10. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/repository/memory.py +196 -186
  11. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/repository/search.py +4 -2
  12. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/templates/dashboard.html +47 -14
  13. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/vector_index.py +11 -0
  14. {kore_memory-3.0.1 → kore_memory-3.0.2}/pyproject.toml +1 -1
  15. {kore_memory-3.0.1 → kore_memory-3.0.2}/sdk/js/package.json +1 -1
  16. {kore_memory-3.0.1 → kore_memory-3.0.2}/start.sh +3 -0
  17. {kore_memory-3.0.1 → kore_memory-3.0.2}/tests/test_api.py +27 -0
  18. {kore_memory-3.0.1 → kore_memory-3.0.2}/.coveragerc +0 -0
  19. {kore_memory-3.0.1 → kore_memory-3.0.2}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  20. {kore_memory-3.0.1 → kore_memory-3.0.2}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  21. {kore_memory-3.0.1 → kore_memory-3.0.2}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  22. {kore_memory-3.0.1 → kore_memory-3.0.2}/.github/workflows/benchmark.yml +0 -0
  23. {kore_memory-3.0.1 → kore_memory-3.0.2}/.github/workflows/build-sdk.yml +0 -0
  24. {kore_memory-3.0.1 → kore_memory-3.0.2}/.github/workflows/ci.yml +0 -0
  25. {kore_memory-3.0.1 → kore_memory-3.0.2}/.github/workflows/publish.yml +0 -0
  26. {kore_memory-3.0.1 → kore_memory-3.0.2}/.gitignore +0 -0
  27. {kore_memory-3.0.1 → kore_memory-3.0.2}/CONTRIBUTING.md +0 -0
  28. {kore_memory-3.0.1 → kore_memory-3.0.2}/LICENSE +0 -0
  29. {kore_memory-3.0.1 → kore_memory-3.0.2}/MIGRATION-v0.6.md +0 -0
  30. {kore_memory-3.0.1 → kore_memory-3.0.2}/README.md +0 -0
  31. {kore_memory-3.0.1 → kore_memory-3.0.2}/article-devto.md +0 -0
  32. {kore_memory-3.0.1 → kore_memory-3.0.2}/assets/favicon.svg +0 -0
  33. {kore_memory-3.0.1 → kore_memory-3.0.2}/assets/logo.svg +0 -0
  34. {kore_memory-3.0.1 → kore_memory-3.0.2}/docs/coding-memory-mode.md +0 -0
  35. {kore_memory-3.0.1 → kore_memory-3.0.2}/docs/competitive-analysis-2025.md +0 -0
  36. {kore_memory-3.0.1 → kore_memory-3.0.2}/docs/quickstart-v2.1.md +0 -0
  37. {kore_memory-3.0.1 → kore_memory-3.0.2}/docs/v1.0-roadmap.md +0 -0
  38. {kore_memory-3.0.1 → kore_memory-3.0.2}/examples/async_usage.py +0 -0
  39. {kore_memory-3.0.1 → kore_memory-3.0.2}/examples/basic_usage.py +0 -0
  40. {kore_memory-3.0.1 → kore_memory-3.0.2}/examples/langchain_example.py +0 -0
  41. {kore_memory-3.0.1 → kore_memory-3.0.2}/hatch_build.py +0 -0
  42. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore-daemon.sh +0 -0
  43. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/__init__.py +0 -0
  44. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/acl.py +0 -0
  45. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/analytics.py +0 -0
  46. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/audit.py +0 -0
  47. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/auto_tuner.py +0 -0
  48. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/cli.py +0 -0
  49. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/client.py +0 -0
  50. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/conflict_detector.py +0 -0
  51. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/consolidation.py +0 -0
  52. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/dashboard.py +0 -0
  53. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/database.py +0 -0
  54. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/decay.py +0 -0
  55. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/embedder.py +0 -0
  56. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/events.py +0 -0
  57. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/filesystem_overlay.py +0 -0
  58. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/integrations/__init__.py +0 -0
  59. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/integrations/crewai.py +0 -0
  60. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/integrations/entities.py +0 -0
  61. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/integrations/langchain.py +0 -0
  62. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/integrations/openai_agents.py +0 -0
  63. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/integrations/pydantic_ai.py +0 -0
  64. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/models.py +0 -0
  65. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/plugins.py +0 -0
  66. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/policy_engine.py +0 -0
  67. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/privacy.py +0 -0
  68. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/py.typed +0 -0
  69. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/ranking.py +0 -0
  70. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/repository/__init__.py +0 -0
  71. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/repository/entity.py +0 -0
  72. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/repository/graph.py +0 -0
  73. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/repository/lifecycle.py +0 -0
  74. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/repository/sessions.py +0 -0
  75. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/scorer.py +0 -0
  76. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/structured.py +0 -0
  77. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/summarizer.py +0 -0
  78. {kore_memory-3.0.1 → kore_memory-3.0.2}/kore_memory/welcome.py +0 -0
  79. {kore_memory-3.0.1 → kore_memory-3.0.2}/presets/claude-code/README.md +0 -0
  80. {kore_memory-3.0.1 → kore_memory-3.0.2}/presets/claude-code/mcp.json +0 -0
  81. {kore_memory-3.0.1 → kore_memory-3.0.2}/presets/claude-code-coding.md +0 -0
  82. {kore_memory-3.0.1 → kore_memory-3.0.2}/presets/cursor/README.md +0 -0
  83. {kore_memory-3.0.1 → kore_memory-3.0.2}/presets/cursor/mcp.json +0 -0
  84. {kore_memory-3.0.1 → kore_memory-3.0.2}/requirements.txt +0 -0
  85. {kore_memory-3.0.1 → kore_memory-3.0.2}/scripts/assert_benchmarks.py +0 -0
  86. {kore_memory-3.0.1 → kore_memory-3.0.2}/scripts/import_memory.py +0 -0
  87. {kore_memory-3.0.1 → kore_memory-3.0.2}/sdk/js/.gitignore +0 -0
  88. {kore_memory-3.0.1 → kore_memory-3.0.2}/sdk/js/README.md +0 -0
  89. {kore_memory-3.0.1 → kore_memory-3.0.2}/sdk/js/package-lock.json +0 -0
  90. {kore_memory-3.0.1 → kore_memory-3.0.2}/sdk/js/src/client.ts +0 -0
  91. {kore_memory-3.0.1 → kore_memory-3.0.2}/sdk/js/src/errors.ts +0 -0
  92. {kore_memory-3.0.1 → kore_memory-3.0.2}/sdk/js/src/index.ts +0 -0
  93. {kore_memory-3.0.1 → kore_memory-3.0.2}/sdk/js/src/types.ts +0 -0
  94. {kore_memory-3.0.1 → kore_memory-3.0.2}/sdk/js/tests/client.test.ts +0 -0
  95. {kore_memory-3.0.1 → kore_memory-3.0.2}/sdk/js/tests/errors.test.ts +0 -0
  96. {kore_memory-3.0.1 → kore_memory-3.0.2}/sdk/js/tests/helpers.ts +0 -0
  97. {kore_memory-3.0.1 → kore_memory-3.0.2}/sdk/js/tsconfig.json +0 -0
  98. {kore_memory-3.0.1 → kore_memory-3.0.2}/sdk/js/tsup.config.ts +0 -0
  99. {kore_memory-3.0.1 → kore_memory-3.0.2}/sdk/js/vitest.config.ts +0 -0
  100. {kore_memory-3.0.1 → kore_memory-3.0.2}/tests/__init__.py +0 -0
  101. {kore_memory-3.0.1 → kore_memory-3.0.2}/tests/benchmarks/__init__.py +0 -0
  102. {kore_memory-3.0.1 → kore_memory-3.0.2}/tests/benchmarks/conftest_bench.py +0 -0
  103. {kore_memory-3.0.1 → kore_memory-3.0.2}/tests/benchmarks/datasets/dataset_a_temporal.json +0 -0
  104. {kore_memory-3.0.1 → kore_memory-3.0.2}/tests/benchmarks/datasets/dataset_b_conflicts.json +0 -0
  105. {kore_memory-3.0.1 → kore_memory-3.0.2}/tests/benchmarks/datasets/dataset_c_coding.json +0 -0
  106. {kore_memory-3.0.1 → kore_memory-3.0.2}/tests/benchmarks/datasets/dataset_d_graph.json +0 -0
  107. {kore_memory-3.0.1 → kore_memory-3.0.2}/tests/benchmarks/datasets/dataset_e_context.json +0 -0
  108. {kore_memory-3.0.1 → kore_memory-3.0.2}/tests/benchmarks/datasets/loaders.py +0 -0
  109. {kore_memory-3.0.1 → kore_memory-3.0.2}/tests/benchmarks/test_benchmarks.py +0 -0
  110. {kore_memory-3.0.1 → kore_memory-3.0.2}/tests/conftest.py +0 -0
  111. {kore_memory-3.0.1 → kore_memory-3.0.2}/tests/test_audit.py +0 -0
  112. {kore_memory-3.0.1 → kore_memory-3.0.2}/tests/test_auth_events.py +0 -0
  113. {kore_memory-3.0.1 → kore_memory-3.0.2}/tests/test_auto_tuner.py +0 -0
  114. {kore_memory-3.0.1 → kore_memory-3.0.2}/tests/test_cli.py +0 -0
  115. {kore_memory-3.0.1 → kore_memory-3.0.2}/tests/test_client.py +0 -0
  116. {kore_memory-3.0.1 → kore_memory-3.0.2}/tests/test_client_sync.py +0 -0
  117. {kore_memory-3.0.1 → kore_memory-3.0.2}/tests/test_conflict_detection.py +0 -0
  118. {kore_memory-3.0.1 → kore_memory-3.0.2}/tests/test_coverage_gaps.py +0 -0
  119. {kore_memory-3.0.1 → kore_memory-3.0.2}/tests/test_crewai.py +0 -0
  120. {kore_memory-3.0.1 → kore_memory-3.0.2}/tests/test_dashboard.py +0 -0
  121. {kore_memory-3.0.1 → kore_memory-3.0.2}/tests/test_entities.py +0 -0
  122. {kore_memory-3.0.1 → kore_memory-3.0.2}/tests/test_langchain.py +0 -0
  123. {kore_memory-3.0.1 → kore_memory-3.0.2}/tests/test_m1_graph_entity.py +0 -0
  124. {kore_memory-3.0.1 → kore_memory-3.0.2}/tests/test_m2_structured_memory.py +0 -0
  125. {kore_memory-3.0.1 → kore_memory-3.0.2}/tests/test_m3a_session_consolidation.py +0 -0
  126. {kore_memory-3.0.1 → kore_memory-3.0.2}/tests/test_mcp.py +0 -0
  127. {kore_memory-3.0.1 → kore_memory-3.0.2}/tests/test_policy_engine.py +0 -0
  128. {kore_memory-3.0.1 → kore_memory-3.0.2}/tests/test_ranking_engine.py +0 -0
  129. {kore_memory-3.0.1 → kore_memory-3.0.2}/tests/test_sessions.py +0 -0
  130. {kore_memory-3.0.1 → kore_memory-3.0.2}/tests/test_v11_fixes.py +0 -0
  131. {kore_memory-3.0.1 → kore_memory-3.0.2}/tests/test_v12_features.py +0 -0
  132. {kore_memory-3.0.1 → kore_memory-3.0.2}/tests/test_v21_temporal.py +0 -0
  133. {kore_memory-3.0.1 → kore_memory-3.0.2}/tests/test_v2_features.py +0 -0
  134. {kore_memory-3.0.1 → kore_memory-3.0.2}/tests/test_wave3_graph.py +0 -0
  135. {kore_memory-3.0.1 → kore_memory-3.0.2}/tests/test_wave3_overlay.py +0 -0
  136. {kore_memory-3.0.1 → kore_memory-3.0.2}/tests/test_wave3_watcher.py +0 -0
@@ -11,6 +11,55 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
11
11
 
12
12
  ---
13
13
 
14
+ ## [3.0.2] - 2026-04-28
15
+
16
+ ### Theme: "Hardening & Bug Sweep"
17
+
18
+ Comprehensive bug-fixing release: 24 fixes across backend, dashboard, security, and infrastructure. No breaking changes.
19
+
20
+ ### Fixed — Backend (Critical)
21
+
22
+ - **Data corruption in compressor**: `DELETE FROM memory_relations WHERE source_id = target_id` was unscoped — deleted ALL self-relations globally instead of only the newly merged record. Now scoped to `(new_id, new_id)`.
23
+ - **save_memory_batch bypassed entire pipeline**: Batch save was missing privacy filter, title generation, content_hash, structured extraction (facts/concepts/narrative), entity extraction, conflict detection, dedup, session_id, and all v2.1/M2 fields. Refactored with shared `_prepare_memory()` helper — full parity with single save.
24
+ - **Race condition in dedup (TOCTOU)**: Dedup check and INSERT were in separate transactions. Two concurrent identical saves could both pass the check. Now merged into a single transaction.
25
+ - **ZeroDivisionError in RRF fusion**: Setting `KORE_RRF_W_FTS=0` caused crash when that stream had results. Added `or 1.0` fallback.
26
+ - **search_by_tag returned incomplete data**: Missing `title`, `invalidated_at`, `compressed_into`, and all structured fields in SELECT. `_row_to_record` silently returned nulls.
27
+ - **assert in context_assembler**: Token budget invariant used `assert` (disabled with `python -O`). Replaced with `if/raise ValueError`.
28
+
29
+ ### Fixed — Security
30
+
31
+ - **Auth bypass behind reverse proxy**: `_is_local()` checked only `request.client.host`, which is the proxy's IP (127.0.0.1) when behind nginx/Caddy. Now detects `X-Forwarded-For`/`X-Real-IP` headers and requires API key when proxy is detected.
32
+ - **MCP import no size limit**: Docstring said "max 500" but code passed full list. Added `memories[:500]` truncation.
33
+ - **Search query DoS vector**: `/search` had no `max_length` on query parameter. Added `max_length=1000`.
34
+ - **CSP redundancy**: Removed `'unsafe-inline'` from `script-src` (redundant when nonce is present).
35
+
36
+ ### Fixed — Infrastructure
37
+
38
+ - **VectorIndex memory leak**: Legacy in-memory cache grew unbounded (one entry per agent, all embeddings in RAM). Added LRU eviction with max 20 agents.
39
+ - **Timer leak in filesystem_watcher**: `stop_all()` stopped observers but didn't cancel pending debounce timers. Timers could fire after shutdown, causing DB operations on closed connections.
40
+ - **init_db() at import time in MCP server**: Any `import kore_memory.mcp_server` triggered full DB initialization. Replaced with lazy `_ensure_db()` using double-check locking.
41
+
42
+ ### Fixed — Dashboard
43
+
44
+ - **Tab lost on refresh**: No URL state persistence. Added `location.hash` routing — tab survives refresh and browser back/forward.
45
+ - **Memories page empty for agents without relations**: Default view used `/graph/hubs` (requires relations). Changed to `/search?q=*`.
46
+ - **Relations section permanently broken**: `loadRelations(data.results)` where `/analytics` has no `results` field. Now fetches memories via `/search`.
47
+ - **Graph detail panel permanently broken after X close**: Inline `style.display='none'` overrode CSS class. Fixed to use `classList.remove('open')`.
48
+ - **Graph animation CPU leak**: `requestAnimationFrame` loop ran at 60fps on hidden canvas after navigating away. Now cancelled on page change.
49
+ - **HiDPI mouse coordinates wrong**: `screenToGraph()` used `canvas.width` (physical pixels) instead of `canvas.offsetWidth` (CSS pixels). Hover/drag/click hit wrong nodes on Retina displays.
50
+ - **Edit from graph detail race condition**: `setTimeout(300ms)` guess for API load. Now opens modal directly with node data.
51
+ - **NaN decay display**: `decay_score * 100` crashed on null. Defaults to 1.0.
52
+ - **No pagination indicator**: Added "Showing X of Y" when `has_more` is true.
53
+ - **Edit modal ignored tags**: Tags field was visible but values discarded on save. Now sends tags via API.
54
+ - **Session memories appended duplicates**: Clicking "View" multiple times appended cards. Now replaces existing detail.
55
+ - **Inline event handlers violated CSP**: `onmouseover`/`onmouseout` replaced with CSS `:hover`.
56
+
57
+ ### Added
58
+
59
+ - Test: batch/single save parity test verifying structured fields (title, content_hash, memory_type) are populated identically.
60
+
61
+ ---
62
+
14
63
  ## [3.0.0] - 2026-04-15
15
64
 
16
65
  ### Theme: "Cognitive Runtime + Coding Vertical GA"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kore-memory
3
- Version: 3.0.1
3
+ Version: 3.0.2
4
4
  Summary: The memory layer that thinks like a human: remembers what matters, forgets what doesn't, and never calls home.
5
5
  Project-URL: Homepage, https://github.com/auriti-labs/kore-memory
6
6
  Project-URL: Documentation, https://auritidesign.it/docs
@@ -64,7 +64,13 @@ def _is_local(request: Request) -> bool:
64
64
  # "testclient" only in explicit test environments
65
65
  if os.getenv("KORE_TEST_MODE", "0") == "1":
66
66
  trusted.add("testclient")
67
- return client_host in trusted
67
+ if client_host not in trusted:
68
+ return False
69
+ # Detect reverse proxy: if X-Forwarded-For is present, the real client
70
+ # is NOT local — require auth even if socket peer is localhost
71
+ if request.headers.get("X-Forwarded-For") or request.headers.get("X-Real-IP"):
72
+ return False
73
+ return True
68
74
 
69
75
 
70
76
  def _local_only_mode() -> bool:
@@ -362,9 +362,10 @@ def _merge_cluster(cluster: list[dict], agent_id: str = "default") -> int | None
362
362
  WHERE target_id IN ({placeholders})""",
363
363
  [new_id, *ids],
364
364
  )
365
- # Remove any self-relations created by the relink
365
+ # Remove any self-relations created by the relink (scoped to new_id only)
366
366
  conn.execute(
367
- "DELETE FROM memory_relations WHERE source_id = target_id",
367
+ "DELETE FROM memory_relations WHERE source_id = ? AND target_id = ?",
368
+ (new_id, new_id),
368
369
  )
369
370
 
370
371
  # Mark originals as compressed
@@ -85,4 +85,4 @@ CONFLICT_MAX_CANDIDATES = int(os.getenv("KORE_CONFLICT_MAX_CANDIDATES", "10"))
85
85
 
86
86
  # ── Version ───────────────────────────────────────────────────────────────────
87
87
 
88
- VERSION = "3.0.1"
88
+ VERSION = "3.0.2"
@@ -100,9 +100,10 @@ def assemble_context(
100
100
  tokens_used += tokens_est
101
101
 
102
102
  # Verifica invariante TOKEN-BUDGET BOUNDED
103
- assert tokens_used <= req.budget_tokens, (
104
- f"INVARIANTE VIOLATO: budget_tokens_used ({tokens_used}) > budget_tokens ({req.budget_tokens})"
105
- )
103
+ if tokens_used > req.budget_tokens:
104
+ raise ValueError(
105
+ f"INVARIANTE VIOLATO: budget_tokens_used ({tokens_used}) > budget_tokens ({req.budget_tokens})"
106
+ )
106
107
 
107
108
  # Carica conflitti tra le memorie selezionate (invariante #3)
108
109
  selected_ids = [m.id for m in selected]
@@ -62,6 +62,7 @@ class _WatcherEntry:
62
62
  self.include_extra_md = include_extra_md
63
63
  self.max_depth = max_depth
64
64
  self.observer: Any = None
65
+ self.handler: Any = None
65
66
  self.started_at: str = datetime.now(UTC).isoformat()
66
67
  self.events_processed: int = 0
67
68
 
@@ -103,12 +104,18 @@ class _WatcherRegistry:
103
104
  ]
104
105
 
105
106
  def stop_all(self) -> int:
106
- """Ferma tutti i watcher — chiamato al shutdown del server."""
107
+ """Ferma tutti i watcher e cancella i timer pendenti — chiamato al shutdown del server."""
107
108
  with self._lock:
108
109
  entries = list(self._watchers.values())
109
110
  self._watchers.clear()
110
111
  count = 0
111
112
  for entry in entries:
113
+ # Cancel pending debounce timers to prevent post-shutdown DB operations
114
+ if entry.handler:
115
+ with entry.handler._timers_lock:
116
+ for timer in entry.handler._timers.values():
117
+ timer.cancel()
118
+ entry.handler._timers.clear()
112
119
  if entry.observer:
113
120
  try:
114
121
  entry.observer.stop()
@@ -277,6 +284,7 @@ def start_watcher(
277
284
  observer.start()
278
285
 
279
286
  entry.observer = observer
287
+ entry.handler = handler
280
288
  _registry.add(entry)
281
289
 
282
290
  logger.info("Watcher avviato: base_path=%s agent=%s", resolved, agent_id)
@@ -189,7 +189,7 @@ class SecurityHeadersMiddleware(BaseHTTPMiddleware):
189
189
  "default-src 'self'; "
190
190
  "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; "
191
191
  "font-src 'self' https://fonts.gstatic.com; "
192
- f"script-src 'nonce-{nonce}' 'unsafe-inline'; "
192
+ f"script-src 'nonce-{nonce}'; "
193
193
  "connect-src 'self'; "
194
194
  "img-src 'self' data:; "
195
195
  "frame-ancestors 'none'"
@@ -315,7 +315,7 @@ def save_batch(
315
315
  @app.get("/search", response_model=MemorySearchResponse)
316
316
  def search(
317
317
  request: Request,
318
- q: str = Query(..., min_length=1, description="Search query (any language)"),
318
+ q: str = Query(..., min_length=1, max_length=1000, description="Search query (any language)"),
319
319
  limit: int = Query(5, ge=1, le=20),
320
320
  cursor: str | None = Query(None, description="Opaque pagination cursor"),
321
321
  category: str | None = Query(None),
@@ -1119,7 +1119,7 @@ def shared_memories(
1119
1119
  @app.get("/stream/search")
1120
1120
  async def stream_search(
1121
1121
  request: Request,
1122
- q: str = Query(..., min_length=1, description="Search query"),
1122
+ q: str = Query(..., min_length=1, max_length=1000, description="Search query"),
1123
1123
  limit: int = Query(10, ge=1, le=50),
1124
1124
  _: str = _Auth,
1125
1125
  agent_id: str = _Agent,
@@ -46,8 +46,20 @@ logger = logging.getLogger(__name__)
46
46
  _SERVER_START_TIME = _time.monotonic()
47
47
  _SESSION_TIMESTAMP = datetime.now().strftime("%Y%m%d-%H%M%S")
48
48
 
49
- # Initialize DB before any operation
50
- init_db()
49
+ # DB initialized lazily on first operation (not at import time)
50
+ _db_initialized = False
51
+ _db_init_lock = threading.Lock()
52
+
53
+
54
+ def _ensure_db() -> None:
55
+ global _db_initialized
56
+ if _db_initialized:
57
+ return
58
+ with _db_init_lock:
59
+ if not _db_initialized:
60
+ init_db()
61
+ _db_initialized = True
62
+
51
63
 
52
64
  mcp = FastMCP(
53
65
  "Kore Memory",
@@ -65,6 +77,7 @@ _session_lock = threading.Lock()
65
77
 
66
78
  def _get_or_create_session(agent_id: str) -> str:
67
79
  """Restituisce la session_id corrente per l'agent, creandola se necessario."""
80
+ _ensure_db()
68
81
  if agent_id in _agent_sessions:
69
82
  return _agent_sessions[agent_id]
70
83
  with _session_lock:
@@ -401,7 +414,7 @@ def memory_import(
401
414
  Import memories from a list of dicts. Each item must have at least 'content'.
402
415
  Optional fields: category, importance. Maximum 500 memories.
403
416
  """
404
- count = import_memories(memories, agent_id=_sanitize_agent_id(agent_id))
417
+ count = import_memories(memories[:500], agent_id=_sanitize_agent_id(agent_id))
405
418
  return {"imported": count, "message": f"{count} memories imported"}
406
419
 
407
420
 
@@ -833,6 +846,8 @@ def _add_health_route() -> None:
833
846
  def main():
834
847
  import argparse
835
848
 
849
+ _ensure_db()
850
+
836
851
  parser = argparse.ArgumentParser(description="Kore MCP Server")
837
852
  parser.add_argument(
838
853
  "--transport",