You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Fix thread scope propagation in AtomicMemory provider
## Summary
Thread scope (`Scope.thread`) was silently dropped before reaching the Core API. Ingest, search, and list operations never forwarded `session_id` on the wire, so thread-scoped reads returned unfiltered results and thread-scoped writes stored memories without a session association. This fix propagates `thread` as `session_id` on every route that Core honors it, strips it from routes that do not (get, delete, expand), and validates that responses echo back a matching `session_id` for thread-scoped requests.
## Changes
- Add `thread: str | None` field to `UserScope` and `WorkspaceScope`.
- Add `include_thread` flag to `scope_to_fields` and `scope_to_query_pairs`; emit `session_id` when opted in.
- Add `strip_read_filters` to `scope_mapper` — drops both `agent_scope` and `thread` for routes (get, delete, expand) that do not accept those filters.
- Fix `strip_agent_scope` to preserve `thread` when clearing workspace filter.
- Propagate `session_id` on ingest and search request bodies in `provider.py` / `async_provider.py`.
- Extract `_build_list_path` helper that appends `session_id` to the list query string when thread is set; use it in both sync and async providers.
- Use `include_thread=True` in `scope_to_fields` / `scope_to_query_pairs` calls for ingest, search, and list in `handle_impl.py` and `async_handle_impl.py`.
- Add `_build_memory_scope` in `handle_impl.py` and `_build_scope` in `mappers.py`: project `session_id` from the backend response back into the returned scope, and raise `ValueError` when a thread-scoped request receives a response without a matching `session_id`.
- Add comprehensive test coverage across `test_provider.py`, `test_async_provider.py`, `test_handle_base.py`, `test_mappers.py`, and `test_scope_mapper.py` for all affected operations.
## Why
The Core API uses `session_id` to partition memories by conversation thread. Without forwarding `thread` as `session_id`, thread-scoped ingests write memories outside any session, thread-scoped searches return the full unfiltered memory set, and there is no way to verify the backend actually applied the requested scope. The validation step on responses closes a silent correctness gap where a mismatch between requested and returned session could go undetected.
## Validation
All new tests are exercised via `respx` mocks against the full HTTP path. Tests cover:
- `session_id` present in request body/query for ingest, search, list, package, and `search_as_of`.
- `session_id` absent from get and delete query strings (routes that do not filter by thread).
- Expand strips `thread` from returned memory scope.
- Response mapper raises on a missing or mismatched `session_id` when thread scope was requested.
- `strip_read_filters` and `strip_agent_scope` preserve or clear `thread` as expected.
0 commit comments