api-hooks
Source: crates/lvcs-api/src/hooks.rs.
/// `track_read` (decision 117), the PostToolUse `Read` hook's op: the
/// tool read `path` (the absolute path it used, in one of this daemon's
/// mirrors), whose bytes had the sha-256 `hash` (lowercase hex). The
/// mirror records the heads those bytes were rendered at, and the file's
/// next save is based on them when the document holds them and they are
/// at or after the author's latest change there. Refused `REJECTED` when
/// `path` is in no mirror of this daemon, or the mirror's author isn't the
/// caller.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct TrackReadParams {
pub path: String,
pub hash: String,
}
/// `track_read` result: an empty object.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct TrackReadResult {}
/// `track_edit` (decisions 117 and 134), the PreToolUse hook's op: the
/// tool is about to replace `old` (`None` for a whole-file write) in
/// `path` (as for `track_read`). The daemon locates `old` at its first
/// occurrence in the text at the heads the tool read (the whole document
/// when it can't), then notifies that range with `op` and `what` and
/// waits, answering [`TrackEditResult`] when the wait ends, as `announce`
/// does. Refused `REJECTED` when `path` is in no mirror of this daemon,
/// or the mirror's author isn't the caller.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct TrackEditParams {
pub path: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub old: Option<String>,
pub op: AnnounceOpParam,
pub what: String,
}
/// `track_edit` result (decision 134): as [`AnnounceResult`], but for holds.
///
/// Each hold is the [`Notification`] the caller's `open` list holds for it
/// (`kind` `hold`, its sender's tier, harness, and model from the caps
/// render, the notified range, its thread), metadata only; its words are
/// read with `notifications.read` (decision 173).
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct TrackEditResult {
pub notification: String,
pub outcome: AnnounceOutcome,
/// The holds received during the wait, as the caller's `open` list
/// holds them.
#[serde(default)]
pub holds: Vec<Notification>,
#[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, as for `announce`.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub recover: Option<String>,
/// The action that goes on, as for `announce`.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub next: Option<Next>,
/// How to name each participant the result names (decision 243), by
/// id.
#[serde(default)]
pub names: BTreeMap<String, Named>,
}
/// `track_write` (decision 117), the `PostToolUse` `Write` and `Edit` hook's op.
///
/// The tool wrote `path` (as for `track_read`); lists the caller's unread
/// notifications as `notifications` does, up to `max_bytes` of their JSON
/// with its `open` notifications counted first (no bound when omitted).
/// Refused `REJECTED` when `path` is in no mirror of this daemon.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct TrackWriteParams {
/// The absolute path the tool wrote.
pub path: String,
/// The most bytes of notification JSON listed.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_bytes: Option<u32>,
}
/// `track_write` result. Listing marks nothing read; it is a hinting
/// listing (decision 242), so it moves the caller's hint watermark.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct TrackWriteResult {
/// Whether the mirror's author isn't the caller (§5's warning: the
/// write is attributed to the author).
#[serde(default)]
pub author_mismatch: bool,
/// The caller's unread notifications that fit, not open.
#[serde(default)]
pub notifications: Vec<Notification>,
/// The caller's open asks and holds, as `notifications` lists them.
#[serde(default)]
pub open: Vec<Notification>,
/// How many didn't fit.
#[serde(default)]
pub omitted: u32,
/// The ids listed, open ones included, that arrived after the caller's
/// last hinting listing (decision 242).
#[serde(default)]
pub fresh: Vec<String>,
/// How to name each participant the result names (decision 243), by
/// id.
#[serde(default)]
pub names: BTreeMap<String, Named>,
}
/// `stop_gate` (decisions 214 and 217), the `Stop` hook's op: whether the
/// caller may stop now. Read-only, and never retried.
///
/// The daemon keeps the caller's newest change to each file it changed
/// through this daemon, and to each workspace document it changed there,
/// since the daemon started. The answer is `failed` while the caller has
/// an open `action_failed` streak (decision 193) and changed nothing since
/// the notice that opened it, once per set of open streaks: a later
/// `stop_gate` with the same set and nothing changed answers `pending`,
/// naming no file and those streaks, until a streak closes or opens, the
/// caller changes something, or `STOP_WAIT_MAX_MS` passed since the
/// `failed`, which then answers `failed` again (decision 230); else `merge` while one of those changes is in
/// a forked workspace no merge took since (decision 217); else `pending`
/// while a streak of its is open, or, for one of those files, no frontier
/// of the change set's value holds its newest change, unless the project
/// configures no check; and `passed` otherwise. A restart forgets the
/// changes, as it forgets `status.checks`.
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct StopGateParams {}
/// What the `Stop` gate decided (decisions 214 and 217).
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum StopGate {
/// The caller may stop: the change set holds its newest changes, and
/// no check of its fails.
Passed,
/// The runners haven't passed its newest changes yet, its fix of a
/// failed check included: ask again after `retry_after_ms`.
Pending,
/// Its newest change is in a workspace it hasn't merged: merge it with
/// `ws.merge`.
Merge,
/// A check failed on its changes: read the `action_failed` with
/// `notifications.read` and fix the change.
Failed,
}
/// `stop_gate` result.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct StopGateResult {
/// The decision.
pub gate: StopGate,
/// `failed`: the caller's open failing streaks, as `status.checks`
/// lists them (metadata only); `pending` with no file: those streaks,
/// already shown at a `Stop` (decision 230); empty otherwise.
#[serde(default)]
pub failed: Vec<Notification>,
/// `merge`: the workspaces holding its unmerged changes, each by its
/// name when this daemon runs it, else its id in `ID_ENCODING`; empty
/// otherwise.
#[serde(default)]
pub workspaces: Vec<String>,
/// `pending`: the files whose newest change of the caller's no frontier
/// of the change set holds yet, sorted; empty otherwise.
#[serde(default)]
pub pending: Vec<String>,
/// `pending`: how long to wait before asking again (`STOP_RETRY_MS`).
#[serde(default, skip_serializing_if = "Option::is_none")]
pub retry_after_ms: Option<u64>,
}