haematite-cli 0.21.7

The haematite command: serve a store, its door and Studio, answer MCP, query with jq, and host the store's assistant seat
Documentation
use super::{Batch, Record};
use serde_json::json;

impl Record {
    pub(super) fn stale_completion(&self) -> bool {
        self.batch.as_ref().is_some_and(|batch| {
            batch.items.iter().any(|item| {
                matches!(
                    item["kind"].as_str(),
                    Some("text" | "thought" | "tool_call")
                ) && item["conversation"]
                    .as_str()
                    .and_then(|id| self.conversations.get(id))
                    .is_some_and(|conversation| {
                        item["seq"]
                            .as_u64()
                            .is_some_and(|seq| seq <= conversation.live_seq)
                    })
            })
        })
    }
    pub(super) fn promote_committed(&mut self) -> anyhow::Result<bool> {
        let head=self.native.read("documents.get",&json!({"selection":{"branch":"main"},"collection":"_assistant","document_id":format!("{}/head",self.person)}));
        let expected = match head {
            Ok(answer) => answer["selection"]["version"].clone(),
            Err(error) => {
                self.failed(&error.code)?;
                return Ok(false);
            }
        };
        let original = self
            .batch
            .as_ref()
            .ok_or_else(|| anyhow::anyhow!("record_batch_absent"))?
            .items
            .clone();
        let mut records = Vec::new();
        let mut items = Vec::new();
        for old in original {
            let id = old["conversation"]
                .as_str()
                .ok_or_else(|| anyhow::anyhow!("record_conversation_invalid"))?
                .to_owned();
            let seq = self.next_sequence(&id)?;
            let old_seq = old["seq"]
                .as_u64()
                .ok_or_else(|| anyhow::anyhow!("record_sequence_invalid"))?;
            let marker = json!({"conversation":id,"seq":old_seq,"durable":true,"kind":"state","state":"record_recovered","superseded_by":seq});
            records.push(json!({"document_id":format!("{}/{id}/{old_seq:020}",self.person),"document":marker}));
            let mut item = old;
            item["seq"] = json!(seq);
            item["durable"] = json!(true);
            records.push(
                json!({"document_id":format!("{}/{id}/{seq:020}",self.person),"document":item}),
            );
            items.push(item);
        }
        for (id, conversation) in &self.conversations {
            records.push(json!({"document_id":format!("{}/{id}/meta",self.person),"document":conversation.meta}));
        }
        let arguments = json!({"request_id":super::super::permission::operation()?,"branch":"main","expected":expected,"collection":"_assistant","records":records});
        self.batch_bytes = self
            .measured(&arguments)?
            .checked_add(serde_json::to_vec(&items)?.len())
            .ok_or_else(|| anyhow::anyhow!("record_buffer_bound_overflow"))?;
        self.batch = Some(Batch {
            arguments,
            items,
            uncertain: false,
            attempted: false,
        });
        self.check_bound()?;
        self.commit()
    }
}