Docs

api-threads

Source: crates/lvcs-api/src/threads.rs.

/// What a notification says its sender will do to the range (design §5),
/// a JSON string: `adding`, `changing`, or `removing`.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum AnnounceOpParam {
    Adding,
    Changing,
    Removing,
}

/// `announce` (design §5, decision 51): publish a notification for the range
/// `start..end` of `address` as `workspace` sees it (`main` when omitted),
/// code points in the text at `base` (the heads the client last read; the
/// document anchors the range there as a cursor pair, `DocEvent::Anchor`),
/// then wait. The result
/// arrives when the wait ends: at once when no other participant is
/// present on the document, otherwise after the wait window
/// (`WAIT_WINDOW_MS`, or `lvcs.toml`'s value up to `WAIT_WINDOW_MAX_MS`),
/// or as soon as a `hold` arrives. A range must hold at least one
/// character, except in an empty document, where the notification covers
/// the whole document. `ref` optionally names more detail (`kad://<key>`
/// or `lvcs://<doc>#<pointer>`). Errors: `UNKNOWN_BASE`, `OUT_OF_BOUNDS`,
/// `RATE_LIMITED` (the gossip bucket), `REJECTED` (no `message`
/// capability).
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct AnnounceParams {
    pub address: String,
    pub base: Vec<String>,
    pub start: usize,
    pub end: usize,
    pub op: AnnounceOpParam,
    pub what: String,
    #[serde(rename = "ref", default, skip_serializing_if = "Option::is_none")]
    pub reference: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub workspace: Option<String>,
}

/// How an `announce` wait ended (design §5, decision 51), a JSON string:
/// proceed with the edit (`proceed`), pause it because someone objected
/// (`hold`), or yield to an earlier overlapping notification (`yield`).
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AnnounceOutcome {
    Proceed,
    Hold,
    Yield,
}

/// One `note` or `hold` a notification received: who sent it
/// (`ID_ENCODING`), its text, the thread a hold opened, and its sender's
/// Unix time in milliseconds.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ResponseItem {
    pub by: String,
    pub text: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub thread: Option<String>,
    pub ts: i64,
}

/// `announce` result, when the wait ends.
///
/// The notification's id (`ID_ENCODING`), how the wait ended, and the holds
/// and notes received during it. On `yield`,
/// `yielded_to` is the earlier notification's id, and the daemon has
/// turned the edit into an `ask` to its sender in the range's thread, with
/// the notification's `what` as its text (chosen at the M3 freeze), named
/// by `ask`; the client does not edit.
/// On `hold`, `recover` is `wait`, and on `yield` it is `yield`, each with
/// `next` `notifications {}`; on `proceed` both are absent (decision 165).
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct AnnounceResult {
    pub notification: String,
    pub outcome: AnnounceOutcome,
    #[serde(default)]
    pub holds: Vec<ResponseItem>,
    #[serde(default)]
    pub notes: Vec<ResponseItem>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub yielded_to: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub ask: Option<AskResult>,
    /// How to go on: a `recover` value, absent on `proceed`.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub recover: Option<String>,
    /// The action that goes on, absent on `proceed`.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub next: Option<Next>,
}

/// `note` and `hold` (design §5): respond to the notification
/// `notification` (its id). A `note` is information only; a `hold` is an
/// objection, and the daemon opens a thread on the notification's range
/// addressed to its sender, with `text` as the first message.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct RespondParams {
    pub notification: String,
    pub text: String,
}

/// `note` result: an empty object.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct NoteResult {}

/// `hold` result: the address of the thread the hold opened.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct HoldResult {
    pub thread: String,
}

/// `ask` (design §9): post an `ask` to the thread `thread`, or open a new
/// thread anchored on `address` (in `workspace`'s view), on `workspace`,
/// or, with neither, on the participants `to`, and post it there. `to`
/// lists the participants it expects an answer from (`ID_ENCODING`); empty
/// means anyone subscribed. A new thread's `to` is fixed when it opens.
/// The ask stays open until answered.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct AskParams {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub thread: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub address: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub workspace: Option<String>,
    #[serde(default)]
    pub to: Vec<String>,
    pub text: String,
}

/// `ask` result: the thread's address and the ask's message id
/// (`ID_ENCODING`), which an `answer` names.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct AskResult {
    pub thread: String,
    pub message: String,
}

/// `answer` (design §9): reply to the ask `ask` (its message id) in the
/// thread `thread`.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct AnswerParams {
    pub thread: String,
    pub ask: String,
    pub text: String,
}

/// `answer` result: the answer's message id.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct AnswerResult {
    pub message: String,
}

/// Whether a response push is a note or a hold, a JSON string.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ResponseKindParam {
    Note,
    Hold,
}

/// Server push, JSON-RPC notification method `response`: a `note` or
/// `hold` on one of the client's participant's notifications (design §5),
/// including those that arrive after its `announce` answered; a hold names
/// its thread. `tier`, `harness`, and `model` are the responder's
/// attribution, as for `notification`.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ResponsePush {
    pub notification: String,
    pub kind: ResponseKindParam,
    pub response: ResponseItem,
    pub tier: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub harness: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub model: Option<String>,
}

/// Server push, JSON-RPC notification method `thread_message`: a message
/// in a thread whose anchor the client subscribes to, or that is addressed
/// to its participant (design §9). `kind` is `say`, `ask`, or `answer` (an
/// open set: a client skips a kind it doesn't know); `ask` names the ask
/// an answer replies to; `author` its author, and `tier`, `harness`, and
/// `model` the author's attribution, as for `notification`; `time_ms` the
/// sending request's time.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ThreadMessagePush {
    pub thread: String,
    pub message: String,
    pub author: String,
    pub tier: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub harness: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub model: Option<String>,
    pub kind: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub ask: Option<String>,
    pub text: String,
    pub time_ms: i64,
}

/// `say` (§9, decision 117): append `text` to the existing thread
/// `thread`, delivered to the thread's `to` and its authors. It never
/// opens a thread or an ask.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct SayParams {
    pub thread: String,
    pub text: String,
}

/// `say` result: the message id (`ID_ENCODING`).
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SayResult {
    pub message: String,
}

/// `thread.list` (decision 303): the threads this daemon knows, metadata
/// only (their text is a `read` of the thread's address), sorted by
/// address; only those anchored at or under the address `anchor` when
/// named, or in the workspace `workspace` (a name or id).
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct ThreadListParams {
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub anchor: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub workspace: Option<String>,
}

/// `thread.list` result.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ThreadListResult {
    #[serde(default)]
    pub threads: Vec<ThreadEntry>,
}

/// One thread's metadata.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ThreadEntry {
    /// The thread document's address.
    pub thread: String,
    /// The address it is anchored on, if any.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub address: Option<String>,
    /// The workspace it is anchored on (`ID_ENCODING`), if any.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub workspace: Option<String>,
    /// The participants it is addressed to, by name (`Named`), sorted.
    #[serde(default)]
    pub to: Vec<String>,
    /// The participants who wrote in it, by name, sorted.
    #[serde(default)]
    pub authors: Vec<String>,
}

/// `thread.read` (decision 312): the messages of the thread
/// document at `thread`, oldest first, the last `limit` of them when
/// named: a pull read of content the viewer opened.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct ThreadReadParams {
    pub thread: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub limit: Option<u32>,
}

/// `thread.read` result.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ThreadReadResult {
    pub thread: String,
    /// The participants it is addressed to, by name (`Named`).
    #[serde(default)]
    pub to: Vec<String>,
    #[serde(default)]
    pub messages: Vec<ThreadReadMessage>,
}

/// One stored message of a thread.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ThreadReadMessage {
    /// Its id (`ID_ENCODING`), which an `answer` names as its `ask`.
    pub id: String,
    /// Its author's participant id (`ID_ENCODING`).
    pub author: String,
    /// Its author's name (`Named`).
    pub name: String,
    /// `say`, `ask`, or `answer`.
    pub kind: String,
    pub text: String,
    /// Its signed time, in Unix milliseconds.
    pub at_ms: i64,
    /// For an answer, the ask it replies to.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub ask: Option<String>,
}