From a6db4bad733ddd7f7d6c4f7a64409e1d111f48fb Mon Sep 17 00:00:00 2001 From: SDK Lead Date: Tue, 28 Jul 2026 18:22:51 +0000 Subject: [PATCH 1/2] sync: add valid_from to StoreMemoryRequest + BatchStoreMemoryItem (DAK-7424) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Server v0.11.98+ (DAK-7424) stores the bi-temporal valid_from field. Adds optional Unix-seconds valid_from to StoreMemoryRequest and BatchStoreMemoryItem with serde skip_serializing_if=None — field is omitted when unset, preserving default ingest-time behaviour. Includes with_valid_from() builder and two serialization unit tests asserting the field is present/absent as expected. Closes DAK-7571. --- src/memory.rs | 24 ++++++++++++++++++++++++ tests/integration_test.rs | 19 +++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/src/memory.rs b/src/memory.rs index 445d128..ed51358 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -52,6 +52,11 @@ pub struct StoreMemoryRequest { /// decay engine on expiry (DECAY-3). #[serde(skip_serializing_if = "Option::is_none")] pub expires_at: Option, + /// Bi-temporal validity start — Unix timestamp (seconds) indicating when this + /// memory becomes temporally valid. Defaults to ingest time when omitted. + /// Used by temporal recall queries (server v0.11.98+, DAK-7424). + #[serde(skip_serializing_if = "Option::is_none")] + pub valid_from: Option, } fn default_importance() -> f32 { @@ -71,6 +76,7 @@ impl StoreMemoryRequest { metadata: None, ttl_seconds: None, expires_at: None, + valid_from: None, } } @@ -117,6 +123,13 @@ impl StoreMemoryRequest { self.expires_at = Some(expires_at); self } + + /// Set bi-temporal validity start as a Unix timestamp (seconds). + /// Defaults to ingest time when omitted (server v0.11.98+, DAK-7424). + pub fn with_valid_from(mut self, valid_from: i64) -> Self { + self.valid_from = Some(valid_from); + self + } } /// Stored memory response from `POST /v1/memory/store`. @@ -1064,6 +1077,10 @@ pub struct BatchStoreMemoryItem { pub ttl_seconds: Option, #[serde(skip_serializing_if = "Option::is_none")] pub expires_at: Option, + /// Bi-temporal validity start — Unix timestamp (seconds). Defaults to ingest time when omitted. + /// Used by temporal recall queries (server v0.11.98+, DAK-7424). + #[serde(skip_serializing_if = "Option::is_none")] + pub valid_from: Option, /// Optional custom ID. Auto-generated if not provided. #[serde(skip_serializing_if = "Option::is_none")] pub id: Option, @@ -1103,6 +1120,13 @@ impl BatchStoreMemoryItem { self } + /// Set bi-temporal validity start as a Unix timestamp (seconds). + /// Defaults to ingest time when omitted (server v0.11.98+, DAK-7424). + pub fn with_valid_from(mut self, valid_from: i64) -> Self { + self.valid_from = Some(valid_from); + self + } + /// Set a custom memory ID. pub fn with_id(mut self, id: impl Into) -> Self { self.id = Some(id.into()); diff --git a/tests/integration_test.rs b/tests/integration_test.rs index 5189c97..33a5926 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -55,6 +55,25 @@ async fn test_health() { assert!(health.healthy); } +// --------------------------------------------------------------------------- +// StoreMemoryRequest — bi-temporal valid_from serialization (DAK-7424) +// --------------------------------------------------------------------------- + +#[test] +fn test_store_memory_request_valid_from_serialized_when_set() { + let req = StoreMemoryRequest::new("agent-1", "temporal memory") + .with_valid_from(1_700_000_000); + let json = serde_json::to_value(&req).unwrap(); + assert_eq!(json["valid_from"], 1_700_000_000i64); +} + +#[test] +fn test_store_memory_request_valid_from_omitted_when_none() { + let req = StoreMemoryRequest::new("agent-1", "non-temporal memory"); + let json = serde_json::to_value(&req).unwrap(); + assert!(json.get("valid_from").is_none(), "valid_from must be absent from JSON when not set"); +} + #[test] fn test_health_response_build_sha_present() { let json = r#"{"healthy":true,"version":"0.11.84","build_sha":"abc1234def5678"}"#; From 62eff82894507436ef411e90ffa1bbf64ef30fb1 Mon Sep 17 00:00:00 2001 From: CTO Agent Date: Tue, 28 Jul 2026 18:27:15 +0000 Subject: [PATCH 2/2] fix(rs): wire valid_from into examples/memory.rs struct literal + rustfmt tests (DAK-7605) - examples/memory.rs: add valid_from: None to StoreMemoryRequest literal (fixes E0063 test/build fail) - tests/integration_test.rs: apply rustfmt (fixes Format check) Co-Authored-By: Claude Opus 4.8 --- examples/memory.rs | 1 + tests/integration_test.rs | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/examples/memory.rs b/examples/memory.rs index 3bee5ff..48a694e 100644 --- a/examples/memory.rs +++ b/examples/memory.rs @@ -29,6 +29,7 @@ async fn main() -> Result<(), Box> { session_id: None, ttl_seconds: None, expires_at: None, + valid_from: None, }) .await?; println!("Stored memory: {}", mem1.memory_id); diff --git a/tests/integration_test.rs b/tests/integration_test.rs index 33a5926..8a69e49 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -61,8 +61,7 @@ async fn test_health() { #[test] fn test_store_memory_request_valid_from_serialized_when_set() { - let req = StoreMemoryRequest::new("agent-1", "temporal memory") - .with_valid_from(1_700_000_000); + let req = StoreMemoryRequest::new("agent-1", "temporal memory").with_valid_from(1_700_000_000); let json = serde_json::to_value(&req).unwrap(); assert_eq!(json["valid_from"], 1_700_000_000i64); } @@ -71,7 +70,10 @@ fn test_store_memory_request_valid_from_serialized_when_set() { fn test_store_memory_request_valid_from_omitted_when_none() { let req = StoreMemoryRequest::new("agent-1", "non-temporal memory"); let json = serde_json::to_value(&req).unwrap(); - assert!(json.get("valid_from").is_none(), "valid_from must be absent from JSON when not set"); + assert!( + json.get("valid_from").is_none(), + "valid_from must be absent from JSON when not set" + ); } #[test]