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
23 changes: 23 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,29 @@ Notable changes to the native `ai-hist` CLI are documented here.

### Rust API

- The change feed reports every evidence table and each row exactly as
stored. `ChangeKind` gains `History`, `Presence`, `CommitLink`,
`Trajectory`, `SourceObservation` and `ObservationEvidence` (`ALL` lists
twelve kinds); their rows are stamped and tombstoned by the same triggers,
and a database the six-kind feed reached stamps them once on open, above its
head, so a cursor bound to every kind resumes into all of them. A named
cursor stored as `*` now spans all twelve kinds. A consumer that passed the
six original kinds as an explicit list was stored as `*` too; that list no
longer equals `ChangeKind::ALL`, so its resume fails with
`ConsumerKindsMismatch` — drain it under a new consumer name, or resync from
`Watermark::START`.
`EvidenceRow::History(HistoryEntry)` is the typed prompt row and
`EvidenceRow::Untyped` marks a kind with none. `Change` gains `columns:
Option<StoredRow>` — every column but `revision`, in table order, values as
SQLite holds them, read from the live table so a new column is carried
without a code change; the export journal's payload is built from the same
column list — and `key: Vec<serde_json::Value>`, the record's identity as
the journal keys it (`["history", source, timestamp_ms, prompt]`), on
upserts and tombstones alike; a prompt's session is not part of its key, so
a prompt gaining one is an upsert, never a delete. `Change::source` is `Option<Source>` and
`Change::source_name` holds the stored name, so a row from a source this
build does not know is carried instead of failing the drain. The `history`
FTS update trigger fires only on the columns it indexes.
- `SessionStore::discover(DiscoveryOptions)` is the shallow catalog sweep:
every local provider's sessions from metadata, as `Shallow` rows, hydrating
none and taking no `SyncRunLock`. `discover`, `sync` and `hydrate` take an
Expand Down
2 changes: 1 addition & 1 deletion crates/ai-hist/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ fn main() -> Result<(), ai_hist::Error> {
let query = ai_hist::ChangeQuery::default().consumer("my-ingest");
let mut changes = store.changes_since(ai_hist::Watermark::CONSUMER, query)?;
for change in changes.by_ref() {
let _change = change?; // `Upsert(EvidenceRow)` or `Delete`, keyed by kind + record_key
let _change = change?; // `Upsert` or `Delete` of `change.key`; `columns` is the stored row
}
changes.commit()?; // the cursor moves only here
Ok(())
Expand Down
23 changes: 22 additions & 1 deletion crates/ai-hist/public-api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,18 @@ pub ai_hist::Capability::Full
pub ai_hist::Capability::Partial
pub ai_hist::Capability::ShallowOnly
#[non_exhaustive] pub enum ai_hist::ChangeKind
pub ai_hist::ChangeKind::CommitLink
pub ai_hist::ChangeKind::FileEdit
pub ai_hist::ChangeKind::History
pub ai_hist::ChangeKind::ObservationEvidence
pub ai_hist::ChangeKind::Presence
pub ai_hist::ChangeKind::Relationship
pub ai_hist::ChangeKind::Session
pub ai_hist::ChangeKind::SessionEvent
pub ai_hist::ChangeKind::SessionMarker
pub ai_hist::ChangeKind::SourceObservation
pub ai_hist::ChangeKind::ToolCall
pub ai_hist::ChangeKind::Trajectory
impl ai_hist::ChangeKind
pub const ai_hist::ChangeKind::ALL: &'static [ai_hist::ChangeKind]
pub fn ai_hist::ChangeKind::as_str(self) -> &'static str
Expand Down Expand Up @@ -107,11 +113,13 @@ impl ai_hist::EvidenceKind
pub fn ai_hist::EvidenceKind::as_str(self) -> &'static str
#[non_exhaustive] pub enum ai_hist::EvidenceRow
pub ai_hist::EvidenceRow::FileEdit(ai_hist::SessionFileEdit)
pub ai_hist::EvidenceRow::History(ai_hist::HistoryEntry)
pub ai_hist::EvidenceRow::Relationship(ai_hist::SessionRelationship)
pub ai_hist::EvidenceRow::Session(ai_hist::ShallowSession)
pub ai_hist::EvidenceRow::SessionEvent(ai_hist::SessionEvent)
pub ai_hist::EvidenceRow::SessionMarker(ai_hist::SessionMarker)
pub ai_hist::EvidenceRow::ToolCall(ai_hist::SessionToolCall)
pub ai_hist::EvidenceRow::Untyped
#[non_exhaustive] pub enum ai_hist::HydrateStatus
pub ai_hist::HydrateStatus::CapabilityLimited
pub ai_hist::HydrateStatus::Hydrated
Expand Down Expand Up @@ -294,12 +302,15 @@ pub ai_hist::CatalogSession::workspace_roots: alloc::vec::Vec<alloc::string::Str
impl ai_hist::CatalogSession
pub fn ai_hist::CatalogSession::session_ref(&self) -> ai_hist::SessionRef
#[non_exhaustive] pub struct ai_hist::Change
pub ai_hist::Change::columns: core::option::Option<ai_hist::StoredRow>
pub ai_hist::Change::key: alloc::vec::Vec<serde_json::value::Value>
pub ai_hist::Change::kind: ai_hist::ChangeKind
pub ai_hist::Change::op: ai_hist::ChangeOp
pub ai_hist::Change::record_key: alloc::string::String
pub ai_hist::Change::revision: u64
pub ai_hist::Change::session_id: alloc::string::String
pub ai_hist::Change::source: ai_hist::Source
pub ai_hist::Change::source: core::option::Option<ai_hist::Source>
pub ai_hist::Change::source_name: alloc::string::String
#[non_exhaustive] pub struct ai_hist::ChangeQuery
pub ai_hist::ChangeQuery::batch: usize
pub ai_hist::ChangeQuery::consumer: core::option::Option<alloc::string::String>
Expand Down Expand Up @@ -705,6 +716,16 @@ pub ai_hist::StoreOptions::db_path: core::option::Option<std::path::PathBuf>
pub ai_hist::StoreOptions::home: core::option::Option<std::path::PathBuf>
pub ai_hist::StoreOptions::read_only: bool
pub ai_hist::StoreOptions::roots: core::option::Option<ai_hist::ProviderRoots>
pub struct ai_hist::StoredRow
impl ai_hist::StoredRow
pub fn ai_hist::StoredRow::get(&self, &str) -> core::option::Option<&serde_json::value::Value>
pub fn ai_hist::StoredRow::is_empty(&self) -> bool
pub fn ai_hist::StoredRow::iter(&self) -> impl core::iter::traits::iterator::Iterator<Item = (&str, &serde_json::value::Value)> + '_
pub fn ai_hist::StoredRow::len(&self) -> usize
impl serde_core::ser::Serialize for ai_hist::StoredRow
pub fn ai_hist::StoredRow::serialize<S: serde_core::ser::Serializer>(&self, S) -> core::result::Result<<S as serde_core::ser::Serializer>::Ok, <S as serde_core::ser::Serializer>::Error>
impl<'de> serde_core::de::Deserialize<'de> for ai_hist::StoredRow
pub fn ai_hist::StoredRow::deserialize<D: serde_core::de::Deserializer<'de>>(D) -> core::result::Result<Self, <D as serde_core::de::Deserializer>::Error>
#[non_exhaustive] pub struct ai_hist::SyncOptions
pub ai_hist::SyncOptions::force: bool
pub ai_hist::SyncOptions::lock_timeout_ms: u64
Expand Down
Loading
Loading