Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "opencortex"
version = "0.7.0"
version = "0.8.0"
description = "Memory and context management system for AI Agents"
readme = "README.md"
requires-python = ">=3.10"
Expand Down
2 changes: 1 addition & 1 deletion src/opencortex/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
Tenant-based multi-user memory and context management system.
"""

__version__ = "0.7.0"
__version__ = "0.8.0"

from opencortex.config import CortexConfig, get_config, init_config

Expand Down
16 changes: 13 additions & 3 deletions src/opencortex/insights/collector.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,21 @@ async def fetch_traces(
Returns:
List of Trace objects
"""
# Filter DSL fix: the canonical shape for storage.filter is
# ``{"op": "and", "conds": [...]}`` at the outer level and
# ``{"op": "must", "field": "X", "conds": [val]}`` at the leaf
# level (see ``SessionRecordsRepository._build_session_filter``
# for the reference pattern). The previous shape used
# ``conditions`` instead of ``conds`` and ``op="="`` instead of
# ``op="must"`` — the storage adapter's filter translator
# silently treated the whole expression as no-op, returning
# all traces in the collection regardless of tenant/user
# scope. Cross-tenant insights leaked.
filter_expr = {
"op": "and",
"conditions": [
{"field": "tenant_id", "op": "=", "value": tenant_id},
{"field": "user_id", "op": "=", "value": user_id},
"conds": [
{"op": "must", "field": "tenant_id", "conds": [tenant_id]},
{"op": "must", "field": "user_id", "conds": [user_id]},
],
}
all_records = await self._trace_store._storage.filter(
Expand Down
13 changes: 13 additions & 0 deletions src/opencortex/orchestrator.py
Original file line number Diff line number Diff line change
Expand Up @@ -2482,6 +2482,19 @@ async def _sync_anchor_projection_records(

all_new_records = anchor_records + fp_records

# REVIEW closure tracker R3-P-06 — short-circuit when the new
# projection is empty AND there's no abstract_json input.
# The defer_derive initial leaf write hits this path with both
# inputs empty and would otherwise pay 2 stale-filter scans
# over Qdrant prefixes that are guaranteed to be empty (the
# leaf is brand new). The ``not abstract_json`` guard keeps
# the cleanup path on legitimate update flows where
# abstract_json is present but happens to contribute no
# anchors or fact_points — those updates DO need stale
# cleanup.
if not all_new_records and not abstract_json:
return

# Embed all texts in a single batch call
if all_new_records and self._embedder:
texts = [r["overview"] for r in all_new_records]
Expand Down
Loading