1use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6
7#[derive(Debug, Serialize, Deserialize)]
10pub struct StoreMemoryRequest {
11 pub content: String,
12 #[serde(default)]
13 pub memory_type: Option<String>,
14 #[serde(default, alias = "domain")]
15 pub space: Option<String>,
16 #[serde(default)]
17 pub source_agent: Option<String>,
18 #[serde(default)]
19 pub title: Option<String>,
20 #[serde(default)]
21 pub confidence: Option<f32>,
22 #[serde(default)]
23 pub supersedes: Option<String>,
24 #[serde(default)]
26 pub entity: Option<String>,
27 #[serde(default)]
29 pub entity_id: Option<String>,
30 #[serde(default)]
31 pub structured_fields: Option<serde_json::Value>,
32 #[serde(default)]
33 pub retrieval_cue: Option<String>,
34}
35
36#[derive(Debug, Serialize, Deserialize)]
37pub struct SearchMemoryRequest {
38 pub query: String,
39 #[serde(default = "default_limit")]
40 pub limit: usize,
41 #[serde(default)]
42 pub memory_type: Option<String>,
43 #[serde(default, alias = "domain")]
44 pub space: Option<String>,
45 #[serde(default)]
46 pub source_agent: Option<String>,
47 #[serde(default)]
54 pub rerank: bool,
55}
56
57#[derive(Debug, Serialize, Deserialize)]
58pub struct ListMemoriesRequest {
59 #[serde(default)]
60 pub memory_type: Option<String>,
61 #[serde(default, alias = "domain")]
62 pub space: Option<String>,
63 #[serde(default = "default_list_limit")]
64 pub limit: usize,
65 #[serde(default, skip_serializing_if = "Option::is_none")]
66 pub confirmed: Option<bool>,
67}
68
69#[derive(Debug, Serialize, Deserialize)]
70pub struct ConfirmRequest {
71 #[serde(default = "default_confirmed")]
72 pub confirmed: bool,
73}
74
75#[derive(Debug, Serialize, Deserialize)]
76pub struct ReclassifyMemoryRequest {
77 pub memory_type: String,
78}
79
80#[derive(Debug, Serialize, Deserialize)]
81pub struct ImportMemoriesRequest {
82 pub source: String,
83 pub content: String,
84 #[serde(default)]
85 pub label: Option<String>,
86}
87
88#[derive(Debug, Serialize, Deserialize)]
91pub struct SearchRequest {
92 pub query: String,
93 #[serde(default = "default_limit")]
94 pub limit: usize,
95 pub source_filter: Option<String>,
96 #[serde(default, alias = "domain")]
97 pub space: Option<String>,
98}
99
100#[doc(hidden)]
101#[derive(Debug, Serialize, Deserialize)]
102pub struct ContextRequest {
103 pub current_file: String,
104 pub cursor_prefix: String,
105 #[serde(default = "default_limit")]
106 pub limit: usize,
107}
108
109#[derive(Debug, Serialize, Deserialize)]
110pub struct ChatContextRequest {
111 #[serde(default)]
112 pub query: Option<String>,
113 #[serde(default)]
114 pub conversation_id: Option<String>,
115 #[serde(default = "default_max_chunks")]
116 pub max_chunks: usize,
117 #[serde(default)]
118 pub relevance_threshold: Option<f64>,
119 #[deprecated(
123 since = "0.3.2",
124 note = "Goal taxonomy folded into Identity by migration 45 (Phase 0). \
125 Daemon ignores this field. Will be removed in 0.4."
126 )]
127 #[serde(default = "default_true")]
128 pub include_goals: bool,
129 #[serde(default, alias = "domain")]
130 pub space: Option<String>,
131}
132
133#[derive(Debug, Serialize, Deserialize)]
136pub struct CreateEntityRequest {
137 pub name: String,
138 pub entity_type: String,
139 #[serde(default, alias = "domain")]
140 pub space: Option<String>,
141 #[serde(default)]
142 pub source_agent: Option<String>,
143 #[serde(default)]
144 pub confidence: Option<f32>,
145}
146
147#[doc(hidden)]
148#[derive(Debug, Serialize, Deserialize)]
149pub struct CreateRelationRequest {
150 pub from_entity: String,
151 pub to_entity: String,
152 pub relation_type: String,
153 #[serde(default)]
154 pub source_agent: Option<String>,
155 #[serde(default)]
156 pub confidence: Option<f64>,
157 #[serde(default)]
158 pub explanation: Option<String>,
159 #[serde(default)]
160 pub source_memory_id: Option<String>,
161 #[serde(default)]
166 pub span: Option<String>,
167 #[serde(default)]
170 pub model_version: Option<String>,
171 #[serde(default)]
174 pub prompt_version: Option<String>,
175}
176
177#[derive(Debug, Serialize, Deserialize)]
178pub struct AddObservationRequest {
179 pub entity_id: String,
180 pub content: String,
181 #[serde(default)]
182 pub source_agent: Option<String>,
183 #[serde(default)]
184 pub confidence: Option<f32>,
185}
186
187#[doc(hidden)]
188#[derive(Debug, Serialize, Deserialize)]
189pub struct LinkEntityRequest {
190 pub source_id: String,
191 pub entity_id: String,
192}
193
194#[derive(Debug, Serialize, Deserialize)]
195pub struct ListEntitiesRequest {
196 #[serde(default)]
197 pub entity_type: Option<String>,
198 #[serde(default, alias = "domain")]
199 pub space: Option<String>,
200}
201
202#[derive(Debug, Serialize, Deserialize)]
203pub struct SearchEntitiesRequest {
204 pub query: String,
205 #[serde(default = "default_entity_search_limit")]
206 pub limit: usize,
207}
208
209#[derive(Debug, Serialize, Deserialize)]
212pub struct UpdateProfileRequest {
213 #[serde(default)]
214 pub name: Option<String>,
215 #[serde(default)]
216 pub display_name: Option<String>,
217 #[serde(default)]
218 pub email: Option<String>,
219 #[serde(default)]
220 pub bio: Option<String>,
221 #[serde(default)]
222 pub avatar_path: Option<String>,
223}
224
225#[derive(Debug, Serialize, Deserialize)]
226pub struct UpdateAgentRequest {
227 #[serde(default)]
228 pub agent_type: Option<String>,
229 #[serde(default)]
230 pub description: Option<String>,
231 #[serde(default)]
232 pub enabled: Option<bool>,
233 #[serde(default)]
234 pub trust_level: Option<String>,
235 #[serde(default)]
237 pub display_name: Option<String>,
238}
239
240#[derive(Debug, Serialize, Deserialize)]
243pub struct CreateSpaceRequest {
244 pub name: String,
245 pub description: Option<String>,
246}
247
248#[derive(Debug, Serialize, Deserialize)]
249pub struct UpdateSpaceRequest {
250 pub new_name: Option<String>,
251 pub description: Option<String>,
252}
253
254#[doc(hidden)]
257#[derive(Debug, Serialize, Deserialize)]
258pub struct CreateConceptRequest {
259 pub title: String,
260 pub content: String,
261 #[serde(default)]
262 pub summary: Option<String>,
263 #[serde(default)]
264 pub entity_id: Option<String>,
265 #[serde(default, alias = "domain")]
266 pub space: Option<String>,
267 #[serde(default)]
268 pub source_memory_ids: Vec<String>,
269 #[serde(default)]
270 pub creation_kind: Option<String>,
271 #[serde(default)]
276 pub workspace: Option<String>,
277}
278
279#[derive(Debug, Clone, PartialEq, Eq)]
285pub struct CreatePageDraftRequest {
286 pub draft_id: String,
288 pub title: String,
289 pub content: String,
290 pub space: Option<String>,
291 space_provided: bool,
292}
293
294impl CreatePageDraftRequest {
295 pub fn new(draft_id: String, title: String, content: String, space: Option<String>) -> Self {
299 Self {
300 draft_id,
301 title,
302 content,
303 space,
304 space_provided: true,
305 }
306 }
307
308 pub fn new_inheriting_header_space(draft_id: String, title: String, content: String) -> Self {
311 Self {
312 draft_id,
313 title,
314 content,
315 space: None,
316 space_provided: false,
317 }
318 }
319
320 pub fn space_was_provided(&self) -> bool {
325 self.space_provided
326 }
327}
328
329impl Serialize for CreatePageDraftRequest {
330 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
331 where
332 S: serde::Serializer,
333 {
334 use serde::ser::SerializeStruct;
335
336 let field_count = if self.space_provided { 4 } else { 3 };
337 let mut state = serializer.serialize_struct("CreatePageDraftRequest", field_count)?;
338 state.serialize_field("draft_id", &self.draft_id)?;
339 state.serialize_field("title", &self.title)?;
340 state.serialize_field("content", &self.content)?;
341 if self.space_provided {
342 state.serialize_field("space", &self.space)?;
343 }
344 state.end()
345 }
346}
347
348impl<'de> Deserialize<'de> for CreatePageDraftRequest {
349 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
350 where
351 D: serde::Deserializer<'de>,
352 {
353 #[derive(Deserialize)]
354 struct Wire {
355 draft_id: String,
356 #[serde(default)]
357 title: String,
358 #[serde(default)]
359 content: String,
360 #[serde(default, deserialize_with = "double_option")]
361 space: Option<Option<String>>,
362 }
363
364 let wire = Wire::deserialize(deserializer)?;
365 let (space_provided, space) = match wire.space {
366 Some(space) => (true, space),
367 None => (false, None),
368 };
369 Ok(Self {
370 draft_id: wire.draft_id,
371 title: wire.title,
372 content: wire.content,
373 space,
374 space_provided,
375 })
376 }
377}
378
379#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
381pub struct UpdatePageDraftRequest {
382 pub expected_version: i64,
383 pub title: String,
384 pub content: String,
385 pub space: Option<String>,
386}
387
388impl<'de> Deserialize<'de> for UpdatePageDraftRequest {
389 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
390 where
391 D: serde::Deserializer<'de>,
392 {
393 #[derive(Deserialize)]
394 struct Wire {
395 expected_version: i64,
396 title: String,
397 content: String,
398 #[serde(default, deserialize_with = "double_option")]
399 space: Option<Option<String>>,
400 }
401
402 let wire = Wire::deserialize(deserializer)?;
403 let space = wire
404 .space
405 .ok_or_else(|| serde::de::Error::missing_field("space"))?;
406 Ok(Self {
407 expected_version: wire.expected_version,
408 title: wire.title,
409 content: wire.content,
410 space,
411 })
412 }
413}
414
415#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
417pub struct PageDraftVersionRequest {
418 pub expected_version: i64,
419}
420
421#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
422#[serde(tag = "action", rename_all = "snake_case")]
423pub enum AcceptRefinementRequest {
424 Accept {
425 #[serde(default, skip_serializing_if = "Option::is_none")]
426 notes: Option<String>,
427 },
428 PickSpace {
429 space: String,
430 #[serde(default, skip_serializing_if = "Option::is_none")]
431 notes: Option<String>,
432 },
433}
434
435#[derive(Debug, Serialize, Deserialize)]
436pub struct SearchPagesRequest {
437 pub query: String,
438 #[serde(default)]
439 pub limit: Option<usize>,
440 #[serde(default, skip_serializing_if = "Option::is_none")]
441 pub page_type: Option<String>,
442 #[serde(default, skip_serializing_if = "Option::is_none")]
443 pub space: Option<String>,
444}
445
446#[derive(Debug, Serialize, Deserialize)]
449pub struct IngestTextRequest {
450 pub source: String,
451 pub source_id: String,
452 pub title: String,
453 pub content: String,
454 pub url: Option<String>,
455 pub metadata: Option<HashMap<String, String>>,
456}
457
458#[doc(hidden)]
459#[derive(Debug, Serialize, Deserialize)]
460pub struct IngestWebpageRequest {
461 pub url: String,
462 pub title: String,
463 pub content: String,
464 pub metadata: Option<HashMap<String, String>>,
465}
466
467#[derive(Debug, Serialize, Deserialize)]
468pub struct IngestMemoryRequest {
469 pub source: String,
470 pub source_id: String,
471 pub title: String,
472 pub content: String,
473 pub url: Option<String>,
474 pub tags: Option<Vec<String>>,
475 pub metadata: Option<HashMap<String, String>>,
476}
477
478#[doc(hidden)]
481#[derive(Debug, Serialize, Deserialize)]
482pub struct AddSourceRequest {
483 pub source_type: String,
484 pub path: String,
487}
488
489fn double_option<'de, T, D>(de: D) -> Result<Option<Option<T>>, D::Error>
494where
495 T: serde::Deserialize<'de>,
496 D: serde::Deserializer<'de>,
497{
498 serde::Deserialize::deserialize(de).map(Some)
499}
500
501#[derive(Debug, Serialize, Deserialize)]
502pub struct UpdateConfigRequest {
503 #[serde(default)]
504 pub skip_apps: Option<Vec<String>>,
505 #[serde(default)]
506 pub skip_title_patterns: Option<Vec<String>>,
507 #[serde(default)]
508 pub private_browsing_detection: Option<bool>,
509 #[serde(default)]
510 pub setup_completed: Option<bool>,
511 #[serde(default)]
512 pub clipboard_enabled: Option<bool>,
513 #[serde(default)]
514 pub screen_capture_enabled: Option<bool>,
515 #[serde(default)]
516 pub remote_access_enabled: Option<bool>,
517 #[serde(default)]
519 pub routine_model: Option<String>,
520 #[serde(default)]
522 pub synthesis_model: Option<String>,
523 #[serde(default)]
525 pub external_llm_endpoint: Option<String>,
526 #[serde(default)]
528 pub external_llm_model: Option<String>,
529 #[serde(
532 default,
533 deserialize_with = "double_option",
534 skip_serializing_if = "Option::is_none"
535 )]
536 pub external_llm_api_key: Option<Option<String>>,
537 #[serde(default)]
541 pub everyday_source: Option<String>,
542 #[serde(default)]
546 pub synthesis_source: Option<String>,
547 #[serde(default)]
550 pub page_map_auto_suggest: Option<bool>,
551}
552
553#[derive(Debug, Serialize, Deserialize)]
556pub struct DeleteByTimeRangeRequest {
557 pub start: i64,
558 pub end: i64,
559}
560
561#[derive(Debug, Serialize, Deserialize)]
562pub struct BulkDeleteItem {
563 pub source: String,
564 pub source_id: String,
565}
566
567#[derive(Debug, Serialize, Deserialize)]
568pub struct BulkDeleteRequest {
569 pub items: Vec<BulkDeleteItem>,
570}
571
572#[derive(Debug, Serialize, Deserialize)]
573pub struct UpdateChunkRequest {
574 pub content: String,
575}
576
577#[derive(Debug, Serialize, Deserialize)]
580pub struct ConfirmEntityRequest {
581 #[serde(default = "default_confirmed")]
582 pub confirmed: bool,
583}
584
585#[derive(Debug, Serialize, Deserialize)]
586pub struct AddEntityObservationRequest {
587 pub content: String,
588 #[serde(default)]
589 pub source_agent: Option<String>,
590 #[serde(default)]
591 pub confidence: Option<f32>,
592}
593
594#[derive(Debug, Serialize, Deserialize)]
595pub struct UpdateObservationRequest {
596 pub content: String,
597}
598
599#[derive(Debug, Serialize, Deserialize)]
600pub struct ConfirmObservationRequest {
601 #[serde(default = "default_confirmed")]
602 pub confirmed: bool,
603}
604
605#[derive(Debug, Serialize, Deserialize)]
608pub struct ReorderSpaceRequest {
609 pub name: String,
610 pub new_order: i64,
611}
612
613#[derive(Debug, Serialize, Deserialize)]
614pub struct SetDocumentSpaceRequest {
615 pub space_name: String,
616}
617
618#[derive(Debug, Serialize, Deserialize)]
621pub struct SetDocumentTagsRequest {
622 #[serde(default)]
623 pub source: Option<String>,
624 pub tags: Vec<String>,
625}
626
627#[derive(Debug, Serialize, Deserialize)]
630pub struct UpdateMemoryRequest {
631 #[serde(default)]
632 pub content: Option<String>,
633 #[serde(default, alias = "domain")]
634 pub space: Option<String>,
635 #[serde(default)]
636 pub confirmed: Option<bool>,
637 #[serde(default)]
638 pub memory_type: Option<String>,
639}
640
641#[derive(Debug, Serialize, Deserialize)]
642pub struct SetStabilityRequest {
643 pub stability: String,
644}
645
646#[derive(Debug, Serialize, Deserialize)]
647pub struct CorrectMemoryRequest {
648 pub correction_prompt: String,
649}
650
651#[derive(Debug, Serialize, Deserialize)]
654pub struct UpdatePageRequest {
655 pub content: String,
656 #[serde(default)]
660 pub source_memory_ids: Vec<String>,
661 #[serde(default)]
669 pub expected_version: Option<i64>,
670 #[serde(default)]
679 pub caller_id: Option<String>,
680 #[serde(default)]
681 pub operation_id: Option<String>,
682}
683
684#[derive(Debug, Serialize, Deserialize)]
694pub struct RefreshPageRequest {
695 pub content: String,
696 pub source_memory_ids: Vec<String>,
697 #[serde(default)]
698 pub summary: Option<String>,
699}
700
701#[derive(Debug, Deserialize, Serialize)]
705pub struct ExportPagesRequest {
706 pub vault_path: Option<String>,
707}
708
709#[derive(Debug, Deserialize, Serialize)]
710pub struct ExportPageRequest {
711 pub vault_path: String,
712}
713
714#[derive(Debug, Clone, Serialize, Deserialize)]
719pub struct TestLlmRequest {
720 pub endpoint: String,
721 pub model: String,
722 #[serde(default, skip_serializing_if = "Option::is_none")]
724 pub prompt: Option<String>,
725 #[serde(default, skip_serializing_if = "Option::is_none")]
727 pub api_key: Option<String>,
728}
729
730#[derive(Debug, Clone, Serialize, Deserialize)]
731pub struct TestLlmResponse {
732 pub response: String,
733}
734
735#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
739pub struct OnDeviceModelRequest {
740 pub model_id: String,
741}
742
743fn default_limit() -> usize {
746 10
747}
748
749fn default_list_limit() -> usize {
750 100
751}
752
753fn default_confirmed() -> bool {
754 true
755}
756
757fn default_max_chunks() -> usize {
758 5
759}
760
761fn default_true() -> bool {
762 true
763}
764
765fn default_entity_search_limit() -> usize {
766 20
767}
768
769#[cfg(test)]
770mod search_pages_page_type_test {
771 use super::*;
772
773 #[test]
774 fn search_pages_request_accepts_page_type() {
775 let json = r#"{"query":"foo","limit":10,"page_type":"recap"}"#;
776 let parsed: SearchPagesRequest = serde_json::from_str(json).unwrap();
777 assert_eq!(parsed.page_type.as_deref(), Some("recap"));
778 }
779
780 #[test]
781 fn search_pages_request_page_type_optional() {
782 let json = r#"{"query":"foo","limit":10}"#;
783 let parsed: SearchPagesRequest = serde_json::from_str(json).unwrap();
784 assert!(parsed.page_type.is_none());
785 }
786
787 #[test]
788 fn search_pages_request_accepts_optional_space() {
789 let json = r#"{"query":"foo","space":"work"}"#;
790 let parsed: SearchPagesRequest = serde_json::from_str(json).unwrap();
791 assert_eq!(parsed.space.as_deref(), Some("work"));
792
793 let omitted: SearchPagesRequest = serde_json::from_str(r#"{"query":"foo"}"#).unwrap();
794 assert!(omitted.space.is_none());
795 }
796}
797
798#[cfg(test)]
799mod list_memories_confirmed_test {
800 use super::*;
801
802 #[test]
803 fn confirmed_false_serializes_to_json() {
804 let req = ListMemoriesRequest {
805 memory_type: None,
806 space: None,
807 limit: 20,
808 confirmed: Some(false),
809 };
810 let s = serde_json::to_string(&req).unwrap();
811 assert!(s.contains("\"confirmed\":false"), "got: {s}");
812 }
813
814 #[test]
815 fn confirmed_none_skips_serialization() {
816 let req = ListMemoriesRequest {
817 memory_type: None,
818 space: None,
819 limit: 20,
820 confirmed: None,
821 };
822 let s = serde_json::to_string(&req).unwrap();
823 assert!(!s.contains("confirmed"), "got: {s}");
824 }
825}
826
827#[cfg(test)]
828mod set_document_tags_test {
829 use super::*;
830
831 #[test]
832 fn set_document_tags_request_accepts_optional_source() {
833 let req: SetDocumentTagsRequest =
834 serde_json::from_str(r#"{"source":"manual","tags":["rust"]}"#).unwrap();
835
836 assert_eq!(req.source.as_deref(), Some("manual"));
837 assert_eq!(req.tags, vec!["rust"]);
838 }
839
840 #[test]
841 fn set_document_tags_request_defaults_source_for_old_payloads() {
842 let req: SetDocumentTagsRequest = serde_json::from_str(r#"{"tags":["rust"]}"#).unwrap();
843
844 assert!(req.source.is_none());
845 assert_eq!(req.tags, vec!["rust"]);
846 }
847}
848
849#[cfg(test)]
850mod on_device_model_request_test {
851 use super::*;
852
853 #[test]
854 fn on_device_model_request_round_trips_model_id() {
855 let req: OnDeviceModelRequest = serde_json::from_str(r#"{"model_id":"qwen3-4b"}"#).unwrap();
856
857 assert_eq!(req.model_id, "qwen3-4b");
858 assert_eq!(
859 serde_json::to_string(&req).unwrap(),
860 r#"{"model_id":"qwen3-4b"}"#
861 );
862 }
863}
864
865#[cfg(test)]
866mod tests {
867 use super::*;
868
869 #[test]
870 fn update_config_request_external_key_tristate() {
871 let r: UpdateConfigRequest = serde_json::from_str("{}").unwrap();
872 assert_eq!(r.external_llm_api_key, None); let r: UpdateConfigRequest =
874 serde_json::from_str(r#"{"external_llm_api_key":null}"#).unwrap();
875 assert_eq!(r.external_llm_api_key, Some(None)); let r: UpdateConfigRequest =
877 serde_json::from_str(r#"{"external_llm_api_key":"sk-x"}"#).unwrap();
878 assert_eq!(r.external_llm_api_key, Some(Some("sk-x".to_string())));
879 }
880
881 #[test]
882 fn test_llm_request_api_key_optional() {
883 let r: TestLlmRequest =
884 serde_json::from_str(r#"{"endpoint":"http://x","model":"m"}"#).unwrap();
885 assert!(r.api_key.is_none());
886 }
887}