11use anyhow:: { Context , Result } ;
22use chrono:: { SecondsFormat , Utc } ;
3- use native_host_rust:: context_packet:: { context_packet_paths, generate_source_context} ;
3+ use native_host_rust:: context_packet:: {
4+ context_packet_paths, generate_source_context, prepare_agent_context_handoff,
5+ AgentContextHandoff , AgentContextHandoffInput ,
6+ } ;
47use native_host_rust:: protocol:: { SourceContextResult , SummaryProviderSettings } ;
58use serde:: { Deserialize , Serialize } ;
69use std:: fs;
@@ -25,6 +28,10 @@ struct AgentRequest {
2528 id : String ,
2629 command : AgentCommand ,
2730 target_directory : Option < String > ,
31+ meetings_root : Option < String > ,
32+ task : Option < String > ,
33+ project_hint : Option < String > ,
34+ working_directory : Option < String > ,
2835 summary_settings : Option < SummaryProviderSettings > ,
2936}
3037
@@ -33,6 +40,7 @@ struct AgentRequest {
3340enum AgentCommand {
3441 Capabilities ,
3542 GenerateContext ,
43+ PrepareAgentContext ,
3644 JobStatus ,
3745 Shutdown ,
3846}
@@ -58,6 +66,8 @@ struct AgentPayload {
5866 job : Option < JobStatus > ,
5967 #[ serde( skip_serializing_if = "Option::is_none" ) ]
6068 source_context : Option < SourceContextResult > ,
69+ #[ serde( skip_serializing_if = "Option::is_none" ) ]
70+ handoff : Option < AgentContextHandoff > ,
6171}
6272
6373#[ derive( Debug , Clone , Serialize , Deserialize ) ]
@@ -156,6 +166,7 @@ fn handle_request(request: AgentRequest, running: &Arc<AtomicBool>) -> AgentResp
156166 agent_version : Some ( env ! ( "CARGO_PKG_VERSION" ) ) ,
157167 job : None ,
158168 source_context : None ,
169+ handoff : None ,
159170 } ) ,
160171 ) ,
161172 AgentCommand :: GenerateContext => match generate_context_job ( & request) {
@@ -165,6 +176,19 @@ fn handle_request(request: AgentRequest, running: &Arc<AtomicBool>) -> AgentResp
165176 agent_version : None ,
166177 job : Some ( job) ,
167178 source_context : Some ( result) ,
179+ handoff : None ,
180+ } ) ,
181+ ) ,
182+ Err ( error) => failure_response ( request. id , format ! ( "{error:#}" ) ) ,
183+ } ,
184+ AgentCommand :: PrepareAgentContext => match prepare_agent_context ( & request) {
185+ Ok ( handoff) => success_response (
186+ request. id ,
187+ Some ( AgentPayload {
188+ agent_version : None ,
189+ job : None ,
190+ source_context : None ,
191+ handoff : Some ( handoff) ,
168192 } ) ,
169193 ) ,
170194 Err ( error) => failure_response ( request. id , format ! ( "{error:#}" ) ) ,
@@ -176,6 +200,7 @@ fn handle_request(request: AgentRequest, running: &Arc<AtomicBool>) -> AgentResp
176200 agent_version : None ,
177201 job,
178202 source_context : None ,
203+ handoff : None ,
179204 } ) ,
180205 ) ,
181206 Err ( error) => failure_response ( request. id , format ! ( "{error:#}" ) ) ,
@@ -188,12 +213,34 @@ fn handle_request(request: AgentRequest, running: &Arc<AtomicBool>) -> AgentResp
188213 agent_version : Some ( env ! ( "CARGO_PKG_VERSION" ) ) ,
189214 job : None ,
190215 source_context : None ,
216+ handoff : None ,
191217 } ) ,
192218 )
193219 }
194220 }
195221}
196222
223+ fn prepare_agent_context ( request : & AgentRequest ) -> Result < AgentContextHandoff > {
224+ let meetings_root = request
225+ . meetings_root
226+ . as_deref ( )
227+ . context ( "A meetings root directory is required." ) ?;
228+ let task = request
229+ . task
230+ . as_deref ( )
231+ . filter ( |task| !task. trim ( ) . is_empty ( ) )
232+ . context ( "A task is required to prepare agent context." ) ?;
233+ let result = prepare_agent_context_handoff (
234+ Path :: new ( meetings_root) ,
235+ AgentContextHandoffInput {
236+ task : task. to_string ( ) ,
237+ project_hint : request. project_hint . clone ( ) ,
238+ working_directory : request. working_directory . clone ( ) ,
239+ } ,
240+ ) ?;
241+ Ok ( result. handoff )
242+ }
243+
197244fn generate_context_job ( request : & AgentRequest ) -> Result < ( JobStatus , SourceContextResult ) > {
198245 let target_directory = request
199246 . target_directory
@@ -364,4 +411,46 @@ mod tests {
364411 let temp = tempfile:: tempdir ( ) . unwrap ( ) ;
365412 assert ! ( read_job_status( temp. path( ) ) . unwrap( ) . is_none( ) ) ;
366413 }
414+
415+ #[ test]
416+ fn prepare_agent_context_request_writes_handoff_files ( ) {
417+ let temp = tempfile:: tempdir ( ) . unwrap ( ) ;
418+ let meetings_root = temp. path ( ) ;
419+ let project = meetings_root
420+ . join ( "projects" )
421+ . join ( "mirrornote-agent-runtime" ) ;
422+ fs:: create_dir_all ( & project) . unwrap ( ) ;
423+ fs:: write (
424+ project. join ( "source-index.jsonl" ) ,
425+ "{\" id\" :\" meeting:note-1\" ,\" type\" :\" meeting\" ,\" title\" :\" Agent Runtime Sync\" ,\" observedAt\" :\" 2026-05-05T01:00:00Z\" ,\" projectHint\" :\" MirrorNote Agent Runtime\" }\n " ,
426+ )
427+ . unwrap ( ) ;
428+ fs:: write (
429+ project. join ( "memory-objects.jsonl" ) ,
430+ "{\" id\" :\" meeting:note-1::decision-1\" ,\" type\" :\" decision\" ,\" title\" :\" Keep context automatic\" ,\" body\" :\" Keep context automatic\" ,\" status\" :\" active\" ,\" confidence\" :0.95,\" evidenceCoverage\" :\" direct\" ,\" sourceRefs\" :[{\" sourceId\" :\" meeting:note-1\" ,\" sourceType\" :\" meeting\" ,\" location\" :{\" kind\" :\" metadata\" }}]}\n " ,
431+ )
432+ . unwrap ( ) ;
433+
434+ let response = handle_request (
435+ AgentRequest {
436+ id : "request-1" . to_string ( ) ,
437+ command : AgentCommand :: PrepareAgentContext ,
438+ target_directory : None ,
439+ meetings_root : Some ( meetings_root. to_string_lossy ( ) . into_owned ( ) ) ,
440+ task : Some ( "Run Codex with prior context" . to_string ( ) ) ,
441+ project_hint : Some ( "MirrorNote Agent Runtime" . to_string ( ) ) ,
442+ working_directory : Some ( "/tmp/MirrorNote" . to_string ( ) ) ,
443+ summary_settings : None ,
444+ } ,
445+ & Arc :: new ( AtomicBool :: new ( true ) ) ,
446+ ) ;
447+
448+ assert ! ( response. ok) ;
449+ let payload = response. payload . unwrap ( ) ;
450+ let handoff = payload. handoff . unwrap ( ) ;
451+ assert_eq ! ( handoff. project_id, "mirrornote-agent-runtime" ) ;
452+ assert_eq ! ( handoff. decisions. len( ) , 1 ) ;
453+ assert ! ( project. join( "handoffs" ) . join( "handoff.json" ) . exists( ) ) ;
454+ assert ! ( project. join( "handoffs" ) . join( "handoff.md" ) . exists( ) ) ;
455+ }
367456}
0 commit comments