Skip to content

Layers and the handshake

Built and verified

All three layers and the memory backend are built: the actor models and the memory backend (memory), Embedder with local_hash and random_projection (embedding), Structure with flat and segment_tree (structure), Strategy with default (strategy) and the ContinuityEngine (engine).

coniti has three layers. The memory backend sits underneath all of them.

flowchart LR
  subgraph Actor["Actor layer"]
    N["Note, TimeRange, Query"]
  end
  subgraph Broker["Broker layer"]
    EN["ContinuityEngine"]
    ST["Strategy"]
  end
  subgraph System["System layer"]
    EM["Embedder"]
    SC["Structure"]
  end
  MB["MemoryBackend"]
  N <--> EN
  EN --> EM
  EN --> ST
  ST --> SC
  EN --> SC
  EN <--> MB
Layer Role
Actor Notes, time ranges and questions. No storage, no vectors to handle. Built.
System Embedder gives each message a vector. Structure organises vectors over time.
Broker Strategy decides what to admit and how to answer. The engine translates between actor objects and system inputs, and back.

The actor layer

The actor is whoever works with the library: a human, an agent, or any other party. A human is one kind of actor.

Built and verified

Import from coniti (they are defined in coniti.actor). These models hold no storage: storing notes is the memory backend’s job (memory).

from datetime import datetime, timezone

from coniti import Note, Query, TimeRange

note = Note.create("met Ana about the budget", project="q3")   # random id, time = now
dated = Note.create("kick-off", timestamp=datetime(2026, 1, 5, tzinfo=timezone.utc))
window = TimeRange(start=dated.timestamp, end=None)             # [start, forever)
query = Query(text="budget", time_range=window, k=5)

Note(id, text, timestamp, metadata) is frozen. id is a non-empty string. metadata must be JSON-serialisable and finite, and is deep-copied on the way in, so later edits to your dict do not change the note. Note.create(text, timestamp=None, **metadata) gives a random id and defaults the time to now.

Timestamp is seconds since the Unix epoch as a finite float. A timezone-aware datetime is accepted and converted. These are rejected: naive datetimes (their meaning depends on the machine’s timezone), booleans, NaN and infinity.

TimeRange(start, end) is half-open, [start, end). Either side may be None (unbounded). Bounds must be finite and start <= end. contains(t) tests membership and is_empty is true when start == end.

Query(text, time_range, near, k) needs at least one of text, time_range or near. near is the id of a note to recall around and must be non-empty (the engine will pass it to structures as anchor_id). k is the number of results, between 1 and 10000 (default 10).

The handshake

Built and verified

The handshake below is what the ContinuityEngine does; see engine.

  • Ingest: Note → embed → strategy decides to admit → insert into every structure → write to memory last. If a structure fails, the engine rebuilds the structures from memory (memory was not written yet), so ingest is atomic.
  • Ask: Query → embed the text if any → strategy queries the structures → hits are resolved to notes → Answer.
  • Remove: delete from memory, then rebuild every structure from memory. Structures are derived data.

There is no session object. All state is the memory backend plus the structures derived from it.