Skip to main content

kindling_client/
body.rs

1//! Wire-facing request bodies for the v1 API.
2//!
3//! These mirror the camelCase shapes in `kindling-server`'s `dto.rs` exactly.
4//! Response bodies are the domain types from `kindling-types` (already
5//! camelCase) and are deserialized directly — no response DTOs needed.
6
7use kindling_types::{CapsuleType, Id, Observation, ObservationInput, PinTargetType, ScopeIds};
8use serde::{Deserialize, Serialize};
9
10/// `POST /v1/capsules` body — open a capsule.
11#[derive(Debug, Clone, Serialize)]
12#[serde(rename_all = "camelCase")]
13pub(crate) struct OpenCapsuleBody {
14    pub kind: CapsuleType,
15    pub intent: String,
16    pub scope_ids: ScopeIds,
17    #[serde(skip_serializing_if = "Option::is_none")]
18    pub id: Option<Id>,
19}
20
21/// `PATCH /v1/capsules/:id/close` body — all fields optional.
22#[derive(Debug, Clone, Default, Serialize)]
23#[serde(rename_all = "camelCase")]
24pub struct CloseCapsuleBody {
25    /// Ask the daemon to generate a summary on close.
26    #[serde(skip_serializing_if = "Option::is_none")]
27    pub generate_summary: Option<bool>,
28    /// Provide summary content directly.
29    #[serde(skip_serializing_if = "Option::is_none")]
30    pub summary_content: Option<String>,
31    /// Confidence for a provided summary, in `[0.0, 1.0]`.
32    #[serde(skip_serializing_if = "Option::is_none")]
33    pub confidence: Option<f64>,
34}
35
36/// `POST /v1/observations` body — `ObservationInput` flattened plus the
37/// top-level routing/append options (`capsuleId`, `validate`).
38#[derive(Debug, Clone, Serialize)]
39#[serde(rename_all = "camelCase")]
40pub(crate) struct AppendObservationBody {
41    #[serde(flatten)]
42    pub input: ObservationInput,
43    #[serde(skip_serializing_if = "Option::is_none")]
44    pub capsule_id: Option<Id>,
45    #[serde(skip_serializing_if = "Option::is_none")]
46    pub validate: Option<bool>,
47}
48
49/// `POST /v1/observations` response — the stored observation (flattened) plus
50/// the daemon's `deduplicated` marker. Mirrors `AppendObservationResponse` in
51/// `kindling-server`'s `dto.rs`.
52#[derive(Debug, Clone, Deserialize)]
53#[serde(rename_all = "camelCase")]
54pub(crate) struct AppendObservationResponseBody {
55    #[serde(flatten)]
56    pub observation: Observation,
57    /// Defaults to `false` for rolling-upgrade safety: an older
58    /// (pre-KINTEG-002) daemon does not emit this field, and an absent marker
59    /// semantically means "a fresh write" (not a duplicate).
60    #[serde(default)]
61    pub deduplicated: bool,
62}
63
64/// `POST /v1/pins` body — create a pin.
65#[derive(Debug, Clone, Serialize)]
66#[serde(rename_all = "camelCase")]
67pub struct CreatePinBody {
68    /// What kind of entity is being pinned.
69    pub target_type: PinTargetType,
70    /// The id of the observation or summary to pin.
71    pub target_id: Id,
72    /// Optional free-text note.
73    #[serde(skip_serializing_if = "Option::is_none")]
74    pub note: Option<String>,
75    /// Optional time-to-live in milliseconds; the pin expires after this.
76    #[serde(skip_serializing_if = "Option::is_none")]
77    pub ttl_ms: Option<i64>,
78    /// Optional scope override for the pin.
79    #[serde(skip_serializing_if = "Option::is_none")]
80    pub scope_ids: Option<ScopeIds>,
81}
82
83impl CreatePinBody {
84    /// Construct a minimal pin body for `target_type`/`target_id`.
85    pub fn new(target_type: PinTargetType, target_id: impl Into<Id>) -> Self {
86        Self {
87            target_type,
88            target_id: target_id.into(),
89            note: None,
90            ttl_ms: None,
91            scope_ids: None,
92        }
93    }
94}
95
96/// `POST /v1/context/session-start` body. Both fields optional on the wire;
97/// the client always sends `scopeIds` (a repo scope built from the project
98/// root) to reproduce the Node hook's `{ repoId: <project root> }` filter.
99#[derive(Debug, Clone, Serialize)]
100#[serde(rename_all = "camelCase")]
101pub(crate) struct SessionStartContextBody {
102    #[serde(skip_serializing_if = "Option::is_none")]
103    pub max_results: Option<u32>,
104    pub scope_ids: ScopeIds,
105}
106
107/// `POST /v1/context/pre-compact` body.
108#[derive(Debug, Clone, Serialize)]
109#[serde(rename_all = "camelCase")]
110pub(crate) struct PreCompactContextBody {
111    pub scope_ids: ScopeIds,
112}