@@ -97,6 +97,11 @@ async fn build_tasks_section(workspace_dir: &Path) -> String {
9797 section
9898}
9999
100+ /// Max chars of content to include per document in the report.
101+ const DOC_CONTENT_SNIPPET_CHARS : usize = 500 ;
102+ /// Max number of new docs to include full content for.
103+ const MAX_DOCS_WITH_CONTENT : usize = 10 ;
104+
100105async fn build_memory_docs_section ( client : & MemoryClient , last_tick_at : f64 ) -> String {
101106 let docs = match client. list_documents ( None ) . await {
102107 Ok ( raw) => raw,
@@ -105,7 +110,6 @@ async fn build_memory_docs_section(client: &MemoryClient, last_tick_at: f64) ->
105110 }
106111 } ;
107112
108- // Parse the raw serde_json::Value into document summaries
109113 let doc_array = docs
110114 . as_array ( )
111115 . or_else ( || docs. get ( "documents" ) . and_then ( |v| v. as_array ( ) ) ) ;
@@ -114,9 +118,10 @@ async fn build_memory_docs_section(client: &MemoryClient, last_tick_at: f64) ->
114118 return "## Memory Documents\n \n No documents found.\n " . to_string ( ) ;
115119 } ;
116120
117- // Filter to docs updated since last tick (or all on cold start)
118121 let is_cold_start = last_tick_at <= 0.0 ;
119- let mut new_docs: Vec < ( & str , & str , f64 ) > = Vec :: new ( ) ;
122+
123+ // Collect new/updated doc metadata
124+ let mut new_doc_keys: Vec < ( String , String , String ) > = Vec :: new ( ) ; // (namespace, key, title)
120125 for doc in doc_array {
121126 let updated_at = doc
122127 . get ( "updated_at" )
@@ -132,13 +137,23 @@ async fn build_memory_docs_section(client: &MemoryClient, last_tick_at: f64) ->
132137 . get ( "title" )
133138 . or_else ( || doc. get ( "key" ) )
134139 . and_then ( |v| v. as_str ( ) )
135- . unwrap_or ( "untitled" ) ;
136- let namespace = doc. get ( "namespace" ) . and_then ( |v| v. as_str ( ) ) . unwrap_or ( "?" ) ;
140+ . unwrap_or ( "untitled" )
141+ . to_string ( ) ;
142+ let namespace = doc
143+ . get ( "namespace" )
144+ . and_then ( |v| v. as_str ( ) )
145+ . unwrap_or ( "?" )
146+ . to_string ( ) ;
147+ let key = doc
148+ . get ( "key" )
149+ . and_then ( |v| v. as_str ( ) )
150+ . unwrap_or ( "" )
151+ . to_string ( ) ;
137152
138- new_docs . push ( ( namespace, title , updated_at ) ) ;
153+ new_doc_keys . push ( ( namespace, key , title ) ) ;
139154 }
140155
141- if new_docs . is_empty ( ) {
156+ if new_doc_keys . is_empty ( ) {
142157 return format ! (
143158 "## Memory Documents\n \n {} total documents. No changes since last tick.\n " ,
144159 doc_array. len( )
@@ -148,14 +163,45 @@ async fn build_memory_docs_section(client: &MemoryClient, last_tick_at: f64) ->
148163 let mut section = format ! (
149164 "## Memory Documents\n \n {} total, {} new/updated since last tick:\n \n " ,
150165 doc_array. len( ) ,
151- new_docs . len( )
166+ new_doc_keys . len( )
152167 ) ;
153- for ( namespace, title, _) in & new_docs {
154- let _ = writeln ! ( section, "- [{namespace}] {title}" ) ;
168+
169+ // Recall content per namespace for new docs
170+ let mut recalled_namespaces = std:: collections:: HashSet :: new ( ) ;
171+ let mut docs_with_content = 0 ;
172+
173+ for ( namespace, _key, title) in & new_doc_keys {
174+ let _ = writeln ! ( section, "### [{namespace}] {title}\n " ) ;
175+
176+ // Only recall each namespace once (may have multiple docs per namespace)
177+ if docs_with_content < MAX_DOCS_WITH_CONTENT
178+ && recalled_namespaces. insert ( namespace. clone ( ) )
179+ {
180+ if let Ok ( Some ( context) ) = client. recall_namespace ( namespace, 3 ) . await {
181+ let snippet = truncate_at_char_boundary ( & context, DOC_CONTENT_SNIPPET_CHARS ) ;
182+ if !snippet. trim ( ) . is_empty ( ) {
183+ let _ = writeln ! ( section, "{snippet}\n " ) ;
184+ }
185+ }
186+ docs_with_content += 1 ;
187+ }
155188 }
156189 section
157190}
158191
192+ fn truncate_at_char_boundary ( text : & str , max_chars : usize ) -> String {
193+ if text. len ( ) <= max_chars {
194+ return text. to_string ( ) ;
195+ }
196+ let truncate_at = text
197+ . char_indices ( )
198+ . take_while ( |( i, _) | * i < max_chars)
199+ . last ( )
200+ . map ( |( i, ch) | i + ch. len_utf8 ( ) )
201+ . unwrap_or ( 0 ) ;
202+ format ! ( "{}..." , & text[ ..truncate_at] )
203+ }
204+
159205async fn build_graph_section ( client : & MemoryClient , last_tick_at : f64 ) -> String {
160206 let relations = match client. graph_query ( None , None , None ) . await {
161207 Ok ( rows) => rows,
0 commit comments