From e7abe86cdadfcf0ab838edbd3aa4b873a7cd49ae Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 7 Oct 2026 06:44:19 +0000 Subject: [PATCH] chore: sync public mirror from internal --- .repository-projection.json | 6 +-- vendor/dex-loop/src/summary.rs | 68 ++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 3 deletions(-) diff --git a/.repository-projection.json b/.repository-projection.json index 09d9ad27d..9ec447b84 100644 --- a/.repository-projection.json +++ b/.repository-projection.json @@ -3,11 +3,11 @@ "projection": "deixic-code", "projectionSchemaVersion": 1, "sourceRepository": "dx-corp/mono", - "sourceSha": "cad6ae4ef4abdce19fa502b83038a07eac298090", + "sourceSha": "0bab655ecdd7a8bf4b0fa0df5045a932278966a7", "destinationRepository": "dx-corp/code", - "priorProjectedBase": "3b9bb6470e3cb58d5bbfda3b92a5a9159f26bac6", + "priorProjectedBase": "b8ba8d5dd808d6e4ccb65996f40974fc3a5a2371", "definitionDigest": "cb9d429542ebb0a2de9b42a7aad60d9d8696a648ceba47c30f05c0b285ca0db7", "toolDigest": "c9112982c70b80737d8faac67d58b06d55bde0dc297eb1009c1b02296f34f4a9", - "contentDigest": "a480358decd3772d195d8b5289b5cf3643920d2a23aa5935fa5df41a5127cde9", + "contentDigest": "4644f31a04a199598153f60167cf36e998791c146b6caa5866efc1a9a79b4a3d", "publicationEligible": true } diff --git a/vendor/dex-loop/src/summary.rs b/vendor/dex-loop/src/summary.rs index ccd4c5f3c..9902d2f8f 100644 --- a/vendor/dex-loop/src/summary.rs +++ b/vendor/dex-loop/src/summary.rs @@ -1032,6 +1032,74 @@ mod tests { .chain(stream::pending()) } } + + #[tokio::test(start_paused = true)] + async fn semantic_summary_can_recover_after_the_mechanical_digest_fills() { + struct DelayedSummary(Duration); + impl Model for DelayedSummary { + fn stream<'a>( + &'a self, + ctx: &'a Context, + tools: &'a [&'a ToolSpec], + ) -> impl Stream> + Send + 'a { + assert!(tools.is_empty()); + let Message::User { text, .. } = &ctx.history()[0].message else { + panic!("summary input"); + }; + assert!(text.contains("Never publish without my approval. Budget is $10.")); + assert!(!text.contains("current request remains verbatim")); + stream::iter([Ok(ModelChunk::Text("Approval required. ".into()))]).chain( + stream::once(async move { + tokio::time::sleep(self.0).await; + Ok(ModelChunk::Text( + "Budget is $10; analysis is unfinished.".into(), + )) + }), + ) + } + } + + let mut entries = history(); + entries.extend((3..40).map(|cursor| Entry { + cursor: Cursor(cursor), + message: Message::Tool { + call: crate::CallId::new(format!("historical-call-{cursor}")), + name: ToolName::new("dex.describe"), + outcome: Outcome::Succeeded, + output: Output::Text("catalog entry ".repeat(100)), + }, + })); + let (_, references, from, to) = material(&entries).unwrap(); + assert!(mechanical_digest(&entries, &references, from, to).is_none()); + let ctx = context(); + let original = ctx.clone(); + for (deadline, latency, succeeds) in [(10, 45, false), (300, 45, true), (300, 305, false)] { + let result = ModelSummarizer::new(DelayedSummary(Duration::from_secs(latency))) + .with_timeout(Duration::from_secs(deadline)) + .summarize(&ctx, &entries) + .await; + assert_eq!(result.text.is_some(), succeeds); + if let Some(text) = result.text { + assert!(text.len() <= SUMMARY_BYTES); + let envelope: Value = serde_json::from_str(&text).unwrap(); + assert_eq!( + envelope["references"], + json!(["attachment@v1", "result@v1"]) + ); + assert_eq!(envelope["covers_from_cursor"], 1); + assert_eq!(envelope["covers_to_cursor"], 39); + assert_eq!( + envelope["summary"], + "Approval required. Budget is $10; analysis is unfinished." + ); + } + assert_eq!( + ctx, original, + "summary planning cannot mutate owner history" + ); + } + } + #[tokio::test] async fn timed_out_summary_falls_back_and_keeps_already_observed_usage() { let summarizer = ModelSummarizer {