Skip to content

Release Notes: v0.1.9

Date: 2026-08-19

This is a large release. Since 0.1.8 the crate gained a network-facing memory server, a segmented memory index, agent integrations for four providers with session recording, and Python bindings, plus a security-hardening pass over the server and a tantivy upgrade that clears a RUSTSEC advisory.

⚠️ Upgrade warning: API removals coming in 0.2.0

Five MemoryIndex query methods are deprecated as of this release and will be removed in 0.2.0:

query_timed · query_with_lexical_hits · query_with_filters · query_with_filters_and_lexical · query_with_filters_multi

0.1.9 is the migration window. Code calling these still compiles and behaves exactly as before, but now emits deprecation warnings naming the replacement. Building with -D deprecated will surface every call site.

Migrate before upgrading to 0.2.0. See Deprecations for the replacement table and a worked example. IndexStore methods are unaffected; if you only use IndexStore, there is nothing to do.

0.2.0 will also stop creating an on-disk lexical directory and will no longer fail initialization when that directory holds unrelated files, since the store's second lexical index is being removed.

Highlights

  • Bumped crate version from 0.1.8 to 0.1.9.
  • Memory server: new server binary exposing GET /health, POST /add, and POST /search, backed by memory_api::MemoryService.
  • Segmented memory index: SegmentedMemoryIndex with pluggable SegmentRoutingStrategy, selectable through MemoryIndexLayout.
  • Agent integrations: Claude Code, Codex, Gemini CLI, and AGY, each behind its own feature flag, with hook handling and MCP tools.
  • Session recording: provider session capture and replay with secret redaction.
  • Python bindings: IndexStore exposed to Python behind the python feature.
  • Store inspection: IndexStore::inspection plus CLI views for source documents, records, and segments.
  • Shared query preparation: query_plan::PreparedQuery, now used by both the CLI and the memory server.
  • Security: server authentication is required by default on non-loopback binds; DoS limits; broader credential redaction; tantivy 0.220.25.

Memory Server

A new server binary serves an Add/Search contract over HTTP, backed by IndexStore.

cargo run --release --bin server -- \
  --bind 0.0.0.0:8080 \
  --index /var/lib/lint-ai/memory-index \
  --server-token "$SERVER_TOKEN"

Endpoints are GET /health, POST /add, and POST /search. The token is accepted as X-Api-Key, Authorization: Bearer <token>, or Authorization: Token <token>, and may also come from the SERVER_TOKEN environment variable. See docs/server.md.

memory_api::MemoryService is usable directly as a library type for callers that want the same Add/Search semantics without the HTTP layer.

Security hardening

  • Authentication is required by default. The server refuses to start on a non-loopback bind address without a token. --allow-unauthenticated overrides this for closed networks.
  • Constant-time token comparison.
  • Socket read/write timeouts, header count and size caps, and a concurrent-connection cap (503 when exceeded).
  • Request bodies are streamed rather than pre-allocated from Content-Length.
  • request_id, user_id, and session_id are rejected if longer than 256 bytes or containing control characters.

Segmented Memory Index

SegmentedMemoryIndex partitions the corpus into per-group segments, each a self-contained MemoryIndex, and routes a query to the most promising segments instead of scanning everything. Select it through PipelineOptions:

let options = PipelineOptions {
    memory_index_layout: MemoryIndexLayout::Segmented {
        query_top_n: 5,
        routing_strategy: SegmentRoutingStrategy::LocalDistinctiveness,
    },
    ..PipelineOptions::default()
};

Routing strategies include sparse overlap, KL divergence, local distinctiveness, coverage-based and team-coverage variants, and typed evidence. Segment selection, enrichment, and router-miss diagnostics are reported through SegmentQueryDiagnostics.

An experimental segment_scoped_benchmark binary (requires the experimental feature) reports segmented retrieval metrics and router failure analysis alongside the standard scoped benchmark.

Agent Integrations

Four provider integrations, each behind a feature flag: claude-code, codex, gemini-cli, and agy. Each maps provider hook events into SourceDocument values, stores them through IndexStore, and exposes MCP search and info tools. See src/integrations/README.md and the per-provider documents in docs/.

Session recording

Provider sessions can be captured and replayed. Recorded events are redacted before they are written: key-name matching plus token-shape detection covering private keys, bearer tokens, JWTs, and provider key prefixes (AWS, Google, GitHub, GitLab, Stripe, Hugging Face, npm, Slack). Manifests and event files are written with 0600 permissions.

Python Bindings

Behind the python feature, IndexStore is exposed to Python via PyO3, covering upsert and query. Build with --features python to produce the extension module.

Store Inspection

IndexStore::inspection() returns an IndexStoreInspection summary of the store's contents, including per-segment detail when the segmented layout is active. The same information is surfaced through CLI inspection views for source documents, records, segments, and store summaries.

Shared Query Preparation

Previously the CLI analyzed queries, inferring routing intent and augmenting query text, while the memory server's /search did neither, so the same question behaved differently depending on how it arrived. That preparation now lives in query_plan::PreparedQuery, and both callers use it.

use lint_ai::query_plan::PreparedQuery;

let prepared = PreparedQuery::new("How many projects did I ship last quarter?");
let context = prepared.temporal_context();   // carries QueryRoutingIntent::Count
let text = prepared.search_query();          // the augmented query text

PreparedQuery owns the QueryAnalysis so the borrowed TemporalQueryContext stays valid; build one per query and keep it alive for the search. temporal_context() leaves allowed_doc_ids unset so callers can scope the search themselves.

let prepared = PreparedQuery::new(&query);
let results = store.query_prepared(&prepared, top_k, &filters)?;

IndexStore::query_prepared applies that preparation and scopes the search through TemporalQueryContext::allowed_doc_ids, resolved from filters via the new MemoryIndex::doc_ids_matching_filters. Prefer it over IndexStore::query_filtered for any caller handling a raw user query.

MemoryService::search now goes through query_prepared, so requests gain query-routing intent (affecting candidate limits, graph-boost suppression for count and sum queries, and intent-specific evidence processing) and query augmentation. Results will differ from the pre-0.1.9 server for queries where either applies. Request and response shapes are unchanged.

This does not affect the count-query branch of group aggregation, which still derives count_query from query-string markers independently of QueryRoutingIntent. Reconciling the two count classifiers is deferred.

Tokenizer Module

index.rs and segments.rs each carried their own tokenizer and stopword list. Both now live in tokenizer behind an explicit TokenizerMode:

  • TokenizerMode::Unstemmed: regex-bounded terms, lowercased, no stemming. Used by the lexical and rerank path.
  • TokenizerMode::Stemmed: split on non-alphanumeric boundaries, each token stemmed. Used by segment routing.

The two modes are deliberate, not drift. Benchmarking showed each is a measured improvement for its own caller and a regression for the other: switching segment routing to unstemmed cost roughly 5pp of recall@5, and switching the lexical path to stemmed cost roughly 0.1–0.3pp across recall, MRR, and NDCG. Only the location of the code changed.

Dependencies

  • tantivy 0.220.25, which brings in a fixed crossbeam-epoch (RUSTSEC-2026-0204) and drops the unmaintained instant crate. 0.26 was evaluated and deliberately not adopted: its TopDocs/TopNComputer tie-breaking change (quickwit-oss/tantivy#2775) cost roughly 1.7pp recall@5 and 1.3pp MRR on LongMemEval-S, while 0.25 reproduces 0.1.8 results within noise.
  • pyo3 updated to clear a security audit finding.

Deprecations

The following MemoryIndex methods are deprecated in 0.1.9 and will be removed in 0.2.0. Each carries a #[deprecated] note naming its replacement.

Deprecated Replacement
query_timed query_with_temporal_context
query_with_lexical_hits query_with_temporal_context
query_with_filters query_with_temporal_context + allowed_doc_ids
query_with_filters_and_lexical query_with_temporal_context + allowed_doc_ids
query_with_filters_multi query_with_temporal_context + allowed_doc_ids

Migrating a filtered query:

// before
let results = index.query_with_filters(query, top_k, &filters);

// after
let allowed = index.doc_ids_matching_filters(&filters);
let context = TemporalQueryContext {
    allowed_doc_ids: allowed.as_ref(),
    ..TemporalQueryContext::default()
};
let results = index.query_with_temporal_context(query, top_k, context).0;

For the multi-query case, resolve allowed once and reuse it across queries; this preserves the batching query_with_filters_multi provided.

IndexStore::query, query_timed, query_filtered, and query_filtered_multi are not deprecated and keep working unchanged.

Retrieval Quality

The global retrieval path is unchanged. A full LongMemEval-S scoped run (500 queries) reproduces the 0.1.8 aggregates exactly on all eight metrics: recall@5/10/20, recall_any@5/10/20, MRR, and NDCG@10. Upgrading does not require re-validating retrieval quality, with the exception of the memory server /search behavior change noted above.